簡體   English   中英

如何從 JavaScript 執行 Kotlin WebAssembly 函數?

[英]How to execute Kotlin WebAssembly function from JavaScript?

我的目標是編寫一個 Kotlin 庫,將其編譯為 WebAssembly 並從 JS 調用其函數。 幾個小時以來,我嘗試讓一個簡單的 hello world 工作。 關於此主題的文檔要么不存在,要么隱藏得很好。

這是我的 kotlin 文件:

@Used
public fun hello() {
    println("Hello world!")
}

fun main(args: Array<String>) {
    println("main() function executed!")
}

當我將它編譯為 WebAssembly 時,我會得到一個hello.wasmhello.wasm.js文件。

首先我嘗試使用這樣的東西來執行函數:

WebAssembly.instantiateStreaming(fetch('hello.wasm'), importObject)
    .then(obj => obj.instance.exports.hello());

然后我明白我需要在importObject參數中傳遞來自我的hello.wasm.js文件的導入 所以我想我需要使用hello.wasm.js文件來正確初始化我的 wasm 程序。

當我像下面這樣加載我的 wasm 時,我沒有收到任何錯誤並且main()函數被執行。

<script wasm="hello.wasm" src="hello.wasm.js"></script>

但是如何從 JavaScript 執行hello()函數呢? 我發現的唯一 kotlin wasm 示例不是調用特定函數,而是從main()函數渲染某些內容。

也非常感謝任何指向相關文檔的鏈接。


更新:我設法執行了該功能,但我認為這不是正確的方法:

<script wasm="hello.wasm" src="hello.wasm.js"></script>
<script>
WebAssembly.instantiateStreaming(fetch('hello.wasm'), konan_dependencies)
        .then(obj => obj.instance.exports['kfun:hello$$ValueType']());
</script>

問題是,如果我這樣做,我的 wasm 文件會被提取兩次。

僅加載沒有 wasm 屬性的 hello.wasm.js 文件會出現以下錯誤:

Uncaught Error: Could not find the wasm attribute pointing to the WebAssembly binary.
    at Object.konan.moduleEntry (stats.wasm.js:433)
    at stats.wasm.js:532

我最近自己對此進行了一些研究,據我了解,到目前為止,您的用例還沒有得到真正的支持。 您正在尋找的本質上是一個庫,但是如果您查看 Kotlin/Native 的文檔,它會指出:

支持以下二進制類型(請注意,並非所有類型都可用於所有本機平台):

[...]

sharedLib - 共享本機庫 - 除 wasm32 之外的所有本機目標

staticLib - 靜態本機庫 - 除 wasm32 之外的所有本機目標

根據我的理解,困難在於在 Javascript 和 WebAssembly 之間傳遞數據,因為它只支持整數或通過線性內存的迂回方式。

目前我喜歡在 Kotlin WASM 項目上工作,並且(對我的研究人員來說很好)我們團隊沒有經驗。 對不起,我腦子里仍然有同樣的問號,但已經很遠了。

在我的例子中,我找到了很多 wasm 的例子,但感覺我不能使用它們,因為在 Kotlin 中事情是不同的......實際上情況並非如此,事情只是隱藏在構建過程中,在我們的例子中是既是福也是禍。 沒有文檔,但 kotlin 為我們做事!

對我來說,填補信息空白的最佳策略是更深入地查看生成的 *wasm.js 文件。 它可能看起來很嚇人,而且它是 JavaScript,但實際上很容易了解 Kotlin 實際能夠為您做什么。 最好的:如果您查看了 Api 參考資料 ( https://developer.mozilla.org/en-US/docs/WebAssembly ) 或者您剛剛觀看了教程但無法應用您所看到的內容,那么很有可能你會在這里找到這些代碼!

誰讀過這篇文章並想嘗試一下:您應該准備一個構建設置,允許您將內容附加到生成的 *.wasm.js 文件中。 我的設置中的重要部分:

    
val jsinterface by tasks.creating(Exec::class) {
    val kotlincExecutable = "${project.properties["konanHome"]}/bin/kotlinc"

    inputs.property("kotlincExecutable", kotlincExecutable)
    outputs.file(jsInterfaceKlibFileName)

    val ktFile = file(workingDir).resolve("src/wasm32Main/kotlin/js_interface/imported_js_funcs.kt")
    val jsStubFile = file(workingDir).resolve("src/wasm32Main/js/stub.js")

    executable(kotlincExecutable)
    args(
        "-include-binary", jsStubFile,
        "-produce", "library",
        "-o", jsInterfaceKlibFileName,
        "-target", "wasm32",
        ktFile
    )
}

val jsinterop by tasks.creating(Exec::class) {
    dependsOn(jsinterface) //inserts customized js functions on xxx.wasm.js
    //jsinterop -pkg kotlinx.interop.wasm.dom  -o build/klib/kotlinx.interop.wasm.dom-jsinterop.klib -target wasm32
    workingDir("./")
    executable("${project.properties["konanHome"]}/bin/jsinterop")
    args("-pkg", "kotlinx.interop.wasm.dom", "-o", jsinteropKlibFileName.toString(), "-target", "wasm32")
}

// generate jsinterop before native compile
tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile::class).all {
    dependsOn(jsinterop)
}

有多少人對這個話題感興趣?

據我所知,您需要將該函數導出到一個變量中才能繼續使用它,或者留在流的實例中。

所以我認為它應該是這樣的:

let helloFunc;

WebAssembly.instantiateStreaming(fetch('hello.wasm'), importObject)
.then(({instance}) => {
helloFunc = instance.exports.hello;
});

之后,您可以將變量用作函數並像這樣調用它:

helloFunc();

否則,如果您不需要導出函數供以后使用,您可以像這樣在實例內部使用它:

WebAssembly.instantiateStreaming(fetch('hello.wasm'), importObject)
.then(({instance}) => {
instance.exports.hello();
});

如果您不想像我使用的那樣使用對象解構,您可以繼續使用 obj.instance 的常規語法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM