WebAssembly - Guia rápido
WebAssembly é uma nova linguagem de programação de computador para a web. O código WebAssembly é um formato binário de baixo nível, compatível com a web e pode ser executado facilmente em navegadores modernos. O tamanho do arquivo gerado é pequeno e carrega e executa mais rápido. Agora você pode compilar linguagens como C, C ++, Rust, etc. para o formato binário e pode ser executado na web como javascript.
Definição de WebAssembly
De acordo com o site oficial da WebAssembly, que está disponível em https://webassembly.org/, ele é definido como WebAssembly (abreviado como Wasm) é um formato de instrução binário para uma máquina virtual baseada em pilha. Wasm foi projetado como um destino portátil para compilação de linguagens de alto nível como C / C ++ / Rust, permitindo a implantação na web para aplicativos cliente e servidor.
Web Assembly não é algo que um desenvolvedor terá que escrever, mas o código é escrito em linguagens como C, C ++, Rust e pode ser compilado para WebAssembly (wasm). O mesmo código pode ser executado dentro dos navegadores da web.
Web Assembly é uma nova linguagem, o código é uma linguagem assembly de baixo nível, mas com seu recurso de formato de texto, o código é legível e a depuração é possível, se necessário.
Objetivos da WebAssembly
Os padrões abertos para WebAssembly são desenvolvidos em um W3C Community Group que inclui representantes de todos os principais navegadores, bem como um W3C Working Group.
Os principais objetivos do WebAssembly são mencionados abaixo -
Faster, Efficient and Portable - O código WebAssembly deve ser executado com mais rapidez em diferentes plataformas, aproveitando o hardware disponível.
Easy to read and debug - WebAssembly, sendo uma linguagem assembly de baixo nível, tem suporte para formato de texto, que permite depurar o código para quaisquer problemas e também reescrever o código, se necessário.
Security - O WebAssembly é seguro para ser executado em navegadores da web, uma vez que cuida das permissões e políticas de mesma origem.
Vantagens do WebAssembly
A seguir estão as vantagens do WebAssembly -
Run is Modern Browsers - O WebAssembly pode ser executado sem problemas nos navegadores modernos disponíveis.
Multiple Language support- Linguagens como C, C ++, Rust, Go agora podem compilar o código para WebAssembly e executar o mesmo em navegadores da web. Portanto, as linguagens que não podiam ser executadas em um navegador agora poderão fazê-lo.
Faster, Efficient and Portable - Devido ao pequeno tamanho do código, ele carrega e executa mais rápido.
Easy to understand- Os desenvolvedores não precisam se esforçar muito para entender a codificação do WebAssembly, pois não precisam escrever o código no WebAssembly. Em vez disso, compile o código em WebAssembly e execute o mesmo na web.
Easy to Debug - Embora o código final esteja em linguagem assembly de baixo nível, você também pode obtê-lo em formato de texto, que é fácil de ler e depurar.
Desvantagens da WebAssembly
A seguir estão as desvantagens do WebAssembly -
O WebAssembly ainda está sendo desenvolvido e é muito cedo para decidir seu futuro.
WebAssembly depende de javascript para interagir com o Document Object Model (DOM).
WebAssembly também é chamado de WASM, que foi introduzido pela primeira vez no ano de 2017. As grandes empresas de tecnologia por trás da origem do WebAssembly são Google, Apple, Microsoft, Mozilla e W3C.
O burburinho é que o WebAssembly vai substituir o Javascript por causa de sua execução mais rápida, mas não é o caso. WebAssembly e Javascript têm o objetivo de trabalhar juntos para resolver os problemas complexos.
Necessidade de WebAssembly
Até agora, temos apenas Javascript que pode funcionar com sucesso dentro do navegador. Existem tarefas muito pesadas que são difíceis de realizar nos navegadores que usam javascript.
Para citar alguns, são aplicativos de reconhecimento de imagem, design auxiliado por computador (CAD), aumento de vídeo ao vivo, RV e realidade aumentada, aplicativos de música, visualização científica e simulação, jogos, edição de imagem / vídeo etc.
WebAssembly é uma nova linguagem com instrução binária que pode carregar e executar mais rapidamente. A tarefa declarada acima pode ser facilmente realizada em linguagens de alto nível como C, C ++, Rust etc. Precisamos de uma maneira que o código que temos em C, C ++, Rust possa ser compilado e possa ser usado em navegadores web. O mesmo é possível com o WebAssembly.
Quando o código WebAssembly é carregado dentro do navegador. Em seguida, o navegador se encarrega de converter para o formato de máquina que pode ser entendido pelos processadores.
Para javascript, o código deve ser baixado, analisado e convertido para o formato de máquina. Muito tempo é gasto nisso e para tarefas pesadas, como mencionamos anteriormente, podem ser muito lentas.
Trabalho de WebAssembly
Linguagens de alto nível como C, C ++ e Rust são compiladas em formato binário, ou seja, .wasm e formato de texto .wat.
O código-fonte escrito em C, C ++ e Rust é compilado para .wasmusando um compilador. Você pode usar o SDK Emscripten para compilar C / C ++ para.wasm.
O fluxo é o seguinte -
O código C / C ++ pode ser compilado para .wasmusando o Emscripten SDK. Mais tarde, o.wasm o código pode ser usado com a ajuda de javascript em seu arquivo html para exibir a saída.
Principais conceitos de WebAssembly
Os conceitos-chave são explicados abaixo -
Módulo
Um módulo é um objeto que é compilado pelo navegador em código de máquina executável. Um módulo é considerado sem estado e pode ser compartilhado entre windows e web workers.
Memória
Memória em WebAssembly, é um arraybufferque contém os dados. Você pode alocar memória usando o Javascript api WebAssembly.memory ().
Mesa
Tabela em WebAssembly é uma matriz digitada, isto é, fora da memória WebAssembly e, principalmente, tem uma referência a funções. Ele armazena o endereço de memória das funções.
Instância
Instância é um objeto que terá todas as funções exportadas que podem ser chamadas de javascript para executar dentro do navegador.
WebAssembly também é chamado de wasm, o que é um aprimoramento do Javascript. Ele é projetado para ser executado dentro de navegadores como javascript e também com nodejs. Acontece que você obtém a saída wasm, quando qualquer linguagem de alto nível como C, C ++, Rust é compilada.
Considere o seguinte programa C -
int factorial(int n) {
if (n == 0)
return 1;
else
return n * factorial(n-1);
}
Faça uso do WasmExplorer, que está disponível emhttps://mbebenita.github.io/WasmExplorer/ para obter o código compilado conforme mostrado abaixo -
O formato de texto WebAssembly para o programa fatorial é conforme indicado abaixo -
(module
(table 0 anyfunc)
(memory $0 1) (export "memory" (memory $0)) (export "factorial" (func $factorial)) (func $factorial (; 0 ;) (param $0 i32) (result i32) (local $1 i32)
(local $2 i32) (block $label$0 (br_if $label$0 (i32.eqz (get_local $0)
)
)
(set_local $2 (i32.const 1) ) (loop $label$1 (set_local $2
(i32.mul
(get_local $0) (get_local $2)
)
)
(set_local $0 (tee_local $1 (i32.add
(get_local $0) (i32.const -1) ) ) ) (br_if $label$1 (get_local $1)
)
)
(return
(get_local $2)
)
)
(i32.const 1)
)
)
Usando a ferramenta Wat2Wasm, você pode visualizar o código WASM, assim como mencionado abaixo -
Os desenvolvedores não devem escrever código no wasm ou aprender a codificar nele, pois ele é gerado principalmente quando você compila linguagens de alto nível.
Stack Machine Model
No WASM, todas as instruções são colocadas na pilha. Os argumentos são removidos e o resultado retornado à pilha.
Considere o seguinte formato de WebAssembly Text que adiciona 2 números -
(module
(func $add (param $a i32) (param $b i32) (result i32)
get_local $a get_local $b
i32.add
)
(export "add" (func $add))
)
O nome da função é $add, leva em 2 parâmetros $a and $b. O resultado é um tipo inteiro de 32 bits. As variáveis locais são acessadas usando get_local e a operação add é executada usando i32.add.
A representação da pilha para adicionar 2 números durante a execução será a seguinte -
Dentro step 1 - A execução de get_local $a instruction, the first parameters i.e., $a é colocado na pilha.
Dentro step 2 - Durante a execução de get_local $b instruction, the second parameters i.e., $b é colocado na pilha.
Dentro step 3- A execução de i32.add irá retirar os elementos da pilha e colocar o resultado de volta na pilha. O valor que fica no final dentro da pilha é o resultado da função $ add.
Neste capítulo, aprenderemos como instalar o Emscripten SDK para compilar C / C ++. Emscripten é uma máquina virtual de baixo nível (LLVM) que pega o bytecode gerado em C / C ++ e o compila em JavaScript que pode ser executado facilmente dentro do navegador.
Para compilar C / C ++ para WebAssembly, precisamos primeiro instalar o Emscripten sdk.
Instale o Emscripten SDK
As etapas para instalar o Emscripten sdk são as seguintes -
Step 1 - Clone o emsdk repo: clone git https://github.com/emscripten-core/emsdk.git.
E:\wa>git clone https://github.com/emscripten-core/emsdk.git
Cloning into 'emsdk'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 1823 (delta 4), reused 4 (delta 2), pack-reused 1809 receiving obje
cts: 99% (1819/1823), 924.01 KiB | 257.00 KiB/s
Receiving objects: 100% (1823/1823), 1.01 MiB | 257.00 KiB/s, done.
Resolving deltas: 100% (1152/1152), done.
Step 2 - Entre no diretório emsdk.
cd emsdk
Step 3 - Para Windows: Execute o seguinte comando.
emsdk install latest
For linux, este comando levará algum tempo para instalar as ferramentas necessárias como java, python etc. Siga o código abaixo mencionado -
./emsdk install latest
Step 4 - Para ativar o SDK mais recente, execute o seguinte comando em seu terminal.
For windows, execute o seguinte comando -
emsdk activate latest
For linux, execute o comando mencionado abaixo -
./emsdk activate latest
Step 5 - Para ativar o PATH e outras variáveis de ambiente, execute o seguinte comando em seu terminal.
For windows, execute o comando -
emsdk_env.bat
For linux, execute o seguinte comando -
source ./emsdk_env.sh
Concluímos a instalação do emsdk e agora podemos compilar o código C ou C ++. A compilação de C / C ++ será feita nos próximos capítulos.
Para compilar qualquer código C ou C ++ a seguir está o comando -
emcc source.c or source.cpp -s WASM=1 -o source.html
A saída fornecerá um arquivo source.html, arquivos source.js e source.wasm. O js terá a api que buscará o source.wasm e você poderá ver a saída quando clicar em source.html no navegador.
Para obter apenas o arquivo wasm, você pode usar o seguinte comando. Este comando fornecerá apenas o arquivo source.wasm.
emcc source.c or source.cpp -s STANDALONE_WASM
Este capítulo discutirá algumas ferramentas fáceis de usar que são muito úteis ao trabalhar com WebAssembly. Vamos começar aprendendo sobre a ferramenta WebAssembly.studio.
WebAssembly.studio
Esta ferramenta permite que você compile C, Rust, Wat to Wasm etc.
Para começar, você pode clicar em Empty C Project, Empty Rust Project, Empty Wat Project para compilar C e Rust para WASM. 5
Possui Build, Run para construir o código e verificar a saída. O botão de download permite que você baixe.wasmarquivo, que pode ser usado para testar dentro do navegador. Esta ferramenta é muito útil para compilar códigos C e Rust e verificar a saída.
WebAssembly Explorer
O WebAssembly Explorer permite que você compile código C e C ++. Consulte o linkhttps://mbebenita.github.io/WasmExplorer/para mais detalhes. A tela que aparecerá após clicar no link é mostrada abaixo -
Você pode escolher a versão C e C ++. O código-fonte de C ou C ++ é escrito aqui -
Depois de clicar no botão Compilar, o formato de texto WebAssembly (WAT) e o código do Firefox x86 Assembly aparecem nos blocos abaixo -
Você pode baixar o .wasm código para testá-lo dentro do navegador.
WASMFiddle
Wasmfiddle ajuda você a compilar o código C para WebAssembly e também testar a saída. Depois de clicar no linkhttps://wasdk.github.io/WasmFiddle/, você verá a seguinte página -
Clique em Build para compilar o código. Você pode baixar o código Wat e Wasm clicando em Wat and Wasm. Para testar a saída, clique no botão Executar.
WASM para WAT
A ferramenta wat2wasmfornecerá o código wasm quando você inserir o formato de texto WebAssembly. Você pode clicar no linkhttps://webassembly.github.io/wabt/demo/wat2wasm/ para demonstração e a tela que aparecerá é fornecida abaixo -
Você pode usar o botão de upload para fazer upload de .wasm e a área de texto exibirá o formato de texto.
WAT para WASM
A ferramenta wat2wasm fornecerá o código wasm quando você inserir o formato de texto WebAssembly. Você pode clicar no linkhttps://webassembly.github.io/wabt/demo/wat2wasm/ para demonstração e a tela que aparecerá é fornecida abaixo -
Esta ferramenta é muito útil, pois ajuda a testar o resultado. Você pode inserir o código WAT e dar uma olhada no código .wasm e também executar o código para ver a saída.
WebAssembly, também chamado de WASM, é um código de baixo nível em formato binário desenvolvido para ser executado dentro de navegadores da maneira mais eficiente. O código WebAssembly é estruturado com os seguintes conceitos -
- Values
- Types
- Instructions
Vamos aprendê-los em detalhes agora.
Valores
Os valores em WebAssembly destinam-se a armazenar dados complexos, como texto, strings e vetores. WebAssembly suporta o seguinte -
- Bytes
- Integers
- Ponto flutuante
- Names
Bytes
Bytes é a forma mais simples de valores com suporte no WebAssembly. O valor está em formato hexadecimal.
For exampleBytes representados como b , também podem receber números naturais n, onde n <256.
byte ::= 0x00| .... |0xFF
Inteiros
No WebAssembly, os inteiros suportados são os fornecidos abaixo -
- i32: inteiro de 32 bits
- i64: inteiro de 64 bits
Ponto flutuante
Em WebAssembly, os números de ponto flutuante suportados são os seguintes -
- f32: ponto flutuante de 32 bits
- f64: ponto flutuante de 64 bits
Nomes
Os nomes são sequências de caracteres, com valores escalares definidos por Unicode, que estão disponíveis no link http://www.unicode.org/versions/Unicode12.1.0/ dado aqui.
Tipos
As entidades em WebAssembly são classificadas como tipos. Os tipos suportados são os indicados abaixo -
- Tipos de valor
- Tipos de Resultado
- Tipos de Função
- Limits
- Tipos de memória
- Tipos de Tabela
- Tipos Globais
- Tipos Externos
Vamos estudá-los um por um.
Tipos de valor
Os tipos de valores suportados pelo WebAssembly são mencionados abaixo -
- i32: inteiro de 32 bits
- i64: inteiro de 64 bits
- f32: ponto flutuante de 32 bits
- f64: ponto flutuante de 64 bits
valtype ::= i32|i64|f32|f64
Tipos de Resultado
Os valores escritos entre colchetes são executados e armazenados dentro dos tipos de resultado. O tipo de resultado é a saída da execução de um bloco de código composto de valores.
resulttype::=[valtype?]
Tipos de Função
Um tipo de função terá um vetor de parâmetros e retorna um vetor de resultados.
functype::=[vec(valtype)]--> [vec(valtype)]
Limites
Limites são o intervalo de armazenamento vinculado aos tipos de memória e tabela.
limits ::= {min u32, max u32}
Tipos de memória
Os tipos de memória lidam com memórias lineares e a faixa de tamanho.
memtype ::= limits
Tipos de Tabela
Tipos de tabela são classificados pelo tipo de elemento atribuído a ela.
tabletype ::= limits elemtype
elemtype ::= funcref
O tipo de tabela depende do limite do tamanho mínimo e máximo atribuído a ela.
Tipos Globais
Tipo global contém as variáveis globais que possuem o valor, que podem mudar ou permanecer as mesmas.
globaltype ::= mut valtype
mut ::= const|var
Tipos Externos
Tipos externos lidam com importações e valores externos.
externtype ::= func functype | table tabletype | mem memtype | global globaltype
Instruções
O código WebAssembly é uma sequência de instruções que segue um modelo de máquina de pilha. Como o WebAssembly segue um modelo de máquina de pilha, as instruções são colocadas na pilha.
Os valores do argumento para uma função, por exemplo, são retirados da pilha e o resultado é colocado de volta na pilha. No final, haverá apenas um valor na pilha e esse é o resultado.
Algumas das instruções comumente usadas são as seguintes -
- Instruções Numéricas
- Instruções Variáveis
Instruções Numéricas
As instruções numéricas são operações realizadas com base em valores numéricos.
For examplenn, mm ::= 32|64
ibinop ::= add|sub|mul|div_sx|rem_sx|and|or|xor
irelop ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx
frelop ::= eq | ne | lt | gt | le | ge
Instruções Variáveis
As instruções de variáveis são sobre como acessar as variáveis locais e globais.
For example
Para acessar variáveis locais -
get_local $a
get_local $b
Para set variáveis locais -
set_local $a
set_local $b
Para access variáveis globais -
get_global $a
get_global $b
Para set variáveis globais -
set_global $a
set_global $b
Este capítulo listará a comparação entre WebAssembly e Javascript.
Javascript é uma linguagem que usamos muito dentro do navegador. Agora, com o lançamento do WebAssembly, também podemos usar o WebAssembly dentro do navegador.
A razão de o WebAssembly ter surgido não é para substituir o javascript, mas para cuidar de certas coisas, que são difíceis de lidar com o javascript.
For example
É difícil fazer com que tarefas como reconhecimento de imagem, aplicativos CAD, aumento de vídeo ao vivo, VR e realidade aumentada, aplicativos de música, visualização e simulação científica, jogos, edição de imagem / vídeo etc. sejam feitas com javascript.
Usando linguagens de alto nível como C / C ++, Rust, que agora podem ser compiladas para WebAssembly, é fácil realizar a tarefa mencionada acima. O WebAssembly gera um código binário fácil de executar dentro do navegador.
Então aqui está a lista de comparação feita entre Javascript e WebAssembly.
Parâmetros | Javascript | WebAssembly |
---|---|---|
Codificação |
Você pode facilmente escrever código em Javascript. O código escrito é legível por humanos e salvo como .js. Quando usado dentro do navegador, você precisa usar uma tag <script>. |
O código pode ser escrito em formato de texto em WebAssembly e é salvo como .wat. É difícil escrever o código no formato .wat. É melhor compilar o código de alguma outra linguagem de alto nível em vez de escrever do início em .wat. Você não pode executar o arquivo .wat dentro do navegador e precisa convertê-lo para .wasm usando os compiladores ou as ferramentas online disponíveis. |
Execução |
O código escrito em javascript quando usado dentro do navegador deve ser baixado, analisado, compilado e otimizado. |
Temos o código WebAssembly em .wasm já compilado e em formato binário. |
Gestão de Memória |
Javascript atribui memória quando, variáveis são criadas e a memória é liberada quando não é usada e é adicionada à coleta de lixo. |
A memória em WebAssembly é um arraybuffer que contém os dados. Você pode alocar memória usando a API Javascript WebAssembly.memory (). A memória do WebAssembly é armazenada em um formato de array, ou seja, um modelo de memória plana que é fácil de entender e executar a execução. A desvantagem do modelo de memória no WebAssembly é -
|
Tempo de carregamento e desempenho |
No caso do javascript, quando chamado dentro do navegador, o arquivo javascript deve ser baixado e analisado. Posteriormente, o analisador converte o código-fonte em bytecode que o mecanismo javascript executa o código no navegador. O mecanismo Javascript é muito poderoso e, portanto, o tempo de carregamento e o desempenho do javascript são muito rápidos em comparação ao WebAssembly. |
Um dos objetivos mais importantes do WebAssembly é ser mais rápido que o JavaScript. O código Wasm gerado a partir de linguagens de alto nível é menor em tamanho e, portanto, o tempo de carregamento é mais rápido. Mas, linguagens como GO, quando compiladas para wasm, produzem um tamanho de arquivo grande para um pequeno trecho de código. O WebAssembly foi projetado para ser mais rápido na compilação e pode ser executado em todos os principais navegadores. O WebAssembly ainda precisa adicionar muitas melhorias em termos de desempenho em comparação ao javascript. |
Depurando |
Javascript é legível por humanos e pode ser depurado facilmente. Adicionar pontos de interrupção ao seu código javascript dentro do navegador permite que você depure o código facilmente. |
O WebAssembly fornece o código em formato de texto, que é legível, mas ainda muito difícil de depurar. O Firefox permite que você visualize o código wasm no formato .wat dentro do navegador. Você não pode adicionar pontos de interrupção em .wat e isso é algo que estará disponível no futuro. |
Suporte de navegador |
Javascript funciona bem em todos os navegadores. |
Todos os principais navegadores da web têm suporte para WebAssembly. |
Neste capítulo, vamos entender como carregar o código wasm e executá-lo no navegador usando a ajuda da API javascript webassembly.
Aqui estão algumas APIs importantes, que usaremos ao longo do tutorial para executar o código wasm.
- API fetch () Browser
- WebAssembly.compile
- WebAssembly.instance
- WebAssembly.instantiate
- WebAssembly.instantiateStreaming
Antes de discutirmos as APIs javascript do WebAssembly, para testar a API e a saída, usaremos o seguinte programa C e o código .wasm gerado a partir do programa c usando o wasm explorer.
Um exemplo para o Programa C é o seguinte -
#include<stdio.h>
int square(int n) {
return n*n;
}
Faremos uso do WASM explorer, para obter o código do wasm -
Baixe o código WASM e use-o para testar o API.
API fetch () Browser
A API fetch () destina-se a carregar o recurso de rede .wasm.
<script>
var result = fetch("findsquare.wasm");
console.log(result);
</script>
Ele retorna uma promessa conforme mostrado abaixo -
Você também pode usar o método XMLHttpRequest para buscar o recurso de rede wasm.
WebAssembly.compile ()
A responsabilidade da api é compilar os detalhes do módulo buscados em .wasm.
Sintaxe
A sintaxe é a seguinte -
WebAssembly.compile(buffer);
Parâmetros
Buffer - Este código de .wasm deve ser convertido em um array digitado ou arraybuffer, antes de fornecer como entrada para compilar.
Valor de retorno
Ele retornará uma promessa de que terá o módulo compilado.
Exemplo
Vejamos um exemplo, que fornece a saída como um módulo compilado usando webAssembly.compile ().
<script>
fetch("findsquare.wasm") .then(bytes => bytes.arrayBuffer())
.then(mod => {
var compiledmod = WebAssembly.compile(mod);
compiledmod.then(test=> {
console.log(test);
})
})
</script>
Resultado
O console.log, quando verificado no navegador, fornecerá os detalhes do módulo compilado -
O módulo possui um objeto construtor com importações, exportações e customSections. Vamos ver a próxima API, para obter mais detalhes do módulo compilado.
WebAssembly.instance
Usando WebAssembly.instance, a API fornecerá a instância executável do módulo compilado que pode ser executado posteriormente para obter a saída.
Sintaxe
A sintaxe é a seguinte -
new WebAssembly.Instance(compiled module)
Valor de retorno
O valor de retorno será um objeto com a função de matriz de exportações que pode ser executada.
Exemplo
<script>
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)).then(module => {
let instance = new WebAssembly.Instance(module);
console.log(instance);
})
</script>
Resultado
A saída nos dará uma matriz de funções de exportação, conforme mostrado abaixo -
Você pode ver a função quadrada, que obtivemos do código C que é compilado.
Para executar a função quadrada, você pode fazer o seguinte -
<script>
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {
let instance = new WebAssembly.Instance(module);
console.log(instance.exports.square(15));
})
</script>
A saída será -
225
WebAssembly.instantiate
Esta API se encarrega de compilar e instanciar o módulo junto.
Sintaxe
A sintaxe é a seguinte -
WebAssembly.instantiate(arraybuffer, importObject)
Parâmetros
arraybuffer - O código de .wasm deve ser convertido em array digitado ou arraybuffer antes de fornecer como entrada para instanciar.
importObject- O objeto de importação deve conter detalhes da memória, funções importadas para serem utilizadas dentro do módulo. Pode ser um objeto de módulo vazio, caso não haja nada a ser compartilhado.
Valor de retorno
Ele retornará uma promessa, que terá detalhes do módulo e da instância.
Exemplo
<script type="text/javascript">
const importObj = {
module: {}
};
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode); console.log(finalcode.instance.exports.square(25));
});
</script>
Resultado
Ao executar o código, você obterá a saída mencionada a seguir.
WebAssembly.instantiateStreaming
Essa API cuida da compilação e também da instanciação do módulo WebAssembly a partir do código .wasm fornecido.
Sintaxe
A sintaxe é a seguinte -
WebAssembly.instantiateStreaming(wasmcode, importObject);
Parâmetros
wasmcode - Resposta da busca ou qualquer outra API que forneça o código wasm e retorne uma promessa.
importObject- O objeto de importação deve conter detalhes da memória, funções importadas para serem utilizadas dentro do módulo. Pode ser um objeto de módulo vazio caso não haja nada a ser compartilhado.
Valor de retorno
Ele retornará uma promessa, que terá detalhes do módulo e da instância.
Exemplo
Um exemplo é discutido abaixo -
<script type="text/javascript">
const importObj = {
module: {}
};
WebAssembly.instantiateStreaming(fetch("findsquare.wasm"), importObj).then(obj => {
console.log(obj);
});
</script>
Ao testar no navegador, você verá um erro -
Para fazê-lo funcionar na extremidade do servidor, você terá que adicionar o tipo MIME application / wasm ou então usar WebAssembly.instantiate (arraybuffer, importObject).
O suporte WebAssembly é adicionado a todos os navegadores mais recentes disponíveis com você hoje, como Chrome, Firefox. A versão 54+ do Firefox em diante oferece um recurso especial para depurar seu código wasm.
Para fazer isso, execute seu código dentro dos navegadores Firefox que chamam wasm. Por exemplo, considere seguir o código C que localiza o quadrado do número.
Um exemplo para o Programa C é o seguinte -
#include<stdio.h>
int square(int n) {
return n*n;
}
Faremos uso do WASM explorer para obter o código do wasm -
Faça download do código WASM e use-o para ver a saída no navegador.
O arquivo html que carrega o wasm é o seguinte -
!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Square function</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
let square;
fetch("findsquare.wasm").then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
square = instance.exports.square(13);
console.log("The square of 13 = " +square);
document.getElementById("textcontent").innerHTML = "The square of 13 = " +square;
});
</script>
</body>
</html>
Abra o navegador Firefox, carregue o arquivo html acima e abra a ferramenta de depuração.
Você deve ver a entrada wasm: // na ferramenta de depuração. Clique em wasm: // e ele mostra o código wasm convertido para o formato .wat conforme mostrado acima.
Você pode dar uma olhada no código da função exportada e pode depurar o código, se surgir algum problema. O Firefox também pretende adicionar pontos de interrupção, para que você possa depurar o código e verificar o fluxo de execução.
Neste capítulo, vamos escrever um programa simples em C e convertê-lo em .wasm e executar o mesmo no navegador para obter o texto "Hello World".
Will make use of wasm explorer tool that will convert the C program to .wasm and will make use of the .wasm inside our .html file.
The Wasm explorer tool which is available at https://mbebenita.github.io/WasmExplorer/ looks as follows −
The C code that we are going to use is as follows −
#include <stdio.h>
char *c_hello() {
return "Hello World";
}
Update the first block in wasm explorer with the C code as shown below −
Click on COMPILE Button to compile to WASM and WAT and Firefox x86 Web Assembly as shown below −
Use the DOWNLOAD to get the .wasm file and save it as firstprog.wasm.
Create a .html file called firstprog.html as shown below −
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Hello World</title>
</head>
<body>
<div id="textcontent"></div>
<script type="text/javascript">
//Your code from webassembly here
</script>
</body>
</html>
Let us now use firstprog.wasm to read the helloworld from the C function c_hello().
Step 1
Use fetch() api to read the firstprog.wasm code.
Step 2
The .wasm code has to be converted into arraybuffer by using ArrayBuffer. The ArrayBuffer object will return you a fixed length binary data buffer.
The code so far will be as follows −
<script type="text/javascript">
fetch("firstprog.wasm") .then(bytes => bytes.arrayBuffer())
</script>
Step 3
The bytes from ArrayBuffer have to be compiled into a module by using WebAssembly.compile(buffer) function.
The code will look like below −
<script type="text/javascript">
fetch("firstprog.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
</script>
Step 4
To get the module we have to call the webassembly.instance constructor as shown below −
<script type="text/javascript">
fetch("firstprog.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
</script>
Step 5
Let us now console the instance to see the details in the browser.
<script type="text/javascript">
fetch("firstprog.wasm") .then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)) .then(module => {
return new WebAssembly.Instance(module)
})
.then(instance => {
console.log(instance);
});
</script>
The console.log details are shown below −
To get the string “Hello World” from the function c_hello(), we need to add some code in javascript.
First, get the memory buffer details as shown below −
let buffer = instance.exports.memory.buffer;;
The buffer value has to be converted to a typed array so that we can read the values from it. The buffer has the string Hello World in it.
To convert to typed call the constructor Uint8Array as shown below −
let buffer = new Uint8Array(instance.exports.memory.buffer);
Now, we can read the value from the buffer in a for - loop.
Let us now get the start point to read the buffer, by calling the function we wrote as shown below −
let test = instance.exports.c_hello();
Now, the test variable has the start point to read our string. WebAssembly does not have anything for string values, everything is stored as integers.
So when, we read the value from the buffer, they will be an integer value and we need to convert it into a string using fromCharCode() in javascript.
The code is as follows −
let mytext = "";
for (let i=test; buffer[i]; i++){
mytext += String.fromCharCode(buffer[i]);
}
Now, when you console mytext you should see the string “Hello World”.
Example
The complete code is as follows −
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Add Function</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
fetch("firstprog.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module)})
.then(instance => {
console.log(instance);
let buffer = new Uint8Array(instance.exports.memory.buffer);
let test = instance.exports.c_hello();
let mytext = "";
for (let i=test; buffer[i]; i++) {
mytext += String.fromCharCode(buffer[i]);
}
console.log(mytext); document.getElementById("textcontent").innerHTML = mytext;
});
</script>
</body>
</html>
We have added a div and the content is added to the div, so the string is displayed on the browser.
Output
The output is mentioned below −
We have seen how to get a .wasm file from c /c++ code. In this chapter, we will convert the wasm into a WebAssembly module and execute the same in the browser.
Let us use the C++ Factorial code as shown below −
int fact(int n) {
if ((n==0)||(n==1))
return 1;
else
return n*fact(n-1);
}
Open Wasm Explorer which is available at https://mbebenita.github.io/WasmExplorer/ as shown below −
The first column has the C++ factorial function, the 2nd column has the WebAssembly text format and the last column has x86 Assembly code.
The WebAssembly Text format −
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0)) (export "_Z4facti" (func $_Z4facti))
(func $_Z4facti (; 0 ;) (param $0 i32) (result i32)
(local $1 i32) (set_local $1
(i32.const 1)
)
(block $label$0
(br_if $label$0
(i32.eq
(i32.or
(get_local $0) (i32.const 1) ) (i32.const 1) ) ) (set_local $1
(i32.const 1)
)
(loop $label$1
(set_local $1 (i32.mul (get_local $0)
(get_local $1) ) ) (br_if $label$1 (i32.ne (i32.or (tee_local $0
(i32.add
(get_local $0) (i32.const -1) ) ) (i32.const 1) ) (i32.const 1) ) ) ) ) (get_local $1)
)
)
The C++ function fact has been exported as “_Z4facti” in WebAssembly Text format.
Click on the download button to download the wasm code and save the file as factorial.wasm.
Now to convert the .wasm code to the module we have to do the following −
Step 1
Convert the .wasm into arraybuffer by using ArrayBuffer. The ArrayBuffer object will return you a fixed-length binary data buffer.
Step 2
The bytes from ArrayBuffer have to be compiled into a module by using WebAssembly.compile(buffer) function.
The WebAssembly.compile() function compiles and returns a WebAssembly.Module from the bytes given.
Here, is the Javascript code that is discussed in Step 1 and 2.
<script type="text/javascript">
let factorial;
fetch("factorial.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
factorial = instance.exports._Z4facti;
console.log('Test the output in Brower Console by using factorial(n)');
});
</script>
Code Explanation
Javascript browser API fetch is used to get the contents of factorial.wasm.
The content is converted to bytes using arrayBuffer().
The module is created from bytes by calling WebAssembly.compile(mod).
The instance of a module is created using new
WebAssembly.Instance(module)
The factorial function export _Z4facti is assigned to variable factorial by using WebAssembly.Module.exports().
Example
Here, is the module.html along with the javascript code −
module.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Module</title>
</head>
<body>
<script>
let factorial;
fetch("factorial.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
factorial = instance.exports._Z4facti;
console.log('Test the output in Browser Console by using factorial(n)');
});
</script>
</body>
</html>
Output
Execute module.html in the browser to see the output −
In this chapter, we are going to discuss the webassembly.validate() function that will validate the .wasm output. The .wasm is available when we compile C, C++ or rust code.
You can make use of the following tools to get the wasm code.
Wasm Fiddler,which is available at https://wasdk.github.io/WasmFiddle/
WebAssembly Explorer, which is available at https://mbebenita.github.io/WasmExplorer/.
Syntax
The syntax is as given below −
WebAssembly.validate(bufferSource);
Parameters
bufferSource − The bufferSource has the binary code that comes from either C, C++ or Rust program. It is in the form of typedarray or ArrayBuffer.
Return Value
The function will return true if the .wasm code is valid and false if not.
Let us try one example. Go to Wasm fiddler, which is available at https://wasdk.github.io/WasmFiddle/, enter C code of your choice and down the wasm code.
The block marked in red is the C code. Click on the Build button at the center to execute the code.
Click on the Wasm , button to download the .wasm code. Save the .wasm at your end and let us use the same for validating.
Example
For Example: validate.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Testing WASM validate()</title>
</head>
<body>
<script>
fetch('program.wasm').then(res => res.arrayBuffer() ).then(function(testbytes) {
var valid = WebAssembly.validate(testbytes);
if (valid) {
console.log("Valid Wasm Bytes!");
} else {
console.log("Invalid Wasm Code!");
}
});
</script>
</body>
</html>
I have hosted the above .html file in wamp server along with the download .wasm file. Here, is the output when you test it in the browser.
Output
The output is the mentioned below −
WebAssembly has the code in a binary format called WASM. You can also get the text format in WebAssembly and it is called WAT (WebAssembly Text format). As a developer you are not supposed to write code in WebAssembly, instead, you have to compile high-level languages like C, C++ and Rust to WebAssembly.
WAT Code
Let us write WAT code stepwise.
Step 1 − The starting point in a WAT is to declare the module.
(module)
Step 2 − Let us now, add some functionality to it in the form of function.
The function is declared as shown below −
(func <parameters/result> <local variables> <function body>)
The function starts with func keyword which is followed by parameters or result.
Parameters/Result
The parameters and the return value as a result.
The parameters can have the following type supported by wasm −
- i32: 32-bit integer
- i64: 64-bit integer
- f32: 32-bit float
- f64: 64-bit float
The params for the functions are written as given below −
- (param i32)
- (param i64)
- (param f32)
- (param f64)
The result will be written as follows −
- (result i32)
- (result i64)
- (result f32)
- (result f64)
The function with parameters and return value will be defined as follows −
(func (param i32) (param i32) (result i64) <function body>)
Variáveis Locais
As variáveis locais são aquelas de que você precisa em sua função. Um valor local para a função será definido da seguinte forma -
(func (param i32) (param i32) (local i32) (result i64) <function body>)
Corpo de Função
O corpo da função é a lógica a ser executada. O programa final será semelhante a este -
(module (func (param i32) (param i32) (local i32) (result i64) <function body>) )
Step 3 - Para ler e definir parâmetros e variáveis locais.
Para ler os parâmetros e variáveis locais, use get_local e set_local comando.
Example
(module
(func (param i32) (param i32) (local i32) (result i64) get_local 0
get_local 1
get_local 2
)
)
De acordo com a assinatura da função,
get_local 0 vai dar o param i32
get_local 1 dará o próximo parâmetro param i32
get_local 2 darei local value i32
Em vez de se referir aos parâmetros e locais usando valores numéricos como 0,1,2, você também pode usar o nome antes dos parâmetros, prefixando o nome com um cifrão.
O exemplo a seguir mostra como usar o nome com parâmetros e locais.
Example
(module
(func
(param $a i32) (param $b i32)
(local $c i32) (result i64) get_local $a get_local $b get_local $c
)
)
Step 4 - Instrução no corpo e execução da função.
A execução no wasm segue a estratégia da pilha. As instruções executadas são enviadas uma a uma na pilha. Por exemplo, a instrução get_local $ a empurra o valor, ele lê na pilha.
A instrução gosta i32.add isso adicionará os elementos da pilha.
(func (param $a i32) (param $b i32) get_local $a
get_local $b
i32.add
)
A instrução para i32.add é ($a+$b). O valor final de i32.add, será colocado na pilha e será atribuído ao resultado.
Se a assinatura da função tiver um resultado declarado, deve haver um valor na pilha no final da execução. Se não houver nenhum parâmetro de resultado, a pilha deve estar vazia no final.
Portanto, o código final junto com o corpo da função será o seguinte -
(module
(func (param $a i32) (param $b i32) (result i32) get_local $a
get_local $b
i32.add
)
)
Step 5 - Fazendo chamada para a função.
O código final com o corpo da função é mostrado na etapa 4. Agora, para chamar a função, precisamos exportá-la.
Para exportar a função, isso pode ser feito com valores de índice como 0,1, mas também podemos fornecer nomes. O nome terá o prefixo $ e será adicionado após a palavra-chave func.
Example
(module
(func $add (param $a i32) (param $b i32) (result i32) get_local $a
get_local $b i32.add
)
)
A função $ add deve ser exportada, usando a palavra-chave export como mostrado abaixo -
(module
(func $add (param $a i32)
(param $b i32) (result i32) get_local $a get_local $b i32.add ) (export "add" (func $add))
)
Para testar o código acima no navegador, você terá que convertê-lo para a forma binária (.wasm). Consulte o próximo capítulo que mostra como converter.WAT to .WASM.
No capítulo anterior, vimos como escrever código em .watou seja, formato de texto WebAssembly. O formato de texto WebAssembly não funcionará diretamente dentro do navegador e você precisa convertê-lo em formato binário, ou seja, WASM para funcionar dentro do navegador.
WAT para WASM
Vamos converter .WAT em .WASM.
O código que vamos usar é o seguinte -
(module
(func $add (param $a i32) (param $b i32) (result i32) get_local $a
get_local $b i32.add ) (export "add" (func $add))
)
Agora vá para WebAssembly Studio, que está disponível em https://webassembly.studio/.
Você deve ver algo assim, quando clicar no link -
Clique em Esvaziar projeto Wat e clique no botão Criar na parte inferior.
Isso o levará a um projeto vazio, conforme mostrado abaixo -
Clique em main.wat e substitua o código existente pelo seu e clique no botão Salvar.
Depois de salvar, clique na construção para converter para .wasm -
Se a compilação for bem-sucedida, você deverá ver o arquivo .wasm criado conforme mostrado abaixo -
Baixe o arquivo main.wasm e use-o dentro do seu arquivo .html para ver a saída conforme mostrado abaixo.
Por exemplo - add.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Add Function</title>
</head>
<body>
<script>
let sum;
fetch("main.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)) .then(module => {
return new WebAssembly.Instance(module)
})
.then(instance => {
sum = instance.exports.add(10,40);
console.log("The sum of 10 and 40 = " +sum);
});
</script>
</body>
</html>
A função add é exportada conforme mostrado no código. Os parâmetros passados são 2 valores inteiros 10 e 40 e retorna a soma deles.
Resultado
A saída é exibida no navegador.
A vinculação dinâmica é o processo no qual dois ou mais módulos serão vinculados durante o tempo de execução.
Para demonstrar como a vinculação dinâmica funciona, usaremos o programa C e o compilaremos em wasm usando Ecmascript sdk.
Então aqui temos -
test1.c
int test1(){
return 100;
}
test2.c
int test2(){
return 200;
}
main.c
#include <stdio.h>
int test1();
int test2();
int main() {
int result = test1() + test2();
return result;
}
No código main.c, ele usa test1 () e test2 (), que são definidos em test1.ce test2.c. Vamos verificar como vincular esses módulos no WebAssembly.
O comando para compilar o código acima é o seguinte: use SIDE_MODULE = 1 para vinculação dinâmica, conforme mostrado no comando.
emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm
Usando WasmtoWat, que está disponível em https://webassembly.github.io/wabt/demo/wasm2wat/, obterá o formato de texto WebAssembly de maintest.wasm.
(module
(type $t0 (func (result i32))) (type $t1 (func))
(type $t2 (func (param i32))) (type $t3 (func (param i32 i32) (result i32)))
(import "env" "stackSave" (func $env.stackSave (type $t0)))
(import "env" "stackRestore" (func $env.stackRestore (type $t2)))
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32))
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
(func $f2 (type $t1)
(call $__wasm_apply_relocs) ) (func $__wasm_apply_relocs (export "__wasm_apply_relocs") (type $t1)) (func $test1 (export "test1") (type $t0) (result i32) (local $l0 i32)
(local.set $l0 (i32.const 100) ) (return (local.get $l0)
)
)
(func $test2 (export "test2") (type $t0) (result i32)
(local $l0 i32) (local.set $l0
(i32.const 200))
(return
(local.get $l0) ) ) (func $__original_main
(export "__original_main")
(type $t0) (result i32) (local $l0 i32)
(local $l1 i32) (local $l2 i32)
(local $l3 i32) (local $l4 i32)
(local $l5 i32) (local $l6 i32)
(local $l7 i32) (local $l8 i32)
(local $l9 i32) (local.set $l0(call $env.stackSave)) (local.set $l1 (i32.const 16))
(local.set $l2 (i32.sub (local.get $l0) (local.get $l1))) (call $env.stackRestore (local.get $l2) ) (local.set $l3(i32.const 0))
(i32.store offset=12 (local.get $l2) (local.get $l3))
(local.set $l4 (call $test1))
(local.set $l5 (call $test2))
(local.set $l6 (i32.add (local.get $l4) (local.get $l5))) (i32.store offset=8 (local.get $l2) (local.get $l6)) (local.set $l7 (i32.load offset=8 (local.get $l2))) (local.set $l8 (i32.const 16))
(local.set $l9 (i32.add (local.get $l2) (local.get $l8))) (call $env.stackRestore (local.get $l9)) (return(local.get $l7))
)
(func $main (export "main") (type $t3)
(param $p0 i32) (param $p1 i32)
(result i32)
(local $l2 i32) (local.set $l2
(call $__original_main)) (return (local.get $l2))
)
(func $__post_instantiate (export "__post_instantiate") (type $t1) (call $f2)) (global $__dso_handle (export "__dso_handle") i32 (i32.const 0))
)
O formato de texto WebAssembly tem algumas importações definidas conforme mostrado abaixo -
(import "env" "stackSave" (func $env.stackSave (type $t0)))
(import "env" "stackRestore" (func $env.stackRestore (type $t2)))
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32))
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
Isso é adicionado durante a compilação do código por emcc (emscripten sdk) e trata do gerenciamento de memória no WebAssembly.
Trabalhando com Importações e Exportações
Agora, para ver a saída, teremos que definir as importações que você pode ver no código .wat -
(import "env" "stackSave" (func $env.stackSave (type $t0)))
(import "env" "stackRestore" (func $env.stackRestore (type $t2)))
(import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32))
(import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
Os termos acima são explicados como segue -
env.stackSave - É usado para gerenciamento de pilha, uma funcionalidade fornecida pelo código compilado emscripten.
env.stackRestore - É usado para gerenciamento de pilha, uma funcionalidade fornecida pelo código compilado emscripten.
env.__memory_base- É um deslocamento global i32 imutável, ou seja, usado em env.memory e reservado para o módulo wasm. O módulo pode utilizar este global no inicializador de seus segmentos de dados, para que sejam carregados no endereço correto.
env.__table_base- É um deslocamento global i32 imutável, ou seja, usado em env.table e reservado para o módulo wasm. O módulo pode usar este global no inicializador de seus segmentos de elementos de tabela, para que sejam carregados no deslocamento correto.
env.memory - Isso terá os detalhes da memória que devem ser compartilhados entre os módulos wasm.
env.table - Isso terá os detalhes da tabela que devem ser compartilhados entre os módulos wasm.
As importações devem ser definidas em javascript da seguinte forma -
var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536});
const importObj = {
env: {
stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => {
throw new Error('overflow');
},
table: new WebAssembly.Table({
initial: 0, maximum: 65536, element: 'anyfunc'
}), __table_base: 0,
memory: wasmMemory, __memory_base: 256
}
};
Exemplo
A seguir está o código javascript que faz uso do importObj dentro de WebAssembly.instantiate.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536});
const importObj = {
env: {
stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => {
throw new Error('overflow');
},
table: new WebAssembly.Table({
initial: 0, maximum: 65536, element: 'anyfunc'
}), __table_base: 0,
memory: wasmMemory, __memory_base: 256
}
};
fetch("maintest.wasm") .then(bytes => bytes.arrayBuffer()) .then(
module => WebAssembly.instantiate(module, importObj)
)
.then(finalcode => {
console.log(finalcode);
console.log(WebAssembly.Module.imports(finalcode.module));
console.log(finalcode.instance.exports.test1());
console.log(finalcode.instance.exports.test2());
console.log(finalcode.instance.exports.main());
});
</script>
</body>
</html>
Resultado
O resultado é o seguinte -
De acordo com o site oficial da WebAssembly.org, que está disponível em https://webassembly.org/docs/security/ o principal objetivo do WebAssembly em termos de segurança é o seguinte -
O modelo de segurança do WebAssembly tem dois objetivos importantes -
Proteja os usuários de módulos maliciosos ou com bugs e
Fornecer aos desenvolvedores primitivos e mitigações úteis para o desenvolvimento de aplicativos seguros, dentro das restrições de (1).
O código compilado, ou seja, WASM de C / C ++ / Rust, não é executado diretamente no navegador e faz uso de APIs de Javascript. O código WASM é colocado em sandbox, ou seja, executado por meio do wrapper da API Javascript e o navegador se comunica com o WASM usando a API.
Aqui está um exemplo de como usar um arquivo .wasm dentro do navegador.
Exemplo - C Program
#include<stdio.h>
int square(int n) {
return n*n;
}
Faremos uso do WASM explorer para obter o código do wasm -
Baixe o código WASM e use-o para testar as APIs.
Exemplo
<script type="text/javascript">
const importObj = {
module: {}
};
fetch("findsquare.wasm")
.then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module,importObj))
.then(finalcode => {
console.log(finalcode); console.log(finalcode.instance.exports.square(25));
});
</script>
Resultado
Você obterá a seguinte saída -
Os objetos de exportação têm uma referência à função a ser chamada. Para chamar o quadrado de função, você terá que fazer o seguinte -
console.log(finalcode.instance.exports.square(25));
Problemas com o código compilado WASM
A seguir estão os problemas com o código compilado WASM -
É difícil verificar se há algum código malicioso sendo inserido, ao compilar o código para o wasm. Não há ferramentas disponíveis no momento para validar o código.
O Wasm é difícil de analisar e o código malicioso / com erros pode ser facilmente executado dentro do navegador.
Neste capítulo, vamos compilar um programa C simples para javascript e executar o mesmo no navegador.
Por exemplo - C Program
#include<stdio.h>
int square(int n) {
return n*n;
}
Fizemos a instalação do emsdk na pasta wa /. Na mesma pasta, crie outra pasta cprog / e salve o código acima como square.c.
Já instalamos o emsdk no capítulo anterior. Aqui, vamos fazer uso do emsdk para compilar o código c acima.
Compile test.c em seu prompt de comando conforme mostrado abaixo -
emcc square.c -s STANDALONE_WASM –o findsquare.wasm
O comando emcc se encarrega de compilar o código, além de fornecer o código .wasm. Usamos a opção STANDALONE_WASM que fornecerá apenas o arquivo .wasm.
Exemplo - findsquare.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Square function</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
let square; fetch("findsquare.wasm").then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod)) .then(module => {
return new WebAssembly.Instance(module)
})
.then(instance => {
square = instance.exports.square(13);
console.log("The square of 13 = " +square);
document.getElementById("textcontent").innerHTML = "The square of 13 = " +square;
});
</script>
</body>
</html>
Resultado
O resultado é como mencionado abaixo -
Neste capítulo, vamos compilar um programa C ++ simples para javascript e executar o mesmo no navegador.
Exemplo
Programa C ++ - Invertendo um determinado número.
#include <iostream>
int reversenumber(int n) {
int reverse=0, rem;
while(n!=0) {
rem=n%10; reverse=reverse*10+rem; n/=10;
}
return reverse;
}
Fizemos a instalação do emsdk na pasta wa /. Na mesma pasta, crie outra pasta cprog / e salve o código acima como reverse.cpp.
Já instalamos o emsdk no capítulo anterior. Aqui, vamos fazer uso do emsdk para compilar o código c acima.
Compile test.c em seu prompt de comando conforme mostrado abaixo -
emcc reverse.cpp -s STANDALONE_WASM –o reverse.wasm
O comando emcc se encarrega de compilar o código, além de fornecer o código .wasm.
Exemplo - reversenumber.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebAssembly Reverse Number</title>
<style>
div {
font-size : 30px; text-align : center; color:orange;
}
</style>
</head>
<body>
<div id="textcontent"></div>
<script>
let reverse;
fetch("reverse.wasm")
.then(bytes => bytes.arrayBuffer())
.then(mod => WebAssembly.compile(mod))
.then(module => {return new WebAssembly.Instance(module) })
.then(instance => {
console.log(instance);
reverse = instance.exports._Z13reversenumberi(1439898);
console.log("The reverse of 1439898 = " +reverse);
document.getElementById("textcontent")
.innerHTML = "The reverse of 1439898 = " +reverse;
});
</script>
</body>
</html>
Resultado
O resultado é o seguinte -
Para obter o código de compilação RUST, usaremos a ferramenta WebAssembly.studio.
Vá para WebAssembly.studio que está disponível em Vá parahttps://webassembly.studio/ e ele exibirá a tela conforme mostrado abaixo -
Clique em Empty Rust Project. Uma vez feito isso, você obterá três arquivos em src / pasta -
Abra o arquivo main.rs e altere o código de sua escolha.
Estou adicionando a seguinte função que adicionará dois números dados -
fn add_ints(lhs: i32, rhs: i32) -> i32 {
lhs+rhs
}
O código disponível em main.rs é o seguinte -
#[no_mangle]
pub extern "C" fn add_one(x: i32) -> i32 {
x + 1
}
Substitua o fn add_one pelo seu como mostrado abaixo -
#[no_mangle]
pub extern "C" fn add_ints(lhs: i32, rhs: i32) -> i32 {
lhs+rhs
}
Em main.js, altere o nome da função de add_one para add_ints
fetch('../out/main.wasm').then(
response =>
response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes)).then(results => {
instance = results.instance;
document.getElementById("container").textContent = instance.exports.add_one(41);
}).catch(console.error);
Substitua instance.exports.add_one por instance.exports.add_ints (100,100)
fetch('../out/main.wasm').then(
response =>
response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes)).then(results => {
instance = results.instance;
document.getElementById("container").textContent = instance.exports.add_ints(100,100)
}).catch(console.error);
Clique no botão build disponível na IU do webassembly.studio para construir o código.
Assim que a construção estiver concluída, clique no botão Executar disponível na IU para ver o resultado -
Obtemos a saída como 200, conforme passamos instance.exports.add_ints (100,100).
Da mesma forma, você pode escrever um programa diferente para a ferrugem e compilá-lo em webassembly.studio.
Go adicionou suporte para WebAssembly da versão 1.1 em diante. Para testá-lo primeiro faça o download, vá.
Vá para o site golang, que está disponível em https://golang.org/dl/e clique em Download Go. De acordo com o seu sistema operacional, baixe e instale o Go.
Uma vez feito isso, escreva um programa simples que adiciona dois números em go.
testnum.go
package main
import "fmt"
func main() {
var a int = 100
var b int = 200
var ret int
ret = sum(a, b)
fmt.Printf( "Sum is : %d\n", ret )
}
/* function returning the max between two numbers */
func sum(num1, num2 int) int {
return num1+num2
}
Para compilar o código acima para wasm, primeiro defina as variáveis de ambiente em Go.
Você terá que executar o seguinte comando -
Set GOOS=js
GOARCH=wasm
Uma vez feito isso, execute o comando abaixo -
go build -o testnum.wasm testnum.go
Você deve obter o arquivo testnum.wasm assim que o comando for executado.
Vamos agora testar o código no navegador. Para fazer isso, precisamos obter o wasm_exec.js, que é instalado com go.
O arquivo wasm_exec.js estará disponível em misc / wasm / folder em go.
Exemplo
Aqui está o código para testgo.html que usa wasm_exec.js e testnum.wasm.
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
</head>
<body>
<script type="text/javascript">
const importObj = {
module: {}
};
const go = new Go();
async function fetchAndInstantiate() {
const response = await fetch("testnum.wasm");
const buffer = await response.arrayBuffer();
const obj = await WebAssembly.instantiate(buffer, go.importObject);
console.log(obj);
go.run(obj.instance);
}
fetchAndInstantiate();
</script>
</body>
</html>
Resultado
O resultado é o seguinte -
Javascript tem um monte de APIs que podem funcionar com o código wasm. A API também é compatível com nodejs.
Instale o NODEJS em seu sistema. Crie um arquivo Factorialtest.js.
Vamos usar o código fatorial C ++ conforme mostrado abaixo -
int fact(int n) {
if ((n==0)||(n==1))
return 1;
else
return n*fact(n-1);
}
Abra o Wasm Explorer, que está disponível em https://mbebenita.github.io/WasmExplorer/ como mostrado abaixo -
A primeira coluna possui a função fatorial C ++, a 2ª coluna possui o formato de texto WebAssembly e a última coluna possui o código Assembly x86.
O formato de texto WebAssembly é o seguinte -
(module
(table 0 anyfunc)
(memory $0 1) (export "memory" (memory $0))
(export "_Z4facti" (func $_Z4facti)) (func $_Z4facti (; 0 ;) (param $0 i32) (result i32) (local $1 i32)
(set_local $1(i32.const 1)) (block $label$0 (br_if $label$0 (i32.eq (i32.or (get_local $0)
(i32.const 1)
)
(i32.const 1)
)
)
(set_local $1 (i32.const 1) ) (loop $label$1 (set_local $1
(i32.mul
(get_local $0) (get_local $1)
)
)
(br_if $label$1
(i32.ne
(i32.or
(tee_local $0 (i32.add (get_local $0)
(i32.const -1)
)
)
(i32.const 1)
)
(i32.const 1)
)
)
)
)
(get_local $1)
)
)
O fato da função C ++ foi exportado como “_Z4facti”No formato WebAssembly Text.
Factorialtest.js
const fs = require('fs');
const buf = fs.readFileSync('./factorial.wasm');
const lib = WebAssembly.instantiate(new Uint8Array(buf)).
then(res => {
for (var i=1;i<=10;i++) {
console.log("The factorial of "+i+" = "+res.instance.exports._Z4facti(i))
}
}
);
Em sua linha de comando, execute o nó de comando factorialtest.js e a saída é a seguinte -
C:\wasmnode>node factorialtest.js
The factorial of 1 = 1
The factorial of 2 = 2
The factorial of 3 = 6
The factorial of 4 = 24
The factorial of 5 = 120
The factorial of 6 = 720
The factorial of 7 = 5040
The factorial of 8 = 40320
The factorial of 9 = 362880
The factorial of 10 = 3628800
O capítulo discute os exemplos com relação ao WebAssembly.
Exemplo 1
A seguir está o exemplo do Programa C para obter o Elemento máximo -
void displaylog(int n);
/* function returning the max between two numbers */
int max(int num1, int num2) {
/* local variable declaration */ int result;
if (num1 > num2)
result = num1;
else result = num2;
displaylog(result);
return result;
}
Compile o código no wasm fiddle e baixe os códigos .wasm e .wat.
Wat code
O código Wat é o seguinte -
(module
(type $FUNCSIG$vi (func (param i32))) (import "env" "displaylog" (func $displaylog (param i32)))
(table 0 anyfunc)
(memory $0 1) (export "memory" (memory $0))
(export "max" (func $max)) (func $max (; 1 ;) (param $0 i32) (param $1 i32) (result i32)
(call $displaylog (tee_local $0
(select
(get_local $0) (get_local $1)
(i32.gt_s (get_local $0) (get_local $1))
)
)
)
(get_local $0)
)
)
Baixe o código .wasm e deixe-nos usar o arquivo .html conforme mostrado abaixo -
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: {
displaylog: n => alert("The max of (400, 130) is " +n)
}
};
fetch("testmax.wasm") .then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode);
console.log(finalcode.instance.exports.max(400,130));
});
</script>
</body>
</html>
Resultado
O resultado é o seguinte -
Exemplo 2
A seguir está o código C ++ para obter a série fibonacci de um determinado número.
#include <iostream>>
void displaylog(int n);
int fibonacciSeries(int number) {
int n1=0,n2=1,n3,i;
for(i=2;i<number;++i) {
n3=n1+n2; displaylog(n); n1=n2; n2=n3;
}
return 0;
}
Estou usando o wasm explorer para compilar o código. Baixe Wat e Wasm e teste o mesmo no navegador.
Você pode usar o código abaixo mencionado -
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: { _Z10displaylogi: n => console.log(n) }
};
fetch("fib.wasm")
.then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode);
console.log(finalcode.instance.exports._Z15fibonacciSeriesi(10));
});
</script>
</body>
</html>
Resultado
O resultado é o seguinte -
Exemplo 3
A seguir está o código Rust para adicionar elementos em uma determinada matriz.
fn add_array(x: i32) -> i32 {
let mut sum = 0;
let mut numbers = [10,20,30]; for i in 0..3 {
sum += numbers[i];
}
sum
}
Vamos usar o WebAssembly Studio para compilar o RUST para o wasm.
Construa o código e baixe o arquivo wasm e execute o mesmo no navegador.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
const importObj = {
env: {
}
};
fetch("add_array.wasm") .then(bytes => bytes.arrayBuffer())
.then(module => WebAssembly.instantiate(module, importObj))
.then(finalcode => {
console.log(finalcode);
console.log(finalcode.instance.exports.add_array());
});
</script>
</body>
</html>
Resultado
O resultado será como fornecido abaixo -