MQuickJS Documentation

repository·main·Indexed 27 days ago

https://github.com/bellard/mquickjs

A memory-efficient JavaScript engine for embedded systems capable of running with 10 kB RAM and 100 kB ROM. Features include the mqjs REPL CLI, bytecode generation for ROM execution, and a C API utilizing a compacting garbage collector with JSGCRef for persistent references. Operates in a permanent stricter mode (mostly ES5) with specific constraints on array holes, the with keyword, and value boxing.

Tokens
1.3K
Snippets
5
Records
8
Agent score
41%

What's inside MQuickJS

  1. Run project tests and benchmarks

    main

    Use the following commands to verify the installation and performance:

    • Basic tests: make test
    • Micro benchmark: make microbench
    • V8 Octane benchmark: make octane (requires downloading extras)
    make test
    make microbench
    make octane
  2. Understand MQuickJS Stricter Mode constraints

    main

    MQuickJS operates in a permanent stricter mode (mostly ES5) to optimize for memory and safety. Key restrictions include:

    • No with keyword: Global variables must be declared with var.
    • No Array Holes: Writing to an index beyond the current length (e.g., a[10] = 2 when a.length is 1) results in a TypeError. Use a plain object {} if you need sparse keys.
    • No Array Literals with holes: [1, , 3] is a SyntaxError.
    • Indirect eval only: eval() cannot access or modify local variables. Only global eval is supported.
    • No Value Boxing: new Number(1) is not supported.
    • Regexp/String limitations: Case folding and toUpperCase/toLowerCase only work with ASCII characters.
  3. Handle memory and moving objects in C API

    main

    Because MQuickJS uses a compacting garbage collector, the address of objects can change whenever a JS allocation occurs.

    Best Practices:

    1. Avoid storing JSValue directly: Only use JSValue for temporary use between API calls. For long-lived references, use a pointer to a JSValue.
    2. Use JSGCRef for persistent references: To safely hold a reference to a JS object, use JS_PushGCRef() to get an opaque pointer that the engine automatically updates when objects move. You must release it with JS_PopGCRef().
    3. Debug with DEBUG_GC: When running on a PC, define DEBUG_GC to force the allocator to move objects at every allocation, helping catch invalid JSValue usage.
    JSValue my_js_func(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
    {
            JSGCRef obj1_ref, obj2_ref;
            JSValue *obj1, *obj2, ret;
    
            ret = JS_EXCEPTION;
            obj1 = JS_PushGCRef(ctx, &obj1_ref);
            obj2 = JS_PushGCRef(ctx, &obj2_ref);
            *obj1 = JS_NewObject(ctx);
            if (JS_IsException(*obj1))
                goto fail;
            *obj2 = JS_NewObject(ctx); // obj1 may move
            if (JS_IsException(*obj2))
                goto fail;
            JS_SetPropertyStr(ctx, *obj1, "x", *obj2);  // obj1 and obj2 may move
            ret = *obj1;
         fail:
            JS_PopGCRef(ctx, &obj2_ref);
            JS_PopGCRef(ctx, &obj1_ref);
            return ret;
    }
  4. Initialize the MQuickJS engine in C

    main

    MQuickJS does not use malloc() or free(). You must provide a pre-allocated memory buffer to JS_NewContext. The engine will perform all allocations within this buffer.

    JSContext *ctx;
    uint8_t mem_buf[8192];
    ctx = JS_NewContext(mem_buf, sizeof(mem_buf), &js_stdlib);
    ...
    JS_FreeContext(ctx);
  5. Use the mqjs REPL CLI

    main

    The mqjs command-line tool allows you to run JavaScript files, evaluate expressions, or enter interactive mode. It is designed for embedded systems and supports memory limiting and bytecode management.

    usage: mqjs [options] [file [args]]
    -h  --help            list options
    -e  --eval EXPR       evaluate EXPR
    -i  --interactive     go to interactive mode
    -I  --include file    include an additional file
    -d  --dump            dump the memory usage stats
        --memory-limit n  limit the memory usage to 'n' bytes
    --no-column           no column number in debug information
    -o FILE               save the bytecode to FILE
    -m32                  force 32 bit bytecode output (use with -o)
    -b  --allow-bytecode  allow bytecode in input file