protected boolean writeDirectMethodCall()

in src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java [149:249]


    protected boolean writeDirectMethodCall(final MethodNode target, final boolean implicitThis, final Expression receiver, final TupleExpression args) {
        if (target == null || target instanceof ExtensionMethodNode) return false;

        ClassNode declaringClass = target.getDeclaringClass();
        ClassNode enclosingClass = controller.getClassNode(), receiverType = enclosingClass;
        if (receiver != null) {
            receiverType = controller.getTypeChooser().resolveType(receiver, enclosingClass);
            if (target.isStatic() && isClassClassNodeWrappingConcreteType(receiverType)) {
                receiverType = receiverType.getGenericsTypes()[0].getType();
            }
        }

        CompileStack compileStack = controller.getCompileStack();
        OperandStack operandStack = controller.getOperandStack();
        MethodVisitor mv = controller.getMethodVisitor();
        int startDepth = operandStack.getStackLength();

        // handle receiver
        if (!target.isStatic()) {
            if (receiver != null) {
                Expression objectExpression = receiver;
                if (!implicitThis && callSuperDefault(enclosingClass, target, receiver)) {
                    compileStack.pushImplicitThis(true);
                    objectExpression = new VariableExpression("this", declaringClass);
                } else if (implicitThis
                        && enclosingClass.getOuterClass() != null
                        && !enclosingClass.isDerivedFrom(declaringClass)
                        && !enclosingClass.implementsInterface(declaringClass)) {
                    // outer class method invocation
                    compileStack.pushImplicitThis(false);
                    if (!controller.isInGeneratedFunction() && isThis(receiver)) {
                        objectExpression = new PropertyExpression(new ClassExpression(declaringClass), "this");
                    }
                } else {
                    compileStack.pushImplicitThis(implicitThis);
                }
                objectExpression.visit(controller.getAcg());
                operandStack.doGroovyCast(declaringClass);
                compileStack.popImplicitThis();
            } else {
                mv.visitIntInsn(ALOAD, 0);
                operandStack.push(enclosingClass);
            }
        }

        int opcode;
        if (target.isStatic()) {
            opcode = INVOKESTATIC;
        } else if (isSuperExpression(receiver) || isClassWithSuper(receiver)) {
            opcode = INVOKESPECIAL;
        } else if (declaringClass.isInterface()) {
            opcode = INVOKEINTERFACE;
        } else {
            opcode = INVOKEVIRTUAL;
        }

        ClassNode ownerClass = declaringClass;
        if (opcode == INVOKESPECIAL) { // GROOVY-8693, GROOVY-9909
            if (!declaringClass.isInterface() || receiverType.implementsInterface(declaringClass)) ownerClass = receiverType;
        } else if (opcode == INVOKEVIRTUAL && isObjectType(declaringClass)) {
            // avoid using a narrowed type if the method is defined on Object, because it can interfere
            // with delegate type inference in static compilation mode and trigger a ClassCastException
        } else if (opcode == INVOKEVIRTUAL
                && !receiverType.isArray()
                && !receiverType.isInterface()
                && !isPrimitiveType(receiverType)
                && !receiverType.equals(declaringClass)
                && receiverType.isDerivedFrom(declaringClass)) {
            ownerClass = receiverType; // use actual for typical call
            if (!receiverType.equals(operandStack.getTopOperand())) {
                mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(ownerClass));
            }
        } else if ((declaringClass.getModifiers() & (ACC_FINAL | ACC_PUBLIC)) == 0 && !receiverType.equals(declaringClass)
                && (declaringClass.isInterface() ? receiverType.implementsInterface(declaringClass) : receiverType.isDerivedFrom(declaringClass))) {
            // GROOVY-6962, GROOVY-9955, GROOVY-10380: method declared by inaccessible class or interface
            if (declaringClass.isInterface() && !receiverType.isInterface()) opcode = INVOKEVIRTUAL;
            ownerClass = receiverType;
        }

        ClassNode returnType = target.getReturnType();
        Parameter[] parameters = target.getParameters();
        if (parameters.length > 0 && parameters[0].isReceiver())
            parameters = Arrays.copyOfRange(parameters, 1, parameters.length);

        loadArguments(args.getExpressions(), parameters);

        String ownerName = BytecodeHelper.getClassInternalName(ownerClass);
        String signature = BytecodeHelper.getMethodDescriptor(returnType, parameters);
        mv.visitMethodInsn(opcode, ownerName, target.getName(), signature, ownerClass.isInterface());
        operandStack.remove(operandStack.getStackLength() - startDepth); // receiver plus argument(s)

        if (isPrimitiveVoid(returnType)) {
            if (currentCall != null && currentCall.getNodeMetaData(AsmClassGenerator.ELIDE_EXPRESSION_VALUE) != null) {
                return true; // do not load value
            }
            returnType = ClassHelper.OBJECT_TYPE;
            mv.visitInsn(ACONST_NULL);
        }
        operandStack.push(returnType);
        return true;
    }