webassembly

Especialista em WebAssembly (Wasm) baseado na documentação oficial do MDN Web Docs (developer.mozilla.org/pt-BR/docs/WebAssembly), complementado por WebAssembly: The Definitive Guide (Brian Sletten),

SKILL.md

GitHub

Habilidade de IA: WebAssembly (Wasm Specialist)

Esta skill orienta a inteligência artificial a atuar como especialista em WebAssembly, alinhada à documentação oficial do MDN Web Docs (https://developer.mozilla.org/pt-BR/docs/WebAssembly). Cobre desde os conceitos de arquitetura (Module, Memory, Table, Instance) até o uso avançado da API JavaScript, compilação cross-language (C/C++, Rust, AssemblyScript), formato texto .wat, SIMD, exceções e JSPI.

📖 Referência canônica: consulte references/wasm-mdn-guide.md para o guia consolidado (conceitos, API JavaScript, Memória, Tabelas, Globais, tipos de valor, segurança).

📚 Referências complementares:


🧭 Diretrizes Fundamentais

Ao atuar nesta skill, aplique os seguintes padrões:

1. Modelo Mental Correto

  • Wasm complementa o JavaScript, não o substitui: execute lado a lado na mesma VM; use wasm para hot paths computacionais (games 3D, AR/VR, visão computacional, edição de imagem/vídeo, codecs, criptografia) e JS/DOM para a camada de aplicação.
  • Conceitos 1:1 com a API JS: Module (binário compilado, sem estado, compartilhável via postMessage() como Blob), Instance (Module + estado), Memory (ArrayBuffer redimensionável — Linear Memory em páginas de 64 KB), Table (array redimensionável de referências — funcref, base para ponteiros de função).
  • Multiplicidade: 1 Module → N Instances; 0–1 Memory/Table por Instance; 1 Memory/Table compartilhada por N Instances (fundamento do dynamic linking).

2. Carregamento e Instanciação

  • Sempre prefira streaming: WebAssembly.instantiateStreaming(fetch(url), importObject) — compila e instancia direto do stream, sem ArrayBuffer intermediário.
  • Sirva .wasm com MIME application/wasm e CORS habilitado (requisito para compile streaming).
  • Valide antes quando a origem dos bytes não for confiável: WebAssembly.validate(bytes);
  • Introspecção: Module.imports(), Module.exports(), Module.customSections().
  • Cache grandes módulos de bytes no IndexedDB para acelerar o startup.

3. Fronteira JS ↔ Wasm

  • Imports usam namespace de dois níveis: { imports: { imported_func: fn } } ↔ (import "imports" "imported_func").
  • Só tipos numéricos primitivos cruzam a fronteira por padrão; dados estruturados passam pela Linear Memory via Uint8Array/Int32Array etc. sobre memory.buffer.
  • i64 cruza a fronteira como BigInt. Referências host-side usam externref; funções usam funcref em WebAssembly.Table.
  • Chamadas frequentes JS↔wasm têm custo de fronteira: mova o loop inteiro para o wasm, não itere elemento a elemento via exports.

4. Memória

  • Sempre declare initial e preferencialmente maximum em WebAssembly.Memory (pré-reserva eficiente; excedente lança RangeError).
  • ⚠️ Buffer detachment: após memory.grow(), o memory.buffer é novo — recrie todas as TypedArrays; views antigas ficam inválidas.
  • Memórias compartilhadas (shared: true) → SharedArrayBuffer transferível entre Window/Worker com postMessage(); sincronize com Atomics.
  • Prefira definir maximum antecipadamente em vez de crescer indefinidamente (fragmentação/reattribuição).

5. Compilação por Ferramenta

  • Emscripten (C/C++): produz .wasm + glue JS + HTML; implementação de SDL/OpenGL/POSIX sobre Web APIs; wasm não acessa DOM — só chama JS. Use para portar código C/C++ legado e apps com AL.
  • Rust: wasm-pack + crate wasm-bindgen; gera pacote npm tipado; preferir para módulos novos com ABI ergonômica.
  • AssemblyScript: sintaxe tipo TypeScript; bundle pequeno; performance um pouco abaixo de C/Rust; ideal para devs web sem background C.
  • WAT direto: para ferramentas/compiladores customizados; converter com WABT (wat2wasm, wasm2wat).

6. Erros e Exceções

  • Trate especificamente: CompileError (bytes inválidos), LinkError (imports/exports incompatíveis), RuntimeError (unreachable, acesso fora da memória), SuspendError (JSPI).
  • Exceções estruturadas: WebAssembly.Tag + WebAssembly.Exception (is(), getArg(), stack); no wasm use try_table/catch/throw/throw_ref.
  • JSPI (quando disponível): new WebAssembly.Suspending(promiseFn) para importações suspensoras e WebAssembly.promising(export) para transformar export wasm em função que retorna Promise — wasm pausa durante operações assíncronas sem bloquear.

🛠️ Padrões de Código Recomendados

Carregamento streaming idiomático

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

WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject)
  .then(({ instance }) => instance.exports.exported_func())
  .catch((err) => {
    if (err instanceof WebAssembly.CompileError) {
      console.error("Bytes wasm inválidos ou MIME incorreto:", err);
    }
  });

Troca de dados via Linear Memory

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

WebAssembly.instantiateStreaming(fetch("memory.wasm"), { js: { mem: memory } })
  .then(({ instance }) => {
    let i32 = new Uint32Array(memory.buffer);
    for (let i = 0; i < 10; i++) i32[i] = i;
    const sum = instance.exports.accumulate(0, 10);
    // ⚠️ se o módulo chamar memory.grow internamente, recrie a view:
    i32 = new Uint32Array(memory.buffer);
  });

Global compartilhado para vínculo dinâmico

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

WebAssembly.instantiateStreaming(fetch("global.wasm"), { js: { global: sharedCounter } })
  .then(({ instance }) => {
    sharedCounter.value = 42;          // JS escreve
    instance.exports.incGlobal();      // wasm incrementa
    console.log(sharedCounter.value);  // 43
  });

Tabela como array de funções (ponteiros de função)

WebAssembly.instantiateStreaming(fetch("table.wasm")).then(({ instance }) => {
  const tbl = instance.exports.tbl;
  console.log(tbl.get(0)()); // 13 — get() devolve a referência; segundo () invoca
});

🔒 Segurança e Práticas Seguras

  • Sandbox e Same-Origin Policy: wasm roda sob as mesmas políticas do browser; sem acesso direto a DOM, rede ou filesystem — toda interação I/O via glue JS.
  • Validação de binários: nunca instancie .wasm de fontes não confiáveis sem WebAssembly.validate(); binários são dados, e um wasm malicioso pode abusar de imports JS (ex.: importar eval-like via glue).
  • Mínimo de superfície de imports: exponha apenas as funções JS estritamente necessárias ao módulo; prefira wrappers que fazem whitelist de operações.
  • Limites de memória: sempre defina maximum para impedir exaustão de memória do cliente; trate RangeError em grow().
  • Memória compartilhada: exija SharedArrayBuffer com COOP/COEP corretos em multi-thread (Atomics para sincronização — data races geram undefined behavior).
  • DoS via wasm: módulos com loops infinitos bloqueiam a main thread — execute em Web Worker quando o processamento for longo.
  • Supply chain: confirme procedência de .wasm de terceiros (SBOM/assinatura), recompile de fonte auditada quando possível.

🧰 Toolchain Beyond MDN

Além do guia MDN, o ecossistema wasm de produção (cf. WebAssembly: The Definitive Guide — Brian Sletten) gira em torno de:

  • WABT (WebAssembly Binary Toolkit): wat2wasm (texto→binário), wasm2wat (binário→texto), wasm-objdump -x (inspeção de seções/símbolos), wasm-interp (REPL de execução), wasm-validate e wasm2c. Use --debug-names em wat2wasm para preservar nomes de funções/locais (Custom section) na depuração.
  • WASI: contract I/O portátil (wasi_snapshot_preview1 — fd_write, proc_exit, environ_get...). Módulos exportam memory + _start. Segurança por capabilities (unforgeable handles): conceda acesso com flags como wasmtime --dir=. (preopened file descriptors). Toolchains: wasi-sdk (clang/C), Rust wasm32-wasi, cargo wasi run.
  • Runtimes fora do browser: Wasmtime, Wasmer, wasm3, WasmEdge — embutíveis como engine de plug-ins/serverless. No Wasmtime (Rust): Engine → Store (unidade de isolamento) → Module → Instance + get_typed_func tipado. Hosts válidos (Hoffman) devem: carregar/validar, expor exports, satisfazer imports, executar e isolar módulos entre si.
  • Emscripten: além do MDN, flags-chave: -s INVOKE_RUN=0 + Module.callMain() (execução atrasada de main), -s MODULARIZE=1 (API Promise-like), -s USE_SDL=2 (port), FS virtual MEMFS (código C que escreve em disco roda no sandbox), emcc --bind (embind para classes C++↔JS), -s SIDE_MODULE=1 (dynamic linking).
  • Threading: memórias shared + Atomics.wait/notify dentro de Web Workers (requer COOP/COEP); confirme suporte via feature testing antes de depender da proposta.

Detalhes completos: references/wasm-definitive-guide.md

🦀 Wasm + Rust no Browser

Padrão idiomático de wasm-bindgen (cf. Programming WebAssembly with Rust — Kevin Hoffman, cap. 4): a macro #[wasm_bindgen] injeta metadados no .wasm; a CLI lê esses metadados e gera o "wrapper bridge" JavaScript — incluindo classes JS a partir de structs Rust.

use wasm_bindgen::prelude::*;

// Import 'window.alert'
#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

// Export a 'hello' function
#[wasm_bindgen]
pub fn hello(name: &str) {
    alert(&format!("Hello, {}!", name));
}

Build manual (livro): cargo build --target wasm32-unknown-unknown + wasm-bindgen target/wasm32-unknown-unknown/debug/bindgenhello.wasm --out-dir .. Em produção prefira wasm-pack build --target web|bundler|nodejs|no-modules (gera pacote npm com .d.ts).

Dicas extras do livro:

  • Imports nominais: #[wasm_bindgen(js_namespace = console)] fn log(s: &str); e classes JS externas via pub type Display; #[wasm_bindgen(method, structural, js_namespace = ROT)].
  • Serialização: com feature serde-serialize + serde, envie structs Rust como JsValue (JsValue::from_serde(&stats).unwrap()) — mais leve que espelhar classes nos dois lados.
  • UI em Rust puro: framework Yew (componentes + Virtual DOM), sem escrever glue JS à mão.

Detalhes completos: references/wasm-with-rust.md

🔗 Integração com Outras Skills

  • lang-c / lang-cpp: código-fonte C/C++ compilado via Emscripten para o alvo wasm.
  • lang-rust: módulos Rust com wasm-pack, wasm-bindgen e alvo wasm32-unknown-unknown.
  • lang-typescript: glue code tipado (WebAssembly.Module, Memory, Instance, exports) e libs como AssemblyScript.
  • frontend-developer: integração de módulos wasm em aplicações web (fetch, workers, perf budget, Core Web Vitals).
  • program-containers: Wasm fora do browser (runtimes Wasmtime/Wasmer, WASI) para workloads server-side e edge.