[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/de/docs/WebAssembly/Guides/Using_the_JavaScript_API [Back]  [Original]

Verwendung der WebAssembly JavaScript API - WebAssembly | MDN

Dieser Inhalt wurde automatisch aus dem Englischen bersetzt, und kann Fehler enthalten. Erfahre mehr ber dieses Experiment.

View in English Always switch to English

Verwendung der WebAssembly JavaScript API

Dieser Artikel lehrt Sie, wie Sie die wichtigsten Funktionen der WebAssembly JavaScript API nutzen, einschlielich des Ladens von Wasm-Modulen und der Manipulation von WebAssembly-Speichern, -Tabellen und -Globalen.

Hinweis: Wenn Sie mit den grundlegenden Konzepten, die in diesem Artikel erwhnt werden, nicht vertraut sind und eine ausfhrlichere Erklrung bentigen, lesen Sie zunchst WebAssembly-Konzepte und kehren Sie dann zurck.

In diesem Artikel

Laden von Wasm-Modulen in JavaScript

Lassen Sie uns einige Beispiele durchgehen, die erklren, wie die WebAssembly JavaScript API verwendet wird und wie damit ein Wasm-Modul in eine Webseite geladen wird.

Hinweis: Sie finden den Beispielcode in unserem webassembly-examples GitHub-Repo.

Vorbereitung des Beispiels

  1. Zuerst bentigen wir ein Wasm-Modul! Holen Sie sich unsere simple.wasm-Datei und speichern Sie eine Kopie in einem neuen Verzeichnis auf Ihrem lokalen Computer.

  2. Erstellen Sie als nchstes eine einfache HTML-Datei namens index.html im selben Verzeichnis wie Ihre Wasm-Datei (Sie knnen unsere einfache Vorlage verwenden, wenn Sie keine leicht verfgbare haben).

  3. Um zu verstehen, was hier vor sich geht, schauen wir uns die textuelle Darstellung unseres Wasm-Moduls an (die wir auch in Konvertierung des WebAssembly-Formats zu Wasm kennenlernen):

    wat
    (module
      (func $i (import "my_namespace" "imported_func") (param i32))
      (func (export "exported_func")
        i32.const 42
        call $i))
    
  4. In der zweiten Zeile sehen Sie, dass der Import einen zweistufigen Namespace hat die interne Funktion $i wird von my_namespace.imported_func importiert. Wir mssen diesen zweistufigen Namespace in JavaScript widerspiegeln, wenn wir das Objekt schreiben, das in das Wasm-Modul importiert werden soll. Erstellen Sie ein <script></script>-Element in Ihrer HTML-Datei und fgen Sie den folgenden Code hinzu:

    js
    const importObject = {
      my_namespace: { imported_func: (arg) => console.log(arg) },
    };
    

Streaming des WebAssembly-Moduls

Neu in Firefox 58 ist die Fhigkeit, WebAssembly-Module direkt aus zugrunde liegenden Quellen zu kompilieren und zu instanziieren. Dies wird mit den Methoden WebAssembly.compileStreaming() und WebAssembly.instantiateStreaming() erreicht. Diese Methoden sind einfacher als ihre nicht streamenden Gegenstcke, da sie den Bytecode direkt in Module-/Instance-Instanzen umwandeln knnen, ohne den Response separat in einen ArrayBuffer umwandeln zu mssen.

Dieses Beispiel (siehe unser instantiate-streaming.html-Demo auf GitHub und ansehen Sie es live) zeigt, wie instantiateStreaming() verwendet wird, um ein Wasm-Modul abzurufen, eine JavaScript-Funktion darin zu importieren, es zu kompilieren und zu instanziieren und auf seine exportierte Funktion zuzugreifen alles in einem Schritt.

Fgen Sie das Folgende zu Ihrem Skript unter dem ersten Block hinzu:

js
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
  (obj) => obj.instance.exports.exported_func(),
);

Das Endergebnis ist, dass wir unsere exportierte WebAssembly-Funktion exported_func aufrufen, die wiederum unsere importierte JavaScript-Funktion imported_func aufruft, welche den im WebAssembly-Instanz bereitgestellten Wert (42) in die Konsole protokolliert. Wenn Sie Ihren Beispielcode jetzt speichern und in einem Browser laden, der WebAssembly untersttzt, sehen Sie dies in Aktion!

Hinweis: Dies ist ein verworrenes, langatmiges Beispiel, das sehr wenig erreicht, aber es dient dazu, zu veranschaulichen, was mglich ist die Verwendung von WebAssembly-Code zusammen mit JavaScript in Ihren Webanwendungen. Wie wir anderswo gesagt haben, beabsichtigt WebAssembly nicht, JavaScript zu ersetzen; die beiden knnen stattdessen zusammenarbeiten und aufeinander aufbauen.

Laden unseres Wasm-Moduls ohne Streaming

Wenn Sie die oben beschriebenen Streaming-Methoden nicht verwenden knnen oder wollen, knnen Sie stattdessen die nicht-Streaming-Methoden WebAssembly.compile() / WebAssembly.instantiate() verwenden.

Diese Methoden greifen nicht direkt auf den Bytecode zu, daher ist ein zustzlicher Schritt erforderlich, um die Antwort vor dem Kompilieren/Instanziieren des Wasm-Moduls in einen ArrayBuffer umzuwandeln.

Der entsprechende Code wrde folgendermaen aussehen:

js
fetch("simple.wasm")
  .then((response) => response.arrayBuffer())
  .then((bytes) => WebAssembly.instantiate(bytes, importObject))
  .then((results) => {
    results.instance.exports.exported_func();
  });

Anzeigen von Wasm in den Entwickler-Tools

In Firefox 54+ hat das Developer Tool Debugger Panel die Funktion, die textuelle Darstellung eines beliebigen Wasm-Codes, der in eine Webseite eingeschlossen ist, offenzulegen. Um es anzuzeigen, knnen Sie zum Debugger-Panel gehen und auf den Eintrag "wasm://" klicken.

Entwickler-Tools Debugger-Panel, das ein Modul hervorhebt. [Entwickler-Tools Debugger-Panel, das ein Modul hervorhebt.]

Zustzlich zur Ansicht von WebAssembly als Text knnen Entwickler WebAssembly mit dem Textformat debuggen (Haltepunkte setzen, den Callstack inspizieren, Schritt-fr-Schritt-Durchfhrung usw.).

Speicher

Im Low-Level-Speichermodell von WebAssembly wird der Speicher als ein zusammenhngender Bereich von ungetypten Bytes bezeichnet, der als Linear Memory bezeichnet wird und durch Lade- und Speicherbefehle innerhalb des Moduls gelesen und geschrieben wird. In diesem Speichermodell kann jeder Ladevorgang oder Speicherzugriff auf jedes Byte im gesamten linearen Speicher zugreifen, was notwendig ist, um C/C++-Konzepte wie Zeiger treu zu reprsentieren.

Im Gegensatz zu einem nativen C/C++-Programm, bei dem der verfgbare Speicherbereich den gesamten Prozess umfasst, ist der Speicher, auf den eine bestimmte WebAssembly-Instanz zugreifen kann, auf einen spezifischen mglicherweise sehr kleinen Bereich beschrnkt, der von einem WebAssembly-Memory-Objekt enthalten ist. Dies ermglicht es einer einzelnen Web-App, mehrere unabhngige Bibliotheken zu verwenden von denen jede intern WebAssembly verwendet , um separate Speicher zu haben, die vollstndig voneinander isoliert sind. Darber hinaus knnen neuere Implementierungen auch geteilte Speicher erstellen, die zwischen Window- und Worker-Kontexten ber postMessage() bertragen und an mehreren Stellen verwendet werden knnen.

In JavaScript kann eine Memory-Instanz als ein skalierbarer ArrayBuffer (oder SharedArrayBuffer, im Fall von geteilten Speichern) angesehen werden, und genauso wie mit ArrayBuffers kann eine einzelne Web-App viele unabhngige Memory-Objekte erstellen. Sie knnen eines mit dem WebAssembly.Memory()-Konstruktor erstellen, der als Argumente eine Anfangsgre und (optional) eine maximale Gre und eine shared-Eigenschaft bentigt, die angibt, ob es sich um einen geteilten Speicher handelt oder nicht.

Lassen Sie uns dies anhand eines kurzen Beispiels erkunden.

  1. Erstellen Sie eine weitere neue, einfache HTML-Seite (Kopieren Sie unsere einfache Vorlage) und nennen Sie sie memory.html. Fgen Sie der Seite ein <script></script>-Element hinzu.

  2. Fgen Sie nun die folgende Zeile am Anfang Ihres Skripts ein, um eine Speicherinstanz zu erstellen:

    js
    const memory = new WebAssembly.Memory({ initial: 10, maximum: 100 });
    

    Die Einheit von initial und maximum sind WebAssembly-Seiten diese sind auf eine Gre von 64 KB festgelegt. Dies bedeutet, dass die oben genannte Speicherinstanz eine Anfangsgre von 640 KB und eine maximale Gre von 6,4 MB hat.

    WebAssembly-Speicher gibt seine Bytes frei, indem es einen Buffer-Getter/Setter bereitstellt, der einen ArrayBuffer zurckgibt. Zum Beispiel, um 42 direkt in das erste Wort des linearen Speichers zu schreiben, knnen Sie dies tun:

    js
    const data = new DataView(memory.buffer);
    data.setUint32(0, 42, true);
    

    Beachten Sie die Verwendung von true, was das Lesen und Schreiben im Little-Endian-Format erzwingt, da WebAssembly-Speicher immer Little-Endian ist. Sie knnen dann den gleichen Wert zurckgeben mittels:

    js
    data.getUint32(0, true);
    
  3. Probieren Sie dies jetzt in Ihrem Demo aus speichern Sie, was Sie bisher hinzugefgt haben, laden Sie es in Ihrem Browser und versuchen Sie dann, die obigen zwei Zeilen in Ihre JavaScript-Konsole einzugeben.

Speicher vergrern

Eine Speicherinstanz kann durch Aufrufe an Memory.prototype.grow() vergrert werden, wobei das Argument wieder in Einheiten von WebAssembly-Seiten angegeben ist:

js
memory.grow(1);

Wenn beim Erstellen der Speicherinstanz ein Maximalwert angegeben wurde, fhren Versuche, diesen Maximalwert zu berschreiten, zu einer RangeError-Ausnahme. Die Engine nutzt diesen angegebenen Obergrenzen, um den Speicher im Voraus zu reservieren, was die Grenanpassung effizienter machen kann.

Hinweis: Da die byteLength eines ArrayBuffer unvernderlich ist, gibt der Buffer-Getter nach einem erfolgreichen Memory.prototype.grow()-Vorgang ein neues ArrayBuffer-Objekt (mit der neuen byteLength) zurck, und alle vorherigen ArrayBuffer-Objekte werden "detached", oder von dem zugrundeliegenden Speicher, auf den sie zuvor verwiesen, getrennt.

Genau wie Funktionen knnen lineare Speicher innerhalb eines Moduls definiert oder importiert werden. Ebenso kann ein Modul optional auch seinen Speicher exportieren. Dies bedeutet, dass JavaScript Zugriff auf den Speicher einer WebAssembly-Instanz erhalten kann, entweder indem es ein neues WebAssembly.Memory erstellt und es als Import bergibt, oder indem es einen Memory-Export erhlt (ber Instance.prototype.exports).

Ein umfassenderes Speicherbeispiel

Lassen Sie uns die obigen Aussagen verdeutlichen, indem wir uns ein ausfhrlicheres Speicherbeispiel ansehen ein WebAssembly-Modul, das die von uns zuvor definierte Speicherinstanz importiert, sie mit einem Array von Ganzzahlen befllt und dann deren Summe berechnet. Sie finden dies unter memory.wasm.

  1. Erstellen Sie eine lokale Kopie von memory.wasm im selben Verzeichnis wie zuvor.

    Hinweis: Sie knnen die textuelle Darstellung des Moduls unter memory.wat sehen.

  2. Gehen Sie zurck zu Ihrer memory.html-Beispieldatei und rufen Sie Ihr Wasm-Modul ab, kompilieren Sie es und instanziieren Sie es wie zuvor fgen Sie das folgende an das Ende Ihres Skripts hinzu:

    js
    WebAssembly.instantiateStreaming(fetch("memory.wasm"), {
      js: { mem: memory },
    }).then((results) => {
      // add code here
    });
    
  3. Da dieses Modul seinen Speicher exportiert, knnen wir bei einer Instanz dieses Moduls namens instance eine exportierte Funktion accumulate() verwenden, um ein Eingabearray direkt im linearen Speicher (mem) des Modul-Instances zu erstellen und zu fllen. Fgen Sie den folgenden Code an der angegebenen Stelle ein:

    js
    const summands = new DataView(memory.buffer);
    for (let i = 0; i < 10; i++) {
      summands.setUint32(i * 4, i, true);
    }
    const sum = results.instance.exports.accumulate(0, 10);
    console.log(sum);
    

Beachten Sie, wie wir das DataView-Objekt auf dem Speicherpuffer des Memory-Objekts (Memory.prototype.buffer), nicht auf dem Memory selbst, erstellen.

Memory-Imports funktionieren genauso wie Funktions-Imports, nur dass Memory-Objekte als Werte und nicht als JavaScript-Funktionen bergeben werden. Memory-Imports sind aus zwei Grnden ntzlich:

  • Sie ermglichen es JavaScript, die anfnglichen Speicherinhalte vor oder gleichzeitig mit der Modulerstellung abzurufen und zu erstellen.
  • Sie ermglichen es mehreren Modulinstanzen, ein einziges Memory-Objekt zu importieren, was ein entscheidender Baustein fr die Implementierung dynamischer Verlinkung in WebAssembly ist.

Hinweis: Sie knnen unser komplettes Demo unter memory.html (sehen Sie es auch live) finden.

Tabellen

Eine WebAssembly-Tabelle ist ein skalierbares typisiertes Array von Referenzen auf das sowohl JavaScript als auch WebAssembly-Code zugreifen kann. Whrend Memory ein skalierbares typisiertes Array von Rohbytes bereitstellt, ist es unsicher, wenn Referenzen in einem Memory gespeichert werden, da eine Referenz ein von der Engine vertrauter Wert ist, dessen Bytes nicht direkt von Inhalten gelesen oder geschrieben werden drfen, aus Sicherheits-, Portabilitts- und Stabilittsgrnden.

Tabellen haben einen Elementtyp, der die Arten von Referenzen begrenzt, die in der Tabelle gespeichert werden knnen. In der aktuellen Iteration von WebAssembly gibt es nur einen Typ von Referenzen, den WebAssembly-Code bentigt Funktionen und daher nur einen gltigen Elementtyp. In zuknftigen Iterationen werden weitere Elementtypen hinzugefgt.

Funktionsreferenzen sind notwendig, um Sprachen wie C/C++ zu kompilieren, die Funktionszeiger haben. In einer nativen Implementierung von C/C++ wird ein Funktionszeiger durch die Rohadresse des Funktionscodes im virtuellen Adressraum des Prozesses dargestellt, und daher kann er aus den oben genannten Sicherheitsgrnden nicht direkt im linearen Speicher gespeichert werden. Stattdessen werden Funktionsreferenzen in einer Tabelle gespeichert, und ihre Indizes, die Ganzzahlen sind und im linearen Speicher gespeichert werden knnen, werden stattdessen herumgereicht.

Wenn die Zeit kommt, einen Funktionszeiger zu rufen, liefert der WebAssembly-Aufrufer den Index, der dann gegen die Tabelle abgegrenzt berprft werden kann, bevor sie indiziert und die indizierte Funktionsreferenz aufgerufen wird. So gesehen, sind Tabellen derzeit ein recht primitives Mittel, um Features von Low-Level-Programmiersprachen sicher und portabel zu kompilieren.

Tabellen knnen ber Table.prototype.set() mutiert werden, das einen der Werte in einer Tabelle aktualisiert, und Table.prototype.grow(), das die Anzahl der in einer Tabelle speicherbaren Werte erhht. Dies ermglicht es, dass sich die indirekt aufrufbare Menge von Funktionen im Laufe der Zeit ndert, was notwendig fr Techniken der dynamischen Verlinkung ist. Die Mutationen sind sofort ber Table.prototype.get() in JavaScript zugnglich und fr Wasm-Module.

Ein Tabellenbeispiel

Lassen Sie uns ein einfaches Tabellenbeispiel betrachten ein WebAssembly-Modul, das eine Tabelle mit zwei Elementen erstellt und exportiert: Element 0 gibt 13 zurck, und Element 1 gibt 42 zurck. Sie finden dies unter table.wasm.

  1. Erstellen Sie eine lokale Kopie von table.wasm in einem neuen Verzeichnis.

    Hinweis: Sie knnen die textuelle Darstellung des Moduls unter table.wat sehen.

  2. Erstellen Sie eine neue Kopie unserer HTML-Vorlage im selben Verzeichnis und nennen Sie sie table.html.

  3. Wie zuvor, rufen Sie Ihr Wasm-Modul ab, kompilieren Sie es und instanziieren Sie es fgen Sie das Folgende in ein <script>-Element am Ende Ihres HTML-Krpers ein:

    js
    WebAssembly.instantiateStreaming(fetch("table.wasm")).then((results) => {
      // add code here
    });
    
  4. Lassen Sie uns nun die Daten in den Tabellen zugreifen fgen Sie die folgenden Zeilen an der angegebenen Stelle in Ihren Code ein:

    js
    const tbl = results.instance.exports.tbl;
    console.log(tbl.get(0)()); // 13
    console.log(tbl.get(1)()); // 42
    

Dieser Code greift auf jede Funktionsreferenz zu, die in der Tabelle gespeichert ist, und instanziiert sie, um die Werte, die sie enthalten, in die Konsole auszugeben beachten Sie, wie jede Funktionsreferenz mit einem Table.prototype.get()-Aufruf abgerufen wird, dann fgen wir ein zustzliches Paar Klammern am Ende hinzu, um die Funktion tatschlich aufzurufen.

Hinweis: Sie knnen unser komplettes Demo unter table.html (sehen Sie es auch live) finden.

Globalen

WebAssembly hat die Fhigkeit, globale Variableninstanzen zu erstellen, die sowohl aus JavaScript heraus zugnglich sind als auch ber eine oder mehrere WebAssembly.Module-Instanzen importierbar/exportierbar sind. Dies ist sehr ntzlich, da es das dynamische Verlinken mehrerer Module ermglicht.

Um eine globale WebAssembly-Instanz von innerhalb Ihres JavaScripts zu erstellen, verwenden Sie den WebAssembly.Global()-Konstruktor, der folgendermaen aussieht:

js
const global = new WebAssembly.Global({ value: "i32", mutable: true }, 0);

Sie sehen, dass dies zwei Parameter annimmt:

  • Ein Objekt, das zwei Eigenschaften enthlt, die die globale Variable beschreiben:

    • value: seinen Datentyp, der jeder innerhalb von WebAssembly-Modulen akzeptierte Datentyp sein kann i32, i64, f32 oder f64.
    • mutable: ein Boolean, der definiert, ob der Wert vernderbar ist oder nicht.
  • Ein Wert, der den tatschlichen Wert der Variablen enthlt. Dies kann jeder Wert sein, solange sein Typ mit dem spezifizierten Datentyp bereinstimmt.

Wie verwenden wir dies? Im folgenden Beispiel definieren wir eine globale Variable als vernderbaren i32-Typ mit einem Wert von 0.

Der Wert der globalen Variable wird dann gendert, zuerst auf 42 mithilfe der Global.value-Eigenschaft und dann auf 43 mithilfe der incGlobal()-Funktion, die aus dem global.wasm-Modul exportiert wird (dies fgt 1 zu jedem Wert hinzu, der ihr gegeben wird, und gibt dann den neuen Wert zurck).

js
const output = document.getElementById("output");

function assertEq(msg, got, expected) {
  const result =
    got === expected
      ? `SUCCESS! Got: ${got}\n`
      : `FAIL!\nGot: ${got}\nExpected: ${expected}\n`;
  output.innerText += `Testing ${msg}: ${result}`;
}

assertEq("WebAssembly.Global exists", typeof WebAssembly.Global, "function");

const global = new WebAssembly.Global({ value: "i32", mutable: true }, 0);

WebAssembly.instantiateStreaming(fetch("global.wasm"), { js: { global } }).then(
  ({ instance }) => {
    assertEq(
      "getting initial value from wasm",
      instance.exports.getGlobal(),
      0,
    );
    global.value = 42;
    assertEq(
      "getting JS-updated value from wasm",
      instance.exports.getGlobal(),
      42,
    );
    instance.exports.incGlobal();
    assertEq("getting wasm-updated value from JS", global.value, 43);
  },
);

Hinweis: Sie knnen das Beispiel live auf GitHub ausfhren; sehen Sie auch den Quellcode.

Multiplizitt

Nachdem wir nun die Verwendung der wichtigsten WebAssembly-Bausteine demonstriert haben, ist dies ein guter Ort, um das Konzept der Multiplizitt zu erwhnen. Dies bietet WebAssembly eine Vielzahl von Fortschritten in Bezug auf architektonische Effizienz:

  • Ein Modul kann N Instanzen haben, genauso wie ein Funktionsliteral N Abschlusswerte erzeugen kann.
  • Eine Modulinstanz kann 01 Speicherinstanzen verwenden, die den "Adressraum" der Instanz bereitstellen. Zuknftige Versionen von WebAssembly knnen 0N Speicherinstanzen pro Modulinstanz zulassen (siehe Mehrere Speicher).
  • Eine Modulinstanz kann 01 Tabelleninstanzen verwenden dies ist der "Funktionsadressraum" der Instanz, der zum Implementieren von C-Funktionszeigern verwendet wird. Zuknftige Versionen von WebAssembly knnen 0N Tabelleninstanzen pro Modulinstanz zulassen.
  • Eine Speicher- oder Tabelleninstanz kann von 0N Modulinstanzen verwendet werden diese Instanzen teilen sich alle denselben Adressraum, was dynamische Verlinkung ermglicht.

Sie knnen die Multiplizitt in Aktion in unserem Artikel ber das Verstehen des Textformats sehen siehe den Abschnitt Mutierende Tabellen und dynamische Verlinkung.

Zusammenfassung

Dieser Artikel fhrte Sie durch die Grundlagen der Verwendung der WebAssembly JavaScript API zur Einbindung eines WebAssembly-Moduls in einen JavaScript-Kontext und die Nutzung seiner Funktionen sowie der Verwendung von WebAssembly-Speicher und -Tabellen in JavaScript. Wir haben auch das Konzept der Multiplizitt angesprochen.

Siehe auch


Web Proxy Viewer  |  New URL  |  Original Page