KT-36024 Generate adapted callable references as lambdas
Make sure both JVM and JVM_IR use the same information to determine whether a callable reference requires argument adaptation.
This commit is contained in:
committed by
Alexander Udalov
parent
ddf7f53118
commit
d1c5a42124
@@ -74,7 +74,9 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
protected final Type asmType;
|
||||
protected final int visibilityFlag;
|
||||
private final boolean shouldHaveBoundReferenceReceiver;
|
||||
private final boolean isRegularFunctionReference;
|
||||
private final boolean isOptimizedFunctionReference;
|
||||
private final boolean isAdaptedFunctionReference;
|
||||
|
||||
private Method constructor;
|
||||
protected Type superClassAsmType;
|
||||
@@ -125,9 +127,17 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
assert closure != null : "Closure must be calculated for class: " + classDescriptor;
|
||||
|
||||
this.shouldHaveBoundReferenceReceiver = CallableReferenceUtilKt.isForBoundCallableReference(closure);
|
||||
|
||||
ClassifierDescriptor superClassDescriptor = superClassType.getConstructor().getDeclarationDescriptor();
|
||||
this.isRegularFunctionReference =
|
||||
functionReferenceTarget != null &&
|
||||
superClassDescriptor == state.getJvmRuntimeTypes().getFunctionReference();
|
||||
this.isOptimizedFunctionReference =
|
||||
functionReferenceTarget != null &&
|
||||
superClassType.getConstructor().getDeclarationDescriptor() == state.getJvmRuntimeTypes().getFunctionReferenceImpl();
|
||||
superClassDescriptor == state.getJvmRuntimeTypes().getFunctionReferenceImpl();
|
||||
this.isAdaptedFunctionReference =
|
||||
functionReferenceTarget != null &&
|
||||
superClassDescriptor == state.getJvmRuntimeTypes().getLambda();
|
||||
|
||||
this.asmType = typeMapper.mapClass(classDescriptor);
|
||||
|
||||
@@ -189,15 +199,29 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
protected void generateClosureBody() {
|
||||
functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), funDescriptor, strategy);
|
||||
|
||||
if (functionReferenceTarget != null && !isOptimizedFunctionReference) {
|
||||
if (isRegularFunctionReference) {
|
||||
generateFunctionReferenceMethods(functionReferenceTarget);
|
||||
}
|
||||
|
||||
if (shouldHaveBoundReferenceReceiver && isAdaptedFunctionReference) {
|
||||
generateBoundAdaptedCallableReferenceReceiverField();
|
||||
}
|
||||
|
||||
functionCodegen.generateDefaultIfNeeded(
|
||||
context.intoFunction(funDescriptor), funDescriptor, context.getContextKind(), DefaultParameterValueLoader.DEFAULT, null
|
||||
);
|
||||
}
|
||||
|
||||
private void generateBoundAdaptedCallableReferenceReceiverField() {
|
||||
v.newField(
|
||||
JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor),
|
||||
ACC_PRIVATE,
|
||||
BOUND_REFERENCE_RECEIVER,
|
||||
OBJECT_TYPE.getDescriptor(),
|
||||
null, null
|
||||
);
|
||||
}
|
||||
|
||||
protected void generateBridges() {
|
||||
FunctionDescriptor erasedInterfaceFunction;
|
||||
if (samType == null) {
|
||||
@@ -500,11 +524,12 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
List<Type> superCtorArgTypes = new ArrayList<>();
|
||||
if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE) ||
|
||||
superClassAsmType.equals(FUNCTION_REFERENCE_IMPL) ||
|
||||
CoroutineCodegenUtilKt.isCoroutineSuperClass(state.getLanguageVersionSettings(), superClassAsmType.getInternalName())) {
|
||||
CoroutineCodegenUtilKt.isCoroutineSuperClass(state.getLanguageVersionSettings(), superClassAsmType.getInternalName())
|
||||
) {
|
||||
int arity = calculateArity();
|
||||
iv.iconst(arity);
|
||||
superCtorArgTypes.add(Type.INT_TYPE);
|
||||
if (shouldHaveBoundReferenceReceiver) {
|
||||
if (shouldHaveBoundReferenceReceiver && !isAdaptedFunctionReference) {
|
||||
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(
|
||||
iv, boundReceiverParameterIndex, boundReceiverType, boundReceiverKotlinType
|
||||
);
|
||||
@@ -533,6 +558,19 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, superCtorArgTypes.toArray(new Type[0])), false
|
||||
);
|
||||
|
||||
// Bound adapted function references store receiver in a separate field.
|
||||
if (shouldHaveBoundReferenceReceiver && isAdaptedFunctionReference) {
|
||||
iv.load(0, superClassAsmType);
|
||||
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(
|
||||
iv, boundReceiverParameterIndex, boundReceiverType, boundReceiverKotlinType
|
||||
);
|
||||
iv.putfield(
|
||||
asmType.getInternalName(),
|
||||
BOUND_REFERENCE_RECEIVER,
|
||||
OBJECT_TYPE.getDescriptor()
|
||||
);
|
||||
}
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
|
||||
FunctionCodegen.endVisit(iv, "constructor", element);
|
||||
|
||||
@@ -43,9 +43,8 @@ class JvmRuntimeTypes(
|
||||
private fun propertyClasses(prefix: String, suffix: String): Lazy<List<ClassDescriptor>> =
|
||||
lazy { (0..2).map { i -> createClass(kotlinJvmInternalPackage, prefix + i + suffix) } }
|
||||
|
||||
private val lambda: ClassDescriptor by internal("Lambda")
|
||||
|
||||
private val functionReference: ClassDescriptor by internal("FunctionReference")
|
||||
val lambda: ClassDescriptor by internal("Lambda")
|
||||
val functionReference: ClassDescriptor by internal("FunctionReference")
|
||||
val functionReferenceImpl: ClassDescriptor by internal("FunctionReferenceImpl")
|
||||
|
||||
private val localVariableReference: ClassDescriptor by internal("LocalVariableReference")
|
||||
@@ -135,7 +134,8 @@ class JvmRuntimeTypes(
|
||||
fun getSupertypesForFunctionReference(
|
||||
referencedFunction: FunctionDescriptor,
|
||||
anonymousFunctionDescriptor: AnonymousFunctionDescriptor,
|
||||
isBound: Boolean
|
||||
isBound: Boolean,
|
||||
isAdaptedCallableReference: Boolean
|
||||
): Collection<KotlinType> {
|
||||
val receivers = computeExpectedNumberOfReceivers(referencedFunction, isBound)
|
||||
|
||||
@@ -151,7 +151,11 @@ class JvmRuntimeTypes(
|
||||
)
|
||||
|
||||
val suspendFunctionType = if (referencedFunction.isSuspend) suspendFunctionInterface?.defaultType else null
|
||||
val superClass = if (generateOptimizedCallableReferenceSuperClasses) functionReferenceImpl else functionReference
|
||||
val superClass = when {
|
||||
isAdaptedCallableReference -> lambda
|
||||
generateOptimizedCallableReferenceSuperClasses -> functionReferenceImpl
|
||||
else -> functionReference
|
||||
}
|
||||
return listOfNotNull(superClass.defaultType, functionType, suspendFunctionType)
|
||||
}
|
||||
|
||||
@@ -172,4 +176,4 @@ class JvmRuntimeTypes(
|
||||
|
||||
return classes[arity].defaultType
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-1
@@ -16,6 +16,7 @@ import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes;
|
||||
import org.jetbrains.kotlin.cfg.WhenChecker;
|
||||
import org.jetbrains.kotlin.codegen.*;
|
||||
@@ -357,6 +358,28 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
classStack.pop();
|
||||
}
|
||||
|
||||
private boolean isAdaptedCallableReference(
|
||||
@NotNull KtCallableReferenceExpression expression,
|
||||
@NotNull ResolvedCall<?> resolvedCall
|
||||
) {
|
||||
CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
|
||||
if (!(resultingDescriptor instanceof FunctionDescriptor)) return false;
|
||||
FunctionDescriptor functionDescriptor = (FunctionDescriptor) resultingDescriptor;
|
||||
|
||||
// Callable reference is adapted if:
|
||||
// - adapter arguments mapping is present in value arguments of corresponding resolved call;
|
||||
// - return type is not Unit, and expected return type is Unit.
|
||||
|
||||
if (!resolvedCall.getValueArguments().isEmpty()) return true;
|
||||
|
||||
KotlinType callableReferenceType = bindingContext.getType(expression);
|
||||
assert callableReferenceType != null : "No type for callable reference: " + expression.getText();
|
||||
KotlinType callableReferenceReturnType = CollectionsKt.last(callableReferenceType.getArguments()).getType();
|
||||
KotlinType functionReturnType = functionDescriptor.getReturnType();
|
||||
assert functionReturnType != null : "No return type for function: " + functionDescriptor;
|
||||
return KotlinBuiltIns.isUnit(callableReferenceReturnType) && !KotlinBuiltIns.isUnit(functionReturnType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression) {
|
||||
ResolvedCall<?> referencedFunction = CallUtilKt.getResolvedCall(expression.getCallableReference(), bindingContext);
|
||||
@@ -380,7 +403,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
if (callableDescriptor == null) return;
|
||||
|
||||
supertypes = runtimeTypes.getSupertypesForFunctionReference(
|
||||
(FunctionDescriptor) target, (AnonymousFunctionDescriptor) callableDescriptor, receiverType != null
|
||||
(FunctionDescriptor) target, (AnonymousFunctionDescriptor) callableDescriptor, receiverType != null,
|
||||
isAdaptedCallableReference(expression, referencedFunction)
|
||||
);
|
||||
}
|
||||
else if (target instanceof PropertyDescriptor) {
|
||||
|
||||
Reference in New Issue
Block a user