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:
Dmitry Petrov
2020-03-19 14:03:01 +03:00
committed by Alexander Udalov
parent ddf7f53118
commit d1c5a42124
29 changed files with 478 additions and 183 deletions
@@ -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
}
}
}
@@ -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) {