JavaScript API
L’oggetto globale WebAssembly
Il browser espone l’oggetto WebAssembly con metodi per caricare, compilare e istanziare moduli .wasm.
WebAssembly.instantiateStreaming()
Il metodo raccomandato per caricare Wasm. Compila e istanzia mentre il download è ancora in corso (streaming).
const importObject = {
env: {
log: (value) => console.log("Da Wasm:", value),
abort: () => { throw new Error("abort chiamato"); },
},
};
// Metodo preferito: streaming
const { module, instance } = await WebAssembly.instantiateStreaming(
fetch("calculator.wasm"),
importObject
);
// Chiamare una funzione esportata
const result = instance.exports.add(10, 20);
console.log(result); // 30
Nota: il server deve restituire il file con Content-Type
application/wasm, altrimentiinstantiateStreamingfallisce.
WebAssembly.instantiate()
Versione non-streaming: riceve un ArrayBuffer già completo. Utile quando il .wasm proviene da fonti diverse dalla rete.
// Caricamento da ArrayBuffer
const response = await fetch("calculator.wasm");
const bytes = await response.arrayBuffer();
const { module, instance } = await WebAssembly.instantiate(
bytes,
importObject
);
console.log(instance.exports.multiply(6, 7)); // 42
WebAssembly.compile() e WebAssembly.Module
Puoi separare la compilazione dall’istanziazione. Questo è utile per creare più istanze dallo stesso modulo.
// Compilare il modulo una sola volta
const module = await WebAssembly.compileStreaming(fetch("game.wasm"));
// Creare più istanze con import diversi
const instance1 = await WebAssembly.instantiate(module, importObj1);
const instance2 = await WebAssembly.instantiate(module, importObj2);
WebAssembly.Module può anche essere inviato a un Web Worker tramite postMessage:
// Thread principale
const module = await WebAssembly.compileStreaming(fetch("heavy.wasm"));
worker.postMessage({ module }); // Module è "structured cloneable"
// Nel Worker
self.onmessage = async (e) => {
const instance = await WebAssembly.instantiate(e.data.module, imports);
// Usare instance...
};
WebAssembly.Instance
L’istanza è l’oggetto che contiene le funzioni e la memoria esportate.
const { instance } = await WebAssembly.instantiateStreaming(
fetch("math.wasm"),
{}
);
// Accedere alle export
console.log(instance.exports);
// { add: [Function], sub: [Function], memory: Memory }
// Chiamare funzioni
instance.exports.add(1, 2); // 3
// Accedere alla memoria esportata
const mem = new Uint8Array(instance.exports.memory.buffer);
L’importObject
L’oggetto di import fornisce al modulo Wasm tutto ciò di cui ha bisogno dall’esterno. La struttura deve corrispondere esattamente alle dichiarazioni import nel modulo.
;; Il modulo WAT si aspetta queste import
(module
(import "env" "log" (func $log (param i32)))
(import "env" "memory" (memory 1))
(import "math" "sqrt" (func $sqrt (param f64) (result f64)))
)
// L'importObject deve rispecchiare i namespace
const importObject = {
env: {
log: (val) => console.log(val),
memory: new WebAssembly.Memory({ initial: 1 }),
},
math: {
sqrt: Math.sqrt,
},
};
Esportare Funzioni da Diversi Linguaggi
Da C (Emscripten):
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int square(int x) {
return x * x;
}
Da Rust (wasm-bindgen):
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn square(x: i32) -> i32 {
x * x
}
Da AssemblyScript:
export function square(x: i32): i32 {
return x * x;
}
In tutti i casi, dopo la compilazione, la funzione square sarà disponibile in instance.exports.square.
Pattern di Caricamento Completo
async function loadWasm(url, imports = {}) {
try {
// Provare streaming per prima cosa
return await WebAssembly.instantiateStreaming(fetch(url), imports);
} catch {
// Fallback per server senza Content-Type corretto
const response = await fetch(url);
const bytes = await response.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
}
}
// Uso
const { instance } = await loadWasm("app.wasm", {
env: { log: console.log },
});
Questo pattern con fallback garantisce la compatibilità anche quando il server non serve il MIME type corretto.