Skip to main content

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:
2

Compile to bytecode

Use the -b flag to generate raw bytecode:
This creates a binary file containing the compiled bytecode.
3

Run the bytecode

QuickJS can execute bytecode files directly:
Expected output:
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:
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:
3

Run the executable

Expected output:
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:
Use -s twice to also strip debug information:
Note: Stripping debug info removes stack traces and line numbers.

Set Stack Size

Limit the maximum stack size:
Stack size is in bytes (default: 262144 = 256KB).

Custom C Names

Set the C variable name for the bytecode:
This names the bytecode array my_bytecode instead of the default.

Set Script Name

Customize the script name used in stack traces:

Compiling Modules

ES6 Module

Compile a module to bytecode:
The -m flag tells qjsc to treat the file as an ES6 module.

Dynamic Module

Create a dynamically loadable module:
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:
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:
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:

Debugging Bytecode

Dump bytecode for inspection:
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:

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