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);
}
}