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

Usando a API JavaScript WebAssembly - WebAssembly | MDN

Esta pgina foi traduzida do ingls pela comunidade. Saiba mais e junte-se comunidade MDN Web Docs.

View in English Always switch to English

Usando a API JavaScript WebAssembly

Se voc j compilou um mdulo de outra linguagem usando ferramentas como Emscripten ou carregou e executou o cdigo voc mesmo, a prxima etapa aprender mais sobre como usar os outros recursos da API JavaScript WebAssembly. Este artigo ensina o que voc precisa saber.

Nota: Se voc no estiver familiarizado com os conceitos bsicos mencionados neste artigo e precisar de mais explicaes, leia Conceitos do WebAssembly primeiro e depois volte.

In this article

Alguns exemplos simples

Vamos percorrer alguns exemplos que explicam como usar a API WebAssembly JavaScript e como us-la para carregar um mdulo Wasm em uma pgina da web.

Nota: Voc pode encontrar o cdigo de exemplo em nosso webassembly-examples repositrio do GitHub.

Preparando o exemplo

  1. Primeiro precisamos de um mdulo Wasm! Pegue nosso arquivo simple.wasm e salve uma cpia em um novo diretrio em seu local mquina.

  2. Em seguida, vamos criar um arquivo HTML simples chamado index.html no mesmo diretrio do seu arquivo Wasm (pode usar nosso modelo simples se voc no tiver um facilmente disponvel).

  3. Agora, para nos ajudar a entender o que est acontecendo aqui, vamos ver a representao de texto do nosso mdulo Wasm (que tambm encontramos em Converting WebAssembly format to Wasm):

    wasm
    (module
      (func $i (import "imports" "imported_func") (param i32))
      (func (export "exported_func")
        i32.const 42
        call $i))
    
  4. Na segunda linha, voc ver que a importao tem um namespace de dois nveis a funo interna $i importada de imports.imported_func. Precisamos refletir esse namespace de dois nveis em JavaScript ao escrever o objeto a ser importado para o mdulo Wasm. Crie um elemento <script></script> em seu arquivo HTML e adicione o seguinte cdigo a ele:

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

Transmitindo o mdulo WebAssembly

Uma novidade no Firefox 58 a capacidade de compilar e instanciar mdulos WebAssembly diretamente de fontes subjacentes. Isso obtido usando WebAssembly.compileStreaming() e WebAssembly.instantiateStreaming(). Esses mtodos so mais fceis do que suas contrapartes sem streaming, porque eles podem transformar o cdigo de byte diretamente em instncias Module/Instance, eliminando a necessidade de colocar separadamente o Response em um ArrayBuffer.

Este exemplo (consulte nossa demonstrao instantiate-streaming.html no GitHub e visualize it live tambm) mostra como usar instantiateStreaming() para buscar um mdulo Wasm, importar uma funo JavaScript nele, compil-lo e instanci-lo e acessar sua funo exportada - tudo em uma nica etapa.

Adicione o seguinte ao seu script, abaixo do primeiro bloco:

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

O resultado disso que chamamos nossa funo WebAssembly exportada exported_func, que por sua vez chama nossa funo JavaScript importada imported_func, que registra o valor fornecido dentro da instncia WebAssembly (42) no console. Se voc salvar seu cdigo de exemplo agora e carreg-lo em um navegador compatvel com WebAssembly, ver isso em ao!

Nota: Este um exemplo complicado e prolixo que alcana muito pouco, mas serve para ilustrar o que possvel usar cdigo WebAssembly juntamente com JavaScript em seus aplicativos da web. Como dissemos em outro lugar, o WebAssembly no pretende substituir o JavaScript; os dois, em vez disso, podem trabalhar juntos aproveitando os pontos fortes um do outro.

Carregando nosso mdulo Wasm sem streaming

Se voc no pode ou no quer usar os mtodos de streaming descritos acima, voc pode usar os mtodos sem streaming WebAssembly.compile() / WebAssembly.instantiate() em vez disso.

Esses mtodos no acessam diretamente o cdigo de byte, ento requerem uma etapa extra para transformar a resposta em um ArrayBuffer antes de compilar/instanciar o mdulo Wasm.

O cdigo equivalente ficaria assim:

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

Como visualizar o Wasm nas ferramentas do desenvolvedor

No Firefox 54+, o painel do depurador da ferramenta do desenvolvedor tem funcionalidade para expor a representao de texto de qualquer cdigo Wasm includo em uma pgina da web. Para visualiz-lo, voc pode ir ao Painel do Depurador e clicar na entrada "wasm://".

Painel do depurador de ferramentas do desenvolvedor destacando um mdulo. [Painel do depurador de ferramentas do desenvolvedor destacando um mdulo.]

Alm de visualizar o WebAssembly como texto, os desenvolvedores podem depurar (colocar pontos de interrupo, inspecionar a pilha de chamadas, passo nico, etc.) WebAssembly usando o formato de texto.

Memria

No modelo de memria de baixo nvel do WebAssembly, a memria representada como um intervalo contguo de bytes no digitados chamados Linear Memory que so lidos e escrito por instrues de carregamento e armazenamento dentro do mdulo. Nesse modelo de memria, qualquer load ou store pode acessar qualquer byte em toda a memria linear, o que necessrio para representar fielmente conceitos C/C++ como ponteiros.

Ao contrrio de um programa C/C++ nativo, no entanto, onde o intervalo de memria disponvel abrange todo o processo, a memria acessvel por uma Instncia WebAssembly especfica confinada a um intervalo especfico potencialmente muito pequeno contido por um objeto WebAssembly Memory. Isso permite que um nico aplicativo da Web use vrias bibliotecas independentes cada uma delas usando o WebAssembly internamente para ter memrias separadas totalmente isoladas umas das outras. Alm disso, implementaes mais recentes tambm podem criar memrias compartilhadas, que podem ser transferidas entre os contextos Window e Worker usando postMessage() e usadas em vrios lugares.

Em JavaScript, uma instncia de Memory pode ser considerada como um ArrayBuffer redimensionvel (ou SharedArrayBuffer, no caso de memrias compartilhadas) e, assim como com ArrayBuffers, um nico aplicativo da web pode criar muitos objetos Memory independentes. Voc pode criar um usando o construtor WebAssembly.Memory(), que recebe como argumentos um tamanho inicial e (opcionalmente) um tamanho mximo e um shared propriedade que informa se uma memria compartilhada ou no.

Vamos comear a explorar isso observando um exemplo rpido.

  1. Crie outra nova pgina HTML simples (copie nosso modelo simples) e chame-a de memory.html. Adicione um elemento <script></script> pgina.

  2. Agora adicione a seguinte linha ao topo do seu script, para criar uma instncia de memria:

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

    A unidade de initial e maximum so as pginas WebAssembly elas so fixadas em 64KB de tamanho. Isso significa que a instncia de memria acima tem um tamanho inicial de 640 KB e um tamanho mximo de 6,4 MB.

    A memria WebAssembly expe seus bytes fornecendo um getter/setter de buffer que retorna um ArrayBuffer. Por exemplo, para escrever 42 diretamente na primeira palavra da memria linear, voc pode fazer isso:

    js
    new Uint32Array(memory.buffer)[0] = 42;
    

    Voc pode retornar o mesmo valor usando:

    js
    new Uint32Array(memory.buffer)[0];
    
  3. Tente isso agora em sua demonstrao salve o que voc adicionou at agora, carregue-o em seu navegador e tente inserir as duas linhas acima em seu console JavaScript.

Aumentando a memria

Uma instncia de memria pode ser aumentada por chamadas para Memory.prototype.grow(), onde novamente o argumento especificado em unidades de pginas WebAssembly:

js
memory.grow(1);

Se um valor mximo foi fornecido na criao da instncia de memria, as tentativas de ultrapassar esse mximo geraro uma exceo RangeError. O mecanismo aproveita esses limites superiores fornecidos para reservar memria antecipadamente, o que pode tornar o redimensionamento mais eficiente.

Nota: Como o byteLength de um ArrayBuffer imutvel, aps um Memory.prototype.grow() bem-sucedido operao, o buffer getter retornar um novo objeto ArrayBuffer (com o novo byteLength) e quaisquer objetos ArrayBuffer anteriores sero "desconectados" ou desconectados da memria subjacente para a qual apontaram anteriormente.

Assim como as funes, as memrias lineares podem ser definidas dentro de um mdulo ou importadas. Da mesma forma, um mdulo tambm pode, opcionalmente, exportar sua memria. Isso significa que o JavaScript pode obter acesso memria de uma instncia do WebAssembly criando um novo WebAssembly.Memory e transmitindo-o como uma importao ou recebendo uma exportao de memria (atravs de Instance.prototype.exports).

Exemplo de memria mais envolvida

Vamos tornar as afirmaes acima mais claras observando um exemplo de memria mais envolvido um mdulo WebAssembly que importa a instncia de memria que definimos anteriormente, a preenche com uma matriz de inteiros e os soma. Voc pode encontrar isso em memory.wasm.

  1. faa uma cpia local de memory.wasm no mesmo diretrio de antes.

    Nota: Voc pode ver a representao de texto do mdulo em memory.wat.

  2. Volte para seu arquivo de exemplo memory.html e busque, compile e instancie seu mdulo Wasm como antes adicione o seguinte ao final de seu script:

    js
    WebAssembly.instantiateStreaming(fetch("memory.wasm"), {
      js: { mem: memory },
    }).then((results) => {
      // adicione o cdigo aqui
    });
    
  3. Como este mdulo exporta sua memria, dada uma instncia deste mdulo chamada instance podemos usar uma funo exportada accumulate() para criar e preencher um array de entrada diretamente na memria linear da instncia do mdulo (mem). Adicione o seguinte em seu cdigo, onde indicado:

    js
    const i32 = new Uint32Array(memory.buffer);
    
    for (let i = 0; i < 10; i++) {
      i32[i] = i;
    }
    
    const sum = results.instance.exports.accumulate(0, 10);
    console.log(sum);
    

Observe como criamos a visualizao Uint32Array no buffer do objeto Memory (Memory.prototype.buffer), no na prpria Memria.

As importaes de memria funcionam exatamente como as importaes de funo, apenas objetos de memria so passados como valores em vez de funes JavaScript. As importaes de memria so teis por dois motivos:

  • Eles permitem que o JavaScript busque e crie o contedo inicial da memria antes ou simultaneamente com a compilao do mdulo.
  • Eles permitem que um nico objeto de memria seja importado por vrias instncias de mdulo, o que um bloco de construo crtico para implementar a vinculao dinmica no WebAssembly.

Nota: Voc pode encontrar nossa demonstrao completa em memory.html (veja ao vivo tambm) .

Tabelas

Uma tabela WebAssembly uma matriz redimensionvel de referncias que pode ser acessada por cdigo JavaScript e WebAssembly. Embora a memria fornea uma matriz digitada redimensionvel de bytes brutos, no seguro que as referncias sejam armazenadas em uma memria, pois uma referncia um valor confivel do mecanismo cujos bytes no devem ser lidos ou gravados diretamente pelo contedo por motivos de segurana, portabilidade e estabilidade .

As tabelas possuem um tipo de elemento, que limita os tipos de referncia que podem ser armazenados na tabela. Na iterao atual do WebAssembly, h apenas um tipo de referncia necessria para o cdigo do WebAssembly funes e, portanto, apenas um tipo de elemento vlido. Em iteraes futuras, mais tipos de elementos sero adicionados.

Referncias de funo so necessrias para compilar linguagens como C/C++ que possuem ponteiros de funo. Em uma implementao nativa de C/C++, um ponteiro de funo representado pelo endereo bruto do cdigo da funo no espao de endereo virtual do processo e, portanto, pelas razes de segurana mencionadas acima, no pode ser armazenado diretamente na memria linear. Em vez disso, as referncias de funo so armazenadas em uma tabela e seus ndices, que so inteiros e podem ser armazenados na memria linear, so passados.

Quando chega a hora de chamar um ponteiro de funo, o chamador do WebAssembly fornece o ndice, que pode ento ter limites de segurana verificados na tabela antes de indexar e chamar a referncia de funo indexada. Assim, as tabelas so atualmente um primitivo de baixo nvel usado para compilar recursos de linguagem de programao de baixo nvel com segurana e portabilidade.

Tabelas podem ser modificadas via Table.prototype.set(), que atualiza um dos valores em uma tabela, e Table.prototype.grow(), que aumenta o nmero de valores que podem ser armazenados em uma tabela. Isso permite que o conjunto de funes que podem ser chamadas indiretamente mude com o tempo, o que necessrio para tcnicas de vinculao dinmica. As mutaes so imediatamente acessveis via Table.prototype.get() em JavaScript e para mdulos Wasm.

Um exemplo de tabela

Vejamos um exemplo de tabela simples um mdulo WebAssembly que cria e exporta uma tabela com dois elementos: o elemento 0 retorna 13 e o elemento 1 retorna 42. Voc pode encontrar isso em [table.wasm](https://raw.githubusercontent. com/mdn/webassembly-examples/master/js-api-examples/table.wasm).

  1. Faa uma cpia local de table.wasm em um novo diretrio.

    Nota: Voc pode ver a representao de texto do mdulo em table.wat.

  2. Crie uma nova cpia do nosso modelo HTML no mesmo diretrio e chame-o de table.html.

  3. Como antes, busque, compile e instancie seu mdulo Wasm adicione o seguinte a um elemento <script> na parte inferior do corpo do HTML:

    js
    WebAssembly.instantiateStreaming(fetch("table.wasm")).then((results) => {
      // adicione o cdigo aqui
    });
    
  4. Agora vamos acessar os dados nas tabelas adicione as seguintes linhas ao seu cdigo no local indicado:

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

Este cdigo acessa cada referncia de funo armazenada na tabela por sua vez e as instncias para imprimir os valores que contm no console observe como cada referncia de funo recuperada com um Table.prototype.get(), adicionamos um conjunto extra de parnteses no final para realmente invocar a funo.

Nota: Voc pode encontrar nossa demonstrao completa em table.html (veja ao vivo tambm).

Globais

O WebAssembly tem a capacidade de criar instncias de variveis globais, acessveis a partir de JavaScript e importveis/exportveis em uma ou mais instncias WebAssembly.Module. Isso muito til, pois permite a vinculao dinmica de vrios mdulos.

Para criar uma instncia global WebAssembly de dentro do seu JavaScript, voc usa o construtor WebAssembly.Global(), que se parece com isto:

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

Voc pode ver que isso requer dois parmetros:

  • Um objeto que contm duas propriedades que descrevem a varivel global:

    • value: seu tipo de dados, que pode ser qualquer tipo de dados aceito nos mdulos WebAssembly i32, i64, f32 ou f64.
    • mutvel: um booleano que define se o valor mutvel ou no.
  • Um valor contendo o valor real da varivel. Pode ser qualquer valor, desde que seu tipo corresponda ao tipo de dados especificado.

Ento, como usamos isso? No exemplo a seguir, definimos um global como um tipo i32 mutvel, com valor 0.

O valor do global ento alterado, primeiro para 42 usando a propriedade Global.value, e ento para 43 usando a funo incGlobal() exportada do mdulo global.wasm (isso adiciona 1 a qualquer valor que lhe for atribudo e, em seguida, retorna o novo valor).

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

function assertEq(msg, got, expected) {
  const result =
    got === expected
      ? `SUCESSO! Obteve: ${got}<br>`
      : `FALHA!<br>Obteve: ${got}<br>Esperado: ${expected}<br>`;
  output.innerHTML += `Testando ${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("obtendo valor inicial de wasm", instance.exports.getGlobal(), 0);
    global.value = 42;
    assertEq(
      "obtendo valor atualizado por JS do wasm",
      instance.exports.getGlobal(),
      42,
    );
    instance.exports.incGlobal();
    assertEq("obtendo valor atualizado de JS", global.value, 43);
  },
);

Nota: Voc pode ver o exemplo executando ao vivo no GitHub; consulte tambm o cdigo-fonte.

Multiplicidade

Agora que demonstramos o uso dos principais blocos de construo do WebAssembly, este um bom lugar para mencionar o conceito de multiplicidade. Isso fornece ao WebAssembly uma infinidade de avanos em termos de eficincia arquitetnica:

  • Um mdulo pode ter N instncias, da mesma forma que um literal de funo pode produzir N valores de fechamento.
  • Uma instncia de mdulo pode usar instncias de memria 01, que fornecem o "espao de endereo" da instncia. Verses futuras do WebAssembly podem permitir instncias de memria 0N por instncia de mdulo (consulte Mltiplas memrias).
  • Uma instncia de mdulo pode usar instncias de tabela 01 este o "espao de endereo de funo" da instncia, usado para implementar ponteiros de funo C. Verses futuras do WebAssembly podem permitir 0N instncias de tabela por instncia de mdulo.
  • Uma instncia de memria ou tabela pode ser usada por instncias de mdulo 0N todas essas instncias compartilham o mesmo espao de endereo, permitindo vinculao dinmica.

Voc pode ver a multiplicidade em ao em nosso artigo Compreendendo o formato de texto consulte a seo Tabelas mutantes e vinculao dinmica.

Resumo

Este artigo apresentou os fundamentos do uso da API WebAssembly JavaScript para incluir um mdulo WebAssembly em um contexto JavaScript e fazer uso de suas funes e como usar a memria e as tabelas do WebAssembly em JavaScript. Tambm tocamos no conceito de multiplicidade.

Veja tambm


Web Proxy Viewer  |  New URL  |  Original Page