JVM IR: change generation scheme of property $delegate methods

Generate $delegate method as instance method in
PropertyReferenceDelegationLowering, and remove dispatch receiver later
in MakePropertyDelegateMethodsStatic. The method needs to be static to
be non-overridable (see delegateMethodIsNonOverridable.kt), and public
to be accessible in reflection.

Otherwise we generated incorrect IR where a static function accessed an
instance field of the containing class, which failed in multiple places
including LocalDeclarationsLowering.

 #KT-48350 Fixed
This commit is contained in:
Alexander Udalov
2021-08-21 02:38:11 +02:00
committed by Alexander Udalov
parent 50508da156
commit 04c5bbdcf8
25 changed files with 565 additions and 48 deletions
@@ -0,0 +1,23 @@
// WITH_RUNTIME
interface I {
var z: String
}
class X {
var p: String = "Fail"
}
class A {
val x = X()
val y = object : I {
override var z: String by x::p
}
}
fun box(): String {
val a = A()
a.y.z = "OK"
return a.y.z
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
interface I {
var z: String
}
class X {
var p: String = "Fail"
}
class A {
val x = X()
inner class Y : I {
override var z: String by x::p
}
val y = Y()
}
fun box(): String {
val a = A()
a.y.z = "OK"
return a.y.z
}
@@ -0,0 +1,34 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.*
import kotlin.reflect.jvm.isAccessible
val a = 1
val b = 2
fun KProperty0<*>.test(): String =
(apply { isAccessible = true }.getDelegate() as KProperty<*>).name
open class C {
open val x by run { ::a }
open val y by ::a
val xc = ::x.test()
val yc = ::y.test()
}
class D : C() {
override val x by run { ::b }
override val y by ::b
val xd = ::x.test()
val yd = ::y.test()
}
fun box(): String {
val result = D().run { "$xc $yc $xd $yd" }
if (result != "a a b b") return "Fail: $result"
return "OK"
}
@@ -11,6 +11,8 @@ class C(var x: Int) {
class D(val c: C) {
var y by c::x
var C.w by C::x
var Int.q by Int::x
}
var x = 1
@@ -40,6 +42,9 @@ fun <T> KMutableProperty1<T, Int>.test(receiver: T) =
fun <T> KMutableProperty1<T, Int>.test(receiver: T, receiver2: T) =
test({ getDelegate(receiver) as KMutableProperty1<T, Int> }, { get(receiver2) }, { set(receiver2, it) })
fun <R1, R2> KMutableProperty2<R1, R2, Int>.test(receiver1: R1, receiver2: R2) =
test({ getDelegate(receiver1, receiver2) as KMutableProperty1<R2, Int> }, { get(receiver2) }, { set(receiver2, it) })
fun box(): String {
C::y.test(C(100), C(1))
C::z.test(C(1))
@@ -47,5 +52,12 @@ fun box(): String {
::y.test()
::z.test()
Int::y.test({ getExtensionDelegate() as KMutableProperty1<Int, Int> }, { get(100) }, { set(100, it) })
val w = D::class.members.single { it.name == "w" } as KMutableProperty2<D, C, Int>
w.test(D(C(100)), C(1))
val q = D::class.members.single { it.name == "q" } as KMutableProperty2<D, Int, Int>
q.test({ getExtensionDelegate(D(C(100))) as KMutableProperty1<Int, Int> }, { get(100) }, { set(100, it) })
return "OK"
}
@@ -0,0 +1,14 @@
// WITH_RUNTIME
val a = 1
val b = 2
open class C {
open val x by run { ::a }
open val y by ::a
}
class D : C() {
override val x by run { ::b }
override val y by ::b
}
@@ -0,0 +1,65 @@
@kotlin.Metadata
synthetic final class C$x$2$1 {
// source: 'delegateMethodIsNonOverridable.kt'
public final static field INSTANCE: kotlin.reflect.KProperty0
static method <clinit>(): void
method <init>(): void
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
}
@kotlin.Metadata
synthetic final class C$y$2 {
// source: 'delegateMethodIsNonOverridable.kt'
public final static field INSTANCE: kotlin.reflect.KProperty0
static method <clinit>(): void
method <init>(): void
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
}
@kotlin.Metadata
public class C {
// source: 'delegateMethodIsNonOverridable.kt'
private final @org.jetbrains.annotations.NotNull field x$delegate: kotlin.reflect.KProperty0
private final @org.jetbrains.annotations.NotNull field y$delegate: kotlin.reflect.KProperty0
public method <init>(): void
public method getX(): int
public method getY(): int
}
@kotlin.Metadata
synthetic final class D$x$2$1 {
// source: 'delegateMethodIsNonOverridable.kt'
public final static field INSTANCE: kotlin.reflect.KProperty0
static method <clinit>(): void
method <init>(): void
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
}
@kotlin.Metadata
synthetic final class D$y$2 {
// source: 'delegateMethodIsNonOverridable.kt'
public final static field INSTANCE: kotlin.reflect.KProperty0
static method <clinit>(): void
method <init>(): void
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
}
@kotlin.Metadata
public final class D {
// source: 'delegateMethodIsNonOverridable.kt'
private final @org.jetbrains.annotations.NotNull field x$delegate: kotlin.reflect.KProperty0
private final @org.jetbrains.annotations.NotNull field y$delegate: kotlin.reflect.KProperty0
public method <init>(): void
public method getX(): int
public method getY(): int
}
@kotlin.Metadata
public final class DelegateMethodIsNonOverridableKt {
// source: 'delegateMethodIsNonOverridable.kt'
private final static field a: int
private final static field b: int
static method <clinit>(): void
public final static method getA(): int
public final static method getB(): int
}
@@ -0,0 +1,53 @@
@kotlin.Metadata
synthetic final class C$x$2$1 {
// source: 'delegateMethodIsNonOverridable.kt'
enclosing method C.<init>()V
public final static field INSTANCE: C$x$2$1
inner (anonymous) class C$x$2$1
static method <clinit>(): void
method <init>(): void
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
}
@kotlin.Metadata
public class C {
// source: 'delegateMethodIsNonOverridable.kt'
private final @org.jetbrains.annotations.NotNull field x$delegate: kotlin.reflect.KProperty0
inner (anonymous) class C$x$2$1
public method <init>(): void
public method getX(): int
public static method getY$delegate(p0: C): java.lang.Object
public method getY(): int
}
@kotlin.Metadata
synthetic final class D$x$2$1 {
// source: 'delegateMethodIsNonOverridable.kt'
enclosing method D.<init>()V
public final static field INSTANCE: D$x$2$1
inner (anonymous) class D$x$2$1
static method <clinit>(): void
method <init>(): void
public @org.jetbrains.annotations.Nullable method get(): java.lang.Object
}
@kotlin.Metadata
public final class D {
// source: 'delegateMethodIsNonOverridable.kt'
private final @org.jetbrains.annotations.NotNull field x$delegate: kotlin.reflect.KProperty0
inner (anonymous) class D$x$2$1
public method <init>(): void
public method getX(): int
public static method getY$delegate(p0: D): java.lang.Object
public method getY(): int
}
@kotlin.Metadata
public final class DelegateMethodIsNonOverridableKt {
// source: 'delegateMethodIsNonOverridable.kt'
private final static field a: int
private final static field b: int
static method <clinit>(): void
public final static method getA(): int
public final static method getB(): int
}
@@ -35,11 +35,11 @@ fun local() {
// 0 private final( static)? Lkotlin/reflect/KMutableProperty[0-2]; [xyz]m?\$delegate
// 2 private final( static)? LC; [xyz]m?\$receiver
// 0 LOCALVARIABLE [xyz]m? Lkotlin/reflect/KMutableProperty[0-2];
// 12 static get[XYZ]m?\$delegate
// 12 public( static)? get[XYZ]m?\$delegate
// JVM_TEMPLATES
// Not optimized, references created as classes and stored in fields:
// 16 extends kotlin/jvm/internal/MutablePropertyReference[0-2]Impl
// 12 private final( static)? Lkotlin/reflect/KMutableProperty[0-2]; [xyz]m?\$delegate
// 4 LOCALVARIABLE [xyz]m? Lkotlin/reflect/KMutableProperty[0-2];
// 0 static get[XYZ]m?\$delegate
// 0 get[XYZ]m?\$delegate