diff --git a/compiler/testData/codegen/boxWithStdlib/delegatedProperty/stackOverflowOnCallFromGetValue.kt b/compiler/testData/codegen/boxWithStdlib/delegatedProperty/stackOverflowOnCallFromGetValue.kt index 377157f78f4..004930da42f 100644 --- a/compiler/testData/codegen/boxWithStdlib/delegatedProperty/stackOverflowOnCallFromGetValue.kt +++ b/compiler/testData/codegen/boxWithStdlib/delegatedProperty/stackOverflowOnCallFromGetValue.kt @@ -1,23 +1,50 @@ // FULL_JDK -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty0 -import kotlin.test.assertFailsWith +import java.lang.reflect.InvocationTargetException +import kotlin.reflect.* -class Delegate { +object Delegate { fun getValue(t: Any?, p: KProperty<*>): String { - return (p as KProperty0).get() + (p as? KProperty0)?.get() + (p as? KProperty1)?.get(O) + (p as? KProperty2)?.get(O, O) + return "Fail" + } + + fun setValue(t: Any?, p: KProperty<*>, v: String) { + (p as? KMutableProperty0)?.set(v) + (p as? KMutableProperty1)?.set(O, v) + (p as? KMutableProperty2)?.set(O, O, v) } } -val prop: String by Delegate() +var topLevel: String by Delegate +object O { + var member: String by Delegate + var O.memExt: String by Delegate +} + +fun check(lambda: () -> Unit) { + try { + lambda() + throw AssertionError("Getting the property value with .get() from getValue() or setting it with .set() in setValue() " + + "is effectively an endless recursion and should fail") + } catch (e: Throwable) { + if (e !is InvocationTargetException && e !is StackOverflowError) { + throw AssertionError("The current implementation uses reflection to get the value of the property," + + "so either InvocationTargetException or StackOverflowError should have happened, but was: ${e}") + } + } +} fun box(): String { - assertFailsWith( - StackOverflowError::class.java, - "Getting the property value with .get() from getValue is effectively an endless recursion and should fail" - ) { - prop + check { topLevel } + check { topLevel = "" } + check { O.member } + check { O.member = "" } + with (O) { + check { O.memExt } + check { O.memExt = "" } } return "OK" diff --git a/compiler/testData/codegen/boxWithStdlib/delegatedProperty/useKPropertyLater.kt b/compiler/testData/codegen/boxWithStdlib/delegatedProperty/useKPropertyLater.kt new file mode 100644 index 00000000000..99900817655 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/delegatedProperty/useKPropertyLater.kt @@ -0,0 +1,43 @@ +import java.util.* +import kotlin.reflect.* + +val properties = HashSet>() + +object Delegate { + fun getValue(t: Any?, p: KProperty<*>): String { + properties.add(p) + return "" + } + + fun setValue(t: Any?, p: KProperty<*>, v: String) { + properties.add(p) + } +} + +var topLevel: String by Delegate +object O { + var member: String by Delegate + var O.memExt: String by Delegate +} + +fun box(): String { + topLevel = "" + O.member = "" + with (O) { + O.memExt = "" + } + + for (p in HashSet(properties)) { + // None of these should fail + + (p as? KProperty0)?.get() + (p as? KProperty1)?.get(O) + (p as? KProperty2)?.get(O, O) + + (p as? KMutableProperty0)?.set("") + (p as? KMutableProperty1)?.set(O, "") + (p as? KMutableProperty2)?.set(O, O, "") + } + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index bba79eb3ac7..a9bd2922546 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -1467,6 +1467,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/delegatedProperty/stackOverflowOnCallFromGetValue.kt"); doTestWithStdlib(fileName); } + + @TestMetadata("useKPropertyLater.kt") + public void testUseKPropertyLater() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/delegatedProperty/useKPropertyLater.kt"); + doTestWithStdlib(fileName); + } } @TestMetadata("compiler/testData/codegen/boxWithStdlib/enum") diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyFromReferenceImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyFromReferenceImpl.kt index bf13fe1599c..d9d3fe553f6 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyFromReferenceImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyFromReferenceImpl.kt @@ -18,56 +18,65 @@ package kotlin.reflect.jvm.internal import kotlin.jvm.internal.* -internal class KProperty0FromReferenceImpl( +internal open class KProperty0Augmented( val reference: PropertyReference0 ) : KProperty0Impl(reference.owner as KDeclarationContainerImpl, reference.name, reference.signature) { override val name: String get() = reference.name +} +internal class KProperty0FromReferenceImpl(reference: PropertyReference0) : KProperty0Augmented(reference) { override fun get(): Any? = reference.get() } -internal class KMutableProperty0FromReferenceImpl( +internal open class KMutableProperty0Augmented( val reference: MutablePropertyReference0 ) : KMutableProperty0Impl(reference.owner as KDeclarationContainerImpl, reference.name, reference.signature) { override val name: String get() = reference.name +} +internal class KMutableProperty0FromReferenceImpl(reference: MutablePropertyReference0) : KMutableProperty0Augmented(reference) { override fun get(): Any? = reference.get() - override fun set(value: Any?) = reference.set(value) } -internal class KProperty1FromReferenceImpl( +internal open class KProperty1Augmented( val reference: PropertyReference1 ) : KProperty1Impl(reference.owner as KDeclarationContainerImpl, reference.name, reference.signature) { override val name: String get() = reference.name +} +internal class KProperty1FromReferenceImpl(reference: PropertyReference1) : KProperty1Augmented(reference) { override fun get(receiver: Any?): Any? = reference.get(receiver) } -internal class KMutableProperty1FromReferenceImpl( +internal open class KMutableProperty1Augmented( val reference: MutablePropertyReference1 ) : KMutableProperty1Impl(reference.owner as KDeclarationContainerImpl, reference.name, reference.signature) { override val name: String get() = reference.name +} +internal class KMutableProperty1FromReferenceImpl(reference: MutablePropertyReference1) : KMutableProperty1Augmented(reference) { override fun get(receiver: Any?): Any? = reference.get(receiver) - override fun set(receiver: Any?, value: Any?) = reference.set(receiver, value) } -internal class KProperty2FromReferenceImpl( +internal open class KProperty2Augmented( val reference: PropertyReference2 ) : KProperty2Impl(reference.owner as KDeclarationContainerImpl, reference.name, reference.signature) { override val name: String get() = reference.name +} +internal class KProperty2FromReferenceImpl(reference: PropertyReference2) : KProperty2Augmented(reference) { override fun get(receiver1: Any?, receiver2: Any?): Any? = reference.get(receiver1, receiver2) } -internal class KMutableProperty2FromReferenceImpl( +internal open class KMutableProperty2Augmented( val reference: MutablePropertyReference2 ) : KMutableProperty2Impl(reference.owner as KDeclarationContainerImpl, reference.name, reference.signature) { override val name: String get() = reference.name +} +internal class KMutableProperty2FromReferenceImpl(reference: MutablePropertyReference2) : KMutableProperty2Augmented(reference) { override fun get(receiver1: Any?, receiver2: Any?): Any? = reference.get(receiver1, receiver2) - override fun set(receiver1: Any?, receiver2: Any?, value: Any?) = reference.set(receiver1, receiver2, value) } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java index e9796373884..c92a4e4de8e 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java @@ -50,32 +50,32 @@ public class ReflectionFactoryImpl extends ReflectionFactory { @Override public KProperty0 property0(PropertyReference0 p) { - return new KProperty0FromReferenceImpl(p); + return p instanceof PropertyReference0Impl ? new KProperty0Augmented(p) : new KProperty0FromReferenceImpl(p); } @Override public KMutableProperty0 mutableProperty0(MutablePropertyReference0 p) { - return new KMutableProperty0FromReferenceImpl(p); + return p instanceof MutablePropertyReference0Impl ? new KMutableProperty0Augmented(p) : new KMutableProperty0FromReferenceImpl(p); } @Override public KProperty1 property1(PropertyReference1 p) { - return new KProperty1FromReferenceImpl(p); + return p instanceof PropertyReference1Impl ? new KProperty1Augmented(p) : new KProperty1FromReferenceImpl(p); } @Override public KMutableProperty1 mutableProperty1(MutablePropertyReference1 p) { - return new KMutableProperty1FromReferenceImpl(p); + return p instanceof MutablePropertyReference1Impl ? new KMutableProperty1Augmented(p) : new KMutableProperty1FromReferenceImpl(p); } @Override public KProperty2 property2(PropertyReference2 p) { - return new KProperty2FromReferenceImpl(p); + return p instanceof PropertyReference2Impl ? new KProperty2Augmented(p) : new KProperty2FromReferenceImpl(p); } @Override public KMutableProperty2 mutableProperty2(MutablePropertyReference2 p) { - return new KMutableProperty2FromReferenceImpl(p); + return p instanceof MutablePropertyReference2Impl ? new KMutableProperty2Augmented(p) : new KMutableProperty2FromReferenceImpl(p); } // Deprecated