Restore anonymous classes for local delegate metadatas in inline functions

The problem is that now that the local delegated property metadata is in
the $$delegatedProperties array of the containing class, the access to
it from code calling an inline function with a local delegated property
is illegal.

Currently it seems to be a lot of work to support this rather rare case
properly (see the comment in ExpressionCodegen.getVariableMetadataValue)
so we postpone it and return the old behavior of using the anonymous
KProperty subclass for metadata
This commit is contained in:
Alexander Udalov
2017-06-14 13:21:09 +03:00
parent 616d575fb6
commit 2611c7de7e
14 changed files with 216 additions and 14 deletions
@@ -1309,6 +1309,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> impleme
Type resultType = initializer.type;
if (isDelegatedLocalVariable(variableDescriptor)) {
StackValue metadataValue = PropertyCodegen.getDelegatedPropertyMetadata(variableDescriptor, bindingContext);
StackValue metadataValue = getVariableMetadataValue(variableDescriptor);
initializePropertyMetadata((KtProperty) variableDeclaration, variableDescriptor, metadataValue);
ResolvedCall<FunctionDescriptor> provideDelegateResolvedCall = bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, variableDescriptor);
if (provideDelegateResolvedCall != null) {
resultType = generateProvideDelegateCallForLocalVariable(initializer, metadataValue, provideDelegateResolvedCall);
ResolvedCall<FunctionDescriptor> 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<StackValue, StackValue> 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 <clinit> 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
@@ -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()
}
}
@@ -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);
@@ -69,6 +69,8 @@ public class CodegenBinding {
Slices.createSimpleSlice();
public static final WritableSlice<VariableDescriptorWithAccessors, Type> DELEGATED_PROPERTY_METADATA_OWNER =
Slices.createSimpleSlice();
public static final WritableSlice<VariableDescriptor, VariableDescriptor> 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;
}
}
@@ -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"
}
@@ -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" }
}
@@ -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()
}
@@ -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");
@@ -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")
@@ -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")
@@ -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");
@@ -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")
@@ -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")
@@ -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");