diff --git a/README.md b/README.md index 043eff90..63320a1f 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,11 @@ Starting from version `0.22.0` of the library your project is required to use: ## Features +* Complete multiplatform support: JVM, Native, JS and Wasm (since Kotlin 1.9.20). * Code it like a boxed value `atomic(0)`, but run it in production efficiently: - * as `java.util.concurrent.atomic.AtomicXxxFieldUpdater` on Kotlin/JVM - * as a plain unboxed value on Kotlin/JS -* Multiplatform: write common Kotlin code with atomics that compiles for Kotlin JVM, JS, and Native backends: - * Compile-only dependency for JVM and JS (no runtime dependencies) - * Compile and runtime dependency for Kotlin/Native + * For **JVM**: an atomic value is represented as a plain value atomically updated with `java.util.concurrent.atomic.AtomicXxxFieldUpdater` from the Java standard library. + * For **JS**: an atomic value is represented as a plain value. + * For **Native** and **Wasm**: an atomic value is not transformed, it remains boxed, and `kotlinx-atomicfu` library is used as a runtime dependency. * Use Kotlin-specific extensions (e.g. inline `loop`, `update`, `updateAndGet` functions). * Use atomic arrays, user-defined extensions on atomics and locks (see [more features](#more-features)). * [Tracing operations](#tracing-operations) for debugging. diff --git a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt index a9c7c25e..74dffd42 100644 --- a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt +++ b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt @@ -171,12 +171,15 @@ private fun Project.needsJvmIrTransformation(target: KotlinTarget): Boolean = rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION) && (target.platformType == KotlinPlatformType.jvm || target.platformType == KotlinPlatformType.androidJvm) -private fun KotlinTarget.isJsIrTarget() = (this is KotlinJsTarget && this.irTarget != null) || this is KotlinJsIrTarget +private fun KotlinTarget.isJsIrTarget() = + (this is KotlinJsTarget && this.irTarget != null) || + (this is KotlinJsIrTarget && this.platformType != KotlinPlatformType.wasm) private fun Project.isTransformationDisabled(target: KotlinTarget): Boolean { val platformType = target.platformType return !config.transformJvm && (platformType == KotlinPlatformType.jvm || platformType == KotlinPlatformType.androidJvm) || - !config.transformJs && platformType == KotlinPlatformType.js + !config.transformJs && platformType == KotlinPlatformType.js || + platformType == KotlinPlatformType.wasm } // Adds kotlinx-atomicfu-runtime as an implementation dependency to the JS IR target: @@ -286,8 +289,11 @@ private fun Project.configureJsTransformation() = private fun Project.configureMultiplatformTransformation() = withKotlinTargets { target -> - if (target.platformType == KotlinPlatformType.common || target.platformType == KotlinPlatformType.native) { - return@withKotlinTargets // skip the common & native targets -- no transformation for them + if (target.platformType == KotlinPlatformType.common || + target.platformType == KotlinPlatformType.native || + target.platformType == KotlinPlatformType.wasm + ) { + return@withKotlinTargets // skip creation of transformation task for common, native and wasm targets } configureTransformationForTarget(target) } diff --git a/atomicfu/build.gradle b/atomicfu/build.gradle index 20f01ef3..d20bb341 100644 --- a/atomicfu/build.gradle +++ b/atomicfu/build.gradle @@ -3,7 +3,6 @@ */ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile -import static KotlinVersion.* apply plugin: 'kotlin-multiplatform' apply from: rootProject.file("gradle/targets.gradle") @@ -20,10 +19,6 @@ ext { } } -// TODO: this block should be removed when Kotlin version is updated to 1.9.20 -// Right now it is used for conditional removal of native targets that will be removed in 1.9.20 (iosArm32, watchosX86) -// and is necessary for testing of Kotlin dev builds. -def enableDeprecatedNativeTargets = !isKotlinVersionAtLeast(ext.kotlin_version, 1, 9, 20) kotlin { targets { @@ -43,6 +38,15 @@ kotlin { // JVM -- always jvm() + // Wasm -- always + wasmJs { + nodejs() + } + + wasmWasi { + nodejs() + } + sourceSets { commonMain { dependencies { @@ -55,7 +59,13 @@ kotlin { implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' } } + + jsAndWasmSharedMain { + dependsOn(sourceSets.commonMain) + } + jsMain { + dependsOn(sourceSets.jsAndWasmSharedMain) dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' } @@ -65,6 +75,32 @@ kotlin { implementation 'org.jetbrains.kotlin:kotlin-test-js' } } + + wasmJsMain { + dependsOn(sourceSets.jsAndWasmSharedMain) + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-wasm-js' + } + } + + wasmJsTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-wasm-js' + } + } + + wasmWasiMain { + dependsOn(sourceSets.jsAndWasmSharedMain) + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-wasm-wasi' + } + } + wasmWasiTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-wasm-wasi' + } + } + jvmMain { dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib' @@ -116,12 +152,6 @@ if (rootProject.ext.native_targets_enabled) { addTarget(presets.androidNativeX64) addTarget(presets.mingwX64) addTarget(presets.watchosDeviceArm64) - - // These targets will be removed in 1.9.20, remove them conditionally for the train builds - if (enableDeprecatedNativeTargets) { - addTarget(presets.iosArm32) - addTarget(presets.watchosX86) - } } } @@ -331,4 +361,4 @@ tasks.clean { setDelete(layout.buildDirectory.asFileTree.matching { exclude("tmp/.cache/expanded/expanded.lock") }) -} \ No newline at end of file +} diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt index 39950e60..2c97ddc2 100644 --- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt +++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt @@ -6,7 +6,6 @@ package kotlinx.atomicfu -import kotlin.js.JsName import kotlin.internal.InlineOnly import kotlinx.atomicfu.TraceBase.None import kotlin.reflect.KProperty @@ -110,7 +109,7 @@ public expect fun atomic(initial: Boolean): AtomicBoolean /** * Creates array of AtomicRef of specified size, where each element is initialised with null value */ -@JsName(ATOMIC_ARRAY_OF_NULLS) +@OptionalJsName(ATOMIC_ARRAY_OF_NULLS) public fun atomicArrayOfNulls(size: Int): AtomicArray = AtomicArray(size) // ==================================== AtomicRef ==================================== @@ -508,15 +507,15 @@ public inline fun AtomicLong.updateAndGet(function: (Long) -> Long): Long { /** * Creates a new array of AtomicInt values of the specified size, where each element is initialised with 0 */ -@JsName(ATOMIC_INT_ARRAY) +@OptionalJsName(ATOMIC_INT_ARRAY) public class AtomicIntArray(size: Int) { private val array = Array(size) { atomic(0) } - @JsName(ARRAY_SIZE) + @OptionalJsName(ARRAY_SIZE) public val size: Int get() = array.size - @JsName(ARRAY_ELEMENT_GET) + @OptionalJsName(ARRAY_ELEMENT_GET) public operator fun get(index: Int): AtomicInt = array[index] } @@ -525,15 +524,15 @@ public class AtomicIntArray(size: Int) { /** * Creates a new array of AtomicLong values of the specified size, where each element is initialised with 0L */ -@JsName(ATOMIC_LONG_ARRAY) +@OptionalJsName(ATOMIC_LONG_ARRAY) public class AtomicLongArray(size: Int) { private val array = Array(size) { atomic(0L) } - @JsName(ARRAY_SIZE) + @OptionalJsName(ARRAY_SIZE) public val size: Int get() = array.size - @JsName(ARRAY_ELEMENT_GET) + @OptionalJsName(ARRAY_ELEMENT_GET) public operator fun get(index: Int): AtomicLong = array[index] } @@ -542,29 +541,29 @@ public class AtomicLongArray(size: Int) { /** * Creates a new array of AtomicBoolean values of the specified size, where each element is initialised with false */ -@JsName(ATOMIC_BOOLEAN_ARRAY) +@OptionalJsName(ATOMIC_BOOLEAN_ARRAY) public class AtomicBooleanArray(size: Int) { private val array = Array(size) { atomic(false) } - @JsName(ARRAY_SIZE) + @OptionalJsName(ARRAY_SIZE) public val size: Int get() = array.size - @JsName(ARRAY_ELEMENT_GET) + @OptionalJsName(ARRAY_ELEMENT_GET) public operator fun get(index: Int): AtomicBoolean = array[index] } // ==================================== AtomicArray ==================================== -@JsName(ATOMIC_REF_ARRAY) +@OptionalJsName(ATOMIC_REF_ARRAY) public class AtomicArray internal constructor(size: Int) { private val array = Array(size) { atomic(null) } - @JsName(ARRAY_SIZE) + @OptionalJsName(ARRAY_SIZE) public val size: Int get() = array.size - @JsName(ARRAY_ELEMENT_GET) + @OptionalJsName(ARRAY_ELEMENT_GET) public operator fun get(index: Int): AtomicRef = array[index] } diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt index 99b42981..8c37d016 100644 --- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt +++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt @@ -1,7 +1,7 @@ package kotlinx.atomicfu /** - * All atomicfu declarations are annotated with [@JsName][kotlin.js.JsName] to have specific names in JS output. + * All atomicfu declarations are annotated with [@OptionalJsName][kotlin.js.JsName] to have specific names in JS output. * JS output transformer relies on these mangled names to erase all atomicfu references. */ diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/OptionalJsName.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/OptionalJsName.kt new file mode 100644 index 00000000..e4e1b44b --- /dev/null +++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/OptionalJsName.kt @@ -0,0 +1,17 @@ +package kotlinx.atomicfu + +/** + * This annotation actualized with JsName in JS platform and not actualized in others. + */ +@OptIn(ExperimentalMultiplatform::class) +@OptionalExpectation +@Retention(AnnotationRetention.BINARY) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY, + AnnotationTarget.CONSTRUCTOR, + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER +) +expect annotation class OptionalJsName(val name: String) diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt index 4cc1e401..4d3a9353 100644 --- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt +++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt @@ -6,7 +6,6 @@ package kotlinx.atomicfu -import kotlin.js.JsName import kotlin.internal.InlineOnly /** @@ -66,30 +65,30 @@ public expect val traceFormatDefault: TraceFormat /** * Base class for implementations of `Trace`. */ -@JsName(TRACE_BASE_CONSTRUCTOR) +@OptionalJsName(TRACE_BASE_CONSTRUCTOR) public open class TraceBase internal constructor() { /** * Accepts the logging [event] and appends it to the trace. */ - @JsName(TRACE_APPEND_1) + @OptionalJsName(TRACE_APPEND_1) public open fun append(event: Any) {} /** * Accepts the logging events [event1], [event2] and appends them to the trace. */ - @JsName(TRACE_APPEND_2) + @OptionalJsName(TRACE_APPEND_2) public open fun append(event1: Any, event2: Any) {} /** * Accepts the logging events [event1], [event2], [event3] and appends them to the trace. */ - @JsName(TRACE_APPEND_3) + @OptionalJsName(TRACE_APPEND_3) public open fun append(event1: Any, event2: Any, event3: Any) {} /** * Accepts the logging events [event1], [event2], [event3], [event4] and appends them to the trace. */ - @JsName(TRACE_APPEND_4) + @OptionalJsName(TRACE_APPEND_4) public open fun append(event1: Any, event2: Any, event3: Any, event4: Any) {} /** diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt index dd8d0de2..17dc1be3 100644 --- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt +++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt @@ -12,12 +12,12 @@ import kotlin.js.JsName /** * Trace string formatter. */ -@JsName(TRACE_FORMAT_CLASS) +@OptionalJsName(TRACE_FORMAT_CLASS) public open class TraceFormat { /** * Formats trace at the given [index] with the given [event] of Any type. */ - @JsName(TRACE_FORMAT_FORMAT_FUNCTION) + @OptionalJsName(TRACE_FORMAT_FORMAT_FUNCTION) public open fun format(index: Int, event: Any): String = "$index: $event" } diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/AtomicFU.kt similarity index 82% rename from atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt rename to atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/AtomicFU.kt index 3879b8b0..403a6ef1 100644 --- a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt +++ b/atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/AtomicFU.kt @@ -16,34 +16,34 @@ import kotlinx.atomicfu.TraceBase.None import kotlin.internal.InlineOnly import kotlin.reflect.KProperty -@JsName(ATOMIC_REF_FACTORY) +@OptionalJsName(ATOMIC_REF_FACTORY) public actual fun atomic(initial: T, trace: TraceBase): AtomicRef = AtomicRef(initial) -@JsName(ATOMIC_REF_FACTORY_BINARY_COMPATIBILITY) +@OptionalJsName(ATOMIC_REF_FACTORY_BINARY_COMPATIBILITY) public actual fun atomic(initial: T): AtomicRef = atomic(initial, None) -@JsName(ATOMIC_INT_FACTORY) +@OptionalJsName(ATOMIC_INT_FACTORY) public actual fun atomic(initial: Int, trace: TraceBase): AtomicInt = AtomicInt(initial) -@JsName(ATOMIC_INT_FACTORY_BINARY_COMPATIBILITY) +@OptionalJsName(ATOMIC_INT_FACTORY_BINARY_COMPATIBILITY) public actual fun atomic(initial: Int): AtomicInt = atomic(initial, None) -@JsName(ATOMIC_LONG_FACTORY) +@OptionalJsName(ATOMIC_LONG_FACTORY) public actual fun atomic(initial: Long, trace: TraceBase): AtomicLong = AtomicLong(initial) -@JsName(ATOMIC_LONG_FACTORY_BINARY_COMPATIBILITY) +@OptionalJsName(ATOMIC_LONG_FACTORY_BINARY_COMPATIBILITY) public actual fun atomic(initial: Long): AtomicLong = atomic(initial, None) -@JsName(ATOMIC_BOOLEAN_FACTORY) +@OptionalJsName(ATOMIC_BOOLEAN_FACTORY) public actual fun atomic(initial: Boolean, trace: TraceBase): AtomicBoolean = AtomicBoolean(initial) -@JsName(ATOMIC_BOOLEAN_FACTORY_BINARY_COMPATIBILITY) +@OptionalJsName(ATOMIC_BOOLEAN_FACTORY_BINARY_COMPATIBILITY) public actual fun atomic(initial: Boolean): AtomicBoolean = atomic(initial, None) // ==================================== AtomicRef ==================================== public actual class AtomicRef internal constructor(value: T) { - @JsName(ATOMIC_VALUE) + @OptionalJsName(ATOMIC_VALUE) public actual var value: T = value @InlineOnly @@ -54,14 +54,14 @@ public actual class AtomicRef internal constructor(value: T) { public actual inline fun lazySet(value: T) { this.value = value } - @JsName(COMPARE_AND_SET) + @OptionalJsName(COMPARE_AND_SET) public actual fun compareAndSet(expect: T, update: T): Boolean { if (value !== expect) return false value = update return true } - @JsName(GET_AND_SET) + @OptionalJsName(GET_AND_SET) public actual fun getAndSet(value: T): T { val oldValue = this.value this.value = value @@ -74,7 +74,7 @@ public actual class AtomicRef internal constructor(value: T) { // ==================================== AtomicBoolean ==================================== public actual class AtomicBoolean internal constructor(value: Boolean) { - @JsName(ATOMIC_VALUE) + @OptionalJsName(ATOMIC_VALUE) public actual var value: Boolean = value @InlineOnly @@ -87,14 +87,14 @@ public actual class AtomicBoolean internal constructor(value: Boolean) { this.value = value } - @JsName(COMPARE_AND_SET) + @OptionalJsName(COMPARE_AND_SET) public actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean { if (value != expect) return false value = update return true } - @JsName(GET_AND_SET) + @OptionalJsName(GET_AND_SET) public actual fun getAndSet(value: Boolean): Boolean { val oldValue = this.value this.value = value @@ -107,7 +107,7 @@ public actual class AtomicBoolean internal constructor(value: Boolean) { // ==================================== AtomicInt ==================================== public actual class AtomicInt internal constructor(value: Int) { - @JsName(ATOMIC_VALUE) + @OptionalJsName(ATOMIC_VALUE) public actual var value: Int = value @InlineOnly @@ -118,43 +118,43 @@ public actual class AtomicInt internal constructor(value: Int) { public actual inline fun lazySet(value: Int) { this.value = value } - @JsName(COMPARE_AND_SET) + @OptionalJsName(COMPARE_AND_SET) public actual fun compareAndSet(expect: Int, update: Int): Boolean { if (value != expect) return false value = update return true } - @JsName(GET_AND_SET) + @OptionalJsName(GET_AND_SET) public actual fun getAndSet(value: Int): Int { val oldValue = this.value this.value = value return oldValue } - @JsName(GET_AND_INCREMENT) + @OptionalJsName(GET_AND_INCREMENT) public actual fun getAndIncrement(): Int = value++ - @JsName(GET_AND_DECREMENT) + @OptionalJsName(GET_AND_DECREMENT) public actual fun getAndDecrement(): Int = value-- - @JsName(GET_AND_ADD) + @OptionalJsName(GET_AND_ADD) public actual fun getAndAdd(delta: Int): Int { val oldValue = value value += delta return oldValue } - @JsName(ADD_AND_GET) + @OptionalJsName(ADD_AND_GET) public actual fun addAndGet(delta: Int): Int { value += delta return value } - @JsName(INCREMENT_AND_GET) + @OptionalJsName(INCREMENT_AND_GET) public actual fun incrementAndGet(): Int = ++value - @JsName(DECREMENT_AND_GET) + @OptionalJsName(DECREMENT_AND_GET) public actual fun decrementAndGet(): Int = --value public actual inline operator fun plusAssign(delta: Int) { getAndAdd(delta) } @@ -167,7 +167,7 @@ public actual class AtomicInt internal constructor(value: Int) { // ==================================== AtomicLong ==================================== public actual class AtomicLong internal constructor(value: Long) { - @JsName(ATOMIC_VALUE) + @OptionalJsName(ATOMIC_VALUE) public actual var value: Long = value @InlineOnly @@ -178,43 +178,43 @@ public actual class AtomicLong internal constructor(value: Long) { public actual inline fun lazySet(value: Long) { this.value = value } - @JsName(COMPARE_AND_SET) + @OptionalJsName(COMPARE_AND_SET) public actual fun compareAndSet(expect: Long, update: Long): Boolean { if (value != expect) return false value = update return true } - @JsName(GET_AND_SET) + @OptionalJsName(GET_AND_SET) public actual fun getAndSet(value: Long): Long { val oldValue = this.value this.value = value return oldValue } - @JsName(GET_AND_INCREMENT_LONG) + @OptionalJsName(GET_AND_INCREMENT_LONG) public actual fun getAndIncrement(): Long = value++ - @JsName(GET_AND_DECREMENT_LONG) + @OptionalJsName(GET_AND_DECREMENT_LONG) public actual fun getAndDecrement(): Long = value-- - @JsName(GET_AND_ADD_LONG) + @OptionalJsName(GET_AND_ADD_LONG) public actual fun getAndAdd(delta: Long): Long { val oldValue = value value += delta return oldValue } - @JsName(ADD_AND_GET_LONG) + @OptionalJsName(ADD_AND_GET_LONG) public actual fun addAndGet(delta: Long): Long { value += delta return value } - @JsName(INCREMENT_AND_GET_LONG) + @OptionalJsName(INCREMENT_AND_GET_LONG) public actual fun incrementAndGet(): Long = ++value - @JsName(DECREMENT_AND_GET_LONG) + @OptionalJsName(DECREMENT_AND_GET_LONG) public actual fun decrementAndGet(): Long = --value public actual inline operator fun plusAssign(delta: Long) { getAndAdd(delta) } diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/Trace.kt b/atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/Trace.kt similarity index 84% rename from atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/Trace.kt rename to atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/Trace.kt index 03a43386..b232b2cd 100644 --- a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/Trace.kt +++ b/atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/Trace.kt @@ -5,10 +5,10 @@ package kotlinx.atomicfu @Suppress("FunctionName") -@JsName(TRACE_FACTORY_FUNCTION) +@OptionalJsName(TRACE_FACTORY_FUNCTION) public actual fun Trace(size: Int, format: TraceFormat): TraceBase = TraceBase.None -@JsName(TRACE_NAMED) +@OptionalJsName(TRACE_NAMED) public actual fun TraceBase.named(name: String): TraceBase = TraceBase.None public actual val traceFormatDefault: TraceFormat = TraceFormat() \ No newline at end of file diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt b/atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt similarity index 76% rename from atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt rename to atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt index 7d8c4502..3a025d58 100644 --- a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt +++ b/atomicfu/src/jsAndWasmSharedMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt @@ -2,9 +2,10 @@ package kotlinx.atomicfu.locks import kotlinx.atomicfu.REENTRANT_LOCK +@Suppress("ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_MEMBERS_AS_NON_FINAL_EXPECT_CLASSIFIER_WARNING") public actual typealias SynchronizedObject = Any -@JsName(REENTRANT_LOCK) +@kotlinx.atomicfu.OptionalJsName(REENTRANT_LOCK) public val Lock = ReentrantLock() @Suppress("NOTHING_TO_INLINE") @@ -19,4 +20,4 @@ public actual class ReentrantLock { public actual inline fun ReentrantLock.withLock(block: () -> T) = block() -public actual inline fun synchronized(lock: SynchronizedObject, block: () -> T): T = block() \ No newline at end of file +public actual inline fun synchronized(lock: SynchronizedObject, block: () -> T): T = block() diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/OptionalJsName.kt b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/OptionalJsName.kt new file mode 100644 index 00000000..67cc2ef7 --- /dev/null +++ b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/OptionalJsName.kt @@ -0,0 +1,3 @@ +package kotlinx.atomicfu + +actual typealias OptionalJsName = JsName diff --git a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt index 1e07e9ee..968bbdf4 100644 --- a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt +++ b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt @@ -11,16 +11,10 @@ import kotlin.concurrent.AtomicReference import kotlin.native.SharedImmutable import kotlin.native.concurrent.* -// fixme replace the suppress with AllowDifferentMembersInActual once stdlib is updated to 1.9.20 https://github.com/Kotlin/kotlinx-atomicfu/issues/333 -@Suppress("ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_MEMBERS_AS_NON_FINAL_EXPECT_CLASSIFIER") public actual open class SynchronizedObject { - // fixme replace the suppress with AllowDifferentMembersInActual once stdlib is updated to 1.9.20 https://github.com/Kotlin/kotlinx-atomicfu/issues/333 - @Suppress("NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION") protected val lock = AtomicReference(LockState(UNLOCKED, 0, 0)) - // fixme replace the suppress with AllowDifferentMembersInActual once stdlib is updated to 1.9.20 https://github.com/Kotlin/kotlinx-atomicfu/issues/333 - @Suppress("NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION") public fun lock() { val currentThreadId = pthread_self()!! while (true) { @@ -72,8 +66,6 @@ public actual open class SynchronizedObject { } } - // fixme replace the suppress with AllowDifferentMembersInActual once stdlib is updated to 1.9.20 https://github.com/Kotlin/kotlinx-atomicfu/issues/333 - @Suppress("NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION") public fun tryLock(): Boolean { val currentThreadId = pthread_self()!! while (true) { @@ -94,8 +86,6 @@ public actual open class SynchronizedObject { } } - // fixme replace the suppress with AllowDifferentMembersInActual once stdlib is updated to 1.9.20 https://github.com/Kotlin/kotlinx-atomicfu/issues/333 - @Suppress("NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION") public fun unlock() { val currentThreadId = pthread_self()!! while (true) { diff --git a/build.gradle b/build.gradle index d8bba036..6a7e49bf 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,6 @@ buildscript { throw new IllegalArgumentException("'kotlin_snapshot_version' should be defined when building with a snapshot compiler") } repositories { - mavenLocal() maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } } @@ -56,7 +55,6 @@ allprojects { if (build_snapshot_train) { project.kotlin_version = project.findProperty("kotlin_snapshot_version") repositories { - mavenLocal() maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } } @@ -107,3 +105,15 @@ if (build_snapshot_train) { // main deployment task task deploy(dependsOn: getTasksByName("publish", true) + getTasksByName("publishNpm", true)) + +// Right now it is used for switching nodejs version which is supports generated wasm bytecode +extensions.findByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.class).with { + // canary nodejs that supports recent Wasm GC changes + it.nodeVersion = '21.0.0-v8-canary202309167e82ab1fa2' + it.nodeDownloadBaseUrl = 'https://nodejs.org/download/v8-canary' +} + +// We need to ignore unsupported engines (i.e. canary) for npm +tasks.withType(org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask).configureEach { + args.add("--ignore-engines") +} diff --git a/buildSrc/src/main/kotlin/KotlinVersion.kt b/buildSrc/src/main/kotlin/KotlinVersion.kt deleted file mode 100644 index 5ac051ec..00000000 --- a/buildSrc/src/main/kotlin/KotlinVersion.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmName("KotlinVersion") - -fun isKotlinVersionAtLeast(kotlinVersion: String, atLeastMajor: Int, atLeastMinor: Int, atLeastPatch: Int): Boolean { - val (major, minor) = kotlinVersion - .split('.') - .take(2) - .map { it.toInt() } - val patch = kotlinVersion.substringAfterLast('.').substringBefore('-').toInt() - return when { - major > atLeastMajor -> true - major < atLeastMajor -> false - else -> (minor == atLeastMinor && patch >= atLeastPatch) || minor > atLeastMinor - } -} diff --git a/integration-testing/examples/mpp-sample/build.gradle.kts b/integration-testing/examples/mpp-sample/build.gradle.kts index 7f3875b0..f7bb99b8 100644 --- a/integration-testing/examples/mpp-sample/build.gradle.kts +++ b/integration-testing/examples/mpp-sample/build.gradle.kts @@ -31,7 +31,12 @@ repositories { kotlin { jvm() + js() + + wasmJs {} + wasmWasi {} + macosArm64() macosX64() linuxArm64() diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MppProjectTest.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MppProjectTest.kt index b99f6acc..3dc4543c 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MppProjectTest.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MppProjectTest.kt @@ -6,7 +6,7 @@ package test import kotlinx.atomicfu.gradle.plugin.test.framework.checker.* import kotlinx.atomicfu.gradle.plugin.test.framework.runner.* -import kotlin.test.Test +import kotlin.test.* class MppProjectTest { private val mppSample: GradleBuild = createGradleBuildFromSources("mpp-sample") @@ -31,14 +31,21 @@ class MppProjectTest { @Test fun testMppWithEnabledJsIrTransformation() { mppSample.enableJsIrTransformation = true - assert(mppSample.cleanAndBuild().isSuccessful) + assertTrue(mppSample.cleanAndBuild().isSuccessful) mppSample.checkConsumableDependencies() } @Test fun testMppWithDisabledJsIrTransformation() { mppSample.enableJsIrTransformation = false - assert(mppSample.cleanAndBuild().isSuccessful) + assertTrue(mppSample.cleanAndBuild().isSuccessful) mppSample.checkConsumableDependencies() } + + @Test + fun testMppWasmBuild() { + assertTrue(mppSample.cleanAndBuild().isSuccessful) + mppSample.checkMppWasmJsImplementationDependencies() + mppSample.checkMppWasmWasiImplementationDependencies() + } } diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/DependenciesChecker.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/DependenciesChecker.kt index f7edac71..5cffdc02 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/DependenciesChecker.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/DependenciesChecker.kt @@ -50,6 +50,15 @@ internal fun GradleBuild.checkMppJvmCompileOnlyDependencies() { checkAtomicfuDependencyIsAbsent(listOf("jvmRuntimeClasspath", "jvmApiElements", "jvmRuntimeElements"), commonAtomicfuDependency) } +// Checks wasmJs target of an MPP project +internal fun GradleBuild.checkMppWasmJsImplementationDependencies() { + checkAtomicfuDependencyIsPresent(listOf("wasmJsCompileClasspath", "wasmJsRuntimeClasspath"), commonAtomicfuDependency) +} + +internal fun GradleBuild.checkMppWasmWasiImplementationDependencies() { + checkAtomicfuDependencyIsPresent(listOf("wasmWasiCompileClasspath", "wasmWasiRuntimeClasspath"), commonAtomicfuDependency) +} + // Some dependencies may be not resolvable but consumable and will not be present in the output of :dependencies task, // in this case we should check .pom or .module file of the published project. // This method checks if the .module file in the sample project publication contains org.jetbrains.kotlinx:atomicfu dependency included.