Closed4
Kotlin/Wasm JS interop
diff --git a/browser-example/build.gradle.kts b/browser-example/build.gradle.kts
index de77ccc..d7eae38 100644
--- a/browser-example/build.gradle.kts
+++ b/browser-example/build.gradle.kts
@@ -47,3 +47,10 @@ kotlin {
val wasmJsTest by getting
}
}
+
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile>().configureEach {
+ kotlinOptions.freeCompilerArgs += listOf(
+ "-Xwasm-generate-wat",
+ )
+}
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.dom.appendElement
import kotlinx.dom.appendText
import org.w3c.dom.Element
import org.w3c.dom.HTMLInputElement
fun main() {
document.body?.appendElement("div") {
appendText("Time in ")
val output = document.createElement("span")
val input = appendElement("input") {
this as HTMLInputElement
type = "text"
placeholder="Timezone"
value = "Europe/Amsterdam"
addEventListener("change") {
updateTime(this, output)
}
} as HTMLInputElement
appendText(" is ")
appendChild(output)
updateTime(input, output)
}
}
val progress = "⡆⠇⠋⠙⠸⢰⣠⣄".map(Char::toString)
private fun updateTime(input: HTMLInputElement, output: Element) {
var i = 0
val progressId = window.setInterval({
output.textContent = progress[i]
i = (i + 1) % progress.size
null
}, 100)
window.fetch("https://worldtimeapi.org/api/timezone/${input.value}")
.then {
window.clearInterval(progressId)
if (it.ok) {
it.json().then {
output.textContent = (it as WorldTimeApiResponse).datetime
?.substringAfter("T")?.substringBefore(".") ?: "🧐"
null
}
} else {
output.textContent = "🤷 " + it.status
}
null
}
.catch {
window.clearInterval(progressId)
output.textContent = "🙅🛜"
null
}
}
external interface WorldTimeApiResponse {
val datetime: String?
}
$ ./gradlew build
build/js/packages/kotlin-wasm-browser-example-wasm-js/kotlin/kotlin-wasm-browser-example-wasm-js.uninstantiated.mjs
export async function instantiate(imports={}, runInitializer=true) {
const externrefBoxes = new WeakMap();
// ref must be non-null
function tryGetOrSetExternrefBox(ref, ifNotCached) {
if (typeof ref !== 'object') return ifNotCached;
const cachedBox = externrefBoxes.get(ref);
if (cachedBox !== void 0) return cachedBox;
externrefBoxes.set(ref, ifNotCached);
return ifNotCached;
}
const js_code = {
'kotlin.captureStackTrace' : () => new Error().stack,
'kotlin.wasm.internal.throwJsError' : (message, wasmTypeName, stack) => {
const error = new Error();
error.message = message;
error.name = wasmTypeName;
error.stack = stack;
throw error;
},
'kotlin.wasm.internal.stringLength' : (x) => x.length,
'kotlin.wasm.internal.jsExportStringToWasm' : (src, srcOffset, srcLength, dstAddr) => {
const mem16 = new Uint16Array(wasmExports.memory.buffer, dstAddr, srcLength);
let arrayIndex = 0;
let srcIndex = srcOffset;
while (arrayIndex < srcLength) {
mem16.set([src.charCodeAt(srcIndex)], arrayIndex);
srcIndex++;
arrayIndex++;
}
},
'kotlin.wasm.internal.importStringFromWasm' : (address, length, prefix) => {
const mem16 = new Uint16Array(wasmExports.memory.buffer, address, length);
const str = String.fromCharCode.apply(null, mem16);
return (prefix == null) ? str : prefix + str;
},
'kotlin.wasm.internal.getJsEmptyString' : () => '',
'kotlin.wasm.internal.externrefToString' : (ref) => String(ref),
'kotlin.wasm.internal.isNullish' : (ref) => ref == null,
'kotlin.wasm.internal.newJsArray' : () => [],
'kotlin.wasm.internal.jsArrayPush' : (array, element) => { array.push(element); },
'kotlin.wasm.internal.tryGetOrSetExternrefBox_$external_fun' : (p0, p1) => tryGetOrSetExternrefBox(p0, p1),
'kotlin.js.then_$external_fun' : (_this, p0) => _this.then(p0),
'kotlin.js.__convertKotlinClosureToJsClosure_((Js?)->Js?)' : (f) => (p0) => wasmExports['__callFunction_((Js?)->Js?)'](f, p0),
'kotlin.js.__convertKotlinClosureToJsClosure_((Js)->Js?)' : (f) => (p0) => wasmExports['__callFunction_((Js)->Js?)'](f, p0),
'kotlin.js.catch_$external_fun' : (_this, p0) => _this.catch(p0),
'kotlin.random.initialSeed' : () => ((Math.random() * Math.pow(2, 32)) | 0),
'kotlinx.browser.document_$external_prop_getter' : () => document,
'kotlinx.browser.window_$external_prop_getter' : () => window,
'org.w3c.dom.encryptedmedia.__convertKotlinClosureToJsClosure_((Js)->Unit)' : (f) => (p0) => wasmExports['__callFunction_((Js)->Unit)'](f, p0),
'org.w3c.dom.events.Event_$external_class_instanceof' : (x) => x instanceof Event,
'org.w3c.dom.events.addEventListener_$external_fun' : (_this, p0, p1) => _this.addEventListener(p0, p1),
'org.w3c.dom.Element_$external_class_instanceof' : (x) => x instanceof Element,
'org.w3c.dom.body_$external_prop_getter' : (_this) => _this.body,
'org.w3c.dom.createElement_$external_fun' : (_this, p0, p1, isDefault0) => _this.createElement(p0, isDefault0 ? undefined : p1, ),
'org.w3c.dom.createTextNode_$external_fun' : (_this, p0) => _this.createTextNode(p0),
'org.w3c.dom.placeholder_$external_prop_setter' : (_this, v) => _this.placeholder = v,
'org.w3c.dom.type_$external_prop_setter' : (_this, v) => _this.type = v,
'org.w3c.dom.value_$external_prop_getter' : (_this) => _this.value,
'org.w3c.dom.value_$external_prop_setter' : (_this, v) => _this.value = v,
'org.w3c.dom.HTMLInputElement_$external_class_instanceof' : (x) => x instanceof HTMLInputElement,
'org.w3c.dom.ownerDocument_$external_prop_getter' : (_this) => _this.ownerDocument,
'org.w3c.dom.textContent_$external_prop_setter' : (_this, v) => _this.textContent = v,
'org.w3c.dom.appendChild_$external_fun' : (_this, p0) => _this.appendChild(p0),
'org.w3c.dom.__convertKotlinClosureToJsClosure_(()->Js?)' : (f) => () => wasmExports['__callFunction_(()->Js?)'](f, ),
'org.w3c.dom.setInterval_$external_fun' : (_this, p0, p1, p2, isDefault0, isDefault1) => _this.setInterval(p0, isDefault0 ? undefined : p1, ...p2, ),
'org.w3c.dom.clearInterval_$external_fun' : (_this, p0, isDefault0) => _this.clearInterval(isDefault0 ? undefined : p0, ),
'org.w3c.dom.fetch_$external_fun' : (_this, p0, p1, isDefault0) => _this.fetch(p0, isDefault0 ? undefined : p1, ),
'org.w3c.fetch.status_$external_prop_getter' : (_this) => _this.status,
'org.w3c.fetch.ok_$external_prop_getter' : (_this) => _this.ok,
'org.w3c.fetch.json_$external_fun' : (_this, ) => _this.json(),
'org.w3c.fetch.Response_$external_class_instanceof' : (x) => x instanceof Response,
'datetime_$external_prop_getter' : (_this) => _this.datetime
}
// Placed here to give access to it from externals (js_code)
let wasmInstance;
let require;
let wasmExports;
const isNodeJs = (typeof process !== 'undefined') && (process.release.name === 'node');
const isStandaloneJsVM =
!isNodeJs && (
typeof d8 !== 'undefined' // V8
|| typeof inIon !== 'undefined' // SpiderMonkey
|| typeof jscOptions !== 'undefined' // JavaScriptCore
);
const isBrowser = !isNodeJs && !isStandaloneJsVM && (typeof window !== 'undefined');
if (!isNodeJs && !isStandaloneJsVM && !isBrowser) {
throw "Supported JS engine not detected";
}
const wasmFilePath = './kotlin-wasm-browser-example-wasm-js.wasm';
const importObject = {
js_code,
};
try {
if (isNodeJs) {
const module = await import(/* webpackIgnore: true */'node:module');
require = module.default.createRequire(import.meta.url);
const fs = require('fs');
const path = require('path');
const url = require('url');
const filepath = url.fileURLToPath(import.meta.url);
const dirpath = path.dirname(filepath);
const wasmBuffer = fs.readFileSync(path.resolve(dirpath, wasmFilePath));
const wasmModule = new WebAssembly.Module(wasmBuffer);
wasmInstance = new WebAssembly.Instance(wasmModule, importObject);
}
if (isStandaloneJsVM) {
const wasmBuffer = read(wasmFilePath, 'binary');
const wasmModule = new WebAssembly.Module(wasmBuffer);
wasmInstance = new WebAssembly.Instance(wasmModule, importObject);
}
if (isBrowser) {
wasmInstance = (await WebAssembly.instantiateStreaming(fetch(wasmFilePath), importObject)).instance;
}
} catch (e) {
if (e instanceof WebAssembly.CompileError) {
let text = `Please make sure that your runtime environment supports the latest version of Wasm GC and Exception-Handling proposals.
For more information, see https://kotl.in/wasm-help
`;
if (isBrowser) {
console.error(text);
} else {
const t = "\n" + text;
if (typeof console !== "undefined" && console.log !== void 0)
console.log(t);
else
print(t);
}
}
throw e;
}
wasmExports = wasmInstance.exports;
if (runInitializer) {
wasmExports._initialize();
}
return { instance: wasmInstance, exports: wasmExports };
}
build/js/packages/kotlin-wasm-browser-example-wasm-js/kotlin/kotlin-wasm-browser-example-wasm-js.wat
とりあえず window
と document
'kotlinx.browser.document_$external_prop_getter' : () => document,
'kotlinx.browser.window_$external_prop_getter' : () => window,
(func $kotlinx.browser.document_$external_prop_getter___fun_16 (import "js_code" "kotlinx.browser.document_$external_prop_getter") (type $____type_12))
(func $kotlinx.browser.window_$external_prop_getter___fun_17 (import "js_code" "kotlinx.browser.window_$external_prop_getter") (type $____type_12))
(type $____type_12 (func (param) (result externref)))
JSExport
@JsExport
fun addOne(x: Int): Int = x + 1
(export "addOne" (func $addOne___fun_60))
(func $addOne___fun_60 (type $____type_1)
(param $0_x i32) (result i32)
(local $1_currentIsNotFirstWasmExportCall i32)
(local $2_tmp i32)
(local $3_e (ref null $kotlin.Throwable___type_33))
(local $4_t (ref null $kotlin.Throwable___type_33))
global.get $kotlin.wasm.internal.isNotFirstWasmExportCall___g_4 ;; type: kotlin.Boolean
local.set $1_currentIsNotFirstWasmExportCall
;; Inlined call of `<UNKNOWN>`
block (result (ref null $kotlin.Unit___type_26))
;; Inlined call of `<UNKNOWN>`
block (result i32)
try
try
i32.const 1
global.set $kotlin.wasm.internal.isNotFirstWasmExportCall___g_4 ;; type: kotlin.Boolean
local.get $0_x ;; type: kotlin.Int
i32.const 1
i32.add
br 2
catch 0
local.set $3_e
local.get $1_currentIsNotFirstWasmExportCall ;; type: kotlin.Boolean
if (result (ref null $kotlin.Unit___type_26))
local.get $3_e ;; type: kotlin.Throwable
throw 0
else
local.get $3_e ;; type: kotlin.Throwable
call $kotlin.wasm.internal.throwAsJsException___fun_51
unreachable
end
br 3
end
unreachable
catch 0
local.set $4_t
local.get $1_currentIsNotFirstWasmExportCall ;; type: kotlin.Boolean
global.set $kotlin.wasm.internal.isNotFirstWasmExportCall___g_4 ;; type: kotlin.Boolean
local.get $4_t ;; type: kotlin.Throwable
throw 0
end
unreachable
end
local.set $2_tmp
local.get $1_currentIsNotFirstWasmExportCall ;; type: kotlin.Boolean
global.set $kotlin.wasm.internal.isNotFirstWasmExportCall___g_4 ;; type: kotlin.Boolean
local.get $2_tmp ;; type: kotlin.Int
return
end
drop
local.get $1_currentIsNotFirstWasmExportCall ;; type: kotlin.Boolean
global.set $kotlin.wasm.internal.isNotFirstWasmExportCall___g_4 ;; type: kotlin.Boolean
unreachable)
Any とか使える?
@JsExport
fun addOne(x: Any): Any = x
e: file:///Users/tanishiking/ghq/github.com/Kotlin/kotlin-wasm-examples/nodejs-example/src/wasmJsMain/kotlin/Main.kt:4:1 Type Any cannot be used in external function return. Only external, primitive, string and function types are supported in Kotlin/Wasm JS interop.
e: file:///Users/tanishiking/ghq/github.com/Kotlin/kotlin-wasm-examples/nodejs-example/src/wasmJsMain/kotlin/Main.kt:5:12 Type Any cannot be used in external function parameter. Only external, primitive, string and function types are supported in Kotlin/Wasm JS interop.
Type Any cannot be used in external function return. Only external, primitive, string and function types are supported in Kotlin/Wasm JS interop
いいね
このスクラップは7日前にクローズされました