A lo largo de los años ha habido varios intentos para permitir ejecutar código binario en los navegadores. Los applets de Java, el player de Flash, Silverlight de Microsoft, Native Client de Chrome, o asm.js de Firefox son ejemplos de ello. El pastel era muy grande y todos querían llevarse el trozo más grande. Después de muchos años intentándolo con iniciativas particulares y tecnologías propietarias, al final todas las grandes compañías detrás de los principales navegadores se pusieron de acuerdo para crear una especificación estándar. Esa especificación es WebAssembly. Y lo más importante que se puede decir sobre WebAssembly es precisamente eso, que es una especificación que está siendo definida bajo el paraguas de la W3C.

WebAssembly permite ejecutar aplicaciones en un navegador con un rendimiento similar al que se obtiene ejecutando código nativo.

WebAssembly lleva un año disponible en los principales navegadores, siendo Firefox el que mayor rendimiento ha ofrecido hasta el momento, con un sorprendente buen comportamiento en Edge y un tanto decepcionante en Chrome. O al menos hasta la incorporación de Litoff a las últimas versiones de Chrome. Litoff es una nueva máquina virtual que se ha añadido a Chrome, específicamente para WebAssembly, con la que ha reducido significativamente la brecha que tenía con el resto de navegadores. Porque WebAssembly es precisamente eso, la especificación de una máquina virtual que permite ejecutar código en formato binario de manera segura.

Uno de los comentarios más habituales cuando se empezó a popularizar WebAssembly era el de que a largo plazo sería el lenguaje de estándar de facto de la web que vendría a reemplazar a JavaScript. Lo que ha resultado ser una verdad a medias. Porque WebAssembly no es sólo una máquina virtual, sino también el lenguaje de programación que se ejecuta sobre dicha máquina virtual. Pero es un lenguaje de muy bajo nivel, no se espera a día de hoy que los desarrolladores escriban código utilizándolo. De hecho, lo que se espera es precisamente todo lo contrario, es decir, que se utilicen otros lenguajes de programación, tradicionalmente utilizados para desarrollos en la parte servidora, como por ejemplo C++ o Go. Afirmación que puede resultar algo sorprendente, pero que se fundamenta en el hecho de que el conjunto de instrucciones que define WebAssembly es muy básico, por lo que es factible compilar código escrito en un lenguaje de alto nivel y generar el código equivalente escrito en WebAssembly. Y esto de hecho es ya una realidad, muchos lenguajes de programación permite compilar actualmente a WebAssembly.

Para entender el impacto de WebAssembly en la web hay que tener en cuenta el objetivo que pretende alcanzar. Y este no es otro que proporcionar un entorno seguro y de alto rendimiento para la ejecución de aplicaciones. Un objetivo realmente ambicioso. La pieza que faltaba para que los navegadores puedan venir a eliminar prácticamente toda interacción de los usuarios con el sistema operativo subyacente. Una afirmación un tanto exagerada quizás, hasta que se empieza a ver ejemplos de lo que algunos equipos de desarrollo están consiguiendo con WebAssembly, siendo AutoCAD posiblemente uno de los ejemplos más paradigmáticos.

AutoCAD es una veterana aplicación para diseño 3D muy popular en entornos profesionales. AutoDesk, su empresa desarrolladora, lleva años intentando llevar su software a la web, y tal como ellos mismos afirman, no ha sido hasta la aparición de WebAssembly que sienten que realmente lo han conseguido. Partiendo de un mismo código base escrito en C++ ahora son capaces de compilar a nativo o a WebAssembly, de forma que su producto puede ejecutarse como una aplicación de escritorio nativa tradicional o distribuirse como una aplicación web ordinaria accesible desde cualquier dispositivo dotado de un navegador. Esto quiere decir que ya no es necesario crear ejecutables para los distintos sistemas operativos y arquitecturas hardware, la versión en WebAssembly se ejecuta sobre cualquier navegador. Y además lo hace de forma eficiente y segura.

Como ya se ha comentando, WebAssembly es la especificación de una máquina virtual. Su propósito es definir un conjunto de instrucciones, un contexto de ejecución, y las reglas que rigen la ejecución de dichas instrucciones en dicho contexto. El conjunto de instrucciones define como realizar operaciones o modificar el flujo de proceso. El contexto de ejecución define las capacidades disponibles para la ejecución de las instrucciones. Y las reglas definen como validar las instrucciones de cara a su ejecución.

Algunas de las características principales de la máquina virtual de WebAssembly son las siguientes:

  • Define un conjunto de instrucciones. En dos formatos. Formato de texto para facilitar la lectura/escritura de código. Y formato binario equivalente, un opcode para cada instrucción, para facilitar la compilación y ejecución del código. El número total de instrucciones disponibles actualmente es pequeño, menos de 256, por lo que 1 byte basta representar todas las instrucciones.
  • Basa su funcionamiento en una pila (stack) estructurada. Todas las instrucciones extraen sus argumentos de la pila y almacenan el resultado de su ejecución en la pila. La elección de una estructura de pila simplifica la gramática de las instrucciones, permite una compilación más directa, y facilita la implementación de algunos patrones, como por ejemplo retornar múltiples valores desde una función.
  • Trabaja con tipos básicos. Los únicos tipos soportados son enteros de 32 bits, enteros de 64 bits, decimales de 32 bits y decimales de 64 bits. Estos tipos son soportados por todas las arquitecturas de hardware actuales. Tipos más pequeños, como los enteros de 8 o 16 bits, en la práctica son tratados de manera interna como enteros de 32 bits por la mayoría de los lenguajes de programación en tiempo de ejecución.
  • Opera sobre un bloque de memoria de tamaño predeterminado, múltiplo de 64 KB, accedido de forma lineal, y direccionable a byte. WebAssembly especifica que la memoria debe reservarse en forma de sandbox, es decir, como una región de memoria reservada de forma expresa para ello, no sobre una región de memoria reservada previamente para otro propósito, ni tan siquiera sobre la memoria reservada para la pila.
  • Los programas se organizan en módulos. Los módulos en secciones. Una sección de código contiene funciones, de la misma forma que se entiende en la mayoría de lenguajes de alto nivel, y pueden definir variables locales. Estas variables se almacenan en su propia pila, alojada fuera del espacio de memoria ordinaria direccionable por las instrucciones. Mantener los valores de las variables locales en su propio espacio de memoria aumenta aún más la seguridad del sistema. Y permite tratar la máquina virtual como si tuviera un número infinito de registros.
  • Es determinista. Al menos en la medida de lo posible. Incluso aunque ello vaya en contra de uno de sus objetivos de diseño. Por ejemplo, muchos lenguajes de programación soportan cierto grado de indeterminismo, como las estructuras de tamaño variable en función de la plataforma hardware. Si se quiere compilar estos lenguajes a WebAssembly, los compiladores tendrán que generar el código necesario para adaptarse al comportamiento determinista definido por WebAssembly, aún a costa de una mayor cantidad de código generado o una ligera pérdida de rendimiento, lo que va en contra de los objetivos de diseño de WebAssembly, que establecen que se debe facilitar el proceso de compilación eficiente.

Otro aspecto importante a tener en cuenta es que, aunque WebAssembly está pensado para ejecutarse embebido en el contexto de un navegador web, en la práctica es totalmente independiente y una implementación de la máquina virtual es factible en cualquier entorno. De hecho, ya se está utilizando la especificación como base para construir microkernels que ejecutan WebAssembly.

Por supuesto el caso de uso más habitual a día de hoy es utilizar WebAssembly dentro de un navegador, por lo que la especificación define un API para JavaScript que permite cargar, ejecutar módulos WebAssembly, y comunicarse con código JavaScript. La compilación y verificación del código WebAssembly es muy rápida, se realiza a medida que el código se descarga, sin tener que esperar que se descargue completamente. Y los navegadores ya empiezan a soportan depurar el código en WebAssembly de igual forma que el código en JavaScript ordinario con la ayuda de sourcemaps.

Por último, comentar que la adopción de WebAssembly es un hecho. Por ejemplo, Unity, el popular motor multiplataforma para la creación de videojuegos, ya permite generar código en WebAssembly para la web. Combinado con WebGL, proporciona una experiencia similar a los videojuegos escritos en código nativo. En un futuro es posible incluso que aparezcan frameworks de alto rendimiento para la creación de aplicaciones web que utilicen un elemento de tipo canvas para el apartado gráfico en vez de utilizar DOM y CSS.

Mi contribución a la causa es una extensión de WebAssembly para Visual Studio Code que publiqué hace unos meses y añade coloreado de sintaxis al código escrito en WebAssembly.