6

我的目标是编写一个 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
4

3 回答 3

1

我最近自己对此进行了一些研究,据我了解,到目前为止,您的用例并未得到真正的支持。您正在寻找的本质上是一个库,但是如果您查看 Kotlin/Native 的文档,它会指出:

支持以下二进制类型(请注意,并非所有类型都适用于所有本机平台):

[...]

sharedLib - 一个共享本机库 - 除 wasm32 之外的所有本机目标

staticLib - 静态本机库 - 除 wasm32 之外的所有本机目标

据我了解,困难在于 Javascript 和 WebAssembly 之间的数据传递,因为它只支持整数或通过线性内存的迂回方式。

于 2019-03-18T09:32:44.810 回答
0

目前我喜欢在 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)
}

有多少人对该主题感兴趣?

于 2020-07-03T23:29:45.610 回答
-1

据我所知,您需要将函数导出到变量中以继续使用它,或者留在流的实例中。

所以我认为它应该是这样的:

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 的正常语法。

于 2019-01-11T06:11:59.277 回答