Refactor FunctionReferenceGenerationStrategy
Draw a clear distinction between the referenced function's parameters and the anonymous synthetic function's parameters (see the comment). This will be useful in supporting advanced callable reference features like KT-8834
This commit is contained in:
@@ -156,10 +156,9 @@ object CodegenUtil {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun constructFakeFunctionCall(project: Project, referencedFunction: FunctionDescriptor): KtCallExpression {
|
||||
val fakeFunctionCall = StringBuilder("callableReferenceFakeCall(")
|
||||
fakeFunctionCall.append(referencedFunction.valueParameters.joinToString(", ") { "p${it.index}" })
|
||||
fakeFunctionCall.append(")")
|
||||
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall.toString()) as KtCallExpression
|
||||
fun constructFakeFunctionCall(project: Project, arity: Int): KtCallExpression {
|
||||
val fakeFunctionCall =
|
||||
(1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" }
|
||||
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression
|
||||
}
|
||||
}
|
||||
|
||||
+33
-25
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
|
||||
@@ -40,8 +37,24 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
|
||||
|
||||
/*
|
||||
* Notice the difference between two function descriptors in this class.
|
||||
* - [referencedFunction] is the function declaration which is referenced by the "::" expression. This is a real function present in code.
|
||||
* - [functionDescriptor] is a synthetically created function which has the same signature as the "invoke" of the generated callable
|
||||
* reference subclass. Its parameters include dispatch/extension receiver parameters of the referenced function, and those value
|
||||
* parameters of the referenced function which are required by the expected function type where the callable reference is passed to.
|
||||
* In simple cases, these value parameters are all of the referenced function's value parameters. But in cases when the referenced
|
||||
* function has parameters with default values, or a vararg parameter, functionDescriptor can take fewer parameters than
|
||||
* referencedFunction if the expected function type takes fewer parameters as well. For example:
|
||||
*
|
||||
* fun foo(a: A, b: B = ..., c: C = ..., vararg d: D) {}
|
||||
*
|
||||
* fun bar(f: (A, B) -> Unit) {}
|
||||
*
|
||||
* // referencedFunction: foo(A, B, C, vararg D)
|
||||
* // functionDescriptor: invoke(A, B)
|
||||
* bar(::foo)
|
||||
*/
|
||||
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased {
|
||||
private final ResolvedCall<?> resolvedCall;
|
||||
private final FunctionDescriptor referencedFunction;
|
||||
@@ -80,20 +93,21 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
||||
every argument boils down to calling LOAD with the corresponding index
|
||||
*/
|
||||
|
||||
KtCallExpression fakeExpression = CodegenUtil.constructFakeFunctionCall(state.getProject(), referencedFunction);
|
||||
int receivers = CallableReferenceUtilKt.computeExpectedNumberOfReceivers(referencedFunction, receiverType != null);
|
||||
KtCallExpression fakeExpression =
|
||||
CodegenUtil.constructFakeFunctionCall(state.getProject(), functionDescriptor.getValueParameters().size() - receivers);
|
||||
List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
|
||||
|
||||
ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
|
||||
ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
|
||||
computeAndSaveArguments(fakeArguments, codegen);
|
||||
computeAndSaveArguments(fakeArguments, codegen, receivers);
|
||||
|
||||
ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
|
||||
|
||||
private final Map<ValueParameterDescriptor, ResolvedValueArgument> argumentMap;
|
||||
private final Map<ValueParameterDescriptor, ResolvedValueArgument> argumentMap = new LinkedHashMap<>();
|
||||
{
|
||||
argumentMap = new LinkedHashMap<>(fakeArguments.size());
|
||||
int index = 0;
|
||||
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
|
||||
List<ValueParameterDescriptor> parameters = referencedFunction.getValueParameters();
|
||||
for (ValueArgument argument : fakeArguments) {
|
||||
argumentMap.put(parameters.get(index), new ExpressionValueArgument(argument));
|
||||
index++;
|
||||
@@ -146,20 +160,14 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
||||
v.areturn(returnType);
|
||||
}
|
||||
|
||||
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
|
||||
int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
|
||||
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) -
|
||||
(receiverType != null ? 1 : 0);
|
||||
|
||||
if (receivers < 0 && referencedFunction instanceof ConstructorDescriptor && isObject(referencedFunction.getContainingDeclaration().getContainingDeclaration())) {
|
||||
//reference to object nested class
|
||||
//TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class)
|
||||
receivers = 0;
|
||||
}
|
||||
|
||||
List<ValueParameterDescriptor> parameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers);
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
ValueParameterDescriptor parameter = parameters.get(i);
|
||||
private void computeAndSaveArguments(
|
||||
@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen, int receivers
|
||||
) {
|
||||
List<ValueParameterDescriptor> valueParameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers);
|
||||
assert valueParameters.size() == fakeArguments.size()
|
||||
: functionDescriptor + ": " + valueParameters.size() + " != " + fakeArguments.size();
|
||||
for (int i = 0; i < valueParameters.size(); i++) {
|
||||
ValueParameterDescriptor parameter = valueParameters.get(i);
|
||||
ValueArgument fakeArgument = fakeArguments.get(i);
|
||||
|
||||
Type type = state.getTypeMapper().mapType(parameter);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.coroutines.isSuspendLambda
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
||||
@@ -29,7 +30,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
|
||||
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
||||
@@ -77,7 +77,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||
descriptor.builtIns,
|
||||
Annotations.EMPTY,
|
||||
actualFunctionDescriptor.extensionReceiverParameter?.type,
|
||||
ExpressionTypingUtils.getValueParametersTypes(actualFunctionDescriptor.valueParameters),
|
||||
actualFunctionDescriptor.valueParameters.map { it.type },
|
||||
null,
|
||||
actualFunctionDescriptor.returnType!!
|
||||
)
|
||||
@@ -94,14 +94,20 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||
return listOf(lambda.defaultType, functionType)
|
||||
}
|
||||
|
||||
fun getSupertypesForFunctionReference(descriptor: FunctionDescriptor, isBound: Boolean): Collection<KotlinType> {
|
||||
fun getSupertypesForFunctionReference(
|
||||
referencedFunction: FunctionDescriptor,
|
||||
anonymousFunctionDescriptor: AnonymousFunctionDescriptor,
|
||||
isBound: Boolean
|
||||
): Collection<KotlinType> {
|
||||
val receivers = computeExpectedNumberOfReceivers(referencedFunction, isBound)
|
||||
|
||||
val functionType = createFunctionType(
|
||||
descriptor.builtIns,
|
||||
referencedFunction.builtIns,
|
||||
Annotations.EMPTY,
|
||||
if (isBound) null else descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type,
|
||||
ExpressionTypingUtils.getValueParametersTypes(descriptor.valueParameters),
|
||||
if (isBound) null else referencedFunction.extensionReceiverParameter?.type ?: referencedFunction.dispatchReceiverParameter?.type,
|
||||
anonymousFunctionDescriptor.valueParameters.drop(receivers).map { it.type },
|
||||
null,
|
||||
descriptor.returnType!!
|
||||
referencedFunction.returnType!!
|
||||
)
|
||||
|
||||
return listOf(functionReference.defaultType, functionType)
|
||||
|
||||
+4
-1
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||
@@ -341,7 +342,9 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
callableDescriptor = bindingContext.get(FUNCTION, expression);
|
||||
if (callableDescriptor == null) return;
|
||||
|
||||
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target, receiverType != null);
|
||||
supertypes = runtimeTypes.getSupertypesForFunctionReference(
|
||||
(FunctionDescriptor) target, (AnonymousFunctionDescriptor) callableDescriptor, receiverType != null
|
||||
);
|
||||
}
|
||||
else if (target instanceof PropertyDescriptor) {
|
||||
callableDescriptor = bindingContext.get(VARIABLE, expression);
|
||||
|
||||
@@ -18,7 +18,10 @@ package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -75,4 +78,19 @@ fun InstructionAdapter.generateClosureFieldsInitializationFromParameters(closure
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun computeExpectedNumberOfReceivers(referencedFunction: FunctionDescriptor, isBound: Boolean): Int {
|
||||
val receivers = (if (referencedFunction.dispatchReceiverParameter != null) 1 else 0) +
|
||||
(if (referencedFunction.extensionReceiverParameter != null) 1 else 0) -
|
||||
(if (isBound) 1 else 0)
|
||||
|
||||
if (receivers < 0 && referencedFunction is ConstructorDescriptor &&
|
||||
DescriptorUtils.isObject(referencedFunction.containingDeclaration.containingDeclaration)) {
|
||||
//reference to object nested class
|
||||
//TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class)
|
||||
return 0
|
||||
}
|
||||
|
||||
return receivers
|
||||
}
|
||||
|
||||
-12
@@ -44,9 +44,6 @@ import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.TYPE_INFERENCE_ERRORS;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.PROCESSED;
|
||||
|
||||
@@ -185,15 +182,6 @@ public class ExpressionTypingUtils {
|
||||
&& ((KtUnaryExpression) expression).getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<KotlinType> getValueParametersTypes(@NotNull List<ValueParameterDescriptor> valueParameters) {
|
||||
List<KotlinType> parameterTypes = new ArrayList<>(valueParameters.size());
|
||||
for (ValueParameterDescriptor parameter : valueParameters) {
|
||||
parameterTypes.add(parameter.getType());
|
||||
}
|
||||
return parameterTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The primary case for local extensions is the following:
|
||||
*
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ object CallableReferenceTranslator {
|
||||
receiver: JsExpression?
|
||||
): JsExpression {
|
||||
val realResolvedCall = expression.callableReference.getFunctionResolvedCallWithAssert(context.bindingContext())
|
||||
val fakeExpression = CodegenUtil.constructFakeFunctionCall(expression.project, descriptor)
|
||||
val fakeExpression = CodegenUtil.constructFakeFunctionCall(expression.project, descriptor.valueParameters.size)
|
||||
|
||||
val fakeCall = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeExpression.valueArguments)
|
||||
val fakeResolvedCall = object : DelegatingResolvedCall<FunctionDescriptor>(realResolvedCall) {
|
||||
|
||||
Reference in New Issue
Block a user