Las apli­ca­cio­nes que se ejecutan en navegador en lugar de ir in­s­ta­la­das en un disco duro son cada vez más completas. Además de los típicos programas de oficina como Microsoft 365 o Google Docs, que siempre presentan funciones nuevas, los juegos de navegador son cada vez más complejos y requieren más recursos. Este tipo de apli­ca­cio­nes web se ofrece a menudo en Ja­va­S­cri­pt, pero, en­tre­ta­n­to, cada vez más de­sa­rro­lla­do­res apuestan por We­bA­s­se­m­bly: un nuevo pla­n­tea­mie­n­to con re­su­l­ta­dos so­r­pre­n­de­n­tes.

¿Qué es We­bA­s­se­m­bly?

We­bA­s­se­m­bly (abreviado, Wasm) es una nueva forma con la que los de­sa­rro­lla­do­res web pueden generar apli­ca­cio­nes en internet. Hasta ahora, para ello había que recurrir a Ja­va­S­cri­pt. Pero Ja­va­S­cri­pt es re­la­ti­va­me­n­te lento y, en de­te­r­mi­na­dos es­ce­na­rios, se ve limitado. Por eso, el World Wide Web Co­n­so­r­tium (W3C) ha impulsado este nuevo método. Sin embargo, para que Wasm pueda funcionar, el navegador debe ser co­m­pa­ti­ble con este lenguaje. Por este motivo, Mozilla (Firefox), Microsoft (Edge), Apple (Safari) y Google (Chrome) han pa­r­ti­ci­pa­do en el de­sa­rro­llo. En todas las versiones de navegador actuales de estos pro­vee­do­res se pueden ejecutar apli­ca­cio­nes en We­bA­s­se­m­bly.

Consejo

Para ex­pe­ri­me­n­tar la capacidad de re­n­di­mie­n­to de We­bA­s­se­m­bly merece la pena jugar una partida a Funky Karts. El juego —en realidad, una apli­ca­ción móvil— se convirtió a We­bA­s­se­m­bly para poder eje­cu­tar­se también en na­ve­ga­do­res. El de­sa­rro­lla­dor ha escrito sobre el proyecto en un in­te­re­sa­n­te blog, en el que ha descrito cada uno de los pasos de la co­n­ve­r­sión.

Fu­n­da­me­n­ta­l­me­n­te, We­bA­s­se­m­bly se re­pre­se­n­ta en forma de bytecode, que puede co­n­si­de­rar­se como un nivel in­te­r­me­dio entre el código máquina —que solo un ordenador puede entender— y un típico lenguaje de pro­gra­ma­ción —legible para humanos, a condición de que se compile primero. Al requerir apenas esfuerzo para convertir el código, esto hace que We­bA­s­se­m­bly sea más rápido. Sin embargo, escribir en bytecode es bastante inusual. La ventaja de Wasm es que no hace falta trabajar con este lenguaje de pro­gra­ma­ción, ya que, en la práctica, la apli­ca­ción web puede es­cri­bi­r­se en C o C++.

El texto fuente se convierte con la apli­ca­ción Em­s­cri­p­ten. Antes de que existiera We­bA­s­se­m­bly, esta he­rra­mie­n­ta ya estaba en uso para convertir código C/C++ a Ja­va­S­cri­pt (o ams.js). Ac­tua­l­me­n­te, con ella también es posible tra­n­s­cri­bir código en Wasm. Esto significa que el código está pre­co­m­pi­la­do y por ello no tiene que co­m­pi­lar­se o in­te­r­pre­tar­se en el momento de la ejecución. Cuando el usuario abre fi­na­l­me­n­te la apli­ca­ción en el navegador, se inicia una pequeña máquina virtual. Y en ella se ejecuta la apli­ca­ción.

Ventajas de We­bA­s­se­m­bly

Ac­tua­l­me­n­te, We­bA­s­se­m­bly presenta un único in­co­n­ve­nie­n­te: se difunde con mucha lentitud. Los de­sa­rro­lla­do­res web están aco­s­tu­m­bra­dos al trabajo con Ja­va­S­cri­pt y no hay planes para de­s­ba­n­car­lo. La dirección del proyecto da una gran im­po­r­ta­n­cia a que en la co­mu­ni­ca­ción Wasm se presente como una opción co­m­ple­me­n­ta­ria de Ja­va­S­cri­pt. Pero, gracias a la co­m­pa­ti­bi­li­dad con los grandes pro­vee­do­res de na­ve­ga­do­res y el W3C, la difusión está co­me­n­za­n­do a despegar. Esto también se debe a que los vi­si­ta­n­tes de las páginas web no tienen que realizar ningún paso por su cuenta: las apli­ca­cio­nes web en We­bA­s­se­m­bly se cargan de manera tan sencilla como el código en Ja­va­S­cri­pt, solo que más rápido.

Muchos de­sa­rro­lla­do­res que ya escriben en C, C++ o en Rust, ahora también pueden programar di­re­c­ta­me­n­te para la web. Los lenguajes de pro­gra­ma­ción a veces también ofrecen otras po­si­bi­li­da­des para el diseño de las apli­ca­cio­nes: quien no encuentra en Ja­va­S­cri­pt las bi­blio­te­cas o los marcos adecuados para su programa, ahora cuenta con una selección más amplia para conseguir su objetivo. Los de­sa­rro­lla­do­res tienen por lo tanto varios motivos para estudiar We­bA­s­se­m­bly con más de­te­ni­mie­n­to:

  • Estándar web abierto de W3C
  • Alto re­n­di­mie­n­to y tamaños de archivo reducidos
  • Por lo tanto, también perfecto para la na­ve­ga­ción móvil
  • En teoría, hace posible la realidad virtual en el navegador
  • No se requiere ningún lenguaje de pro­gra­ma­ción nuevo
  • También se pueden utilizar C, C++ o Rust para la pro­gra­ma­ción de apli­ca­cio­nes web
  • Co­m­pa­ti­ble con todos los grandes pro­vee­do­res de na­ve­ga­do­res
  • Sin li­mi­ta­cio­nes para el usuario

We­bA­s­se­m­bly en la práctica

Realmente, no se espera que nadie programe en We­bA­s­se­m­bly. De hecho, la principal ventaja de esta te­c­no­lo­gía es que el pro­gra­ma­dor puede usar uno de los lenguajes conocidos, como C. El código se tra­n­s­fie­re luego al formato Wasm. No obstante, tiene sentido ahondar en el código ya compilado y echar un vistazo al fu­n­cio­na­mie­n­to de We­bA­s­se­m­bly.

Hay dos variantes di­fe­re­n­tes del código fuente: We­bA­s­se­m­bly Text Format (WAT) y We­bA­s­se­m­bly Binary Format (Wasm). Este último es el código real que la máquina ejecuta. Sin embargo, como está compuesto ex­clu­si­va­me­n­te de código binario, no resulta útil para un análisis humano, motivo por el que existe el formato in­te­r­me­dio WAT. Como el código utiliza ex­pre­sio­nes legibles, los propios pro­gra­ma­do­res pueden ana­li­zar­lo, si bien carece de la comodidad de trabajo que se conoce de los lenguajes de pro­gra­ma­ción es­ta­ble­ci­dos.

En este ejemplo se usa un código fuente muy simple en C:

#define WASM_EXPORT __attribute__((visibility("default")))
WASM_EXPORT
int main() {
    return 1;
}

El mismo código en formato WAT es mucho más largo:

(module
    (type $t0 (func))
    (type $t1 (func (result i32)))
    (func $__wasm_call_ctors (type $t0))
    (func $main (export "main") (type $t1) (result i32)
        i32.const 1)
    (table $T0 1 1 anyfunc)
    (memory $memory (export "memory") 2)
    (global $g0 (mut i32) (i32.const 66560))
    (global $__heap_base (export "__heap_base") i32 (i32.const 66560))
    (global $__data_end (export "__data_end") i32 (i32.const 1024)))

La le­gi­bi­li­dad está muy limitada, si bien se pueden di­s­ti­n­guir algunos elementos. En We­bA­s­se­m­bly todo se divide en di­fe­re­n­tes módulos. A su vez los módulos se dividen en funciones, que se es­pe­ci­fi­can con pa­rá­me­tros. En total se di­s­ti­n­guen cinco elementos:

  • module: la unidad superior de We­bA­s­se­m­bly
  • function: agru­pa­ción dentro de un módulo
  • memory: array con Bytes
  • global: valor que se puede usar con di­fe­re­n­tes módulos
  • table: al­ma­ce­na­mie­n­to de re­fe­re­n­cias

Si el código se traduce en forma binaria, es imposible entender nada:

0000000: 0061 736d                                 ; WASM_BINARY_MAGIC
0000004: 0100 0000                                 ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01                                        ; section code
0000009: 00                                        ; section size (guess)
000000a: 02                                        ; num types
; type 0
000000b: 60                                        ; func
000000c: 00                                        ; num params
000000d: 00                                        ; num results
; type 1
000000e: 60                                        ; func
000000f: 00                                        ; num params
0000010: 01                                        ; num results
0000011: 7f                                        ; i32
0000009: 08                                        ; FIXUP section size
; section "Function" (3)
0000012: 03                                        ; section code
0000013: 00                                        ; section size (guess)
0000014: 02                                        ; num functions
0000015: 00                                        ; function 0 signature index
0000016: 01                                        ; function 1 signature index
0000013: 03                                        ; FIXUP section size
; section "Table" (4)
0000017: 04                                        ; section code
0000018: 00                                        ; section size (guess)
0000019: 01                                        ; num tables
; table 0
000001a: 70                                        ; funcref
000001b: 01                                        ; limits: flags
000001c: 01                                        ; limits: initial
000001d: 01                                        ; limits: max
0000018: 05                                        ; FIXUP section size
; section "Memory" (5)
000001e: 05                                        ; section code
000001f: 00                                        ; section size (guess)
0000020: 01                                        ; num memories
; memory 0
0000021: 00                                        ; limits: flags
0000022: 02                                        ; limits: initial
000001f: 03                                        ; FIXUP section size
; section "Global" (6)
0000023: 06                                        ; section code
0000024: 00                                        ; section size (guess)
0000025: 03                                        ; num globals
0000026: 7f                                        ; i32
0000027: 01                                        ; global mutability
0000028: 41                                        ; i32.const
0000029: 8088 04                                   ; i32 literal
000002c: 0b                                        ; end
000002d: 7f                                        ; i32
000002e: 00                                        ; global mutability
000002f: 41                                        ; i32.const
0000030: 8088 04                                   ; i32 literal
0000033: 0b                                        ; end
0000034: 7f                                        ; i32
0000035: 00                                        ; global mutability
0000036: 41                                        ; i32.const
0000037: 8008                                      ; i32 literal
0000039: 0b                                        ; end
0000024: 15                                        ; FIXUP section size
; section "Export" (7)
000003a: 07                                        ; section code
000003b: 00                                        ; section size (guess)
000003c: 04                                        ; num exports
000003d: 04                                        ; string length
000003e: 6d61 696e                                main  ; export name
0000042: 00                                        ; export kind
0000043: 01                                        ; export func index
0000044: 06                                        ; string length
0000045: 6d65 6d6f 7279                           memory  ; export name
000004b: 02                                        ; export kind
000004c: 00                                        ; export memory index
000004d: 0b                                        ; string length
000004e: 5f5f 6865 6170 5f62 6173 65              __heap_base  ; export name
0000059: 03                                        ; export kind
000005a: 01                                        ; export global index
000005b: 0a                                        ; string length
000005c: 5f5f 6461 7461 5f65 6e64                 __data_end  ; export name
0000066: 03                                        ; export kind
0000067: 02                                        ; export global index
000003b: 2c                                        ; FIXUP section size
; section "Code" (10)
0000068: 0a                                        ; section code
0000069: 00                                        ; section size (guess)
000006a: 02                                        ; num functions
; function body 0
000006b: 00                                        ; func body size (guess)
000006c: 00                                        ; local decl count
000006d: 0b                                        ; end
000006b: 02                                        ; FIXUP func body size
; function body 1
000006e: 00                                        ; func body size (guess)
000006f: 00                                        ; local decl count
0000070: 41                                        ; i32.const
0000071: 01                                        ; i32 literal
0000072: 0b                                        ; end
000006e: 04                                        ; FIXUP func body size
0000069: 09                                        ; FIXUP section size
; section "name"
0000073: 00                                        ; section code
0000074: 00                                        ; section size (guess)
0000075: 04                                        ; string length
0000076: 6e61 6d65                                name  ; custom section name
000007a: 01                                        ; function name type
000007b: 00                                        ; subsection size (guess)
000007c: 02                                        ; num functions
000007d: 00                                        ; function index
000007e: 11                                        ; string length
000007f: 5f5f 7761 736d 5f63 616c 6c5f 6374 6f72  __wasm_call_ctor
000008f: 73                                       s  ; func name 0
0000090: 01                                        ; function index
0000091: 04                                        ; string length
0000092: 6d61 696e                                main  ; func name 1
000007b: 1a                                        ; FIXUP subsection size
0000096: 02                                        ; local name type
0000097: 00                                        ; subsection size (guess)
0000098: 02                                        ; num functions
0000099: 00                                        ; function index
000009a: 00                                        ; num locals
000009b: 01                                        ; function index
000009c: 00                                        ; num locals
0000097: 05                                        ; FIXUP subsection size
0000074: 28                                        ; FIXUP section size
Consejo

Si quieres ex­pe­ri­me­n­tar con We­bA­s­se­m­bly por primera vez, puedes hacerlo en el We­bA­s­se­m­bly Studio. Aquí hay di­s­po­ni­ble un entorno de de­sa­rro­llo online para Wasm.

Ir al menú principal