Atomicfu plugin (JS IR): fix for private delegated properties

Fixes https://github.com/Kotlin/kotlinx-atomicfu/issues/260
This commit is contained in:
mvicsokolova
2022-11-03 14:40:21 +00:00
committed by Space Team
parent 92ec8e6a3e
commit 5708b2229a
2 changed files with 38 additions and 28 deletions
@@ -125,13 +125,13 @@ class AtomicfuJsIrTransformer(private val context: IrPluginContext) {
// fun <get-a>() = a$delegate.value -> _a.value // fun <get-a>() = a$delegate.value -> _a.value
// fun <set-a>(value: Int) = { a$delegate.value = value } -> { _a.value = value } // fun <set-a>(value: Int) = { a$delegate.value = value } -> { _a.value = value }
val originalField = initializer.getBackingField() val originalField = initializer.getBackingField()
declaration.transform(DelegatePropertyTransformer(originalField, initializer.dispatchReceiver, false), null) declaration.transform(DelegatePropertyTransformer(originalField), null)
} }
initializer.isAtomicFactory() -> { initializer.isAtomicFactory() -> {
// var a by atomic(77) -> var a: Int = 77 // var a by atomic(77) -> var a: Int = 77
it.expression = initializer.eraseAtomicFactory() it.expression = initializer.eraseAtomicFactory()
?: error("Atomic factory was expected but found ${initializer.render()}") ?: error("Atomic factory was expected but found ${initializer.render()}")
declaration.transform(DelegatePropertyTransformer(delegateBackingField, initializer.dispatchReceiver, true), null) declaration.transform(DelegatePropertyTransformer(delegateBackingField), null)
} }
else -> error("Unexpected initializer of the delegated property: $initializer") else -> error("Unexpected initializer of the delegated property: $initializer")
} }
@@ -275,9 +275,7 @@ class AtomicfuJsIrTransformer(private val context: IrPluginContext) {
} }
private inner class DelegatePropertyTransformer( private inner class DelegatePropertyTransformer(
val originalField: IrField, val originalField: IrField
val receiver: IrExpression?,
val isInitializedWithAtomicFactory: Boolean
): IrElementTransformerVoid() { ): IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
// Accessors of the delegated property have following signatures: // Accessors of the delegated property have following signatures:
@@ -291,11 +289,14 @@ class AtomicfuJsIrTransformer(private val context: IrPluginContext) {
val type = originalField.type.atomicToValueType() val type = originalField.type.atomicToValueType()
val isSetter = name == SET_VALUE val isSetter = name == SET_VALUE
val runtimeFunction = getRuntimeFunctionSymbol(name, type) val runtimeFunction = getRuntimeFunctionSymbol(name, type)
val dispatchReceiver = if (isInitializedWithAtomicFactory) { // val _a = atomic(77)
val thisRef = expression.getValueArgument(0)!! // var a: Int by _a
if (thisRef.isConstNull()) null else thisRef // This is the delegate getValue operator of property `a`, which should be transformed to getting the value of the original atomic `_a`
} else { // operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>) {
receiver // return thisRef._a
// }
val dispatchReceiver = expression.getValueArgument(0)?.let {
if (it.isConstNull()) null else it
} }
val fieldAccessors = listOf( val fieldAccessors = listOf(
context.buildFieldAccessor(originalField, dispatchReceiver, false), context.buildFieldAccessor(originalField, dispatchReceiver, false),
@@ -7,37 +7,47 @@ var topLevelInt: Int by _topLevelInt
private var topLevelVolatile by atomic(56) private var topLevelVolatile by atomic(56)
class DelegatedProperties { class DelegatedProperties {
// Delegated properties should be declared in the same scope as the original atomic values
private val _a = atomic(42) private val _a = atomic(42)
var a: Int by _a var a: Int by _a
private var privateA: Int by _a
private val _l = atomic(55555555555) private val _l = atomic(55555555555)
var l: Long by _l private var l: Long by _l
private val _b = atomic(false) private val _b = atomic(false)
var b: Boolean by _b private var b: Boolean by _b
private val _ref = atomic(A(B(77))) private val _ref = atomic(A(B(77)))
var ref: A by _ref private var ref: A by _ref
var vInt by atomic(77) private var vInt by atomic(77)
var vLong by atomic(777777777) private var vLong by atomic(777777777)
var vBoolean by atomic(false) private var vBoolean by atomic(false)
var vRef by atomic(A(B(77))) private var vRef by atomic(A(B(77)))
class A (val b: B) class A (val b: B)
class B (val n: Int) class B (val n: Int)
fun testDelegatedAtomicInt() { fun testDelegatedAtomicInt() {
assertEquals(42, a) assertEquals(42, a)
assertEquals(42, privateA)
_a.compareAndSet(42, 56) _a.compareAndSet(42, 56)
assertEquals(56, a) assertEquals(56, a)
assertEquals(56, privateA)
a = 77 a = 77
_a.compareAndSet(77, 66) _a.compareAndSet(77, 66)
privateA = 88
_a.compareAndSet(88, 66)
assertEquals(66, _a.value) assertEquals(66, _a.value)
assertEquals(66, a) assertEquals(66, a)
assertEquals(66, privateA)
val aValue = a + privateA
assertEquals(132, aValue)
} }
fun testDelegatedAtomicLong() { fun testDelegatedAtomicLong() {
@@ -95,25 +105,23 @@ class DelegatedProperties {
} }
inner class D { inner class D {
var b: Int by _a
var c by atomic("aaa") var c by atomic("aaa")
} }
fun testScopedDelegatedProperties() { fun testScopedVolatileProperties() {
val clazz = D() val clazz = D()
clazz.b = 42
_a.compareAndSet(42, 56)
assertEquals(56, clazz.b)
clazz.b = 77
_a.compareAndSet(77, 66)
assertEquals(66, _a.value)
assertEquals(66, clazz.b)
assertEquals("aaa", clazz.c) assertEquals("aaa", clazz.c)
clazz.c = "bbb" clazz.c = "bbb"
assertEquals("bbb", clazz.c) assertEquals("bbb", clazz.c)
} }
fun testDelegatedVariablesFlow() {
_a.lazySet(55)
assertEquals(55, _a.value)
assertEquals(55, a)
var aValue = a
}
fun test() { fun test() {
testDelegatedAtomicInt() testDelegatedAtomicInt()
testDelegatedAtomicLong() testDelegatedAtomicLong()
@@ -123,7 +131,8 @@ class DelegatedProperties {
testVolatileBoolean() testVolatileBoolean()
testVolatileLong() testVolatileLong()
testVolatileRef() testVolatileRef()
testScopedDelegatedProperties() testScopedVolatileProperties()
testDelegatedVariablesFlow()
} }
} }