in src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java [83:205]
public void writeMethodReferenceExpression(final MethodReferenceExpression methodReferenceExpression) {
// functional interface target is required for native method reference generation
ClassNode functionalType = methodReferenceExpression.getNodeMetaData(StaticTypesMarker.PARAMETER_TYPE);
MethodNode abstractMethod = ClassHelper.findSAM(functionalType);
if (abstractMethod == null || !functionalType.isInterface()) {
// generate the default bytecode -- most likely a method closure
super.writeMethodReferenceExpression(methodReferenceExpression);
return;
}
ClassNode classNode = controller.getClassNode();
Expression typeOrTargetRef = methodReferenceExpression.getExpression();
boolean isClassExpression = (typeOrTargetRef instanceof ClassExpression);
boolean targetIsArgument = false; // implied argument for expr::staticMethod?
ClassNode typeOrTargetRefType = isClassExpression ? typeOrTargetRef.getType()
: controller.getTypeChooser().resolveType(typeOrTargetRef, classNode);
if (ClassHelper.isPrimitiveType(typeOrTargetRefType)) // GROOVY-11353
typeOrTargetRefType = ClassHelper.getWrapper(typeOrTargetRefType);
ClassNode[] methodReferenceParamTypes = methodReferenceExpression.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
Parameter[] parametersWithExactType = createParametersWithExactType(abstractMethod, methodReferenceParamTypes);
String methodRefName = methodReferenceExpression.getMethodName().getText();
boolean isConstructorReference = isConstructorReference(methodRefName);
MethodNode methodRefMethod;
if (isConstructorReference) {
methodRefName = controller.getContext().getNextConstructorReferenceSyntheticMethodName(controller.getMethodNode());
methodRefMethod = addSyntheticMethodForConstructorReference(methodRefName, typeOrTargetRefType, parametersWithExactType);
} else {
// TODO: move the findMethodRefMethod and checking to StaticTypeCheckingVisitor
methodRefMethod = findMethodRefMethod(methodRefName, parametersWithExactType, typeOrTargetRef, typeOrTargetRefType);
if (methodReferenceExpression.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS) != null) { // GROOVY-11301, GROOVY-11365: access bridge indicated
Map<MethodNode,MethodNode> bridgeMethods = typeOrTargetRefType.redirect().getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS);
if (bridgeMethods != null) methodRefMethod = bridgeMethods.getOrDefault(methodRefMethod, methodRefMethod); // bridge may not have been generated
}
}
validate(methodReferenceExpression, typeOrTargetRefType, methodRefName, methodRefMethod, parametersWithExactType,
resolveClassNodeGenerics(extractPlaceholders(functionalType), null, abstractMethod.getReturnType()));
if (isBridgeMethod(methodRefMethod)) {
targetIsArgument = true; // GROOVY-11301, GROOVY-11365
if (isClassExpression) { // method expects an instance argument
methodRefMethod = addSyntheticMethodForDGSM(methodRefMethod);
}
} else if (isExtensionMethod(methodRefMethod)) {
ExtensionMethodNode extensionMethodNode = (ExtensionMethodNode) methodRefMethod;
methodRefMethod = extensionMethodNode.getExtensionMethodNode();
boolean isStatic = extensionMethodNode.isStaticExtension();
if (isStatic) { // create adapter method to pass extra argument
methodRefMethod = addSyntheticMethodForDGSM(methodRefMethod);
}
if (isStatic || isClassExpression) {
// replace expression with declaring type
typeOrTargetRefType = methodRefMethod.getDeclaringClass();
typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
} else { // GROOVY-10653
targetIsArgument = true; // ex: "string"::size
}
} else if (isVargs(methodRefMethod.getParameters())) {
int mParameters = abstractMethod.getParameters().length;
int nParameters = methodRefMethod.getParameters().length;
if (isTypeReferringInstanceMethod(typeOrTargetRef, methodRefMethod)) nParameters += 1;
if (mParameters > nParameters || mParameters == nParameters-1 || (mParameters == nParameters
&& !isAssignableTo(last(parametersWithExactType).getType(), last(methodRefMethod.getParameters()).getType()))) {
// GROOVY-9813: reference to variadic method which needs adapter method to match runtime arguments to its parameters
if (!isClassExpression && !methodRefMethod.isStatic() && !methodRefMethod.getDeclaringClass().equals(classNode)) {
targetIsArgument = true; // GROOVY-10653: create static adapter in source class with target as first parameter
mParameters += 1;
}
methodRefMethod = addSyntheticMethodForVariadicReference(methodRefMethod, mParameters, isClassExpression || targetIsArgument);
if (methodRefMethod.isStatic() && !targetIsArgument) {
// replace expression with declaring type
typeOrTargetRefType = methodRefMethod.getDeclaringClass();
typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
}
}
}
if (!isClassExpression) {
if (isConstructorReference) { // TODO: move this check to the parser
addFatalError("Constructor reference must be TypeName::new", methodReferenceExpression);
} else if (methodRefMethod.isStatic() && !targetIsArgument) {
// "string"::valueOf refers to static method, so instance is superfluous
typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
isClassExpression = true;
} else {
typeOrTargetRef.visit(controller.getAcg());
controller.getOperandStack().box(); // GROOVY-11353
}
}
int referenceKind;
if (isConstructorReference || methodRefMethod.isStatic()) {
referenceKind = Opcodes.H_INVOKESTATIC;
} else if (methodRefMethod.getDeclaringClass().isInterface()) {
referenceKind = Opcodes.H_INVOKEINTERFACE; // GROOVY-9853
} else {
referenceKind = Opcodes.H_INVOKEVIRTUAL;
}
String methodName = abstractMethod.getName();
String methodDesc = BytecodeHelper.getMethodDescriptor(functionalType.redirect(),
isClassExpression ? Parameter.EMPTY_ARRAY : new Parameter[]{new Parameter(typeOrTargetRefType, "__METHODREF_EXPR_INSTANCE")});
Handle bootstrapMethod = createBootstrapMethod(classNode.isInterface(), false);
Object[] bootstrapArgs = createBootstrapMethodArguments(
createMethodDescriptor(abstractMethod),
referenceKind,
methodRefMethod.getDeclaringClass(),
methodRefMethod,
parametersWithExactType,
false
);
controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, methodDesc, bootstrapMethod, bootstrapArgs);
if (isClassExpression) {
controller.getOperandStack().push(functionalType);
} else {
controller.getOperandStack().replace(functionalType, 1);
}
}