Appel d’un pointeur de fonction de style C dans un WebAssembly à partir de JavaScript

Existe-t-il un moyen d’accéder aux pointeurs de fonction présents dans un module WebAssembly?

Par exemple, étant donné le “module” suivant compilé pour WebAssembly:

extern void set_callback(void (*callback)(void *arg), void *arg); static void callback(void *arg) { /* ... */ } int main() { set_callback(&callback, 0); return 0; } 

Une implémentation de do_callback en JavaScript peut-elle appeler le rappel sans avoir à compter sur une exportation de fonction C intermédiaire pour effectuer l’appel de fonction proprement dit?

 var instance = new WebAssembly.Instance(module, { memory: /* ... */ env: { set_callback: function set_callback(callbackptr, argptr) { // We only got the pointer, is there any }, }, }); 

Par exportation de fonction intermédiaire, je veux dire que je pourrais append une fonction interne avec une visibilité publique.

 do_callback(void (*callback)(void *arg), void *arg) { callback(); } 

Ensuite, la fonction JavaScript set_callback peut appeler le pointeur de la fonction via la fonction delegate do_callback .

 function set_callback(callbackptr, argptr) { instance.exports.do_callback(callbackptr, argptr); } 

Mais, il est préférable de faire cela sans avoir à passer par cette indirection explicite, est-ce possible, avec des tables de fonctions peut-être?

Vous pouvez appeler des pointeurs de fonction à partir de Javascript.

Les pointeurs de fonction sont stockés dans une table. Lorsqu’un pointeur de fonction est passé à Javascript, vous recevez l’index entier dans la table pour ce pointeur de fonction. Table.prototype.get() cet index à Table.prototype.get() et vous pourrez appeler la fonction.

 ... set_callback: function set_callback(callbackptr, argptr) { tbl.get(callbackptr)(argptr); }, ... 

Vous pouvez en savoir plus à ce sujet sur cette page MDN sous la section Tables: https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API#Tables

Edit: Voici l’exemple minimal que j’ai utilisé pour tester cela.

Le premier fichier est fptr.c compilé avec emcc fptr.c -Os -s WASM=1 -s SIDE_MODULE=1 -o fptr.wasm

 typedef int (*fptr_type)(void); extern void pass_fptr_to_js(fptr_type fptr); static int callback_0(void) { return 26; } static int callback_1(void) { return 42; } void run_test() { pass_fptr_to_js(callback_0); pass_fptr_to_js(callback_1); } 

Et voici fptr.html

     WebAssembly Experiment   

Check the console.

Le problème à l’époque était que Clang n’avait pas implémenté call_indirect et n’avait en fait rempli la table de fonctions avec aucun élément lors de la génération du code.

A été résolu dans la version actuelle de LLVM.