Support bound callable references in codegen
This commit is contained in:
@@ -241,7 +241,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen) {
|
||||
public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen, @Nullable final StackValue functionReferenceReceiver) {
|
||||
return StackValue.operation(
|
||||
functionReferenceTarget != null ? K_FUNCTION : asmType,
|
||||
new Function1<InstructionAdapter, Unit>() {
|
||||
@@ -254,7 +254,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
v.anew(asmType);
|
||||
v.dup();
|
||||
|
||||
codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator);
|
||||
codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator, functionReferenceReceiver);
|
||||
v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor(), false);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.intellij.util.containers.Stack;
|
||||
import kotlin.Pair;
|
||||
import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -1418,7 +1419,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
|
||||
|
||||
return genClosure(
|
||||
declaration, descriptor, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), samType, null
|
||||
declaration, descriptor, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), samType, null, null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1428,7 +1429,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull FunctionGenerationStrategy strategy,
|
||||
@Nullable SamType samType,
|
||||
@Nullable FunctionDescriptor functionReferenceTarget
|
||||
@Nullable FunctionDescriptor functionReferenceTarget,
|
||||
@Nullable StackValue functionReferenceReceiver
|
||||
) {
|
||||
ClassBuilder cv = state.getFactory().newVisitor(
|
||||
JvmDeclarationOriginKt.OtherOrigin(declaration, descriptor),
|
||||
@@ -1448,7 +1450,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages());
|
||||
}
|
||||
|
||||
return closureCodegen.putInstanceOnStack(this);
|
||||
return closureCodegen.putInstanceOnStack(this, functionReferenceReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1466,7 +1468,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
v.anew(type);
|
||||
v.dup();
|
||||
|
||||
pushClosureOnStack(classDescriptor, true, defaultCallGenerator);
|
||||
pushClosureOnStack(classDescriptor, true, defaultCallGenerator, /* functionReferenceReceiver = */ null);
|
||||
|
||||
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
|
||||
assert primaryConstructor != null : "There should be primary constructor for object literal";
|
||||
@@ -1510,7 +1512,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
});
|
||||
}
|
||||
|
||||
public void pushClosureOnStack(@NotNull ClassDescriptor classDescriptor, boolean putThis, @NotNull CallGenerator callGenerator) {
|
||||
public void pushClosureOnStack(
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
boolean putThis,
|
||||
@NotNull CallGenerator callGenerator,
|
||||
@Nullable StackValue functionReferenceReceiver
|
||||
) {
|
||||
CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
|
||||
if (closure == null) return;
|
||||
|
||||
@@ -1528,7 +1535,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
KotlinType captureReceiver = closure.getCaptureReceiverType();
|
||||
if (captureReceiver != null) {
|
||||
Type asmType = typeMapper.mapType(captureReceiver);
|
||||
StackValue.Local capturedReceiver = StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
|
||||
StackValue capturedReceiver =
|
||||
functionReferenceReceiver != null ? functionReferenceReceiver :
|
||||
StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
|
||||
callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
|
||||
}
|
||||
|
||||
@@ -1545,9 +1554,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassNotAny(classDescriptor);
|
||||
if (superClass != null) {
|
||||
pushClosureOnStack(
|
||||
superClass,
|
||||
putThis && closure.getCaptureThis() == null,
|
||||
callGenerator
|
||||
superClass, putThis && closure.getCaptureThis() == null, callGenerator, /* functionReferenceReceiver = */ null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2852,17 +2859,28 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
@Override
|
||||
public StackValue visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, StackValue data) {
|
||||
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression.getCallableReference(), bindingContext);
|
||||
|
||||
KotlinType receiverExpressionType = expressionJetType(expression.getReceiverExpression());
|
||||
Type receiverAsmType = receiverExpressionType != null ? asmType(receiverExpressionType) : null;
|
||||
StackValue receiverValue = receiverExpressionType != null ? gen(expression.getReceiverExpression()) : null;
|
||||
|
||||
FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
|
||||
if (functionDescriptor != null) {
|
||||
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
|
||||
return genClosure(expression, functionDescriptor, strategy, null, (FunctionDescriptor) resolvedCall.getResultingDescriptor());
|
||||
FunctionReferenceGenerationStrategy strategy =
|
||||
new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall, receiverAsmType, null);
|
||||
|
||||
return genClosure(
|
||||
expression, functionDescriptor, strategy, null,
|
||||
(FunctionDescriptor) resolvedCall.getResultingDescriptor(), receiverValue
|
||||
);
|
||||
}
|
||||
|
||||
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
|
||||
if (variableDescriptor != null) {
|
||||
return generatePropertyReference(expression, variableDescriptor,
|
||||
(VariableDescriptor) resolvedCall.getResultingDescriptor(),
|
||||
resolvedCall.getDispatchReceiver());
|
||||
return generatePropertyReference(
|
||||
expression, variableDescriptor, (VariableDescriptor) resolvedCall.getResultingDescriptor(),
|
||||
resolvedCall.getDispatchReceiver(), receiverAsmType, receiverValue
|
||||
);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText());
|
||||
@@ -2873,7 +2891,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
@NotNull KtElement element,
|
||||
@NotNull VariableDescriptor variableDescriptor,
|
||||
@NotNull VariableDescriptor target,
|
||||
@Nullable ReceiverValue dispatchReceiver
|
||||
@Nullable ReceiverValue dispatchReceiver,
|
||||
@Nullable final Type receiverAsmType,
|
||||
@Nullable final StackValue receiverValue
|
||||
) {
|
||||
ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
|
||||
|
||||
@@ -2885,11 +2905,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
|
||||
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
|
||||
element, classBuilder, target, dispatchReceiver
|
||||
element, classBuilder, target, dispatchReceiver, receiverAsmType
|
||||
);
|
||||
codegen.generate();
|
||||
|
||||
return codegen.putInstanceOnStack();
|
||||
return codegen.putInstanceOnStack(receiverValue == null ? null : new Function0<Unit>() {
|
||||
@Override
|
||||
public Unit invoke() {
|
||||
assert receiverAsmType != null : "Receiver type should not be null when receiver value is not null: " + receiverValue;
|
||||
receiverValue.put(receiverAsmType, v);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -3524,8 +3551,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
@NotNull StackValue metadataVar
|
||||
) {
|
||||
//noinspection ConstantConditions
|
||||
StackValue value =
|
||||
generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null);
|
||||
StackValue value = generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null, null, null);
|
||||
value.put(K_PROPERTY0_TYPE, v);
|
||||
metadataVar.storeSelector(K_PROPERTY0_TYPE, v);
|
||||
}
|
||||
@@ -3570,7 +3596,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
// Resolved call to local class constructor doesn't have dispatchReceiver, so we need to generate closure on stack
|
||||
// See StackValue.receiver for more info
|
||||
pushClosureOnStack(containingDeclaration, dispatchReceiver == null, defaultCallGenerator);
|
||||
pushClosureOnStack(
|
||||
containingDeclaration, dispatchReceiver == null, defaultCallGenerator, /* functionReferenceReceiver = */ null
|
||||
);
|
||||
|
||||
constructor = SamCodegenUtil.resolveSamAdapter(constructor);
|
||||
CallableMethod method = typeMapper.mapToCallableMethod(constructor, false);
|
||||
|
||||
+24
-6
@@ -39,15 +39,24 @@ import java.util.*;
|
||||
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
|
||||
private final ResolvedCall<?> resolvedCall;
|
||||
private final FunctionDescriptor referencedFunction;
|
||||
private final Type receiverType; // non-null for bound references
|
||||
private final StackValue receiverValue;
|
||||
|
||||
public FunctionReferenceGenerationStrategy(
|
||||
@NotNull GenerationState state,
|
||||
@NotNull FunctionDescriptor functionDescriptor,
|
||||
@NotNull ResolvedCall<?> resolvedCall
|
||||
@NotNull ResolvedCall<?> resolvedCall,
|
||||
@Nullable Type receiverType,
|
||||
@Nullable StackValue receiverValue
|
||||
) {
|
||||
super(state, functionDescriptor);
|
||||
this.resolvedCall = resolvedCall;
|
||||
this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
|
||||
this.receiverType = receiverType;
|
||||
this.receiverValue = receiverValue;
|
||||
assert receiverType != null || receiverValue == null
|
||||
: "A receiver value is provided for unbound function reference. Either this is a bound reference and you forgot " +
|
||||
"to pass receiverType, or you accidentally passed some receiverValue for a reference without receiver";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,7 +151,8 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
||||
|
||||
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
|
||||
int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
|
||||
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0);
|
||||
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) -
|
||||
(receiverType != null ? 1 : 0);
|
||||
|
||||
List<ValueParameterDescriptor> parameters = CollectionsKt.drop(callableDescriptor.getValueParameters(), receivers);
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
@@ -163,14 +173,22 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
||||
) {
|
||||
if (receiver == null) return null;
|
||||
|
||||
KtExpression receiverExpression = KtPsiFactoryKt
|
||||
.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
|
||||
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
|
||||
KtExpression receiverExpression = KtPsiFactoryKt.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
|
||||
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature, codegen));
|
||||
return ExpressionReceiver.Companion.create(receiverExpression, receiver.getType(), BindingContext.EMPTY);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
|
||||
private StackValue receiverParameterStackValue(@NotNull JvmMethodSignature signature, @NotNull ExpressionCodegen codegen) {
|
||||
if (receiverValue != null) return receiverValue;
|
||||
|
||||
if (receiverType != null) {
|
||||
return StackValue.field(
|
||||
receiverType, Type.getObjectType(codegen.getParentCodegen().getClassName()), AsmUtil.CAPTURED_RECEIVER_FIELD,
|
||||
/* isStatic = */ false, StackValue.LOCAL_0
|
||||
);
|
||||
}
|
||||
|
||||
// 0 is this (the callable reference class), 1 is the invoke() method's first parameter
|
||||
return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class JvmRuntimeTypes {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<KotlinType> getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor) {
|
||||
public Collection<KotlinType> getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor, boolean isBound) {
|
||||
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
|
||||
ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
|
||||
|
||||
@@ -106,7 +106,7 @@ public class JvmRuntimeTypes {
|
||||
KotlinType functionType = FunctionTypeResolveUtilsKt.createFunctionType(
|
||||
DescriptorUtilsKt.getBuiltIns(descriptor),
|
||||
Annotations.Companion.getEMPTY(),
|
||||
receiverType,
|
||||
isBound ? null : receiverType,
|
||||
ExpressionTypingUtils.getValueParametersTypes(descriptor.getValueParameters()),
|
||||
descriptor.getReturnType()
|
||||
);
|
||||
@@ -115,13 +115,14 @@ public class JvmRuntimeTypes {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor) {
|
||||
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor, boolean isBound) {
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType();
|
||||
}
|
||||
|
||||
int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) +
|
||||
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0);
|
||||
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0) -
|
||||
(isBound ? 1 : 0);
|
||||
return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.method
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
@@ -46,7 +47,8 @@ class PropertyReferenceCodegen(
|
||||
expression: KtElement,
|
||||
classBuilder: ClassBuilder,
|
||||
private val target: VariableDescriptor,
|
||||
dispatchReceiver: ReceiverValue?
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
private val receiverType: Type? // non-null for bound references
|
||||
) : MemberCodegen<KtElement>(state, parentCodegen, context, expression, classBuilder) {
|
||||
private val classDescriptor = context.contextDescriptor
|
||||
private val asmType = typeMapper.mapClass(classDescriptor)
|
||||
@@ -55,7 +57,9 @@ class PropertyReferenceCodegen(
|
||||
private val extensionReceiverType = target.extensionReceiverParameter?.type
|
||||
|
||||
private val receiverCount =
|
||||
(if (dispatchReceiverType != null) 1 else 0) + (if (extensionReceiverType != null) 1 else 0)
|
||||
(if (dispatchReceiverType != null) 1 else 0) +
|
||||
(if (extensionReceiverType != null) 1 else 0) -
|
||||
(if (receiverType != null) 1 else 0)
|
||||
|
||||
// e.g. MutablePropertyReference0
|
||||
private val superAsmType = typeMapper.mapClass(classDescriptor.getSuperClassNotAny().sure { "No super class for $classDescriptor" })
|
||||
@@ -63,6 +67,18 @@ class PropertyReferenceCodegen(
|
||||
// e.g. mutableProperty0(Lkotlin/jvm/internal/MutablePropertyReference0;)Lkotlin/reflect/KMutableProperty0;
|
||||
private val wrapperMethod = getWrapperMethodForPropertyReference(target, receiverCount)
|
||||
|
||||
private val closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor)!!.apply {
|
||||
assert((captureReceiverType != null) == (receiverType != null)) {
|
||||
"Bound property reference can only be generated with the type of the receiver. " +
|
||||
"Captured type = $captureReceiverType, actual type = $receiverType"
|
||||
}
|
||||
}
|
||||
|
||||
private val constructorArgs = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, asmType).apply {
|
||||
assert(size <= 1) { "Bound property reference should capture only one value: $this" }
|
||||
}
|
||||
private val constructor = method("<init>", Type.VOID_TYPE, *constructorArgs.map { it.fieldType }.toTypedArray())
|
||||
|
||||
override fun generateDeclaration() {
|
||||
v.defineClass(
|
||||
element,
|
||||
@@ -79,12 +95,14 @@ class PropertyReferenceCodegen(
|
||||
|
||||
// TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
|
||||
override fun generateBody() {
|
||||
generateConstInstance(asmType, wrapperMethod.returnType)
|
||||
|
||||
generateMethod("property reference init", 0, method("<init>", Type.VOID_TYPE)) {
|
||||
load(0, OBJECT_TYPE)
|
||||
invokespecial(superAsmType.internalName, "<init>", "()V", false)
|
||||
if (JvmCodegenUtil.isConst(closure)) {
|
||||
generateConstInstance(asmType, wrapperMethod.returnType)
|
||||
}
|
||||
else {
|
||||
AsmUtil.genClosureFields(closure, v, typeMapper)
|
||||
}
|
||||
|
||||
generateConstructor()
|
||||
|
||||
generateMethod("property reference getName", ACC_PUBLIC, method("getName", JAVA_STRING_TYPE)) {
|
||||
aconst(target.name.asString())
|
||||
@@ -102,6 +120,18 @@ class PropertyReferenceCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateConstructor() {
|
||||
generateMethod("property reference init", 0, constructor) {
|
||||
constructorArgs.fold(1) {
|
||||
i, fieldInfo ->
|
||||
AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, i, this)
|
||||
}
|
||||
|
||||
load(0, OBJECT_TYPE)
|
||||
invokespecial(superAsmType.internalName, "<init>", "()V", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateAccessors() {
|
||||
fun generateAccessor(method: Method, accessorBody: InstructionAdapter.(StackValue) -> Unit) {
|
||||
generateMethod("property reference $method", ACC_PUBLIC, method) {
|
||||
@@ -123,8 +153,14 @@ class PropertyReferenceCodegen(
|
||||
StackValue.singleton(containingObject, typeMapper).put(typeMapper.mapClass(containingObject), this)
|
||||
}
|
||||
|
||||
for ((index, type) in listOfNotNull(dispatchReceiverType, extensionReceiverType).withIndex()) {
|
||||
StackValue.local(index + 1, OBJECT_TYPE).put(typeMapper.mapType(type), this)
|
||||
if (receiverType != null) {
|
||||
StackValue.field(receiverType, asmType, AsmUtil.CAPTURED_RECEIVER_FIELD, /* isStatic = */ false, StackValue.LOCAL_0)
|
||||
.put(receiverType, this)
|
||||
}
|
||||
else {
|
||||
for ((index, type) in listOfNotNull(dispatchReceiverType, extensionReceiverType).withIndex()) {
|
||||
StackValue.local(index + 1, OBJECT_TYPE).put(typeMapper.mapType(type), this)
|
||||
}
|
||||
}
|
||||
|
||||
val value = if (target is LocalVariableDescriptor) {
|
||||
@@ -166,10 +202,21 @@ class PropertyReferenceCodegen(
|
||||
writeSyntheticClassMetadata(v)
|
||||
}
|
||||
|
||||
fun putInstanceOnStack(): StackValue =
|
||||
StackValue.operation(wrapperMethod.returnType) { iv ->
|
||||
fun putInstanceOnStack(receiverValue: (() -> Unit)?): StackValue {
|
||||
return StackValue.operation(wrapperMethod.returnType) { iv ->
|
||||
if (JvmCodegenUtil.isConst(closure)) {
|
||||
assert(receiverValue == null) { "No receiver expected for unbound property reference: $classDescriptor" }
|
||||
iv.getstatic(asmType.internalName, JvmAbi.INSTANCE_FIELD, wrapperMethod.returnType.descriptor)
|
||||
}
|
||||
else {
|
||||
assert(receiverValue != null) { "Receiver expected for bound property reference: $classDescriptor" }
|
||||
iv.anew(asmType)
|
||||
iv.dup()
|
||||
receiverValue!!()
|
||||
iv.invokespecial(asmType.internalName, "<init>", constructor.descriptor, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
|
||||
+18
-7
@@ -288,17 +288,22 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
CallableDescriptor callableDescriptor;
|
||||
Collection<KotlinType> supertypes;
|
||||
|
||||
KtExpression receiverExpression = expression.getReceiverExpression();
|
||||
KotlinType receiverType = receiverExpression != null ? bindingContext.getType(receiverExpression) : null;
|
||||
|
||||
if (target instanceof FunctionDescriptor) {
|
||||
callableDescriptor = bindingContext.get(FUNCTION, expression);
|
||||
if (callableDescriptor == null) return;
|
||||
|
||||
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target);
|
||||
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target, receiverType != null);
|
||||
}
|
||||
else if (target instanceof PropertyDescriptor) {
|
||||
callableDescriptor = bindingContext.get(VARIABLE, expression);
|
||||
if (callableDescriptor == null) return;
|
||||
|
||||
supertypes = Collections.singleton(runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target));
|
||||
supertypes = Collections.singleton(
|
||||
runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target, receiverType != null)
|
||||
);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
@@ -306,7 +311,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
|
||||
String name = inventAnonymousClassName();
|
||||
ClassDescriptor classDescriptor = recordClassForCallable(expression, callableDescriptor, supertypes, name);
|
||||
recordClosure(classDescriptor, name);
|
||||
MutableClosure closure = recordClosure(classDescriptor, name);
|
||||
|
||||
if (receiverType != null) {
|
||||
closure.setCaptureReceiverType(receiverType);
|
||||
}
|
||||
|
||||
classStack.push(classDescriptor);
|
||||
nameStack.push(name);
|
||||
@@ -315,9 +324,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
classStack.pop();
|
||||
}
|
||||
|
||||
private void recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
||||
CodegenBinding.recordClosure(bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name),
|
||||
fileClassesProvider);
|
||||
@NotNull
|
||||
private MutableClosure recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
||||
return CodegenBinding.recordClosure(
|
||||
bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name), fileClassesProvider
|
||||
);
|
||||
}
|
||||
|
||||
private void recordLocalVariablePropertyMetadata(LocalVariableDescriptor variableDescriptor) {
|
||||
@@ -369,7 +380,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) {
|
||||
VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor;
|
||||
String name = inventAnonymousClassName();
|
||||
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor);
|
||||
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, /* bound = */ false);
|
||||
ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name);
|
||||
recordClosure(classDescriptor, name);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,8 @@ public class CodegenBinding {
|
||||
return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
|
||||
}
|
||||
|
||||
static void recordClosure(
|
||||
@NotNull
|
||||
static MutableClosure recordClosure(
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
@Nullable ClassDescriptor enclosing,
|
||||
@@ -164,6 +165,8 @@ public class CodegenBinding {
|
||||
if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
|
||||
recordInnerClass(trace, enclosing, classDescriptor);
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
private static void recordInnerClass(
|
||||
|
||||
@@ -33,11 +33,12 @@ public final class MutableClosure implements CalculatedClosure {
|
||||
private final CallableDescriptor enclosingFunWithReceiverDescriptor;
|
||||
|
||||
private boolean captureThis;
|
||||
private boolean captureReceiver;
|
||||
private boolean captureEnclosingReceiver;
|
||||
|
||||
private Map<DeclarationDescriptor, EnclosedValueDescriptor> captureVariables;
|
||||
private Map<DeclarationDescriptor, Integer> parameterOffsetInConstructor;
|
||||
private List<Pair<String, Type>> recordedFields;
|
||||
private KotlinType captureReceiverType;
|
||||
|
||||
MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) {
|
||||
this.enclosingClass = enclosingClass;
|
||||
@@ -72,7 +73,11 @@ public final class MutableClosure implements CalculatedClosure {
|
||||
|
||||
@Override
|
||||
public KotlinType getCaptureReceiverType() {
|
||||
if (captureReceiver) {
|
||||
if (captureReceiverType != null) {
|
||||
return captureReceiverType;
|
||||
}
|
||||
|
||||
if (captureEnclosingReceiver) {
|
||||
ReceiverParameterDescriptor parameter = getEnclosingReceiverDescriptor();
|
||||
assert parameter != null : "Receiver parameter should exist in " + enclosingFunWithReceiverDescriptor;
|
||||
return parameter.getType();
|
||||
@@ -85,7 +90,7 @@ public final class MutableClosure implements CalculatedClosure {
|
||||
if (enclosingFunWithReceiverDescriptor == null) {
|
||||
throw new IllegalStateException("Extension receiver parameter should exist");
|
||||
}
|
||||
this.captureReceiver = true;
|
||||
this.captureEnclosingReceiver = true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -94,6 +99,10 @@ public final class MutableClosure implements CalculatedClosure {
|
||||
return captureVariables != null ? captureVariables : Collections.<DeclarationDescriptor, EnclosedValueDescriptor>emptyMap();
|
||||
}
|
||||
|
||||
public void setCaptureReceiverType(@NotNull KotlinType type) {
|
||||
this.captureReceiverType = type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Pair<String, Type>> getRecordedFields() {
|
||||
|
||||
@@ -490,16 +490,26 @@ public class InlineCodegen extends CallGenerator {
|
||||
: state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName()
|
||||
);
|
||||
|
||||
FunctionGenerationStrategy strategy =
|
||||
expression instanceof KtCallableReferenceExpression ?
|
||||
new FunctionReferenceGenerationStrategy(
|
||||
state,
|
||||
descriptor,
|
||||
CallUtilKt.getResolvedCallWithAssert(
|
||||
((KtCallableReferenceExpression) expression).getCallableReference(), codegen.getBindingContext()
|
||||
)
|
||||
) :
|
||||
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression);
|
||||
FunctionGenerationStrategy strategy;
|
||||
if (expression instanceof KtCallableReferenceExpression) {
|
||||
KtCallableReferenceExpression callableReferenceExpression = (KtCallableReferenceExpression) expression;
|
||||
KtExpression receiverExpression = callableReferenceExpression.getReceiverExpression();
|
||||
StackValue receiverValue =
|
||||
receiverExpression != null && codegen.getBindingContext().getType(receiverExpression) != null
|
||||
? codegen.gen(receiverExpression)
|
||||
: null;
|
||||
|
||||
strategy = new FunctionReferenceGenerationStrategy(
|
||||
state,
|
||||
descriptor,
|
||||
CallUtilKt.getResolvedCallWithAssert(callableReferenceExpression.getCallableReference(), codegen.getBindingContext()),
|
||||
receiverValue != null ? receiverValue.type : null,
|
||||
receiverValue
|
||||
);
|
||||
}
|
||||
else {
|
||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression);
|
||||
}
|
||||
|
||||
FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen);
|
||||
|
||||
@@ -735,7 +745,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
private void putClosureParametersOnStack() {
|
||||
for (LambdaInfo next : expressionMap.values()) {
|
||||
activeLambda = next;
|
||||
codegen.pushClosureOnStack(next.getClassDescriptor(), true, this);
|
||||
codegen.pushClosureOnStack(next.getClassDescriptor(), true, this, /* functionReferenceReceiver = */ null);
|
||||
}
|
||||
activeLambda = null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
inline fun foo(x: () -> String) = x()
|
||||
|
||||
fun String.id() = this
|
||||
|
||||
fun box() = foo("OK"::id)
|
||||
@@ -0,0 +1,4 @@
|
||||
fun box(): String {
|
||||
val f = "KOTLIN"::get
|
||||
return "${f(1)}${f(0)}"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun box(): String {
|
||||
val f = "kotlin"::length
|
||||
val result = f.get()
|
||||
return if (result == 6) "OK" else "Fail: $result"
|
||||
}
|
||||
@@ -1438,6 +1438,42 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/callableReference/bound")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Bound extends AbstractBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInBound() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleFunction.kt")
|
||||
public void testSimpleFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleProperty.kt")
|
||||
public void testSimpleProperty() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleProperty.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Inline extends AbstractBlackBoxCodegenTest {
|
||||
public void testAllFilesPresentInInline() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound/inline"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/inline/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/callableReference/function")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user