Wie kann ich eine JavaScript-Zeichenfolge von einer WebAssembly-function zurückgeben?

Wie kann ich eine JavaScript-Zeichenfolge von einer WebAssembly-function zurückgeben?

Kann das folgende Modul in C (++) geschrieben werden?

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

Außerdem: Kann ich dies an die JS-Engine übergeben, um Müll gesammelt zu bekommen?

WebAssembly unterstützt nativ keinen String-Typ, sondern unterstützt i32 / i64 / f32 / f64 f32 sowie i8 / i16 für den Speicher.

Sie können mit einer WebAssembly-Instanz interagieren, indem Sie Folgendes verwenden:

  • exports , wo von JavaScript Sie in WebAssembly aufrufen, und WebAssembly gibt einen einzelnen Werttyp zurück.
  • imports denen WebAssembly JavaScript aufruft, mit so vielen Werttypen wie Sie möchten (Hinweis: Die Anzahl muss zur Modulkompilierungszeit bekannt sein, dies ist kein Array und ist nicht variadisch).
  • Memory.buffer , ein ArrayBuffer , der unter anderem mit Uint8Array indiziert werden Uint8Array .

Es hängt davon ab, was Sie tun möchten, aber es scheint, als ob der Zugriff auf den Puffer direkt am einfachsten ist:

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

Wenn Ihr Modul eine Startfunktion hatte, wurde es zur Instanziierungszeit ausgeführt. Andernfalls haben Sie wahrscheinlich einen Export, den Sie aufrufen, zB instance.exports.doIt() .

Sobald dies erledigt ist, müssen Sie String-Größe + Index im Speicher abrufen, die Sie auch durch einen Export verfügbar machen würden:

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

Sie würden es dann aus dem Puffer lesen:

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

Beachten Sie, dass ich 8-Bit-Werte aus dem Puffer lesen, ich nehme daher an, dass die Zeichenfolgen ASCII waren. Das wäre, was std::string Ihnen geben würde (Index im Speicher wäre, was .c_str() zurückgibt), aber um etwas anderes wie UTF-8 .c_str() zu machen, müssten Sie eine C ++ Bibliothek verwenden, die UTF-8 unterstützt, und dann lesen UTF-8 selbst von JavaScript, erhalten Sie die Codepunkte und verwenden Sie String.fromCodePoint .

Sie könnten sich auch darauf verlassen, dass die Zeichenfolge null-terminiert ist, was ich hier nicht getan habe.

Sie können auch die TextDecoder API verwenden, sobald sie in den Browsern verfügbar ist, indem Sie eine ArrayBufferView in den WebAssembly.Memory - buffer (der ein ArrayBuffer ) ArrayBuffer .


Wenn Sie stattdessen etwas wie die Protokollierung von WebAssembly auf JavaScript ausführen, können Sie den Memory wie oben verfügbar machen und dann von WebAssembly einen Import deklarieren, der JavaScript mit der Größe + Position aufruft. Sie können Ihr Modul wie folgt instanziieren:

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

Dies hat den Nachteil, dass, wenn Sie den Speicher vergrößern (entweder durch JavaScript mit Memory.prototype.grow oder mit dem grow_memory Opcode), der ArrayBuffer kastriert wird und Sie ihn neu erstellen müssen.


Bei der Garbage-Collection: WebAssembly.Module / WebAssembly.Instance / WebAssembly.Memory werden alle von der JavaScript-Engine gesammelt, aber das ist ein ziemlich großer Hammer. Wahrscheinlich möchten Sie GC-Strings, und das ist derzeit nicht möglich für Objekte, die in einem WebAssembly.Memory . Wir haben darüber diskutiert , in Zukunft GC-Unterstützung hinzuzufügen .

Es gibt einen einfacheren Weg, das zu tun. Zuerst benötigen Sie die Instanz Ihrer Binärdatei:

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

Wenn Sie dann console.log(instance) , wird fast an der Spitze dieses Objekts die function AsciiToString . Übergeben Sie Ihre function von C ++, die Zeichenfolge zurückgibt, und Sie werden die Ausgabe sehen. In diesem Fall, sieh dir diese Bibliothek an .