MicroQuickJS: un motor JavaScript embebido de 10 kB

MicroQuickJS: un motor JavaScript embebido de 10 kB

MicroQuickJS (también conocido como MQuickJS) es un motor JavaScript de código abierto escrito en C, diseñado específicamente para dispositivos con recursos limitados. Se basa en la base de código de QuickJS, pero reemplaza el GC de conteo de referencias por un recolector con trazado y compactación, y elimina muchas características del lenguaje que incrementarían el uso de memoria. El resultado es un entorno de ejecución de 32 bits que necesita solo 10 kB de RAM y alrededor de 100 kB de ROM en un objetivo Arm Thumb‑2.

¿Por qué MicroQuickJS?

  1. Huella muy reducida – El núcleo del motor ocupa alrededor de 10 kB de RAM en una CPU de 32 bits.
  2. Estricto y seguro por diseño – Solo se admite un subconjunto estricto ES5; las características como with o la asignación dinámica son omitidas.
  3. GC con trazado – Un recolector con compactación reduce la sobrecarga de objetos y elimina la fragmentación.
  4. Sin dependencia de libc – El motor utiliza su propio asignador y no llama a malloc/free, lo que lo hace adecuado para firmware sin sistema operativo.
  5. API en C – Expone el motor a programas nativos en C, gestionando la reubicación de punteros automáticamente.
  6. Exportación de bytecode – Los scripts pueden compilarse una vez y almacenarse en ROM.

Empezando – Construir el REPL

Clone the repository:

git clone https://github.com/bellard/mquickjs.git
cd mquickjs

Compile the REPL (mqjs):

make

You should see mqjs in the repository root. A quick sanity check:

./mqjs -e \"console.log('Hello MQuickJS');\"
# Hello MQuickJS

Usando la opción de límite de memoria

Embedded targets can set a hard RAM ceiling. The REPL accepts --memory-limit:

./mqjs --memory-limit 10k tests/mandelbrot.js
# Renders the Mandelbrot set in the terminal

For a 32‑bit ARM board you can even generate 32‑bit bytecode for a 64‑bit host:

./mqjs -o mandelbrot.bin -m32 tests/mandelbrot.js

Ejecutando bytecode precompilado

The -b flag allows running bytecode directly:

./mqjs -b mandelbrot.bin

El formato de bytecode es basado en pila, pero internamente utiliza una tabla de átomos, por lo que es portátil entre arquitecturas que comparten el mismo orden de bytes.

Explorando la API en C

MicroQuickJS expone una API en C extremadamente similar a QuickJS pero adaptada para el GC compacto.

#include \"mquickjs.h\"

int main(void) {
    uint8_t mem_buf[8192];
    JSContext *ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib);
    if (!ctx) {
        fprintf(stderr, \"Cannot allocate context\\n\");
        return 1;
    }

    // Run a simple script
    JSValue result = JS_EvalCode(ctx, \"1 + 2\", \"test.js\", JS_EVAL_TYPE_GLOBAL);
    int i = JS_ToInt32(ctx, &i, result);
    printf(\"Result: %d\\n\", i);

    JS_FreeContext(ctx);
    return 0;
}

Puntos clave para usar la API:

  • Sin liberación explícita – Los objetos son movidos por el GC, por lo que nunca se llama a JS_FreeValue.
  • Use JS_PushGCRef / JS_PopGCRef – Para mantener referencias vivas entre asignaciones.
  • Librerías personalizadas – Compile la biblioteca estándar a ROM con mquickjs_build.c.

Modo estricto y subconjunto de lenguaje

MicroQuickJS es solo modo estricto. Algunas trampas típicas son eliminadas:

  • Todas las variables globales deben declararse con var.
  • Array no puede tener huecos; escribir más allá de su longitud provoca un TypeError.
  • eval solo se admite indirectamente a través de la función global eval.
  • No new Number(1). El envoltorio no es soportado.
  • String.prototype.toLowerCase()/toUpperCase() solo manejan ASCII.
  • RegExp usa sólo folding en ASCII.

Estas restricciones hacen que el motor sea predecible y ahorran memoria.

Rendimiento y Benchmarks

MicroQuickJS pasa la microprueba de QuickJS en un tiempo comparable mientras usa aproximadamente la mitad de memoria en hardware de 32 bits. Pruebas adicionales muestran que puede ejecutar el benchmark V8 Octane en modo estricto con una penalización de velocidad de 1–2× pero con un impacto de memoria 4× menor.

make test
# All unit tests pass.
make microbench
# 10 kB memory usage, script execution ~120 ms on a RasPi‑Zero.

Resumen

MicroQuickJS ofrece una solución atractiva para los desarrolladores que necesitan un tiempo de ejecución de JavaScript en dispositivos pequeños. Su huella de 10 kB de RAM, el subconjunto estricto ES5 y el GC con trazado lo convierten en una excelente opción para firmware, pasarelas IoT o cualquier sistema embebido que se beneficie de la flexibilidad de JavaScript sin la sobrecarga de un motor grande. Ya sea que construya directamente con el REPL, incruste la API en C en su firmware o distribuye bytecode precompilado, MicroQuickJS ofrece un camino simplificado hacia JavaScript en el borde.

Artículo original: Ver original

Compartir este artículo