6
votes

Comment exécuter la fonction Kotlin WebAssembly à partir de JavaScript?

Mon objectif est d'écrire une bibliothèque Kotlin, de la compiler dans WebAssembly et d'appeler ses fonctions depuis JS. Depuis quelques heures, j'essaye de faire fonctionner un simple bonjour monde. La documentation sur ce sujet est soit inexistante, soit bien cachée.

Voici mon fichier kotlin:

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

Quand je le compile dans WebAssembly, j'obtiens un hello.wasm et hello.wasm.js fichier.

J'ai d'abord essayé d'utiliser quelque chose comme ceci pour exécuter la fonction:

<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>

Ensuite, j'ai compris que je devais passer les importations de mon fichier hello.wasm.js dans le paramètre importObject . Je suppose donc que je dois utiliser le fichier hello.wasm.js pour initialiser correctement mon programme wasm.

Quand je charge mon wasm comme suit, je n'obtiens pas toutes les erreurs et la fonction main () est exécutée.

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

Mais comment puis-je exécuter la fonction hello () à partir de JavaScript? Les seuls exemples de kotlin wasm que j'ai trouvés n'appellent pas de fonctions spécifiques mais rendent quelque chose à partir de la fonction main () .

De plus, les liens vers la documentation pertinente sont très appréciés. >


MISE À JOUR: J'ai réussi à exécuter la fonction, mais je ne pense pas que ce soit la bonne façon:

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

Le problème est que mon fichier wasm est récupéré deux fois si je le fais comme ça.

Le chargement du fichier hello.wasm.js sans l'attribut wasm me donne l'erreur suivante:

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

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


1 commentaires

J'ai fini par écrire ma bibliothèque dans Rust, l'outillage et la documentation sont bien meilleurs et tout fonctionne comme prévu.


3 Réponses :


-1
votes

Autant que je sache, vous devrez exporter la fonction dans une variable pour continuer à l'utiliser, ou rester dans l'instance du streaming.

Donc, je pense que ça devrait être quelque chose comme ça:

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

après cela, vous pouvez simplement utiliser votre variable comme fonction et l'appeler comme ceci:

helloFunc();

Sinon, si vous n'avez pas besoin pour exporter la fonction pour une utilisation ultérieure, vous pouvez simplement l'utiliser à l'intérieur de l'instance comme ceci:

let helloFunc;

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

Si vous ne voulez pas utiliser la déconstruction d'objet comme je l'ai utilisée, vous peut continuer à utiliser votre syntaxe normale avec obj.instance.


2 commentaires

Il est également à noter que parfois le nom de la fonction get est modifié lors de la compilation wasm. C ++, par exemple, aime ajouter quelque chose comme «_Z» au début du nom de la fonction. Pour analyser si cela se produit également avec kotlin, vous pouvez assembler votre code WebAssembly en WAT (WebAssembly Text Format) pour le vérifier.


Je ne peux pas exécuter la fonction de cette manière car le wasm n'est pas correctement initialisé avec toutes les importations konan. Le fichier * .wasm.js contient des fonctions comme initiateAndRun (..) qui initialise tout correctement mais n'exécute que la fonction principale et ne renvoie pas l'instance. Puisqu'il s'agit d'un fichier généré, je pense vraiment qu'il me manque quelque chose.



1
votes

J'ai récemment fait des recherches à ce sujet moi-même et d'après ce que je comprends, votre cas d'utilisation n'est pas vraiment pris en charge jusqu'à présent. Ce que vous recherchez est essentiellement une bibliothèque, mais si vous regardez dans le documentation de Kotlin / Native il déclare:

Les types binaires suivants sont pris en charge (notez que tous les types ne sont pas disponibles pour toutes les plates-formes natives):

[...]

sharedLib - une bibliothèque native partagée - toutes les cibles natives sauf wasm32

staticLib - une bibliothèque native statique - toutes les cibles natives sauf wasm32

D'après ce que je comprends, la difficulté réside dans le passage des données entre Javascript et WebAssembly car il ne prend en charge que les ints ou de manière plutôt détournée via la mémoire linéaire.


0 commentaires

0
votes

Actuellement, j'aime travailler sur un projet Kotlin WASM et (bien pour le chercheur en moi) il n'y a pas d'expérience dans notre équipe. Désolé, j'ai toujours le même point d'interrogation dans ma tête, mais je l'ai assez loin.

Dans mon cas, j'ai trouvé de nombreux exemples de wasm, mais j'avais l'impression que je ne pouvais pas les utiliser, car à Kotlin, les choses étaient différentes. .. En fait, ce n'est pas le cas et les choses sont simplement cachées dans les processus de construction qui dans notre cas est à la fois une bénédiction et une malédiction. Il n'y a pas de docu, mais kotlin fait des choses pour nous!

Pour moi, la meilleure stratégie pour combler cette lacune d'information est d'examiner plus en profondeur le fichier * wasm.js généré. Cela peut sembler effrayant et il s'agit de JavaScript, mais il est en fait assez facile de se faire une idée de ce que Kotlin est réellement capable de faire pour vous. Le meilleur: Si vous avez jeté un œil sur les références Api ( https: //developer.mozilla .org / fr-FR / docs / WebAssembly ) ou vous venez de regarder un tutoriel mais vous n'avez pas pu appliquer ce que vous avez vu, il y a de fortes chances que vous trouviez ces morceaux de code ici!

Quiconque lit ceci et veut essayer des choses: vous devez préparer une configuration de construction qui vous permet d'ajouter des éléments au fichier * .wasm.js généré. Les parties importantes pour cela dans ma configuration:

    
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)
}

Quel est l'intérêt pour ce sujet?


0 commentaires