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
|
@JvmStatic
|
||||||
fun constructFakeFunctionCall(project: Project, referencedFunction: FunctionDescriptor): KtCallExpression {
|
fun constructFakeFunctionCall(project: Project, arity: Int): KtCallExpression {
|
||||||
val fakeFunctionCall = StringBuilder("callableReferenceFakeCall(")
|
val fakeFunctionCall =
|
||||||
fakeFunctionCall.append(referencedFunction.valueParameters.joinToString(", ") { "p${it.index}" })
|
(1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" }
|
||||||
fakeFunctionCall.append(")")
|
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression
|
||||||
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall.toString()) as KtCallExpression
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-25
@@ -24,10 +24,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
|||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||||
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.util.CallMaker;
|
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
|
||||||
@@ -40,8 +37,24 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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 {
|
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased {
|
||||||
private final ResolvedCall<?> resolvedCall;
|
private final ResolvedCall<?> resolvedCall;
|
||||||
private final FunctionDescriptor referencedFunction;
|
private final FunctionDescriptor referencedFunction;
|
||||||
@@ -80,20 +93,21 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
|||||||
every argument boils down to calling LOAD with the corresponding index
|
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();
|
List<? extends ValueArgument> fakeArguments = fakeExpression.getValueArguments();
|
||||||
|
|
||||||
ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
|
ReceiverValue dispatchReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getDispatchReceiverParameter());
|
||||||
ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
|
ReceiverValue extensionReceiver = computeAndSaveReceiver(signature, codegen, referencedFunction.getExtensionReceiverParameter());
|
||||||
computeAndSaveArguments(fakeArguments, codegen);
|
computeAndSaveArguments(fakeArguments, codegen, receivers);
|
||||||
|
|
||||||
ResolvedCall<CallableDescriptor> fakeResolvedCall = new DelegatingResolvedCall<CallableDescriptor>(resolvedCall) {
|
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;
|
int index = 0;
|
||||||
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
|
List<ValueParameterDescriptor> parameters = referencedFunction.getValueParameters();
|
||||||
for (ValueArgument argument : fakeArguments) {
|
for (ValueArgument argument : fakeArguments) {
|
||||||
argumentMap.put(parameters.get(index), new ExpressionValueArgument(argument));
|
argumentMap.put(parameters.get(index), new ExpressionValueArgument(argument));
|
||||||
index++;
|
index++;
|
||||||
@@ -146,20 +160,14 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
|||||||
v.areturn(returnType);
|
v.areturn(returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
|
private void computeAndSaveArguments(
|
||||||
int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
|
@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen, int receivers
|
||||||
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) -
|
) {
|
||||||
(receiverType != null ? 1 : 0);
|
List<ValueParameterDescriptor> valueParameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers);
|
||||||
|
assert valueParameters.size() == fakeArguments.size()
|
||||||
if (receivers < 0 && referencedFunction instanceof ConstructorDescriptor && isObject(referencedFunction.getContainingDeclaration().getContainingDeclaration())) {
|
: functionDescriptor + ": " + valueParameters.size() + " != " + fakeArguments.size();
|
||||||
//reference to object nested class
|
for (int i = 0; i < valueParameters.size(); i++) {
|
||||||
//TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class)
|
ValueParameterDescriptor parameter = valueParameters.get(i);
|
||||||
receivers = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ValueParameterDescriptor> parameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers);
|
|
||||||
for (int i = 0; i < parameters.size(); i++) {
|
|
||||||
ValueParameterDescriptor parameter = parameters.get(i);
|
|
||||||
ValueArgument fakeArgument = fakeArguments.get(i);
|
ValueArgument fakeArgument = fakeArguments.get(i);
|
||||||
|
|
||||||
Type type = state.getTypeMapper().mapType(parameter);
|
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.coroutines.isSuspendLambda
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
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.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
|
||||||
|
|
||||||
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||||
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
||||||
@@ -77,7 +77,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
|||||||
descriptor.builtIns,
|
descriptor.builtIns,
|
||||||
Annotations.EMPTY,
|
Annotations.EMPTY,
|
||||||
actualFunctionDescriptor.extensionReceiverParameter?.type,
|
actualFunctionDescriptor.extensionReceiverParameter?.type,
|
||||||
ExpressionTypingUtils.getValueParametersTypes(actualFunctionDescriptor.valueParameters),
|
actualFunctionDescriptor.valueParameters.map { it.type },
|
||||||
null,
|
null,
|
||||||
actualFunctionDescriptor.returnType!!
|
actualFunctionDescriptor.returnType!!
|
||||||
)
|
)
|
||||||
@@ -94,14 +94,20 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
|||||||
return listOf(lambda.defaultType, functionType)
|
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(
|
val functionType = createFunctionType(
|
||||||
descriptor.builtIns,
|
referencedFunction.builtIns,
|
||||||
Annotations.EMPTY,
|
Annotations.EMPTY,
|
||||||
if (isBound) null else descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type,
|
if (isBound) null else referencedFunction.extensionReceiverParameter?.type ?: referencedFunction.dispatchReceiverParameter?.type,
|
||||||
ExpressionTypingUtils.getValueParametersTypes(descriptor.valueParameters),
|
anonymousFunctionDescriptor.valueParameters.drop(receivers).map { it.type },
|
||||||
null,
|
null,
|
||||||
descriptor.returnType!!
|
referencedFunction.returnType!!
|
||||||
)
|
)
|
||||||
|
|
||||||
return listOf(functionReference.defaultType, functionType)
|
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.coroutines.CoroutineUtilKt;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
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.LocalVariableDescriptor;
|
||||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||||
@@ -341,7 +342,9 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
|||||||
callableDescriptor = bindingContext.get(FUNCTION, expression);
|
callableDescriptor = bindingContext.get(FUNCTION, expression);
|
||||||
if (callableDescriptor == null) return;
|
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) {
|
else if (target instanceof PropertyDescriptor) {
|
||||||
callableDescriptor = bindingContext.get(VARIABLE, expression);
|
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.codegen.binding.CalculatedClosure
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
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.psi.KtCallableReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -75,4 +78,19 @@ fun InstructionAdapter.generateClosureFieldsInitializationFromParameters(closure
|
|||||||
else ->
|
else ->
|
||||||
null
|
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.KotlinType;
|
||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
|
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.diagnostics.Errors.TYPE_INFERENCE_ERRORS;
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContext.PROCESSED;
|
import static org.jetbrains.kotlin.resolve.BindingContext.PROCESSED;
|
||||||
|
|
||||||
@@ -185,15 +182,6 @@ public class ExpressionTypingUtils {
|
|||||||
&& ((KtUnaryExpression) expression).getOperationReference().getReferencedNameElementType() == KtTokens.EXCLEXCL;
|
&& ((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:
|
* The primary case for local extensions is the following:
|
||||||
*
|
*
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@ object CallableReferenceTranslator {
|
|||||||
receiver: JsExpression?
|
receiver: JsExpression?
|
||||||
): JsExpression {
|
): JsExpression {
|
||||||
val realResolvedCall = expression.callableReference.getFunctionResolvedCallWithAssert(context.bindingContext())
|
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 fakeCall = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeExpression.valueArguments)
|
||||||
val fakeResolvedCall = object : DelegatingResolvedCall<FunctionDescriptor>(realResolvedCall) {
|
val fakeResolvedCall = object : DelegatingResolvedCall<FunctionDescriptor>(realResolvedCall) {
|
||||||
|
|||||||
Reference in New Issue
Block a user