Skip to content

Commit 4be5899

Browse files
authored
Add the Wasm/WASI target support (#4064)
1 parent 61dd23c commit 4be5899

File tree

30 files changed

+436
-91
lines changed

30 files changed

+436
-91
lines changed

buildSrc/src/main/kotlin/AuxBuildConfiguration.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ object AuxBuildConfiguration {
1919
}
2020

2121
CacheRedirector.configureJsPackageManagers(rootProject)
22-
CacheRedirector.configureWasmNodeRepositories(rootProject)
2322

2423
// Sigh, there is no BuildScanExtension in classpath when there is no --scan
2524
rootProject.extensions.findByName("buildScan")?.withGroovyBuilder {

buildSrc/src/main/kotlin/CacheRedirector.kt

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -138,23 +138,6 @@ object CacheRedirector {
138138
project.configureYarnAndNodeRedirects()
139139
}
140140

141-
/**
142-
* Temporary repositories to depend on until GC milestone 4 in KGP
143-
* and stable Node release. Safe to remove when its removal does not break WASM tests.
144-
*/
145-
@JvmStatic
146-
fun configureWasmNodeRepositories(project: Project) {
147-
val extension = project.extensions.findByType<NodeJsRootExtension>()
148-
if (extension != null) {
149-
extension.nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2"
150-
extension.nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary"
151-
}
152-
153-
project.tasks.withType<KotlinNpmInstallTask>().configureEach {
154-
args.add("--ignore-engines")
155-
}
156-
}
157-
158141
@JvmStatic
159142
fun maybeRedirect(url: String): String {
160143
if (!cacheRedirectorEnabled) return url

buildSrc/src/main/kotlin/kotlin-multiplatform-conventions.gradle.kts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ kotlin {
6363
api("org.jetbrains.kotlinx:atomicfu-wasm-js:${version("atomicfu")}")
6464
}
6565
}
66+
@OptIn(org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl::class)
67+
wasmWasi {
68+
nodejs()
69+
compilations["main"]?.dependencies {
70+
api("org.jetbrains.kotlinx:atomicfu-wasm-wasi:${version("atomicfu")}")
71+
}
72+
compilations.configureEach {
73+
compilerOptions.configure {
74+
optIn.add("kotlin.wasm.internal.InternalWasmApi")
75+
}
76+
}
77+
}
6678
applyDefaultHierarchyTemplate()
6779
sourceSets {
6880
commonTest {
@@ -101,15 +113,26 @@ kotlin {
101113
api("org.jetbrains.kotlin:kotlin-test-wasm-js:${version("kotlin")}")
102114
}
103115
}
104-
groupSourceSets("jsAndWasmShared", listOf("js", "wasmJs"), listOf("common"))
116+
val wasmWasiMain by getting {
117+
}
118+
val wasmWasiTest by getting {
119+
dependencies {
120+
api("org.jetbrains.kotlin:kotlin-test-wasm-wasi:${version("kotlin")}")
121+
}
122+
}
123+
groupSourceSets("jsAndWasmJsShared", listOf("js", "wasmJs"), emptyList())
124+
groupSourceSets("jsAndWasmShared", listOf("jsAndWasmJsShared", "wasmWasi"), listOf("common"))
105125
}
106126
}
107127

108-
// Disable intermediate sourceSet compilation because we do not need js-wasmJs artifact
128+
// Disable intermediate sourceSet compilation because we do not need js-wasm common artifact
109129
tasks.configureEach {
110130
if (name == "compileJsAndWasmSharedMainKotlinMetadata") {
111131
enabled = false
112132
}
133+
if (name == "compileJsAndWasmJsSharedMainKotlinMetadata") {
134+
enabled = false
135+
}
113136
}
114137

115138
tasks.named("jvmTest", Test::class) {

integration-testing/smokeTest/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ kotlin {
4848
implementation kotlin('test-wasm-js')
4949
}
5050
}
51+
wasmWasiTest {
52+
dependencies {
53+
implementation kotlin('test-wasm-wasi')
54+
}
55+
}
5156
jvmTest {
5257
dependencies {
5358
implementation kotlin('test')

kotlinx-coroutines-core/api/kotlinx-coroutines-core.klib.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Klib ABI Dump
2-
// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
2+
// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
33
// Alias: native => [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
44
// Rendering settings:
55
// - Signature version: 2

kotlinx-coroutines-core/build.gradle.kts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ apply(plugin = "pub-conventions")
1818
Configure source sets structure for kotlinx-coroutines-core:
1919
2020
TARGETS SOURCE SETS
21-
------- ----------------------------------------------
22-
wasmJs \----------> jsAndWasmShared --------------------+
23-
js / |
24-
V
21+
------------------------------------------------------------
22+
wasmJs \------> jsAndWasmJsShared ----+
23+
js / |
24+
V
25+
wasmWasi --------------------> jsAndWasmShared ----------+
26+
|
27+
V
2528
jvmCore\ --------> jvm ---------> concurrent -------> common
2629
jdk8 / ^
2730
|

kotlinx-coroutines-core/common/test/flow/VirtualTime.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,21 @@ internal class VirtualTimeDispatcher(enclosingScope: CoroutineScope) : Coroutine
2222
val delayNanos = ThreadLocalEventLoop.currentOrNull()?.processNextEvent()
2323
?: error("Event loop is missing, virtual time source works only as part of event loop")
2424
if (delayNanos <= 0) continue
25-
if (delayNanos > 0 && delayNanos != Long.MAX_VALUE) error("Unexpected external delay: $delayNanos")
25+
if (delayNanos > 0 && delayNanos != Long.MAX_VALUE) {
26+
if (usesSharedEventLoop) {
27+
val targetTime = currentTime + delayNanos
28+
while (currentTime < targetTime) {
29+
val nextTask = heap.minByOrNull { it.deadline } ?: break
30+
if (nextTask.deadline > targetTime) break
31+
heap.remove(nextTask)
32+
currentTime = nextTask.deadline
33+
nextTask.run()
34+
}
35+
currentTime = maxOf(currentTime, targetTime)
36+
} else {
37+
error("Unexpected external delay: $delayNanos")
38+
}
39+
}
2640
val nextTask = heap.minByOrNull { it.deadline } ?: return@launch
2741
heap.remove(nextTask)
2842
currentTime = nextTask.deadline
Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package kotlinx.coroutines
22

33
import kotlinx.browser.*
4-
import kotlinx.coroutines.internal.*
5-
import kotlin.coroutines.*
64

75
private external val navigator: dynamic
86
private const val UNDEFINED = "undefined"
@@ -28,30 +26,3 @@ private fun isJsdom() = jsTypeOf(navigator) != UNDEFINED &&
2826
jsTypeOf(navigator.userAgent) != UNDEFINED &&
2927
jsTypeOf(navigator.userAgent.match) != UNDEFINED &&
3028
navigator.userAgent.match("\\bjsdom\\b")
31-
32-
@PublishedApi // Used from kotlinx-coroutines-test via suppress, not part of ABI
33-
internal actual val DefaultDelay: Delay
34-
get() = Dispatchers.Default as Delay
35-
36-
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
37-
val combined = coroutineContext + context
38-
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
39-
combined + Dispatchers.Default else combined
40-
}
41-
42-
public actual fun CoroutineContext.newCoroutineContext(addedContext: CoroutineContext): CoroutineContext {
43-
return this + addedContext
44-
}
45-
46-
// No debugging facilities on JS
47-
internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block()
48-
internal actual inline fun <T> withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T = block()
49-
internal actual fun Continuation<*>.toDebugString(): String = toString()
50-
internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on JS
51-
52-
internal actual class UndispatchedCoroutine<in T> actual constructor(
53-
context: CoroutineContext,
54-
uCont: Continuation<T>
55-
) : ScopeCoroutine<T>(context, uCont) {
56-
override fun afterResume(state: Any?) = uCont.resumeWith(recoverResult(state, uCont))
57-
}

0 commit comments

Comments
 (0)