WebAssembly - Como Desenvolver Sua Primeira Aplicação

O WebAssembly (WASM) é um tipo de código de baixo nível que permite desenvolver aplicações com alto desempenho, como jogos virtuais, realidade virtual e aumentada, edição de foto e vídeo, dentre outras, que requerem desempenho nativo. É focado principalmente para uso na Internet, mas também pode ser utilizado em outros ambientes.[1] Pode-se escrever código em linguagens como C, C++ e Rust e, em seguida, compilá-lo para WebAssembly, que pode ser então convertido pelos navegadores em linguagem de máquina e executado juntamente com JavaScript. [2] O WebAssembly não pretende substituir o JavaScript, mas ser um complemento a ele.

Ele foi criado para ser, dentre outras coisas:

  • Rápido
  • Seguro
  • Portátil
  • Independente de hardware
  • Independente de linguagem
  • Independente de plataforma
  • Fácil de ler e inspecionar

Não é esperado que um programador escreva um código em WebAssembly, uma vez que ele pode ser produzido a partir de linguagens de mais alto nível. Porém, é importante conhecer a ferramenta caso ela seja utilizada. Aqui, será mostrado como criar uma aplicação WebAssembly a partir de um código C++, como criar a partir de Rust e como escrever no formato textual de WebAssembly.

Criando WebAssembly a partir de C++

editar

Inicialmente, é preciso instalar o Emscripten, uma cadeia de ferramentas (toolchain) de compilação para WebAssembly, que permite compilar código em C, C++, Rust e outras linguagens que utilizam LLVM em WebAssembly. [3] Em um diretório, executamos o seguinte comando Git: [4]

git clone https://github.com/emscripten-core/emsdk.git

Em seguida, entramos na pasta criada, pegamos a versão mais recente com o pull do Git e instalamos no nosso computador.

cd emsdk 
git pull
./emsdk install latest
./emsdk activate latest

Obs.: É preciso ter o Python e o Git instalados anteriormente para ser possível instalar o Emscripten.

Após a instalação, é útil ter adicionar os diretórios emsdk e emsdk/upstream/emscripten ao PATH, de modo a permitir a execução dos comandos instalados no shell. Isso pode ser feito com:

source ./emsdk_env.sh
echo source /caminho-para-pasta/emsdk/emsdk_env.sh >> ~/.bashrc

Uma vez que temos o Emscripten instalado, podemos compilar os arquivos que desejarmos. Podemos fazer isso de modo a criar um arquivo HTML que utiliza o nosso código, permitindo testar o nosso exemplo num navegador, ou apenas um arquivo JavaScript, que pode ser incorporado posteriormente a um projeto.

Começamos criando um arquivo C++ (Ex.: Hello World, em um arquivo hello.cpp).

#include <iostream>
using namespace std;

int main() {
    cout << "Hello World" << endl;
    return 0;
}

Agora, compilamos código no terminal, utilizando o comando em++ (ou emcc, para arquivos C):

em++ hello.cpp -o hello.html

Após a compilação, teremos três arquivos novos:

  • Um arquivo hello.html, utilizado para exibir o código no navegador;
  • Um arquivo hello.js, que integra o WebAssembly com as aplicações web;
  • Um arquivo hello.wasm, em WebAssembly

Para verificar o resultado, abrimos o HTML em um navegador por meio de um servidor HTTP. Se fizermos isso diretamente do disco, haverá um erro, como medida de segurança. [5]

Podemos utilizar para essa situação Python, executando:

python3 -m http.server 8000

E depois indo ao endereço http://localhost/8000

Ou utilizando NodeJS, com o comando:

npx http-server /caminho-para-pasta -o -p 8000

E seguindo ao mesmo endereço http://localhost/8000 após isso.

Veremos, então, o resultado apresentado na imagem.

 
Conseguimos executar nosso primeiro WebAssembly!

Se quisermos o arquivo JavaScript apenas, podemos passar apenas o nome do arquivo C++ (sem o arquivo html a ser gerado). considerando o uso de um arquivo hello.cpp, como no caso anterior, podemos usar:

em++ hello.cpp

Isso criará um arquivo .wasm e um arquivo .js, que poderá então ser acoplado a um arquivo HTML.

Para arquivos C o procedimento é idêntico, diferindo apenas no uso do comando emcc no lugar de em++

Criando WebAssembly a partir de Rust

editar

Inicialmente, é preciso instalar o Rust, que vem com o rustc, compilador de Rust; o cargo, gerenciador de pacotes; e o rustup, dentre outras ferramentas. [6] Para compilar o código Rust para WebAssembly, necessita-se do wasm-pack, que pode ser instalado com o gerenciador de pacotes do Rust por meio da instrução: [7]

cargo install wasm-pack

A partir daí, criamos um novo projeto Rust utilizando o cargo.

cargo new hello_world

Dentro dele, teremos uma pasta src com um arquivo main.rs, que já possuirá um código Hello World em Rust, como mostrado abaixo:

fn main() {
    println!("Hello, World!");
}

Inicialmente, precisamos adaptar o arquivo main.rs para rodar como WebAssembly.

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn hello_world() {
    web_sys::console::log_1(&"Hello, World!".into());
}

A primeira linha importa ao código funcionalidades do módulo wasm_bindgen::prelude, que é utilizado na função, que recebe um atributo #[wasm_bindgen]. O web_sys::console::log_1, por sua vez, permite o acesso ao console do JavaScript.

Para podermos compilar para o formato WebAssembly, precisamos do target wasm32-unknown-unknown, que pode ser instalado com o rustup.

rustup target add wasm32-unknown-unknown

E, no arquivo Cargo.toml, adicionamos as linhas:

[dependencies]
wasm-bindgen = "0.2"

Finalmente, podemos criar o arquivo WebAssembly com o comando:

cargo build --target wasm32-unknown-unknown

E o arquivo JavaScript para a interação com o WebAssembly com os comandos:

cargo install wasm-bindgen-cli
wasm-bindgen target/wasm32-unknown-unknown/debug/hello_world.wasm --out-dir ./out --web

Agora podemos utilizar o nosso WebAssembly na web, criando um arquivo HTML com as seguintes linhas:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello, World! em Rust e WebAssembly</title>
</head>
<body>
    <script type="module">
        import init, { hello_world } from "./out/hello_world.js";

        async function run() {
            await init();
            hello_world();
        }
        
        run();
    </script>
</body>
</html>

Como no caso de C++, devemos executar o arquivo utilizando um servidor HTTP, como Python ou NodeJS.

Escrevendo no formato textual

editar

O formato WebAssembly criado anteriormente é um formato binário, difícil de ser entendido por humanos. Porém, há uma representação textual desse formato de forma a ser exibido em editores de texto, chamada WAT (WebAssembly Text Format). [8] Não é necessário utilizar esse formato para a maioria das aplicações, mas é importante caso se queira construir módulos WebAssembly ou compiladores. Aqui não será apresentado um tutorial sobre o formato textual, mas pode-se encontrar um aqui: Understanding_the_text_format

Podemos começar criando um arquivo simple.wat, com o seguinte conteúdo:

(module
  (func $i (import "my_namespace" "imported_func") (param i32))
  (func (export "exported_func")
    i32.const 42
    call $i
  )
)

Para converter um arquivo WAT em WebAssembly binário, podemos utilizar a ferramenta wabt (WebAssembly Binary Toolkit), que inclui dentro dela a wat2wasm, que, converte códigos WAT em WASM, e a wasm2wat, que faz o oposto. O wabt pode ser obtido por meio de seu repositório no GitHub: [9]

$ git clone --recursive https://github.com/WebAssembly/wabt
$ cd wabt
$ git submodule update --init
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .

Em seguida, adicionamos o diretório /wabt/out/clang/Debug ao PATH:

echo export PATH="/wabt/out/clang/Debug:$PATH" >> ~/.bashrc
source ~/.bashrc

Finalmente, podemos converter o arquivo com:

wat2wasm simple.wat -o simple.wasm

Com isso, obtemos um arquivo em formato binário, que pode ser usado em aplicações web.

Referências

editar
  1. https://webassembly.github.io/spec/core/intro/introduction.html
  2. https://developer.mozilla.org/en-US/docs/WebAssembly
  3. https://emscripten.org/docs/introducing_emscripten/about_emscripten.html
  4. https://emscripten.org/docs/getting_started/downloads.html
  5. https://developer.mozilla.org/en-US/docs/WebAssembly/C_to_Wasm
  6. https://www.rust-lang.org/tools/install
  7. https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm
  8. https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format
  9. https://github.com/WebAssembly/wabt