Conceitos do WebAssembly

Este artigo explica os conceitos por trás de como o WebAssembly funciona, incluindo seus objetivos, os problemas que ele resolve e como ele roda dentro do mecanismo de renderização de um browser.

O que é WebAssembly?

WebAssembly é um novo tipo de código que pode ser executado nos browsers modernos e fornece novas carecterísticas e maiores ganhos em performance. Ele não foi pensado incialmente para ser escrito à mão, ao contrário, foi pensado para ser um compilador-alvo eficiente para linguagens de baixo nível como C, C++, Rusty, etc.

Ele possui implicações enormes para a plataforma web — ele fornece um caminho para executar códigos escritos em múltiplas linguagens na web, com velocidades próximas às das linguagens nativas com apps sendo executados na web, onde antes não era possível.

Além disso, você não precisa saber como criar código WebAssembly para tirar vantagem dele. Os módulos WebAssembly podem ser importados num app web (ou Node.js), expondo funções do WebAssembly para uso via JavaScript. Os frameworks de JavaScript poderiam usar o WebAssembly para conseguir enormes vantagens de performance e novas características enquanto disponibiliza funcionalidades aos desenvolvedores web.

Objetivos do WebAssembly

WebAssembly está sendo criado em código aberto dentro do W3C WebAssembly Community Group com os seguintes objetivos:

  • Ser rápido, eficiente e móvel — o código WebAssembly pode ser executado a velocidades próximas de nativas entre diferentes plataformas, tirando vantagem das capacidades comuns de hardware.
  • Ser compreensível e debuggable — WebAssembly é uma linguagem assembly de baixo nível, mas ela tem um formato de texto compreensível para os humanos (especificação pela qual ainda está sendo finalizado) que permite que o código seja escrito, visto e debugado à mão.
  • Manter a segurança — WebAssembly é especificado para ser executado num ambiente seguro e controlado. Como outros códigos web, ele reforçará as mesmas políticas de origem e permissões dos browsers.
  • Não quebrar a web — WebAssembly foi pensado de maneira que ele seja executado em harmonia com outras tecnologias web, mantendo a compatibilidade retroativa.

Nota: WebAssembly também terá usos fora dos ambientes web e JavaScript (veja Non-web embeddings).

Como o WebAssembly se encaixa na plataforma web?

A plataforma web pode ser dividida em duas partes:

  • Uma máquina virtual (VM) que executa o código de uma aplicação Web, por exemplo códigos JavaScript que enriquecem suas aplicações.
  • Um conjunto de Web APIs que um Web app pode invocar para controlar funcionalidades web browser/device (dispositivo) e fazer as coisas acontecerem (DOM, CSSOM, WebGL, IndexedDB, Web Audio API, etc.).

Historicamente, a VM tem tido permissão para carregar apenas JavaScript. Isto tem funcionado bem para nós, já que o JavaScript é poderoso o suficiente para resolver a maioria dos problemas da Web atualmente. No entanto, temos enfrentado problemas de performance, quando tentamos usar o JavaScript para tarefas mais intensivas como games em 3D, realidades virtual e aumentada, visão de computador, edição de imagens ou videos e um sem número de outros domínios que demandam performance nativa (veja Casos de uso WebAssembly para mais ideias).

Adicionalmente, o custo de baixar, parsear e compilar aplicações JavaScript muito grandes, é proibitivo. Plataformas mobile e outras de recursos restritos, podem ampliar ainda mais estes gargalos de performance.

WebAssembly é uma linguagem diferente do JavaScript, mas não foi pensada para ser sua substituta. Ao contrário, foi pensada para complementar e trabalhar lado a lado com o JavaScript, permitindo aos desenvolvedores web tirarem vantagem dos pontos fortes das duas linguagens:

  • JavaScript é uma linguagem de alto nível, flexível e expressiva o suficiente para escrever aplicações web. Ela tem muitas vantagens — é dinamicamente tipada, não ncessita ser compilada e tem um enorme ecossistema que disponibiliza poderosos frameworks, bibliotecas (libs) e outros recursos.
  • WebAssembly é uma linguagem de baixo nível do tipo assembly com um formato binário compacto, que é executado com performance próximo à nativa, que disponibiliza linguagens com modelos de memória de baixo nível como C++ e Rusty, com uma compilação-alvo, assim podendo ser executados na web. (Note que o WebAssembly tem uma meta futura de suporte de alto nível para linguagens com modelos de memória garbage-collected.)

Com o advento do WebAssembly nos browsers, a máquina virtual a que nos referimos antes, vai carregar e executar dois tipos de código — JavaScript E WebAssembly.

Os diferentes tipos de códigos podem invocar um ao outro conforme necessário — o WebAssembly JavaScript API encapsula código WebAssembly exportado com funções JavaScript que podem ser invocados normalmente, e código WebAssembly pode importar de forma síncrona, funções normais de JavaScript. Na verdade, a unidade básica do código WebAssembly é chamado de módulo, e módulos WebAssembly são semelhantes em vários níveis aos módulos de ES2015.

Conceitos-chave do WebAssembly

Existem diversos conceitos-chave que precisam ser compreendidos, sobre como o WebAssembly é executado no browser. Todos estes conceitos são refletidos 1:1 na WebAssembly JavaScript API.

  • Módulo: Representa o binário do WebAssembly que foi compilado pelo browser, em código executável pela máquina. Um módulo não tem estado e, tal qual um Blob, pode ser explicitamente compartilhado entre janelas e workers (via postMessage()). Um Módulo declara imports e exports, assim com um módulo ES2015.
  • Memória: Um ArrayBuffer redimensionável que contém um array linear de bytes, lidos e escritos pelas intruções de memória de baixo nível do WebAssembly.
  • Tabela: Um array tipado de referências redimensionável (por exemplo para funções) que, em outra situação, não poderia ser armazenado como bytes puros na Memória (por questões de segurança e portabilidade).
  • Instância: Um Módulo pareado com todo o estado utilizado durante a execução, incluindo uma Memória, Tabela e um conjunto de valores importados. Uma Instância é como um módulo ES2015 que foi carregado em um global específico com um conjunto de importações específico.

A API JavaScript disponibiliza aos desenvolvedores a habilidade de criar módulos, memórias, tabelas e instâncias. Dada uma instância WebAssembly, o código JavaScript pode, de forma síncrona, invocar seus exports, os quais são expostos como funções normais de JavaScript. Funções arbitrárias de JavaScript também podem ser invocadas de forma síncrona pelo código WebAssembly, sendo passados nestas funções JavaScript como imports para um instância WebAssembly.

Uma vez que o JavaScript tem controle total sobre como o código WebAssembly é carregado, compilado e executado, desenvolvedores JavaScript poderiam até pensar no WebAssembly como apenas uma ferramenta do JavaScript, para geração de funções de alta performance de forma eficiente.

No futuro, módulos WebAssembly serão carregáveis assim como módulos ES2015 (usando <script type='module'>), o que quer dizer que o JavaScript será capaz de buscar, compilar e importar um módulo WebAssembly tão facilmente quanto um módulo ES2015.

Como eu utilizo o WebAssembly em minha aplicação?

Acima nós falamos sobre primitivas puras que o WebAssembly adiciona à plataforma Web: um formato binário para código e APIs para carregar e executar esse código binário. Agora vamos falar sobre como nós podemos utilizar estas primitivas na prática.

O ecossistema do WebAssembly está em seu estado inicial: sem dúvidas, mais ferramentas vão aparecer. Neste momento, existem quatro pontos de entrada principais: 

  • Portando uma aplicação C/C++ com Emscripten.
  • Escrevendo ou gerando WebAssembly diretamente no nível do assembly.
  • Escrevendo uma aplicação Rust e definindo WebAssembly como seu output.
  • Usando AssemblyScript que se parece com TypeScript e compila ao binário WebAssembly.

Vamos falar sobre estas opções:

Portando a partir de C/C++

Duas das muitas opções para criar códigos WASM, são um assembler wasm online ou Emscripten. Existem algumas opções de assembler wasm online, como:

Estes são excelentes recursos para pessoas que estão tentando descobrir por onde começar, mas não possuem as otimizações e ferramentas do Emscripten.

A ferramenta Emscripten é capaz de pegar qualquer código fonte C/C++ e compilá-lo para um módulo .wasm, além da "cola" JavaScript necessária para carregar e executar o módulo, e um documento HTML para exibir os resultados do código.

De maneira resumida, o processo funciona da seguinte maneira:

  1. Emscripten first feeds the C/C++ into clang+LLVM — a mature open-source C/C++ compiler toolchain, shipped as part of XCode on OSX for example.
  2. Emscripten transforms the compiled result of clang+LLVM into a .wasm binary.
  3. By itself, WebAssembly cannot currently directly access the DOM; it can only call JavaScript, passing in integer and floating point primitive data types. Thus, to access any Web API, WebAssembly needs to call out to JavaScript, which then makes the Web API call. Emscripten therefore creates the HTML and JavaScript glue code needed to achieve this.

Note: There are future plans to allow WebAssembly to call Web APIs directly.

The JavaScript glue code is not as simple as you might imagine. For a start, Emscripten implements popular C/C++ libraries like SDL, OpenGL, OpenAL, and parts of POSIX. These libraries are implemented in terms of Web APIs and thus each one requires some JavaScript glue code to connect WebAssembly to the underlying Web API.

So part of the glue code is implementing the functionality of each respective library used by the C/C++ code. The glue code also contains the logic for calling the above-mentioned WebAssembly JavaScript APIs to fetch, load and run the .wasm file.

The generated HTML document loads the JavaScript glue file and writes stdout to a <textarea>. If the application uses OpenGL, the HTML also contains a <canvas> element that is used as the rendering target. It’s very easy to modify the Emscripten output and turn it into whatever web app you require.

You can find full documentation on Emscripten at emscripten.org, and a guide to implementing the toolchain and compiling your own C/C++ app across to wasm at Compiling from C/C++ to WebAssembly.

Writing WebAssembly directly

Do you want to build your own compiler, or your own tools, or make a JavaScript library that generates WebAssembly at runtime?

In the same fashion as physical assembly languages, the WebAssembly binary format has a text representation — the two have a 1:1 correspondence. You can write or generate this format by hand and then convert it into the binary format with any of several WebAssemby text-to-binary tools.

For a simple guide on how to do this, see our Converting WebAssembly text format to wasm article.

Writing Rust Targetting WebAssembly

It is also possible to write Rust code and compile over to WebAssembly, thanks to the tireless work of the Rust WebAssembly Working Group. You can get started with installing the necessary toolchain, compiling a sample Rust program to a WebAssembly npm package, and using that in a sample web app, over at our Compiling from Rust to WebAssembly article.

Using AssemblyScript

For web developers which want to try WebAssembly without needing to learn the details of C or Rust, AssemblyScript will be the best option. It generates a small bundle and it's performance is slightly slower compared to C or Rust. You can check it's documentation on https://docs.assemblyscript.org/.

Sumário

Este artigo apresentou a você uma explicação sobre o que é o WebAssembly, porque ele é tão útil, como ele se encaixa na web e como você pode se utilizar dele.

Veja também