WebAssembly Concepts

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 est谩ndar 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 navegador as铆 como sus pol铆ticas 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 plataforma web?

La plataforma web puede pensar como constituida de dos partes:

  • Una m谩quina virtual (VM por sus siglas en ingl茅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 m谩quina virtual ha sido capaz de cargar solamente JavaScript. Esto nos ha funcionado bien debido a que JavaScript es suficientemente capaz 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 rendimiento 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 v铆deo/im谩genes y algunos otros dominios de cosas que demandan rendimiento como el de c贸digo 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 (tabletas, etc.) pueden amplificar m谩s estos cuellos de botella del desempe帽o.

WebAssembly es un lenguaje distinto a 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 es un lenguaje de alto nivel, flexible y suficientemente expresivo para desarrollar aplicaciones web. Tiene muchas ventajas - es tipado din谩micamente, no necesita el paso de compilarlo, y tiene un gran ecosistema que lo provee de entornos, librer铆as y otras herramientas.
  • WebAssembly es un lenguaje de bajo nivel similar a ensamblador, con un  binario de un tama帽o compacto que se ejecuta con una rendimiento casi nativo, y provee a lenguajes con esquemas de memoria de bajo nivel como C++ y Rust, con un objeto de compilaci贸n que tambi茅n pueden ejecutar en la web. (Notar que WebAssembly tambi茅n tiene el objetivo de soportar a lenguajes de alto nivel con recogedor de basura (garbage-collector) en el futuro).

Con la llegada de WebAssembly en los navegadores, la m谩quina virtual que se mencion贸 anteriormente, cargar谩 y ejecutar谩 dos tipos de c贸digo - JavaScript y WebAssembly.

Los distintos tipos de c贸digo pueden llamarse uno al otro seg煤n necesiten.  WebAssembly JavaScript API  envuelve c贸digo WebAssembly exportado con funciones JavaScript, que pueden ser llamadas normalmente, y WebAssembly puede importar y llamar s铆ncronamente funciones JavaScript. De hecho la unidad b谩sica de c贸digo en WebAssembly se llama m贸dulo y los m贸dulos en WebAssembly son sim茅tricos de muchas maneras a los m贸dulos de ES2015.

Conceptos clave en WebAssembly

Hay varios conceptos claves que son necesarios para entender c贸mo se ejecuta WebAssembly en un navegador. Todos estos conceptos est谩n reflejados uno a uno en WebAssembly JavaScript API.

  • M贸dulo: Representa un binario de WebAssembly que ha sido compilado por el navegador en un ejecutable de c贸digo m谩quina. Un m贸dulo no tiene estado, y es solo eso, como un Blob, puede ser expl铆citamente compartido entre ventanas y workers (por medio de postMessage()). Un m贸dulo declara 'imports' y 'exports' igual que un m贸dulo ES2015module.
  • Memoria: Un conjunto de tama帽o variable que contiene una serie lineal y continua de bytes, que puede ser le铆do o escrito por las instrucciones de memoria de bajo nivel de WebAssembly.
  • Tabla: Una lista tipada de tama帽o variable que contiene referencias (por ejemplo a funciones) que no podr铆an ser guardadas como bytes en memoria (por razones de seguridad o portabilidad).
  • Instancia: Un modulo junto con todos los estados que use durante la ejecuci贸n e incluyendo la memoria, tabla y un conjunto de valores importados. Una instancia es como un m贸dulo ES2015 que ha sido cargado en un global particular con un conjunto particular de 'imports'. 

La API de JavaScript provee a los desarrolladores con la habilidad de crear m贸dulos, memoria, tablas e instancias. Dada una instancia de WebAssembly, el c贸digo de JavaScript puede hacer llamadas s铆ncronas a sus 'exports', que son expuestos, como funciones de JavaScript normal. Funciones de JavaScript arbitrarias pueden ser llamadas s铆ncronamente tambi茅n desde el c贸digo de WebAssembly, pasando dichas funciones de JavaScript como 'imports' a la instancia de WebAssembly. 

Dado que JavaScript tiene un control completo sobre como el c贸digo de WebAssembly es descargado, compilado y ejecutado, los desarrolladores de JavaScript pueden pensar sobre WebAssembly como simplemente una funcionalidad de JavaScript para generar funciones de alto rendimiento.

En el futuro, los m贸dulos de WebAssembly se podr谩n cargar igual que los m贸dulos de ES2015 (usando <script type='module'>), implicando que JavaScript ser谩 capaz de ir a buscar, compilar e importar un m贸dulo de WebAssembly tan f谩cilmente como un m贸dulo de ES2015.

驴C贸mo usar WebAssembly en mi aplicaci贸n?

Previamente se describieron las primitivas que WebAssembly a帽ade a la plataforma Web: un formato binario para el c贸digo y APIs para cargar y ejecutar este c贸digo binario. Ahora se describir谩 c贸mo usar estas primitivas en la pr谩ctica.

El ecosistema de WebAssembly est谩 en sus comienzos; sin duda m谩s herramientas parecer谩n en un futuro. Ahora mismo hay cuatro puntos principales donde comenzar:

  • Portar una aplicaci贸n C/C++ con Emscripten.
  • Escribir o generar WebAssembly directamente a nivel de ensamblador.
  • Escribir una aplicaci贸n en Rust y generar su salida como WebAssembly.
  • Usar AssemblyScript que se parece a TypeScript y se compila a un binario de WebAssembly.

Detallemos m谩s cada una de estas opciones:

Portando desde C/C++

Dos de las muchas opciones para crear c贸digo WASM son o un ensamblador WASM en l铆nea, o Emscripten. Hay varias opciones para un ensamblador WASM en l铆nea, como pueden ser:

Estos son puntos adecuados donde se puede empezar a plantearse desde d贸nde empezar, pero que pueden no tener algunas optimizaciones para Emscripten.

La herramienta Emscripten est谩 disponible para casi cualquier tipo de c贸digo C/C++ y compila en un m贸dulo .wasm, adem谩s del c贸digo "pegamento" necesario en JavaScript, necesario para cargar y ejecutar el m贸dulo, y un documento HTML donde se muestre el resultado del c贸digo.

Resumiendo, el proceso es el que sigue:

  1. Emscripten primero compila C/C++  en clang+LLVM 鈥 un compilador de c贸digo abierto, de C/C++, que por ejemplo se distribuye como parte de XCode en OSX.
  2. Emscripten transforma el resultado de la compilaci贸n de clang+LLVM en un binario .wasm.
  3. Por s铆 mismo, WebAssembly no puede acceder directamente a el DOM; 煤nicamente puede llamar a JavaScript, y pasarle datos enteros o de coma flotante como datos. Luego, para acceder a cualquier API Web, WebAssembly necesita llamar a JavaScript, que entonces har谩 lla llamada a la API Web. Emscripten por lo tanto crea el documento HTML y el c贸digo "pegamento" en JavaScritp necesario para conseguir esto.

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鈥檚 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