diff --git a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt index 6197f4c12d8..6f8bf802925 100644 --- a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt +++ b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/codegen/BytecodeListingTextCollectingVisitor.kt @@ -239,7 +239,6 @@ class BytecodeListingTextCollectingVisitor( val fieldDeclaration = Declaration("field $fieldSignature$name: $type") declarationsInsideClass.add(fieldDeclaration) handleModifiers(ModifierTarget.FIELD, access) - if (access and Opcodes.ACC_VOLATILE != 0) addModifier("volatile", fieldDeclaration.annotations) return object : FieldVisitor(Opcodes.API_VERSION) { override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? = diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index e032c7e3138..56f90cff742 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListin import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJsIrTest +import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJvmIrTest fun main(args: Array) { System.setProperty("java.awt.headless", "true") @@ -382,6 +383,16 @@ fun main(args: Array) { } } + testGroup( + "plugins/atomicfu/atomicfu-compiler/test", + "plugins/atomicfu/atomicfu-compiler/testData", + testRunnerMethodName = "runTest0" + ) { + testClass { + model("box/") + } + } + testGroup("plugins/allopen/tests-gen", "plugins/allopen/testData") { testClass { model("bytecodeListing", excludedPattern = excludedFirTestdataPattern) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 1531c93645e..e4d1ec8ec7d 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -7213,6 +7213,17 @@ + + + + + + + + + + + @@ -7245,6 +7256,21 @@ + + + + + + + + + + + + + + + diff --git a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts index 18fc560f22f..5ed97455e17 100644 --- a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts +++ b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts @@ -17,14 +17,16 @@ val testJsRuntime by configurations.creating { } } -val atomicfuClasspath by configurations.creating { +val atomicfuJsClasspath by configurations.creating { attributes { attribute(KotlinPlatformType.attribute, KotlinPlatformType.js) attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir) } } -val atomicfuRuntimeForTests by configurations.creating { +val atomicfuJvmClasspath by configurations.creating + +val atomicfuJsIrRuntimeForTests by configurations.creating { attributes { attribute(KotlinPlatformType.attribute, KotlinPlatformType.js) attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir) @@ -45,10 +47,14 @@ dependencies { compileOnly(project(":compiler:frontend")) compileOnly(project(":compiler:backend")) compileOnly(project(":compiler:ir.backend.common")) + compileOnly(project(":js:js.frontend")) compileOnly(project(":js:js.translator")) compileOnly(project(":compiler:backend.js")) + compileOnly(project(":compiler:backend.jvm")) + compileOnly(project(":compiler:ir.tree")) + compileOnly(kotlinStdlib()) testApi(projectTests(":compiler:tests-common")) @@ -60,6 +66,7 @@ dependencies { testImplementation(projectTests(":js:js.tests")) testApi(commonDependency("junit:junit")) + testApi(project(":kotlin-test:kotlin-test-jvm")) testRuntimeOnly(kotlinStdlib()) testRuntimeOnly(project(":kotlin-reflect")) @@ -67,9 +74,9 @@ dependencies { testRuntimeOnly(project(":compiler:backend-common")) testRuntimeOnly(commonDependency("org.fusesource.jansi", "jansi")) - atomicfuClasspath("org.jetbrains.kotlinx:atomicfu-js:0.16.3") { isTransitive = false } - - atomicfuRuntimeForTests(project(":atomicfu-runtime")) { isTransitive = false } + atomicfuJsClasspath("org.jetbrains.kotlinx:atomicfu-js:0.17.1") { isTransitive = false } + atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false } + atomicfuJsIrRuntimeForTests(project(":atomicfu-runtime")) { isTransitive = false } embedded(project(":atomicfu-runtime")) { attributes { @@ -80,6 +87,8 @@ dependencies { isTransitive = false } + testImplementation("org.jetbrains.kotlinx:atomicfu:0.17.1") + testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2") } @@ -96,10 +105,11 @@ testsJar() projectTest(jUnitMode = JUnitMode.JUnit5) { useJUnitPlatform() workingDir = rootDir - dependsOn(atomicfuRuntimeForTests) + dependsOn(atomicfuJsIrRuntimeForTests) doFirst { - systemProperty("atomicfuRuntimeForTests.classpath", atomicfuRuntimeForTests.asPath) - systemProperty("atomicfu.classpath", atomicfuClasspath.asPath) + systemProperty("atomicfuJsIrRuntimeForTests.classpath", atomicfuJsIrRuntimeForTests.asPath) + systemProperty("atomicfuJs.classpath", atomicfuJsClasspath.asPath) + systemProperty("atomicfuJvm.classpath", atomicfuJvmClasspath.asPath) } setUpJsIrBoxTests() } diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/TransformerUtil.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt similarity index 73% rename from plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/TransformerUtil.kt rename to plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt index 6cca01a4ed7..1b17468df61 100644 --- a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/TransformerUtil.kt +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/TransformerUtil.kt @@ -1,27 +1,25 @@ /* - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlinx.atomicfu.compiler.extensions +package org.jetbrains.kotlinx.atomicfu.compiler.backend -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.backend.common.extensions.* +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder.buildValueParameter -import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* 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 +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.util.capitalizeDecapitalize.* private const val KOTLIN = "kotlin" private const val GET = "get" @@ -262,6 +260,10 @@ internal fun IrCall.getBackingField(): IrField = propertySymbol.owner.backingField ?: error("Property expected to have backing field") } ?: error("Atomic property accessor ${this.render()} expected to have non-null correspondingPropertySymbol") +internal fun IrCall.getCorrespondingProperty(): IrProperty = + symbol.owner.correspondingPropertySymbol?.owner + ?: error("Atomic property accessor ${this.render()} expected to have non-null correspondingPropertySymbol") + internal fun IrPluginContext.referencePackageFunction( packageName: String, name: String, @@ -298,6 +300,100 @@ internal fun IrPluginContext.getArrayConstructorSymbol( } } +internal fun IrPluginContext.addProperty( + field: IrField, + parent: IrDeclarationContainer, + visibility: DescriptorVisibility, + isStatic: Boolean +): IrProperty = + irFactory.buildProperty { + name = field.name + this.visibility = visibility // equal to the atomic property visibility + }.apply { + backingField = field + this.parent = parent + if (!isStatic) { + addDefaultGetter(this, field.parent as IrClass) + } else { + addStaticGetter(this) + } + parent.declarations.add(this) + } + +internal fun IrPluginContext.addDefaultGetter(property: IrProperty, parentClass: IrDeclarationContainer) { + val field = property.backingField!! + property.addGetter { + origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR + visibility = property.visibility + returnType = field.type + }.apply { + dispatchReceiverParameter = (parentClass as? IrClass)?.thisReceiver?.deepCopyWithSymbols(this) + body = factory.createBlockBody( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( + IrReturnImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + irBuiltIns.nothingType, + symbol, + IrGetFieldImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + field.symbol, + field.type, + dispatchReceiverParameter?.let { + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + it.type, + it.symbol + ) + } + ) + ) + ) + ) + } +} + +internal fun IrPluginContext.addStaticGetter(property: IrProperty) { + val field = property.backingField!! + property.addGetter { + origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR + visibility = property.visibility + returnType = field.type + }.apply { + dispatchReceiverParameter = null + body = factory.createBlockBody( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( + IrReturnImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + irBuiltIns.nothingType, + symbol, + IrGetFieldImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + symbol = field.symbol, + type = field.type, + receiver = null + ) + ) + ) + ) + } +} + +internal fun IrPluginContext.buildClassInstance(irClass: IrClass, parentClass: IrDeclarationContainer) = + irFactory.buildField { + this.name = Name.identifier(irClass.name.asString().decapitalizeAsciiOnly()) + type = irClass.defaultType + isFinal = true + isStatic = true + }.apply { + initializer = IrExpressionBodyImpl( + IrConstructorCallImpl.fromSymbolOwner( + irClass.defaultType, + irClass.primaryConstructor!!.symbol + ) + ) + this.parent = parentClass + } + private fun IrSimpleType.getArrayClassFqName(): FqName = classifier.signature?.let { signature -> signature.getDeclarationNameBySignature().let { name -> @@ -310,4 +406,5 @@ private fun IrSimpleType.getArrayClassFqName(): FqName = internal fun IdSignature.getDeclarationNameBySignature(): String? { val commonSignature = if (this is IdSignature.AccessorSignature) accessorSignature else asPublic() return commonSignature?.declarationFqName -} \ No newline at end of file +} + diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/js/AtomicfuJsIrTransformer.kt similarity index 97% rename from plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuTransformer.kt rename to plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/js/AtomicfuJsIrTransformer.kt index 1b929a1da09..7bc4858af16 100644 --- a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuTransformer.kt +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/js/AtomicfuJsIrTransformer.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlinx.atomicfu.compiler.extensions +package org.jetbrains.kotlinx.atomicfu.compiler.backend.js import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.ir.* @@ -19,6 +19,11 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.expressions.IrTypeOperator.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.platform.js.isJs +import org.jetbrains.kotlinx.atomicfu.compiler.backend.* +import org.jetbrains.kotlinx.atomicfu.compiler.backend.buildCall +import org.jetbrains.kotlinx.atomicfu.compiler.backend.buildGetterType +import org.jetbrains.kotlinx.atomicfu.compiler.backend.buildSetterType +import org.jetbrains.kotlinx.atomicfu.compiler.backend.getBackingField private const val AFU_PKG = "kotlinx.atomicfu" private const val LOCKS = "locks" @@ -38,7 +43,7 @@ private const val APPEND = "append" private const val ATOMIC_ARRAY_OF_NULLS_FACTORY = "atomicArrayOfNulls" private const val REENTRANT_LOCK_FACTORY = "reentrantLock" -class AtomicfuTransformer(private val context: IrPluginContext) { +class AtomicfuJsIrTransformer(private val context: IrPluginContext) { private val irBuiltIns = context.irBuiltIns @@ -203,7 +208,7 @@ class AtomicfuTransformer(private val context: IrPluginContext) { receiver.getReceiverAccessors(data)?.let { accessors -> val declaration = expression.symbol.owner val transformedAtomicExtension = getDeclarationWithAccessorParameters(declaration, declaration.extensionReceiverParameter) - return buildCall( + val irCall = buildCall( expression.startOffset, expression.endOffset, target = transformedAtomicExtension.symbol, @@ -212,6 +217,7 @@ class AtomicfuTransformer(private val context: IrPluginContext) { ).apply { dispatchReceiver = expression.dispatchReceiver } + return super.visitCall(irCall, data) } } } @@ -301,7 +307,7 @@ class AtomicfuTransformer(private val context: IrPluginContext) { 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 + fieldAccessors ) } return super.visitCall(expression) diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicSymbols.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicSymbols.kt new file mode 100644 index 00000000000..25a9059fbfb --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicSymbols.kt @@ -0,0 +1,605 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm + +import org.jetbrains.kotlin.backend.common.ir.* +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.builders.declarations.* +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.* +import org.jetbrains.kotlin.ir.expressions.* +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.* +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlinx.atomicfu.compiler.backend.* + +// Contains IR declarations needed by the atomicfu plugin. +class AtomicSymbols( + val irBuiltIns: IrBuiltIns, + private val moduleFragment: IrModuleFragment +) { + private val irFactory: IrFactory = IrFactoryImpl + private val javaLang: IrPackageFragment = createPackage("java.lang") + private val javaUtilConcurrent: IrPackageFragment = createPackage("java.util.concurrent.atomic") + private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm") + val javaLangClass: IrClassSymbol = createClass(javaLang, "Class", ClassKind.CLASS, Modality.FINAL) + + // AtomicIntegerFieldUpdater + val atomicIntFieldUpdaterClass: IrClassSymbol = + createClass(javaUtilConcurrent, "AtomicIntegerFieldUpdater", ClassKind.CLASS, Modality.FINAL) + + val atomicIntNewUpdater: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction( + name = "newUpdater", + returnType = atomicIntFieldUpdaterClass.defaultType, + isStatic = true + ).apply { + addValueParameter("tclass", javaLangClass.starProjectedType) + addValueParameter("fieldName", irBuiltIns.stringType) + }.symbol + + val atomicIntGet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "get", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicIntSet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", irBuiltIns.intType) + }.symbol + + val atomicIntCompareAndSet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("expect", irBuiltIns.intType) + addValueParameter("update", irBuiltIns.intType) + }.symbol + + val atomicIntAddAndGet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("delta", irBuiltIns.intType) + }.symbol + + val atomicIntGetAndAdd: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("delta", irBuiltIns.intType) + }.symbol + + val atomicIntIncrementAndGet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicIntGetAndIncrement: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicIntDecrementAndGet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicIntGetAndDecrement: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicIntLazySet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", irBuiltIns.intType) + }.symbol + + val atomicIntGetAndSet: IrSimpleFunctionSymbol = + atomicIntFieldUpdaterClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.intType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", irBuiltIns.intType) + }.symbol + + // AtomicLongFieldUpdater + val atomicLongFieldUpdaterClass: IrClassSymbol = + createClass(javaUtilConcurrent, "AtomicLongFieldUpdater", ClassKind.CLASS, Modality.FINAL) + + val atomicLongNewUpdater: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction( + name = "newUpdater", + returnType = atomicLongFieldUpdaterClass.defaultType, + isStatic = true + ).apply { + addValueParameter("tclass", javaLangClass.starProjectedType) + addValueParameter("fieldName", irBuiltIns.stringType) + }.symbol + + val atomicLongGet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "get", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicLongSet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", irBuiltIns.longType) + }.symbol + + val atomicLongCompareAndSet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("expect", irBuiltIns.longType) + addValueParameter("update", irBuiltIns.longType) + }.symbol + + val atomicLongAddAndGet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("delta", irBuiltIns.longType) + }.symbol + + val atomicLongGetAndAdd: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("delta", irBuiltIns.longType) + }.symbol + + val atomicLongIncrementAndGet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicLongGetAndIncrement: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicLongDecrementAndGet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicLongGetAndDecrement: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + }.symbol + + val atomicLongLazySet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", irBuiltIns.longType) + }.symbol + + val atomicLongGetAndSet: IrSimpleFunctionSymbol = + atomicLongFieldUpdaterClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.longType).apply { + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", irBuiltIns.longType) + }.symbol + + // AtomicReferenceFieldUpdater + val atomicRefFieldUpdaterClass: IrClassSymbol = + createClass(javaUtilConcurrent, "AtomicReferenceFieldUpdater", ClassKind.CLASS, Modality.FINAL) + + val atomicRefNewUpdater: IrSimpleFunctionSymbol = + atomicRefFieldUpdaterClass.owner.addFunction( + name = "newUpdater", + returnType = atomicRefFieldUpdaterClass.defaultType, + isStatic = true + ).apply { + addValueParameter("tclass", javaLangClass.starProjectedType) + addValueParameter("vclass", javaLangClass.starProjectedType) + addValueParameter("fieldName", irBuiltIns.stringType) + }.symbol + + val atomicRefGet: IrSimpleFunctionSymbol = + atomicRefFieldUpdaterClass.owner.addFunction(name = "get", returnType = irBuiltIns.anyNType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("obj", irBuiltIns.anyType) + returnType = valueType.defaultType + }.symbol + + val atomicRefSet: IrSimpleFunctionSymbol = + atomicRefFieldUpdaterClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", valueType.defaultType) + }.symbol + + val atomicRefCompareAndSet: IrSimpleFunctionSymbol = + atomicRefFieldUpdaterClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("expect", valueType.defaultType) + addValueParameter("update", valueType.defaultType) + }.symbol + + val atomicRefLazySet: IrSimpleFunctionSymbol = + atomicRefFieldUpdaterClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", valueType.defaultType) + }.symbol + + val atomicRefGetAndSet: IrSimpleFunctionSymbol = + atomicRefFieldUpdaterClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.anyNType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("obj", irBuiltIns.anyType) + addValueParameter("newValue", valueType.defaultType) + returnType = valueType.defaultType + }.symbol + + // AtomicIntegerArray + val atomicIntArrayClass: IrClassSymbol = + createClass(javaUtilConcurrent, "AtomicIntegerArray", ClassKind.CLASS, Modality.FINAL) + + val atomicIntArrayConstructor: IrConstructorSymbol = atomicIntArrayClass.owner.addConstructor().apply { + addValueParameter("length", irBuiltIns.intType) + }.symbol + + val atomicIntArrayGet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "get", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicIntArraySet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", irBuiltIns.intType) + }.symbol + + val atomicIntArrayCompareAndSet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("expect", irBuiltIns.intType) + addValueParameter("update", irBuiltIns.intType) + }.symbol + + val atomicIntArrayAddAndGet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("delta", irBuiltIns.intType) + }.symbol + + val atomicIntArrayGetAndAdd: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("delta", irBuiltIns.intType) + }.symbol + + val atomicIntArrayIncrementAndGet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicIntArrayGetAndIncrement: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicIntArrayDecrementAndGet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicIntArrayGetAndDecrement: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicIntArrayLazySet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", irBuiltIns.intType) + }.symbol + + val atomicIntArrayGetAndSet: IrSimpleFunctionSymbol = + atomicIntArrayClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.intType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", irBuiltIns.intType) + }.symbol + + // AtomicLongArray + val atomicLongArrayClass: IrClassSymbol = + createClass(javaUtilConcurrent, "AtomicLongArray", ClassKind.CLASS, Modality.FINAL) + + val atomicLongArrayConstructor: IrConstructorSymbol = atomicLongArrayClass.owner.addConstructor().apply { + addValueParameter("length", irBuiltIns.intType) + }.symbol + + val atomicLongArrayGet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "get", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicLongArraySet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", irBuiltIns.longType) + }.symbol + + val atomicLongArrayCompareAndSet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("expect", irBuiltIns.longType) + addValueParameter("update", irBuiltIns.longType) + }.symbol + + val atomicLongArrayAddAndGet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("delta", irBuiltIns.longType) + }.symbol + + val atomicLongArrayGetAndAdd: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("delta", irBuiltIns.longType) + }.symbol + + val atomicLongArrayIncrementAndGet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicLongArrayGetAndIncrement: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicLongArrayDecrementAndGet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicLongArrayGetAndDecrement: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + }.symbol + + val atomicLongArrayLazySet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", irBuiltIns.longType) + }.symbol + + val atomicLongArrayGetAndSet: IrSimpleFunctionSymbol = + atomicLongArrayClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.longType).apply { + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", irBuiltIns.longType) + }.symbol + + // AtomicReferenceArray + val atomicRefArrayClass: IrClassSymbol = + createClass(javaUtilConcurrent, "AtomicReferenceArray", ClassKind.CLASS, Modality.FINAL) + + val atomicRefArrayConstructor: IrConstructorSymbol = atomicRefArrayClass.owner.addConstructor().apply { + addValueParameter("length", irBuiltIns.intType) + }.symbol + + val atomicRefArrayGet: IrSimpleFunctionSymbol = + atomicRefArrayClass.owner.addFunction(name = "get", returnType = irBuiltIns.anyNType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("i", irBuiltIns.intType) + returnType = valueType.defaultType + }.symbol + + val atomicRefArraySet: IrSimpleFunctionSymbol = + atomicRefArrayClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", valueType.defaultType) + }.symbol + + val atomicRefArrayCompareAndSet: IrSimpleFunctionSymbol = + atomicRefArrayClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("i", irBuiltIns.intType) + addValueParameter("expect", valueType.defaultType) + addValueParameter("update", valueType.defaultType) + }.symbol + + val atomicRefArrayLazySet: IrSimpleFunctionSymbol = + atomicRefArrayClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", valueType.defaultType) + }.symbol + + val atomicRefArrayGetAndSet: IrSimpleFunctionSymbol = + atomicRefArrayClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.anyNType).apply { + val valueType = addTypeParameter("T", irBuiltIns.anyNType) + addValueParameter("i", irBuiltIns.intType) + addValueParameter("newValue", valueType.defaultType) + returnType = valueType.defaultType + }.symbol + + private val VALUE_TYPE_TO_ATOMIC_ARRAY_CLASS: Map = mapOf( + irBuiltIns.intType to atomicIntArrayClass, + irBuiltIns.booleanType to atomicIntArrayClass, + irBuiltIns.longType to atomicLongArrayClass, + irBuiltIns.anyNType to atomicRefArrayClass + ) + + private val ATOMIC_ARRAY_TYPES: Set = setOf( + atomicIntArrayClass, + atomicLongArrayClass, + atomicRefArrayClass + ) + + private val ATOMIC_FIELD_UPDATER_TYPES: Set = setOf( + atomicIntFieldUpdaterClass, + atomicLongFieldUpdaterClass, + atomicRefFieldUpdaterClass + ) + + fun getJucaAFUClass(valueType: IrType): IrClassSymbol = + when { + valueType.isInt() -> atomicIntFieldUpdaterClass + valueType.isLong() -> atomicLongFieldUpdaterClass + valueType.isBoolean() -> atomicIntFieldUpdaterClass + else -> atomicRefFieldUpdaterClass + } + + fun getFieldUpdaterType(valueType: IrType) = getJucaAFUClass(valueType).defaultType + + fun getAtomicArrayClassByAtomicfuArrayType(atomicfuArrayType: IrType): IrClassSymbol = + when (atomicfuArrayType.classFqName?.shortName()?.asString()) { + "AtomicIntArray" -> atomicIntArrayClass + "AtomicLongArray" -> atomicLongArrayClass + "AtomicBooleanArray" -> atomicIntArrayClass + "AtomicArray" -> atomicRefArrayClass + else -> error("Unexpected atomicfu array type ${atomicfuArrayType.render()}") + } + + fun getAtomicArrayClassByValueType(valueType: IrType): IrClassSymbol = + VALUE_TYPE_TO_ATOMIC_ARRAY_CLASS[valueType] + ?: error("No corresponding atomic array class found for this value type ${valueType.render()} ") + + fun getAtomicArrayType(valueType: IrType) = getAtomicArrayClassByValueType(valueType).defaultType + + fun isAtomicArrayHandlerType(valueType: IrType) = valueType.classOrNull in ATOMIC_ARRAY_TYPES + + fun isAtomicFieldUpdaterType(valueType: IrType) = valueType.classOrNull in ATOMIC_FIELD_UPDATER_TYPES + + fun getNewUpdater(atomicUpdaterClassSymbol: IrClassSymbol): IrSimpleFunctionSymbol = + atomicUpdaterClassSymbol.getSimpleFunction("newUpdater") ?: error("No newUpdater function was found for ${atomicUpdaterClassSymbol.owner.render()} ") + + fun getAtomicArrayConstructor(atomicArrayClassSymbol: IrClassSymbol): IrConstructorSymbol = + atomicArrayClassSymbol.constructors.firstOrNull() ?: error("No constructors declared for ${atomicArrayClassSymbol.owner.render()} ") + + fun getAtomicHandlerFunctionSymbol(atomicHandlerClass: IrClassSymbol, name: String): IrSimpleFunctionSymbol = + when (name) { + "", "getValue" -> atomicHandlerClass.getSimpleFunction("get") + "", "setValue" -> atomicHandlerClass.getSimpleFunction("set") + else -> atomicHandlerClass.getSimpleFunction(name) + } ?: error("No $name function found in $name") + + val kotlinKClassJava: IrPropertySymbol = irFactory.buildProperty { + name = Name.identifier("java") + }.apply { + parent = kotlinJvm + addGetter().apply { + addExtensionReceiver(irBuiltIns.kClassClass.starProjectedType) + returnType = javaLangClass.defaultType + } + }.symbol + + fun kClassToJavaClass(kClassReference: IrExpression): IrCall = + buildIrGet(javaLangClass.starProjectedType, null, kotlinKClassJava.owner.getter!!.symbol).apply { + extensionReceiver = kClassReference + } + + fun javaClassReference(classType: IrType): IrCall = kClassToJavaClass(kClassReference(classType)) + + private fun kClassReference(classType: IrType): IrClassReferenceImpl = + IrClassReferenceImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, irBuiltIns.kClassClass.starProjectedType, irBuiltIns.kClassClass, classType + ) + + fun function0Type(returnType: IrType) = buildFunctionSimpleType( + irBuiltIns.functionN(0).symbol, + listOf(returnType) + ) + + fun function1Type(argType: IrType, returnType: IrType) = buildFunctionSimpleType( + irBuiltIns.functionN(1).symbol, + listOf(argType, returnType) + ) + + val invoke0Symbol = irBuiltIns.functionN(0).getSimpleFunction("invoke")!! + val invoke1Symbol = irBuiltIns.functionN(1).getSimpleFunction("invoke")!! + + private fun buildIrGet( + type: IrType, + receiver: IrExpression?, + getterSymbol: IrFunctionSymbol + ): IrCall = IrCallImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + type, + getterSymbol as IrSimpleFunctionSymbol, + typeArgumentsCount = getterSymbol.owner.typeParameters.size, + valueArgumentsCount = 0, + origin = IrStatementOrigin.GET_PROPERTY + ).apply { + dispatchReceiver = receiver + } + + private val volatileConstructor = buildAnnotationConstructor(buildClass(JvmNames.VOLATILE_ANNOTATION_FQ_NAME, ClassKind.ANNOTATION_CLASS, kotlinJvm)) + val volatileAnnotationConstructorCall = + IrConstructorCallImpl.fromSymbolOwner(volatileConstructor.returnType, volatileConstructor.symbol) + + fun buildClassWithPrimaryConstructor( + name: String, + parent: IrDeclarationContainer + ) = buildClass( + FqName(name), + ClassKind.CLASS, + parent + ).apply { + val irClass = this + addConstructor { + isPrimary = true + }.apply { + body = createBuilder(symbol).irBlockBody(startOffset, endOffset) { + +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) + +IrInstanceInitializerCallImpl(startOffset, endOffset, irClass.symbol, context.irBuiltIns.unitType) + } + } + } + + fun buildClass( + fqName: FqName, + classKind: ClassKind, + parent: IrDeclarationContainer + ): IrClass = irFactory.buildClass { + name = fqName.shortName() + kind = classKind + }.apply { + val irClass = this + this.parent = parent + parent.addChild(irClass) + thisReceiver = buildValueParameter(irClass) { + name = Name.identifier("\$this") + type = IrSimpleTypeImpl(irClass.symbol, false, emptyList(), emptyList()) + } + } + + private fun buildAnnotationConstructor(annotationClass: IrClass): IrConstructor = + annotationClass.addConstructor { isPrimary = true } + + private fun createPackage(packageName: String): IrPackageFragment = + IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment( + moduleFragment.descriptor, + FqName(packageName) + ) + + private fun createClass( + irPackage: IrPackageFragment, + shortName: String, + classKind: ClassKind, + classModality: Modality, + isValueClass: Boolean = false, + ): IrClassSymbol = irFactory.buildClass { + name = Name.identifier(shortName) + kind = classKind + modality = classModality + isValue = isValueClass + }.apply { + parent = irPackage + createImplicitParameterDeclarationWithWrappedDescriptor() + }.symbol + + fun createBuilder( + symbol: IrSymbol, + startOffset: Int = UNDEFINED_OFFSET, + endOffset: Int = UNDEFINED_OFFSET + ) = AtomicfuIrBuilder(this, symbol, startOffset, endOffset) +} diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuIrBuilder.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuIrBuilder.kt new file mode 100644 index 00000000000..aeda51c479e --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuIrBuilder.kt @@ -0,0 +1,304 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm + +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* + +// An IR builder with access to AtomicSymbols and convenience methods to build IR constructions for atomicfu JVM/IR transformation. +class AtomicfuIrBuilder internal constructor( + val atomicSymbols: AtomicSymbols, + symbol: IrSymbol, + startOffset: Int, + endOffset: Int +) : IrBuilderWithScope(IrGeneratorContextBase(atomicSymbols.irBuiltIns), Scope(symbol), startOffset, endOffset) { + + fun getProperty(property: IrProperty, dispatchReceiver: IrExpression?) = + irCall(property.getter?.symbol ?: error("Getter is not defined for the property ${property.render()}")).apply { + this.dispatchReceiver = dispatchReceiver?.deepCopyWithSymbols() + } + + // a$FU.get(obj) + fun atomicGetValue(valueType: IrType, receiver: IrExpression, obj: IrExpression) = + irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicSymbols.getJucaAFUClass(valueType), "get")).apply { + dispatchReceiver = receiver + putValueArgument(0, obj) + } + + // atomicArr.get(index) + fun atomicGetArrayElement(atomicArrayClass: IrClassSymbol, receiver: IrExpression, index: IrExpression) = + irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "get")).apply { + dispatchReceiver = receiver + putValueArgument(0, index) + } + + fun irCallWithArgs(symbol: IrSimpleFunctionSymbol, dispatchReceiver: IrExpression?, valueArguments: List) = + irCall(symbol).apply { + this.dispatchReceiver = dispatchReceiver + valueArguments.forEachIndexed { i, arg -> + putValueArgument(i, arg) + } + } + + // atomicArr.compareAndSet(index, expect, update) + fun callAtomicArray( + arrayClassSymbol: IrClassSymbol, + functionName: String, + dispatchReceiver: IrExpression?, + index: IrExpression, + valueArguments: List, + isBooleanReceiver: Boolean + ): IrCall { + val irCall = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(arrayClassSymbol, functionName)).apply { + this.dispatchReceiver = dispatchReceiver + putValueArgument(0, index) // array element index + valueArguments.forEachIndexed { index, arg -> + putValueArgument(index + 1, arg) // function arguments + } + } + return if (isBooleanReceiver && irCall.type.isInt()) irCall.toBoolean() else irCall + } + + // a$FU.compareAndSet(obj, expect, update) + fun callFieldUpdater( + fieldUpdaterSymbol: IrClassSymbol, + functionName: String, + dispatchReceiver: IrExpression?, + obj: IrExpression?, + valueArguments: List, + castType: IrType?, + isBooleanReceiver: Boolean, + ): IrExpression { + val irCall = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(fieldUpdaterSymbol, functionName)).apply { + this.dispatchReceiver = dispatchReceiver + putValueArgument(0, obj) // instance of the class, containing the field + valueArguments.forEachIndexed { index, arg -> + putValueArgument(index + 1, arg) // function arguments + } + } + if (functionName == "" && castType != null) { + return irAs(irCall, castType) + } + // j.u.c.a AtomicIntegerFieldUpdater is used to update boolean values, + // so cast return value to boolean if necessary + return if (isBooleanReceiver && irCall.type.isInt()) irCall.toBoolean() else irCall + } + + private fun IrExpression.toBoolean() = irNotEquals(this, irInt(0)) as IrCall + + fun callAtomicExtension( + symbol: IrSimpleFunctionSymbol, + dispatchReceiver: IrExpression?, + syntheticValueArguments: List, + valueArguments: List + ) = irCallWithArgs(symbol, dispatchReceiver, syntheticValueArguments + valueArguments) + + // val a$FU = j.u.c.a.AtomicIntegerFieldUpdater.newUpdater(A::class, "a") + fun newUpdater( + fieldUpdaterClass: IrClassSymbol, + parentClass: IrClass, + valueType: IrType, + fieldName: String + ) = irCall(atomicSymbols.getNewUpdater(fieldUpdaterClass)).apply { + putValueArgument(0, atomicSymbols.javaClassReference(parentClass.symbol.starProjectedType)) // tclass + if (fieldUpdaterClass == atomicSymbols.atomicRefFieldUpdaterClass) { + putValueArgument(1, atomicSymbols.javaClassReference(valueType)) // vclass + putValueArgument(2, irString(fieldName)) // fieldName + } else { + putValueArgument(1, irString(fieldName)) // fieldName + } + } + + // val atomicArr = j.u.c.a.AtomicIntegerArray(size) + fun newJucaAtomicArray( + atomicArrayClass: IrClassSymbol, + size: IrExpression, + dispatchReceiver: IrExpression? + ) = irCall(atomicSymbols.getAtomicArrayConstructor(atomicArrayClass)).apply { + putValueArgument(0, size) // size + this.dispatchReceiver = dispatchReceiver + } + + /* + inline fun atomicfu$loop(atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Unit, dispatchReceiver: Any?) { + while (true) { + val cur = atomicfu$handler.get() + atomicfu$action(cur) + } + } + */ + fun atomicfuLoopBody(valueType: IrType, valueParameters: List) = + irBlockBody { + +irWhile().apply { + condition = irTrue() + body = irBlock { + val cur = createTmpVariable( + atomicGetValue(valueType, irGet(valueParameters[0]), irGet(valueParameters[2])), + "atomicfu\$cur", false + ) + +irCall(atomicSymbols.invoke1Symbol).apply { + dispatchReceiver = irGet(valueParameters[1]) + putValueArgument(0, irGet(cur)) + } + } + } + } + + /* + inline fun atomicfu$array$loop(atomicfu$handler: AtomicIntegerArray, index: Int, atomicfu$action: (Int) -> Unit) { + while (true) { + val cur = atomicfu$handler.get(index) + atomicfu$action(cur) + } + } + */ + fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueParameters: List) = + irBlockBody { + +irWhile().apply { + condition = irTrue() + body = irBlock { + val cur = createTmpVariable( + atomicGetArrayElement(atomicArrayClass, irGet(valueParameters[0]), irGet(valueParameters[1])), + "atomicfu\$cur", false + ) + +irCall(atomicSymbols.invoke1Symbol).apply { + dispatchReceiver = irGet(valueParameters[2]) + putValueArgument(0, irGet(cur)) + } + } + } + } + + /* + inline fun atomicfu$update(atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Int, dispatchReceiver: Any?) { + while (true) { + val cur = atomicfu$handler.get() + val upd = atomicfu$action(cur) + if (atomicfu$handler.CAS(cur, upd)) return + } + } + */ + + /* + inline fun atomicfu$getAndUpdate(atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Int, dispatchReceiver: Any?) { + while (true) { + val cur = atomicfu$handler.get() + val upd = atomicfu$action(cur) + if (atomicfu$handler.CAS(cur, upd)) return cur + } + } + */ + + /* + inline fun atomicfu$updateAndGet(atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Int, dispatchReceiver: Any?) { + while (true) { + val cur = atomicfu$handler.get() + val upd = atomicfu$action(cur) + if (atomicfu$handler.CAS(cur, upd)) return upd + } + } + */ + fun atomicfuUpdateBody(functionName: String, valueParameters: List, valueType: IrType) = + irBlockBody { + +irWhile().apply { + condition = irTrue() + body = irBlock { + val cur = createTmpVariable( + atomicGetValue(valueType, irGet(valueParameters[0]), irGet(valueParameters[2])), + "atomicfu\$cur", false + ) + val upd = createTmpVariable( + irCall(atomicSymbols.invoke1Symbol).apply { + dispatchReceiver = irGet(valueParameters[1]) + putValueArgument(0, irGet(cur)) + }, "atomicfu\$upd", false + ) + +irIfThen( + type = atomicSymbols.irBuiltIns.unitType, + condition = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicSymbols.getJucaAFUClass(valueType), "compareAndSet")).apply { + putValueArgument(0, irGet(valueParameters[2])) + putValueArgument(1, irGet(cur)) + putValueArgument(2, irGet(upd)) + dispatchReceiver = irGet(valueParameters[0]) + }, + thenPart = when (functionName) { + "update" -> irReturnUnit() + "getAndUpdate" -> irReturn(irGet(cur)) + "updateAndGet" -> irReturn(irGet(upd)) + else -> error("Unsupported atomicfu inline loop function name: $functionName") + } + ) + } + } + } + + /* + inline fun atomicfu$array$update(atomicfu$handler: AtomicIntegerArray, index: Int, atomicfu$action: (Int) -> Int) { + while (true) { + val cur = atomicfu$handler.get(index) + val upd = atomicfu$action(cur) + if (atomicfu$handler.CAS(index, cur, upd)) return + } + } + */ + + /* + inline fun atomicfu$array$getAndUpdate(atomicfu$handler: AtomicIntegerArray, index: Int, atomicfu$action: (Int) -> Int) { + while (true) { + val cur = atomicfu$handler.get(index) + val upd = atomicfu$action(cur) + if (atomicfu$handler.CAS(index, cur, upd)) return cur + } + } + */ + + /* + inline fun atomicfu$array$updateAndGet(atomicfu$handler: AtomicIntegerArray, index: Int, atomicfu$action: (Int) -> Int) { + while (true) { + val cur = atomicfu$handler.get(index) + val upd = atomicfu$action(cur) + if (atomicfu$handler.CAS(index, cur, upd)) return upd + } + } + */ + fun atomicfuArrayUpdateBody(functionName: String, atomicArrayClass: IrClassSymbol, valueParameters: List) = + irBlockBody { + +irWhile().apply { + condition = irTrue() + body = irBlock { + val cur = createTmpVariable( + atomicGetArrayElement(atomicArrayClass, irGet(valueParameters[0]), irGet(valueParameters[1])), + "atomicfu\$cur", false + ) + val upd = createTmpVariable( + irCall(atomicSymbols.invoke1Symbol).apply { + dispatchReceiver = irGet(valueParameters[2]) + putValueArgument(0, irGet(cur)) + }, "atomicfu\$upd", false + ) + +irIfThen( + type = atomicSymbols.irBuiltIns.unitType, + condition = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "compareAndSet")).apply { + putValueArgument(0, irGet(valueParameters[1])) // index + putValueArgument(1, irGet(cur)) + putValueArgument(2, irGet(upd)) + dispatchReceiver = irGet(valueParameters[0]) + }, + thenPart = when (functionName) { + "update" -> irReturnUnit() + "getAndUpdate" -> irReturn(irGet(cur)) + "updateAndGet" -> irReturn(irGet(upd)) + else -> error("Unsupported atomicfu inline loop function name: $functionName") + } + ) + } + } + } +} diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt new file mode 100644 index 00000000000..bfff872f22c --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/jvm/AtomicfuJvmIrTransformer.kt @@ -0,0 +1,880 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm + +import org.jetbrains.kotlin.backend.common.extensions.* +import org.jetbrains.kotlin.backend.common.lower.parents +import org.jetbrains.kotlin.backend.jvm.ir.* +import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.builders.declarations.* +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.name.* +import org.jetbrains.kotlin.util.capitalizeDecapitalize.* +import org.jetbrains.kotlinx.atomicfu.compiler.backend.* +import kotlin.collections.set + +private const val AFU_PKG = "kotlinx.atomicfu" +private const val TRACE_BASE_TYPE = "TraceBase" +private const val ATOMIC_VALUE_FACTORY = "atomic" +private const val INVOKE = "invoke" +private const val APPEND = "append" +private const val GET = "get" +private const val ATOMICFU = "atomicfu" +private const val ATOMIC_ARRAY_RECEIVER_SUFFIX = "\$array" +private const val DISPATCH_RECEIVER = "${ATOMICFU}\$dispatchReceiver" +private const val ATOMIC_HANDLER = "${ATOMICFU}\$handler" +private const val ACTION = "${ATOMICFU}\$action" +private const val INDEX = "${ATOMICFU}\$index" +private const val VOLATILE_WRAPPER_SUFFIX = "\$VolatileWrapper" +private const val LOOP = "loop" +private const val UPDATE = "update" + +class AtomicfuJvmIrTransformer( + private val context: IrPluginContext, + private val atomicSymbols: AtomicSymbols +) { + private val irBuiltIns = context.irBuiltIns + + private val AFU_VALUE_TYPES: Map = mapOf( + "AtomicInt" to irBuiltIns.intType, + "AtomicLong" to irBuiltIns.longType, + "AtomicBoolean" to irBuiltIns.booleanType, + "AtomicRef" to irBuiltIns.anyNType + ) + + private val ATOMICFU_INLINE_FUNCTIONS = setOf("loop", "update", "getAndUpdate", "updateAndGet") + protected val ATOMIC_VALUE_TYPES = setOf("AtomicInt", "AtomicLong", "AtomicBoolean", "AtomicRef") + protected val ATOMIC_ARRAY_TYPES = setOf("AtomicIntArray", "AtomicLongArray", "AtomicBooleanArray", "AtomicArray") + + fun transform(moduleFragment: IrModuleFragment) { + transformAtomicFields(moduleFragment) + transformAtomicExtensions(moduleFragment) + transformAtomicfuDeclarations(moduleFragment) + for (irFile in moduleFragment.files) { + irFile.patchDeclarationParents() + } + } + + private fun transformAtomicFields(moduleFragment: IrModuleFragment) { + for (irFile in moduleFragment.files) { + irFile.transform(AtomicHandlerTransformer(), null) + } + } + + private fun transformAtomicExtensions(moduleFragment: IrModuleFragment) { + for (irFile in moduleFragment.files) { + irFile.transform(AtomicExtensionTransformer(), null) + } + } + + private fun transformAtomicfuDeclarations(moduleFragment: IrModuleFragment) { + for (irFile in moduleFragment.files) { + irFile.transform(AtomicfuTransformer(), null) + } + } + + private val propertyToAtomicHandler = mutableMapOf() + + private inner class AtomicHandlerTransformer : IrElementTransformer { + override fun visitClass(declaration: IrClass, data: IrFunction?): IrStatement { + declaration.declarations.filter(::fromKotlinxAtomicfu).forEach { + (it as IrProperty).transformAtomicfuProperty(declaration) + } + return super.visitClass(declaration, data) + } + + override fun visitFile(declaration: IrFile, data: IrFunction?): IrFile { + declaration.declarations.filter(::fromKotlinxAtomicfu).forEach { + (it as IrProperty).transformAtomicfuProperty(declaration) + } + return super.visitFile(declaration, data) + } + + private fun IrProperty.transformAtomicfuProperty(parent: IrDeclarationContainer) { + val isTopLevel = parent is IrFile + when { + isAtomic() -> { + if (isTopLevel) { + val parentClass = generateWrapperClass(this, parent) + transformAtomicProperty(parentClass) + moveFromFileToClass(parent as IrFile, parentClass) + } else { + transformAtomicProperty(parent as IrClass) + } + } + isDelegatedToAtomic() -> transformDelegatedProperty(parent) + isAtomicArray() -> transformAtomicArrayProperty(parent) + isTrace() -> parent.declarations.remove(this) + else -> {} + } + } + + private fun IrProperty.moveFromFileToClass( + parentFile: IrFile, + parentClass: IrClass + ) { + parentFile.declarations.remove(this) + parentClass.declarations.add(this) + parent = parentClass + } + + private fun IrProperty.transformAtomicProperty(parentClass: IrClass) { + // Atomic property transformation: + // 1. replace it's backingField with a volatile property of atomic value type + // 2. create j.u.c.a.Atomic*FieldUpdater for this volatile property to handle it's value atomically + // val a = atomic(0) -> + // volatile var a: Int = 0 + // val a$FU = AtomicIntegerFieldUpdater.newUpdater(parentClass, "a") + // + // Top-level atomic properties transformation: + // 1. replace it's backingField with a volatile property of atomic value type + // 2. wrap this volatile property into the generated class + // 3. create j.u.c.a.Atomic*FieldUpdater for the volatile property to handle it's value atomically + // val a = atomic(0) -> + // class A$ParentFile$VolatileWrapper { volatile var a: Int = 0 } + // val a$FU = AtomicIntegerFieldUpdater.newUpdater(A$ParentFile$VolatileWrapper::class, "a") + backingField = buildVolatileRawField(this, parentClass) + // update property accessors + context.addDefaultGetter(this, parentClass) + val fieldUpdater = addJucaAFUProperty(this, parentClass) + registerAtomicHandler(fieldUpdater) + } + + private fun IrProperty.transformAtomicArrayProperty(parent: IrDeclarationContainer) { + // Replace atomicfu array classes with the corresponding atomic arrays from j.u.c.a.: + // val intArr = atomicArrayOfNulls(5) -> + // val intArr = AtomicReferenceArray(5) + backingField = buildJucaArrayField(this, parent) + // update property accessors + context.addDefaultGetter(this, parent) + registerAtomicHandler(this) + } + + private fun IrProperty.transformDelegatedProperty(parent: IrDeclarationContainer) { + backingField?.let { + it.initializer?.let { + val initializer = it.expression as IrCall + if (initializer.isAtomicFactory()) { + // Property delegated to atomic factory invocation: + // 1. replace it's backingField with a volatile property of value type + // 2. transform getter/setter + // var a by atomic(0) -> + // volatile var a: Int = 0 + val volatileField = buildVolatileRawField(this, parent).also { + parent.declarations.add(it) + } + backingField = null + getter?.transformAccessor(volatileField, getter?.dispatchReceiverParameter?.capture()) + setter?.transformAccessor(volatileField, setter?.dispatchReceiverParameter?.capture()) + } else { + // Property delegated to the atomic property: + // 1. delegate it's accessors to get/set of the backingField of the atomic delegate + // (that is already transformed to a volatile field of value type) + // val _a = atomic(0) + // var a by _a -> + // volatile var _a: Int = 0 + // var a by _a + val atomicProperty = initializer.getCorrespondingProperty() + val volatileField = atomicProperty.backingField!! + backingField = null + if (atomicProperty.isTopLevel()) { + with(atomicSymbols.createBuilder(symbol)) { + val wrapper = getStaticVolatileWrapperInstance(atomicProperty) + getter?.transformAccessor(volatileField, getProperty(wrapper, null)) + setter?.transformAccessor(volatileField, getProperty(wrapper, null)) + } + } else { + if (this.parent == atomicProperty.parent) { + //class A { + // val _a = atomic() + // var a by _a + //} + getter?.transformAccessor(volatileField, getter?.dispatchReceiverParameter?.capture()) + setter?.transformAccessor(volatileField, setter?.dispatchReceiverParameter?.capture()) + } else { + //class A { + // val _a = atomic() + // inner class B { + // var a by _a + // } + //} + val thisReceiver = atomicProperty.parentAsClass.thisReceiver + getter?.transformAccessor(volatileField, thisReceiver?.capture()) + setter?.transformAccessor(volatileField, thisReceiver?.capture()) + } + } + } + } + } + } + + private fun IrFunction.transformAccessor(volatileField: IrField, parent: IrExpression?) { + val accessor = this + with(atomicSymbols.createBuilder(symbol)) { + body = irExprBody( + irReturn( + if (accessor.isGetter) { + irGetField(parent, volatileField) + } else { + irSetField(parent, volatileField, accessor.valueParameters[0].capture()) + } + ) + ) + } + } + + private fun IrProperty.registerAtomicHandler(atomicHandlerProperty: IrProperty) { + propertyToAtomicHandler[this] = atomicHandlerProperty + } + + private fun buildVolatileRawField(property: IrProperty, parent: IrDeclarationContainer): IrField = + // Generate a new backing field for the given property: + // a volatile variable of the atomic value type + // val a = atomic(0) + // volatile var a: Int = 0 + property.backingField?.let { backingField -> + val init = backingField.initializer?.expression + val valueType = backingField.type.atomicToValueType() + context.irFactory.buildField { + name = property.name + type = if (valueType.isBoolean()) irBuiltIns.intType else valueType + visibility = backingField.visibility // private + isFinal = false + isStatic = parent is IrFile + }.apply { + if (init != null) { + val value = (init as IrCall).getAtomicFactoryValueArgument() + initializer = IrExpressionBodyImpl(value) + } else { + // if lateinit field -> initialize it in IrAnonymousInitializer + transformLateInitializer(backingField, parent) { init -> + val value = (init as IrCall).getAtomicFactoryValueArgument() + with(atomicSymbols.createBuilder(this.symbol)) { + irSetField((parent as? IrClass)?.thisReceiver?.capture(), this@apply, value) + } + } + } + annotations = backingField.annotations + atomicSymbols.volatileAnnotationConstructorCall + this.parent = parent + } + } ?: error("Backing field of the atomic property ${property.render()} is null") + + private fun addJucaAFUProperty(atomicProperty: IrProperty, parentClass: IrClass): IrProperty = + // Generate an atomic field updater for the volatile backing field of the given property: + // val a = atomic(0) + // volatile var a: Int = 0 + // val a$FU = AtomicIntegerFieldUpdater.newUpdater(parentClass, "a") + atomicProperty.backingField?.let { volatileField -> + val fuClass = atomicSymbols.getJucaAFUClass(volatileField.type) + val fieldName = volatileField.name.asString() + val fuField = context.irFactory.buildField { + name = Name.identifier(mangleFUName(fieldName)) + type = fuClass.defaultType + visibility = volatileField.visibility // private + isFinal = true + isStatic = true + }.apply { + initializer = IrExpressionBodyImpl( + with(atomicSymbols.createBuilder(symbol)) { + newUpdater(fuClass, parentClass, irBuiltIns.anyNType, fieldName) + } + ) + parent = parentClass + } + return context.addProperty(fuField, parentClass, atomicProperty.visibility, true) + } ?: error("Atomic property ${atomicProperty.render()} should have a non-null generated volatile backingField") + + private fun buildJucaArrayField(atomicfuArrayProperty: IrProperty, parent: IrDeclarationContainer) = + atomicfuArrayProperty.backingField?.let { atomicfuArray -> + val init = atomicfuArray.initializer?.expression as? IrFunctionAccessExpression + val atomicArrayClass = atomicSymbols.getAtomicArrayClassByAtomicfuArrayType(atomicfuArray.type) + context.irFactory.buildField { + name = atomicfuArray.name + type = atomicArrayClass.defaultType + visibility = atomicfuArray.visibility // private + isFinal = atomicfuArray.isFinal + isStatic = atomicfuArray.isStatic + }.apply { + if (init != null) { + this.initializer = IrExpressionBodyImpl( + with(atomicSymbols.createBuilder(symbol)) { + val size = init.getArraySizeArgument() + newJucaAtomicArray(atomicArrayClass, size, init.dispatchReceiver) + } + ) + } else { + // if lateinit field -> initialize it in IrAnonymousInitializer + transformLateInitializer(atomicfuArray, parent) { init -> + init as IrFunctionAccessExpression + val size = init.getArraySizeArgument() + with(atomicSymbols.createBuilder(this.symbol)) { + irSetField( + (parent as? IrClass)?.thisReceiver?.capture(), + this@apply, + newJucaAtomicArray(atomicArrayClass, size, init.dispatchReceiver) + ) + } + } + } + annotations = atomicfuArray.annotations + this.parent = parent + } + } ?: error("Atomic property does not have backingField") + + private fun generateWrapperClass(atomicProperty: IrProperty, parentFile: IrDeclarationContainer): IrClass { + val wrapperClassName = getVolatileWrapperClassName(atomicProperty) + val volatileWrapperClass = parentFile.declarations.singleOrNull { it is IrClass && it.name.asString() == wrapperClassName } + ?: atomicSymbols.buildClassWithPrimaryConstructor(wrapperClassName, parentFile) + return (volatileWrapperClass as IrClass).also { + context.addProperty( + field = context.buildClassInstance(it, parentFile), + parent = parentFile, + visibility = atomicProperty.visibility, + isStatic = true + ) + } + } + + private fun transformLateInitializer( + field: IrField, + parent: IrDeclarationContainer, + generateIrSetField: (init: IrExpression) -> IrExpression + ) { + for (declaration in parent.declarations) { + if (declaration is IrAnonymousInitializer) { + declaration.body.statements.singleOrNull { + it is IrSetField && it.symbol == field.symbol + }?.let { + declaration.body.statements.remove(it) + val init = (it as IrSetField).value + declaration.body.statements.add( + generateIrSetField(init) + ) + } + } + } + } + + private fun IrCall.getAtomicFactoryValueArgument() = + getValueArgument(0)?.deepCopyWithSymbols() + ?: error("Atomic factory should take at least one argument: ${this.render()}") + + private fun IrFunctionAccessExpression.getArraySizeArgument() = + getValueArgument(0)?.deepCopyWithSymbols() + ?: error("Atomic array constructor should take at least one argument: ${this.render()}") + + private fun fromKotlinxAtomicfu(declaration: IrDeclaration): Boolean = + declaration is IrProperty && + declaration.backingField?.type?.isKotlinxAtomicfuPackage() ?: false + + private fun IrProperty.isAtomic(): Boolean = + !isDelegated && backingField?.type?.isAtomicValueType() ?: false + + private fun IrProperty.isDelegatedToAtomic(): Boolean = + isDelegated && backingField?.type?.isAtomicValueType() ?: false + + private fun IrProperty.isAtomicArray(): Boolean = + backingField?.type?.isAtomicArrayType() ?: false + + private fun IrProperty.isTrace(): Boolean = + backingField?.type?.isTraceBaseType() ?: false + + private fun IrProperty.isTopLevel(): Boolean = + parent is IrClass && (parent as IrClass).name.asString().endsWith(VOLATILE_WRAPPER_SUFFIX) + + private fun mangleFUName(fieldName: String) = "$fieldName\$FU" + } + + private inner class AtomicExtensionTransformer : IrElementTransformerVoid() { + override fun visitFile(declaration: IrFile): IrFile { + declaration.transformAllAtomicExtensions() + return super.visitFile(declaration) + } + + override fun visitClass(declaration: IrClass): IrStatement { + declaration.transformAllAtomicExtensions() + return super.visitClass(declaration) + } + + private fun IrDeclarationContainer.transformAllAtomicExtensions() { + // Transform the signature of kotlinx.atomicfu.Atomic* class extension functions: + // inline fun AtomicInt.foo(arg: T) + // For every signature there are 2 new declarations generated (because of different types of atomic handlers): + // 1. for the case of atomic value receiver at the invocation: + // inline fun foo$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': T) + // 2. for the case of atomic array element receiver at the invocation: + // inline fun foo$atomicfu$array(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerArray, index: Int, arg': T) + declarations.filter { it is IrFunction && it.isAtomicExtension() }.forEach { atomicExtension -> + atomicExtension as IrFunction + declarations.add(generateAtomicExtension(atomicExtension, this, false)) + declarations.add(generateAtomicExtension(atomicExtension, this, true)) + declarations.remove(atomicExtension) + } + } + + private fun generateAtomicExtension( + atomicExtension: IrFunction, + parent: IrDeclarationParent, + isArrayReceiver: Boolean + ): IrFunction { + val mangledName = mangleFunctionName(atomicExtension.name.asString(), isArrayReceiver) + val valueType = atomicExtension.extensionReceiverParameter!!.type.atomicToValueType() + return context.irFactory.buildFun { + name = Name.identifier(mangledName) + isInline = true + }.apply { + val newDeclaration = this + extensionReceiverParameter = null + dispatchReceiverParameter = atomicExtension.dispatchReceiverParameter?.deepCopyWithSymbols(this) + if (isArrayReceiver) { + addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType) + addValueParameter(ATOMIC_HANDLER, atomicSymbols.getAtomicArrayClassByValueType(valueType).defaultType) + addValueParameter(INDEX, irBuiltIns.intType) + } else { + addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType) + addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType)) + } + atomicExtension.valueParameters.forEach { addValueParameter(it.name, it.type) } + // the body will be transformed later by `AtomicFUTransformer` + body = atomicExtension.body?.deepCopyWithSymbols(this) + body?.transform( + object : IrElementTransformerVoid() { + override fun visitReturn(expression: IrReturn): IrExpression = super.visitReturn( + if (expression.returnTargetSymbol == atomicExtension.symbol) { + with(atomicSymbols.createBuilder(newDeclaration.symbol)) { + irReturn(expression.value) + } + } else { + expression + } + ) + }, null + ) + returnType = atomicExtension.returnType + this.parent = parent + } + } + } + + private data class AtomicFieldInfo(val dispatchReceiver: IrExpression?, val atomicHandler: IrExpression) + + private inner class AtomicfuTransformer : IrElementTransformer { + override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement { + return super.visitFunction(declaration, declaration) + } + + override fun visitCall(expression: IrCall, data: IrFunction?): IrElement { + (expression.extensionReceiver ?: expression.dispatchReceiver)?.transform(this, data)?.let { + with(atomicSymbols.createBuilder(expression.symbol)) { + val receiver = if (it is IrTypeOperatorCallImpl) it.argument else it + if (receiver.type.isAtomicValueType()) { + val valueType = if (it is IrTypeOperatorCallImpl) { + // If receiverExpression is a cast `s as AtomicRef` + // then valueType is the type argument of Atomic* class `String` + (it.type as IrSimpleType).arguments[0] as IrSimpleType + } else { + receiver.type.atomicToValueType() + } + getAtomicFieldInfo(receiver, data)?.let { (dispatchReceiver, atomicHandler) -> + val isArrayReceiver = atomicSymbols.isAtomicArrayHandlerType(atomicHandler.type) + if (expression.symbol.isKotlinxAtomicfuPackage()) { + // Transform invocations of atomic functions, delegating them to the atomicHandler. + // 1. For atomic properties (j.u.c.a.Atomic*FieldUpdater): + // a.compareAndSet(expect, update) -> a$FU.compareAndSet(dispatchReceiver, expect, update) + // 2. For atomic array elements (j.u.c.a.Atomic*Array): + // intArr[0].compareAndSet(expect, update) -> intArr.compareAndSet(index, expect, update) + val functionName = expression.symbol.owner.name.asString() + if (functionName in ATOMICFU_INLINE_FUNCTIONS) { + // If the inline atomicfu loop function was invoked + // a.loop { value -> a.compareAndSet(value, 777) } + // then loop function is generated to replace this declaration. + // `AtomicInt.loop(action: (Int) -> Unit)` for example will be replaced with + // inline fun atomicfu$loop(atomicHandler: AtomicIntegerFieldUpdater, action: (Int) -> Unit) { + // while (true) { + // val cur = atomicfu$handler.get() + // atomicfu$action(cur) + // } + // } + // And the invocation in place will be transformed: + // a.atomicfu$loop(atomicHandler, action) + require(data != null) { "Function containing loop invocation ${expression.render()} is null" } + val loopFunc = data.parentDeclarationContainer.getOrBuildInlineLoopFunction( + functionName = functionName, + valueType = if (valueType.isBoolean()) irBuiltIns.intType else valueType, + isArrayReceiver = isArrayReceiver + ) + val action = (expression.getValueArgument(0) as IrFunctionExpression).apply { + function.body?.transform(this@AtomicfuTransformer, data) + if (function.valueParameters[0].type.isBoolean()) { + function.valueParameters[0].type = irBuiltIns.intType + function.returnType = irBuiltIns.intType + } + } + val loopCall = irCallWithArgs( + symbol = loopFunc.symbol, + dispatchReceiver = data.containingFunction.dispatchReceiverParameter?.capture(), + valueArguments = if (isArrayReceiver) { + val index = receiver.getArrayElementIndex(data) + listOf(atomicHandler, index, action) + } else { + listOf(atomicHandler, action, dispatchReceiver) + } + ) + return super.visitCall(loopCall, data) + } + val irCall = if (isArrayReceiver) { + callAtomicArray( + arrayClassSymbol = atomicHandler.type.classOrNull!!, + functionName = functionName, + dispatchReceiver = atomicHandler, + index = receiver.getArrayElementIndex(data), + valueArguments = expression.getValueArguments(), + isBooleanReceiver = valueType.isBoolean() + ) + } else { + callFieldUpdater( + fieldUpdaterSymbol = atomicSymbols.getJucaAFUClass(valueType), + functionName = functionName, + dispatchReceiver = atomicHandler, + obj = dispatchReceiver, + valueArguments = expression.getValueArguments(), + castType = if (it is IrTypeOperatorCall) valueType else null, + isBooleanReceiver = valueType.isBoolean() + ) + } + return super.visitExpression(irCall, data) + } + if (expression.symbol.owner.isInline && expression.extensionReceiver != null) { + // Transform invocation of the kotlinx.atomicfu.Atomic* class extension functions, + // delegating them to the corresponding transformed atomic extensions: + // for atomic property recevers: + // inline fun foo$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int) { ... } + // for atomic array element receivers: + // inline fun foo$atomicfu$array(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerArray, index: Int, arg': Int) { ... } + + // The invocation on the atomic property will be transformed: + // a.foo(arg) -> a.foo$atomicfu(dispatchReceiver, atomicHandler, arg) + // The invocation on the atomic array element will be transformed: + // a.foo(arg) -> a.foo$atomicfu$array(dispatchReceiver, atomicHandler, index, arg) + val declaration = expression.symbol.owner + val parent = declaration.parent as IrDeclarationContainer + val transformedAtomicExtension = parent.getTransformedAtomicExtension(declaration, isArrayReceiver) + require(data != null) { "Function containing invocation of the extension function ${expression.render()} is null" } + val irCall = callAtomicExtension( + symbol = transformedAtomicExtension.symbol, + dispatchReceiver = expression.dispatchReceiver, + syntheticValueArguments = if (isArrayReceiver) { + listOf(dispatchReceiver, atomicHandler, receiver.getArrayElementIndex(data)) + } else { + listOf(dispatchReceiver, atomicHandler) + }, + valueArguments = expression.getValueArguments() + ) + return super.visitCall(irCall, data) + } + } ?: return expression + } + } + } + return super.visitCall(expression, data) + } + + override fun visitGetValue(expression: IrGetValue, data: IrFunction?): IrExpression { + // For transformed atomic extension functions + // replace old value parameters with the new parameters of the transformed declaration: + // inline fun foo$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int) { + // arg -> arg` + //} + if (expression.symbol is IrValueParameterSymbol) { + val valueParameter = expression.symbol.owner as IrValueParameter + val parent = valueParameter.parent + if (data != null && data.isTransformedAtomicExtension() && + parent is IrFunctionImpl && !parent.isTransformedAtomicExtension() && + parent.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) { + val index = valueParameter.index + if (index < 0 && !valueParameter.type.isAtomicValueType()) { + // index == -1 for `this` parameter + return data.dispatchReceiverParameter?.capture() ?: error { "Dispatchreceiver of ${data.render()} is null" } + } + if (index >= 0) { + val shift = if (data.name.asString().endsWith(ATOMIC_ARRAY_RECEIVER_SUFFIX)) 3 else 2 + val transformedValueParameter = data.valueParameters[index + shift] + return buildGetValue( + expression.startOffset, + expression.endOffset, + transformedValueParameter.symbol + ) + } + } + } + return super.visitGetValue(expression, data) + } + + override fun visitBlockBody(body: IrBlockBody, data: IrFunction?): IrBody { + // Erase messages added by the Trace object from the function body: + // val trace = Trace(size) + // Messages may be added via trace invocation: + // trace { "Doing something" } + // or via multi-append of arguments: + // trace.append(index, "CAS", value) + body.statements.removeIf { + it.isTraceCall() + } + return super.visitBlockBody(body, data) + } + + override fun visitContainerExpression(expression: IrContainerExpression, data: IrFunction?): IrExpression { + // Erase messages added by the Trace object from blocks. + expression.statements.removeIf { + it.isTraceCall() + } + return super.visitContainerExpression(expression, data) + } + + private fun AtomicfuIrBuilder.getAtomicFieldInfo( + receiver: IrExpression, + parentFunction: IrFunction? + ): AtomicFieldInfo? { + // For the given function call receiver of atomic type returns: + // the dispatchReceiver and the atomic handler of the corresponding property + when { + receiver is IrCall -> { + // Receiver is a property getter call + val isArrayReceiver = receiver.isArrayElementGetter() + val getAtomicProperty = if (isArrayReceiver) receiver.dispatchReceiver as IrCall else receiver + val atomicProperty = getAtomicProperty.getCorrespondingProperty() + val dispatchReceiver = getAtomicProperty.dispatchReceiver + ?: run { + if (getAtomicProperty.symbol.owner.returnType.isAtomicValueType()) { + // for top-level atomic properties get wrapper class instance as a parent + getProperty(getStaticVolatileWrapperInstance(atomicProperty), null) + } else null + } + // atomic property is handled by the Atomic*FieldUpdater instance + // atomic array elementis handled by the Atomic*Array instance + val atomicHandler = propertyToAtomicHandler[atomicProperty] + ?: error("No atomic handler found for the atomic property ${atomicProperty.render()}") + return AtomicFieldInfo( + dispatchReceiver = dispatchReceiver, + atomicHandler = getProperty(atomicHandler, if (isArrayReceiver) dispatchReceiver else null) + ) + } + receiver.isThisReceiver() -> { + // Receiver is extension receiver of transformed atomic extesnion declaration. + // The old function before `AtomicExtensionTransformer` application: + // inline fun foo(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int) { + // this().lazySet(arg) + //} + // By this moment the atomic extension has it's signature transformed, + // but still has the untransformed body copied from the old declaration: + // inline fun foo$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int) { + // this().lazySet(arg) <---- + //} + // The dispatchReceiver and the atomic handler for this receiver are the corresponding arguments + // passed to the transformed declaration/ + return if (parentFunction != null && parentFunction.isTransformedAtomicExtension()) { + val params = parentFunction.valueParameters.take(2).map { it.capture() } + AtomicFieldInfo(params[0], params[1]) + } else null + } + else -> error("Unsupported type of atomic receiver expression: ${receiver.render()}") + } + } + + private val IrDeclaration.parentDeclarationContainer: IrDeclarationContainer + get() = parents.filterIsInstance().firstOrNull() ?: + error("In the sequence of parents for ${this.render()} no IrDeclarationContainer was found") + + private val IrFunction.containingFunction: IrFunction + get() { + if (this.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) return this + return parents.filterIsInstance().firstOrNull { + it.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA + } ?: error("In the sequence of parents for the local function ${this.render()} no containing function was found") + } + + private fun IrExpression.getArrayElementIndex(parentFunction: IrFunction?): IrExpression = + when { + this is IrCall -> getValueArgument(0)!! + this.isThisReceiver() -> { + require(parentFunction != null) + parentFunction.valueParameters[2].capture() + } + else -> error("Unsupported type of atomic receiver expression: ${this.render()}") + } + + private fun IrExpression.isThisReceiver() = + this is IrGetValue && symbol.owner.name.asString() == "" + + private fun IrFunction.isTransformedAtomicExtension(): Boolean { + val isArrayReceiver = name.asString().endsWith(ATOMIC_ARRAY_RECEIVER_SUFFIX) + return if (isArrayReceiver) checkSyntheticArrayElementExtensionParameter() else checkSyntheticAtomicExtensionParameters() + } + + private fun IrFunction.checkSyntheticArrayElementExtensionParameter(): Boolean { + if (valueParameters.size < 3) return false + return valueParameters[0].name.asString() == DISPATCH_RECEIVER && valueParameters[0].type == irBuiltIns.anyNType && + valueParameters[1].name.asString() == ATOMIC_HANDLER && atomicSymbols.isAtomicArrayHandlerType(valueParameters[1].type) && + valueParameters[2].name.asString() == INDEX && valueParameters[2].type == irBuiltIns.intType + } + + private fun IrFunction.checkSyntheticAtomicExtensionParameters(): Boolean { + if (valueParameters.size < 2) return false + return valueParameters[0].name.asString() == DISPATCH_RECEIVER && valueParameters[0].type == irBuiltIns.anyNType && + valueParameters[1].name.asString() == ATOMIC_HANDLER && atomicSymbols.isAtomicFieldUpdaterType(valueParameters[1].type) + } + + private fun IrDeclarationContainer.getOrBuildInlineLoopFunction( + functionName: String, + valueType: IrType, + isArrayReceiver: Boolean + ): IrSimpleFunction { + val parent = this + val mangledName = mangleFunctionName(functionName, isArrayReceiver) + val updaterType = if (isArrayReceiver) atomicSymbols.getAtomicArrayType(valueType) else atomicSymbols.getFieldUpdaterType(valueType) + findDeclaration { + it.name.asString() == mangledName && it.valueParameters[0].type == updaterType + }?.let { return it } + return context.irFactory.buildFun { + name = Name.identifier(mangledName) + isInline = true + }.apply { + dispatchReceiverParameter = (parent as? IrClass)?.thisReceiver?.deepCopyWithSymbols(this) + if (functionName == LOOP) { + if (isArrayReceiver) generateAtomicfuArrayLoop(valueType) else generateAtomicfuLoop(valueType) + } else { + if (isArrayReceiver) generateAtomicfuArrayUpdate(functionName, valueType) else generateAtomicfuUpdate(functionName, valueType) + } + this.parent = parent + parent.declarations.add(this) + } + } + + private fun IrDeclarationContainer.getTransformedAtomicExtension( + declaration: IrSimpleFunction, + isArrayReceiver: Boolean + ): IrSimpleFunction = findDeclaration { + it.name.asString() == mangleFunctionName(declaration.name.asString(), isArrayReceiver) && + it.isTransformedAtomicExtension() + } ?: error("Could not find corresponding transformed declaration for the atomic extension ${declaration.render()}") + + private fun IrSimpleFunction.generateAtomicfuLoop(valueType: IrType) { + addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType)) + addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType)) + addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType) + body = with(atomicSymbols.createBuilder(symbol)) { + atomicfuLoopBody(valueType, valueParameters) + } + returnType = irBuiltIns.unitType + } + + private fun IrSimpleFunction.generateAtomicfuArrayLoop(valueType: IrType) { + val atomicfuArrayClass = atomicSymbols.getAtomicArrayClassByValueType(valueType) + addValueParameter(ATOMIC_HANDLER, atomicfuArrayClass.defaultType) + addValueParameter(INDEX, irBuiltIns.intType) + addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType)) + body = with(atomicSymbols.createBuilder(symbol)) { + atomicfuArrayLoopBody(atomicfuArrayClass, valueParameters) + } + returnType = irBuiltIns.unitType + } + + private fun IrSimpleFunction.generateAtomicfuUpdate(functionName: String, valueType: IrType) { + addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType)) + addValueParameter(ACTION, atomicSymbols.function1Type(valueType, valueType)) + addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType) + body = with(atomicSymbols.createBuilder(symbol)) { + atomicfuUpdateBody(functionName, valueParameters, valueType) + } + returnType = if (functionName == UPDATE) irBuiltIns.unitType else valueType + } + + private fun IrSimpleFunction.generateAtomicfuArrayUpdate(functionName: String, valueType: IrType) { + val atomicfuArrayClass = atomicSymbols.getAtomicArrayClassByValueType(valueType) + addValueParameter(ATOMIC_HANDLER, atomicfuArrayClass.defaultType) + addValueParameter(INDEX, irBuiltIns.intType) + addValueParameter(ACTION, atomicSymbols.function1Type(valueType, valueType)) + body = with(atomicSymbols.createBuilder(symbol)) { + atomicfuArrayUpdateBody(functionName, atomicfuArrayClass, valueParameters) + } + returnType = if (functionName == UPDATE) irBuiltIns.unitType else valueType + } + } + + private fun getStaticVolatileWrapperInstance(atomicProperty: IrProperty): IrProperty { + val refVolatileStaticPropertyName = getVolatileWrapperClassName(atomicProperty).replaceFirstChar { it.lowercaseChar() } + return atomicProperty.fileParent.declarations.singleOrNull { + it is IrProperty && it.name.asString() == refVolatileStaticPropertyName + } as? IrProperty + ?: error("Static instance of $refVolatileStaticPropertyName is missing in the file ${atomicProperty.fileParent.render()}") + } + + private fun IrType.isKotlinxAtomicfuPackage() = + classFqName?.let { it.parent().asString() == AFU_PKG } ?: false + + private fun IrSimpleFunctionSymbol.isKotlinxAtomicfuPackage(): Boolean = + owner.parentClassOrNull?.classId?.let { + it.packageFqName.asString() == AFU_PKG + } ?: false + + private fun IrType.isAtomicValueType() = + classFqName?.let { + it.parent().asString() == AFU_PKG && it.shortName().asString() in ATOMIC_VALUE_TYPES + } ?: false + + private fun IrType.isAtomicArrayType() = + classFqName?.let { + it.parent().asString() == AFU_PKG && it.shortName().asString() in ATOMIC_ARRAY_TYPES + } ?: false + + private fun IrType.isTraceBaseType() = + classFqName?.let { + it.parent().asString() == AFU_PKG && it.shortName().asString() == TRACE_BASE_TYPE + } ?: false + + private fun IrCall.isArrayElementGetter(): Boolean = + dispatchReceiver?.let { + it.type.isAtomicArrayType() && symbol.owner.name.asString() == GET + } ?: false + + private fun IrType.atomicToValueType(): IrType = + classFqName?.let { + AFU_VALUE_TYPES[it.shortName().asString()] + } ?: error("No corresponding value type was found for this atomic type: ${this.render()}") + + private fun IrCall.isAtomicFactory(): Boolean = + symbol.isKotlinxAtomicfuPackage() && symbol.owner.name.asString() == ATOMIC_VALUE_FACTORY && + type.isAtomicValueType() + + private fun IrFunction.isAtomicExtension(): Boolean = + extensionReceiverParameter?.let { it.type.isAtomicValueType() && this.isInline } ?: false + + private fun IrStatement.isTraceCall() = this is IrCall && (isTraceInvoke() || isTraceAppend()) + + private fun IrCall.isTraceInvoke(): Boolean = + symbol.isKotlinxAtomicfuPackage() && + 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 + + private fun getVolatileWrapperClassName(property: IrProperty) = + property.name.asString().capitalizeAsciiOnly() + '$' + property.fileParent.name.substringBefore('.') + VOLATILE_WRAPPER_SUFFIX + + private fun mangleFunctionName(name: String, isArrayReceiver: Boolean) = + if (isArrayReceiver) "$name$$ATOMICFU$ATOMIC_ARRAY_RECEIVER_SUFFIX" else "$name$$ATOMICFU" +} diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuComponentRegistrar.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuComponentRegistrar.kt similarity index 100% rename from plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuComponentRegistrar.kt rename to plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuComponentRegistrar.kt diff --git a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuLoweringExtension.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt similarity index 62% rename from plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuLoweringExtension.kt rename to plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt index 0ce8c33f5fc..4383d3371be 100644 --- a/plugins/atomicfu/atomicfu-compiler/src/org.jetbrains.kotlinx.atomicfu.compiler/extensions/AtomicfuLoweringExtension.kt +++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ @@ -8,6 +8,7 @@ package org.jetbrains.kotlinx.atomicfu.compiler.extensions import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* @@ -15,15 +16,35 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.platform.js.isJs +import org.jetbrains.kotlin.platform.jvm.isJvm +import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.AtomicSymbols +import org.jetbrains.kotlinx.atomicfu.compiler.backend.js.AtomicfuJsIrTransformer +import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.AtomicfuJvmIrTransformer public open class AtomicfuLoweringExtension : IrGenerationExtension { override fun generate( moduleFragment: IrModuleFragment, pluginContext: IrPluginContext ) { - val atomicfuClassLowering = AtomicfuClassLowering(pluginContext) - for (file in moduleFragment.files) { - atomicfuClassLowering.runOnFileInOrder(file) + if (pluginContext.platform.isJvm()) { + val atomicSymbols = AtomicSymbols(pluginContext.irBuiltIns, moduleFragment) + AtomicfuJvmIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment) + } + if (pluginContext.platform.isJs()) { + for (file in moduleFragment.files) { + AtomicfuClassLowering(pluginContext).runOnFileInOrder(file) + } + } + } +} + +private class AtomicfuClassLowering( + val context: IrPluginContext +) : IrElementTransformerVoid(), FileLoweringPass { + override fun lower(irFile: IrFile) { + if (context.platform.isJs()) { + AtomicfuJsIrTransformer(context).transform(irFile) } } } @@ -42,12 +63,4 @@ fun FileLoweringPass.runOnFileInOrder(irFile: IrFile) { declaration.acceptChildrenVoid(this) } }) -} - -private class AtomicfuClassLowering( - val context: IrPluginContext -) : IrElementTransformerVoid(), FileLoweringPass { - override fun lower(irFile: IrFile) { - AtomicfuTransformer(context).transform(irFile) - } } \ No newline at end of file diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt index 616589be64b..413958f6a2d 100644 --- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJsIrTest.kt @@ -17,8 +17,8 @@ import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuLoweringExtension import java.io.File -private val atomicfuCompileDependency = System.getProperty("atomicfu.classpath") -private val atomicfuRuntime = System.getProperty("atomicfuRuntimeForTests.classpath") +private val atomicfuJsCompileDependency = System.getProperty("atomicfuJs.classpath") +private val atomicfuJsIrRuntime = System.getProperty("atomicfuJsIrRuntimeForTests.classpath") open class AbstractAtomicfuJsIrTest : AbstractJsIrTest( pathToTestDir = "plugins/atomicfu/atomicfu-compiler/testData/box/", @@ -28,19 +28,21 @@ open class AbstractAtomicfuJsIrTest : AbstractJsIrTest( super.configure(builder) with(builder) { useConfigurators(::AtomicfuEnvironmentConfigurator) - useCustomRuntimeClasspathProviders(::AtomicfuRuntimeClasspathProvider) + useCustomRuntimeClasspathProviders(::AtomicfuJsRuntimeClasspathProvider) } } } -class AtomicfuRuntimeClasspathProvider(testServices: TestServices) : RuntimeClasspathProvider(testServices) { - override fun runtimeClassPaths(module: TestModule): List { - return listOf(atomicfuCompileDependency, atomicfuRuntime).map { File(it) } - } -} - class AtomicfuEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) { override fun registerCompilerExtensions(project: Project, module: TestModule, configuration: CompilerConfiguration) { IrGenerationExtension.registerExtension(project, AtomicfuLoweringExtension()) } } + +class AtomicfuJsRuntimeClasspathProvider( + testServices: TestServices +) : RuntimeClasspathProvider(testServices) { + override fun runtimeClassPaths(module: TestModule): List { + return listOf(atomicfuJsCompileDependency, atomicfuJsIrRuntime).map { File(it) } + } +} \ No newline at end of file diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJvmIrTest.kt b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJvmIrTest.kt new file mode 100644 index 00000000000..a5f4e1f1700 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AbstractAtomicfuJvmIrTest.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.atomicfu + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.ObsoleteTestInfrastructure +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot +import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.codegen.AbstractAsmLikeInstructionListingTest +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.directives.CodegenTestDirectives +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.runners.codegen.AbstractIrBlackBoxCodegenTest +import org.jetbrains.kotlin.test.services.EnvironmentConfigurator +import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider +import org.jetbrains.kotlin.utils.PathUtil +import org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuComponentRegistrar +import java.io.File + +private val coreLibraryPath = getLibraryJar("kotlinx.atomicfu.AtomicFU") +private val kotlinTestPath = getLibraryJar("kotlin.test.AssertionsKt") +private val javaUtilConcurrentPath = getLibraryJar("java.util.concurrent.atomic.AtomicIntegerFieldUpdater") +private val kotlinJvm = getLibraryJar("kotlin.jvm.JvmField") + +open class AbstractAtomicfuJvmIrTest : AbstractIrBlackBoxCodegenTest() { + override fun configure(builder: TestConfigurationBuilder) { + super.configure(builder) + val librariesPaths = listOf(coreLibraryPath!!, kotlinTestPath!!, javaUtilConcurrentPath!!, kotlinJvm!!) + builder.configureForKotlinxAtomicfu(librariesPaths) + } +} + +private fun getLibraryJar(classToDetect: String): File? = try { + PathUtil.getResourcePathForClass(Class.forName(classToDetect)) +} catch (e: ClassNotFoundException) { + null +} + +private fun TestConfigurationBuilder.configureForKotlinxAtomicfu(librariesPaths: List) { + useConfigurators( + { services -> + object : EnvironmentConfigurator(services) { + override fun configureCompilerConfiguration( + configuration: CompilerConfiguration, + module: TestModule + ) { + configuration.addJvmClasspathRoots(librariesPaths) + } + + override fun registerCompilerExtensions(project: Project, module: TestModule, configuration: CompilerConfiguration) { + AtomicfuComponentRegistrar.registerExtensions(project) + } + } + }) + + useCustomRuntimeClasspathProviders( + { + object : RuntimeClasspathProvider(it) { + override fun runtimeClassPaths(module: TestModule): List = librariesPaths + } + } + ) + + defaultDirectives { + +CodegenTestDirectives.CHECK_BYTECODE_LISTING + } +} + diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java index 7909e5b19ee..0737c20a003 100644 --- a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java +++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJsIrTestGenerated.java @@ -32,9 +32,15 @@ public class AtomicfuJsIrTestGenerated extends AbstractAtomicfuJsIrTest { } @Test - @TestMetadata("ArrayInlineFunctionTest.kt") - public void testArrayInlineFunctionTest() throws Exception { - runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineFunctionTest.kt"); + @TestMetadata("ArrayInlineExtensionTest.kt") + public void testArrayInlineExtensionTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.kt"); + } + + @Test + @TestMetadata("ArrayLoopTest.kt") + public void testArrayLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.kt"); } @Test @@ -43,12 +49,24 @@ public class AtomicfuJsIrTestGenerated extends AbstractAtomicfuJsIrTest { runTest("plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt"); } + @Test + @TestMetadata("ComplexLoopTest.kt") + public void testComplexLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.kt"); + } + @Test @TestMetadata("DelegatedPropertiesTest.kt") public void testDelegatedPropertiesTest() throws Exception { runTest("plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt"); } + @Test + @TestMetadata("ExtensionLoopTest.kt") + public void testExtensionLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.kt"); + } + @Test @TestMetadata("ExtensionsTest.kt") public void testExtensionsTest() throws Exception { @@ -67,6 +85,18 @@ public class AtomicfuJsIrTestGenerated extends AbstractAtomicfuJsIrTest { runTest("plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.kt"); } + @Test + @TestMetadata("LambdaTest.kt") + public void testLambdaTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.kt"); + } + + @Test + @TestMetadata("LateinitPropertiesTest.kt") + public void testLateinitPropertiesTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.kt"); + } + @Test @TestMetadata("LockFreeIntBitsTest.kt") public void testLockFreeIntBitsTest() throws Exception { @@ -115,12 +145,6 @@ public class AtomicfuJsIrTestGenerated extends AbstractAtomicfuJsIrTest { runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ParameterizedInlineFunExtensionTest.kt"); } - @Test - @TestMetadata("PropertyDeclarationTest.kt") - public void testPropertyDeclarationTest() throws Exception { - runTest("plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.kt"); - } - @Test @TestMetadata("ReentrantLockTest.kt") public void testReentrantLockTest() throws Exception { diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmIrTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmIrTestGenerated.java new file mode 100644 index 00000000000..d9fd6ae0f80 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlinx/atomicfu/AtomicfuJvmIrTestGenerated.java @@ -0,0 +1,189 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlinx.atomicfu; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/box") +@TestDataPath("$PROJECT_ROOT") +public class AtomicfuJvmIrTestGenerated extends AbstractAtomicfuJvmIrTest { + @Test + public void testAllFilesPresentInBox() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("ArithmeticTest.kt") + public void testArithmeticTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.kt"); + } + + @Test + @TestMetadata("ArrayInlineExtensionTest.kt") + public void testArrayInlineExtensionTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.kt"); + } + + @Test + @TestMetadata("ArrayLoopTest.kt") + public void testArrayLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.kt"); + } + + @Test + @TestMetadata("AtomicArrayTest.kt") + public void testAtomicArrayTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt"); + } + + @Test + @TestMetadata("ComplexLoopTest.kt") + public void testComplexLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.kt"); + } + + @Test + @TestMetadata("DelegatedPropertiesTest.kt") + public void testDelegatedPropertiesTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt"); + } + + @Test + @TestMetadata("ExtensionLoopTest.kt") + public void testExtensionLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.kt"); + } + + @Test + @TestMetadata("ExtensionsTest.kt") + public void testExtensionsTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionsTest.kt"); + } + + @Test + @TestMetadata("IndexArrayElementGetterTest.kt") + public void testIndexArrayElementGetterTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.kt"); + } + + @Test + @TestMetadata("InlineExtensionWithTypeParameterTest.kt") + public void testInlineExtensionWithTypeParameterTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.kt"); + } + + @Test + @TestMetadata("LambdaTest.kt") + public void testLambdaTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.kt"); + } + + @Test + @TestMetadata("LateinitPropertiesTest.kt") + public void testLateinitPropertiesTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.kt"); + } + + @Test + @TestMetadata("LockFreeIntBitsTest.kt") + public void testLockFreeIntBitsTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.kt"); + } + + @Test + @TestMetadata("LockFreeLongCounterTest.kt") + public void testLockFreeLongCounterTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.kt"); + } + + @Test + @TestMetadata("LockFreeQueueTest.kt") + public void testLockFreeQueueTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.kt"); + } + + @Test + @TestMetadata("LockFreeStackTest.kt") + public void testLockFreeStackTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.kt"); + } + + @Test + @TestMetadata("LockTest.kt") + public void testLockTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LockTest.kt"); + } + + @Test + @TestMetadata("LoopTest.kt") + public void testLoopTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.kt"); + } + + @Test + @TestMetadata("MultiInitTest.kt") + public void testMultiInitTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/MultiInitTest.kt"); + } + + @Test + @TestMetadata("ParameterizedInlineFunExtensionTest.kt") + public void testParameterizedInlineFunExtensionTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ParameterizedInlineFunExtensionTest.kt"); + } + + @Test + @TestMetadata("ReentrantLockTest.kt") + public void testReentrantLockTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.kt"); + } + + @Test + @TestMetadata("ScopeTest.kt") + public void testScopeTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ScopeTest.kt"); + } + + @Test + @TestMetadata("SimpleLockTest.kt") + public void testSimpleLockTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/SimpleLockTest.kt"); + } + + @Test + @TestMetadata("SynchronizedObjectTest.kt") + public void testSynchronizedObjectTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/SynchronizedObjectTest.kt"); + } + + @Test + @TestMetadata("TopLevelTest.kt") + public void testTopLevelTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.kt"); + } + + @Test + @TestMetadata("TraceTest.kt") + public void testTraceTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/TraceTest.kt"); + } + + @Test + @TestMetadata("UncheckedCastTest.kt") + public void testUncheckedCastTest() throws Exception { + runTest("plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.kt"); + } +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.kt index c9e3a634d69..e2e273a45ac 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.kt @@ -23,20 +23,34 @@ class ReferenceArithmetic { val _x = atomic(null) } +class VisibilitiesTest { + val a = atomic(0) + public val b = atomic(1) + private val c = atomic(2) + internal val d = atomic(3) + + fun test() { + a.lazySet(45) + b.lazySet(56) + c.lazySet(46) + d.lazySet(67) + } +} + class ArithmeticTest { val local = atomic(0) fun testGetValue() { val a = IntArithmetic() a._x.value = 5 - check(a._x.value == 5) + assertEquals(5, a._x.value) var aValue = a._x.value - check(aValue == 5) - check(a.x == 5) + assertEquals(5, aValue) + assertEquals(5, a.x) local.value = 555 aValue = local.value - check(local.value == aValue) + assertEquals(aValue, local.value) } fun testAtomicCallPlaces(): Boolean { @@ -44,76 +58,77 @@ class ArithmeticTest { a._x.value = 5 a._x.compareAndSet(5, 42) val res = a._x.compareAndSet(42, 45) - check(res) - check(a._x.compareAndSet(45, 77)) - check(!a._x.compareAndSet(95, 77)) + assertTrue(res) + assertTrue(a._x.compareAndSet(45, 77)) + assertFalse(a._x.compareAndSet(95, 77)) return a._x.compareAndSet(77, 88) } fun testInt() { val a = IntArithmetic() - check(a.x == 0) + assertEquals(0, a.x) val update = 3 - check(a._x.getAndSet(update) == 0) - check(a._x.compareAndSet(update, 8)) + assertEquals(0, a._x.getAndSet(update)) + assertTrue(a._x.compareAndSet(update, 8)) a._x.lazySet(1) - check(a.x == 1) - check(a._x.getAndSet(2) == 1) - check(a.x == 2) - check(a._x.getAndIncrement() == 2) - check(a.x == 3) - check(a._x.getAndDecrement() == 3) - check(a.x == 2) - check(a._x.getAndAdd(2) == 2) - check(a.x == 4) - check(a._x.addAndGet(3) == 7) - check(a.x == 7) - check(a._x.incrementAndGet() == 8) - check(a.x == 8) - check(a._x.decrementAndGet() == 7) - check(a.x == 7) - check(a._x.compareAndSet(7, 10)) + assertEquals(1, a.x) + assertEquals(1, a._x.getAndSet(2)) + assertEquals(2, a.x) + assertEquals(2, a._x.getAndIncrement()) + assertEquals(3, a.x) + assertEquals(3, a._x.getAndDecrement()) + assertEquals(2, a.x) + assertEquals(2, a._x.getAndAdd(2)) + assertEquals(4, a.x) + assertEquals(7, a._x.addAndGet(3)) + assertEquals(7, a.x) + assertEquals(8, a._x.incrementAndGet()) + assertEquals(8, a.x) + assertEquals(7, a._x.decrementAndGet()) + assertEquals(7, a.x) + assertTrue(a._x.compareAndSet(7, 10)) } fun testLong() { val a = LongArithmetic() - check(a.z.value == 2424920024888888848) + assertEquals(2424920024888888848, a.z.value) a.z.lazySet(8424920024888888848) - check(a.z.value == 8424920024888888848) - check(a.z.getAndSet(8924920024888888848) == 8424920024888888848) - check(a.z.value == 8924920024888888848) - check(a.z.incrementAndGet() == 8924920024888888849) // fails - check(a.z.value == 8924920024888888849) - check(a.z.getAndDecrement() == 8924920024888888849) - check(a.z.value == 8924920024888888848) - check(a.z.getAndAdd(100000000000000000) == 8924920024888888848) - check(a.z.value == 9024920024888888848) - check(a.z.addAndGet(-9223372036854775807) == -198452011965886959) - check(a.z.value == -198452011965886959) - check(a.z.incrementAndGet() == -198452011965886958) - check(a.z.value == -198452011965886958) - check(a.z.decrementAndGet() == -198452011965886959) - check(a.z.value == -198452011965886959) + assertEquals(8424920024888888848, a.z.value) + assertEquals(8424920024888888848, a.z.getAndSet(8924920024888888848)) + assertEquals(8924920024888888848, a.z.value) + assertEquals(8924920024888888849, a.z.incrementAndGet()) + assertEquals(8924920024888888849, a.z.value) + assertEquals(8924920024888888849, a.z.getAndDecrement()) + assertEquals(8924920024888888848, a.z.value) + assertEquals(8924920024888888848, a.z.getAndAdd(100000000000000000)) + assertEquals(9024920024888888848, a.z.value) + assertEquals(-198452011965886959, a.z.addAndGet(-9223372036854775807)) + assertEquals(-198452011965886959, a.z.value) + assertEquals(-198452011965886958, a.z.incrementAndGet()) + assertEquals(-198452011965886958, a.z.value) + assertEquals(-198452011965886959, a.z.decrementAndGet()) + assertEquals(-198452011965886959, a.z.value) } fun testBoolean() { val a = BooleanArithmetic() - check(!a.x) + assertEquals(false, a._x.value) + assertFalse(a.x) a._x.lazySet(true) - check(a.x) - check(a._x.getAndSet(true)) - check(a._x.compareAndSet(true, false)) - check(!a.x) + assertTrue(a.x) + assertTrue(a._x.getAndSet(true)) + assertTrue(a._x.compareAndSet(true, false)) + assertFalse(a.x) } fun testReference() { val a = ReferenceArithmetic() a._x.value = "aaa" - check(a._x.value == "aaa") + assertEquals("aaa", a._x.value) a._x.lazySet("bb") - check(a._x.value == "bb") - check(a._x.getAndSet("ccc") == "bb") - check(a._x.value == "ccc") + assertEquals("bb", a._x.value) + assertEquals("bb", a._x.getAndSet("ccc")) + assertEquals("ccc", a._x.value) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.txt new file mode 100644 index 00000000000..c0caa0ced1a --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.txt @@ -0,0 +1,103 @@ +@kotlin.Metadata +public final class ArithmeticTest { + // source: 'ArithmeticTest.kt' + private final static @org.jetbrains.annotations.NotNull field local$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field local: int + static method (): void + public method (): void + public final static @org.jetbrains.annotations.NotNull method getLocal$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getLocal(): int + public final method testAtomicCallPlaces(): boolean + public final method testBoolean(): void + public final method testGetValue(): void + public final method testInt(): void + public final method testLong(): void + public final method testReference(): void +} + +@kotlin.Metadata +public final class ArithmeticTestKt { + // source: 'ArithmeticTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public final class BooleanArithmetic { + // source: 'ArithmeticTest.kt' + private final static @org.jetbrains.annotations.NotNull field _x$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field _x: int + static method (): void + public method (): void + public final method getX(): boolean + public final static @org.jetbrains.annotations.NotNull method get_x$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method get_x(): int +} + +@kotlin.Metadata +public final class IntArithmetic { + // source: 'ArithmeticTest.kt' + private final static @org.jetbrains.annotations.NotNull field _x$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field _x: int + static method (): void + public method (): void + public final method getX(): int + public final static @org.jetbrains.annotations.NotNull method get_x$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method get_x(): int +} + +@kotlin.Metadata +public final class LongArithmetic { + // source: 'ArithmeticTest.kt' + private final static @org.jetbrains.annotations.NotNull field _x$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field _x: long + private final static @org.jetbrains.annotations.NotNull field max$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field max: long + private final static @org.jetbrains.annotations.NotNull field y$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field y: long + private final static @org.jetbrains.annotations.NotNull field z$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field z: long + static method (): void + public method (): void + public final static @org.jetbrains.annotations.NotNull method getMax$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method getMax(): long + public final method getX(): long + public final static @org.jetbrains.annotations.NotNull method getY$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method getY(): long + public final static @org.jetbrains.annotations.NotNull method getZ$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method getZ(): long + public final static @org.jetbrains.annotations.NotNull method get_x$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method get_x(): long +} + +@kotlin.Metadata +public final class ReferenceArithmetic { + // source: 'ArithmeticTest.kt' + private final static @org.jetbrains.annotations.NotNull field _x$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field _x: java.lang.Object + static method (): void + public method (): void + public final static @org.jetbrains.annotations.NotNull method get_x$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method get_x(): java.lang.Object +} + +@kotlin.Metadata +public final class VisibilitiesTest { + // source: 'ArithmeticTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field b: int + private final static @org.jetbrains.annotations.NotNull field c$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field c: int + private final static @org.jetbrains.annotations.NotNull field d$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field d: int + static method (): void + public method (): void + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final static @org.jetbrains.annotations.NotNull method getB$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getB(): int + public final static @org.jetbrains.annotations.NotNull method getD$FU$main(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getD$main(): int + public final method test(): void +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.kt new file mode 100644 index 00000000000..d8c04b62542 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.kt @@ -0,0 +1,79 @@ +import kotlinx.atomicfu.* +import kotlin.test.* + +class ArrayInlineExtensionTest { + val intArr = AtomicIntArray(10) + val a = atomic(100) + val longArr = AtomicLongArray(10) + val refArr = atomicArrayOfNulls(5) + + class A(val s: String) + + private inline fun casLoop(to: Int): Int { + intArr[0].loop { cur -> + if (intArr[0].compareAndSet(cur, to)) return intArr[0].value + return 777 + } + } + + private inline fun casLoopExpression(to: Long): Long = longArr[3].loop { cur -> + if (longArr[3].compareAndSet(cur, to)) return longArr[3].value + return 777 + } + + private inline fun AtomicInt.extensionLoop(to: Int): Int { + loop { cur -> + if (compareAndSet(cur, to)) return value + return 777 + } + } + + private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur -> + lazySet(cur + 10) + return if (compareAndSet(cur, to)) value else incrementAndGet() + } + + private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int, index: Int): Int { + loop { cur -> + compareAndSet(cur, first) + intArr[index].compareAndSet(first, second) + return value + } + } + + private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int { + loop { cur -> + compareAndSet(cur, to) + a.extensionLoop(5) + return value + } + } + + private inline fun AtomicInt.foo(to: Int): Int { + loop { cur -> + if (compareAndSet(cur, to)) return 777 + else return value + } + } + + private inline fun AtomicInt.bar(delta: Int): Int { + return foo(value + delta) + } + + fun testIntExtensionLoops() { + assertEquals(5, casLoop(5)) + assertEquals(6, casLoopExpression(6)) + assertEquals(66, intArr[1].extensionLoop(66)) + assertEquals(66, intArr[2].extensionLoop(66)) + assertEquals(77, intArr[1].extensionLoopExpression(777)) + assertEquals(99, intArr[1].extensionLoopMixedReceivers(88, 99, 1)) + assertEquals(100, intArr[1].extensionLoopRecursive(100)) + assertEquals(777, intArr[1].bar(100)) + } +} + +fun box(): String { + val testClass = ArrayInlineExtensionTest() + testClass.testIntExtensionLoops() + return "OK" +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.txt new file mode 100644 index 00000000000..8537aff3c8a --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineExtensionTest.txt @@ -0,0 +1,50 @@ +@kotlin.Metadata +public final class ArrayInlineExtensionTest$A { + // source: 'ArrayInlineExtensionTest.kt' + private final @org.jetbrains.annotations.NotNull field s: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method getS(): java.lang.String + public final inner class ArrayInlineExtensionTest$A +} + +@kotlin.Metadata +public final class ArrayInlineExtensionTest { + // source: 'ArrayInlineExtensionTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final @org.jetbrains.annotations.NotNull field intArr: java.util.concurrent.atomic.AtomicIntegerArray + private final @org.jetbrains.annotations.NotNull field longArr: java.util.concurrent.atomic.AtomicLongArray + private final @org.jetbrains.annotations.NotNull field refArr: java.util.concurrent.atomic.AtomicReferenceArray + static method (): void + public method (): void + public final method bar$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method bar$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + private final method casLoop(p0: int): int + private final method casLoopExpression(p0: long): long + public final method extensionLoop$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionLoop$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final method extensionLoopExpression$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionLoopExpression$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final method extensionLoopMixedReceivers$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int, p4: int, p5: int): int + public final method extensionLoopMixedReceivers$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int, p3: int, p4: int): int + public final method extensionLoopRecursive$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionLoopRecursive$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final method foo$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method foo$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final @org.jetbrains.annotations.NotNull method getIntArr(): java.util.concurrent.atomic.AtomicIntegerArray + public final @org.jetbrains.annotations.NotNull method getLongArr(): java.util.concurrent.atomic.AtomicLongArray + public final @org.jetbrains.annotations.NotNull method getRefArr(): java.util.concurrent.atomic.AtomicReferenceArray + public final method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method testIntExtensionLoops(): void + public final inner class ArrayInlineExtensionTest$A +} + +@kotlin.Metadata +public final class ArrayInlineExtensionTestKt { + // source: 'ArrayInlineExtensionTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineFunctionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.kt similarity index 99% rename from plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineFunctionTest.kt rename to plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.kt index c9d0b1d1ddc..996a10342b7 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineFunctionTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.kt @@ -16,21 +16,18 @@ class ArrayInlineFunctionTest { private fun action(cur: Box?) = cur?.let { Box(cur.n * 10) } - fun testArrayElementUpdate() { refArr[0].lazySet(Box(5)) refArr[0].update { cur -> cur?.let { Box(cur.n * 10) } } assertEquals(refArr[0].value!!.n, 50) } - fun testArrayElementGetAndUpdate() { refArr[0].lazySet(Box(5)) assertEquals(refArr[0].getAndUpdate { cur -> action(cur) }!!.n, 5) assertEquals(refArr[0].value!!.n, 50) } - fun testArrayElementUpdateAndGet() { refArr[0].lazySet(Box(5)) assertEquals(refArr[0].updateAndGet { cur -> action(cur) }!!.n, 50) diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.txt new file mode 100644 index 00000000000..c49b13724b5 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ArrayLoopTest.txt @@ -0,0 +1,38 @@ +@kotlin.Metadata +final class ArrayInlineFunctionTest$Box { + // source: 'ArrayLoopTest.kt' + private final field n: int + public method (p0: int): void + public final method component1(): int + public synthetic static method copy$default(p0: ArrayInlineFunctionTest$Box, p1: int, p2: int, p3: java.lang.Object): ArrayInlineFunctionTest$Box + public final @org.jetbrains.annotations.NotNull method copy(p0: int): ArrayInlineFunctionTest$Box + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final method getN(): int + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + private final inner class ArrayInlineFunctionTest$Box +} + +@kotlin.Metadata +public final class ArrayInlineFunctionTest { + // source: 'ArrayLoopTest.kt' + private final @org.jetbrains.annotations.NotNull field anyArr: java.util.concurrent.atomic.AtomicReferenceArray + private final @org.jetbrains.annotations.NotNull field refArr: java.util.concurrent.atomic.AtomicReferenceArray + public method (): void + private final method action(p0: ArrayInlineFunctionTest$Box): ArrayInlineFunctionTest$Box + public final @org.jetbrains.annotations.Nullable method getAndUpdate$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): java.lang.Object + public final method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final method testArrayElementGetAndUpdate(): void + public final method testArrayElementUpdate(): void + public final method testArrayElementUpdateAndGet(): void + public final method testSetArrayElementValueInLoop(): void + public final method update$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final @org.jetbrains.annotations.Nullable method updateAndGet$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): java.lang.Object + private final inner class ArrayInlineFunctionTest$Box +} + +@kotlin.Metadata +public final class ArrayLoopTestKt { + // source: 'ArrayLoopTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt index 70e23e4c8ef..c330596016d 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.kt @@ -5,59 +5,57 @@ class AtomicArrayTest { fun testIntArray() { val A = AtomicArrayClass() - check(A.intArr[0].compareAndSet(0, 3)) - check(A.intArr[1].value == 0) + assertTrue(A.intArr[0].compareAndSet(0, 3)) + assertEquals(0, A.intArr[1].value) A.intArr[0].lazySet(5) - check(A.intArr[0].value + A.intArr[1].value + A.intArr[2].value == 5) - check(A.intArr[0].compareAndSet(5, 10)) - check(A.intArr[0].getAndDecrement() == 10) - check(A.intArr[0].value == 9) + assertEquals(5, A.intArr[0].value + A.intArr[1].value + A.intArr[2].value) + assertTrue(A.intArr[0].compareAndSet(5, 10)) + assertEquals(10, A.intArr[0].getAndDecrement()) + assertEquals(9, A.intArr[0].value) A.intArr[2].value = 2 - check(A.intArr[2].value == 2) - check(A.intArr[2].compareAndSet(2, 34)) - check(A.intArr[2].value == 34) + assertEquals(2, A.intArr[2].value) + assertTrue(A.intArr[2].compareAndSet(2, 34)) + assertEquals(34, A.intArr[2].value) } - fun testLongArray() { val A = AtomicArrayClass() A.longArr[0].value = 2424920024888888848 - check(A.longArr[0].value == 2424920024888888848) + assertEquals(2424920024888888848, A.longArr[0].value) A.longArr[0].lazySet(8424920024888888848) - check(A.longArr[0].value == 8424920024888888848) + assertEquals(8424920024888888848, A.longArr[0].value) val ac = A.longArr[0].value A.longArr[3].value = ac - check(A.longArr[3].getAndSet(8924920024888888848) == 8424920024888888848) - check(A.longArr[3].value == 8924920024888888848) + assertEquals(8424920024888888848, A.longArr[3].getAndSet(8924920024888888848)) + assertEquals(8924920024888888848, A.longArr[3].value) val ac1 = A.longArr[3].value A.longArr[4].value = ac1 - check(A.longArr[4].incrementAndGet() == 8924920024888888849) - check(A.longArr[4].value == 8924920024888888849) - check(A.longArr[4].getAndDecrement() == 8924920024888888849) - check(A.longArr[4].value == 8924920024888888848) + assertEquals(8924920024888888849, A.longArr[4].incrementAndGet()) + assertEquals(8924920024888888849, A.longArr[4].value) + assertEquals(8924920024888888849, A.longArr[4].getAndDecrement()) + assertEquals(8924920024888888848, A.longArr[4].value) A.longArr[4].value = 8924920024888888848 - check(A.longArr[4].getAndAdd(100000000000000000) == 8924920024888888848) + assertEquals(8924920024888888848, A.longArr[4].getAndAdd(100000000000000000)) val ac2 = A.longArr[4].value A.longArr[1].value = ac2 - check(A.longArr[1].value == 9024920024888888848) - check(A.longArr[1].addAndGet(-9223372036854775807) == -198452011965886959) - check(A.longArr[1].value == -198452011965886959) - check(A.longArr[1].incrementAndGet() == -198452011965886958) - check(A.longArr[1].value == -198452011965886958) - check(A.longArr[1].decrementAndGet() == -198452011965886959) - check(A.longArr[1].value == -198452011965886959) + assertEquals(9024920024888888848, A.longArr[1].value) + assertEquals(-198452011965886959, A.longArr[1].addAndGet(-9223372036854775807)) + assertEquals(-198452011965886959, A.longArr[1].value) + assertEquals(-198452011965886958, A.longArr[1].incrementAndGet()) + assertEquals(-198452011965886958, A.longArr[1].value) + assertEquals(-198452011965886959, A.longArr[1].decrementAndGet()) + assertEquals(-198452011965886959, A.longArr[1].value) } - fun testBooleanArray() { val A = AtomicArrayClass() - check(!A.booleanArr[1].value) - A.booleanArr[1].compareAndSet(false, true) + assertFalse(A.booleanArr[1].value) + assertTrue(A.booleanArr[1].compareAndSet(false, true)) A.booleanArr[0].lazySet(true) - check(!A.booleanArr[2].getAndSet(true)) - check(A.booleanArr[0].value && A.booleanArr[1].value && A.booleanArr[2].value) + assertFalse(A.booleanArr[2].getAndSet(true)) + assertTrue(A.booleanArr[0].value && A.booleanArr[1].value && A.booleanArr[2].value) A.booleanArr[0].value = false - check(!A.booleanArr[0].value) + assertFalse(A.booleanArr[0].value) } fun testRefArray() { @@ -65,14 +63,27 @@ class AtomicArrayTest { val a2 = ARef(2) val a3 = ARef(3) A.refArr[0].value = a2 - check(A.refArr[0].value!!.n == 2) - check(A.refArr[0].compareAndSet(a2, a3)) - check(A.refArr[0].value!!.n == 3) + assertEquals(2, A.refArr[0].value!!.n) + assertTrue(A.refArr[0].compareAndSet(a2, a3)) + assertEquals(3, A.refArr[0].value!!.n) val r0 = A.refArr[0].value A.refArr[3].value = r0 - check(A.refArr[3].value!!.n == 3) + assertEquals(3, A.refArr[3].value!!.n) val a = A.a.value - check(A.refArr[3].compareAndSet(a3, a)) + assertTrue(A.refArr[3].compareAndSet(a3, a)) + } + + fun testAnyArray() { + val A = AtomicArrayClass() + val s1 = "aaa" + val s2 = "bbb" + A.anyArr[0].value = s1 + assertEquals("aaa", A.anyArr[0].value) + assertTrue(A.anyArr[0].compareAndSet(s1, s2)) + assertEquals("bbb", A.anyArr[0].value) + val r0 = A.anyArr[0].value + A.anyArr[3].value = r0 + assertEquals("bbb", A.anyArr[3].value) } } @@ -93,5 +104,6 @@ fun box(): String { testClass.testLongArray() testClass.testBooleanArray() testClass.testRefArray() + testClass.testAnyArray() return "OK" } \ No newline at end of file diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.txt new file mode 100644 index 00000000000..8714e859e03 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.txt @@ -0,0 +1,51 @@ +@kotlin.Metadata +public final class ARef { + // source: 'AtomicArrayTest.kt' + private final field n: int + public method (p0: int): void + public final method component1(): int + public synthetic static method copy$default(p0: ARef, p1: int, p2: int, p3: java.lang.Object): ARef + public final @org.jetbrains.annotations.NotNull method copy(p0: int): ARef + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final method getN(): int + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String +} + +@kotlin.Metadata +public final class AtomicArrayClass { + // source: 'AtomicArrayTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field a: java.lang.Object + private final @org.jetbrains.annotations.NotNull field anyArr: java.util.concurrent.atomic.AtomicReferenceArray + private final @org.jetbrains.annotations.NotNull field booleanArr: java.util.concurrent.atomic.AtomicIntegerArray + private final @org.jetbrains.annotations.NotNull field intArr: java.util.concurrent.atomic.AtomicIntegerArray + private final @org.jetbrains.annotations.NotNull field longArr: java.util.concurrent.atomic.AtomicLongArray + private final @org.jetbrains.annotations.NotNull field refArr: java.util.concurrent.atomic.AtomicReferenceArray + static method (): void + public method (): void + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getA(): java.lang.Object + public final @org.jetbrains.annotations.NotNull method getAnyArr(): java.util.concurrent.atomic.AtomicReferenceArray + public final @org.jetbrains.annotations.NotNull method getBooleanArr(): java.util.concurrent.atomic.AtomicIntegerArray + public final @org.jetbrains.annotations.NotNull method getIntArr(): java.util.concurrent.atomic.AtomicIntegerArray + public final @org.jetbrains.annotations.NotNull method getLongArr(): java.util.concurrent.atomic.AtomicLongArray + public final @org.jetbrains.annotations.NotNull method getRefArr(): java.util.concurrent.atomic.AtomicReferenceArray +} + +@kotlin.Metadata +public final class AtomicArrayTest { + // source: 'AtomicArrayTest.kt' + public method (): void + public final method testAnyArray(): void + public final method testBooleanArray(): void + public final method testIntArray(): void + public final method testLongArray(): void + public final method testRefArray(): void +} + +@kotlin.Metadata +public final class AtomicArrayTestKt { + // source: 'AtomicArrayTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.kt new file mode 100644 index 00000000000..98c356b5a4f --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.kt @@ -0,0 +1,51 @@ +import kotlinx.atomicfu.* +import kotlin.test.* + +class LoopTest { + val a = atomic(10) + val b = atomic(11) + val c = atomic(12) + val r = atomic("aaa") + val intArr = AtomicIntArray(10) + + private inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int = + loop { cur1 -> + compareAndSet(cur1, to) + loop { cur2 -> + return cur2 + } + } + + private inline fun embeddedLoops(to: Int): Int = + a.loop { aValue -> + b.loop { bValue -> + if (b.compareAndSet(bValue, to)) return aValue + bValue + } + } + + private inline fun embeddedUpdate(to: Int): Int = + a.loop { aValue -> + a.compareAndSet(aValue, to) + return a.updateAndGet { cur -> cur + 100 } + } + + private inline fun AtomicRef.extesntionEmbeddedRefUpdate(to: String): String = + loop { value -> + compareAndSet(value, to) + return updateAndGet { cur -> "${cur}AAA" } + } + + fun test() { + assertEquals(21, embeddedLoops(12)) + assertEquals(77, c.extensionEmbeddedLoops(77)) + assertEquals(66, intArr[0].extensionEmbeddedLoops(66)) + assertEquals(166, embeddedUpdate(66)) + assertEquals("bbbAAA", r.extesntionEmbeddedRefUpdate("bbb")) + } +} + +fun box(): String { + val testClass = LoopTest() + testClass.test() + return "OK" +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.txt new file mode 100644 index 00000000000..05c2ab1e2ac --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ComplexLoopTest.txt @@ -0,0 +1,44 @@ +@kotlin.Metadata +public final class ComplexLoopTestKt { + // source: 'ComplexLoopTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public final class LoopTest { + // source: 'ComplexLoopTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field b: int + private final static @org.jetbrains.annotations.NotNull field c$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field c: int + private final @org.jetbrains.annotations.NotNull field intArr: java.util.concurrent.atomic.AtomicIntegerArray + private final static @org.jetbrains.annotations.NotNull field r$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field r: java.lang.Object + static method (): void + public method (): void + private final method embeddedLoops(p0: int): int + private final method embeddedUpdate(p0: int): int + public final method extensionEmbeddedLoops$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionEmbeddedLoops$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final @org.jetbrains.annotations.NotNull method extesntionEmbeddedRefUpdate$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String): java.lang.String + public final @org.jetbrains.annotations.NotNull method extesntionEmbeddedRefUpdate$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p2: java.lang.String): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final static @org.jetbrains.annotations.NotNull method getB$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getB(): int + public final static @org.jetbrains.annotations.NotNull method getC$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getC(): int + public final @org.jetbrains.annotations.NotNull method getIntArr(): java.util.concurrent.atomic.AtomicIntegerArray + public final static @org.jetbrains.annotations.NotNull method getR$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getR(): java.lang.Object + public final method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method test(): void + public final @org.jetbrains.annotations.Nullable method updateAndGet$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): java.lang.Object + public final method updateAndGet$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): int + public final @org.jetbrains.annotations.Nullable method updateAndGet$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): java.lang.Object +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt index 7c62be1b0d5..af7b28cbddb 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.kt @@ -4,7 +4,7 @@ import kotlin.test.* private val _topLevelInt = atomic(42) var topLevelInt: Int by _topLevelInt -var vIntTopLevel by atomic(55) +private var topLevelVolatile by atomic(56) class DelegatedProperties { private val _a = atomic(42) @@ -30,7 +30,7 @@ class DelegatedProperties { class A (val b: B) class B (val n: Int) - fun testDelegatedAtomicInt() { + fun testDelegatedAtomicInt() { assertEquals(42, a) _a.compareAndSet(42, 56) assertEquals(56, a) @@ -96,6 +96,7 @@ class DelegatedProperties { inner class D { var b: Int by _a + var c by atomic("aaa") } fun testScopedDelegatedProperties() { @@ -107,6 +108,10 @@ class DelegatedProperties { _a.compareAndSet(77, 66) assertEquals(66, _a.value) assertEquals(66, clazz.b) + + assertEquals("aaa", clazz.c) + clazz.c = "bbb" + assertEquals("bbb", clazz.c) } fun test() { @@ -130,15 +135,18 @@ fun testTopLevelDelegatedProperties() { _topLevelInt.compareAndSet(77, 66) assertEquals(66, _topLevelInt.value) assertEquals(66, topLevelInt) +} - assertEquals(55, vIntTopLevel) - vIntTopLevel = 70 - assertEquals(140, vIntTopLevel * 2) +fun testTopLevelVolatileProperties() { + assertEquals(56, topLevelVolatile) + topLevelVolatile = 55 + assertEquals(110, topLevelVolatile * 2) } fun box(): String { val testClass = DelegatedProperties() testClass.test() - //testTopLevelDelegatedProperties() + testTopLevelDelegatedProperties() + testTopLevelVolatileProperties() return "OK" -} \ No newline at end of file +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.txt new file mode 100644 index 00000000000..5585e41d9b6 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/DelegatedPropertiesTest.txt @@ -0,0 +1,107 @@ +@kotlin.Metadata +public final class DelegatedProperties$A { + // source: 'DelegatedPropertiesTest.kt' + private final @org.jetbrains.annotations.NotNull field b: DelegatedProperties$B + public method (@org.jetbrains.annotations.NotNull p0: DelegatedProperties$B): void + public final @org.jetbrains.annotations.NotNull method getB(): DelegatedProperties$B + public final inner class DelegatedProperties$A +} + +@kotlin.Metadata +public final class DelegatedProperties$B { + // source: 'DelegatedPropertiesTest.kt' + private final field n: int + public method (p0: int): void + public final method getN(): int + public final inner class DelegatedProperties$B +} + +@kotlin.Metadata +public final class DelegatedProperties$D { + // source: 'DelegatedPropertiesTest.kt' + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field c: java.lang.Object + synthetic final field this$0: DelegatedProperties + public method (p0: DelegatedProperties): void + public final method getB(): int + public final @org.jetbrains.annotations.NotNull method getC(): java.lang.String + public final method setB(p0: int): void + public final method setC(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final inner class DelegatedProperties$D +} + +@kotlin.Metadata +public final class DelegatedProperties { + // source: 'DelegatedPropertiesTest.kt' + private final static @org.jetbrains.annotations.NotNull field _a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field _a: int + private final static @org.jetbrains.annotations.NotNull field _b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field _b: int + private final static @org.jetbrains.annotations.NotNull field _l$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field _l: long + private final static @org.jetbrains.annotations.NotNull field _ref$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field _ref: java.lang.Object + private volatile @kotlin.jvm.Volatile field vBoolean: int + private volatile @kotlin.jvm.Volatile field vInt: int + private volatile @kotlin.jvm.Volatile field vLong: int + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field vRef: java.lang.Object + static method (): void + public method (): void + public synthetic final static method access$get_a$p(p0: DelegatedProperties): int + public synthetic final static method access$set_a$p(p0: DelegatedProperties, p1: int): void + public final method getA(): int + public final method getB(): boolean + public final method getL(): long + public final @org.jetbrains.annotations.NotNull method getRef(): DelegatedProperties$A + public final method getVBoolean(): boolean + public final method getVInt(): int + public final method getVLong(): int + public final @org.jetbrains.annotations.NotNull method getVRef(): DelegatedProperties$A + public final method setA(p0: int): void + public final method setB(p0: boolean): void + public final method setL(p0: long): void + public final method setRef(@org.jetbrains.annotations.NotNull p0: DelegatedProperties$A): void + public final method setVBoolean(p0: boolean): void + public final method setVInt(p0: int): void + public final method setVLong(p0: int): void + public final method setVRef(@org.jetbrains.annotations.NotNull p0: DelegatedProperties$A): void + public final method test(): void + public final method testDelegatedAtomicBoolean(): void + public final method testDelegatedAtomicInt(): void + public final method testDelegatedAtomicLong(): void + public final method testDelegatedAtomicRef(): void + public final method testScopedDelegatedProperties(): void + public final method testVolatileBoolean(): void + public final method testVolatileInt(): void + public final method testVolatileLong(): void + public final method testVolatileRef(): void + public final inner class DelegatedProperties$A + public final inner class DelegatedProperties$B + public final inner class DelegatedProperties$D +} + +@kotlin.Metadata +public final class DelegatedPropertiesTestKt { + // source: 'DelegatedPropertiesTest.kt' + public final static @org.jetbrains.annotations.NotNull field _topLevelInt$DelegatedPropertiesTest$VolatileWrapper: _topLevelInt$DelegatedPropertiesTest$VolatileWrapper + private volatile static @kotlin.jvm.Volatile field topLevelVolatile: int + static method (): void + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method getTopLevelInt(): int + private final static method getTopLevelVolatile(): int + public final static method setTopLevelInt(p0: int): void + private final static method setTopLevelVolatile(p0: int): void + public final static method testTopLevelDelegatedProperties(): void + public final static method testTopLevelVolatileProperties(): void +} + +@kotlin.Metadata +public final class _topLevelInt$DelegatedPropertiesTest$VolatileWrapper { + // source: 'DelegatedPropertiesTest.kt' + private final static @org.jetbrains.annotations.NotNull field _topLevelInt$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field _topLevelInt: int + static method (): void + public method (): void + public synthetic final static method access$get_topLevelInt$FU$p(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public synthetic final static method access$get_topLevelInt$p(p0: _topLevelInt$DelegatedPropertiesTest$VolatileWrapper): int + public synthetic final static method access$set_topLevelInt$p(p0: _topLevelInt$DelegatedPropertiesTest$VolatileWrapper, p1: int): void +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.kt new file mode 100644 index 00000000000..ec5acaa494d --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.kt @@ -0,0 +1,92 @@ +import kotlinx.atomicfu.* +import kotlin.test.* + +class LoopTest { + val a = atomic(0) + val a1 = atomic(1) + val b = atomic(true) + val l = atomic(5000000000) + val r = atomic(A("aaaa")) + val rs = atomic("bbbb") + + class A(val s: String) + + private inline fun casLoop(to: Int): Int { + a.loop { cur -> + if (a.compareAndSet(cur, to)) return a.value + return 777 + } + } + + private inline fun casLoopExpression(to: Int): Int = a.loop { cur -> + if (a.compareAndSet(cur, to)) return a.value + return 777 + } + + private inline fun AtomicInt.extensionLoop(to: Int): Int { + loop { cur -> + if (compareAndSet(cur, to)) return value + return 777 + } + } + + private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur -> + lazySet(cur + 10) + return if (compareAndSet(cur, to)) value else incrementAndGet() + } + + private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int { + loop { cur -> + compareAndSet(cur, first) + a.compareAndSet(first, second) + return value + } + } + + private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int { + loop { cur -> + compareAndSet(cur, to) + a.extensionLoop(5) + return value + } + } + + private inline fun AtomicInt.foo(to: Int): Int { + loop { cur -> + if (compareAndSet(cur, to)) return 777 + else return value + } + } + + private inline fun AtomicInt.bar(delta: Int): Int { + return foo(value + delta) + } + + fun testIntExtensionLoops() { + assertEquals(5, casLoop(5)) + assertEquals(6, casLoopExpression(6)) + assertEquals(66, a.extensionLoop(66)) + assertEquals(77, a.extensionLoopExpression(777)) + assertEquals(99, a.extensionLoopMixedReceivers(88, 99)) + assertEquals(5, a.extensionLoopRecursive(100)) + assertEquals(777, a.bar(100)) + } +} + +private val ref = atomic("aaa") + +private inline fun AtomicRef.topLevelExtensionLoop(to: String): String = loop { cur -> + lazySet(cur + to) + return value +} + +fun testTopLevelExtensionLoop() { + assertEquals("aaattt", ref.topLevelExtensionLoop("ttt")) +} + +fun box(): String { + val testClass = LoopTest() + testClass.testIntExtensionLoops() + testTopLevelExtensionLoop() + return "OK" +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.txt new file mode 100644 index 00000000000..9d12d510cfa --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionLoopTest.txt @@ -0,0 +1,80 @@ +@kotlin.Metadata +public final class ExtensionLoopTestKt { + // source: 'ExtensionLoopTest.kt' + public final static @org.jetbrains.annotations.NotNull field ref$ExtensionLoopTest$VolatileWrapper: Ref$ExtensionLoopTest$VolatileWrapper + static method (): void + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final static method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final static method testTopLevelExtensionLoop(): void + public final static @org.jetbrains.annotations.NotNull method topLevelExtensionLoop$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, @org.jetbrains.annotations.NotNull p3: java.lang.String): java.lang.String + public final static @org.jetbrains.annotations.NotNull method topLevelExtensionLoop$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p2: java.lang.String): java.lang.String +} + +@kotlin.Metadata +public final class LoopTest$A { + // source: 'ExtensionLoopTest.kt' + private final @org.jetbrains.annotations.NotNull field s: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method getS(): java.lang.String + public final inner class LoopTest$A +} + +@kotlin.Metadata +public final class LoopTest { + // source: 'ExtensionLoopTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private final static @org.jetbrains.annotations.NotNull field a1$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a1: int + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field b: int + private final static @org.jetbrains.annotations.NotNull field l$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field l: long + private final static @org.jetbrains.annotations.NotNull field r$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field r: java.lang.Object + private final static @org.jetbrains.annotations.NotNull field rs$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field rs: java.lang.Object + static method (): void + public method (): void + public final method bar$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method bar$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + private final method casLoop(p0: int): int + private final method casLoopExpression(p0: int): int + public final method extensionLoop$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionLoop$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final method extensionLoopExpression$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionLoopExpression$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final method extensionLoopMixedReceivers$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int, p4: int): int + public final method extensionLoopMixedReceivers$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int, p3: int): int + public final method extensionLoopRecursive$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method extensionLoopRecursive$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final method foo$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int + public final method foo$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final static @org.jetbrains.annotations.NotNull method getA1$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA1(): int + public final static @org.jetbrains.annotations.NotNull method getB$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getB(): int + public final static @org.jetbrains.annotations.NotNull method getL$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method getL(): long + public final static @org.jetbrains.annotations.NotNull method getR$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getR(): java.lang.Object + public final static @org.jetbrains.annotations.NotNull method getRs$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getRs(): java.lang.Object + public final method loop$atomicfu$array(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method testIntExtensionLoops(): void + public final inner class LoopTest$A +} + +@kotlin.Metadata +public final class Ref$ExtensionLoopTest$VolatileWrapper { + // source: 'ExtensionLoopTest.kt' + private final static @org.jetbrains.annotations.NotNull field ref$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field ref: java.lang.Object + static method (): void + public method (): void + public synthetic final static method access$getRef$FU$p(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionsTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionsTest.txt new file mode 100644 index 00000000000..b866de48529 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ExtensionsTest.txt @@ -0,0 +1,38 @@ +@kotlin.Metadata +public final class ExtensionsTest { + // source: 'ExtensionsTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field b: int + private final static @org.jetbrains.annotations.NotNull field l$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field l: long + private final static @org.jetbrains.annotations.NotNull field s$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field s: java.lang.Object + static method (): void + public method (): void + public final method booleanExtensionArithmetic$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int): void + public final method booleanExtensionArithmetic$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater): void + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final static @org.jetbrains.annotations.NotNull method getB$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getB(): int + public final static @org.jetbrains.annotations.NotNull method getL$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method getL(): long + public final static @org.jetbrains.annotations.NotNull method getS$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getS(): java.lang.Object + public final method intExtensionArithmetic$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int): void + public final method intExtensionArithmetic$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater): void + public final method longExtensionArithmetic$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicLongArray, p2: int): void + public final method longExtensionArithmetic$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicLongFieldUpdater): void + public final method refExtension$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int): void + public final method refExtension$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater): void + public final method testExtension(): void + public final method testScopedFieldGetters(): void +} + +@kotlin.Metadata +public final class ExtensionsTestKt { + // source: 'ExtensionsTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.kt index 69254e07707..e2bc18b8518 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.kt @@ -10,13 +10,13 @@ class IndexArrayElementGetterTest { clazz.intArr[8].value = 3 val i = fib(4) val j = fib(5) - assertEquals(clazz.intArr[i + j].value, 3) - assertEquals(clazz.intArr[fib(4) + fib(5)].value, 3) + assertEquals(3, clazz.intArr[i + j].value) + assertEquals(3, clazz.intArr[fib(4) + fib(5)].value) clazz.longArr[3].value = 100 - assertEquals(clazz.longArr[fib(6) - fib(5)].value, 100) - assertEquals(clazz.longArr[(fib(6) + fib(4)) % 8].value, 100) - assertEquals(clazz.longArr[(fib(6) + fib(4)) % 8].value, 100) - assertEquals(clazz.longArr[(fib(4) + fib(5)) % fib(5)].value, 100) + assertEquals(100, clazz.longArr[fib(6) - fib(5)].value) + assertEquals(100, clazz.longArr[(fib(6) + fib(4)) % 8].value) + assertEquals(100, clazz.longArr[(fib(6) + fib(4)) % 8].value) + assertEquals(100, clazz.longArr[(fib(4) + fib(5)) % fib(5)].value) } class AtomicArrayClass { diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.txt new file mode 100644 index 00000000000..70ab6517779 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/IndexArrayElementGetterTest.txt @@ -0,0 +1,26 @@ +@kotlin.Metadata +public final class IndexArrayElementGetterTest$AtomicArrayClass { + // source: 'IndexArrayElementGetterTest.kt' + private final @org.jetbrains.annotations.NotNull field intArr: java.util.concurrent.atomic.AtomicIntegerArray + private final @org.jetbrains.annotations.NotNull field longArr: java.util.concurrent.atomic.AtomicLongArray + public method (): void + public final @org.jetbrains.annotations.NotNull method getIntArr(): java.util.concurrent.atomic.AtomicIntegerArray + public final @org.jetbrains.annotations.NotNull method getLongArr(): java.util.concurrent.atomic.AtomicLongArray + public final inner class IndexArrayElementGetterTest$AtomicArrayClass +} + +@kotlin.Metadata +public final class IndexArrayElementGetterTest { + // source: 'IndexArrayElementGetterTest.kt' + private final @org.jetbrains.annotations.NotNull field clazz: IndexArrayElementGetterTest$AtomicArrayClass + public method (): void + public final method fib(p0: int): int + public final method testIndexArrayElementGetting(): void + public final inner class IndexArrayElementGetterTest$AtomicArrayClass +} + +@kotlin.Metadata +public final class IndexArrayElementGetterTestKt { + // source: 'IndexArrayElementGetterTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.kt index 9965829c89c..ea58420875e 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.kt @@ -8,8 +8,9 @@ class InlineExtensionWithTypeParameterTest { private inline fun > AtomicRef.foo( id: Int, startFrom: S - ) { - startFrom.getSegmentId() + ): Int { + lazySet(startFrom) + return value.getSegmentId() } private inline fun > S.getSegmentId(): Int { @@ -17,10 +18,11 @@ class InlineExtensionWithTypeParameterTest { return cur.id } + val sref = atomic(SemaphoreSegment(0)) + fun testInlineExtensionWithTypeParameter() { - val s = SemaphoreSegment(0) - val sref = atomic(s) - sref.foo(0, s) + val s = SemaphoreSegment(77) + assertEquals(77, sref.foo(0, s)) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.txt new file mode 100644 index 00000000000..7e8096bb130 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/InlineExtensionWithTypeParameterTest.txt @@ -0,0 +1,38 @@ +@kotlin.Metadata +public abstract class InlineExtensionWithTypeParameterTest$Segment { + // source: 'InlineExtensionWithTypeParameterTest.kt' + private final field id: int + public method (p0: int): void + public final method getId(): int + public abstract inner class InlineExtensionWithTypeParameterTest$Segment +} + +@kotlin.Metadata +public final class InlineExtensionWithTypeParameterTest$SemaphoreSegment { + // source: 'InlineExtensionWithTypeParameterTest.kt' + public method (p0: int): void + public final inner class InlineExtensionWithTypeParameterTest$SemaphoreSegment +} + +@kotlin.Metadata +public final class InlineExtensionWithTypeParameterTest { + // source: 'InlineExtensionWithTypeParameterTest.kt' + private final static @org.jetbrains.annotations.NotNull field sref$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field sref: java.lang.Object + static method (): void + public method (): void + public final method foo$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: InlineExtensionWithTypeParameterTest$Segment): int + public final method foo$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: int, @org.jetbrains.annotations.NotNull p3: InlineExtensionWithTypeParameterTest$Segment): int + private final method getSegmentId(p0: InlineExtensionWithTypeParameterTest$Segment): int + public final static @org.jetbrains.annotations.NotNull method getSref$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getSref(): java.lang.Object + public final method testInlineExtensionWithTypeParameter(): void + public abstract inner class InlineExtensionWithTypeParameterTest$Segment + public final inner class InlineExtensionWithTypeParameterTest$SemaphoreSegment +} + +@kotlin.Metadata +public final class InlineExtensionWithTypeParameterTestKt { + // source: 'InlineExtensionWithTypeParameterTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.kt new file mode 100644 index 00000000000..e90fc8ba7a7 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.kt @@ -0,0 +1,37 @@ +import kotlinx.atomicfu.* +import kotlin.test.* + +class LambdaTest { + val a = atomic(0) + val rs = atomic("bbbb") + + private inline fun inlineLambda( + arg: Int, + crossinline block: (Int) -> Unit + ) = block(arg) + + fun loopInLambda1(to: Int) = inlineLambda(to) sc@ { arg -> + a.loop { value -> + a.compareAndSet(value, arg) + return@sc + } + } + + fun loopInLambda2(to: Int) = inlineLambda(to) { arg1 -> + inlineLambda(arg1) sc@ { arg2 -> + a.loop { value -> + a.compareAndSet(value, arg2) + return@sc + } + } + } +} + +fun box(): String { + val testClass = LambdaTest() + testClass.loopInLambda1(34) + assertEquals(34, testClass.a.value) + testClass.loopInLambda1(77) + assertEquals(77, testClass.a.value) + return "OK" +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.txt new file mode 100644 index 00000000000..ccc0f4873f3 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LambdaTest.txt @@ -0,0 +1,24 @@ +@kotlin.Metadata +public final class LambdaTest { + // source: 'LambdaTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field rs$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field rs: java.lang.Object + static method (): void + public method (): void + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final static @org.jetbrains.annotations.NotNull method getRs$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getRs(): java.lang.Object + private final method inlineLambda(p0: int, p1: kotlin.jvm.functions.Function1): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method loopInLambda1(p0: int): void + public final method loopInLambda2(p0: int): void +} + +@kotlin.Metadata +public final class LambdaTestKt { + // source: 'LambdaTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.kt similarity index 60% rename from plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.kt rename to plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.kt index 3a240a45f6d..6010d7db450 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.kt @@ -2,33 +2,37 @@ import kotlinx.atomicfu.* import kotlinx.atomicfu.locks.* import kotlin.test.* -class PropertyDeclarationTest { +class LateinitPropertiesTest { private val a: AtomicInt private val head: AtomicRef + private val dataRef: AtomicRef private val lateIntArr: AtomicIntArray private val lateRefArr: AtomicArray - private val lock: ReentrantLock + + private class Data(val n: Int) init { a = atomic(0) head = atomic("AAA") lateIntArr = AtomicIntArray(55) - lateRefArr = atomicArrayOfNulls(10) - lock = reentrantLock() + val data = Data(77) + dataRef = atomic(data) + val size = 10 + lateRefArr = atomicArrayOfNulls(size) } fun test() { assertEquals(0, a.value) - check(head.compareAndSet("AAA", "BBB")) + assertTrue(head.compareAndSet("AAA", "BBB")) assertEquals("BBB", head.value) assertEquals(0, lateIntArr[35].value) + assertEquals(77, dataRef.value.n) assertEquals(null, lateRefArr[5].value) - assertEquals(null, lock) } } fun box(): String { - val testClass = PropertyDeclarationTest() + val testClass = LateinitPropertiesTest() testClass.test() return "OK" -} \ No newline at end of file +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.txt new file mode 100644 index 00000000000..c13986805b0 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LateinitPropertiesTest.txt @@ -0,0 +1,31 @@ +@kotlin.Metadata +final class LateinitPropertiesTest$Data { + // source: 'LateinitPropertiesTest.kt' + private final field n: int + public method (p0: int): void + public final method getN(): int + private final inner class LateinitPropertiesTest$Data +} + +@kotlin.Metadata +public final class LateinitPropertiesTest { + // source: 'LateinitPropertiesTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field dataRef$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field dataRef: java.lang.Object + private final static @org.jetbrains.annotations.NotNull field head$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field head: java.lang.Object + private final @org.jetbrains.annotations.NotNull field lateIntArr: java.util.concurrent.atomic.AtomicIntegerArray + private final @org.jetbrains.annotations.NotNull field lateRefArr: java.util.concurrent.atomic.AtomicReferenceArray + static method (): void + public method (): void + public final method test(): void + private final inner class LateinitPropertiesTest$Data +} + +@kotlin.Metadata +public final class LateinitPropertiesTestKt { + // source: 'LateinitPropertiesTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.kt index 78d84a992b7..47b755e8055 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.kt @@ -4,22 +4,22 @@ import kotlin.test.* class LockFreeIntBitsTest { fun testBasic() { val bs = LockFreeIntBits() - check(!bs[0]) - check(bs.bitSet(0)) - check(bs[0]) - check(!bs.bitSet(0)) + assertTrue(!bs[0]) + assertTrue(bs.bitSet(0)) + assertTrue(bs[0]) + assertTrue(!bs.bitSet(0)) - check(!bs[1]) - check(bs.bitSet(1)) - check(bs[1]) - check(!bs.bitSet(1)) - check(!bs.bitSet(0)) + assertTrue(!bs[1]) + assertTrue(bs.bitSet(1)) + assertTrue(bs[1]) + assertTrue(!bs.bitSet(1)) + assertTrue(!bs.bitSet(0)) - check(bs[0]) - check(bs.bitClear(0)) - check(!bs.bitClear(0)) + assertTrue(bs[0]) + assertTrue(bs.bitClear(0)) + assertTrue(!bs.bitClear(0)) - check(bs[1]) + assertTrue(bs[1]) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.txt new file mode 100644 index 00000000000..e2653d0d1c0 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeIntBitsTest.txt @@ -0,0 +1,27 @@ +@kotlin.Metadata +public final class LockFreeIntBits { + // source: 'LockFreeIntBitsTest.kt' + private final static @org.jetbrains.annotations.NotNull field bits$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field bits: int + static method (): void + public method (): void + public final method bitClear(p0: int): boolean + public final method bitSet(p0: int): boolean + private final method bitUpdate(p0: kotlin.jvm.functions.Function1, p1: kotlin.jvm.functions.Function1): boolean + public final method get(p0: int): boolean + private final method mask(p0: int): int + public final method update$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void +} + +@kotlin.Metadata +public final class LockFreeIntBitsTest { + // source: 'LockFreeIntBitsTest.kt' + public method (): void + public final method testBasic(): void +} + +@kotlin.Metadata +public final class LockFreeIntBitsTestKt { + // source: 'LockFreeIntBitsTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.kt index 853ca9e76d4..580a125e048 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.kt @@ -4,11 +4,11 @@ import kotlin.test.* class LockFreeLongCounterTest { private inline fun testWith(g: LockFreeLongCounter.() -> Long) { val c = LockFreeLongCounter() - check(c.g() == 0L) - check(c.increment() == 1L) - check(c.g() == 1L) - check(c.increment() == 2L) - check(c.g() == 2L) + assertEquals(0L, c.g()) + assertEquals(1L, c.increment()) + assertEquals(1L, c.g()) + assertEquals(2L, c.increment()) + assertEquals(2L, c.g()) } fun testBasic() = testWith { get() } @@ -18,15 +18,15 @@ class LockFreeLongCounterTest { fun testAdd2() { val c = LockFreeLongCounter() c.add2() - check(c.get() == 2L) + assertEquals(2L, c.get()) c.add2() - check(c.get() == 4L) + assertEquals(4L, c.get()) } fun testSetM2() { val c = LockFreeLongCounter() c.setM2() - check(c.get() == -2L) + assertEquals(-2L, c.get()) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.txt new file mode 100644 index 00000000000..03b022fc381 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeLongCounterTest.txt @@ -0,0 +1,41 @@ +@kotlin.Metadata +final class LockFreeLongCounter$Inner { + // source: 'LockFreeLongCounterTest.kt' + synthetic final field this$0: LockFreeLongCounter + public method (p0: LockFreeLongCounter): void + public final method getFromOuter(): long + private final inner class LockFreeLongCounter$Inner +} + +@kotlin.Metadata +public final class LockFreeLongCounter { + // source: 'LockFreeLongCounterTest.kt' + private final static @org.jetbrains.annotations.NotNull field counter$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field counter: long + static method (): void + public method (): void + public synthetic final static method access$getCounter$FU$p(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method add2(): long + public final method get(): long + public final method getInner(): long + public final method increment(): long + public final method setM2(): void + private final inner class LockFreeLongCounter$Inner +} + +@kotlin.Metadata +public final class LockFreeLongCounterTest { + // source: 'LockFreeLongCounterTest.kt' + public method (): void + public final method testAdd2(): void + public final method testBasic(): void + public final method testGetInner(): void + public final method testSetM2(): void + private final method testWith(p0: kotlin.jvm.functions.Function1): void +} + +@kotlin.Metadata +public final class LockFreeLongCounterTestKt { + // source: 'LockFreeLongCounterTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.kt index ae36b81b4cf..9d5a8c8df9f 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.kt @@ -4,15 +4,15 @@ import kotlin.test.* class LockFreeQueueTest { fun testBasic() { val q = LockFreeQueue() - check(q.dequeue() == -1) + assertEquals(-1, q.dequeue()) q.enqueue(42) - check(q.dequeue() == 42) - check(q.dequeue() == -1) + assertEquals(42, q.dequeue()) + assertEquals(-1, q.dequeue()) q.enqueue(1) q.enqueue(2) - check(q.dequeue() == 1) - check(q.dequeue() == 2) - check(q.dequeue() == -1) + assertEquals(1, q.dequeue()) + assertEquals(2, q.dequeue()) + assertEquals(-1, q.dequeue()) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.txt new file mode 100644 index 00000000000..82cfa4dcbf8 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeQueueTest.txt @@ -0,0 +1,41 @@ +@kotlin.Metadata +final class LockFreeQueue$Node { + // source: 'LockFreeQueueTest.kt' + private final static @org.jetbrains.annotations.NotNull field next$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field next: java.lang.Object + private final field value: int + static method (): void + public method (p0: int): void + public final static @org.jetbrains.annotations.NotNull method getNext$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getNext(): java.lang.Object + public final method getValue(): int + private final inner class LockFreeQueue$Node +} + +@kotlin.Metadata +public final class LockFreeQueue { + // source: 'LockFreeQueueTest.kt' + private final static @org.jetbrains.annotations.NotNull field head$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field head: java.lang.Object + private final static @org.jetbrains.annotations.NotNull field tail$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field tail: java.lang.Object + static method (): void + public method (): void + public final method dequeue(): int + public final method enqueue(p0: int): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + private final inner class LockFreeQueue$Node +} + +@kotlin.Metadata +public final class LockFreeQueueTest { + // source: 'LockFreeQueueTest.kt' + public method (): void + public final method testBasic(): void +} + +@kotlin.Metadata +public final class LockFreeQueueTestKt { + // source: 'LockFreeQueueTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.kt index f4c674a5edf..fb97d586d81 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.kt @@ -2,32 +2,31 @@ import kotlinx.atomicfu.* import kotlin.test.* class LockFreeStackTest { - fun testClear() { val s = LockFreeStack() - check(s.isEmpty()) + assertTrue(s.isEmpty()) s.pushLoop("A") - check(!s.isEmpty()) + assertTrue(!s.isEmpty()) s.clear() - check(s.isEmpty()) + assertTrue(s.isEmpty()) } fun testPushPopLoop() { val s = LockFreeStack() - check(s.isEmpty()) + assertTrue(s.isEmpty()) s.pushLoop("A") - check(!s.isEmpty()) - check(s.popLoop() == "A") - check(s.isEmpty()) + assertTrue(!s.isEmpty()) + assertEquals("A", s.popLoop()) + assertTrue(s.isEmpty()) } fun testPushPopUpdate() { val s = LockFreeStack() - check(s.isEmpty()) + assertTrue(s.isEmpty()) s.pushUpdate("A") - check(!s.isEmpty()) - check(s.popUpdate() == "A") - check(s.isEmpty()) + assertTrue(!s.isEmpty()) + assertEquals("A", s.popUpdate()) + assertTrue(s.isEmpty()) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.txt new file mode 100644 index 00000000000..32770ea1fa5 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockFreeStackTest.txt @@ -0,0 +1,44 @@ +@kotlin.Metadata +final class LockFreeStack$Node { + // source: 'LockFreeStackTest.kt' + private final @org.jetbrains.annotations.Nullable field next: LockFreeStack$Node + private final field value: java.lang.Object + public method (p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: LockFreeStack$Node): void + public final @org.jetbrains.annotations.Nullable method getNext(): LockFreeStack$Node + public final method getValue(): java.lang.Object + private final inner class LockFreeStack$Node +} + +@kotlin.Metadata +public final class LockFreeStack { + // source: 'LockFreeStackTest.kt' + private final static @org.jetbrains.annotations.NotNull field top$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field top: java.lang.Object + static method (): void + public method (): void + public final method clear(): void + public final @org.jetbrains.annotations.Nullable method getAndUpdate$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): java.lang.Object + public final method isEmpty(): boolean + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final @org.jetbrains.annotations.Nullable method popLoop(): java.lang.Object + public final @org.jetbrains.annotations.Nullable method popUpdate(): java.lang.Object + public final method pushLoop(p0: java.lang.Object): void + public final method pushUpdate(p0: java.lang.Object): void + public final method update$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + private final inner class LockFreeStack$Node +} + +@kotlin.Metadata +public final class LockFreeStackTest { + // source: 'LockFreeStackTest.kt' + public method (): void + public final method testClear(): void + public final method testPushPopLoop(): void + public final method testPushPopUpdate(): void +} + +@kotlin.Metadata +public final class LockFreeStackTestKt { + // source: 'LockFreeStackTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LockTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LockTest.txt new file mode 100644 index 00000000000..06841461fad --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LockTest.txt @@ -0,0 +1,18 @@ +@kotlin.Metadata +public final class LockTest { + // source: 'LockTest.kt' + private final static @org.jetbrains.annotations.NotNull field inProgressLock$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field inProgressLock: int + static method (): void + public method (): void + public final method testLock(): void +} + +@kotlin.Metadata +public final class LockTestKt { + // source: 'LockTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method reflectionTest(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: java.util.Map): java.util.List + public final static method tryAcquire$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int): boolean + public final static method tryAcquire$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater): boolean +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.kt index b73d1d5b508..c0a9b447053 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.kt @@ -2,81 +2,128 @@ import kotlinx.atomicfu.* import kotlin.test.* class LoopTest { - private val a = atomic(0) - private val r = atomic(A("aaaa")) - private val rs = atomic("bbbb") + val a = atomic(0) + val a1 = atomic(1) + val b = atomic(true) + val l = atomic(5000000000) + val r = atomic(A("aaaa")) + val rs = atomic("bbbb") - private class A(val s: String) + class A(val s: String) - private inline fun casLoop(to: Int): Int { - a.loop { cur -> - if (a.compareAndSet(cur, to)) return a.value - return 777 + fun atomicfuIntLoopTest() { + a.loop { value -> + if (a.compareAndSet(value, 777)) { + assertEquals(777, a.value) + return + } } } - private inline fun casLoopExpression(to: Int): Int = a.loop { cur -> - if (a.compareAndSet(cur, to)) return a.value - return 777 - } - - private inline fun AtomicInt.extensionLoop(to: Int): Int { - loop { cur -> - if (compareAndSet(cur, to)) return value - return 777 + fun atomicfuBooleanLoopTest() { + b.loop { value -> + assertTrue(value) + if (!b.value) return + if (b.compareAndSet(value, false)) { + return + } } } - private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur -> - lazySet(cur + 10) - return if (compareAndSet(cur, to)) value else incrementAndGet() - } - - private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int { - loop { cur -> - compareAndSet(cur, first) - a.compareAndSet(first, second) - return value + fun atomicfuLongLoopTest() { + l.loop { cur -> + if (l.compareAndSet(5000000003, 9000000000)) { + return + } else { + l.incrementAndGet() + } } } - private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int { - loop { cur -> - compareAndSet(cur, to) - a.extensionLoop(5) - return value + fun atomicfuRefLoopTest() { + r.loop { cur -> + assertEquals("aaaa", cur.s) + if (r.compareAndSet(cur, A("bbbb"))) { + return + } } } - fun testIntExtensionLoops() { - assertEquals(5, casLoop(5)) - assertEquals(6, casLoopExpression(6)) - assertEquals(66, a.extensionLoop(66)) - assertEquals(77, a.extensionLoopExpression(777)) - assertEquals(99, a.extensionLoopMixedReceivers(88, 99)) - assertEquals(5, a.extensionLoopRecursive(100)) + inline fun atomicfuLoopTest() { + atomicfuIntLoopTest() + assertEquals(777, a.value) + atomicfuBooleanLoopTest() + assertFalse(b.value) + atomicfuLongLoopTest() + assertEquals(9000000000, l.value) + atomicfuRefLoopTest() + assertEquals("bbbb", r.value.s) } - private inline fun AtomicRef.casLoop(to: String): String = loop { cur -> - if (compareAndSet(cur, A(to))) { - val res = value.s - return "${res}_AtomicRef" + fun atomicfuUpdateTest() { + a.value = 0 + a.update { value -> + val newValue = value + 1000 + if (newValue >= 0) Int.MAX_VALUE else newValue } + assertEquals(Int.MAX_VALUE, a.value) + b.update { true } + assertEquals(true, b.value) + assertTrue(b.value) + l.value = 0L + l.update { cur -> + val newValue = cur + 1000 + if (newValue >= 0L) Long.MAX_VALUE else newValue + } + assertEquals(Long.MAX_VALUE, l.value) + r.lazySet(A("aaaa")) + r.update { cur -> + A("cccc${cur.s}") + } + assertEquals("ccccaaaa", r.value.s) } - private inline fun AtomicRef.casLoop(to: String): String = loop { cur -> - if (compareAndSet(cur, to)) return "${value}_AtomicRef" + fun atomicfuUpdateAndGetTest() { + val res1 = a.updateAndGet { value -> + if (value >= 0) Int.MAX_VALUE else value + } + assertEquals(Int.MAX_VALUE, res1) + assertTrue(b.updateAndGet { true }) + val res2 = l.updateAndGet { cur -> + if (cur >= 0L) Long.MAX_VALUE else cur + } + assertEquals(Long.MAX_VALUE, res2) + r.lazySet(A("aaaa")) + val res3 = r.updateAndGet { cur -> + A("cccc${cur.s}") + } + assertEquals("ccccaaaa", res3.s) } - fun testDeclarationWithEqualNames() { - check(r.casLoop("kk") == "kk_AtomicRef") - check(rs.casLoop("pp") == "pp_AtomicRef") + fun atomicfuGetAndUpdateTest() { + a.getAndUpdate { value -> + if (value >= 0) Int.MAX_VALUE else value + } + assertEquals(Int.MAX_VALUE, a.value) + b.getAndUpdate { true } + assertTrue(b.value) + l.getAndUpdate { cur -> + if (cur >= 0L) Long.MAX_VALUE else cur + } + assertEquals(Long.MAX_VALUE, l.value) + r.lazySet(A("aaaa")) + r.getAndUpdate { cur -> + A("cccc${cur.s}") + } + assertEquals("ccccaaaa", r.value.s) } } fun box(): String { val testClass = LoopTest() - testClass.testIntExtensionLoops() - testClass.testDeclarationWithEqualNames() + testClass.atomicfuLoopTest() + testClass.atomicfuUpdateTest() + testClass.atomicfuUpdateAndGetTest() + testClass.atomicfuGetAndUpdateTest() return "OK" -} \ No newline at end of file +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.txt new file mode 100644 index 00000000000..6fa778594df --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/LoopTest.txt @@ -0,0 +1,66 @@ +@kotlin.Metadata +public final class LoopTest$A { + // source: 'LoopTest.kt' + private final @org.jetbrains.annotations.NotNull field s: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method getS(): java.lang.String + public final inner class LoopTest$A +} + +@kotlin.Metadata +public final class LoopTest { + // source: 'LoopTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private final static @org.jetbrains.annotations.NotNull field a1$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a1: int + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field b: int + private final static @org.jetbrains.annotations.NotNull field l$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field l: long + private final static @org.jetbrains.annotations.NotNull field r$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field r: java.lang.Object + private final static @org.jetbrains.annotations.NotNull field rs$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field rs: java.lang.Object + static method (): void + public method (): void + public final method atomicfuBooleanLoopTest(): void + public final method atomicfuGetAndUpdateTest(): void + public final method atomicfuIntLoopTest(): void + public final method atomicfuLongLoopTest(): void + public final method atomicfuLoopTest(): void + public final method atomicfuRefLoopTest(): void + public final method atomicfuUpdateAndGetTest(): void + public final method atomicfuUpdateTest(): void + public final static @org.jetbrains.annotations.NotNull method getA$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA(): int + public final static @org.jetbrains.annotations.NotNull method getA1$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getA1(): int + public final method getAndUpdate$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): int + public final method getAndUpdate$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): long + public final @org.jetbrains.annotations.Nullable method getAndUpdate$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): java.lang.Object + public final static @org.jetbrains.annotations.NotNull method getB$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater + public final method getB(): int + public final static @org.jetbrains.annotations.NotNull method getL$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater + public final method getL(): long + public final static @org.jetbrains.annotations.NotNull method getR$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getR(): java.lang.Object + public final static @org.jetbrains.annotations.NotNull method getRs$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getRs(): java.lang.Object + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method update$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method update$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method update$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method updateAndGet$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): int + public final method updateAndGet$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): long + public final @org.jetbrains.annotations.Nullable method updateAndGet$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): java.lang.Object + public final inner class LoopTest$A +} + +@kotlin.Metadata +public final class LoopTestKt { + // source: 'LoopTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/MultiInitTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/MultiInitTest.txt new file mode 100644 index 00000000000..544dbac7e40 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/MultiInitTest.txt @@ -0,0 +1,36 @@ +@kotlin.Metadata +public final class MultiInit$Companion { + // source: 'MultiInitTest.kt' + private method (): void + public synthetic method (p0: kotlin.jvm.internal.DefaultConstructorMarker): void + public final method foo(): void + public final inner class MultiInit$Companion +} + +@kotlin.Metadata +public final class MultiInit { + // source: 'MultiInitTest.kt' + public final static @org.jetbrains.annotations.NotNull field Companion: MultiInit$Companion + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field b: int + static method (): void + public method (): void + public final method incA(): int + public final method incB(): int + public final inner class MultiInit$Companion +} + +@kotlin.Metadata +public final class MultiInitTest { + // source: 'MultiInitTest.kt' + public method (): void + public final method testBasic(): void +} + +@kotlin.Metadata +public final class MultiInitTestKt { + // source: 'MultiInitTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ParameterizedInlineFunExtensionTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ParameterizedInlineFunExtensionTest.txt new file mode 100644 index 00000000000..a16a127dde0 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ParameterizedInlineFunExtensionTest.txt @@ -0,0 +1,19 @@ +@kotlin.Metadata +public final class ParameterizedInlineFunExtensionTest { + // source: 'ParameterizedInlineFunExtensionTest.kt' + private final static @org.jetbrains.annotations.NotNull field tail$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field tail: java.lang.Object + static method (): void + public method (): void + public final method bar$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: java.lang.Object, p4: java.lang.Object): java.lang.Object + public final method bar$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.Object, p3: java.lang.Object): java.lang.Object + public final method foo$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: java.lang.Object, p4: java.lang.Object, @org.jetbrains.annotations.NotNull p5: kotlin.jvm.functions.Function1): java.lang.Object + public final method foo$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.Object, p3: java.lang.Object, @org.jetbrains.annotations.NotNull p4: kotlin.jvm.functions.Function1): java.lang.Object + public final method testClose(): void +} + +@kotlin.Metadata +public final class ParameterizedInlineFunExtensionTestKt { + // source: 'ParameterizedInlineFunExtensionTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.txt new file mode 100644 index 00000000000..756bd516c71 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/PropertyDeclarationTest.txt @@ -0,0 +1,19 @@ +@kotlin.Metadata +public final class PropertyDeclarationTest { + // source: 'PropertyDeclarationTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field head$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field head: java.lang.Object + private final @org.jetbrains.annotations.NotNull field lateIntArr: java.util.concurrent.atomic.AtomicIntegerArray + private final @org.jetbrains.annotations.NotNull field lateRefArr: java.util.concurrent.atomic.AtomicReferenceArray + static method (): void + public method (): void + public final method test(): void +} + +@kotlin.Metadata +public final class PropertyDeclarationTestKt { + // source: 'PropertyDeclarationTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.kt index e04d7ad488a..879e3fa2fa7 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.kt @@ -1,20 +1,34 @@ import kotlinx.atomicfu.locks.* import kotlin.test.* -class ReentrantLockTest { +//class ReentrantLockTest { +// private val lock = reentrantLock() +// private var state = 0 +// +// fun testLockField() { +// lock.withLock { +// state = 1 +// } +// assertEquals(1, state) +// } +//} + +class ReentrantLockFieldTest { private val lock = reentrantLock() private var state = 0 - fun testLockField() { + fun testLock() { lock.withLock { state = 1 } assertEquals(1, state) + assertTrue(lock.tryLock()) + lock.unlock() } } fun box(): String { - val testClass = ReentrantLockTest() - testClass.testLockField() + val testClass = ReentrantLockFieldTest() + testClass.testLock() return "OK" } \ No newline at end of file diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.txt new file mode 100644 index 00000000000..76024bda5a3 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ReentrantLockTest.txt @@ -0,0 +1,14 @@ +@kotlin.Metadata +public final class ReentrantLockFieldTest { + // source: 'ReentrantLockTest.kt' + private final @org.jetbrains.annotations.NotNull field lock: java.util.concurrent.locks.ReentrantLock + private field state: int + public method (): void + public final method testLock(): void +} + +@kotlin.Metadata +public final class ReentrantLockTestKt { + // source: 'ReentrantLockTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/ScopeTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/ScopeTest.txt new file mode 100644 index 00000000000..73738e2641e --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/ScopeTest.txt @@ -0,0 +1,69 @@ +@kotlin.Metadata +public final class AA { + // source: 'ScopeTest.kt' + private final @org.jetbrains.annotations.NotNull field b: B + private final @org.jetbrains.annotations.NotNull field c: C + private final field value: int + public method (p0: int): void + public final @org.jetbrains.annotations.NotNull method getB(): B + public final @org.jetbrains.annotations.NotNull method getC(): C + public final method getValue(): int + public final method manyProperties(@org.jetbrains.annotations.NotNull p0: java.lang.Object): boolean + public final method updateToB(@org.jetbrains.annotations.NotNull p0: java.lang.Object): boolean +} + +@kotlin.Metadata +public final class AtomicState { + // source: 'ScopeTest.kt' + private final static @org.jetbrains.annotations.NotNull field state$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field state: java.lang.Object + static method (): void + public method (@org.jetbrains.annotations.NotNull p0: java.lang.Object): void + public final static @org.jetbrains.annotations.NotNull method getState$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater + public final @org.jetbrains.annotations.Nullable method getState(): java.lang.Object +} + +@kotlin.Metadata +public final class B { + // source: 'ScopeTest.kt' + private final field value: int + public method (p0: int): void + public final method getValue(): int +} + +@kotlin.Metadata +public final class C { + // source: 'ScopeTest.kt' + private final @org.jetbrains.annotations.NotNull field d: D + public method (@org.jetbrains.annotations.NotNull p0: D): void + public final @org.jetbrains.annotations.NotNull method getD(): D +} + +@kotlin.Metadata +public final class D { + // source: 'ScopeTest.kt' + private final @org.jetbrains.annotations.NotNull field e: E + public method (@org.jetbrains.annotations.NotNull p0: E): void + public final @org.jetbrains.annotations.NotNull method getE(): E +} + +@kotlin.Metadata +public final class E { + // source: 'ScopeTest.kt' + private final field x: int + public method (p0: int): void + public final method getX(): int +} + +@kotlin.Metadata +public final class ScopeTest { + // source: 'ScopeTest.kt' + public method (): void + public final method scopeTest(): void +} + +@kotlin.Metadata +public final class ScopeTestKt { + // source: 'ScopeTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/SimpleLockTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/SimpleLockTest.txt new file mode 100644 index 00000000000..cdbd1c724db --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/SimpleLockTest.txt @@ -0,0 +1,36 @@ +@kotlin.Metadata +public final class SimpleLock { + // source: 'SimpleLockTest.kt' + private final static @org.jetbrains.annotations.NotNull field _locked$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field _locked: int + static method (): void + public method (): void + public final method loop$atomicfu(@org.jetbrains.annotations.NotNull p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.Nullable p2: java.lang.Object): void + public final method withLock(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): java.lang.Object +} + +@kotlin.Metadata +final class SimpleLockTest$withLock$result$1 { + // source: 'SimpleLockTest.kt' + enclosing method SimpleLockTest.withLock()V + public final static field INSTANCE: SimpleLockTest$withLock$result$1 + inner (anonymous) class SimpleLockTest$withLock$result$1 + static method (): void + method (): void + public synthetic bridge method invoke(): java.lang.Object + public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String +} + +@kotlin.Metadata +public final class SimpleLockTest { + // source: 'SimpleLockTest.kt' + inner (anonymous) class SimpleLockTest$withLock$result$1 + public method (): void + public final method withLock(): void +} + +@kotlin.Metadata +public final class SimpleLockTestKt { + // source: 'SimpleLockTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/SynchronizedObjectTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/SynchronizedObjectTest.txt new file mode 100644 index 00000000000..1a4ab9c0d7a --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/SynchronizedObjectTest.txt @@ -0,0 +1,13 @@ +@kotlin.Metadata +public final class SynchronizedObjectTest { + // source: 'SynchronizedObjectTest.kt' + public method (): void + private final method bar(): java.lang.String + public final method testSync(): void +} + +@kotlin.Metadata +public final class SynchronizedObjectTestKt { + // source: 'SynchronizedObjectTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.kt index 22fe616936a..9c8df62dd70 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.kt @@ -18,126 +18,125 @@ private val stringAtomicNullArr = atomicArrayOfNulls(10) class TopLevelPrimitiveTest { fun testTopLevelInt() { - a.value - check(a.value == 0) - check(a.getAndSet(3) == 0) - check(a.compareAndSet(3, 8)) + assertEquals(0, a.value) + assertEquals(0, a.getAndSet(3)) + assertTrue(a.compareAndSet(3, 8)) a.lazySet(1) - check(a.value == 1) - check(a.getAndSet(2) == 1) - check(a.value == 2) - check(a.getAndIncrement() == 2) - check(a.value == 3) - check(a.getAndDecrement() == 3) - check(a.value == 2) - check(a.getAndAdd(2) == 2) - check(a.value == 4) - check(a.addAndGet(3) == 7) - check(a.value == 7) - check(a.incrementAndGet() == 8) - check(a.value == 8) - check(a.decrementAndGet() == 7) - check(a.value == 7) - a.compareAndSet(7, 10) + assertEquals(1, a.value) + assertEquals(1, a.getAndSet(2)) + assertEquals(2, a.value) + assertEquals(2, a.getAndIncrement()) + assertEquals(3, a.value) + assertEquals(3, a.getAndDecrement()) + assertEquals(2, a.value) + assertEquals(2, a.getAndAdd(2)) + assertEquals(4, a.value) + assertEquals(7, a.addAndGet(3)) + assertEquals(7, a.value) + assertEquals(8, a.incrementAndGet()) + assertEquals(8, a.value) + assertEquals(7, a.decrementAndGet()) + assertEquals(7, a.value) + assertTrue(a.compareAndSet(7, 10)) } fun testTopLevelLong() { - check(b.value == 2424920024888888848) + assertEquals(2424920024888888848, b.value) b.lazySet(8424920024888888848) - check(b.value == 8424920024888888848) - check(b.getAndSet(8924920024888888848) == 8424920024888888848) - check(b.value == 8924920024888888848) - check(b.incrementAndGet() == 8924920024888888849) - check(b.value == 8924920024888888849) - check(b.getAndDecrement() == 8924920024888888849) - check(b.value == 8924920024888888848) - check(b.getAndAdd(100000000000000000) == 8924920024888888848) - check(b.value == 9024920024888888848) - check(b.addAndGet(-9223372036854775807) == -198452011965886959) - check(b.value == -198452011965886959) - check(b.incrementAndGet() == -198452011965886958) - check(b.value == -198452011965886958) - check(b.decrementAndGet() == -198452011965886959) - check(b.value == -198452011965886959) + assertEquals(8424920024888888848, b.value) + assertEquals(8424920024888888848, b.getAndSet(8924920024888888848)) + assertEquals(8924920024888888848, b.value) + assertEquals(8924920024888888849, b.incrementAndGet()) + assertEquals(8924920024888888849, b.value) + assertEquals(8924920024888888849, b.getAndDecrement()) + assertEquals(8924920024888888848, b.value) + assertEquals(8924920024888888848, b.getAndAdd(100000000000000000)) + assertEquals(9024920024888888848, b.value) + assertEquals(-198452011965886959, b.addAndGet(-9223372036854775807)) + assertEquals(-198452011965886959, b.value) + assertEquals(-198452011965886958, b.incrementAndGet()) + assertEquals(-198452011965886958, b.value) + assertEquals(-198452011965886959, b.decrementAndGet()) + assertEquals(-198452011965886959, b.value) } fun testTopLevelBoolean() { - check(c.value) + assertTrue(c.value) c.lazySet(false) - check(!c.value) - check(!c.getAndSet(true)) - check(c.compareAndSet(true, false)) - check(!c.value) + assertFalse(c.value) + assertTrue(!c.getAndSet(true)) + assertTrue(c.compareAndSet(true, false)) + assertFalse(c.value) } fun testTopLevelRef() { - check(abcNode.value.b.c.d == 8) + assertEquals(8, abcNode.value.b.c.d) val newNode = ANode(BNode(CNode(76))) - check(abcNode.getAndSet(newNode).b.c.d == 8) - check(abcNode.value.b.c.d == 76) + assertEquals(8, abcNode.getAndSet(newNode).b.c.d) + assertEquals(76, abcNode.value.b.c.d) val l = IntArray(4){i -> i} any.lazySet(l) - check((any.value as IntArray)[2] == 2) + assertEquals(2, (any.value as IntArray)[2]) } fun testTopLevelArrayOfNulls() { - check(stringAtomicNullArr[0].value == null) - check(stringAtomicNullArr[0].compareAndSet(null, "aa")) + assertEquals(null, stringAtomicNullArr[0].value) + assertTrue(stringAtomicNullArr[0].compareAndSet(null, "aa")) stringAtomicNullArr[1].lazySet("aa") - check(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value) + assertTrue(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value) } } class TopLevelArrayTest { fun testIntArray() { - check(intArr[0].compareAndSet(0, 3)) - check(intArr[1].value == 0) + assertTrue(intArr[0].compareAndSet(0, 3)) + assertEquals(0, intArr[1].value) intArr[0].lazySet(5) - check(intArr[0].value + intArr[1].value + intArr[2].value == 5) - check(intArr[0].compareAndSet(5, 10)) - check(intArr[0].getAndDecrement() == 10) - check(intArr[0].value == 9) + assertEquals(5, intArr[0].value + intArr[1].value + intArr[2].value) + assertTrue(intArr[0].compareAndSet(5, 10)) + assertEquals(10, intArr[0].getAndDecrement()) + assertEquals(9, intArr[0].value) intArr[2].value = 2 - check(intArr[2].value == 2) - check(intArr[2].compareAndSet(2, 34)) - check(intArr[2].value == 34) + assertEquals(2, intArr[2].value) + assertTrue(intArr[2].compareAndSet(2, 34)) + assertEquals(34, intArr[2].value) } fun testLongArray() { longArr[0].value = 2424920024888888848 - check(longArr[0].value == 2424920024888888848) + assertEquals(2424920024888888848, longArr[0].value) longArr[0].lazySet(8424920024888888848) - check(longArr[0].value == 8424920024888888848) + assertEquals(8424920024888888848, longArr[0].value) val ac = longArr[0].value longArr[3].value = ac - check(longArr[3].getAndSet(8924920024888888848) == 8424920024888888848) - check(longArr[3].value == 8924920024888888848) + assertEquals(8424920024888888848, longArr[3].getAndSet(8924920024888888848)) + assertEquals(8924920024888888848, longArr[3].value) val ac1 = longArr[3].value longArr[4].value = ac1 - check(longArr[4].incrementAndGet() == 8924920024888888849) - check(longArr[4].value == 8924920024888888849) - check(longArr[4].getAndDecrement() == 8924920024888888849) - check(longArr[4].value == 8924920024888888848) + assertEquals(8924920024888888849, longArr[4].incrementAndGet()) + assertEquals(8924920024888888849, longArr[4].value) + assertEquals(8924920024888888849, longArr[4].getAndDecrement()) + assertEquals(8924920024888888848, longArr[4].value) longArr[4].value = 8924920024888888848 - check(longArr[4].getAndAdd(100000000000000000) == 8924920024888888848) + assertEquals(8924920024888888848, longArr[4].getAndAdd(100000000000000000)) val ac2 = longArr[4].value longArr[1].value = ac2 - check(longArr[1].value == 9024920024888888848) - check(longArr[1].addAndGet(-9223372036854775807) == -198452011965886959) - check(longArr[1].value == -198452011965886959) - check(longArr[1].incrementAndGet() == -198452011965886958) - check(longArr[1].value == -198452011965886958) - check(longArr[1].decrementAndGet() == -198452011965886959) - check(longArr[1].value == -198452011965886959) + assertEquals(9024920024888888848, longArr[1].value) + assertEquals(-198452011965886959, longArr[1].addAndGet(-9223372036854775807)) + assertEquals(-198452011965886959, longArr[1].value) + assertEquals(-198452011965886958, longArr[1].incrementAndGet()) + assertEquals(-198452011965886958, longArr[1].value) + assertEquals(-198452011965886959, longArr[1].decrementAndGet()) + assertEquals(-198452011965886959, longArr[1].value) } fun testBooleanArray() { - check(!booleanArr[1].value) + assertFalse(booleanArr[1].value) booleanArr[1].compareAndSet(false, true) booleanArr[0].lazySet(true) - check(!booleanArr[2].getAndSet(true)) - check(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value) + assertFalse(booleanArr[2].getAndSet(true)) + assertTrue(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value) } @Suppress("UNCHECKED_CAST") @@ -145,14 +144,14 @@ class TopLevelArrayTest { val a2 = ANode(BNode(CNode(2))) val a3 = ANode(BNode(CNode(3))) refArr[0].value = a2 - check(refArr[0].value!!.b.c.d == 2) - check(refArr[0].compareAndSet(a2, a3)) - check(refArr[0].value!!.b.c.d == 3) + assertEquals(2, refArr[0].value!!.b.c.d) + assertTrue(refArr[0].compareAndSet(a2, a3)) + assertEquals(3, refArr[0].value!!.b.c.d) val r0 = refArr[0].value refArr[3].value = r0 - check(refArr[3].value!!.b.c.d == 3) + assertEquals(3, refArr[3].value!!.b.c.d) val a = abcNode.value - check(refArr[3].compareAndSet(a3, a)) + assertTrue(refArr[3].compareAndSet(a3, a)) } } diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.txt new file mode 100644 index 00000000000..6b2bd30c0f7 --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/TopLevelTest.txt @@ -0,0 +1,135 @@ +@kotlin.Metadata +public final class A$TopLevelTest$VolatileWrapper { + // source: 'TopLevelTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a: int + static method (): void + public method (): void + public synthetic final static method access$getA$FU$p(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater +} + +@kotlin.Metadata +public final class ANode { + // source: 'TopLevelTest.kt' + private final field b: java.lang.Object + public method (p0: java.lang.Object): void + public final method component1(): java.lang.Object + public synthetic static method copy$default(p0: ANode, p1: java.lang.Object, p2: int, p3: java.lang.Object): ANode + public final @org.jetbrains.annotations.NotNull method copy(p0: java.lang.Object): ANode + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final method getB(): java.lang.Object + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String +} + +@kotlin.Metadata +public final class AbcNode$TopLevelTest$VolatileWrapper { + // source: 'TopLevelTest.kt' + private final static @org.jetbrains.annotations.NotNull field abcNode$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field abcNode: java.lang.Object + static method (): void + public method (): void + public synthetic final static method access$getAbcNode$FU$p(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater +} + +@kotlin.Metadata +public final class Any$TopLevelTest$VolatileWrapper { + // source: 'TopLevelTest.kt' + private final static @org.jetbrains.annotations.NotNull field any$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field any: java.lang.Object + static method (): void + public method (): void + public synthetic final static method access$getAny$FU$p(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater +} + +@kotlin.Metadata +public final class B$TopLevelTest$VolatileWrapper { + // source: 'TopLevelTest.kt' + private final static @org.jetbrains.annotations.NotNull field b$FU: java.util.concurrent.atomic.AtomicLongFieldUpdater + private volatile @kotlin.jvm.Volatile field b: long + static method (): void + public method (): void + public synthetic final static method access$getB$FU$p(): java.util.concurrent.atomic.AtomicLongFieldUpdater +} + +@kotlin.Metadata +public final class BNode { + // source: 'TopLevelTest.kt' + private final field c: java.lang.Object + public method (p0: java.lang.Object): void + public final method component1(): java.lang.Object + public synthetic static method copy$default(p0: BNode, p1: java.lang.Object, p2: int, p3: java.lang.Object): BNode + public final @org.jetbrains.annotations.NotNull method copy(p0: java.lang.Object): BNode + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final method getC(): java.lang.Object + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String +} + +@kotlin.Metadata +public final class C$TopLevelTest$VolatileWrapper { + // source: 'TopLevelTest.kt' + private final static @org.jetbrains.annotations.NotNull field c$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field c: int + static method (): void + public method (): void + public synthetic final static method access$getC$FU$p(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater +} + +@kotlin.Metadata +public final class CNode { + // source: 'TopLevelTest.kt' + private final field d: int + public method (p0: int): void + public final method component1(): int + public synthetic static method copy$default(p0: CNode, p1: int, p2: int, p3: java.lang.Object): CNode + public final @org.jetbrains.annotations.NotNull method copy(p0: int): CNode + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final method getD(): int + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String +} + +@kotlin.Metadata +public final class TopLevelArrayTest { + // source: 'TopLevelTest.kt' + public method (): void + public final method testBooleanArray(): void + public final method testIntArray(): void + public final method testLongArray(): void + public final method testRefArray(): void +} + +@kotlin.Metadata +public final class TopLevelPrimitiveTest { + // source: 'TopLevelTest.kt' + public method (): void + public final method testTopLevelArrayOfNulls(): void + public final method testTopLevelBoolean(): void + public final method testTopLevelInt(): void + public final method testTopLevelLong(): void + public final method testTopLevelRef(): void +} + +@kotlin.Metadata +public final class TopLevelTestKt { + // source: 'TopLevelTest.kt' + public final static @org.jetbrains.annotations.NotNull field a$TopLevelTest$VolatileWrapper: A$TopLevelTest$VolatileWrapper + public final static @org.jetbrains.annotations.NotNull field abcNode$TopLevelTest$VolatileWrapper: AbcNode$TopLevelTest$VolatileWrapper + public final static @org.jetbrains.annotations.NotNull field any$TopLevelTest$VolatileWrapper: Any$TopLevelTest$VolatileWrapper + private final static @org.jetbrains.annotations.NotNull field anyRefArr: java.util.concurrent.atomic.AtomicReferenceArray + public final static @org.jetbrains.annotations.NotNull field b$TopLevelTest$VolatileWrapper: B$TopLevelTest$VolatileWrapper + private final static @org.jetbrains.annotations.NotNull field booleanArr: java.util.concurrent.atomic.AtomicIntegerArray + public final static @org.jetbrains.annotations.NotNull field c$TopLevelTest$VolatileWrapper: C$TopLevelTest$VolatileWrapper + private final static @org.jetbrains.annotations.NotNull field intArr: java.util.concurrent.atomic.AtomicIntegerArray + private final static @org.jetbrains.annotations.NotNull field longArr: java.util.concurrent.atomic.AtomicLongArray + private final static @org.jetbrains.annotations.NotNull field refArr: java.util.concurrent.atomic.AtomicReferenceArray + private final static @org.jetbrains.annotations.NotNull field stringAtomicNullArr: java.util.concurrent.atomic.AtomicReferenceArray + static method (): void + public synthetic final static method access$getBooleanArr$p(): java.util.concurrent.atomic.AtomicIntegerArray + public synthetic final static method access$getIntArr$p(): java.util.concurrent.atomic.AtomicIntegerArray + public synthetic final static method access$getLongArr$p(): java.util.concurrent.atomic.AtomicLongArray + public synthetic final static method access$getRefArr$p(): java.util.concurrent.atomic.AtomicReferenceArray + public synthetic final static method access$getStringAtomicNullArr$p(): java.util.concurrent.atomic.AtomicReferenceArray + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/TraceTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/TraceTest.txt new file mode 100644 index 00000000000..b7e26629e3e --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/TraceTest.txt @@ -0,0 +1,44 @@ +@kotlin.Metadata +final enum class TraceTest$Status { + // source: 'TraceTest.kt' + private synthetic final static field $VALUES: TraceTest$Status[] + public final enum static field END: TraceTest$Status + public final enum static field START: TraceTest$Status + private synthetic final static method $values(): TraceTest$Status[] + static method (): void + private method (p0: java.lang.String, p1: int): void + public static method valueOf(p0: java.lang.String): TraceTest$Status + public static method values(): TraceTest$Status[] + private final inner class TraceTest$Status +} + +@kotlin.Metadata +public final class TraceTest { + // source: 'TraceTest.kt' + private final static @org.jetbrains.annotations.NotNull field a$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private final static @org.jetbrains.annotations.NotNull field a1$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a1: int + private final static @org.jetbrains.annotations.NotNull field a2$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a2: int + private final static @org.jetbrains.annotations.NotNull field a3$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field a3: int + private volatile @kotlin.jvm.Volatile field a: int + private final static @org.jetbrains.annotations.NotNull field s$FU: java.util.concurrent.atomic.AtomicIntegerFieldUpdater + private volatile @kotlin.jvm.Volatile field s: int + static method (): void + public method (): void + public final method test(): void + public final method testDefaultTrace(): void + public final method testMultipleAppend(): void + public final method testNamedTrace(): void + public final method testTraceInBlock(): void + public final method testTraceWithFormat(): void + public final method testTraceWithSize(): void + private final inner class TraceTest$Status +} + +@kotlin.Metadata +public final class TraceTestKt { + // source: 'TraceTest.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.kt index d3fd78d5a3a..b7664c89214 100644 --- a/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.kt +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.kt @@ -45,10 +45,10 @@ class UncheckedCastTest { fun box(): String { val testClass = UncheckedCastTest() + testClass.testAtomicValUncheckedCast() testClass.testTopLevelValUnchekedCast() testClass.testArrayValueUncheckedCast() testClass.testArrayValueUncheckedCastInlineFunc() - testClass.testAtomicValUncheckedCast() testClass.testInlineFunc() return "OK" } \ No newline at end of file diff --git a/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.txt b/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.txt new file mode 100644 index 00000000000..620d1dbe61d --- /dev/null +++ b/plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.txt @@ -0,0 +1,52 @@ +@kotlin.Metadata +public final class TopLevelS$UncheckedCastTest$VolatileWrapper { + // source: 'UncheckedCastTest.kt' + private final static @org.jetbrains.annotations.NotNull field topLevelS$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field topLevelS: java.lang.Object + static method (): void + public method (): void + public synthetic final static method access$getTopLevelS$FU$p(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater +} + +@kotlin.Metadata +final class UncheckedCastTest$Box { + // source: 'UncheckedCastTest.kt' + private final field b: int + public method (p0: int): void + public final method component1(): int + public synthetic static method copy$default(p0: UncheckedCastTest$Box, p1: int, p2: int, p3: java.lang.Object): UncheckedCastTest$Box + public final @org.jetbrains.annotations.NotNull method copy(p0: int): UncheckedCastTest$Box + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final method getB(): int + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + private final inner class UncheckedCastTest$Box +} + +@kotlin.Metadata +public final class UncheckedCastTest { + // source: 'UncheckedCastTest.kt' + private final @org.jetbrains.annotations.NotNull field a: java.util.concurrent.atomic.AtomicReferenceArray + private final static @org.jetbrains.annotations.NotNull field bs$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field bs: java.lang.Object + private final static @org.jetbrains.annotations.NotNull field s$FU: java.util.concurrent.atomic.AtomicReferenceFieldUpdater + private volatile @kotlin.jvm.Volatile @org.jetbrains.annotations.Nullable field s: java.lang.Object + static method (): void + public method (): void + public final @org.jetbrains.annotations.NotNull method getString$atomicfu$array(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int): java.lang.String + public final @org.jetbrains.annotations.NotNull method getString$atomicfu(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater): java.lang.String + public final method testArrayValueUncheckedCast(): void + public final method testArrayValueUncheckedCastInlineFunc(): void + public final method testAtomicValUncheckedCast(): void + public final method testInlineFunc(): void + public final method testTopLevelValUnchekedCast(): void + private final inner class UncheckedCastTest$Box +} + +@kotlin.Metadata +public final class UncheckedCastTestKt { + // source: 'UncheckedCastTest.kt' + public final static @org.jetbrains.annotations.NotNull field topLevelS$UncheckedCastTest$VolatileWrapper: TopLevelS$UncheckedCastTest$VolatileWrapper + static method (): void + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +}