Una breve introducción al futuro: Web Assembly

¿Qué es Web Assembly?
Web Assembly es una de las nuevas tecnologías más importantes que ha llegado al entorno web en quizá una década, tal vez desde los JIT. Puedes leer más sobre los JIT (compiladores justo a tiempo) en este artículo aquí. Como los JIT, Web Assembly promete mejoras importantes en las aplicaciones web.
En pocas palabras, Web Assembly permite ejecutar en el navegador código distinto de tu código javascript. O, en palabras de Mozilla: "un estándar emergente cuyo objetivo es definir un destino binario para compiladores seguro, portable y eficiente en tamaño y tiempo de carga, que ofrece un rendimiento casi nativo: una CPU virtual para la web."
Bien, Web Assembly me permite ejecutar mi código (rust/c/c++) en el navegador. Es interesante, pero ¿cómo funciona? Para responder, debemos conocer un poco más sobre código máquina, diseño de compiladores y todo lo relacionado. Profundicemos.
¿Cómo funciona Web Assembly?
Cuando se compila un programa escrito en un lenguaje de alto nivel, pasa por una serie de transformaciones hasta llegar al código ensamblador que interpretará la máquina.
Como cada arquitectura admite su propio dialecto ensamblador, para cada lenguaje de alto nivel tendríamos que crear una transformación específica para cada dialecto. Si tenemos n lenguajes y m dialectos, necesitaríamos n \* m formas de transformación.
Como esto no es óptimo, el compilador tiene una representación intermedia del código fuente. Sea cual sea el lenguaje de alto nivel que usemos, la IR será la misma para todos. Esto nos evita trabajar con las otras n \* m transformaciones y nos deja solo una. El compilador tiene un frontend que transforma el código fuente en IR y un backend que la transforma en un dialecto específico que entiende la máquina.

Podríamos decir que Web Assembly es uno de esos dialectos, pero no sería completamente preciso, porque cada dialecto es único y lo determina la arquitectura de una máquina física.
Cuando enviamos código a la máquina de un usuario por internet, no sabemos nada sobre su equipo ni su arquitectura. Por eso Web Assembly es distinto de los otros dialectos ensambladores: es un dialecto virtual que se ejecutará en una CPU virtual.
Lo que distingue a Web Assembly de otros dialectos ensambladores es que es un dialecto virtual ejecutado en una CPU virtual.
El navegador descarga este dialecto de Web Assembly y el código tiene un atajo al dialecto específico de la máquina, sin tiempo de compilación, a diferencia del código javascript y los JIT.

Actualmente, WebAssembly está en la fase de producto mínimo viable, o MVP (puedes leer más sobre ella aquí. Lo admiten los cuatro navegadores principales (Chrome, Firefox, Safari y Opera) y busca sustituir a asm.js, que es lo que se está usando, o se ha usado, para llevar código de lenguajes como c/c++ a la web.
El compilador con mayor soporte para WebAssembly es LLVM, y se pueden conectar muchos frontends y backends a él. Además, es una cadena de herramientas de compilación ampliamente probada.
Configurar LLVM lleva tiempo, pero puedes encontrar las instrucciones aquí.
Para el siguiente ejemplo usaremos WebAssebly fiddle, que puedes probar aquí. Nos permitirá descargar los módulos WebAssembly producidos por los programas en c que escribamos.
Actualmente WebAssembly no puede comunicarse con ninguna API web y el navegador no puede descargar módulos web assembly. Esto se implementará en el futuro; mientras tanto, usaremos javascript para cargar los módulos.
Fibonacci
Para empezar, hagamos fibonacci en C. Necesitaremos python para crear un servidor sencillo y WebAssembly fiddle.
Comencemos creando el archivo fib.c, una implementación sencilla y recursiva de fibonacci.
int fib (int number) {
if (number <= 1) return 1;
return fib(number - 1) + fib(number - 2);
}
Coloca este código en Web Assembly fiddle y pulsa el botón Build. Obtendrás la representación textual de nuestro módulo Web Assembly con sintaxis de expresiones S.

Por ahora no profundizaremos en esta representación ni en su formato, pero puedes consultarlo aquí si quieres.
Descarga el archivo Web Assembly, no el archivo wast 😅, y colócalo junto a fib.c.
Después, creemos el javascript que actúa como código de enlace para cargar nuestro programa en C.
Usaremos la API de Web Assembly para javascript para compilar e instanciar el módulo.
Puedes consultar esta API aquí.
Implementemos una función auxiliar de javascript para obtener el archivo WebAssembly e instanciarlo:
(async (global) => {
async function fetchAndInstantiateWasm (url) {
let binary = await fetch(url)
let bytes = await binary.arrayBuffer()
let module = await WebAssembly.compile(bytes)
let instance = await WebAssembly.instantiate(module)
return instance
}
let module = await fetchAndInstantiateWasm("./fib.wasm")
global.fib = module.exports.fib;
})(window);
En la primera línea de fetchAndInstantiateWasm obtenemos el archivo WebAssembly, que es la representación binaria del módulo. En la segunda, guardamos este binario en un array buffer, un arreglo con números binarios como [1111010, 10001111, 10000101, 00001111, …, 10001111]. En las líneas siguientes devolvemos el módulo compilado e instanciado.
Luego llamamos a fetchAndInstantiateWasm, guardamos el resultado en la variable module y lo colocamos en el ámbito global.
Una instancia de module siempre contiene tus funciones compiladas, en nuestro caso desde C, bajo la clave exports. Nuestro módulo tendrá esta forma:
{
memory: {…}
exports: {
fib: function () { [native code]}
}
}
La clave memory contiene información sobre la memoria de WebAssembly durante la ejecución, compartida con javascript.
Ahora necesitamos llevar esto al navegador. Creamos fib.html y cargamos el módulo js que cargará web assembly.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript" src="./fib.js"></script>
</body>
</html>
Guardamos el archivo junto a los otros. La estructura de nuestro pequeño proyecto debería verse así:

Por último, servimos estos archivos con python: ejecuta python -m SimpleHTTPServer 8080 o, si tienes python 3, python -m http.server 8080.
Abre el navegador y ve a localhost:8080, entra a fib.html, abre la consola y... ¡listo! Deberías tener una función nativa fib global.

Un buen repositorio para seguir leyendo sobre Web Assembly es este, que tiene mucha información bastante actualizada.