Support bound callable references in codegen

This commit is contained in:
Alexander Udalov
2016-04-29 21:59:27 +03:00
parent 526a64dc36
commit b9e61f035f
13 changed files with 242 additions and 65 deletions
@@ -241,7 +241,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
} }
@NotNull @NotNull
public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen) { public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen, @Nullable final StackValue functionReferenceReceiver) {
return StackValue.operation( return StackValue.operation(
functionReferenceTarget != null ? K_FUNCTION : asmType, functionReferenceTarget != null ? K_FUNCTION : asmType,
new Function1<InstructionAdapter, Unit>() { new Function1<InstructionAdapter, Unit>() {
@@ -254,7 +254,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
v.anew(asmType); v.anew(asmType);
v.dup(); v.dup();
codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator); codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator, functionReferenceReceiver);
v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor(), false); v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor(), false);
} }
@@ -28,6 +28,7 @@ import com.intellij.util.containers.Stack;
import kotlin.Pair; import kotlin.Pair;
import kotlin.Unit; import kotlin.Unit;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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(); assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
return genClosure( 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 FunctionDescriptor descriptor,
@NotNull FunctionGenerationStrategy strategy, @NotNull FunctionGenerationStrategy strategy,
@Nullable SamType samType, @Nullable SamType samType,
@Nullable FunctionDescriptor functionReferenceTarget @Nullable FunctionDescriptor functionReferenceTarget,
@Nullable StackValue functionReferenceReceiver
) { ) {
ClassBuilder cv = state.getFactory().newVisitor( ClassBuilder cv = state.getFactory().newVisitor(
JvmDeclarationOriginKt.OtherOrigin(declaration, descriptor), JvmDeclarationOriginKt.OtherOrigin(declaration, descriptor),
@@ -1448,7 +1450,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages()); propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages());
} }
return closureCodegen.putInstanceOnStack(this); return closureCodegen.putInstanceOnStack(this, functionReferenceReceiver);
} }
@Override @Override
@@ -1466,7 +1468,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
v.anew(type); v.anew(type);
v.dup(); v.dup();
pushClosureOnStack(classDescriptor, true, defaultCallGenerator); pushClosureOnStack(classDescriptor, true, defaultCallGenerator, /* functionReferenceReceiver = */ null);
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
assert primaryConstructor != null : "There should be primary constructor for object literal"; 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); CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
if (closure == null) return; if (closure == null) return;
@@ -1528,7 +1535,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
KotlinType captureReceiver = closure.getCaptureReceiverType(); KotlinType captureReceiver = closure.getCaptureReceiverType();
if (captureReceiver != null) { if (captureReceiver != null) {
Type asmType = typeMapper.mapType(captureReceiver); 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++); callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
} }
@@ -1545,9 +1554,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassNotAny(classDescriptor); ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassNotAny(classDescriptor);
if (superClass != null) { if (superClass != null) {
pushClosureOnStack( pushClosureOnStack(
superClass, superClass, putThis && closure.getCaptureThis() == null, callGenerator, /* functionReferenceReceiver = */ null
putThis && closure.getCaptureThis() == null,
callGenerator
); );
} }
} }
@@ -2852,17 +2859,28 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Override @Override
public StackValue visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, StackValue data) { public StackValue visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, StackValue data) {
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression.getCallableReference(), bindingContext); 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); FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
if (functionDescriptor != null) { if (functionDescriptor != null) {
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall); FunctionReferenceGenerationStrategy strategy =
return genClosure(expression, functionDescriptor, strategy, null, (FunctionDescriptor) resolvedCall.getResultingDescriptor()); new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall, receiverAsmType, null);
return genClosure(
expression, functionDescriptor, strategy, null,
(FunctionDescriptor) resolvedCall.getResultingDescriptor(), receiverValue
);
} }
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression); VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor != null) { if (variableDescriptor != null) {
return generatePropertyReference(expression, variableDescriptor, return generatePropertyReference(
(VariableDescriptor) resolvedCall.getResultingDescriptor(), expression, variableDescriptor, (VariableDescriptor) resolvedCall.getResultingDescriptor(),
resolvedCall.getDispatchReceiver()); resolvedCall.getDispatchReceiver(), receiverAsmType, receiverValue
);
} }
throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText()); 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 KtElement element,
@NotNull VariableDescriptor variableDescriptor, @NotNull VariableDescriptor variableDescriptor,
@NotNull VariableDescriptor target, @NotNull VariableDescriptor target,
@Nullable ReceiverValue dispatchReceiver @Nullable ReceiverValue dispatchReceiver,
@Nullable final Type receiverAsmType,
@Nullable final StackValue receiverValue
) { ) {
ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor); ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
@@ -2885,11 +2905,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen( PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION), state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
element, classBuilder, target, dispatchReceiver element, classBuilder, target, dispatchReceiver, receiverAsmType
); );
codegen.generate(); 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 @NotNull
@@ -3524,8 +3551,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull StackValue metadataVar @NotNull StackValue metadataVar
) { ) {
//noinspection ConstantConditions //noinspection ConstantConditions
StackValue value = StackValue value = generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null, null, null);
generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null);
value.put(K_PROPERTY0_TYPE, v); value.put(K_PROPERTY0_TYPE, v);
metadataVar.storeSelector(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 // Resolved call to local class constructor doesn't have dispatchReceiver, so we need to generate closure on stack
// See StackValue.receiver for more info // See StackValue.receiver for more info
pushClosureOnStack(containingDeclaration, dispatchReceiver == null, defaultCallGenerator); pushClosureOnStack(
containingDeclaration, dispatchReceiver == null, defaultCallGenerator, /* functionReferenceReceiver = */ null
);
constructor = SamCodegenUtil.resolveSamAdapter(constructor); constructor = SamCodegenUtil.resolveSamAdapter(constructor);
CallableMethod method = typeMapper.mapToCallableMethod(constructor, false); CallableMethod method = typeMapper.mapToCallableMethod(constructor, false);
@@ -39,15 +39,24 @@ import java.util.*;
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> { public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
private final ResolvedCall<?> resolvedCall; private final ResolvedCall<?> resolvedCall;
private final FunctionDescriptor referencedFunction; private final FunctionDescriptor referencedFunction;
private final Type receiverType; // non-null for bound references
private final StackValue receiverValue;
public FunctionReferenceGenerationStrategy( public FunctionReferenceGenerationStrategy(
@NotNull GenerationState state, @NotNull GenerationState state,
@NotNull FunctionDescriptor functionDescriptor, @NotNull FunctionDescriptor functionDescriptor,
@NotNull ResolvedCall<?> resolvedCall @NotNull ResolvedCall<?> resolvedCall,
@Nullable Type receiverType,
@Nullable StackValue receiverValue
) { ) {
super(state, functionDescriptor); super(state, functionDescriptor);
this.resolvedCall = resolvedCall; this.resolvedCall = resolvedCall;
this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor(); 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 @Override
@@ -142,7 +151,8 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) { private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) + 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); List<ValueParameterDescriptor> parameters = CollectionsKt.drop(callableDescriptor.getValueParameters(), receivers);
for (int i = 0; i < parameters.size(); i++) { for (int i = 0; i < parameters.size(); i++) {
@@ -163,14 +173,22 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
) { ) {
if (receiver == null) return null; if (receiver == null) return null;
KtExpression receiverExpression = KtPsiFactoryKt KtExpression receiverExpression = KtPsiFactoryKt.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver"); codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature, codegen));
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
return ExpressionReceiver.Companion.create(receiverExpression, receiver.getType(), BindingContext.EMPTY); return ExpressionReceiver.Companion.create(receiverExpression, receiver.getType(), BindingContext.EMPTY);
} }
@NotNull @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 // 0 is this (the callable reference class), 1 is the invoke() method's first parameter
return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]); return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
} }
@@ -95,7 +95,7 @@ public class JvmRuntimeTypes {
} }
@NotNull @NotNull
public Collection<KotlinType> getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor) { public Collection<KotlinType> getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor, boolean isBound) {
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter(); ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter(); ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
@@ -106,7 +106,7 @@ public class JvmRuntimeTypes {
KotlinType functionType = FunctionTypeResolveUtilsKt.createFunctionType( KotlinType functionType = FunctionTypeResolveUtilsKt.createFunctionType(
DescriptorUtilsKt.getBuiltIns(descriptor), DescriptorUtilsKt.getBuiltIns(descriptor),
Annotations.Companion.getEMPTY(), Annotations.Companion.getEMPTY(),
receiverType, isBound ? null : receiverType,
ExpressionTypingUtils.getValueParametersTypes(descriptor.getValueParameters()), ExpressionTypingUtils.getValueParametersTypes(descriptor.getValueParameters()),
descriptor.getReturnType() descriptor.getReturnType()
); );
@@ -115,13 +115,14 @@ public class JvmRuntimeTypes {
} }
@NotNull @NotNull
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor) { public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor, boolean isBound) {
if (descriptor instanceof LocalVariableDescriptor) { if (descriptor instanceof LocalVariableDescriptor) {
return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType(); return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType();
} }
int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) + 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(); return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
} }
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.AsmUtil.method 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.context.ClassContext
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -46,7 +47,8 @@ class PropertyReferenceCodegen(
expression: KtElement, expression: KtElement,
classBuilder: ClassBuilder, classBuilder: ClassBuilder,
private val target: VariableDescriptor, private val target: VariableDescriptor,
dispatchReceiver: ReceiverValue? dispatchReceiver: ReceiverValue?,
private val receiverType: Type? // non-null for bound references
) : MemberCodegen<KtElement>(state, parentCodegen, context, expression, classBuilder) { ) : MemberCodegen<KtElement>(state, parentCodegen, context, expression, classBuilder) {
private val classDescriptor = context.contextDescriptor private val classDescriptor = context.contextDescriptor
private val asmType = typeMapper.mapClass(classDescriptor) private val asmType = typeMapper.mapClass(classDescriptor)
@@ -55,7 +57,9 @@ class PropertyReferenceCodegen(
private val extensionReceiverType = target.extensionReceiverParameter?.type private val extensionReceiverType = target.extensionReceiverParameter?.type
private val receiverCount = 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 // e.g. MutablePropertyReference0
private val superAsmType = typeMapper.mapClass(classDescriptor.getSuperClassNotAny().sure { "No super class for $classDescriptor" }) 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; // e.g. mutableProperty0(Lkotlin/jvm/internal/MutablePropertyReference0;)Lkotlin/reflect/KMutableProperty0;
private val wrapperMethod = getWrapperMethodForPropertyReference(target, receiverCount) 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() { override fun generateDeclaration() {
v.defineClass( v.defineClass(
element, element,
@@ -79,12 +95,14 @@ class PropertyReferenceCodegen(
// TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction? // TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
override fun generateBody() { override fun generateBody() {
generateConstInstance(asmType, wrapperMethod.returnType) if (JvmCodegenUtil.isConst(closure)) {
generateConstInstance(asmType, wrapperMethod.returnType)
generateMethod("property reference init", 0, method("<init>", Type.VOID_TYPE)) {
load(0, OBJECT_TYPE)
invokespecial(superAsmType.internalName, "<init>", "()V", false)
} }
else {
AsmUtil.genClosureFields(closure, v, typeMapper)
}
generateConstructor()
generateMethod("property reference getName", ACC_PUBLIC, method("getName", JAVA_STRING_TYPE)) { generateMethod("property reference getName", ACC_PUBLIC, method("getName", JAVA_STRING_TYPE)) {
aconst(target.name.asString()) 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() { private fun generateAccessors() {
fun generateAccessor(method: Method, accessorBody: InstructionAdapter.(StackValue) -> Unit) { fun generateAccessor(method: Method, accessorBody: InstructionAdapter.(StackValue) -> Unit) {
generateMethod("property reference $method", ACC_PUBLIC, method) { generateMethod("property reference $method", ACC_PUBLIC, method) {
@@ -123,8 +153,14 @@ class PropertyReferenceCodegen(
StackValue.singleton(containingObject, typeMapper).put(typeMapper.mapClass(containingObject), this) StackValue.singleton(containingObject, typeMapper).put(typeMapper.mapClass(containingObject), this)
} }
for ((index, type) in listOfNotNull(dispatchReceiverType, extensionReceiverType).withIndex()) { if (receiverType != null) {
StackValue.local(index + 1, OBJECT_TYPE).put(typeMapper.mapType(type), this) 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) { val value = if (target is LocalVariableDescriptor) {
@@ -166,10 +202,21 @@ class PropertyReferenceCodegen(
writeSyntheticClassMetadata(v) writeSyntheticClassMetadata(v)
} }
fun putInstanceOnStack(): StackValue = fun putInstanceOnStack(receiverValue: (() -> Unit)?): StackValue {
StackValue.operation(wrapperMethod.returnType) { iv -> 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) 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 { companion object {
@JvmStatic @JvmStatic
@@ -288,17 +288,22 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
CallableDescriptor callableDescriptor; CallableDescriptor callableDescriptor;
Collection<KotlinType> supertypes; Collection<KotlinType> supertypes;
KtExpression receiverExpression = expression.getReceiverExpression();
KotlinType receiverType = receiverExpression != null ? bindingContext.getType(receiverExpression) : null;
if (target instanceof FunctionDescriptor) { if (target instanceof FunctionDescriptor) {
callableDescriptor = bindingContext.get(FUNCTION, expression); callableDescriptor = bindingContext.get(FUNCTION, expression);
if (callableDescriptor == null) return; if (callableDescriptor == null) return;
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target); supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target, receiverType != null);
} }
else if (target instanceof PropertyDescriptor) { else if (target instanceof PropertyDescriptor) {
callableDescriptor = bindingContext.get(VARIABLE, expression); callableDescriptor = bindingContext.get(VARIABLE, expression);
if (callableDescriptor == null) return; if (callableDescriptor == null) return;
supertypes = Collections.singleton(runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target)); supertypes = Collections.singleton(
runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target, receiverType != null)
);
} }
else { else {
return; return;
@@ -306,7 +311,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
String name = inventAnonymousClassName(); String name = inventAnonymousClassName();
ClassDescriptor classDescriptor = recordClassForCallable(expression, callableDescriptor, supertypes, name); ClassDescriptor classDescriptor = recordClassForCallable(expression, callableDescriptor, supertypes, name);
recordClosure(classDescriptor, name); MutableClosure closure = recordClosure(classDescriptor, name);
if (receiverType != null) {
closure.setCaptureReceiverType(receiverType);
}
classStack.push(classDescriptor); classStack.push(classDescriptor);
nameStack.push(name); nameStack.push(name);
@@ -315,9 +324,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
classStack.pop(); classStack.pop();
} }
private void recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) { @NotNull
CodegenBinding.recordClosure(bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name), private MutableClosure recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
fileClassesProvider); return CodegenBinding.recordClosure(
bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name), fileClassesProvider
);
} }
private void recordLocalVariablePropertyMetadata(LocalVariableDescriptor variableDescriptor) { private void recordLocalVariablePropertyMetadata(LocalVariableDescriptor variableDescriptor) {
@@ -369,7 +380,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) { if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) {
VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor; VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor;
String name = inventAnonymousClassName(); String name = inventAnonymousClassName();
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor); KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, /* bound = */ false);
ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name); ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name);
recordClosure(classDescriptor, name); recordClosure(classDescriptor, name);
} }
@@ -139,7 +139,8 @@ public class CodegenBinding {
return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor); return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
} }
static void recordClosure( @NotNull
static MutableClosure recordClosure(
@NotNull BindingTrace trace, @NotNull BindingTrace trace,
@NotNull ClassDescriptor classDescriptor, @NotNull ClassDescriptor classDescriptor,
@Nullable ClassDescriptor enclosing, @Nullable ClassDescriptor enclosing,
@@ -164,6 +165,8 @@ public class CodegenBinding {
if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) { if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
recordInnerClass(trace, enclosing, classDescriptor); recordInnerClass(trace, enclosing, classDescriptor);
} }
return closure;
} }
private static void recordInnerClass( private static void recordInnerClass(
@@ -33,11 +33,12 @@ public final class MutableClosure implements CalculatedClosure {
private final CallableDescriptor enclosingFunWithReceiverDescriptor; private final CallableDescriptor enclosingFunWithReceiverDescriptor;
private boolean captureThis; private boolean captureThis;
private boolean captureReceiver; private boolean captureEnclosingReceiver;
private Map<DeclarationDescriptor, EnclosedValueDescriptor> captureVariables; private Map<DeclarationDescriptor, EnclosedValueDescriptor> captureVariables;
private Map<DeclarationDescriptor, Integer> parameterOffsetInConstructor; private Map<DeclarationDescriptor, Integer> parameterOffsetInConstructor;
private List<Pair<String, Type>> recordedFields; private List<Pair<String, Type>> recordedFields;
private KotlinType captureReceiverType;
MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) { MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) {
this.enclosingClass = enclosingClass; this.enclosingClass = enclosingClass;
@@ -72,7 +73,11 @@ public final class MutableClosure implements CalculatedClosure {
@Override @Override
public KotlinType getCaptureReceiverType() { public KotlinType getCaptureReceiverType() {
if (captureReceiver) { if (captureReceiverType != null) {
return captureReceiverType;
}
if (captureEnclosingReceiver) {
ReceiverParameterDescriptor parameter = getEnclosingReceiverDescriptor(); ReceiverParameterDescriptor parameter = getEnclosingReceiverDescriptor();
assert parameter != null : "Receiver parameter should exist in " + enclosingFunWithReceiverDescriptor; assert parameter != null : "Receiver parameter should exist in " + enclosingFunWithReceiverDescriptor;
return parameter.getType(); return parameter.getType();
@@ -85,7 +90,7 @@ public final class MutableClosure implements CalculatedClosure {
if (enclosingFunWithReceiverDescriptor == null) { if (enclosingFunWithReceiverDescriptor == null) {
throw new IllegalStateException("Extension receiver parameter should exist"); throw new IllegalStateException("Extension receiver parameter should exist");
} }
this.captureReceiver = true; this.captureEnclosingReceiver = true;
} }
@NotNull @NotNull
@@ -94,6 +99,10 @@ public final class MutableClosure implements CalculatedClosure {
return captureVariables != null ? captureVariables : Collections.<DeclarationDescriptor, EnclosedValueDescriptor>emptyMap(); return captureVariables != null ? captureVariables : Collections.<DeclarationDescriptor, EnclosedValueDescriptor>emptyMap();
} }
public void setCaptureReceiverType(@NotNull KotlinType type) {
this.captureReceiverType = type;
}
@NotNull @NotNull
@Override @Override
public List<Pair<String, Type>> getRecordedFields() { public List<Pair<String, Type>> getRecordedFields() {
@@ -490,16 +490,26 @@ public class InlineCodegen extends CallGenerator {
: state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName() : state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName()
); );
FunctionGenerationStrategy strategy = FunctionGenerationStrategy strategy;
expression instanceof KtCallableReferenceExpression ? if (expression instanceof KtCallableReferenceExpression) {
new FunctionReferenceGenerationStrategy( KtCallableReferenceExpression callableReferenceExpression = (KtCallableReferenceExpression) expression;
state, KtExpression receiverExpression = callableReferenceExpression.getReceiverExpression();
descriptor, StackValue receiverValue =
CallUtilKt.getResolvedCallWithAssert( receiverExpression != null && codegen.getBindingContext().getType(receiverExpression) != null
((KtCallableReferenceExpression) expression).getCallableReference(), codegen.getBindingContext() ? codegen.gen(receiverExpression)
) : null;
) :
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression); 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); FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen);
@@ -735,7 +745,7 @@ public class InlineCodegen extends CallGenerator {
private void putClosureParametersOnStack() { private void putClosureParametersOnStack() {
for (LambdaInfo next : expressionMap.values()) { for (LambdaInfo next : expressionMap.values()) {
activeLambda = next; activeLambda = next;
codegen.pushClosureOnStack(next.getClassDescriptor(), true, this); codegen.pushClosureOnStack(next.getClassDescriptor(), true, this, /* functionReferenceReceiver = */ null);
} }
activeLambda = 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); 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") @TestMetadata("compiler/testData/codegen/box/callableReference/function")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)