diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 8ee2cfa6df3..b4a19cf0953 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1309,6 +1309,10 @@ public class ExpressionCodegen extends KtVisitor impleme Type type = getVariableType(variableDescriptor); int index = myFrameMap.enter(variableDescriptor, type); + if (isDelegatedLocalVariable(variableDescriptor)) { + myFrameMap.enter(getDelegatedLocalVariableMetadata(variableDescriptor, bindingContext), AsmTypes.K_PROPERTY0_TYPE); + } + if (isSharedVarType(type)) { markLineNumber(statement, false); v.anew(type); @@ -1356,6 +1360,10 @@ public class ExpressionCodegen extends KtVisitor impleme v.mark(scopeStart); leaveTasks.add(answer -> { + if (isDelegatedLocalVariable(variableDescriptor)) { + myFrameMap.leave(getDelegatedLocalVariableMetadata(variableDescriptor, bindingContext)); + } + int index = myFrameMap.leave(variableDescriptor); v.visitLocalVariable(variableDescriptor.getName().asString(), type.getDescriptor(), null, scopeStart, blockEnd, index); @@ -1769,9 +1777,14 @@ public class ExpressionCodegen extends KtVisitor impleme } StackValue value = context.lookupInContext(descriptor, StackValue.LOCAL_0, state, false); - if (value == null) return null; + if (isDelegatedLocalVariable(descriptor) && value != null) { + VariableDescriptor metadata = getDelegatedLocalVariableMetadata((VariableDescriptor) descriptor, bindingContext); + StackValue metadataValue = context.lookupInContext(metadata, StackValue.LOCAL_0, state, false); + assert metadataValue != null : "Metadata stack value should be non-null for local delegated property: " + descriptor; + return delegatedVariableValue(value, metadataValue, (VariableDescriptorWithAccessors) descriptor, typeMapper); + } - return adjustVariableValue(value, descriptor); + return value; } @Nullable @@ -3466,12 +3479,19 @@ public class ExpressionCodegen extends KtVisitor impleme } } + @NotNull + private StackValue getVariableMetadataValue(@NotNull VariableDescriptorWithAccessors variableDescriptor) { + StackValue value = findLocalOrCapturedValue(getDelegatedLocalVariableMetadata(variableDescriptor, bindingContext)); + assert value != null : "Can't find stack value for local delegated variable metadata: " + variableDescriptor; + return value; + } + @NotNull private StackValue adjustVariableValue(@NotNull StackValue varValue, DeclarationDescriptor descriptor) { if (!isDelegatedLocalVariable(descriptor)) return varValue; VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor; - StackValue metadataValue = PropertyCodegen.getDelegatedPropertyMetadata(variableDescriptor, bindingContext); + StackValue metadataValue = getVariableMetadataValue(variableDescriptor); return delegatedVariableValue(varValue, metadataValue, variableDescriptor, typeMapper); } @@ -3503,16 +3523,16 @@ public class ExpressionCodegen extends KtVisitor impleme Type resultType = initializer.type; if (isDelegatedLocalVariable(variableDescriptor)) { - StackValue metadataValue = PropertyCodegen.getDelegatedPropertyMetadata(variableDescriptor, bindingContext); + StackValue metadataValue = getVariableMetadataValue(variableDescriptor); + initializePropertyMetadata((KtProperty) variableDeclaration, variableDescriptor, metadataValue); - ResolvedCall provideDelegateResolvedCall = bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor); - if (provideDelegateResolvedCall != null) { - resultType = generateProvideDelegateCallForLocalVariable(initializer, metadataValue, provideDelegateResolvedCall); + ResolvedCall provideDelegateCall = bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor); + if (provideDelegateCall != null) { + resultType = generateProvideDelegateCallForLocalVariable(initializer, metadataValue, provideDelegateCall); } } storeTo.storeSelector(resultType, v); - } @NotNull @@ -3559,10 +3579,16 @@ public class ExpressionCodegen extends KtVisitor impleme @NotNull LocalVariableDescriptor variableDescriptor, @NotNull StackValue metadataVar ) { + // TODO: do not generate anonymous classes for local delegated properties in inline functions + // We can use the $$delegatedProperties array as in non-inline functions and upon inlining, detect elements at what indices + // of that array are used in the inline function body, load the corresponding initializing bytecode from of the + // container class (where the PropertyReferenceNImpl instance is created), copy and adapt it at the call site //noinspection ConstantConditions - StackValue value = generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null, null); - value.put(K_PROPERTY0_TYPE, v); - metadataVar.storeSelector(K_PROPERTY0_TYPE, v); + StackValue value = context.getFunctionDescriptor().isInline() + ? generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null, null) + : PropertyCodegen.getDelegatedPropertyMetadata(variableDescriptor, bindingContext); + value.put(K_PROPERTY_TYPE, v); + metadataVar.storeSelector(K_PROPERTY_TYPE, v); } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index e1939769d04..fd186b74c0c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -116,10 +116,11 @@ class PropertyReferenceCodegen( generateCallableReferenceSignature(this, target, state) } + generateMethod("property reference getOwner", ACC_PUBLIC, method("getOwner", K_DECLARATION_CONTAINER_TYPE)) { + ClosureCodegen.generateCallableReferenceDeclarationContainer(this, target, state) + } + if (!isLocalDelegatedProperty) { - generateMethod("property reference getOwner", ACC_PUBLIC, method("getOwner", K_DECLARATION_CONTAINER_TYPE)) { - ClosureCodegen.generateCallableReferenceDeclarationContainer(this, target, state) - } generateAccessors() } } 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 bdf76c18bc3..71a5d283ef1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -34,6 +34,8 @@ import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; import org.jetbrains.kotlin.coroutines.CoroutineUtilKt; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.annotations.Annotations; +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.fileClasses.FileClasses; import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor; @@ -372,12 +374,32 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { return CodegenBinding.recordClosure(bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name)); } + private void recordLocalVariablePropertyMetadata(LocalVariableDescriptor variableDescriptor) { + KotlinType delegateType = JvmCodegenUtil.getPropertyDelegateType(variableDescriptor, bindingContext); + if (delegateType == null) return; + + LocalVariableDescriptor metadataVariableDescriptor = new LocalVariableDescriptor( + variableDescriptor.getContainingDeclaration(), + Annotations.Companion.getEMPTY(), + Name.identifier(variableDescriptor.getName().asString() + "$metadata"), + ReflectionTypes.Companion.createKPropertyStarType(DescriptorUtilsKt.getModule(variableDescriptor)), + false, + false, + SourceElement.NO_SOURCE + ); + bindingTrace.record(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor, metadataVariableDescriptor); + } + @Override public void visitProperty(@NotNull KtProperty property) { DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, property); // working around a problem with shallow analysis if (descriptor == null) return; + if (descriptor instanceof LocalVariableDescriptor) { + recordLocalVariablePropertyMetadata((LocalVariableDescriptor) descriptor); + } + String nameForClassOrPackageMember = getNameForClassOrPackageMember(descriptor); if (nameForClassOrPackageMember != null) { nameStack.push(nameForClassOrPackageMember); 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 17e1c1bae73..44cfada189c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java @@ -69,6 +69,8 @@ public class CodegenBinding { Slices.createSimpleSlice(); public static final WritableSlice DELEGATED_PROPERTY_METADATA_OWNER = Slices.createSimpleSlice(); + public static final WritableSlice LOCAL_VARIABLE_PROPERTY_METADATA = + Slices.createSimpleSlice(); static { BasicWritableSlice.initSliceDebugNames(CodegenBinding.class); @@ -223,4 +225,14 @@ public class CodegenBinding { assert type != null : "Type is not yet recorded for " + klass; return type; } + + @NotNull + public static VariableDescriptor getDelegatedLocalVariableMetadata( + @NotNull VariableDescriptor variableDescriptor, + @NotNull BindingContext bindingContext + ) { + VariableDescriptor metadataVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor); + assert metadataVariableDescriptor != null : "Metadata for local delegated property should be not null: " + variableDescriptor; + return metadataVariableDescriptor; + } } diff --git a/compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt b/compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt new file mode 100644 index 00000000000..74716048087 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt @@ -0,0 +1,26 @@ +// TARGET_BACKEND: JVM +// WITH_REFLECT + +import kotlin.reflect.* +import kotlin.test.assertEquals + +object Delegate { + lateinit var property: KProperty<*> + + operator fun getValue(instance: Any?, kProperty: KProperty<*>) { + property = kProperty + } +} + +class Foo { + inline fun foo() { + val x by Delegate + x + } +} + +fun box(): String { + Foo().foo() + assertEquals("val x: kotlin.Unit", Delegate.property.toString()) + return "OK" +} diff --git a/compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt b/compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt new file mode 100644 index 00000000000..c098619f778 --- /dev/null +++ b/compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt @@ -0,0 +1,26 @@ +// FILE: 1.kt +package test + +import kotlin.reflect.KProperty + +class Delegate { + operator fun getValue(t: Any?, p: KProperty<*>): String = "O" +} + +inline fun test(crossinline s: () -> String): String { + val delegate = Delegate() + val o = object { + fun run(): String { + val prop: String by delegate + return prop + s() + } + } + return o.run() +} + +// FILE: 2.kt +import test.* + +fun box(): String { + return test { "K" } +} diff --git a/compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt b/compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt new file mode 100644 index 00000000000..851a60fab73 --- /dev/null +++ b/compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt @@ -0,0 +1,23 @@ +// FILE: 1.kt +package test + +import kotlin.reflect.KProperty + +class Delegate { + operator fun getValue(t: Any?, p: KProperty<*>): String = "OK" +} + +inline fun test(): String { + val b by Delegate() + + return run { + b + } +} + +// FILE: 2.kt +import test.* + +fun box(): String { + return test() +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 548b2ff753d..9a47efc8d02 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -16050,6 +16050,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/properties/localDelegated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("inlineFun.kt") + public void testInlineFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); + doTest(fileName); + } + @TestMetadata("localDelegatedProperty.kt") public void testLocalDelegatedProperty() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/localDelegated/localDelegatedProperty.kt"); diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java index 101211d16c8..819946d8385 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java @@ -1354,6 +1354,18 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/local.kt"); doTest(fileName); } + + @TestMetadata("localInAnonymousObject.kt") + public void testLocalInAnonymousObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt"); + doTest(fileName); + } + + @TestMetadata("localInLambda.kt") + public void testLocalInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/boxInline/enclosingInfo") diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java index aa4afd387b6..330636a05b3 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1354,6 +1354,18 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/local.kt"); doTest(fileName); } + + @TestMetadata("localInAnonymousObject.kt") + public void testLocalInAnonymousObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt"); + doTest(fileName); + } + + @TestMetadata("localInLambda.kt") + public void testLocalInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/boxInline/enclosingInfo") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 845c96629d5..a4e9f2369d8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -16050,6 +16050,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/properties/localDelegated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("inlineFun.kt") + public void testInlineFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); + doTest(fileName); + } + @TestMetadata("localDelegatedProperty.kt") public void testLocalDelegatedProperty() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/localDelegated/localDelegatedProperty.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index c2a55987be4..2285c508c9f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -1354,6 +1354,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/local.kt"); doTest(fileName); } + + @TestMetadata("localInAnonymousObject.kt") + public void testLocalInAnonymousObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt"); + doTest(fileName); + } + + @TestMetadata("localInLambda.kt") + public void testLocalInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/boxInline/enclosingInfo") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index b9af20d66f6..2f945c929af 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1354,6 +1354,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/local.kt"); doTest(fileName); } + + @TestMetadata("localInAnonymousObject.kt") + public void testLocalInAnonymousObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInAnonymousObject.kt"); + doTest(fileName); + } + + @TestMetadata("localInLambda.kt") + public void testLocalInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/delegatedProperty/localInLambda.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/codegen/boxInline/enclosingInfo") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 076960d9855..da20743245c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -16050,6 +16050,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/properties/localDelegated"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("inlineFun.kt") + public void testInlineFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); + doTest(fileName); + } + @TestMetadata("localDelegatedProperty.kt") public void testLocalDelegatedProperty() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/properties/localDelegated/localDelegatedProperty.kt");