kotlinx.atomicfu compiler plugin for JS_IR backend (#4581)
* kotlinx.atomicfu compiler plugin for JS_IR Support transformations of atomic operations introduced by the kotlinx.atomicfu library for the JS_IR backend. Compiler plugin is applied externally by the kotlinx.atomicfu gradle plugin. * Apply compiler plugin for JS platform only * New plugin test structure * testGroupOutputDirPrefix changed
This commit is contained in:
+1
-1
@@ -314,7 +314,7 @@ class ClassicFrontendFacade(
|
||||
dependentDescriptors: List<ModuleDescriptorImpl>,
|
||||
friendsDescriptors: List<ModuleDescriptorImpl>,
|
||||
): AnalysisResult {
|
||||
val runtimeKlibsNames = JsEnvironmentConfigurator.getStdlibPathsForModule(module)
|
||||
val runtimeKlibsNames = JsEnvironmentConfigurator.getRuntimePathsForModule(module, testServices)
|
||||
val runtimeKlibs = loadKlib(runtimeKlibsNames, configuration)
|
||||
val transitiveLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.RegularDependency)
|
||||
val friendLibraries = JsEnvironmentConfigurator.getDependencies(module, testServices, DependencyRelation.FriendDependency)
|
||||
|
||||
+6
-2
@@ -137,13 +137,17 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu
|
||||
return getMainModule(testServices).name
|
||||
}
|
||||
|
||||
fun getStdlibPathsForModule(module: TestModule): List<String> {
|
||||
fun getRuntimePathsForModule(module: TestModule, testServices: TestServices): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
val needsFullIrRuntime = JsEnvironmentConfigurationDirectives.KJS_WITH_FULL_RUNTIME in module.directives ||
|
||||
ConfigurationDirectives.WITH_STDLIB in module.directives ||
|
||||
ConfigurationDirectives.WITH_RUNTIME in module.directives
|
||||
|
||||
val names = if (needsFullIrRuntime) listOf("full.stdlib", "kotlin.test") else listOf("reduced.stdlib")
|
||||
return names.map { System.getProperty("kotlin.js.$it.path") }
|
||||
names.mapTo(result) { System.getProperty("kotlin.js.$it.path") }
|
||||
val runtimeClasspaths = testServices.runtimeClasspathProviders.flatMap { it.runtimeClassPaths(module) }
|
||||
runtimeClasspaths.mapTo(result) { it.absolutePath }
|
||||
return result
|
||||
}
|
||||
|
||||
fun getDependencies(module: TestModule, testServices: TestServices, kind: DependencyRelation): List<ModuleDescriptorImpl> {
|
||||
|
||||
@@ -65,6 +65,7 @@ dependencies {
|
||||
testApi(projectTests(":plugins:lombok:lombok-compiler-plugin"))
|
||||
testApi(projectTests(":kotlin-sam-with-receiver-compiler-plugin"))
|
||||
testApi(projectTests(":kotlinx-serialization-compiler-plugin"))
|
||||
testApi(projectTests(":kotlinx-atomicfu-compiler-plugin"))
|
||||
testApi(projectTests(":plugins:fir:fir-plugin-prototype"))
|
||||
testApi(projectTests(":generators:test-generator"))
|
||||
testCompileOnly(project(":kotlin-reflect-api"))
|
||||
@@ -73,6 +74,7 @@ dependencies {
|
||||
testImplementation(projectTests(":compiler:test-infrastructure-utils"))
|
||||
testImplementation(projectTests(":compiler:test-infrastructure"))
|
||||
testImplementation(projectTests(":compiler:tests-common-new"))
|
||||
testImplementation(projectTests(":js:js.tests"))
|
||||
testApiJUnit5()
|
||||
|
||||
if (Ide.IJ()) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||
import org.jetbrains.kotlinx.atomicfu.AbstractAtomicfuJsIrTest
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
System.setProperty("java.awt.headless", "true")
|
||||
@@ -395,5 +396,15 @@ fun main(args: Array<String>) {
|
||||
model("box")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
"plugins/atomicfu/atomicfu-compiler/test",
|
||||
"plugins/atomicfu/atomicfu-compiler/testData",
|
||||
testRunnerMethodName = "runTest0"
|
||||
) {
|
||||
testClass<AbstractAtomicfuJsIrTest> {
|
||||
model("box/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7258,6 +7258,11 @@
|
||||
<sha256 value="7a6bd3e09a63d6a57725cc3016465d2148b8febb82a6b165b33956f8a15e0b94" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="atomicfu-js" version="0.16.3">
|
||||
<artifact name="atomicfu-jsir-0.16.3.klib">
|
||||
<sha256 value="8b2ab53f08ee262fe2bea798011b3bda230b039fc14c1d7e2ab888f4384a9194" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="binary-compatibility-validator" version="0.7.1">
|
||||
<artifact name="binary-compatibility-validator-0.7.1.jar">
|
||||
<md5 value="f188e849f683da58684012bbdc732abc" origin="Generated by Gradle"/>
|
||||
|
||||
@@ -162,7 +162,7 @@ class JsIrIncrementalDataProvider(private val testServices: TestServices) : Test
|
||||
}
|
||||
|
||||
private fun recordIncrementalDataForRuntimeKlib(module: TestModule) {
|
||||
val runtimeKlibPath = JsEnvironmentConfigurator.getStdlibPathsForModule(module)
|
||||
val runtimeKlibPath = JsEnvironmentConfigurator.getRuntimePathsForModule(module, testServices)
|
||||
val libs = runtimeKlibPath.map {
|
||||
val descriptor = testServices.jsLibraryProvider.getDescriptorByPath(it)
|
||||
testServices.jsLibraryProvider.getCompiledLibraryByDescriptor(descriptor)
|
||||
|
||||
@@ -3,6 +3,7 @@ def pluginProjects = [
|
||||
project(':kotlin-allopen'),
|
||||
project(':kotlin-noarg'),
|
||||
project(':kotlin-serialization'),
|
||||
project(':atomicfu'),
|
||||
project(':kotlin-lombok')
|
||||
]
|
||||
configure(pluginProjects) { project ->
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'jps-compatible'
|
||||
|
||||
configurePublishing(project)
|
||||
|
||||
pill {
|
||||
variant = 'FULL'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':kotlin-gradle-plugin')
|
||||
compileOnly project(':kotlin-gradle-plugin-api')
|
||||
|
||||
compileOnly kotlinStdlib()
|
||||
compileOnly project(':kotlin-compiler-embeddable')
|
||||
|
||||
embedded(project(":kotlinx-atomicfu-compiler-plugin"))
|
||||
}
|
||||
|
||||
jar {
|
||||
manifestAttributes(manifest, project)
|
||||
}
|
||||
|
||||
ArtifactsKt.runtimeJar(project, EmbeddableKt.rewriteDefaultJarDepsToShadedCompiler(project, {}), {})
|
||||
configureSourcesJar()
|
||||
configureJavadocJar()
|
||||
|
||||
pluginBundle {
|
||||
plugins {
|
||||
atomicfu {
|
||||
id = 'org.jetbrains.kotlin.plugin.atomicfu'
|
||||
description = displayName = 'Kotlin compiler plugin for kotlinx.atomicfu library'
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.atomicfu.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
|
||||
class AtomicfuKotlinGradleSubplugin :
|
||||
KotlinCompilerPluginSupportPlugin,
|
||||
@Suppress("DEPRECATION_ERROR") // implementing to fix KT-39809
|
||||
KotlinGradleSubplugin<AbstractCompile> {
|
||||
companion object {
|
||||
const val ATOMICFU_ARTIFACT_NAME = "atomicfu"
|
||||
}
|
||||
|
||||
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true
|
||||
|
||||
override fun applyToCompilation(
|
||||
kotlinCompilation: KotlinCompilation<*>
|
||||
): Provider<List<SubpluginOption>> =
|
||||
kotlinCompilation.target.project.provider { emptyList<SubpluginOption>() }
|
||||
|
||||
override fun getPluginArtifact(): SubpluginArtifact =
|
||||
JetBrainsSubpluginArtifact(ATOMICFU_ARTIFACT_NAME)
|
||||
|
||||
override fun getCompilerPluginId() = "org.jetbrains.kotlinx.atomicfu"
|
||||
|
||||
//region Stub implementation for legacy API, KT-39809
|
||||
override fun isApplicable(project: Project, task: AbstractCompile): Boolean = true
|
||||
|
||||
override fun apply(
|
||||
project: Project,
|
||||
kotlinCompile: AbstractCompile,
|
||||
javaCompile: AbstractCompile?,
|
||||
variantData: Any?,
|
||||
androidProjectHandler: Any?,
|
||||
kotlinCompilation: KotlinCompilation<*>?
|
||||
): List<SubpluginOption> {
|
||||
return emptyList()
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuKotlinGradleSubplugin
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuKotlinGradleSubplugin
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Copyright 2010-2020 JetBrains s.r.o.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuKotlinGradleSubplugin
|
||||
@@ -0,0 +1,132 @@
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
|
||||
description = "Atomicfu Compiler Plugin"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
id("com.github.node-gradle.node") version "2.2.0"
|
||||
id("de.undercouch.download")
|
||||
}
|
||||
|
||||
node {
|
||||
download = true
|
||||
version = "10.16.2"
|
||||
}
|
||||
|
||||
val antLauncherJar by configurations.creating
|
||||
val testJsRuntime by configurations.creating {
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_RUNTIME))
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
|
||||
}
|
||||
}
|
||||
|
||||
val atomicfuClasspath by configurations.creating {
|
||||
attributes {
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
|
||||
attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir)
|
||||
}
|
||||
}
|
||||
|
||||
val atomicfuRuntimeForTests by configurations.creating {
|
||||
attributes {
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
|
||||
attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir)
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_RUNTIME))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "asm-all", rootProject = rootProject) }
|
||||
|
||||
compileOnly(project(":compiler:plugin-api"))
|
||||
compileOnly(project(":compiler:cli-common"))
|
||||
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(kotlinStdlib())
|
||||
|
||||
testApi(projectTests(":compiler:tests-common"))
|
||||
testApi(projectTests(":compiler:test-infrastructure"))
|
||||
testApi(projectTests(":compiler:test-infrastructure-utils"))
|
||||
testApi(projectTests(":compiler:tests-compiler-utils"))
|
||||
testApi(projectTests(":compiler:tests-common-new"))
|
||||
testImplementation(projectTests(":generators:test-generator"))
|
||||
|
||||
testImplementation(projectTests(":js:js.tests"))
|
||||
testApi(commonDep("junit:junit"))
|
||||
|
||||
testRuntimeOnly(kotlinStdlib())
|
||||
testRuntimeOnly(project(":kotlin-reflect"))
|
||||
testRuntimeOnly(project(":kotlin-preloader")) // it's required for ant tests
|
||||
testRuntimeOnly(project(":compiler:backend-common"))
|
||||
testRuntimeOnly(commonDep("org.fusesource.jansi", "jansi"))
|
||||
|
||||
atomicfuClasspath("org.jetbrains.kotlinx:atomicfu-js:0.16.3") { isTransitive = false }
|
||||
atomicfuRuntimeForTests(project(":kotlinx-atomicfu-runtime")) { isTransitive = false }
|
||||
|
||||
embedded(project(":kotlinx-atomicfu-runtime")) {
|
||||
attributes {
|
||||
attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
|
||||
attribute(KotlinJsCompilerAttribute.jsCompilerAttribute, KotlinJsCompilerAttribute.ir)
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_RUNTIME))
|
||||
}
|
||||
isTransitive = false
|
||||
}
|
||||
|
||||
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
testsJar()
|
||||
|
||||
projectTest(jUnitMode = JUnitMode.JUnit5) {
|
||||
useJUnitPlatform()
|
||||
workingDir = rootDir
|
||||
dependsOn(atomicfuRuntimeForTests)
|
||||
doFirst {
|
||||
systemProperty("atomicfuRuntimeForTests.classpath", atomicfuRuntimeForTests.asPath)
|
||||
systemProperty("atomicfu.classpath", atomicfuClasspath.asPath)
|
||||
}
|
||||
setUpJsIrBoxTests()
|
||||
}
|
||||
|
||||
fun Test.setupV8() {
|
||||
dependsOn(":js:js.tests:unzipV8")
|
||||
doFirst {
|
||||
val unzipV8Task = project.tasks.getByPath(":js:js.tests:unzipV8")
|
||||
systemProperty("javascript.engine.path.V8", File(unzipV8Task.outputs.files.single().path, "d8"))
|
||||
}
|
||||
}
|
||||
|
||||
fun Test.setUpJsIrBoxTests() {
|
||||
setupV8()
|
||||
|
||||
dependsOn(":dist")
|
||||
dependsOn(":kotlin-stdlib-js-ir:compileKotlinJs")
|
||||
systemProperty("kotlin.js.full.stdlib.path", "libraries/stdlib/js-ir/build/classes/kotlin/js/main")
|
||||
dependsOn(":kotlin-stdlib-js-ir-minimal-for-test:compileKotlinJs")
|
||||
systemProperty("kotlin.js.reduced.stdlib.path", "libraries/stdlib/js-ir-minimal-for-test/build/classes/kotlin/js/main")
|
||||
dependsOn(":kotlin-test:kotlin-test-js-ir:compileKotlinJs")
|
||||
systemProperty("kotlin.js.kotlin.test.path", "libraries/kotlin.test/js-ir/build/classes/kotlin/js/main")
|
||||
systemProperty("kotlin.js.kotlin.test.path", "libraries/kotlin.test/js-ir/build/classes/kotlin/js/main")
|
||||
systemProperty("kotlin.js.test.root.out.dir", "$buildDir/")
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Copyright 2010-2017 JetBrains s.r.o.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.jetbrains.kotlinx.atomicfu.compiler.extensions.AtomicfuComponentRegistrar
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.atomicfu.compiler.extensions
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
|
||||
class AtomicfuComponentRegistrar : ComponentRegistrar {
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
registerExtensions(project)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun registerExtensions(project: Project) {
|
||||
IrGenerationExtension.registerExtension(project, AtomicfuLoweringExtension()) }
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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
|
||||
|
||||
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.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
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
|
||||
|
||||
public open class AtomicfuLoweringExtension : IrGenerationExtension {
|
||||
override fun generate(
|
||||
moduleFragment: IrModuleFragment,
|
||||
pluginContext: IrPluginContext
|
||||
) {
|
||||
val atomicfuClassLowering = AtomicfuClassLowering(pluginContext)
|
||||
for (file in moduleFragment.files) {
|
||||
atomicfuClassLowering.runOnFileInOrder(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of [runOnFilePostfix], but this implementation first lowers declaration, then its children.
|
||||
*/
|
||||
fun FileLoweringPass.runOnFileInOrder(irFile: IrFile) {
|
||||
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFile(declaration: IrFile) {
|
||||
lower(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private class AtomicfuClassLowering(
|
||||
val context: IrPluginContext
|
||||
) : IrElementTransformerVoid(), FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
AtomicfuTransformer(context).transform(irFile)
|
||||
}
|
||||
}
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder.buildValueParameter
|
||||
import org.jetbrains.kotlin.ir.util.IdSignature.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
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.*
|
||||
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
|
||||
|
||||
private const val AFU_PKG = "kotlinx.atomicfu"
|
||||
private const val LOCKS = "locks"
|
||||
private const val AFU_LOCKS_PKG = "$AFU_PKG.$LOCKS"
|
||||
private const val ATOMICFU_RUNTIME_FUNCTION_PREDICATE = "atomicfu_"
|
||||
private const val REENTRANT_LOCK_TYPE = "ReentrantLock"
|
||||
private const val GETTER = "atomicfu\$getter"
|
||||
private const val SETTER = "atomicfu\$setter"
|
||||
private const val GET = "get"
|
||||
private const val ATOMIC_VALUE_FACTORY = "atomic"
|
||||
private const val ATOMIC_ARRAY_OF_NULLS_FACTORY = "atomicArrayOfNulls"
|
||||
private const val REENTRANT_LOCK_FACTORY = "reentrantLock"
|
||||
|
||||
class AtomicfuTransformer(private val context: IrPluginContext) {
|
||||
|
||||
private val irBuiltIns = context.irBuiltIns
|
||||
|
||||
private val AFU_CLASSES: Map<String, IrType> = mapOf(
|
||||
"AtomicInt" to irBuiltIns.intType,
|
||||
"AtomicLong" to irBuiltIns.longType,
|
||||
"AtomicRef" to irBuiltIns.anyNType,
|
||||
"AtomicBoolean" to irBuiltIns.booleanType
|
||||
)
|
||||
|
||||
private val ATOMIC_VALUE_TYPES = setOf("AtomicInt", "AtomicLong", "AtomicBoolean", "AtomicRef")
|
||||
private val ATOMIC_ARRAY_TYPES = setOf("AtomicIntArray", "AtomicLongArray", "AtomicBooleanArray", "AtomicArray")
|
||||
private val ATOMICFU_INLINE_FUNCTIONS = setOf("atomicfu_loop", "atomicfu_update", "atomicfu_getAndUpdate", "atomicfu_updateAndGet")
|
||||
|
||||
fun transform(irFile: IrFile) {
|
||||
if (context.platform.isJs()) {
|
||||
irFile.transform(AtomicExtensionTransformer(), null)
|
||||
irFile.transformChildren(AtomicTransformer(), null)
|
||||
|
||||
irFile.patchDeclarationParents()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AtomicExtensionTransformer : IrElementTransformerVoid() {
|
||||
override fun visitFile(declaration: IrFile): IrFile {
|
||||
declaration.declarations.addAllTransformedAtomicExtensions()
|
||||
return super.visitFile(declaration)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
declaration.declarations.addAllTransformedAtomicExtensions()
|
||||
return super.visitClass(declaration)
|
||||
}
|
||||
|
||||
private fun MutableList<IrDeclaration>.addAllTransformedAtomicExtensions() {
|
||||
val transformedDeclarations = mutableListOf<IrDeclaration>()
|
||||
forEach { irDeclaration ->
|
||||
irDeclaration.transformAtomicExtension()?.let { it -> transformedDeclarations.add(it) }
|
||||
}
|
||||
addAll(transformedDeclarations)
|
||||
}
|
||||
|
||||
private fun IrDeclaration.transformAtomicExtension(): IrDeclaration? {
|
||||
// Transform the signature of the inline Atomic* extension declaration:
|
||||
// inline fun AtomicRef<T>.foo(arg) { ... } -> inline fun <T> foo(arg', atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit)
|
||||
if (this is IrFunction && isAtomicExtension()) {
|
||||
val newDeclaration = deepCopyWithSymbols(parent)
|
||||
val valueParametersCount = valueParameters.size
|
||||
val type = newDeclaration.extensionReceiverParameter!!.type.atomicToValueType()
|
||||
val getterType = context.buildGetterType(type)
|
||||
val setterType = context.buildSetterType(type)
|
||||
newDeclaration.valueParameters = newDeclaration.valueParameters + listOf(
|
||||
buildValueParameter(newDeclaration, GETTER, valueParametersCount, getterType),
|
||||
buildValueParameter(newDeclaration, SETTER, valueParametersCount + 1, setterType)
|
||||
)
|
||||
newDeclaration.extensionReceiverParameter = null
|
||||
return newDeclaration
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AtomicTransformer : IrElementTransformer<IrFunction?> {
|
||||
|
||||
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement {
|
||||
return super.visitFunction(declaration, declaration)
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, data: IrFunction?): IrElement {
|
||||
expression.eraseAtomicFactory()?.let { return it.transform(this, data) }
|
||||
val isInline = expression.symbol.owner.isInline
|
||||
(expression.extensionReceiver ?: expression.dispatchReceiver)?.transform(this, data)?.let { receiver ->
|
||||
// Transform invocations of atomic functions
|
||||
if (expression.symbol.isKotlinxAtomicfuPackage() && receiver.type.isAtomicValueType()) {
|
||||
// Substitute invocations of atomic functions on atomic receivers
|
||||
// with the corresponding inline declarations from `kotlinx-atomicfu-runtime`,
|
||||
// passing atomic receiver accessors as atomicfu$getter and atomicfu$setter parameters.
|
||||
|
||||
// In case of the atomic field receiver, pass field accessors:
|
||||
// a.incrementAndGet() -> atomicfu_incrementAndGet(get_a {..}, set_a {..})
|
||||
|
||||
// In case of the atomic `this` receiver, pass the corresponding atomicfu$getter and atomicfu$setter parameters
|
||||
// from the parent transformed atomic extension declaration:
|
||||
// Note: inline atomic extension signatures are already transformed with the [AtomicExtensionTransformer]
|
||||
// inline fun foo(atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit) { incrementAndGet() } ->
|
||||
// inline fun foo(atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit) { atomicfu_incrementAndGet(atomicfu$getter, atomicfu$setter) }
|
||||
receiver.getReceiverAccessors(data)?.let { accessors ->
|
||||
val receiverValueType = receiver.type.atomicToValueType()
|
||||
val inlineAtomic = expression.inlineAtomicFunction(receiverValueType, accessors).apply {
|
||||
if (symbol.owner.name.asString() in ATOMICFU_INLINE_FUNCTIONS) {
|
||||
val lambdaLoop = (getValueArgument(0) as IrFunctionExpression).function
|
||||
lambdaLoop.body?.transform(this@AtomicTransformer, data)
|
||||
}
|
||||
}
|
||||
return super.visitCall(inlineAtomic, data)
|
||||
}
|
||||
}
|
||||
// Transform invocations of atomic extension functions
|
||||
if (isInline && receiver.type.isAtomicValueType()) {
|
||||
// Transform invocation of the atomic extension on the atomic receiver,
|
||||
// passing field accessors as atomicfu$getter and atomicfu$setter parameters.
|
||||
|
||||
// In case of the atomic field receiver, pass field accessors:
|
||||
// a.foo(arg) -> foo(arg, get_a {..}, set_a {..})
|
||||
|
||||
// In case of the atomic `this` receiver, pass the corresponding atomicfu$getter and atomicfu$setter parameters
|
||||
// from the parent transformed atomic extension declaration:
|
||||
// Note: inline atomic extension signatures are already transformed with the [AtomicExtensionTransformer]
|
||||
// inline fun bar(atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit) { ... }
|
||||
// inline fun foo(atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit) { this.bar() } ->
|
||||
// inline fun foo(atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit) { bar(atomicfu$getter, atomicfu$setter) }
|
||||
receiver.getReceiverAccessors(data)?.let { accessors ->
|
||||
val declaration = expression.symbol.owner
|
||||
val transformedAtomicExtension = getDeclarationWithAccessorParameters(declaration, declaration.extensionReceiverParameter)
|
||||
return buildCall(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
target = transformedAtomicExtension.symbol,
|
||||
type = expression.type,
|
||||
valueArguments = expression.getValueArguments() + accessors
|
||||
).apply {
|
||||
dispatchReceiver = expression.dispatchReceiver
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitCall(expression, data)
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, data: IrFunction?): IrExpression {
|
||||
// For transformed atomic extension functions:
|
||||
// replace all usages of old value parameters with the new parameters of the transformed declaration
|
||||
// inline fun foo(arg', atomicfu$getter: () -> T, atomicfu$setter: (T) -> Unit) { bar(arg) } -> { bar(arg') }
|
||||
if (expression.symbol is IrValueParameterSymbol) {
|
||||
val valueParameter = expression.symbol.owner as IrValueParameter
|
||||
val parent = valueParameter.parent
|
||||
if (parent is IrFunction && parent.isTransformedAtomicExtensionFunction()) {
|
||||
val index = valueParameter.index
|
||||
if (index >= 0) { // index == -1 for `this` parameter
|
||||
val transformedValueParameter = parent.valueParameters[index]
|
||||
return buildGetValue(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
transformedValueParameter.symbol
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitGetValue(expression, data)
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: IrFunction?): IrExpression {
|
||||
// Erase unchecked casts:
|
||||
// val a = atomic<Any>("AAA")
|
||||
// (a as AtomicRef<String>).value -> a.value
|
||||
if ((expression.operator == CAST || expression.operator == IMPLICIT_CAST) && expression.typeOperand.isAtomicValueType()) {
|
||||
return expression.argument
|
||||
}
|
||||
return super.visitTypeOperator(expression, data)
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall, data: IrFunction?): IrElement {
|
||||
// Erase constructor of Atomic(Int|Long|Boolean|)Array:
|
||||
// val arr = AtomicIntArray(size) -> val arr = new Int32Array(size)
|
||||
if (expression.isAtomicArrayConstructor()) {
|
||||
val arrayConstructorSymbol =
|
||||
context.getArrayConstructorSymbol(expression.type as IrSimpleType) { it.owner.valueParameters.size == 1 }
|
||||
val size = expression.getValueArgument(0)
|
||||
return IrConstructorCallImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
arrayConstructorSymbol.owner.returnType, arrayConstructorSymbol,
|
||||
arrayConstructorSymbol.owner.typeParameters.size, 0, 1
|
||||
).apply {
|
||||
putValueArgument(0, size)
|
||||
}
|
||||
}
|
||||
return super.visitConstructorCall(expression, data)
|
||||
}
|
||||
|
||||
private fun IrExpression.getReceiverAccessors(parent: IrFunction?): List<IrExpression>? =
|
||||
when {
|
||||
this is IrCall -> getAccessors()
|
||||
isThisReceiver() -> {
|
||||
if (parent is IrFunction && parent.isTransformedAtomicExtensionFunction()) {
|
||||
parent.valueParameters.takeLast(2).map { it.capture() }
|
||||
} else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun IrExpression.isThisReceiver() =
|
||||
this is IrGetValue && symbol.owner.name.asString() == "<this>"
|
||||
|
||||
private fun IrCall.inlineAtomicFunction(atomicType: IrType, accessors: List<IrExpression>): IrCall {
|
||||
val valueArguments = getValueArguments()
|
||||
val functionName = getAtomicFunctionName()
|
||||
val runtimeFunction = getRuntimeFunctionSymbol(functionName, atomicType)
|
||||
return buildCall(
|
||||
startOffset, endOffset,
|
||||
target = runtimeFunction,
|
||||
type = type,
|
||||
typeArguments = if (runtimeFunction.owner.typeParameters.size == 1) listOf(atomicType) else emptyList(),
|
||||
valueArguments = valueArguments + accessors
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrFunction.hasReceiverAccessorParameters(): Boolean {
|
||||
if (valueParameters.size < 2) return false
|
||||
val params = valueParameters.takeLast(2)
|
||||
return params[0].name.asString() == GETTER && params[1].name.asString() == SETTER
|
||||
}
|
||||
|
||||
private fun IrDeclaration.isTransformedAtomicExtensionFunction(): Boolean =
|
||||
this is IrFunction && hasReceiverAccessorParameters()
|
||||
|
||||
private fun getDeclarationWithAccessorParameters(
|
||||
declaration: IrFunction,
|
||||
extensionReceiverParameter: IrValueParameter?
|
||||
): IrSimpleFunction {
|
||||
require(extensionReceiverParameter != null)
|
||||
val paramsCount = declaration.valueParameters.size
|
||||
val receiverType = extensionReceiverParameter.type.atomicToValueType()
|
||||
return (declaration.parent as? IrDeclarationContainer)?.let { parent ->
|
||||
parent.declarations.singleOrNull {
|
||||
it is IrSimpleFunction &&
|
||||
it.name == declaration.symbol.owner.name &&
|
||||
it.valueParameters.size == paramsCount + 2 &&
|
||||
it.valueParameters.dropLast(2).withIndex()
|
||||
.all { p -> p.value.render() == declaration.valueParameters[p.index].render() } &&
|
||||
it.valueParameters[paramsCount].name.asString() == GETTER && it.valueParameters[paramsCount + 1].name.asString() == SETTER &&
|
||||
it.getGetterReturnType()?.render() == receiverType.render()
|
||||
} as? IrSimpleFunction
|
||||
} ?: error(
|
||||
"Failed to find the transformed atomic extension function with accessor parameters " +
|
||||
"corresponding to the original declaration: ${declaration.render()} in the parent: ${declaration.parent.render()}"
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrCall.isArrayElementGetter(): Boolean =
|
||||
dispatchReceiver?.let {
|
||||
it.type.isAtomicArrayType() && symbol.owner.name.asString() == GET
|
||||
} ?: false
|
||||
|
||||
private fun IrCall.getAccessors(): List<IrExpression> {
|
||||
val isArrayElement = isArrayElementGetter()
|
||||
val valueType = type.atomicToValueType()
|
||||
return listOf(
|
||||
context.buildAccessorLambda(this, valueType, false, isArrayElement),
|
||||
context.buildAccessorLambda(this, valueType, true, isArrayElement)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getRuntimeFunctionSymbol(name: String, type: IrType): IrSimpleFunctionSymbol {
|
||||
val functionName = when (name) {
|
||||
"value.<get-value>" -> "getValue"
|
||||
"value.<set-value>" -> "setValue"
|
||||
else -> name
|
||||
}
|
||||
return context.referencePackageFunction(AFU_PKG, "$ATOMICFU_RUNTIME_FUNCTION_PREDICATE$functionName") {
|
||||
val typeArg = it.owner.getGetterReturnType()
|
||||
!(typeArg as IrType).isPrimitiveType() || typeArg == type
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.getGetterReturnType(): IrType? =
|
||||
valueParameters.getOrNull(valueParameters.lastIndex - 1)?.let { getter ->
|
||||
if (getter.name.asString() == GETTER) {
|
||||
(getter.type as IrSimpleType).arguments.first().typeOrNull
|
||||
} else null
|
||||
}
|
||||
|
||||
private fun IrCall.getAtomicFunctionName(): String =
|
||||
symbol.signature?.let { signature ->
|
||||
if (signature is IdSignature.AccessorSignature) signature.accessorSignature else signature.asPublic()
|
||||
}?.declarationFqName?.let { name ->
|
||||
if (name.substringBefore('.') in ATOMIC_VALUE_TYPES) {
|
||||
name.substringAfter('.')
|
||||
} else name
|
||||
} ?: error("Incorrect pattern of the atomic function name: ${symbol.owner.render()}")
|
||||
|
||||
private fun IrCall.eraseAtomicFactory() =
|
||||
when {
|
||||
isAtomicFactory() -> getValueArgument(0) ?: error("Atomic factory should take at least one argument: ${this.render()}")
|
||||
isAtomicArrayFactory() -> buildObjectArray()
|
||||
isReentrantLockFactory() -> context.buildConstNull()
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun IrCall.buildObjectArray(): IrCall {
|
||||
val arrayFactorySymbol = context.referencePackageFunction("kotlin", "arrayOfNulls")
|
||||
val arrayElementType = getTypeArgument(0) ?: error("AtomicArray factory should have a type argument: ${symbol.owner.render()}")
|
||||
val size = getValueArgument(0)
|
||||
return buildCall(
|
||||
startOffset, endOffset,
|
||||
target = arrayFactorySymbol,
|
||||
type = type,
|
||||
typeArguments = listOf(arrayElementType),
|
||||
valueArguments = listOf(size)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.isAtomicExtension(): Boolean =
|
||||
extensionReceiverParameter?.let { it.type.isAtomicValueType() && this.isInline } ?: false
|
||||
|
||||
private fun IrSymbol.isKotlinxAtomicfuPackage() =
|
||||
this.isPublicApi && signature?.packageFqName()?.asString() == AFU_PKG
|
||||
|
||||
private fun IrType.isAtomicValueType() = belongsTo(AFU_PKG, ATOMIC_VALUE_TYPES)
|
||||
private fun IrType.isAtomicArrayType() = belongsTo(AFU_PKG, ATOMIC_ARRAY_TYPES)
|
||||
private fun IrType.isReentrantLockType() = belongsTo(AFU_LOCKS_PKG, REENTRANT_LOCK_TYPE)
|
||||
|
||||
private fun IrType.belongsTo(packageName: String, typeNames: Set<String>) =
|
||||
getSignature()?.let { sig ->
|
||||
sig.packageFqName == packageName && sig.declarationFqName in typeNames
|
||||
} ?: false
|
||||
|
||||
private fun IrType.belongsTo(packageName: String, typeName: String) =
|
||||
getSignature()?.let { sig ->
|
||||
sig.packageFqName == packageName && sig.declarationFqName == typeName
|
||||
} ?: false
|
||||
|
||||
private fun IrType.getSignature(): CommonSignature? = classOrNull?.let { it.signature?.asPublic() }
|
||||
|
||||
private fun IrType.atomicToValueType(): IrType {
|
||||
require(this is IrSimpleType)
|
||||
return classifier.signature?.asPublic()?.declarationFqName?.let { classId ->
|
||||
if (classId == "AtomicRef")
|
||||
arguments.first().typeOrNull ?: error("$AFU_PKG.AtomicRef type parameter is not IrTypeProjection")
|
||||
else
|
||||
AFU_CLASSES[classId] ?: error("IrType ${this.getClass()} does not match any of atomicfu types")
|
||||
} ?: error("Unexpected signature of the atomic type: ${this.render()}")
|
||||
}
|
||||
|
||||
private fun IrCall.isAtomicFactory(): Boolean =
|
||||
symbol.isKotlinxAtomicfuPackage() && symbol.owner.name.asString() == ATOMIC_VALUE_FACTORY &&
|
||||
type.isAtomicValueType()
|
||||
|
||||
private fun IrCall.isAtomicArrayFactory(): Boolean =
|
||||
symbol.isKotlinxAtomicfuPackage() && symbol.owner.name.asString() == ATOMIC_ARRAY_OF_NULLS_FACTORY &&
|
||||
type.isAtomicArrayType()
|
||||
|
||||
private fun IrConstructorCall.isAtomicArrayConstructor(): Boolean = type.isAtomicArrayType()
|
||||
|
||||
private fun IrCall.isReentrantLockFactory(): Boolean =
|
||||
symbol.owner.name.asString() == REENTRANT_LOCK_FACTORY && type.isReentrantLockType()
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
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.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
private const val KOTLIN = "kotlin"
|
||||
private const val GET = "get"
|
||||
private const val SET = "set"
|
||||
|
||||
private val AFU_ARRAY_CLASSES: Map<String, String> = mapOf(
|
||||
"AtomicIntArray" to "IntArray",
|
||||
"AtomicLongArray" to "LongArray",
|
||||
"AtomicBooleanArray" to "BooleanArray",
|
||||
"AtomicArray" to "Array"
|
||||
)
|
||||
|
||||
internal fun buildCall(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
target: IrSimpleFunctionSymbol,
|
||||
type: IrType? = null,
|
||||
origin: IrStatementOrigin? = null,
|
||||
typeArguments: List<IrType> = emptyList(),
|
||||
valueArguments: List<IrExpression?> = emptyList()
|
||||
): IrCall =
|
||||
IrCallImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
type ?: target.owner.returnType,
|
||||
target,
|
||||
typeArguments.size,
|
||||
valueArguments.size,
|
||||
origin
|
||||
).apply {
|
||||
typeArguments.let {
|
||||
it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) }
|
||||
}
|
||||
valueArguments.let {
|
||||
it.withIndex().forEach { (i, arg) -> putValueArgument(i, arg) }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IrFactory.buildBlockBody(statements: List<IrStatement>) =
|
||||
createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, statements)
|
||||
|
||||
internal fun buildSetField(
|
||||
symbol: IrFieldSymbol,
|
||||
receiver: IrExpression?,
|
||||
value: IrExpression,
|
||||
superQualifierSymbol: IrClassSymbol? = null
|
||||
): IrSetField =
|
||||
IrSetFieldImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
symbol,
|
||||
receiver,
|
||||
value,
|
||||
value.type,
|
||||
IrStatementOrigin.GET_PROPERTY,
|
||||
superQualifierSymbol
|
||||
)
|
||||
|
||||
internal fun buildGetField(
|
||||
symbol: IrFieldSymbol,
|
||||
receiver: IrExpression?,
|
||||
superQualifierSymbol: IrClassSymbol? = null,
|
||||
type: IrType? = null
|
||||
): IrGetField =
|
||||
IrGetFieldImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
symbol,
|
||||
type ?: symbol.owner.type,
|
||||
receiver,
|
||||
IrStatementOrigin.GET_PROPERTY,
|
||||
superQualifierSymbol
|
||||
)
|
||||
|
||||
internal fun buildFunctionSimpleType(
|
||||
symbol: IrClassifierSymbol,
|
||||
typeParameters: List<IrType>
|
||||
): IrSimpleType =
|
||||
IrSimpleTypeImpl(
|
||||
classifier = symbol,
|
||||
hasQuestionMark = false,
|
||||
arguments = typeParameters.map { makeTypeProjection(it, Variance.INVARIANT) },
|
||||
annotations = emptyList()
|
||||
)
|
||||
|
||||
internal fun buildGetValue(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
symbol: IrValueSymbol
|
||||
): IrGetValue =
|
||||
IrGetValueImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
symbol.owner.type,
|
||||
symbol
|
||||
)
|
||||
|
||||
internal fun IrPluginContext.buildConstNull() = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irBuiltIns.anyNType)
|
||||
|
||||
internal fun getterName(getterCall: IrCall) = "<get-${getterCall.symbol.owner.name.getFieldName()}>"
|
||||
internal fun setterName(getterCall: IrCall) = "<set-${getterCall.symbol.owner.name.getFieldName()}>"
|
||||
|
||||
private fun Name.getFieldName() = "<get-(\\w+)>".toRegex().find(asString())?.groupValues?.get(1)
|
||||
?: error("Getter name ${this.asString()} does not match special name pattern <get-fieldName>")
|
||||
|
||||
internal fun IrFunctionAccessExpression.getValueArguments() =
|
||||
(0 until valueArgumentsCount).map { i ->
|
||||
getValueArgument(i)
|
||||
}
|
||||
|
||||
internal fun IrValueParameter.capture() = buildGetValue(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol)
|
||||
|
||||
internal fun IrPluginContext.buildGetterType(valueType: IrType): IrSimpleType =
|
||||
buildFunctionSimpleType(
|
||||
irBuiltIns.functionN(0).symbol,
|
||||
listOf(valueType)
|
||||
)
|
||||
|
||||
internal fun IrPluginContext.buildSetterType(valueType: IrType): IrSimpleType =
|
||||
buildFunctionSimpleType(
|
||||
irBuiltIns.functionN(1).symbol,
|
||||
listOf(valueType, irBuiltIns.unitType)
|
||||
)
|
||||
|
||||
private fun buildSetField(backingField: IrField, ownerClass: IrExpression?, value: IrGetValue): IrSetField {
|
||||
val receiver = if (ownerClass is IrTypeOperatorCall) ownerClass.argument as IrGetValue else ownerClass
|
||||
return buildSetField(
|
||||
symbol = backingField.symbol,
|
||||
receiver = receiver,
|
||||
value = value
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildGetField(backingField: IrField, ownerClass: IrExpression?): IrGetField {
|
||||
val receiver = if (ownerClass is IrTypeOperatorCall) ownerClass.argument as IrGetValue else ownerClass
|
||||
return buildGetField(
|
||||
symbol = backingField.symbol,
|
||||
receiver = receiver
|
||||
)
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.buildAccessorLambda(
|
||||
getter: IrCall,
|
||||
valueType: IrType,
|
||||
isSetter: Boolean,
|
||||
isArrayElement: Boolean
|
||||
): IrExpression {
|
||||
val getterCall = if (isArrayElement) getter.dispatchReceiver as IrCall else getter
|
||||
val type = if (isSetter) buildSetterType(valueType) else buildGetterType(valueType)
|
||||
val name = if (isSetter) setterName(getterCall) else getterName(getterCall)
|
||||
val returnType = if (isSetter) irBuiltIns.unitType else valueType
|
||||
val accessorFunction = irFactory.buildFun {
|
||||
startOffset = UNDEFINED_OFFSET
|
||||
endOffset = UNDEFINED_OFFSET
|
||||
this.origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||
this.name = Name.identifier(name)
|
||||
this.visibility = DescriptorVisibilities.LOCAL
|
||||
this.isInline = true
|
||||
this.returnType = returnType
|
||||
}.apply {
|
||||
val valueParameter = JsIrBuilder.buildValueParameter(this, name, 0, valueType)
|
||||
this.valueParameters = if (isSetter) listOf(valueParameter) else emptyList()
|
||||
val body = if (isSetter) {
|
||||
if (isArrayElement) {
|
||||
val setSymbol = referenceFunction(referenceArrayClass(getterCall.type as IrSimpleType), SET)
|
||||
val elementIndex = getter.getValueArgument(0)!!.deepCopyWithVariables()
|
||||
buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = setSymbol,
|
||||
type = irBuiltIns.unitType,
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
valueArguments = listOf(elementIndex, valueParameter.capture())
|
||||
).apply {
|
||||
dispatchReceiver = getterCall
|
||||
}
|
||||
} else {
|
||||
buildSetField(getterCall.getBackingField(), getterCall.dispatchReceiver, valueParameter.capture())
|
||||
}
|
||||
} else {
|
||||
val getField = buildGetField(getterCall.getBackingField(), getterCall.dispatchReceiver)
|
||||
if (isArrayElement) {
|
||||
val getSymbol = referenceFunction(referenceArrayClass(getterCall.type as IrSimpleType), GET)
|
||||
val elementIndex = getter.getValueArgument(0)!!.deepCopyWithVariables()
|
||||
buildCall(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
target = getSymbol,
|
||||
type = valueType,
|
||||
origin = IrStatementOrigin.LAMBDA,
|
||||
valueArguments = listOf(elementIndex)
|
||||
).apply {
|
||||
dispatchReceiver = getField.deepCopyWithVariables()
|
||||
}
|
||||
} else {
|
||||
getField.deepCopyWithVariables()
|
||||
}
|
||||
}
|
||||
this.body = irFactory.buildBlockBody(listOf(body))
|
||||
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||
}
|
||||
return IrFunctionExpressionImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
type,
|
||||
accessorFunction,
|
||||
IrStatementOrigin.LAMBDA
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrCall.getBackingField(): IrField {
|
||||
val correspondingPropertySymbol = symbol.owner.correspondingPropertySymbol!!
|
||||
return correspondingPropertySymbol.owner.backingField!!
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.referencePackageFunction(
|
||||
packageName: String,
|
||||
name: String,
|
||||
predicate: (IrFunctionSymbol) -> Boolean = { true }
|
||||
) = try {
|
||||
referenceFunctions(FqName("$packageName.$name")).single(predicate)
|
||||
} catch (e: RuntimeException) {
|
||||
error("Exception while looking for the function `$name` in package `$packageName`: ${e.message}")
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.referenceFunction(classSymbol: IrClassSymbol, functionName: String): IrSimpleFunctionSymbol {
|
||||
val functionId = FqName("$KOTLIN.${classSymbol.owner.name}.$functionName")
|
||||
return try {
|
||||
referenceFunctions(functionId).single()
|
||||
} catch (e: RuntimeException) {
|
||||
error("Exception while looking for the function `$functionId`: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrPluginContext.referenceArrayClass(irType: IrSimpleType): IrClassSymbol {
|
||||
val afuClassId = (irType.classifier.signature!!.asPublic())!!.declarationFqName
|
||||
val classId = FqName("$KOTLIN.${AFU_ARRAY_CLASSES[afuClassId]!!}")
|
||||
return referenceClass(classId)!!
|
||||
}
|
||||
|
||||
internal fun IrPluginContext.getArrayConstructorSymbol(irType: IrSimpleType, predicate: (IrConstructorSymbol) -> Boolean = { true }): IrConstructorSymbol {
|
||||
val afuClassId = (irType.classifier.signature!!.asPublic())!!.declarationFqName
|
||||
val classId = FqName("$KOTLIN.${AFU_ARRAY_CLASSES[afuClassId]!!}")
|
||||
return referenceConstructors(classId).single(predicate)
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.js.test.ir.AbstractJsIrTest
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
|
||||
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")
|
||||
|
||||
open class AbstractAtomicfuJsIrTest : AbstractJsIrTest(
|
||||
pathToTestDir = "plugins/atomicfu/atomicfu-compiler/testData/box/",
|
||||
testGroupOutputDirPrefix = "box/"
|
||||
) {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
with(builder) {
|
||||
useConfigurators(::AtomicfuEnvironmentConfigurator)
|
||||
useCustomRuntimeClasspathProviders(::AtomicfuRuntimeClasspathProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AtomicfuRuntimeClasspathProvider(testServices: TestServices) : RuntimeClasspathProvider(testServices) {
|
||||
override fun runtimeClassPaths(module: TestModule): List<File> {
|
||||
return listOf(atomicfuCompileDependency, atomicfuRuntime).map { File(it) }
|
||||
}
|
||||
}
|
||||
|
||||
class AtomicfuEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
override fun registerCompilerExtensions(project: Project) {
|
||||
IrGenerationExtension.registerExtension(project, AtomicfuLoweringExtension())
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 AtomicfuJsIrTestGenerated extends AbstractAtomicfuJsIrTest {
|
||||
@Test
|
||||
public void testAllFilesPresentInBox() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ArithmeticTest.kt")
|
||||
public void testArithmeticTest() throws Exception {
|
||||
runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArithmeticTest.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ArrayInlineFunctionTest.kt")
|
||||
public void testArrayInlineFunctionTest() throws Exception {
|
||||
runTest("plugins/atomicfu/atomicfu-compiler/testData/box/ArrayInlineFunctionTest.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("AtomicArrayTest.kt")
|
||||
public void testAtomicArrayTest() throws Exception {
|
||||
runTest("plugins/atomicfu/atomicfu-compiler/testData/box/AtomicArrayTest.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("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("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 {
|
||||
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("UncheckedCastTest.kt")
|
||||
public void testUncheckedCastTest() throws Exception {
|
||||
runTest("plugins/atomicfu/atomicfu-compiler/testData/box/UncheckedCastTest.kt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class IntArithmetic {
|
||||
val _x = atomic(0)
|
||||
val x get() = _x.value
|
||||
}
|
||||
|
||||
class LongArithmetic {
|
||||
val _x = atomic(4294967296)
|
||||
val x get() = _x.value
|
||||
val y = atomic(5000000000)
|
||||
val z = atomic(2424920024888888848)
|
||||
val max = atomic(9223372036854775807)
|
||||
}
|
||||
|
||||
class BooleanArithmetic {
|
||||
val _x = atomic(false)
|
||||
val x get() = _x.value
|
||||
}
|
||||
|
||||
class ReferenceArithmetic {
|
||||
val _x = atomic<String?>(null)
|
||||
}
|
||||
|
||||
class ArithmeticTest {
|
||||
val local = atomic(0)
|
||||
|
||||
fun testGetValue() {
|
||||
val a = IntArithmetic()
|
||||
a._x.value = 5
|
||||
check(a._x.value == 5)
|
||||
var aValue = a._x.value
|
||||
check(aValue == 5)
|
||||
check(a.x == 5)
|
||||
|
||||
local.value = 555
|
||||
aValue = local.value
|
||||
check(local.value == aValue)
|
||||
}
|
||||
|
||||
fun testAtomicCallPlaces(): Boolean {
|
||||
val a = IntArithmetic()
|
||||
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))
|
||||
return a._x.compareAndSet(77, 88)
|
||||
}
|
||||
|
||||
fun testInt() {
|
||||
val a = IntArithmetic()
|
||||
check(a.x == 0)
|
||||
val update = 3
|
||||
check(a._x.getAndSet(update) == 0)
|
||||
check(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))
|
||||
}
|
||||
|
||||
fun testLong() {
|
||||
val a = LongArithmetic()
|
||||
check(a.z.value == 2424920024888888848)
|
||||
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)
|
||||
}
|
||||
|
||||
fun testBoolean() {
|
||||
val a = BooleanArithmetic()
|
||||
check(!a.x)
|
||||
a._x.lazySet(true)
|
||||
check(a.x)
|
||||
check(a._x.getAndSet(true))
|
||||
check(a._x.compareAndSet(true, false))
|
||||
check(!a.x)
|
||||
}
|
||||
|
||||
fun testReference() {
|
||||
val a = ReferenceArithmetic()
|
||||
a._x.value = "aaa"
|
||||
check(a._x.value == "aaa")
|
||||
a._x.lazySet("bb")
|
||||
check(a._x.value == "bb")
|
||||
check(a._x.getAndSet("ccc") == "bb")
|
||||
check(a._x.value == "ccc")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ArithmeticTest()
|
||||
|
||||
testClass.testGetValue()
|
||||
if (!testClass.testAtomicCallPlaces()) return "testAtomicCallPlaces: FAILED"
|
||||
|
||||
testClass.testInt()
|
||||
testClass.testLong()
|
||||
testClass.testBoolean()
|
||||
testClass.testReference()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class ArrayInlineFunctionTest {
|
||||
private val anyArr = atomicArrayOfNulls<Any?>(5)
|
||||
private val refArr = atomicArrayOfNulls<Box>(5)
|
||||
|
||||
private data class Box(val n: Int)
|
||||
|
||||
fun testSetArrayElementValueInLoop() {
|
||||
anyArr[0].loop { cur ->
|
||||
assertTrue(anyArr[0].compareAndSet(cur, IntArray(5)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ArrayInlineFunctionTest()
|
||||
testClass.testSetArrayElementValueInLoop()
|
||||
testClass.testArrayElementGetAndUpdate()
|
||||
testClass.testArrayElementUpdate()
|
||||
testClass.testArrayElementUpdateAndGet()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class AtomicArrayTest {
|
||||
|
||||
fun testIntArray() {
|
||||
val A = AtomicArrayClass()
|
||||
check(A.intArr[0].compareAndSet(0, 3))
|
||||
check(A.intArr[1].value == 0)
|
||||
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)
|
||||
A.intArr[2].value = 2
|
||||
check(A.intArr[2].value == 2)
|
||||
check(A.intArr[2].compareAndSet(2, 34))
|
||||
check(A.intArr[2].value == 34)
|
||||
}
|
||||
|
||||
|
||||
fun testLongArray() {
|
||||
val A = AtomicArrayClass()
|
||||
A.longArr[0].value = 2424920024888888848
|
||||
check(A.longArr[0].value == 2424920024888888848)
|
||||
A.longArr[0].lazySet(8424920024888888848)
|
||||
check(A.longArr[0].value == 8424920024888888848)
|
||||
val ac = A.longArr[0].value
|
||||
A.longArr[3].value = ac
|
||||
check(A.longArr[3].getAndSet(8924920024888888848) == 8424920024888888848)
|
||||
check(A.longArr[3].value == 8924920024888888848)
|
||||
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)
|
||||
A.longArr[4].value = 8924920024888888848
|
||||
check(A.longArr[4].getAndAdd(100000000000000000) == 8924920024888888848)
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
fun testBooleanArray() {
|
||||
val A = AtomicArrayClass()
|
||||
check(!A.booleanArr[1].value)
|
||||
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)
|
||||
A.booleanArr[0].value = false
|
||||
check(!A.booleanArr[0].value)
|
||||
}
|
||||
|
||||
fun testRefArray() {
|
||||
val A = AtomicArrayClass()
|
||||
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)
|
||||
val r0 = A.refArr[0].value
|
||||
A.refArr[3].value = r0
|
||||
check(A.refArr[3].value!!.n == 3)
|
||||
val a = A.a.value
|
||||
check(A.refArr[3].compareAndSet(a3, a))
|
||||
}
|
||||
}
|
||||
|
||||
class AtomicArrayClass {
|
||||
val intArr = AtomicIntArray(10)
|
||||
val longArr = AtomicLongArray(10)
|
||||
val booleanArr = AtomicBooleanArray(10)
|
||||
val refArr = atomicArrayOfNulls<ARef>(10)
|
||||
val anyArr = atomicArrayOfNulls<Any?>(10)
|
||||
val a = atomic(ARef(8))
|
||||
}
|
||||
|
||||
data class ARef(val n: Int)
|
||||
|
||||
fun box(): String {
|
||||
val testClass = AtomicArrayTest()
|
||||
testClass.testIntArray()
|
||||
testClass.testLongArray()
|
||||
testClass.testBooleanArray()
|
||||
testClass.testRefArray()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class ExtensionsTest {
|
||||
val a = atomic(0)
|
||||
val l = atomic(0L)
|
||||
val s = atomic<String?>(null)
|
||||
val b = atomic(true)
|
||||
|
||||
fun testScopedFieldGetters() {
|
||||
check(a.value == 0)
|
||||
val update = 3
|
||||
a.lazySet(update)
|
||||
check(a.compareAndSet(update, 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)
|
||||
check(a.compareAndSet(7, 10))
|
||||
}
|
||||
|
||||
inline fun AtomicInt.intExtensionArithmetic() {
|
||||
value = 0
|
||||
check(value == 0)
|
||||
val update = 3
|
||||
lazySet(update)
|
||||
check(compareAndSet(update, 8))
|
||||
lazySet(1)
|
||||
check(value == 1)
|
||||
check(getAndSet(2) == 1)
|
||||
check(value == 2)
|
||||
check(getAndIncrement() == 2)
|
||||
check(value == 3)
|
||||
check(getAndDecrement() == 3)
|
||||
check(value == 2)
|
||||
check(getAndAdd(2) == 2)
|
||||
check(value == 4)
|
||||
check(addAndGet(3) == 7)
|
||||
check(value == 7)
|
||||
check(incrementAndGet() == 8)
|
||||
check(value == 8)
|
||||
check(decrementAndGet() == 7)
|
||||
check(value == 7)
|
||||
check(compareAndSet(7, 10))
|
||||
check(compareAndSet(value, 55))
|
||||
check(value == 55)
|
||||
}
|
||||
|
||||
inline fun AtomicLong.longExtensionArithmetic() {
|
||||
value = 2424920024888888848
|
||||
check(value == 2424920024888888848)
|
||||
lazySet(8424920024888888848)
|
||||
check(value == 8424920024888888848)
|
||||
check(getAndSet(8924920024888888848) == 8424920024888888848)
|
||||
check(value == 8924920024888888848)
|
||||
check(incrementAndGet() == 8924920024888888849) // fails
|
||||
check(value == 8924920024888888849)
|
||||
check(getAndDecrement() == 8924920024888888849)
|
||||
check(value == 8924920024888888848)
|
||||
check(getAndAdd(100000000000000000) == 8924920024888888848)
|
||||
check(value == 9024920024888888848)
|
||||
check(addAndGet(-9223372036854775807) == -198452011965886959)
|
||||
check(value == -198452011965886959)
|
||||
check(incrementAndGet() == -198452011965886958)
|
||||
check(value == -198452011965886958)
|
||||
check(decrementAndGet() == -198452011965886959)
|
||||
check(value == -198452011965886959)
|
||||
}
|
||||
|
||||
inline fun AtomicRef<String?>.refExtension() {
|
||||
value = "aaa"
|
||||
check(value == "aaa")
|
||||
lazySet("bb")
|
||||
check(value == "bb")
|
||||
check(getAndSet("ccc") == "bb")
|
||||
check(value == "ccc")
|
||||
}
|
||||
|
||||
inline fun AtomicBoolean.booleanExtensionArithmetic() {
|
||||
value = false
|
||||
check(!value)
|
||||
lazySet(true)
|
||||
check(value)
|
||||
check(getAndSet(true))
|
||||
check(compareAndSet(value, false))
|
||||
check(!value)
|
||||
}
|
||||
|
||||
fun testExtension() {
|
||||
a.intExtensionArithmetic()
|
||||
l.longExtensionArithmetic()
|
||||
s.refExtension()
|
||||
b.booleanExtensionArithmetic()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ExtensionsTest()
|
||||
testClass.testScopedFieldGetters()
|
||||
testClass.testExtension()
|
||||
return "OK"
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class IndexArrayElementGetterTest {
|
||||
private val clazz = AtomicArrayClass()
|
||||
|
||||
fun fib(a: Int): Int = if (a == 0 || a == 1) a else fib(a - 1) + fib(a - 2)
|
||||
|
||||
fun testIndexArrayElementGetting() {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
class AtomicArrayClass {
|
||||
val intArr = AtomicIntArray(10)
|
||||
val longArr = AtomicLongArray(10)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = IndexArrayElementGetterTest()
|
||||
testClass.testIndexArrayElementGetting()
|
||||
return "OK"
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class InlineExtensionWithTypeParameterTest {
|
||||
abstract class Segment<S : Segment<S>>(val id: Int)
|
||||
class SemaphoreSegment(id: Int) : Segment<SemaphoreSegment>(id)
|
||||
|
||||
private inline fun <S : Segment<S>> AtomicRef<S>.foo(
|
||||
id: Int,
|
||||
startFrom: S
|
||||
) {
|
||||
startFrom.getSegmentId()
|
||||
}
|
||||
|
||||
private inline fun <S : Segment<S>> S.getSegmentId(): Int {
|
||||
var cur: S = this
|
||||
return cur.id
|
||||
}
|
||||
|
||||
fun testInlineExtensionWithTypeParameter() {
|
||||
val s = SemaphoreSegment(0)
|
||||
val sref = atomic(s)
|
||||
sref.foo(0, s)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = InlineExtensionWithTypeParameterTest()
|
||||
testClass.testInlineExtensionWithTypeParameter()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class LockFreeIntBitsTest {
|
||||
fun testBasic() {
|
||||
val bs = LockFreeIntBits()
|
||||
check(!bs[0])
|
||||
check(bs.bitSet(0))
|
||||
check(bs[0])
|
||||
check(!bs.bitSet(0))
|
||||
|
||||
check(!bs[1])
|
||||
check(bs.bitSet(1))
|
||||
check(bs[1])
|
||||
check(!bs.bitSet(1))
|
||||
check(!bs.bitSet(0))
|
||||
|
||||
check(bs[0])
|
||||
check(bs.bitClear(0))
|
||||
check(!bs.bitClear(0))
|
||||
|
||||
check(bs[1])
|
||||
}
|
||||
}
|
||||
|
||||
class LockFreeIntBits {
|
||||
private val bits = atomic(0)
|
||||
|
||||
private fun Int.mask() = 1 shl this
|
||||
|
||||
operator fun get(index: Int): Boolean = bits.value and index.mask() != 0
|
||||
|
||||
// User-defined private inline function
|
||||
private inline fun bitUpdate(check: (Int) -> Boolean, upd: (Int) -> Int): Boolean {
|
||||
bits.update {
|
||||
if (check(it)) return false
|
||||
upd(it)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun bitSet(index: Int): Boolean {
|
||||
val mask = index.mask()
|
||||
return bitUpdate({ it and mask != 0 }, { it or mask })
|
||||
}
|
||||
|
||||
fun bitClear(index: Int): Boolean {
|
||||
val mask = index.mask()
|
||||
return bitUpdate({ it and mask == 0 }, { it and mask.inv() })
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = LockFreeIntBitsTest()
|
||||
testClass.testBasic()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import kotlinx.atomicfu.*
|
||||
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)
|
||||
}
|
||||
|
||||
fun testBasic() = testWith { get() }
|
||||
|
||||
fun testGetInner() = testWith { getInner() }
|
||||
|
||||
fun testAdd2() {
|
||||
val c = LockFreeLongCounter()
|
||||
c.add2()
|
||||
check(c.get() == 2L)
|
||||
c.add2()
|
||||
check(c.get() == 4L)
|
||||
}
|
||||
|
||||
fun testSetM2() {
|
||||
val c = LockFreeLongCounter()
|
||||
c.setM2()
|
||||
check(c.get() == -2L)
|
||||
}
|
||||
}
|
||||
|
||||
class LockFreeLongCounter {
|
||||
private val counter = atomic(0L)
|
||||
|
||||
fun get(): Long = counter.value
|
||||
|
||||
fun increment(): Long = counter.incrementAndGet()
|
||||
|
||||
fun add2() = counter.getAndAdd(2)
|
||||
|
||||
fun setM2() {
|
||||
counter.value = -2L // LDC instruction here
|
||||
}
|
||||
|
||||
fun getInner(): Long = Inner().getFromOuter()
|
||||
|
||||
// testing how an inner class can get access to it
|
||||
private inner class Inner {
|
||||
fun getFromOuter(): Long = counter.value
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = LockFreeLongCounterTest()
|
||||
testClass.testBasic()
|
||||
testClass.testAdd2()
|
||||
testClass.testSetM2()
|
||||
testClass.testGetInner()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class LockFreeQueueTest {
|
||||
fun testBasic() {
|
||||
val q = LockFreeQueue()
|
||||
check(q.dequeue() == -1)
|
||||
q.enqueue(42)
|
||||
check(q.dequeue() == 42)
|
||||
check(q.dequeue() == -1)
|
||||
q.enqueue(1)
|
||||
q.enqueue(2)
|
||||
check(q.dequeue() == 1)
|
||||
check(q.dequeue() == 2)
|
||||
check(q.dequeue() == -1)
|
||||
}
|
||||
}
|
||||
|
||||
// MS-queue
|
||||
public class LockFreeQueue {
|
||||
private val head = atomic(Node(0))
|
||||
private val tail = atomic(head.value)
|
||||
|
||||
private class Node(val value: Int) {
|
||||
val next = atomic<Node?>(null)
|
||||
}
|
||||
|
||||
public fun enqueue(value: Int) {
|
||||
val node = Node(value)
|
||||
tail.loop { curTail ->
|
||||
val curNext = curTail.next.value
|
||||
if (curNext != null) {
|
||||
tail.compareAndSet(curTail, curNext)
|
||||
return@loop
|
||||
}
|
||||
if (curTail.next.compareAndSet(null, node)) {
|
||||
tail.compareAndSet(curTail, node)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun dequeue(): Int {
|
||||
head.loop { curHead ->
|
||||
val next = curHead.next.value ?: return -1
|
||||
if (head.compareAndSet(curHead, next)) return next.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = LockFreeQueueTest()
|
||||
testClass.testBasic()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class LockFreeStackTest {
|
||||
|
||||
fun testClear() {
|
||||
val s = LockFreeStack<String>()
|
||||
check(s.isEmpty())
|
||||
s.pushLoop("A")
|
||||
check(!s.isEmpty())
|
||||
s.clear()
|
||||
check(s.isEmpty())
|
||||
}
|
||||
|
||||
fun testPushPopLoop() {
|
||||
val s = LockFreeStack<String>()
|
||||
check(s.isEmpty())
|
||||
s.pushLoop("A")
|
||||
check(!s.isEmpty())
|
||||
check(s.popLoop() == "A")
|
||||
check(s.isEmpty())
|
||||
}
|
||||
|
||||
fun testPushPopUpdate() {
|
||||
val s = LockFreeStack<String>()
|
||||
check(s.isEmpty())
|
||||
s.pushUpdate("A")
|
||||
check(!s.isEmpty())
|
||||
check(s.popUpdate() == "A")
|
||||
check(s.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
class LockFreeStack<T> {
|
||||
private val top = atomic<Node<T>?>(null)
|
||||
|
||||
private class Node<T>(val value: T, val next: Node<T>?)
|
||||
|
||||
fun isEmpty() = top.value == null
|
||||
|
||||
fun clear() { top.value = null }
|
||||
|
||||
fun pushLoop(value: T) {
|
||||
top.loop { cur ->
|
||||
val upd = Node(value, cur)
|
||||
if (top.compareAndSet(cur, upd)) return
|
||||
}
|
||||
}
|
||||
|
||||
fun popLoop(): T? {
|
||||
top.loop { cur ->
|
||||
if (cur == null) return null
|
||||
if (top.compareAndSet(cur, cur.next)) return cur.value
|
||||
}
|
||||
}
|
||||
|
||||
fun pushUpdate(value: T) {
|
||||
top.update { cur -> Node(value, cur) }
|
||||
}
|
||||
|
||||
fun popUpdate(): T? =
|
||||
top.getAndUpdate { cur -> cur?.next } ?.value
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = LockFreeStackTest()
|
||||
testClass.testClear()
|
||||
testClass.testPushPopLoop()
|
||||
testClass.testPushPopUpdate()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class LockTest {
|
||||
private val inProgressLock = atomic(false)
|
||||
|
||||
fun testLock() {
|
||||
var result = ""
|
||||
if (inProgressLock.tryAcquire()) {
|
||||
result = "OK"
|
||||
}
|
||||
assertEquals("OK", result)
|
||||
}
|
||||
}
|
||||
|
||||
// This function will be removed by transformer
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true)
|
||||
|
||||
// This function is here to test if the Kotlin metadata still consistent after transform
|
||||
// It is used in ReflectionTest, DO NOT REMOVE
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun <AA, BB : Number> String.reflectionTest(mapParam: Map<in AA, BB>): List<BB> = error("no impl")
|
||||
|
||||
fun box(): String {
|
||||
val testClass = LockTest()
|
||||
testClass.testLock()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class LoopTest {
|
||||
private val a = atomic(0)
|
||||
private val r = atomic<A>(A("aaaa"))
|
||||
private val rs = atomic<String>("bbbb")
|
||||
|
||||
private 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
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
private inline fun AtomicRef<A>.casLoop(to: String): String = loop { cur ->
|
||||
if (compareAndSet(cur, A(to))) {
|
||||
val res = value.s
|
||||
return "${res}_AtomicRef<A>"
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun AtomicRef<String>.casLoop(to: String): String = loop { cur ->
|
||||
if (compareAndSet(cur, to)) return "${value}_AtomicRef<String>"
|
||||
}
|
||||
|
||||
fun testDeclarationWithEqualNames() {
|
||||
check(r.casLoop("kk") == "kk_AtomicRef<A>")
|
||||
check(rs.casLoop("pp") == "pp_AtomicRef<String>")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = LoopTest()
|
||||
testClass.testIntExtensionLoops()
|
||||
testClass.testDeclarationWithEqualNames()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class MultiInitTest {
|
||||
fun testBasic() {
|
||||
val t = MultiInit()
|
||||
check(t.incA() == 1)
|
||||
check(t.incA() == 2)
|
||||
check(t.incB() == 1)
|
||||
check(t.incB() == 2)
|
||||
}
|
||||
}
|
||||
|
||||
class MultiInit {
|
||||
private val a = atomic(0)
|
||||
private val b = atomic(0)
|
||||
|
||||
fun incA() = a.incrementAndGet()
|
||||
fun incB() = b.incrementAndGet()
|
||||
|
||||
companion object {
|
||||
fun foo() {} // just to force some clinit in outer file
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = MultiInitTest()
|
||||
testClass.testBasic()
|
||||
return "OK"
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class ParameterizedInlineFunExtensionTest {
|
||||
|
||||
private inline fun <S> AtomicRef<S>.foo(res1: S, res2: S, foo: (S) -> S): S {
|
||||
val res = bar(res1, res2)
|
||||
return res
|
||||
}
|
||||
|
||||
private inline fun <S> AtomicRef<S>.bar(res1: S, res2: S): S {
|
||||
return res2
|
||||
}
|
||||
|
||||
private val tail = atomic("aaa")
|
||||
|
||||
fun testClose() {
|
||||
val res = tail.foo("bbb", "ccc") { s -> s }
|
||||
assertEquals("ccc", res)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ParameterizedInlineFunExtensionTest()
|
||||
testClass.testClose()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlinx.atomicfu.locks.*
|
||||
import kotlin.test.*
|
||||
|
||||
class PropertyDeclarationTest {
|
||||
private val a: AtomicInt
|
||||
private val head: AtomicRef<String>
|
||||
private val lateIntArr: AtomicIntArray
|
||||
private val lateRefArr: AtomicArray<String?>
|
||||
private val lock: ReentrantLock
|
||||
|
||||
init {
|
||||
a = atomic(0)
|
||||
head = atomic("AAA")
|
||||
lateIntArr = AtomicIntArray(55)
|
||||
lateRefArr = atomicArrayOfNulls<String?>(10)
|
||||
lock = reentrantLock()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
assertEquals(0, a.value)
|
||||
check(head.compareAndSet("AAA", "BBB"))
|
||||
assertEquals("BBB", head.value)
|
||||
assertEquals(0, lateIntArr[35].value)
|
||||
assertEquals(null, lateRefArr[5].value)
|
||||
assertEquals(null, lock)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = PropertyDeclarationTest()
|
||||
testClass.test()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import kotlinx.atomicfu.locks.*
|
||||
import kotlin.test.*
|
||||
|
||||
class ReentrantLockTest {
|
||||
private val lock = reentrantLock()
|
||||
private var state = 0
|
||||
|
||||
fun testLockField() {
|
||||
lock.withLock {
|
||||
state = 1
|
||||
}
|
||||
assertEquals(1, state)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ReentrantLockTest()
|
||||
testClass.testLockField()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class AA(val value: Int) {
|
||||
val b = B(value + 1)
|
||||
val c = C(D(E(value + 1)))
|
||||
|
||||
fun updateToB(affected: Any): Boolean {
|
||||
(affected as AtomicState).state.compareAndSet(this, b)
|
||||
return (affected.state.value is B && (affected.state.value as B).value == value + 1)
|
||||
}
|
||||
|
||||
fun manyProperties(affected: Any): Boolean {
|
||||
(affected as AtomicState).state.compareAndSet(this, c.d.e)
|
||||
return (affected.state.value is E && (affected.state.value as E).x == value + 1)
|
||||
}
|
||||
}
|
||||
|
||||
class B (val value: Int)
|
||||
|
||||
class C (val d: D)
|
||||
class D (val e: E)
|
||||
class E (val x: Int)
|
||||
|
||||
|
||||
class AtomicState(value: Any) {
|
||||
val state = atomic<Any?>(value)
|
||||
}
|
||||
|
||||
class ScopeTest {
|
||||
fun scopeTest() {
|
||||
val a = AA(0)
|
||||
val affected: Any = AtomicState(a)
|
||||
check(a.updateToB(affected))
|
||||
val a1 = AA(0)
|
||||
val affected1: Any = AtomicState(a1)
|
||||
check(a1.manyProperties(affected1))
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = ScopeTest()
|
||||
testClass.scopeTest()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
class SimpleLockTest {
|
||||
fun withLock() {
|
||||
val lock = SimpleLock()
|
||||
val result = lock.withLock {
|
||||
"OK"
|
||||
}
|
||||
assertEquals("OK", result)
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleLock {
|
||||
private val _locked = atomic(0)
|
||||
|
||||
fun <T> withLock(block: () -> T): T {
|
||||
// this contrieves construct triggers Kotlin compiler to reuse local variable slot #2 for
|
||||
// the exception in `finally` clause
|
||||
try {
|
||||
_locked.loop { locked ->
|
||||
check(locked == 0)
|
||||
if (!_locked.compareAndSet(0, 1)) return@loop // continue
|
||||
return block()
|
||||
}
|
||||
} finally {
|
||||
_locked.value = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = SimpleLockTest()
|
||||
testClass.withLock()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import kotlinx.atomicfu.locks.*
|
||||
import kotlin.test.*
|
||||
|
||||
class SynchronizedObjectTest : SynchronizedObject() {
|
||||
|
||||
fun testSync() {
|
||||
val result = synchronized(this) { bar() }
|
||||
assertEquals("OK", result)
|
||||
}
|
||||
|
||||
private fun bar(): String =
|
||||
synchronized(this) {
|
||||
"OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = SynchronizedObjectTest()
|
||||
testClass.testSync()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
private val a = atomic(0)
|
||||
private val b = atomic(2424920024888888848)
|
||||
private val c = atomic(true)
|
||||
private val abcNode = atomic(ANode(BNode(CNode(8))))
|
||||
private val any = atomic<Any?>(null)
|
||||
|
||||
private val intArr = AtomicIntArray(3)
|
||||
private val longArr = AtomicLongArray(5)
|
||||
private val booleanArr = AtomicBooleanArray(4)
|
||||
private val refArr = atomicArrayOfNulls<ANode<BNode<CNode>>>(5)
|
||||
private val anyRefArr = atomicArrayOfNulls<Any>(10)
|
||||
|
||||
private val stringAtomicNullArr = atomicArrayOfNulls<String>(10)
|
||||
|
||||
class TopLevelPrimitiveTest {
|
||||
|
||||
fun testTopLevelInt() {
|
||||
a.value
|
||||
check(a.value == 0)
|
||||
check(a.getAndSet(3) == 0)
|
||||
check(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)
|
||||
}
|
||||
|
||||
fun testTopLevelLong() {
|
||||
check(b.value == 2424920024888888848)
|
||||
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)
|
||||
}
|
||||
|
||||
fun testTopLevelBoolean() {
|
||||
check(c.value)
|
||||
c.lazySet(false)
|
||||
check(!c.value)
|
||||
check(!c.getAndSet(true))
|
||||
check(c.compareAndSet(true, false))
|
||||
check(!c.value)
|
||||
}
|
||||
|
||||
fun testTopLevelRef() {
|
||||
check(abcNode.value.b.c.d == 8)
|
||||
val newNode = ANode(BNode(CNode(76)))
|
||||
check(abcNode.getAndSet(newNode).b.c.d == 8)
|
||||
check(abcNode.value.b.c.d == 76)
|
||||
val l = IntArray(4){i -> i}
|
||||
any.lazySet(l)
|
||||
check((any.value as IntArray)[2] == 2)
|
||||
}
|
||||
|
||||
fun testTopLevelArrayOfNulls() {
|
||||
check(stringAtomicNullArr[0].value == null)
|
||||
check(stringAtomicNullArr[0].compareAndSet(null, "aa"))
|
||||
stringAtomicNullArr[1].lazySet("aa")
|
||||
check(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
|
||||
}
|
||||
}
|
||||
|
||||
class TopLevelArrayTest {
|
||||
|
||||
fun testIntArray() {
|
||||
check(intArr[0].compareAndSet(0, 3))
|
||||
check(intArr[1].value == 0)
|
||||
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)
|
||||
intArr[2].value = 2
|
||||
check(intArr[2].value == 2)
|
||||
check(intArr[2].compareAndSet(2, 34))
|
||||
check(intArr[2].value == 34)
|
||||
}
|
||||
|
||||
fun testLongArray() {
|
||||
longArr[0].value = 2424920024888888848
|
||||
check(longArr[0].value == 2424920024888888848)
|
||||
longArr[0].lazySet(8424920024888888848)
|
||||
check(longArr[0].value == 8424920024888888848)
|
||||
val ac = longArr[0].value
|
||||
longArr[3].value = ac
|
||||
check(longArr[3].getAndSet(8924920024888888848) == 8424920024888888848)
|
||||
check(longArr[3].value == 8924920024888888848)
|
||||
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)
|
||||
longArr[4].value = 8924920024888888848
|
||||
check(longArr[4].getAndAdd(100000000000000000) == 8924920024888888848)
|
||||
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)
|
||||
}
|
||||
|
||||
fun testBooleanArray() {
|
||||
check(!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)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun testRefArray() {
|
||||
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)
|
||||
val r0 = refArr[0].value
|
||||
refArr[3].value = r0
|
||||
check(refArr[3].value!!.b.c.d == 3)
|
||||
val a = abcNode.value
|
||||
check(refArr[3].compareAndSet(a3, a))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class ANode<T>(val b: T)
|
||||
data class BNode<T>(val c: T)
|
||||
data class CNode(val d: Int)
|
||||
|
||||
fun box(): String {
|
||||
val primitiveTest = TopLevelPrimitiveTest()
|
||||
primitiveTest.testTopLevelInt()
|
||||
primitiveTest.testTopLevelLong()
|
||||
primitiveTest.testTopLevelBoolean()
|
||||
primitiveTest.testTopLevelRef()
|
||||
primitiveTest.testTopLevelArrayOfNulls()
|
||||
|
||||
val arrayTest = TopLevelArrayTest()
|
||||
arrayTest.testIntArray()
|
||||
arrayTest.testLongArray()
|
||||
arrayTest.testBooleanArray()
|
||||
arrayTest.testRefArray()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import kotlinx.atomicfu.*
|
||||
import kotlin.test.*
|
||||
|
||||
private val topLevelS = atomic<Any>(arrayOf("A", "B"))
|
||||
|
||||
class UncheckedCastTest {
|
||||
private val s = atomic<Any>("AAA")
|
||||
private val bs = atomic<Any?>(null)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun testAtomicValUncheckedCast() {
|
||||
assertEquals((s as AtomicRef<String>).value, "AAA")
|
||||
bs.lazySet(arrayOf(arrayOf(Box(1), Box(2))))
|
||||
assertEquals((bs as AtomicRef<Array<Array<Box>>>).value[0]!![0].b * 10, 10)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun testTopLevelValUnchekedCast() {
|
||||
assertEquals((topLevelS as AtomicRef<Array<String>>).value[1], "B")
|
||||
}
|
||||
|
||||
private data class Box(val b: Int)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
|
||||
private inline fun <T> AtomicRef<T>.getString(): String =
|
||||
(this as AtomicRef<String>).value
|
||||
|
||||
fun testInlineFunc() {
|
||||
assertEquals("AAA", s.getString())
|
||||
}
|
||||
|
||||
private val a = atomicArrayOfNulls<Any?>(10)
|
||||
|
||||
fun testArrayValueUncheckedCast() {
|
||||
a[0].value = "OK"
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
assertEquals("OK", (a[0] as AtomicRef<String>).value)
|
||||
}
|
||||
|
||||
fun testArrayValueUncheckedCastInlineFunc() {
|
||||
a[0].value = "OK"
|
||||
assertEquals("OK", a[0].getString())
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val testClass = UncheckedCastTest()
|
||||
testClass.testTopLevelValUnchekedCast()
|
||||
testClass.testArrayValueUncheckedCast()
|
||||
testClass.testArrayValueUncheckedCastInlineFunc()
|
||||
testClass.testAtomicValUncheckedCast()
|
||||
testClass.testInlineFunc()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
description = "Runtime library for the Atomicfu compiler plugin"
|
||||
|
||||
plugins {
|
||||
kotlin("js")
|
||||
}
|
||||
|
||||
group = "org.jetbrains.kotlin"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js()
|
||||
|
||||
sourceSets {
|
||||
js().compilations["main"].defaultSourceSet {
|
||||
dependencies {
|
||||
compileOnly(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
kotlin.js.compiler=both
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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 kotlinx.atomicfu
|
||||
|
||||
/**
|
||||
* Inline functions that are substituted instead of the corresponding atomic functions defined in `kotlinx.atomicfu`
|
||||
* during Js/Ir transformation.
|
||||
*
|
||||
* Example of transformation:
|
||||
* ```
|
||||
* val a = atomic(0)
|
||||
* a.compareAndSet(expect, update)
|
||||
* ```
|
||||
* is transformed to:
|
||||
* ```
|
||||
* var a = 0
|
||||
* atomicfu_compareAndSet(expect, update, { return a }, { v: Int -> a.value = v })
|
||||
* ```
|
||||
*/
|
||||
|
||||
internal inline fun <T> atomicfu_getValue(`atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_setValue(value: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Unit {
|
||||
`atomicfu$setter`(value)
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_lazySet(value: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Unit {
|
||||
`atomicfu$setter`(value)
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_compareAndSet(expect: T, update: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Boolean {
|
||||
if (`atomicfu$getter`() == expect) {
|
||||
`atomicfu$setter`(update)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_getAndSet(value: T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(value)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_getAndIncrement(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(oldValue + 1)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_getAndIncrement(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(oldValue + 1)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_incrementAndGet(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
|
||||
`atomicfu$setter`(`atomicfu$getter`() + 1)
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_incrementAndGet(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
|
||||
`atomicfu$setter`(`atomicfu$getter`() + 1)
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_getAndDecrement(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(oldValue - 1)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_getAndDecrement(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(oldValue - 1)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_decrementAndGet(`atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
|
||||
`atomicfu$setter`(`atomicfu$getter`() - 1)
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_decrementAndGet(`atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
|
||||
`atomicfu$setter`(`atomicfu$getter`() - 1)
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_getAndAdd(value: Int, `atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(oldValue + value)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_getAndAdd(value: Long, `atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
|
||||
val oldValue = `atomicfu$getter`()
|
||||
`atomicfu$setter`(oldValue + value)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_addAndGet(value: Int, `atomicfu$getter`: () -> Int, `atomicfu$setter`: (Int) -> Unit): Int {
|
||||
`atomicfu$setter`(`atomicfu$getter`() + value)
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun atomicfu_addAndGet(value: Long, `atomicfu$getter`: () -> Long, `atomicfu$setter`: (Long) -> Unit): Long {
|
||||
`atomicfu$setter`(`atomicfu$getter`() + value)
|
||||
return `atomicfu$getter`()
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_loop(action: (T) -> Unit, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): Nothing {
|
||||
while (true) {
|
||||
val cur = `atomicfu$getter`()
|
||||
action(cur)
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_update(function: (T) -> T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit) {
|
||||
while (true) {
|
||||
val cur = `atomicfu$getter`()
|
||||
val upd = function(cur)
|
||||
if (atomicfu_compareAndSet(cur, upd, `atomicfu$getter`, `atomicfu$setter`)) return
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_getAndUpdate(function: (T) -> T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
|
||||
while (true) {
|
||||
val cur = `atomicfu$getter`()
|
||||
val upd = function(cur)
|
||||
if (atomicfu_compareAndSet(cur, upd, `atomicfu$getter`, `atomicfu$setter`)) return cur
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> atomicfu_updateAndGet(function: (T) -> T, `atomicfu$getter`: () -> T, `atomicfu$setter`: (T) -> Unit): T {
|
||||
while (true) {
|
||||
val cur = `atomicfu$getter`()
|
||||
val upd = function(cur)
|
||||
if (atomicfu_compareAndSet(cur, upd, `atomicfu$getter`, `atomicfu$setter`)) return upd
|
||||
}
|
||||
}
|
||||
@@ -287,6 +287,9 @@ include ":benchmarks",
|
||||
":kotlin-serialization-unshaded",
|
||||
":wasm:wasm.ir"
|
||||
|
||||
include ":kotlinx-atomicfu-compiler-plugin",
|
||||
":kotlinx-atomicfu-runtime",
|
||||
":atomicfu"
|
||||
|
||||
include ":compiler:fir",
|
||||
":compiler:fir:cones",
|
||||
@@ -670,6 +673,10 @@ project(':kotlinx-serialization-compiler-plugin').projectDir = file("$rootDir/pl
|
||||
project(':kotlin-serialization').projectDir = file("$rootDir/libraries/tools/kotlin-serialization")
|
||||
project(':kotlin-serialization-unshaded').projectDir = file("$rootDir/libraries/tools/kotlin-serialization-unshaded")
|
||||
|
||||
project(':kotlinx-atomicfu-compiler-plugin').projectDir = file("$rootDir/plugins/atomicfu/atomicfu-compiler")
|
||||
project(':kotlinx-atomicfu-runtime').projectDir = file("$rootDir/plugins/atomicfu/atomicfu-runtime")
|
||||
project(':atomicfu').projectDir = file("$rootDir/libraries/tools/atomicfu")
|
||||
|
||||
project(':kotlin-scripting-ide-services-unshaded').projectDir = "$rootDir/plugins/scripting/scripting-ide-services" as File
|
||||
project(':kotlin-scripting-ide-services-test').projectDir = "$rootDir/plugins/scripting/scripting-ide-services-test" as File
|
||||
project(':kotlin-scripting-ide-services').projectDir = "$rootDir/plugins/scripting/scripting-ide-services-embeddable" as File
|
||||
|
||||
Reference in New Issue
Block a user