[Wasm] Wasi stdlib implementation
KT-56608
This commit is contained in:
committed by
Zalim Bashorov
parent
090f393f97
commit
8cc0660693
@@ -1,4 +1,6 @@
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
|
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetAttribute
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
|
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink
|
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink
|
||||||
|
|
||||||
@@ -81,14 +83,20 @@ kotlin {
|
|||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@OptIn(org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl::class)
|
@OptIn(org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl::class)
|
||||||
wasm("wasm") {
|
wasm("wasm") {
|
||||||
d8()
|
nodejs()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KotlinSourceSet.addWasmMainDirs() {
|
||||||
|
kotlin.srcDirs("builtins", "internal", "runtime", "src", "stubs")
|
||||||
|
kotlin.srcDirs("$rootDir/libraries/stdlib/native-wasm/src")
|
||||||
|
kotlin.srcDirs(files(builtInsSources.map { it.destinationDir }))
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
named("wasmMain") {
|
named("wasmMain") {
|
||||||
kotlin.srcDirs("builtins", "internal", "runtime", "src", "stubs")
|
addWasmMainDirs()
|
||||||
kotlin.srcDirs("$rootDir/libraries/stdlib/native-wasm/src")
|
// kotlin.srcDirs("js/builtins/kotlin", "js/internal", "js/src/kotlin", "js/src/kotlinx", "js/src/org.w3c")
|
||||||
kotlin.srcDirs(files(builtInsSources.map { it.destinationDir }))
|
kotlin.srcDirs("wasi/builtins/kotlin", "wasi/internal", "wasi/src/kotlin", "wasi/src/kotlinx")
|
||||||
}
|
}
|
||||||
|
|
||||||
named("commonMain") {
|
named("commonMain") {
|
||||||
@@ -153,8 +161,17 @@ val compileTestDevelopmentExecutableKotlinWasm = tasks.named<KotlinJsIrLink>("co
|
|||||||
(this as KotlinCompile<*>).kotlinOptions.freeCompilerArgs += listOf("-Xwasm-enable-array-range-checks")
|
(this as KotlinCompile<*>).kotlinOptions.freeCompilerArgs += listOf("-Xwasm-enable-array-range-checks")
|
||||||
}
|
}
|
||||||
|
|
||||||
val runtimeElements by configurations.creating {}
|
val runtimeElements by configurations.creating {
|
||||||
val apiElements by configurations.creating {}
|
attributes {
|
||||||
|
attributes.attribute(KotlinWasmTargetAttribute.wasmTargetAttribute, KotlinWasmTargetAttribute.js)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val apiElements by configurations.creating {
|
||||||
|
attributes {
|
||||||
|
attributes.attribute(KotlinWasmTargetAttribute.wasmTargetAttribute, KotlinWasmTargetAttribute.js)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
publish(sbom = false) {
|
publish(sbom = false) {
|
||||||
pom.packaging = "klib"
|
pom.packaging = "klib"
|
||||||
|
|||||||
@@ -4,49 +4,3 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
package kotlin
|
package kotlin
|
||||||
|
|
||||||
import kotlin.wasm.internal.ExternalInterfaceType
|
|
||||||
import kotlin.wasm.internal.getSimpleName
|
|
||||||
import kotlin.wasm.internal.jsToKotlinStringAdapter
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base class for all errors and exceptions. Only instances of this class can be thrown or caught.
|
|
||||||
*
|
|
||||||
* @param message the detail message string.
|
|
||||||
* @param cause the cause of this throwable.
|
|
||||||
*/
|
|
||||||
public open class Throwable(open val message: String?, open val cause: kotlin.Throwable?) {
|
|
||||||
constructor(message: String?) : this(message, null)
|
|
||||||
|
|
||||||
constructor(cause: Throwable?) : this(cause?.toString(), cause)
|
|
||||||
|
|
||||||
constructor() : this(null, null)
|
|
||||||
|
|
||||||
internal val jsStack: ExternalInterfaceType = captureStackTrace()
|
|
||||||
|
|
||||||
private var _stack: String? = null
|
|
||||||
internal val stack: String
|
|
||||||
get() {
|
|
||||||
var value = _stack
|
|
||||||
if (value == null) {
|
|
||||||
value = jsToKotlinStringAdapter(jsStack).removePrefix("Error\n")
|
|
||||||
_stack = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
internal var suppressedExceptionsList: MutableList<Throwable>? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the short description of this throwable consisting of the exception class name
|
|
||||||
* followed by the exception message if it is not null.
|
|
||||||
*/
|
|
||||||
public override fun toString(): String {
|
|
||||||
val s = getSimpleName(this.typeInfo)
|
|
||||||
return if (message != null) s + ": " + message.toString() else s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun captureStackTrace(): ExternalInterfaceType =
|
|
||||||
js("new Error().stack")
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.wasm.internal
|
||||||
|
|
||||||
|
internal var isNotFirstWasmExportCall: Boolean = false
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.wasm.internal
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@ExcludedFromCodegen
|
||||||
|
/*
|
||||||
|
* Compiler generates inplace next code:
|
||||||
|
* ```
|
||||||
|
* block (result anyref) {
|
||||||
|
* local.get 0
|
||||||
|
* extern.internalize
|
||||||
|
* br_on_cast_fail 0000_0011b 0 (type any) (type $kotlin.Any)
|
||||||
|
* return
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
internal fun returnArgumentIfItIsKotlinAny(): Unit = implementedAsIntrinsic
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -9,8 +9,6 @@
|
|||||||
|
|
||||||
package kotlin.wasm.internal
|
package kotlin.wasm.internal
|
||||||
|
|
||||||
import kotlin.wasm.internal.ExternalInterfaceType
|
|
||||||
|
|
||||||
@WasmOp(WasmOp.UNREACHABLE)
|
@WasmOp(WasmOp.UNREACHABLE)
|
||||||
internal fun wasm_unreachable(): Nothing =
|
internal fun wasm_unreachable(): Nothing =
|
||||||
implementedAsIntrinsic
|
implementedAsIntrinsic
|
||||||
@@ -496,9 +494,3 @@ internal fun wasm_i64_popcnt(a: Long): Long =
|
|||||||
@WasmOp(WasmOp.I64_CTZ)
|
@WasmOp(WasmOp.I64_CTZ)
|
||||||
internal fun wasm_i64_ctz(a: Long): Long =
|
internal fun wasm_i64_ctz(a: Long): Long =
|
||||||
implementedAsIntrinsic
|
implementedAsIntrinsic
|
||||||
|
|
||||||
// Reference type operators
|
|
||||||
|
|
||||||
@WasmOp(WasmOp.REF_IS_NULL)
|
|
||||||
internal external fun wasm_externref_is_null(a: ExternalInterfaceType?): Boolean =
|
|
||||||
implementedAsIntrinsic
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin
|
||||||
|
|
||||||
|
import kotlin.wasm.internal.ExternalInterfaceType
|
||||||
|
import kotlin.wasm.internal.getSimpleName
|
||||||
|
import kotlin.wasm.internal.jsToKotlinStringAdapter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for all errors and exceptions. Only instances of this class can be thrown or caught.
|
||||||
|
*
|
||||||
|
* @param message the detail message string.
|
||||||
|
* @param cause the cause of this throwable.
|
||||||
|
*/
|
||||||
|
public open class Throwable(open val message: String?, open val cause: kotlin.Throwable?) {
|
||||||
|
constructor(message: String?) : this(message, null)
|
||||||
|
|
||||||
|
constructor(cause: Throwable?) : this(cause?.toString(), cause)
|
||||||
|
|
||||||
|
constructor() : this(null, null)
|
||||||
|
|
||||||
|
internal val jsStack: ExternalInterfaceType = captureStackTrace()
|
||||||
|
|
||||||
|
private var _stack: String? = null
|
||||||
|
internal val stack: String
|
||||||
|
get() {
|
||||||
|
var value = _stack
|
||||||
|
if (value == null) {
|
||||||
|
value = jsToKotlinStringAdapter(jsStack).removePrefix("Error\n")
|
||||||
|
_stack = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var suppressedExceptionsList: MutableList<Throwable>? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the short description of this throwable consisting of the exception class name
|
||||||
|
* followed by the exception message if it is not null.
|
||||||
|
*/
|
||||||
|
public override fun toString(): String {
|
||||||
|
val s = getSimpleName(this.typeInfo)
|
||||||
|
return if (message != null) s + ": " + message.toString() else s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun captureStackTrace(): ExternalInterfaceType =
|
||||||
|
js("new Error().stack")
|
||||||
-2
@@ -19,5 +19,3 @@ private fun throwJsError(message: String?, wasmTypeName: String?, stack: Externa
|
|||||||
internal fun throwAsJsException(t: Throwable): Nothing {
|
internal fun throwAsJsException(t: Throwable): Nothing {
|
||||||
throwJsError(t.message, getSimpleName(t.typeInfo), t.jsStack)
|
throwJsError(t.message, getSimpleName(t.typeInfo), t.jsStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal var isNotFirstWasmExportCall: Boolean = false
|
|
||||||
+1
-16
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -145,21 +145,6 @@ private fun Any.asWasmExternRef(): ExternalInterfaceType =
|
|||||||
internal fun isNullish(ref: ExternalInterfaceType?): Boolean =
|
internal fun isNullish(ref: ExternalInterfaceType?): Boolean =
|
||||||
js("ref == null")
|
js("ref == null")
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
@ExcludedFromCodegen
|
|
||||||
/*
|
|
||||||
* Compiler generates inplace next code:
|
|
||||||
* ```
|
|
||||||
* block (result anyref) {
|
|
||||||
* local.get 0
|
|
||||||
* extern.internalize
|
|
||||||
* br_on_cast_fail 0000_0011b 0 (type any) (type $kotlin.Any)
|
|
||||||
* return
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
internal fun returnArgumentIfItIsKotlinAny(): Unit = implementedAsIntrinsic
|
|
||||||
|
|
||||||
internal fun externRefToAny(ref: ExternalInterfaceType): Any? {
|
internal fun externRefToAny(ref: ExternalInterfaceType): Any? {
|
||||||
// TODO rewrite it so to get something like:
|
// TODO rewrite it so to get something like:
|
||||||
// block {
|
// block {
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:ExcludedFromCodegen
|
||||||
|
@file:Suppress("unused", "NON_ABSTRACT_FUNCTION_WITH_NO_BODY", "INLINE_CLASS_IN_EXTERNAL_DECLARATION", "UNUSED_PARAMETER")
|
||||||
|
|
||||||
|
|
||||||
|
package kotlin.wasm.internal
|
||||||
|
|
||||||
|
import kotlin.wasm.internal.ExternalInterfaceType
|
||||||
|
|
||||||
|
// Reference type operators
|
||||||
|
|
||||||
|
@WasmOp(WasmOp.REF_IS_NULL)
|
||||||
|
internal external fun wasm_externref_is_null(a: ExternalInterfaceType?): Boolean
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -38,5 +38,3 @@ public actual fun readln(): String = throw UnsupportedOperationException("readln
|
|||||||
|
|
||||||
@SinceKotlin("1.6")
|
@SinceKotlin("1.6")
|
||||||
public actual fun readlnOrNull(): String? = throw UnsupportedOperationException("readlnOrNull is not supported in Kotlin/WASM")
|
public actual fun readlnOrNull(): String? = throw UnsupportedOperationException("readlnOrNull is not supported in Kotlin/WASM")
|
||||||
|
|
||||||
internal actual interface Serializable
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.random
|
||||||
|
|
||||||
|
private fun initialSeed(): Int =
|
||||||
|
js("((Math.random() * Math.pow(2, 32)) | 0)")
|
||||||
|
|
||||||
|
internal actual fun defaultPlatformRandom(): Random =
|
||||||
|
Random(initialSeed())
|
||||||
-1
@@ -23,7 +23,6 @@ internal actual fun formatToExactDecimals(value: Double, decimals: Int): String
|
|||||||
} else {
|
} else {
|
||||||
val pow = (10.0).pow(decimals)
|
val pow = (10.0).pow(decimals)
|
||||||
round(abs(value) * pow) / pow * sign(value)
|
round(abs(value) * pow) / pow * sign(value)
|
||||||
round(abs(value) * pow) / pow * sign(value)
|
|
||||||
}
|
}
|
||||||
return if (abs(rounded) < 1e21) {
|
return if (abs(rounded) < 1e21) {
|
||||||
// toFixed switches to scientific format after 1e21
|
// toFixed switches to scientific format after 1e21
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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 kotlin.io
|
||||||
|
|
||||||
|
internal actual interface Serializable
|
||||||
@@ -5,11 +5,5 @@
|
|||||||
|
|
||||||
package kotlin.random
|
package kotlin.random
|
||||||
|
|
||||||
private fun initialSeed(): Int =
|
|
||||||
js("((Math.random() * Math.pow(2, 32)) | 0)")
|
|
||||||
|
|
||||||
internal actual fun defaultPlatformRandom(): Random =
|
|
||||||
Random(initialSeed())
|
|
||||||
|
|
||||||
internal actual fun doubleFromParts(hi26: Int, low27: Int): Double =
|
internal actual fun doubleFromParts(hi26: Int, low27: Int): Double =
|
||||||
(hi26.toLong().shl(27) + low27) / (1L shl 53).toDouble()
|
(hi26.toLong().shl(27) + low27) / (1L shl 53).toDouble()
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ package test.wasm.unsafe
|
|||||||
import kotlin.wasm.unsafe.*
|
import kotlin.wasm.unsafe.*
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
private fun jsConcatStrings(a: String, b: String): String =
|
|
||||||
js("a + b")
|
|
||||||
|
|
||||||
@OptIn(UnsafeWasmMemoryApi::class)
|
@OptIn(UnsafeWasmMemoryApi::class)
|
||||||
class MemoryAllocationTest {
|
class MemoryAllocationTest {
|
||||||
val pageSize = 65_536
|
val pageSize = 65_536
|
||||||
@@ -128,14 +125,6 @@ class MemoryAllocationTest {
|
|||||||
assertTrue(max1 < min1_1)
|
assertTrue(max1 < min1_1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testJsIntropInsideAllocations() {
|
|
||||||
withScopedMemoryAllocator { allocator ->
|
|
||||||
assertEquals(jsConcatStrings("str1", "str2"), "str1str2")
|
|
||||||
allocator.allocate(10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNestedAllocatorThrows() {
|
fun testNestedAllocatorThrows() {
|
||||||
var leakedAllocator1: MemoryAllocator? = null
|
var leakedAllocator1: MemoryAllocator? = null
|
||||||
|
|||||||
+5
@@ -1,3 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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 test.js
|
package test.js
|
||||||
|
|
||||||
import kotlin.js.*
|
import kotlin.js.*
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package test.wasm.unsafe
|
||||||
|
|
||||||
|
import kotlin.wasm.unsafe.*
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
private fun jsConcatStrings(a: String, b: String): String =
|
||||||
|
js("a + b")
|
||||||
|
|
||||||
|
@OptIn(UnsafeWasmMemoryApi::class)
|
||||||
|
class MemoryAllocationJsTest {
|
||||||
|
@Test
|
||||||
|
fun testJsIntropInsideAllocations() {
|
||||||
|
withScopedMemoryAllocator { allocator ->
|
||||||
|
assertEquals(jsConcatStrings("str1", "str2"), "str1str2")
|
||||||
|
allocator.allocate(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin
|
||||||
|
|
||||||
|
import kotlin.wasm.internal.getSimpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for all errors and exceptions. Only instances of this class can be thrown or caught.
|
||||||
|
*
|
||||||
|
* @param message the detail message string.
|
||||||
|
* @param cause the cause of this throwable.
|
||||||
|
*/
|
||||||
|
public open class Throwable(open val message: String?, open val cause: kotlin.Throwable?) {
|
||||||
|
constructor(message: String?) : this(message, null)
|
||||||
|
|
||||||
|
constructor(cause: Throwable?) : this(cause?.toString(), cause)
|
||||||
|
|
||||||
|
constructor() : this(null, null)
|
||||||
|
|
||||||
|
//TODO: Investigate possibility to make WASI stack discoverable (KT-60965)
|
||||||
|
internal val stack: String get() = ""
|
||||||
|
|
||||||
|
internal var suppressedExceptionsList: MutableList<Throwable>? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the short description of this throwable consisting of the exception class name
|
||||||
|
* followed by the exception message if it is not null.
|
||||||
|
*/
|
||||||
|
public override fun toString(): String {
|
||||||
|
val s = getSimpleName(this.typeInfo)
|
||||||
|
return if (message != null) s + ": " + message.toString() else s
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,398 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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 kotlin.wasm
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [WASI Error codes for preview1](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md)
|
||||||
|
*/
|
||||||
|
internal enum class WasiErrorCode {
|
||||||
|
/**
|
||||||
|
* No error occurred. System call completed successfully.
|
||||||
|
*/
|
||||||
|
SUCCESS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Argument list too long.
|
||||||
|
*/
|
||||||
|
_2BIG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission denied.
|
||||||
|
*/
|
||||||
|
ACCES,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address in use.
|
||||||
|
*/
|
||||||
|
ADDRINUSE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address not available.
|
||||||
|
*/
|
||||||
|
ADDRNOTAVAIL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address family not supported.
|
||||||
|
*/
|
||||||
|
AFNOSUPPORT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource unavailable, or operation would block.
|
||||||
|
*/
|
||||||
|
AGAIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection already in progress.
|
||||||
|
*/
|
||||||
|
ALREADY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bad file descriptor.
|
||||||
|
*/
|
||||||
|
BADF,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bad message.
|
||||||
|
*/
|
||||||
|
BADMSG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device or resource busy.
|
||||||
|
*/
|
||||||
|
BUSY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation canceled.
|
||||||
|
*/
|
||||||
|
CANCELED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No child processes.
|
||||||
|
*/
|
||||||
|
CHILD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection aborted.
|
||||||
|
*/
|
||||||
|
CONNABORTED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection refused.
|
||||||
|
*/
|
||||||
|
CONNREFUSED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection reset.
|
||||||
|
*/
|
||||||
|
CONNRESET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource deadlock would occur.
|
||||||
|
*/
|
||||||
|
DEADLK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destination address required.
|
||||||
|
*/
|
||||||
|
DESTADDRREQ,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mathematics argument out of domain of function.
|
||||||
|
*/
|
||||||
|
DOM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved.
|
||||||
|
*/
|
||||||
|
DQUOT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File exists.
|
||||||
|
*/
|
||||||
|
EXIST,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bad address.
|
||||||
|
*/
|
||||||
|
FAULT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File too large.
|
||||||
|
*/
|
||||||
|
FBIG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host is unreachable.
|
||||||
|
*/
|
||||||
|
HOSTUNREACH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier removed.
|
||||||
|
*/
|
||||||
|
IDRM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Illegal byte sequence.
|
||||||
|
*/
|
||||||
|
ILSEQ,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation in progress.
|
||||||
|
*/
|
||||||
|
INPROGRESS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupted function.
|
||||||
|
*/
|
||||||
|
INTR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid argument.
|
||||||
|
*/
|
||||||
|
INVAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I/O error.
|
||||||
|
*/
|
||||||
|
IO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket is connected.
|
||||||
|
*/
|
||||||
|
ISCONN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is a directory.
|
||||||
|
*/
|
||||||
|
ISDIR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Too many levels of symbolic links.
|
||||||
|
*/
|
||||||
|
LOOP,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File descriptor value too large.
|
||||||
|
*/
|
||||||
|
MFILE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Too many links.
|
||||||
|
*/
|
||||||
|
MLINK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message too large.
|
||||||
|
*/
|
||||||
|
MSGSIZE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved.
|
||||||
|
*/
|
||||||
|
MULTIHOP,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filename too long.
|
||||||
|
*/
|
||||||
|
NAMETOOLONG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network is down.
|
||||||
|
*/
|
||||||
|
NETDOWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection aborted by network.
|
||||||
|
*/
|
||||||
|
NETRESET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network unreachable.
|
||||||
|
*/
|
||||||
|
NETUNREACH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Too many files open in system.
|
||||||
|
*/
|
||||||
|
NFILE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No buffer space available.
|
||||||
|
*/
|
||||||
|
NOBUFS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No such device.
|
||||||
|
*/
|
||||||
|
NODEV,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No such file or directory.
|
||||||
|
*/
|
||||||
|
NOENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executable file format error.
|
||||||
|
*/
|
||||||
|
NOEXEC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No locks available.
|
||||||
|
*/
|
||||||
|
NOLCK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved.
|
||||||
|
*/
|
||||||
|
NOLINK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not enough space.
|
||||||
|
*/
|
||||||
|
NOMEM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No message of the desired type.
|
||||||
|
*/
|
||||||
|
NOMSG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol not available.
|
||||||
|
*/
|
||||||
|
NOPROTOOPT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No space left on device.
|
||||||
|
*/
|
||||||
|
NOSPC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function not supported.
|
||||||
|
*/
|
||||||
|
NOSYS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The socket is not connected.
|
||||||
|
*/
|
||||||
|
NOTCONN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not a directory or a symbolic link to a directory.
|
||||||
|
*/
|
||||||
|
NOTDIR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory not empty.
|
||||||
|
*/
|
||||||
|
NOTEMPTY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State not recoverable.
|
||||||
|
*/
|
||||||
|
NOTRECOVERABLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not a socket.
|
||||||
|
*/
|
||||||
|
NOTSOCK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not supported, or operation not supported on socket.
|
||||||
|
*/
|
||||||
|
NOTSUP,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inappropriate I/O control operation.
|
||||||
|
*/
|
||||||
|
NOTTY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No such device or address.
|
||||||
|
*/
|
||||||
|
NXIO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value too large to be stored in data type.
|
||||||
|
*/
|
||||||
|
OVERFLOW,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous owner died.
|
||||||
|
*/
|
||||||
|
OWNERDEAD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation not permitted.
|
||||||
|
*/
|
||||||
|
PERM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broken pipe.
|
||||||
|
*/
|
||||||
|
PIPE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol error.
|
||||||
|
*/
|
||||||
|
PROTO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol not supported.
|
||||||
|
*/
|
||||||
|
PROTONOSUPPORT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol wrong type for socket.
|
||||||
|
*/
|
||||||
|
PROTOTYPE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result too large.
|
||||||
|
*/
|
||||||
|
RANGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-only file system.
|
||||||
|
*/
|
||||||
|
ROFS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid seek.
|
||||||
|
*/
|
||||||
|
SPIPE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No such process.
|
||||||
|
*/
|
||||||
|
SRCH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved.
|
||||||
|
*/
|
||||||
|
STALE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection timed out.
|
||||||
|
*/
|
||||||
|
TIMEDOUT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text file busy.
|
||||||
|
*/
|
||||||
|
TXTBSY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross-device link.
|
||||||
|
*/
|
||||||
|
XDEV,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension: Capabilities insufficient.
|
||||||
|
*/
|
||||||
|
NOTCAPABLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class WasiError(val error: WasiErrorCode) : Throwable(message = "WASI call failed with $error")
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.io
|
||||||
|
|
||||||
|
import kotlin.wasm.WasiError
|
||||||
|
import kotlin.wasm.WasiErrorCode
|
||||||
|
import kotlin.wasm.WasmImport
|
||||||
|
import kotlin.wasm.unsafe.MemoryAllocator
|
||||||
|
import kotlin.wasm.unsafe.withScopedMemoryAllocator
|
||||||
|
|
||||||
|
private const val STDOUT = 1
|
||||||
|
private const val STDERR = 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to a file descriptor. Note: This is similar to `writev` in POSIX.
|
||||||
|
*/
|
||||||
|
@WasmImport("wasi_snapshot_preview1", "fd_write")
|
||||||
|
private external fun wasiRawFdWrite(descriptor: Int, scatterPtr: Int, scatterSize: Int, errorPtr: Int): Int
|
||||||
|
|
||||||
|
internal fun wasiPrintImpl(
|
||||||
|
allocator: MemoryAllocator,
|
||||||
|
data: ByteArray?,
|
||||||
|
newLine: Boolean,
|
||||||
|
useErrorStream: Boolean
|
||||||
|
) {
|
||||||
|
val dataSize = data?.size ?: 0
|
||||||
|
val memorySize = dataSize + (if (newLine) 1 else 0)
|
||||||
|
if (memorySize == 0) return
|
||||||
|
|
||||||
|
val ptr = allocator.allocate(memorySize)
|
||||||
|
if (data != null) {
|
||||||
|
var currentPtr = ptr
|
||||||
|
for (el in data) {
|
||||||
|
currentPtr.storeByte(el)
|
||||||
|
currentPtr += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newLine) {
|
||||||
|
(ptr + dataSize).storeByte(0x0A)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scatterPtr = allocator.allocate(8)
|
||||||
|
(scatterPtr + 0).storeInt(ptr.address.toInt())
|
||||||
|
(scatterPtr + 4).storeInt(memorySize)
|
||||||
|
|
||||||
|
val rp0 = allocator.allocate(4)
|
||||||
|
|
||||||
|
val ret =
|
||||||
|
wasiRawFdWrite(
|
||||||
|
descriptor = if (useErrorStream) STDERR else STDOUT,
|
||||||
|
scatterPtr = scatterPtr.address.toInt(),
|
||||||
|
scatterSize = 1,
|
||||||
|
errorPtr = rp0.address.toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
throw WasiError(WasiErrorCode.values()[ret])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun printImpl(message: String?, useErrorStream: Boolean, newLine: Boolean) {
|
||||||
|
withScopedMemoryAllocator { allocator ->
|
||||||
|
wasiPrintImpl(
|
||||||
|
allocator = allocator,
|
||||||
|
data = message?.encodeToByteArray(),
|
||||||
|
newLine = newLine,
|
||||||
|
useErrorStream = useErrorStream,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun printError(error: String?) {
|
||||||
|
printImpl(error, useErrorStream = true, newLine = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prints the line separator to the standard output stream. */
|
||||||
|
public actual fun println() {
|
||||||
|
printImpl(null, useErrorStream = false, newLine = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prints the given [message] and the line separator to the standard output stream. */
|
||||||
|
public actual fun println(message: Any?) {
|
||||||
|
printImpl(message?.toString(), useErrorStream = false, newLine = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prints the given [message] to the standard output stream. */
|
||||||
|
public actual fun print(message: Any?) {
|
||||||
|
printImpl(message?.toString(), useErrorStream = false, newLine = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SinceKotlin("1.6")
|
||||||
|
public actual fun readln(): String = TODO("wasi")
|
||||||
|
|
||||||
|
@SinceKotlin("1.6")
|
||||||
|
public actual fun readlnOrNull(): String? = TODO("wasi")
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.random
|
||||||
|
|
||||||
|
import kotlin.wasm.WasiError
|
||||||
|
import kotlin.wasm.WasiErrorCode
|
||||||
|
import kotlin.wasm.WasmImport
|
||||||
|
import kotlin.wasm.unsafe.withScopedMemoryAllocator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write high-quality random data into a buffer. This function blocks when the implementation is
|
||||||
|
* unable to immediately provide sufficient high-quality random data. This function may execute
|
||||||
|
* slowly, so when large mounts of random data are required, it's advisable to use this function to
|
||||||
|
* seed a pseudo-random number generator, rather than to provide the random data directly.
|
||||||
|
*/
|
||||||
|
@WasmImport("wasi_snapshot_preview1", "random_get")
|
||||||
|
//TODO: Better to use 64-bit Long as a seed if possible. (KT-60962)
|
||||||
|
internal external fun wasiRawRandomGet(address: Int, size: Int): Int
|
||||||
|
|
||||||
|
private fun wasiRandomGet(): Int {
|
||||||
|
withScopedMemoryAllocator { allocator ->
|
||||||
|
val memory = allocator.allocate(Int.SIZE_BYTES)
|
||||||
|
val ret = wasiRawRandomGet(memory.address.toInt(), Int.SIZE_BYTES)
|
||||||
|
return if (ret == 0) {
|
||||||
|
memory.loadInt()
|
||||||
|
} else {
|
||||||
|
throw WasiError(WasiErrorCode.values()[ret])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal actual fun defaultPlatformRandom(): Random =
|
||||||
|
Random(wasiRandomGet())
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.time
|
||||||
|
|
||||||
|
import kotlin.math.fdlibm.__ieee754_pow
|
||||||
|
|
||||||
|
internal actual fun formatToExactDecimals(value: Double, decimals: Int): String {
|
||||||
|
// TODO Make correct implementation (KT-60964)
|
||||||
|
val pow = __ieee754_pow(10.0, decimals.toDouble())
|
||||||
|
val round = kotlin.math.fdlibm.rint(value * pow)
|
||||||
|
return (round / pow).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal actual fun formatUpToDecimals(value: Double, decimals: Int): String = TODO("Wasi Implement")
|
||||||
|
|
||||||
|
internal actual inline val durationAssertionsEnabled: Boolean get() = true
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.time
|
||||||
|
|
||||||
|
import kotlin.time.Duration.Companion.nanoseconds
|
||||||
|
import kotlin.time.TimeSource.Monotonic.ValueTimeMark
|
||||||
|
import kotlin.wasm.WasiError
|
||||||
|
import kotlin.wasm.WasiErrorCode
|
||||||
|
import kotlin.wasm.WasmImport
|
||||||
|
import kotlin.wasm.unsafe.Pointer
|
||||||
|
import kotlin.wasm.unsafe.withScopedMemoryAllocator
|
||||||
|
|
||||||
|
private const val MONOTONIC = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the time value of a clock. Note: This is similar to `clock_gettime` in POSIX.
|
||||||
|
*/
|
||||||
|
@WasmImport("wasi_snapshot_preview1", "clock_time_get")
|
||||||
|
private external fun wasiRawClockTimeGet(clockId: Int, precision: Long, resultPtr: Int): Int
|
||||||
|
|
||||||
|
private fun clockTimeGet(): Long = withScopedMemoryAllocator { allocator ->
|
||||||
|
val rp0 = allocator.allocate(8)
|
||||||
|
val ret = wasiRawClockTimeGet(
|
||||||
|
clockId = MONOTONIC,
|
||||||
|
precision = 1,
|
||||||
|
resultPtr = rp0.address.toInt()
|
||||||
|
)
|
||||||
|
if (ret == 0) {
|
||||||
|
(Pointer(rp0.address.toInt().toUInt())).loadLong()
|
||||||
|
} else {
|
||||||
|
throw WasiError(WasiErrorCode.values()[ret])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SinceKotlin("1.3")
|
||||||
|
//TODO: Try use implementation of K/JVM since it uses a Long nanosecond counter similar to System.nanoTime (KT-60963)
|
||||||
|
internal actual object MonotonicTimeSource : TimeSource.WithComparableMarks {
|
||||||
|
actual override fun markNow(): ValueTimeMark =
|
||||||
|
ValueTimeMark(clockTimeGet())
|
||||||
|
|
||||||
|
actual fun elapsedFrom(timeMark: ValueTimeMark): Duration =
|
||||||
|
(clockTimeGet() - timeMark.reading).nanoseconds
|
||||||
|
|
||||||
|
actual fun adjustReading(timeMark: ValueTimeMark, duration: Duration): ValueTimeMark =
|
||||||
|
ValueTimeMark(timeMark.reading + duration.toLong(DurationUnit.NANOSECONDS))
|
||||||
|
|
||||||
|
actual fun differenceBetween(one: ValueTimeMark, another: ValueTimeMark): Duration {
|
||||||
|
val ms1 = one.reading
|
||||||
|
val ms2 = another.reading
|
||||||
|
return if (ms1 == ms2) Duration.ZERO else (ms1 - ms2).nanoseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "WASI monotonic time source"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ACTUAL_WITHOUT_EXPECT") // visibility
|
||||||
|
internal actual typealias ValueTimeMarkReading = Long
|
||||||
Reference in New Issue
Block a user