public void makeCall()

in src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java [471:615]


    public void makeCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean safe, final boolean spreadSafe, final boolean implicitThis) {
        if (origin.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) != null) {
            StaticTypesWriterController staticController = (StaticTypesWriterController) controller;
            if (origin instanceof MethodCallExpression) {
                ((MethodCallExpression) origin).setMethodTarget(null);
            }
            InvocationWriter dynamicInvocationWriter = staticController.getRegularInvocationWriter();
            dynamicInvocationWriter.makeCall(origin, receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
            return;
        }
        if (implicitThis && tryImplicitReceiver(origin, message, arguments, adapter, safe, spreadSafe)) {
            return;
        }
        // if call is spread safe, replace it with a for in loop
        if (spreadSafe && (origin instanceof MethodCallExpression || (origin instanceof PropertyExpression && !controller.getCompileStack().isLHS()))) {
            // receiver expressions with side-effects should not be re-visited; avoid by using a temporary variable
            Expression tmpReceiver = receiver;
            if (!(receiver instanceof VariableExpression || receiver instanceof ConstantExpression)) {
                tmpReceiver = new TemporaryVariableExpression(receiver);
            }

            Label nonNull = new Label();
            Label allDone = new Label();
            MethodVisitor mv = controller.getMethodVisitor();
            OperandStack operandStack = controller.getOperandStack();
            boolean produceResultList = origin.getNodeMetaData(AsmClassGenerator.ELIDE_EXPRESSION_VALUE) == null;

            // if (receiver == null)
            tmpReceiver.visit(controller.getAcg());
            mv.visitJumpInsn(IFNONNULL, nonNull);
            operandStack.remove(1);

            // result is null
            if (produceResultList)
                mv.visitInsn(ACONST_NULL);
            mv.visitJumpInsn(GOTO, allDone);

            // else
            mv.visitLabel(nonNull);

            ClassNode resultType = origin.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
            ClassNode valuesType = origin.getNodeMetaData(StaticCompilationMetadataKeys.COMPONENT_TYPE);
            if (valuesType == null) valuesType = StaticTypeCheckingVisitor.inferLoopElementType(resultType);

            // def result = new ArrayList()
            ConstructorCallExpression cce = ctorX(StaticCompilationVisitor.ARRAYLIST_CLASSNODE);
            cce.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET,
                    StaticCompilationVisitor.ARRAYLIST_CONSTRUCTOR);
            var result = new TemporaryVariableExpression(cce);
            if (produceResultList) result.visit(controller.getAcg());

            ClassNode elementType = StaticTypeCheckingVisitor.inferLoopElementType(controller.getTypeChooser().resolveType(receiver, controller.getClassNode()));
            Parameter element = new Parameter(elementType, "for$it$" + labelCounter.incrementAndGet());

            Expression nextValue;
            if (origin instanceof MethodCallExpression) {
                MethodCallExpression oldMCE = (MethodCallExpression) origin;
                MethodCallExpression newMCE = callX(
                        varX(element),
                        oldMCE.getMethod(),
                        oldMCE.getArguments()
                );
                newMCE.setGenericsTypes(oldMCE.getGenericsTypes());
                newMCE.setImplicitThis(false);
                MethodNode target = oldMCE.getMethodTarget();
                newMCE.setMethodTarget(target);
                if (target == null || !target.isVoidMethod())
                    newMCE.setNodeMetaData(StaticTypesMarker.INFERRED_TYPE, valuesType);
                newMCE.setSafe(true);
                nextValue = newMCE;
            } else {
                PropertyExpression oldPE = (PropertyExpression) origin;
                PropertyExpression newPE = origin instanceof AttributeExpression
                    ? new AttributeExpression(varX(element), oldPE.getProperty(), true)
                    : new  PropertyExpression(varX(element), oldPE.getProperty(), true);
                newPE.setImplicitThis(false);
                newPE.setNodeMetaData(StaticTypesMarker.INFERRED_TYPE, valuesType);
                nextValue = newPE;
            }

            MethodCallExpression addNextValue = callX(result, "add", /*castX(valuesType, */nextValue/*)*/);
            addNextValue.setImplicitThis(false);
            addNextValue.setMethodTarget(StaticCompilationVisitor.ARRAYLIST_ADD_METHOD);

            // for (element in receiver) result.add(element?.method(arguments));
            var stmt = new ForStatement(
                    element,
                    tmpReceiver,
                    stmt(produceResultList ? addNextValue : nextValue)
            );
            stmt.visit(controller.getAcg());
            if (produceResultList) {
                result.remove(controller);
            }
            // end of if/else
            mv.visitLabel(allDone);

            if (tmpReceiver instanceof TemporaryVariableExpression) {
                ((TemporaryVariableExpression) tmpReceiver).remove(controller);
            }
        } else if (safe && origin instanceof MethodCallExpression) {
            CompileStack compileStack = controller.getCompileStack();
            OperandStack operandStack = controller.getOperandStack();
            MethodVisitor mv = controller.getMethodVisitor();
            int counter = labelCounter.incrementAndGet();
            // (receiver != null) ? receiver.name(args) : null
            Label ifnull = compileStack.createLocalLabel("ifnull_" + counter);
            Label nonull = compileStack.createLocalLabel("nonull_" + counter);
            Label theEnd = compileStack.createLocalLabel("ending_" + counter);
            var slot = new ExpressionAsVariableSlot(controller, receiver);
            slot.visit(controller.getAcg());
            operandStack.box();
            mv.visitJumpInsn(IFNULL, ifnull);
            operandStack.remove(1); // receiver consumed
            mv.visitLabel(nonull);
            var newMCE = (MethodCallExpression) origin.transformExpression((expression) -> expression);
            newMCE.setObjectExpression(new VariableSlotLoader(slot.getType(), slot.getIndex(), operandStack));
            newMCE.getObjectExpression().setSourcePosition(((MethodCallExpression) origin).getObjectExpression());
            newMCE.setSafe(false);
            int osl = operandStack.getStackLength();
            newMCE.visit(controller.getAcg());
            compileStack.removeVar(slot.getIndex());
            if (operandStack.getStackLength() > osl) {
                operandStack.box(); // non-void method
                mv.visitJumpInsn(GOTO, theEnd);
                mv.visitLabel(ifnull);
                mv.visitInsn(ACONST_NULL);
                mv.visitLabel(theEnd);
            } else {
                mv.visitLabel(ifnull);
            }
        } else {
            if (origin instanceof AttributeExpression && (adapter == AsmClassGenerator.getField || adapter == AsmClassGenerator.getGroovyObjectField)) {
                CallSiteWriter callSiteWriter = controller.getCallSiteWriter();
                String fieldName = ((AttributeExpression) origin).getPropertyAsString();
                if (fieldName != null && callSiteWriter instanceof StaticTypesCallSiteWriter) {
                    ClassNode receiverType = controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
                    if (((StaticTypesCallSiteWriter) callSiteWriter).makeGetField(receiver, receiverType, fieldName, safe, false)) {
                        return;
                    }
                }
            }
            super.makeCall(origin, receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
        }
    }