Come posso restituire una stringa JavaScript da una funzione di WebAssembly

Come posso restituire una stringa JavaScript da una funzione WebAssembly?

Il seguente modulo può essere scritto in C (++)?

export function foo() { return 'Hello World!'; } 

Inoltre: Posso passare questo al motore JS per essere raccolto dalla spazzatura?

WebAssembly non supporta nativamente un tipo di stringa, supporta piuttosto i tipi di valore i32 / i64 / f32 / f64 e i8 / i16 per l’archiviazione.

È ansible interagire con un’istanza di WebAssembly utilizzando:

  • exports , dove da JavaScript si chiama in WebAssembly e WebAssembly restituisce un singolo tipo di valore.
  • imports dove WebAssembly chiama in JavaScript, con tutti i tipi di valore desiderati (nota: il conteggio deve essere noto al momento della compilazione del modulo, non è un array e non è variadic).
  • Memory.buffer , che è un ArrayBuffer che può essere indicizzato usando (tra gli altri) Uint8Array .

Dipende da cosa vuoi fare, ma sembra che l’accesso diretto al buffer sia il più semplice:

 const bin = ...; // WebAssembly binary, I assume below that it imports a memory from module "imports", field "memory". const module = new WebAssembly.Module(bin); const memory = new WebAssembly.Memory({ initial: 2 }); // Size is in pages. const instance = new WebAssembly.Instance(module, { imports: { memory: memory } }); const arrayBuffer = memory.buffer; const buffer = new Uint8Array(arrayBuffer); 

Se il tuo modulo ha una funzione di start , viene eseguito al momento dell’istanziazione. Altrimenti è probabile che esista un’esportazione che chiami, ad instance.exports.doIt() .

Una volta fatto, è necessario ottenere la dimensione della stringa + l’indice in memoria, che si esporrà anche attraverso un’esportazione:

 const size = instance.exports.myStringSize(); const index = instance.exports.myStringIndex(); 

Dovresti quindi leggerlo dal buffer:

 let s = ""; for (let i = index; i < index + size; ++i) s += String.fromCharCode(buffer[i]); 

Nota che sto leggendo i valori a 8 bit dal buffer, quindi presumo che le stringhe siano ASCII. Questo è ciò che std::string ti darebbe (l'indice nella memoria sarebbe quello che .c_str() ), ma per esporre qualcos'altro come UTF-8 avresti bisogno di usare una libreria C ++ che supporti UTF-8 e poi leggere UTF-8 da JavaScript, ottenere i codepoint e utilizzare String.fromCodePoint .

Si potrebbe anche fare affidamento sul fatto che la stringa sia terminata da null, cosa che non ho fatto qui.

È inoltre ansible utilizzare l' API TextDecoder una volta che è disponibile più ampiamente nei browser creando un object ArrayBufferView nel buffer WebAssembly.Memory (che è un ArrayBuffer ).


Se, invece, stai facendo qualcosa come la registrazione da WebAssembly a JavaScript, puoi esporre la Memory come sopra, e poi da WebAssembly dichiarare un'importazione che chiama JavaScript con dimensione + posizione. Puoi istanziare il tuo modulo come:

 const memory = new WebAssembly.Memory({ initial: 2 }); const arrayBuffer = memory.buffer; const buffer = new Uint8Array(arrayBuffer); const instance = new WebAssembly.Instance(module, { imports: { memory: memory, logString: (size, index) => { let s = ""; for (let i = index; i < index + size; ++i) s += String.fromCharCode(buffer[i]); console.log(s); } }); 

Questo ha l'avvertenza che se si Memory.prototype.grow la memoria (tramite JavaScript utilizzando Memory.prototype.grow o usando l'opcode grow_memory ), ArrayBuffer viene neutralizzato e occorre crearlo nuovamente.


Sulla garbage collection: WebAssembly.Module / WebAssembly.Instance / WebAssembly.Memory sono tutti garbage WebAssembly.Memory raccolti dal motore JavaScript, ma è un bel martello. Probabilmente vuoi le stringhe di GC e al momento non è ansible per oggetti che vivono all'interno di un WebAssembly.Memory . Abbiamo discusso dell'aggiunta del supporto GC in futuro .

C’è un modo più semplice per farlo. Innanzitutto, hai bisogno dell’istanza del tuo binario:

 const module = new WebAssembly.Module(bin); const memory = new WebAssembly.Memory({ initial: 2 }); const instance = new WebAssembly.Instance(module, { imports: { memory: memory } }); 

Quindi, se si esegue console.log(instance) , quasi nella parte superiore di questo object verrà visualizzata la funzione AsciiToString . Passa la tua funzione da C ++ che restituisce la stringa e vedrai l’output. In questo caso, dai un’occhiata a questa libreria .