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 Operations
QuickJS supports loading and executing pre-compiled bytecode, which is faster than parsing source code and allows for smaller executables when the compiler is not needed.
js_std_eval_binary
Evaluate compiled bytecode from quickjs-libc.
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf,
size_t buf_len, int flags);
Parameters
ctx - The JavaScript context
buf - Pointer to bytecode buffer
buf_len - Length of bytecode buffer
flags - Load flags
Example
#include <quickjs-libc.h>
// Assuming bytecode was compiled with qjsc
extern const uint8_t my_script_bytecode[];
extern const size_t my_script_bytecode_size;
js_std_eval_binary(ctx, my_script_bytecode, my_script_bytecode_size, 0);
Low-Level Bytecode API
JS_WriteObject
Serialize a JavaScript value to bytecode.
uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, int flags);
Parameters
ctx - The JavaScript context
psize - Pointer to receive the bytecode size
obj - The value to serialize (typically a function or module)
flags - Write flags
Write Flags
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references */
#define JS_WRITE_OBJ_STRIP_SOURCE (1 << 4) /* do not write source code */
#define JS_WRITE_OBJ_STRIP_DEBUG (1 << 5) /* do not write debug info */
Returns
Returns a buffer containing the bytecode (must be freed with js_free()), or NULL on error.
Example
// Compile a function to bytecode
const char *code = "function greet(name) { return 'Hello, ' + name; }";
JSValue func = JS_Eval(ctx, code, strlen(code), "greet.js",
JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(func)) {
size_t bytecode_size;
uint8_t *bytecode = JS_WriteObject(ctx, &bytecode_size, func,
JS_WRITE_OBJ_BYTECODE);
if (bytecode) {
// Save bytecode to file or embed in executable
FILE *f = fopen("greet.qjsc", "wb");
fwrite(bytecode, 1, bytecode_size, f);
fclose(f);
js_free(ctx, bytecode);
}
JS_FreeValue(ctx, func);
}
JS_ReadObject
Deserialize bytecode to a JavaScript value.
JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags);
Parameters
ctx - The JavaScript context
buf - Pointer to bytecode buffer
buf_len - Length of bytecode
flags - Read flags
Read Flags
#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
Returns
Returns the deserialized value (function or module), or JS_EXCEPTION on error.
Example
// Load bytecode from file
FILE *f = fopen("greet.qjsc", "rb");
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *buf = malloc(size);
fread(buf, 1, size, f);
fclose(f);
JSValue func = JS_ReadObject(ctx, buf, size, JS_READ_OBJ_BYTECODE);
free(buf);
if (!JS_IsException(func)) {
// Execute the bytecode
JSValue result = JS_EvalFunction(ctx, func);
JS_FreeValue(ctx, result);
}
JS_EvalFunction
Execute a bytecode function or module.
JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj);
See the JS_Eval documentation for details on evaluation flags.
Module Bytecode
JS_ResolveModule
Load the dependencies of a module.
int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
Parameters
ctx - The JavaScript context
obj - A module object (from JS_ReadObject())
Returns
Returns 0 on success, -1 on error.
Example
// Load module bytecode
uint8_t *bytecode = load_bytecode_from_file("module.qjsc", &size);
JSValue module = JS_ReadObject(ctx, bytecode, size, JS_READ_OBJ_BYTECODE);
free(bytecode);
if (!JS_IsException(module)) {
// Resolve module dependencies
if (JS_ResolveModule(ctx, module) == 0) {
// Execute the module
JSValue result = JS_EvalFunction(ctx, module);
JS_FreeValue(ctx, result);
}
}
Complete Example: Compile and Execute
#include <quickjs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void compile_to_bytecode(JSContext *ctx, const char *source, const char *filename)
{
// Compile source to bytecode
JSValue obj = JS_Eval(ctx, source, strlen(source), filename,
JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(obj)) {
size_t bytecode_size;
uint8_t *bytecode = JS_WriteObject(ctx, &bytecode_size, obj,
JS_WRITE_OBJ_BYTECODE);
if (bytecode) {
// Save to file
FILE *f = fopen("output.qjsc", "wb");
fwrite(bytecode, 1, bytecode_size, f);
fclose(f);
printf("Compiled to output.qjsc (%zu bytes)\n", bytecode_size);
js_free(ctx, bytecode);
}
JS_FreeValue(ctx, obj);
} else {
js_std_dump_error(ctx);
}
}
void execute_bytecode(JSContext *ctx, const char *filename)
{
FILE *f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Cannot open %s\n", filename);
return;
}
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *buf = malloc(size);
fread(buf, 1, size, f);
fclose(f);
JSValue obj = JS_ReadObject(ctx, buf, size, JS_READ_OBJ_BYTECODE);
free(buf);
if (!JS_IsException(obj)) {
JSValue result = JS_EvalFunction(ctx, obj);
if (JS_IsException(result)) {
js_std_dump_error(ctx);
}
JS_FreeValue(ctx, result);
} else {
js_std_dump_error(ctx);
}
}
Using qjsc Compiler
The qjsc tool compiles JavaScript to C code with embedded bytecode:
# Compile to C code
qjsc -o script.c script.js
# Compile to bytecode file
qjsc -c -o script.qjsc script.js
Generated C code:
// script.c (generated by qjsc)
const uint8_t qjsc_script[] = {
0x02, 0x04, 0x0e, 0x73, 0x63, 0x72, 0x69, 0x70,
// ... bytecode bytes ...
};
const uint32_t qjsc_script_size = 1234;
Use in your program:
#include "script.c"
js_std_eval_binary(ctx, qjsc_script, qjsc_script_size, 0);
Security Considerations
Warning: The bytecode format is linked to a specific QuickJS version. No security checks are performed before execution. Only load bytecode from trusted sources.
- Bytecode is version-specific
- No signature verification
- No sandboxing of bytecode execution
- Malicious bytecode can compromise the runtime
Notes
- Bytecode is faster to load than source code (no parsing)
- Smaller executables when compiler is excluded
- Use
JS_WRITE_OBJ_STRIP_SOURCE and JS_WRITE_OBJ_STRIP_DEBUG to reduce bytecode size
- Bytecode format may change between QuickJS versions
- For modules, use
JS_ResolveModule() before JS_EvalFunction()