Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/quickjs-ng/quickjs/llms.txt

Use this file to discover all available pages before exploring further.

Bytecode Compilation

QuickJS can compile JavaScript to bytecode for faster loading and to protect source code. This tutorial covers different bytecode compilation methods.

Raw Bytecode Output

1

Create a JavaScript file

Create hello.js:
console.log("Hello World");
2

Compile to bytecode

Use the -b flag to generate raw bytecode:
qjsc -b -o hello.bin hello.js
This creates a binary file containing the compiled bytecode.
3

Run the bytecode

QuickJS can execute bytecode files directly:
qjs hello.bin
Expected output:
Hello World
Bytecode files load faster than source files since they skip the parsing step.

Embedding Bytecode in C

1

Generate C code with bytecode

Use the -e flag to generate a complete C program:
qjsc -e -o hello.c hello.js
This generates a C file containing:
  • Bytecode as a static array
  • A main() function that initializes QuickJS and executes the bytecode
2

Compile the C program

Compile with the QuickJS library:
cc hello.c dtoa.c libregexp.c libunicode.c quickjs.c quickjs-libc.c -I. -o hello
3

Run the executable

./hello
Expected output:
Hello World
The executable is self-contained and doesn’t require the original JavaScript source.

Advanced Compilation Options

Strip Source Code

Remove source code from bytecode to reduce size and protect IP:
qjsc -b -s -o hello.bin hello.js
Use -s twice to also strip debug information:
qjsc -b -s -s -o hello.bin hello.js
Note: Stripping debug info removes stack traces and line numbers.

Set Stack Size

Limit the maximum stack size:
qjsc -b -S 524288 -o hello.bin hello.js
Stack size is in bytes (default: 262144 = 256KB).

Custom C Names

Set the C variable name for the bytecode:
qjsc -N my_bytecode -o hello.c hello.js
This names the bytecode array my_bytecode instead of the default.

Set Script Name

Customize the script name used in stack traces:
qjsc -b -n "MyApp" -o hello.bin hello.js

Compiling Modules

ES6 Module

Compile a module to bytecode:
qjsc -m -b -o fib_module.bin fib_module.js
The -m flag tells qjsc to treat the file as an ES6 module.

Dynamic Module

Create a dynamically loadable module:
qjsc -D my_module -o module.c module.js
The -D flag generates code for a module that can be loaded with import().

External C Module

Add initialization code for an external C module:
qjsc -M my_module,my_init -o app.c app.js
This references a C module named my_module with init function my_init.

Standalone Executables

QuickJS offers a simpler way to create standalone executables without manual C compilation:
qjs -c hello.js -o hello --exe qjs
Features:
  • Compiles JavaScript to bytecode
  • Bundles bytecode into a copy of the qjs executable
  • Same runtime dependencies as qjs
  • No bundling (use esbuild or similar for multi-file apps)
Example with bundling:
# Bundle with esbuild
npx esbuild my-app/index.js \
    --bundle \
    --outfile=app.js \
    --external:qjs:* \
    --minify \
    --target=es2023 \
    --platform=neutral \
    --format=esm \
    --main-fields=main,module

# Create standalone executable
qjs -c app.js -o app --exe $HOME/bin/qjs

Debugging Bytecode

Dump bytecode for inspection:
qjs -D 0x01 hello.js
Dump flags:
  • 0x01 - Final bytecode
  • 0x02 - Pass 2 bytecode
  • 0x04 - Pass 1 bytecode
  • 0x10 - Bytecode in hex format
  • 0x20 - Line number table
Combine flags with bitwise OR:
qjs -D 0x11 hello.js  # Final bytecode in hex (0x01 | 0x10)

Performance Considerations

Bytecode advantages:
  • Faster loading (no parsing required)
  • Smaller file size (when stripped)
  • Source code protection
Bytecode trade-offs:
  • Not human-readable
  • Debugging is harder (especially when stripped)
  • Must recompile for code changes
Best practices:
  • Use bytecode for production deployments
  • Keep source files for development
  • Don’t strip debug info during development
  • Test bytecode before deploying

Next Steps