kotlinx.atomicfu compiler plugin (JS): delegated properties supported (#4692)
* kotlinx.atomicfu compiler plugin (JS): delegated properties supported * NPE handling fixed * Scoped delegated properties supported
This commit is contained in:
+104
-17
@@ -29,6 +29,8 @@ private const val TRACE_BASE_TYPE = "TraceBase"
|
||||
private const val GETTER = "atomicfu\$getter"
|
||||
private const val SETTER = "atomicfu\$setter"
|
||||
private const val GET = "get"
|
||||
private const val GET_VALUE = "getValue"
|
||||
private const val SET_VALUE = "setValue"
|
||||
private const val ATOMIC_VALUE_FACTORY = "atomic"
|
||||
private const val TRACE = "Trace"
|
||||
private const val INVOKE = "invoke"
|
||||
@@ -101,6 +103,39 @@ class AtomicfuTransformer(private val context: IrPluginContext) {
|
||||
|
||||
private inner class AtomicTransformer : IrElementTransformer<IrFunction?> {
|
||||
|
||||
override fun visitProperty(declaration: IrProperty, data: IrFunction?): IrStatement {
|
||||
// Support transformation for delegated properties:
|
||||
if (declaration.isDelegated && declaration.backingField?.type?.isAtomicValueType() == true) {
|
||||
declaration.backingField?.let { delegateBackingField ->
|
||||
delegateBackingField.initializer?.let {
|
||||
val initializer = it.expression as IrCall
|
||||
when {
|
||||
initializer.isAtomicFieldGetter() -> {
|
||||
// val _a = atomic(0)
|
||||
// var a: Int by _a
|
||||
// Accessors of the delegated property `a` are implemented via the generated property `a$delegate`,
|
||||
// that is the copy of the original `_a`.
|
||||
// They should be delegated to the value of the original field `_a` instead of `a$delegate`.
|
||||
|
||||
// fun <get-a>() = a$delegate.value -> _a.value
|
||||
// fun <set-a>(value: Int) = { a$delegate.value = value } -> { _a.value = value }
|
||||
val originalField = initializer.getBackingField()
|
||||
declaration.transform(DelegatePropertyTransformer(originalField, initializer.dispatchReceiver, false), null)
|
||||
}
|
||||
initializer.isAtomicFactory() -> {
|
||||
// var a by atomic(77) -> var a: Int = 77
|
||||
it.expression = initializer.eraseAtomicFactory()
|
||||
?: error("Atomic factory was expected but found ${initializer.render()}")
|
||||
declaration.transform(DelegatePropertyTransformer(delegateBackingField, initializer.dispatchReceiver, true), null)
|
||||
}
|
||||
else -> error("Unexpected initializer of the delegated property: $initializer")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitProperty(declaration, data)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement {
|
||||
return super.visitFunction(declaration, declaration)
|
||||
}
|
||||
@@ -233,6 +268,46 @@ class AtomicfuTransformer(private val context: IrPluginContext) {
|
||||
return super.visitConstructorCall(expression, data)
|
||||
}
|
||||
|
||||
private inner class DelegatePropertyTransformer(
|
||||
val originalField: IrField,
|
||||
val receiver: IrExpression?,
|
||||
val isInitializedWithAtomicFactory: Boolean
|
||||
): IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
// Accessors of the delegated property have following signatures:
|
||||
|
||||
// public inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)
|
||||
// public inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T
|
||||
|
||||
// getValue/setValue should get and set the value of the originalField
|
||||
val name = expression.symbol.owner.name.asString()
|
||||
if (expression.symbol.isKotlinxAtomicfuPackage() && (name == GET_VALUE || name == SET_VALUE)) {
|
||||
val type = originalField.type.atomicToValueType()
|
||||
val isSetter = name == SET_VALUE
|
||||
val runtimeFunction = getRuntimeFunctionSymbol(name, type)
|
||||
val dispatchReceiver = if (isInitializedWithAtomicFactory) {
|
||||
val thisRef = expression.getValueArgument(0)!!
|
||||
if (thisRef.isConstNull()) null else thisRef
|
||||
} else {
|
||||
receiver
|
||||
}
|
||||
val fieldAccessors = listOf(
|
||||
context.buildFieldAccessor(originalField, dispatchReceiver, false),
|
||||
context.buildFieldAccessor(originalField, dispatchReceiver, true)
|
||||
)
|
||||
return buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = runtimeFunction,
|
||||
type = type,
|
||||
typeArguments = if (runtimeFunction.owner.typeParameters.size == 1) listOf(type) else emptyList(),
|
||||
valueArguments = if (isSetter) listOf(expression.getValueArgument(2)!!, fieldAccessors[0], fieldAccessors[1]) else
|
||||
fieldAccessors
|
||||
)
|
||||
}
|
||||
return super.visitCall(expression)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrExpression.getReceiverAccessors(parent: IrFunction?): List<IrExpression>? =
|
||||
when {
|
||||
this is IrCall -> getAccessors()
|
||||
@@ -297,27 +372,36 @@ class AtomicfuTransformer(private val context: IrPluginContext) {
|
||||
it.type.isAtomicArrayType() && symbol.owner.name.asString() == GET
|
||||
} ?: false
|
||||
|
||||
private fun IrCall.getAccessors(): List<IrExpression> =
|
||||
if (!isArrayElementGetter()) {
|
||||
val field = getBackingField()
|
||||
listOf(
|
||||
context.buildFieldAccessor(field, dispatchReceiver, false),
|
||||
context.buildFieldAccessor(field, dispatchReceiver, true)
|
||||
)
|
||||
} else {
|
||||
val index = getValueArgument(0)!!
|
||||
val arrayGetter = dispatchReceiver as IrCall
|
||||
val arrayField = arrayGetter.getBackingField()
|
||||
listOf(
|
||||
context.buildArrayElementAccessor(arrayField, arrayGetter, index, false),
|
||||
context.buildArrayElementAccessor(arrayField, arrayGetter, index, true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrStatement.isTrace() =
|
||||
this is IrCall && (isTraceInvoke() || isTraceAppend())
|
||||
|
||||
private fun IrCall.isTraceInvoke(): Boolean =
|
||||
symbol.isKotlinxAtomicfuPackage() &&
|
||||
symbol.owner.name.asString() == INVOKE &&
|
||||
symbol.owner.dispatchReceiverParameter?.type?.isTraceBaseType() == true
|
||||
symbol.owner.name.asString() == INVOKE &&
|
||||
symbol.owner.dispatchReceiverParameter?.type?.isTraceBaseType() == true
|
||||
|
||||
private fun IrCall.isTraceAppend(): Boolean =
|
||||
symbol.isKotlinxAtomicfuPackage() &&
|
||||
symbol.owner.name.asString() == APPEND &&
|
||||
symbol.owner.dispatchReceiverParameter?.type?.isTraceBaseType() == true
|
||||
symbol.owner.name.asString() == APPEND &&
|
||||
symbol.owner.dispatchReceiverParameter?.type?.isTraceBaseType() == true
|
||||
|
||||
private fun IrCall.getAccessors(): List<IrExpression> {
|
||||
val isArrayElement = isArrayElementGetter()
|
||||
val valueType = type.atomicToValueType()
|
||||
return listOf(
|
||||
context.buildAccessorLambda(this, valueType, false, isArrayElement),
|
||||
context.buildAccessorLambda(this, valueType, true, isArrayElement)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getRuntimeFunctionSymbol(name: String, type: IrType): IrSimpleFunctionSymbol {
|
||||
val functionName = when (name) {
|
||||
@@ -340,11 +424,11 @@ class AtomicfuTransformer(private val context: IrPluginContext) {
|
||||
|
||||
private fun IrCall.getAtomicFunctionName(): String =
|
||||
symbol.signature?.let { signature ->
|
||||
if (signature is IdSignature.AccessorSignature) signature.accessorSignature else signature.asPublic()
|
||||
}?.declarationFqName?.let { name ->
|
||||
if (name.substringBefore('.') in ATOMIC_VALUE_TYPES) {
|
||||
name.substringAfter('.')
|
||||
} else name
|
||||
signature.getDeclarationNameBySignature()?.let { name ->
|
||||
if (name.substringBefore('.') in ATOMIC_VALUE_TYPES) {
|
||||
name.substringAfter('.')
|
||||
} else name
|
||||
}
|
||||
} ?: error("Incorrect pattern of the atomic function name: ${symbol.owner.render()}")
|
||||
|
||||
private fun IrCall.eraseAtomicFactory() =
|
||||
@@ -415,6 +499,9 @@ class AtomicfuTransformer(private val context: IrPluginContext) {
|
||||
symbol.isKotlinxAtomicfuPackage() && symbol.owner.name.asString() == ATOMIC_ARRAY_OF_NULLS_FACTORY &&
|
||||
type.isAtomicArrayType()
|
||||
|
||||
private fun IrCall.isAtomicFieldGetter(): Boolean =
|
||||
type.isAtomicValueType() && symbol.owner.name.asString().startsWith("<get-")
|
||||
|
||||
private fun IrConstructorCall.isAtomicArrayConstructor(): Boolean = type.isAtomicArrayType()
|
||||
|
||||
private fun IrCall.isReentrantLockFactory(): Boolean =
|
||||
|
||||
+116
-68
@@ -5,12 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlinx.atomicfu.compiler.extensions
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder.buildValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -18,6 +17,8 @@ import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.*
|
||||
import org.jetbrains.kotlin.ir.util.IdSignature
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -120,11 +121,13 @@ internal fun buildGetValue(
|
||||
|
||||
internal fun IrPluginContext.buildConstNull() = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irBuiltIns.anyNType)
|
||||
|
||||
internal fun getterName(getterCall: IrCall) = "<get-${getterCall.symbol.owner.name.getFieldName()}>"
|
||||
internal fun setterName(getterCall: IrCall) = "<set-${getterCall.symbol.owner.name.getFieldName()}>"
|
||||
internal fun IrExpression.isConstNull() = this is IrConst<*> && this.kind.asString == "Null"
|
||||
|
||||
private fun Name.getFieldName() = "<get-(\\w+)>".toRegex().find(asString())?.groupValues?.get(1)
|
||||
?: error("Getter name ${this.asString()} does not match special name pattern <get-fieldName>")
|
||||
internal fun IrField.getterName() = "<get-${name.asString()}>"
|
||||
internal fun IrField.setterName() = "<set-${name.asString()}>"
|
||||
|
||||
internal fun String.getFieldName() = "<get-(\\w+)>".toRegex().find(this)?.groupValues?.get(1)
|
||||
?: error("Getter name $this does not match special name pattern <get-fieldName>")
|
||||
|
||||
internal fun IrFunctionAccessExpression.getValueArguments() =
|
||||
(0 until valueArgumentsCount).map { i ->
|
||||
@@ -162,82 +165,108 @@ private fun buildGetField(backingField: IrField, ownerClass: IrExpression?): IrG
|
||||
)
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.buildAccessorLambda(
|
||||
getter: IrCall,
|
||||
valueType: IrType,
|
||||
isSetter: Boolean,
|
||||
isArrayElement: Boolean
|
||||
): IrExpression {
|
||||
val getterCall = if (isArrayElement) getter.dispatchReceiver as IrCall else getter
|
||||
val type = if (isSetter) buildSetterType(valueType) else buildGetterType(valueType)
|
||||
val name = if (isSetter) setterName(getterCall) else getterName(getterCall)
|
||||
val returnType = if (isSetter) irBuiltIns.unitType else valueType
|
||||
val accessorFunction = irFactory.buildFun {
|
||||
private fun IrPluginContext.buildDefaultPropertyAccessor(name: String): IrSimpleFunction =
|
||||
irFactory.buildFun {
|
||||
startOffset = UNDEFINED_OFFSET
|
||||
endOffset = UNDEFINED_OFFSET
|
||||
this.origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||
this.name = Name.identifier(name)
|
||||
this.visibility = DescriptorVisibilities.LOCAL
|
||||
this.isInline = true
|
||||
this.returnType = returnType
|
||||
}.apply {
|
||||
val valueParameter = JsIrBuilder.buildValueParameter(this, name, 0, valueType)
|
||||
this.name = Name.identifier(name)
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.buildArrayElementAccessor(
|
||||
arrayField: IrField,
|
||||
arrayGetter: IrCall,
|
||||
index: IrExpression,
|
||||
isSetter: Boolean
|
||||
): IrExpression {
|
||||
val valueType = arrayField.type
|
||||
val functionType = if (isSetter) buildSetterType(valueType) else buildGetterType(valueType)
|
||||
val returnType = if (isSetter) irBuiltIns.unitType else valueType
|
||||
val name = if (isSetter) arrayField.setterName() else arrayField.getterName()
|
||||
val accessorFunction = buildDefaultPropertyAccessor(name).apply {
|
||||
val valueParameter = buildValueParameter(this, name, 0, valueType)
|
||||
this.valueParameters = if (isSetter) listOf(valueParameter) else emptyList()
|
||||
val body = if (isSetter) {
|
||||
if (isArrayElement) {
|
||||
val setSymbol = referenceFunction(referenceArrayClass(getterCall.type as IrSimpleType), SET)
|
||||
val elementIndex = getter.getValueArgument(0)!!.deepCopyWithVariables()
|
||||
buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = setSymbol,
|
||||
type = irBuiltIns.unitType,
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
valueArguments = listOf(elementIndex, valueParameter.capture())
|
||||
).apply {
|
||||
dispatchReceiver = getterCall
|
||||
body = irFactory.buildBlockBody(
|
||||
listOf(
|
||||
if (isSetter) {
|
||||
val setSymbol = referenceFunction(referenceArrayClass(arrayField.type as IrSimpleType), SET)
|
||||
buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = setSymbol,
|
||||
type = irBuiltIns.unitType,
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
valueArguments = listOf(index, valueParameter.capture())
|
||||
).apply {
|
||||
this.dispatchReceiver = arrayGetter
|
||||
}
|
||||
} else {
|
||||
val getField = buildGetField(arrayField, arrayGetter.dispatchReceiver)
|
||||
val getSymbol = referenceFunction(referenceArrayClass(arrayField.type as IrSimpleType), GET)
|
||||
buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = getSymbol,
|
||||
type = valueType,
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
valueArguments = listOf(index)
|
||||
).apply {
|
||||
dispatchReceiver = getField
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buildSetField(getterCall.getBackingField(), getterCall.dispatchReceiver, valueParameter.capture())
|
||||
}
|
||||
} else {
|
||||
val getField = buildGetField(getterCall.getBackingField(), getterCall.dispatchReceiver)
|
||||
if (isArrayElement) {
|
||||
val getSymbol = referenceFunction(referenceArrayClass(getterCall.type as IrSimpleType), GET)
|
||||
val elementIndex = getter.getValueArgument(0)!!.deepCopyWithVariables()
|
||||
buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = getSymbol,
|
||||
type = valueType,
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
valueArguments = listOf(elementIndex)
|
||||
).apply {
|
||||
dispatchReceiver = getField.deepCopyWithVariables()
|
||||
}
|
||||
} else {
|
||||
getField.deepCopyWithVariables()
|
||||
}
|
||||
}
|
||||
this.body = irFactory.buildBlockBody(listOf(body))
|
||||
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||
)
|
||||
)
|
||||
this.returnType = returnType
|
||||
}
|
||||
return IrFunctionExpressionImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
type,
|
||||
functionType,
|
||||
accessorFunction,
|
||||
IrStatementOrigin.LAMBDA
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrCall.getBackingField(): IrField {
|
||||
val correspondingPropertySymbol = symbol.owner.correspondingPropertySymbol!!
|
||||
return correspondingPropertySymbol.owner.backingField!!
|
||||
internal fun IrPluginContext.buildFieldAccessor(
|
||||
field: IrField,
|
||||
dispatchReceiver: IrExpression?,
|
||||
isSetter: Boolean
|
||||
): IrExpression {
|
||||
val valueType = field.type
|
||||
val functionType = if (isSetter) buildSetterType(valueType) else buildGetterType(valueType)
|
||||
val returnType = if (isSetter) irBuiltIns.unitType else valueType
|
||||
val name = if (isSetter) field.setterName() else field.getterName()
|
||||
val accessorFunction = buildDefaultPropertyAccessor(name).apply {
|
||||
val valueParameter = buildValueParameter(this, name, 0, valueType)
|
||||
valueParameters = if (isSetter) listOf(valueParameter) else emptyList()
|
||||
body = irFactory.buildBlockBody(
|
||||
listOf(
|
||||
if (isSetter) {
|
||||
buildSetField(field, dispatchReceiver, valueParameter.capture())
|
||||
} else {
|
||||
buildGetField(field, dispatchReceiver)
|
||||
}
|
||||
)
|
||||
)
|
||||
this.returnType = returnType
|
||||
}
|
||||
return IrFunctionExpressionImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
functionType,
|
||||
accessorFunction,
|
||||
IrStatementOrigin.LAMBDA
|
||||
)
|
||||
}
|
||||
|
||||
internal fun IrCall.getBackingField(): IrField =
|
||||
symbol.owner.correspondingPropertySymbol?.let { propertySymbol ->
|
||||
propertySymbol.owner.backingField ?: error("Property expected to have backing field")
|
||||
} ?: error("Atomic property accessor ${this.render()} expected to have non-null correspondingPropertySymbol")
|
||||
|
||||
internal fun IrPluginContext.referencePackageFunction(
|
||||
packageName: String,
|
||||
name: String,
|
||||
predicate: (IrFunctionSymbol) -> Boolean = { true }
|
||||
) = try {
|
||||
): IrSimpleFunctionSymbol = try {
|
||||
referenceFunctions(FqName("$packageName.$name")).single(predicate)
|
||||
} catch (e: RuntimeException) {
|
||||
error("Exception while looking for the function `$name` in package `$packageName`: ${e.message}")
|
||||
@@ -253,13 +282,32 @@ internal fun IrPluginContext.referenceFunction(classSymbol: IrClassSymbol, funct
|
||||
}
|
||||
|
||||
private fun IrPluginContext.referenceArrayClass(irType: IrSimpleType): IrClassSymbol {
|
||||
val afuClassId = (irType.classifier.signature!!.asPublic())!!.declarationFqName
|
||||
val classId = FqName("$KOTLIN.${AFU_ARRAY_CLASSES[afuClassId]!!}")
|
||||
return referenceClass(classId)!!
|
||||
val jsArrayName = irType.getArrayClassFqName()
|
||||
return referenceClass(jsArrayName) ?: error("Array class $jsArrayName was not found in the context")
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.getArrayConstructorSymbol(irType: IrSimpleType, predicate: (IrConstructorSymbol) -> Boolean = { true }): IrConstructorSymbol {
|
||||
val afuClassId = (irType.classifier.signature!!.asPublic())!!.declarationFqName
|
||||
val classId = FqName("$KOTLIN.${AFU_ARRAY_CLASSES[afuClassId]!!}")
|
||||
return referenceConstructors(classId).single(predicate)
|
||||
internal fun IrPluginContext.getArrayConstructorSymbol(
|
||||
irType: IrSimpleType,
|
||||
predicate: (IrConstructorSymbol) -> Boolean = { true }
|
||||
): IrConstructorSymbol {
|
||||
val jsArrayName = irType.getArrayClassFqName()
|
||||
return try {
|
||||
referenceConstructors(jsArrayName).single(predicate)
|
||||
} catch (e: RuntimeException) {
|
||||
error("Array constructor $jsArrayName matching the predicate was not found in the context")
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleType.getArrayClassFqName(): FqName =
|
||||
classifier.signature?.let { signature ->
|
||||
signature.getDeclarationNameBySignature().let { name ->
|
||||
AFU_ARRAY_CLASSES[name]?.let { jsArrayName ->
|
||||
FqName("$KOTLIN.$jsArrayName")
|
||||
}
|
||||
}
|
||||
} ?: error("Unexpected array type ${this.render()}")
|
||||
|
||||
internal fun IdSignature.getDeclarationNameBySignature(): String? {
|
||||
val commonSignature = if (this is IdSignature.AccessorSignature) accessorSignature else asPublic()
|
||||
return commonSignature?.declarationFqName
|
||||
}
|
||||
+6
@@ -43,6 +43,12 @@ public class AtomicfuJsIrTestGenerated extends AbstractAtomicfuJsIrTest {
|
||||
runTest("plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("DelegatedPropertiesTest.kt")
|
||||
public void testDelegatedPropertiesTest() throws Exception {
|
||||
runTest("plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ExtensionsTest.kt")
|
||||
public void testExtensionsTest() throws Exception {
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
private val _topLevelInt = atomic(42)
|
||||
var topLevelInt: Int by _topLevelInt
|
||||
|
||||
var vIntTopLevel by atomic(55)
|
||||
|
||||
class DelegatedProperties {
|
||||
private val _a = atomic(42)
|
||||
var a: Int by _a
|
||||
|
||||
private val _l = atomic(55555555555)
|
||||
var l: Long by _l
|
||||
|
||||
private val _b = atomic(false)
|
||||
var b: Boolean by _b
|
||||
|
||||
private val _ref = atomic(A(B(77)))
|
||||
var ref: A by _ref
|
||||
|
||||
var vInt by atomic(77)
|
||||
|
||||
var vLong by atomic(777777777)
|
||||
|
||||
var vBoolean by atomic(false)
|
||||
|
||||
var vRef by atomic(A(B(77)))
|
||||
|
||||
class A (val b: B)
|
||||
class B (val n: Int)
|
||||
|
||||
fun testDelegatedAtomicInt() {
|
||||
assertEquals(42, a)
|
||||
_a.compareAndSet(42, 56)
|
||||
assertEquals(56, a)
|
||||
a = 77
|
||||
_a.compareAndSet(77, 66)
|
||||
assertEquals(66, _a.value)
|
||||
assertEquals(66, a)
|
||||
}
|
||||
|
||||
fun testDelegatedAtomicLong() {
|
||||
assertEquals(55555555555, l)
|
||||
_l.getAndIncrement()
|
||||
assertEquals(55555555556, l)
|
||||
l = 7777777777777
|
||||
assertTrue(_l.compareAndSet(7777777777777, 66666666666))
|
||||
assertEquals(66666666666, _l.value)
|
||||
assertEquals(66666666666, l)
|
||||
}
|
||||
|
||||
fun testDelegatedAtomicBoolean() {
|
||||
assertEquals(false, b)
|
||||
_b.lazySet(true)
|
||||
assertEquals(true, b)
|
||||
b = false
|
||||
assertTrue(_b.compareAndSet(false, true))
|
||||
assertEquals(true, _b.value)
|
||||
assertEquals(true, b)
|
||||
}
|
||||
|
||||
fun testDelegatedAtomicRef() {
|
||||
assertEquals(77, ref.b.n)
|
||||
_ref.lazySet(A(B(66)))
|
||||
assertEquals(66, ref.b.n)
|
||||
assertTrue(_ref.compareAndSet(_ref.value, A(B(56))))
|
||||
assertEquals(56, ref.b.n)
|
||||
ref = A(B(99))
|
||||
assertEquals(99, _ref.value.b.n)
|
||||
}
|
||||
|
||||
fun testVolatileInt() {
|
||||
assertEquals(77, vInt)
|
||||
vInt = 55
|
||||
assertEquals(110, vInt * 2)
|
||||
}
|
||||
|
||||
fun testVolatileLong() {
|
||||
assertEquals(777777777, vLong)
|
||||
vLong = 55
|
||||
assertEquals(55, vLong)
|
||||
}
|
||||
|
||||
fun testVolatileBoolean() {
|
||||
assertEquals(false, vBoolean)
|
||||
vBoolean = true
|
||||
assertEquals(true, vBoolean)
|
||||
}
|
||||
|
||||
fun testVolatileRef() {
|
||||
assertEquals(77, vRef.b.n)
|
||||
vRef = A(B(99))
|
||||
assertEquals(99, vRef.b.n)
|
||||
}
|
||||
|
||||
inner class D {
|
||||
var b: Int by _a
|
||||
}
|
||||
|
||||
fun testScopedDelegatedProperties() {
|
||||
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)
|
||||
}
|
||||
|
||||
fun test() {
|
||||
testDelegatedAtomicInt()
|
||||
testDelegatedAtomicLong()
|
||||
testDelegatedAtomicBoolean()
|
||||
testDelegatedAtomicRef()
|
||||
testVolatileInt()
|
||||
testVolatileBoolean()
|
||||
testVolatileLong()
|
||||
testVolatileRef()
|
||||
testScopedDelegatedProperties()
|
||||
}
|
||||
}
|
||||
|
||||
fun testTopLevelDelegatedProperties() {
|
||||
assertEquals(42, topLevelInt)
|
||||
_topLevelInt.compareAndSet(42, 56)
|
||||
assertEquals(56, topLevelInt)
|
||||
topLevelInt = 77
|
||||
_topLevelInt.compareAndSet(77, 66)
|
||||
assertEquals(66, _topLevelInt.value)
|
||||
assertEquals(66, topLevelInt)
|
||||
|
||||
assertEquals(55, vIntTopLevel)
|
||||
vIntTopLevel = 70
|
||||
assertEquals(140, vIntTopLevel * 2)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = DelegatedProperties()
|
||||
testClass.test()
|
||||
//testTopLevelDelegatedProperties()
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user