diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index b0722e6d6f5..2d83cb5d85e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -241,7 +241,7 @@ public class ClosureCodegen extends MemberCodegen { } @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() { @@ -254,7 +254,7 @@ public class ClosureCodegen extends MemberCodegen { v.anew(asmType); v.dup(); - codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator); + codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator, functionReferenceReceiver); v.invokespecial(asmType.getInternalName(), "", constructor.getDescriptor(), false); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 0d06fbb0a8e..09ae28ab47c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -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 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 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 impleme propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages()); } - return closureCodegen.putInstanceOnStack(this); + return closureCodegen.putInstanceOnStack(this, functionReferenceReceiver); } @Override @@ -1466,7 +1468,7 @@ public class ExpressionCodegen extends KtVisitor 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 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 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 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 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 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 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() { + @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 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 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); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java index 58b11fe2106..69c8cc341f2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java @@ -39,15 +39,24 @@ import java.util.*; public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased { 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 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 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]); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java index b5281d3381d..ef764501228 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java @@ -95,7 +95,7 @@ public class JvmRuntimeTypes { } @NotNull - public Collection getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor) { + public Collection 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(); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index c49eabd102a..8a601c22e41 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -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(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("", 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("", Type.VOID_TYPE)) { - load(0, OBJECT_TYPE) - invokespecial(superAsmType.internalName, "", "()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, "", "()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, "", constructor.descriptor, false) + } + } + } companion object { @JvmStatic diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 9b2a2a62ce4..4a4cd5a3b0c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -288,17 +288,22 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { CallableDescriptor callableDescriptor; Collection 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); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java index 32dd336f4f0..0fbce52dc66 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java @@ -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( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java index 142f125afee..dd2a011917f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java @@ -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 captureVariables; private Map parameterOffsetInConstructor; private List> 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.emptyMap(); } + public void setCaptureReceiverType(@NotNull KotlinType type) { + this.captureReceiverType = type; + } + @NotNull @Override public List> getRecordedFields() { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index 904d12944d6..a7107ce8747 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -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; } diff --git a/compiler/testData/codegen/box/callableReference/bound/inline/simple.kt b/compiler/testData/codegen/box/callableReference/bound/inline/simple.kt new file mode 100644 index 00000000000..ab672b23ac4 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/inline/simple.kt @@ -0,0 +1,5 @@ +inline fun foo(x: () -> String) = x() + +fun String.id() = this + +fun box() = foo("OK"::id) diff --git a/compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt b/compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt new file mode 100644 index 00000000000..15fae151f5f --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt @@ -0,0 +1,4 @@ +fun box(): String { + val f = "KOTLIN"::get + return "${f(1)}${f(0)}" +} diff --git a/compiler/testData/codegen/box/callableReference/bound/simpleProperty.kt b/compiler/testData/codegen/box/callableReference/bound/simpleProperty.kt new file mode 100644 index 00000000000..c271b1c6418 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/simpleProperty.kt @@ -0,0 +1,5 @@ +fun box(): String { + val f = "kotlin"::length + val result = f.get() + return if (result == 6) "OK" else "Fail: $result" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index cc65cdedb9e..bfb29438430 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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)