WebAssembly Concepts

This translation is incomplete. Please help translate this article from English

En este artículo se explica los conceptos detrás de cómo funciona WebAssembly, sus objetivos, los problemas que resuelve, y como se ejecuta dentro del motor de renderizado de un navegador.

¿Qué es WebAssembly?

WebAssembly es un nuevo tipo de código que puede ser ejecutado en navegadores modernos, y provee nuevas funcionalidades y mejoras en rendimiento. No está pensado para ser ser escrito a mano, si no que está diseñado par ser un objeto final de compilación para lenguajes de bajo nivel como C, C++, Rust, etc.

Esto tiene enormes implicaciones para la plataforma web -- presenta un medio para ejecutar código escrito en múltiples lenguajes en la web, haciendo que una aplicación web, se ejecute casi a la misma velocidad de código nativo, algo que previamente se podía hacer. 

Lo que es más, no es necesario conocer como se crea código WebAssembly para usar sus ventajas. Los módulos de WebAssembly pueden importase en una aplicación web (o Node.js), exponiendo funciones de WebAssembly para ser usadas mediante Javascript. Los entornos de Javascript pueden usar WebAssembly para obtener grandes mejoras de rendimiento y nuevas funcionalidades y ser fácilmente disponibles por los desarrolladores Web.  

Objetivos de WebAssembly

WebAssembly ha sido creado por como un estandar abierto dentro de W3C WebAssembly Community Group con los siguientes objetivos:

  • Ser rápido, eficiente y portable — el código WebAssembly puede ejecutarse se puede ejecutar a una velocidad casi nativa en diferentes plataformas aprovechando las capacidades comunes del hardware.
  • Ser legible y depurable — WebAssembly es un lenguaje ensamblador de bajo nivel, pero tiene un formato de texto que puede ser entendido por las personas (la especificación aún se está terminando) lo cual permite al código ser escrito, visualizado y depurado a mano.
  • Mantenerse seguro — WebAssembly se especifica para ser ejecutado de manera segura en un entorno de ejecución de espacio aislado (sandbox).Como otros códigos web, reforzará el propio origen del navegadior así como sus politicas de seguridad.
  • No quebrantar a la red — WebAssembly está diseñado de tal forma que se lleve bien con otras tecnologías web y mantenga compatibilidad con versiones anteriores.

Nota: WebAssembly tendrá también usos fuera de la red y de los ambientes JavaScript (vea Incrustaciones no-web).

¿Cómo se inserta WebAssembly dentro de la platafroma web?

La plataforma web puede pensar como constituida de doa partes:

  • Una máquina virtual (VM por sus siglas en ingés) que ejecuta el código de la aplicación Web p.e. el código JavaScript que potencia sus aplicaciones.
  • Un conjunto de interfaces Web (Web APIs) que la aplicación Web puede llamar para controlar la funcionalidad del navegador/dispositivo web y hace que las cosas sucedan (DOM, CSSOM, WebGL, IndexedDB, Web Audio API, etc.).

Históricamente, la MV ha sido capaz de cargar solamente JavaScript. Esto nos ha funcionado bien debido a que JavaScript es suficientemente poderoso para resolver la mayor parte de los problemas que las personas tienen en la Web hoy día. Sin embargo hemos llegado a tener problemas de desempeño cuando se trata de usar JavaScript para casos de uso más intensos como juegos 3D, Realidad Virtual y Aumentada, visión por computadora, edición de video/imágenes y algunos otros dominios de cosas que demandan desempeño nativo (vea Casos de Uso WebAssembly para más ideas).

Adicionalmente, el costo de descargar, analizar gramaticalmente (parsing) y compilar aplicaciones JavaScript muy grandes resulta prohibitivo. Plataformas en móviles (celulares y otros) y otras de recursos limitados (tablets, etc.) pueden amplificar más estos cuellos de botella del desempeño.

WebAssembly se presenta como un lenguaje derivador a partir de JavaScript, aunque no se pretende sea un reemplazo. En lugar de ello, se diseña para complementar y trabajar en conjunto con JavaScript, permitiendo a los desarrolladores web tener una ventaja sobre las fortalezas de ambos lenguajes:

  • JavaScript is a high-level language, flexible and expressive enough to write web applications. It has many advantages — it is dynamically typed, requires no compile step, and has a huge ecosystem that provides powerful frameworks, libraries, and other tools.
  • WebAssembly is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages with low-level memory models such as C++ and Rust with a compilation target so that they can run on the web. (Note that WebAssembly has the high-level goal of supporting languages with garbage-collected memory models in the future.)

With the advent of WebAssembly appearing in browsers, the virtual machine that we talked about earlier will now load and run two types of code — JavaScript AND WebAssembly.

The different code types can call each other as required — the WebAssembly JavaScript API wraps exported WebAssembly code with JavaScript functions that can be called normally, and WebAssembly code can import and synchronously call normal JavaScript functions. In fact, the basic unit of WebAssembly code is called a module and WebAssembly modules are symmetric in many ways to ES2015 modules.

WebAssembly key concepts

There are several key concepts needed to understand how WebAssembly runs in the browser. All of these concepts are reflected 1:1 in the WebAssembly JavaScript API.

  • Module: Represents a WebAssembly binary that has been compiled by the browser into executable machine code. A Module is stateless and thus, like a Blob, can be explicitly shared between windows and workers (via postMessage()). A Module declares imports and exports just like an ES2015module.
  • Memory: A resizable ArrayBuffer that contains the linear array of bytes read and written by WebAssembly’s low-level memory access instructions.
  • Table: A resizable typed array of references (e.g. to functions) that could not otherwise be stored as raw bytes in Memory (for safety and portability reasons).
  • Instance: A Module paired with all the state it uses at runtime including a Memory, Table, and set of imported values. An Instance is like an ES2015 module that has been loaded into a particular global with a particular set of imports.

The JavaScript API provides developers with the ability to create modules, memories, tables, and instances. Given a WebAssembly instance, JavaScript code can synchronously call its exports, which are exposed as normal JavaScript functions. Arbitrary JavaScript functions can also be synchronously called by WebAssembly code by passing in those JavaScript functions as the imports to a WebAssembly instance.

Since JavaScript has complete control over how WebAssembly code is downloaded, compiled and run, JavaScript developers could even think of WebAssembly as just a JavaScript feature for efficiently generating high-performance functions.

In the future, WebAssembly modules will be loadable just like ES2015 modules (using <script type='module'>), meaning that JavaScript will be able to fetch, compile, and import a WebAssembly module as easily as an ES2015 module.

How do I use WebAssembly in my app?

Above we talked about the raw primitives that WebAssembly adds to the Web platform: a binary format for code and APIs for loading and running this binary code. Now let’s talk about how we can use these primitives in practice.

The WebAssembly ecosystem is at a nascent stage; more tools will undoubtedly emerge going forward. Right now, there are four main entry points:

  • Porting a C/C++ application with Emscripten.
  • Writing or generating WebAssembly directly at the assembly level.
  • Writing a Rust application and targeting WebAssembly as its output.
  • Using AssemblyScript which looks similar to TypeScript and compiles to WebAssembly binary.

Let’s talk about these options:

Porting from C/C++

Two of the many options for creating WASM code are an online wasm assembler or Emscripten. There are a number of online WASM assembler choices, such as:

These are great resources for people who are trying to figure out where to start, but they lack some of the tooling and optimizations of Emscripten.

The Emscripten tool is able to take just about any C/C++ source code and compile it into a .wasm module, plus the necessary JavaScript "glue" code for loading and running the module, and an HTML document to display the results of the code.

In a nutshell, the process works as follows:

  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 Targeting 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 who 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 its documentation on https://assemblyscript.org/.

Summary

This article has given you an explanation of what WebAssembly is, why it is so useful, how it fits into the web, and how you can make use of it.

See also