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