WebAssembly e TypeScript: técnicas avançadas de integração

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução à integração avançada de WebAssembly e TypeScript



WebAssembly (Wasm) revolucionou o desenvolvimento web, permitindo executar código em velocidade próxima à nativa diretamente no navegador. No entanto, em nível avançado, os desenvolvedores enfrentam o desafio: como combinar efetivamente a tipagem estrita do TypeScript com os recursos de baixo nível do Wasm? Neste artigo, exploraremos técnicas que vão além dos exemplos básicos: gerenciamento manual de memória, criação de wrappers com tipagem completa, otimização de chamadas e compilação de projetos complexos.



O material é destinado a desenvolvedores já familiarizados com os fundamentos do Wasm e TypeScript. Não vamos nos deter na compilação de "Hello World" — focaremos em problemas reais: trabalho com buffers, estruturas de dados, multithreading e profiling.



1. Gerenciamento avançado de memória: alocação manual e estruturas



O exemplo básico de exportação de uma função do Wasm para TypeScript geralmente trabalha com primitivos. Em nível avançado, você precisará transmitir dados complexos: strings, arrays de estruturas, imagens. Vejamos como isso é feito através de WebAssembly.Memory.



1.1. Alocação e liberação de memória



Suponha que temos um módulo Wasm, escrito em AssemblyScript ou C, que exporta as funções malloc e free. Em TypeScript, criamos um wrapper:



// types.tsexport interface WasmMemory {  malloc(size: number): number;  free(ptr: number): void;  memory: WebAssembly.Memory;}

// memory-manager.tsexport class MemoryManager { private wasm: WasmMemory;

constructor(wasm: WasmMemory) { this.wasm = wasm; }

allocateString(str: string): number { const encoder = new TextEncoder(); const bytes = encoder.encode(str); const ptr = this.wasm.malloc(bytes.length + 1); // +1 para o terminador nulo const view = new Uint8Array(this.wasm.memory.buffer); view.set(bytes, ptr); view[ptr + bytes.length] = 0; // terminador nulo return ptr; }

readString(ptr: number): string { const view = new Uint8Array(this.wasm.memory.buffer); let end = ptr; while (view[end] !== 0) end++; const bytes = view.slice(ptr, end); return new TextDecoder().decode(bytes); }

freeString(ptr: number): void { this.wasm.free(ptr); }}


Essa abordagem garante que a memória não vaze e que o TypeScript controle totalmente o ciclo de vida dos dados.



1.2. Trabalhando com estruturas de dados



Para transmitir objetos (por exemplo, um ponto com coordenadas {x: f32, y: f32}), use DataView:



interface Point { x: number; y: number; }

export class PointManager { private wasm: WasmMemory; private readonly SIZE = 8; // 2 float32

constructor(wasm: WasmMemory) { this.wasm = wasm; }

createPoint(p: Point): number { const ptr = this.wasm.malloc(this.SIZE); const view = new DataView(this.wasm.memory.buffer); view.setFloat32(ptr, p.x, true); view.setFloat32(ptr + 4, p.y, true); return ptr; }

readPoint(ptr: number): Point { const view = new DataView(this.wasm.memory.buffer); return { x: view.getFloat32(ptr, true), y: view.getFloat32(ptr + 4, true) }; }}


Importante: sempre verifique o alinhamento da memória. Wasm requer alinhamento de 4 bytes para f32.



2. Tipagem de importações e exportações Wasm em TypeScript



Sem tipagem, trabalhar com Wasm é perigoso: você pode passar um número em vez de um ponteiro e obter um segfault. Use interfaces estritas.



2.1. Geração de tipos a partir do Wasm



A ferramenta wasm-objdump ou wasm2wat permite obter as assinaturas das funções. Para um projeto em AssemblyScript, use um gerador de tipos:



// wasm-types.d.tsdeclare namespace WasmModule {  interface Exports {    add(a: number, b: number): numb                

Blogs

Book Recommendations