强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

QuickJS 嵌入式 JavaScript 引擎完全教程 / 04 - C API 详解

C API 详解

本章深入 QuickJS 的 C API,这是将 QuickJS 嵌入到 C/C++ 应用中的核心接口。

4.1 核心概念

QuickJS 的 C API 由三层核心对象构成:

┌────────────────────────────────────────────────┐
│                 JSRuntime                       │
│  ┌──────────────────────────────────────────┐  │
│  │           JSContext (1)                  │  │
│  │  ┌───────────────────────────────────┐   │  │
│  │  │ JSValue (多个)                    │   │  │
│  │  │ - 数字、字符串、对象、函数...     │   │  │
│  │  └───────────────────────────────────┘   │  │
│  └──────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────┐  │
│  │           JSContext (2)                  │  │
│  │  ┌───────────────────────────────────┐   │  │
│  │  │ JSValue (多个)                    │   │  │
│  │  └───────────────────────────────────┘   │  │
│  └──────────────────────────────────────────┘  │
└────────────────────────────────────────────────┘
对象说明生命周期
JSRuntime运行时环境,包含垃圾回收器、内存分配器通常每个进程一个
JSContext执行上下文,包含全局对象、内置对象一个 Runtime 可有多个 Context
JSValueJavaScript 值的 C 表示需手动管理引用计数

4.2 JSRuntime 管理

// runtime_example.c — 运行时管理
#include "quickjs-libc.h"
#include <stdio.h>

int main() {
    // 创建运行时
    JSRuntime *rt = JS_NewRuntime();

    // 设置内存限制(默认 256MB)
    JS_SetMemoryLimit(rt, 128 * 1024 * 1024); // 128MB

    // 设置栈大小(默认 256 * 1024)
    JS_SetMaxStackSize(rt, 1024 * 1024); // 1MB

    // 设置 GC 阈值调整
    // JS_SetGCThreshold(rt, threshold);

    // 获取运行时信息
    JSMemoryUsage mem_usage;
    JS_ComputeMemoryUsage(rt, &mem_usage);
    printf("Memory allocated: %zu bytes\n", (size_t)mem_usage.malloc_size);

    // 创建上下文
    JSContext *ctx = JS_NewContext(rt);
    if (!ctx) {
        fprintf(stderr, "Failed to create context\n");
        JS_FreeRuntime(rt);
        return 1;
    }

    // 使用上下文执行代码...
    JSValue result = JS_Eval(ctx, "1 + 2", 5, "<eval>", 0);
    printf("Result: %d\n", JS_VALUE_GET_INT(result));
    JS_FreeValue(ctx, result);

    // 释放上下文
    JS_FreeContext(ctx);

    // 释放运行时
    JS_FreeRuntime(rt);

    return 0;
}

编译运行:

gcc -o runtime_example runtime_example.c \
    quickjs.c quickjs-libc.c cutils.c libregexp.c libunicode.c \
    -lm -lpthread -ldl
./runtime_example

4.3 JSValue 详解

JSValue 是 QuickJS 中最重要的类型,它是所有 JavaScript 值的统一表示。

JSValue 的内部结构

// JSValue 的本质(简化表示)
typedef union JSValueUnion {
    int32_t int32;         // 整数值
    float64_t float64;     // 浮点值
    void *ptr;             // 指针(对象、字符串等)
} JSValueUnion;

typedef struct JSValue {
    JSValueUnion u;        // 值的联合体
    int64_t tag;           // 类型标签
} JSValue;

类型标签

标签常量JavaScript 类型
JS_TAG_NULL0null
JS_TAG_UNDEFINED1undefined
JS_TAG_BOOL2boolean
JS_TAG_INT3number(整数)
JS_TAG_FLOAT644number(浮点数)
JS_TAG_STRING-1string
JS_TAG_OBJECT-2objectfunctionarray
JS_TAG_BIG_INT-3bigint
JS_TAG_BIG_DECIMAL-4bigdecimal
JS_TAG_BIG_FLOAT-5bigfloat
JS_TAG_SYMBOL-6symbol
JS_TAG_EXCEPTION-7异常

值的创建

// value_creation.c — JSValue 创建示例
#include "quickjs.h"
#include <stdio.h>
#include <string.h>

void demo_values(JSContext *ctx) {
    // 基本类型值
    JSValue null_val = JS_NULL;
    JSValue undefined_val = JS_UNDEFINED;
    JSValue true_val = JS_TRUE;
    JSValue false_val = JS_FALSE;
    JSValue int_val = JS_NewInt32(ctx, 42);
    JSValue float_val = JS_NewFloat64(ctx, 3.14);
    JSValue str_val = JS_NewString(ctx, "Hello, QuickJS!");

    // 使用值...
    const char *str = JS_ToCString(ctx, str_val);
    printf("String value: %s\n", str);
    JS_FreeCString(ctx, str);

    // 对象创建
    JSValue obj_val = JS_NewObject(ctx);

    // 数组创建
    JSValue arr_val = JS_NewArray(ctx);

    // 函数创建(后续详细说明)
    // JSValue func_val = JS_NewCFunction(ctx, my_func, "name", argc);

    // 释放所有值(重要!)
    JS_FreeValue(ctx, int_val);
    JS_FreeValue(ctx, float_val);
    JS_FreeValue(ctx, str_val);
    JS_FreeValue(ctx, obj_val);
    JS_FreeValue(ctx, arr_val);

    // null/undefined/bool 是特殊值,不需要释放
    // JS_FreeValue(ctx, null_val);     // 不需要
    // JS_FreeValue(ctx, undefined_val); // 不需要
    // JS_FreeValue(ctx, true_val);     // 不需要
}

类型检查

// type_check.c — JSValue 类型检查
#include "quickjs.h"
#include <stdio.h>

const char* get_js_type(JSContext *ctx, JSValue val) {
    int tag = JS_VALUE_GET_TAG(val);

    if (JS_IsNull(val))         return "null";
    if (JS_IsUndefined(val))    return "undefined";
    if (JS_IsBool(val))         return "boolean";
    if (JS_IsNumber(val))       return "number";
    if (JS_IsString(val))       return "string";
    if (JS_IsObject(val))       return "object";
    if (JS_IsFunction(ctx, val)) return "function";
    if (JS_IsArray(ctx, val))   return "array";
    if (JS_IsError(ctx, val))   return "error";
    if (JS_IsException(val))    return "exception";
    if (JS_IsBigInt(ctx, val))  return "bigint";

    return "unknown";
}

值的转换

// value_conversion.c — JSValue 类型转换
#include "quickjs.h"
#include <stdio.h>

void convert_values(JSContext *ctx) {
    JSValue str_val = JS_NewString(ctx, "12345");
    JSValue num_val = JS_NewFloat64(ctx, 3.14);
    JSValue bool_val = JS_TRUE;

    // 字符串 → 整数
    int32_t int_val;
    JS_ToInt32(ctx, &int_val, str_val);
    printf("String to int: %d\n", int_val); // 12345

    // 字符串 → 浮点数
    double dbl_val;
    JS_ToFloat64(ctx, &dbl_val, JS_NewString(ctx, "3.14"));
    printf("String to float: %f\n", dbl_val); // 3.140000

    // 数字 → 字符串
    const char *s = JS_ToCString(ctx, num_val);
    printf("Float to string: %s\n", s); // "3.14"
    JS_FreeCString(ctx, s);

    // 任意值 → C 字符串(调用 toString())
    JSValue js_str = JS_ToString(ctx, bool_val);
    const char *bool_str = JS_ToCString(ctx, js_str);
    printf("Bool to string: %s\n", bool_str); // "true"
    JS_FreeCString(ctx, bool_str);
    JS_FreeValue(ctx, js_str);

    // 释放
    JS_FreeValue(ctx, str_val);
    JS_FreeValue(ctx, num_val);
}

4.4 执行 JavaScript 代码

JS_Eval 基本用法

// eval_example.c — 执行 JavaScript 代码
#include "quickjs-libc.h"
#include <stdio.h>

int main() {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);

    // 添加标准库模块(console.log 等)
    js_init_module_std(ctx, "std");
    js_init_module_os(ctx, "os");

    // === 1. 执行简单表达式 ===
    JSValue result = JS_Eval(ctx, "1 + 2 + 3", 9, "<input>", 0);
    if (!JS_IsException(result)) {
        int32_t val;
        JS_ToInt32(ctx, &val, result);
        printf("1 + 2 + 3 = %d\n", val);
    }
    JS_FreeValue(ctx, result);

    // === 2. 执行多行代码 ===
    const char *code = R"(
        function fibonacci(n) {
            if (n <= 1) return n;
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
        fibonacci(10);
    )";
    result = JS_Eval(ctx, code, strlen(code), "<fibonacci>", 0);
    if (!JS_IsException(result)) {
        int32_t val;
        JS_ToInt32(ctx, &val, result);
        printf("fibonacci(10) = %d\n", val);
    }
    JS_FreeValue(ctx, result);

    // === 3. 处理异常 ===
    const char *bad_code = "throw new Error('test error')";
    result = JS_Eval(ctx, bad_code, strlen(bad_code), "<error>", 0);
    if (JS_IsException(result)) {
        // 获取异常对象
        JSValue exception = JS_GetException(ctx);
        JSValue message = JS_GetPropertyStr(ctx, exception, "message");
        const char *msg = JS_ToCString(ctx, message);
        printf("Caught exception: %s\n", msg);
        JS_FreeCString(ctx, msg);
        JS_FreeValue(ctx, message);
        JS_FreeValue(ctx, exception);
    }
    JS_FreeValue(ctx, result);

    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

执行标志(flags)

标志说明
JS_EVAL_TYPE_GLOBAL0全局代码
JS_EVAL_TYPE_MODULE(1 « 0)模块代码
JS_EVAL_TYPE_MASK(3 « 0)类型掩码
JS_EVAL_FLAG_STRICT(1 « 3)强制严格模式
JS_EVAL_FLAG_STRIP(1 « 4)去除源码信息
// 以模块模式执行
result = JS_Eval(ctx, code, code_len, "module.js", JS_EVAL_TYPE_MODULE);

// 以严格模式执行
result = JS_Eval(ctx, code, code_len, "strict.js", JS_EVAL_FLAG_STRICT);

4.5 C 函数回调到 JavaScript

基本函数注册

// callback_example.c — C 函数注册到 JavaScript
#include "quickjs-libc.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

// C 函数实现:JSValue (*)(JSContext*, JSValue, int, JSValue*)
static JSValue js_print(JSContext *ctx, JSValue this_val,
                        int argc, JSValue *argv) {
    for (int i = 0; i < argc; i++) {
        if (i > 0) printf(" ");
        const char *str = JS_ToCString(ctx, argv[i]);
        if (str) {
            printf("%s", str);
            JS_FreeCString(ctx, str);
        }
    }
    printf("\n");
    return JS_UNDEFINED;
}

// 带返回值的函数
static JSValue js_add(JSContext *ctx, JSValue this_val,
                      int argc, JSValue *argv) {
    double a, b;
    if (JS_ToFloat64(ctx, &a, argv[0])) return JS_EXCEPTION;
    if (JS_ToFloat64(ctx, &b, argv[1])) return JS_EXCEPTION;
    return JS_NewFloat64(ctx, a + b);
}

// 返回字符串的函数
static JSValue js_get_env(JSContext *ctx, JSValue this_val,
                          int argc, JSValue *argv) {
    const char *name = JS_ToCString(ctx, argv[0]);
    if (!name) return JS_EXCEPTION;

    const char *value = getenv(name);
    JS_FreeCString(ctx, name);

    if (value) {
        return JS_NewString(ctx, value);
    }
    return JS_UNDEFINED;
}

// 返回对象的函数
static JSValue js_get_system_info(JSContext *ctx, JSValue this_val,
                                   int argc, JSValue *argv) {
    JSValue obj = JS_NewObject(ctx);

    JS_SetPropertyStr(ctx, obj, "platform",
        JS_NewString(ctx, "linux"));
    JS_SetPropertyStr(ctx, obj, "arch",
        JS_NewString(ctx, "x86_64"));
    JS_SetPropertyStr(ctx, obj, "pid",
        JS_NewInt32(ctx, (int32_t)getpid()));

    return obj;
}

int main() {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);

    // 获取全局对象
    JSValue global = JS_GetGlobalObject(ctx);

    // 注册函数到全局
    JS_SetPropertyStr(ctx, global, "nativePrint",
        JS_NewCFunction(ctx, js_print, "nativePrint", 1));

    JS_SetPropertyStr(ctx, global, "nativeAdd",
        JS_NewCFunction(ctx, js_add, "nativeAdd", 2));

    JS_SetPropertyStr(ctx, global, "getEnv",
        JS_NewCFunction(ctx, js_get_env, "getEnv", 1));

    JS_SetPropertyStr(ctx, global, "getSystemInfo",
        JS_NewCFunction(ctx, js_get_system_info, "getSystemInfo", 0));

    JS_FreeValue(ctx, global);

    // 在 JavaScript 中调用 C 函数
    const char *code = R"(
        nativePrint("Hello from JS!");

        const sum = nativeAdd(3.14, 2.86);
        nativePrint("Sum:", sum);

        const home = getEnv("HOME");
        nativePrint("HOME:", home);

        const info = getSystemInfo();
        nativePrint("System:", JSON.stringify(info, null, 2));
    )";

    JSValue result = JS_Eval(ctx, code, strlen(code), "<callback>", 0);
    if (JS_IsException(result)) {
        JSValue ex = JS_GetException(ctx);
        const char *msg = JS_ToCString(ctx, ex);
        fprintf(stderr, "Error: %s\n", msg);
        JS_FreeCString(ctx, msg);
        JS_FreeValue(ctx, ex);
    }
    JS_FreeValue(ctx, result);

    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

4.6 对象属性操作

// object_example.c — JavaScript 对象的 C API 操作
#include "quickjs-libc.h"
#include <stdio.h>

void manipulate_object(JSContext *ctx) {
    // 创建对象
    JSValue obj = JS_NewObject(ctx);

    // 设置属性(字符串键)
    JS_SetPropertyStr(ctx, obj, "name", JS_NewString(ctx, "QuickJS"));
    JS_SetPropertyStr(ctx, obj, "version", JS_NewFloat64(ctx, 2024.01));
    JS_SetPropertyStr(ctx, obj, "active", JS_TRUE);
    JS_SetPropertyStr(ctx, obj, "count", JS_NewInt32(ctx, 42));

    // 创建嵌套对象
    JSValue nested = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, nested, "x", JS_NewInt32(ctx, 10));
    JS_SetPropertyStr(ctx, nested, "y", JS_NewInt32(ctx, 20));
    JS_SetPropertyStr(ctx, obj, "position", nested);

    // 创建数组属性
    JSValue arr = JS_NewArray(ctx);
    JS_SetPropertyUint32(ctx, arr, 0, JS_NewString(ctx, "tag1"));
    JS_SetPropertyUint32(ctx, arr, 1, JS_NewString(ctx, "tag2"));
    JS_SetPropertyUint32(ctx, arr, 2, JS_NewString(ctx, "tag3"));
    JS_SetPropertyStr(ctx, obj, "tags", arr);

    // 读取属性
    JSValue name_val = JS_GetPropertyStr(ctx, obj, "name");
    const char *name = JS_ToCString(ctx, name_val);
    printf("name: %s\n", name);
    JS_FreeCString(ctx, name);
    JS_FreeValue(ctx, name_val);

    // 读取数字属性
    JSValue version_val = JS_GetPropertyStr(ctx, obj, "version");
    double version;
    JS_ToFloat64(ctx, &version, version_val);
    printf("version: %.2f\n", version);
    JS_FreeValue(ctx, version_val);

    // 检查属性是否存在
    JSValue check = JS_GetPropertyStr(ctx, obj, "nonexistent");
    if (JS_IsUndefined(check)) {
        printf("Property 'nonexistent' is undefined\n");
    }
    JS_FreeValue(ctx, check);

    // 删除属性
    JS_DeletePropertyStr(ctx, obj, "count", 0);

    // 使用 JSValue 作为键
    JSAtom atom = JS_NewAtom(ctx, "active");
    JSValue active_val = JS_GetProperty(ctx, obj, atom);
    printf("active: %s\n", JS_ToBool(ctx, active_val) ? "true" : "false");
    JS_FreeValue(ctx, active_val);
    JS_FreeAtom(ctx, atom);

    // 将对象转为 JSON 字符串
    JSValue json_fn = JS_GetPropertyStr(ctx, JS_GetGlobalObject(ctx), "JSON");
    JSValue stringify = JS_GetPropertyStr(ctx, json_fn, "stringify");
    JSValue json_args[] = { obj, JS_NULL, JS_NewInt32(ctx, 2) };
    JSValue json_str = JS_Call(ctx, stringify, json_fn, 3, json_args);
    const char *json = JS_ToCString(ctx, json_str);
    printf("JSON:\n%s\n", json);
    JS_FreeCString(ctx, json);
    JS_FreeValue(ctx, json_str);
    JS_FreeValue(ctx, stringify);
    JS_FreeValue(ctx, json_fn);

    // 释放对象(级联释放其属性)
    JS_FreeValue(ctx, obj);
}

4.7 自定义对象与类

定义自定义类

// custom_class_example.c — 定义 C 驱动的 JavaScript 类
#include "quickjs-libc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// C 结构体:存储 Point 的数据
typedef struct {
    double x;
    double y;
} PointData;

// 类 ID(每个自定义类需要唯一 ID)
static JSClassID js_point_class_id;

// 析构函数
static void js_point_finalizer(JSRuntime *rt, JSValue val) {
    PointData *point = JS_GetOpaque(val, js_point_class_id);
    if (point) {
        free(point);
    }
}

// 类定义
static JSClassDef js_point_class = {
    "Point",
    .finalizer = js_point_finalizer,
};

// 构造函数 new Point(x, y)
static JSValue js_point_constructor(JSContext *ctx, JSValue this_val,
                                     int argc, JSValue *argv) {
    double x = 0, y = 0;

    if (argc >= 1) JS_ToFloat64(ctx, &x, argv[0]);
    if (argc >= 2) JS_ToFloat64(ctx, &y, argv[1]);

    PointData *point = malloc(sizeof(PointData));
    point->x = x;
    point->y = y;

    JSValue obj = JS_NewObjectClass(ctx, js_point_class_id);
    if (JS_IsException(obj)) {
        free(point);
        return obj;
    }

    JS_SetOpaque(obj, point);
    return obj;
}

// 方法:point.distance(other)
static JSValue js_point_distance(JSContext *ctx, JSValue this_val,
                                  int argc, JSValue *argv) {
    PointData *p1 = JS_GetOpaque(this_val, js_point_class_id);
    PointData *p2 = JS_GetOpaque(argv[0], js_point_class_id);
    if (!p1 || !p2) return JS_EXCEPTION;

    double dx = p1->x - p2->x;
    double dy = p1->y - p2->y;
    return JS_NewFloat64(ctx, sqrt(dx * dx + dy * dy));
}

// 方法:point.toString()
static JSValue js_point_toString(JSContext *ctx, JSValue this_val,
                                  int argc, JSValue *argv) {
    PointData *p = JS_GetOpaque(this_val, js_point_class_id);
    if (!p) return JS_EXCEPTION;

    char buf[64];
    snprintf(buf, sizeof(buf), "Point(%.2f, %.2f)", p->x, p->y);
    return JS_NewString(ctx, buf);
}

// 属性 getter: point.x
static JSValue js_point_get_x(JSContext *ctx, JSValue this_val) {
    PointData *p = JS_GetOpaque(this_val, js_point_class_id);
    return p ? JS_NewFloat64(ctx, p->x) : JS_UNDEFINED;
}

// 属性 setter: point.x = value
static JSValue js_point_set_x(JSContext *ctx, JSValue this_val, JSValue val) {
    PointData *p = JS_GetOpaque(this_val, js_point_class_id);
    if (p) JS_ToFloat64(ctx, &p->x, val);
    return JS_UNDEFINED;
}

// 注册类
void js_point_init(JSContext *ctx) {
    // 分配类 ID
    JS_NewClassID(&js_point_class_id);
    JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class);

    JSValue proto = JS_NewObject(ctx);

    // 定义方法
    JS_SetPropertyStr(ctx, proto, "distance",
        JS_NewCFunction(ctx, js_point_distance, "distance", 1));
    JS_SetPropertyStr(ctx, proto, "toString",
        JS_NewCFunction(ctx, js_point_toString, "toString", 0));

    // 定义访问器属性(getter/setter)
    JS_SetPropertyGetSet(ctx, proto,
        JS_NewAtom(ctx, "x"),
        JS_NewCFunction2(ctx, js_point_get_x, "get x", 0, JS_CFUNC_getter),
        JS_NewCFunction2(ctx, js_point_set_x, "set x", 1, JS_CFUNC_setter));

    // 注册构造函数
    JSValue ctor = JS_NewCFunction2(ctx, js_point_constructor,
                                     "Point", 2, JS_CFUNC_constructor, 0);
    JS_SetConstructor(ctx, ctor, proto);
    JS_SetClassProto(ctx, js_point_class_id, proto);

    // 挂载到全局
    JSValue global = JS_GetGlobalObject(ctx);
    JS_SetPropertyStr(ctx, global, "Point", ctor);
    JS_FreeValue(ctx, global);
}

int main() {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);

    js_init_module_std(ctx, "std");
    js_init_module_os(ctx, "os");
    js_point_init(ctx);

    const char *code = R"(
        const p1 = new Point(0, 0);
        const p2 = new Point(3, 4);

        print(p1.toString());          // Point(0.00, 0.00)
        print(p2.toString());          // Point(3.00, 4.00)
        print("Distance:", p1.distance(p2)); // 5

        p1.x = 10;
        p1.y = 20;
        print("p1 after modify:", p1.toString()); // Point(10.00, 20.00)
    )";

    JSValue result = JS_Eval(ctx, code, strlen(code), "<custom_class>", 0);
    if (JS_IsException(result)) {
        JSValue ex = JS_GetException(ctx);
        const char *msg = JS_ToCString(ctx, ex);
        fprintf(stderr, "Error: %s\n", msg);
        JS_FreeCString(ctx, msg);
        JS_FreeValue(ctx, ex);
    }
    JS_FreeValue(ctx, result);

    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

4.8 内存管理

引用计数规则

QuickJS 使用**引用计数(Reference Counting)进行主要的内存管理,辅以循环引用检测(Cycle Detection)**的垃圾回收。

规则说明
创建即拥有JS_New*() 返回的值拥有 1 个引用
获取需释放JS_Get*() 返回的值增加了引用计数,用完需 JS_FreeValue()
传递即转移JSValue 传递给 JS_Set*() 等函数时,所有权被转移
JS_DupValue()手动增加引用计数
JS_FreeValue()减少引用计数,计数为 0 时释放

常见错误模式

// ❌ 错误:忘记释放
void bad_example(JSContext *ctx) {
    JSValue str = JS_NewString(ctx, "hello");
    // 忘记 JS_FreeValue(ctx, str); — 内存泄漏!
}

// ❌ 错误:重复释放
void bad_example2(JSContext *ctx) {
    JSValue str = JS_NewString(ctx, "hello");
    JS_FreeValue(ctx, str);
    JS_FreeValue(ctx, str);  // 双重释放!
}

// ❌ 错误:使用已释放的值
void bad_example3(JSContext *ctx) {
    JSValue str = JS_NewString(ctx, "hello");
    JS_FreeValue(ctx, str);
    const char *s = JS_ToCString(ctx, str);  // 悬垂引用!
    printf("%s\n", s);
    JS_FreeCString(ctx, s);
}

// ✅ 正确的内存管理模式
void good_example(JSContext *ctx) {
    JSValue str = JS_NewString(ctx, "hello");
    const char *s = JS_ToCString(ctx, str);
    if (s) {
        printf("%s\n", s);
        JS_FreeCString(ctx, s);  // 释放 C 字符串
    }
    JS_FreeValue(ctx, str);     // 释放 JSValue
}

内存使用监控

// memory_monitor.c — 监控 QuickJS 内存使用
#include "quickjs-libc.h"
#include <stdio.h>

void print_memory_info(JSRuntime *rt) {
    JSMemoryUsage usage;
    JS_ComputeMemoryUsage(rt, &usage);

    printf("=== Memory Usage ===\n");
    printf("  malloc_size:    %10zu bytes\n", (size_t)usage.malloc_size);
    printf("  malloc_limit:   %10zu bytes\n", (size_t)usage.malloc_limit);
    printf("  memory_used:    %10zu bytes\n", (size_t)usage.memory_used_size);
    printf("  memory_limit:   %10zu bytes\n", (size_t)usage.memory_limit);
    printf("  obj_count:      %10zu\n", (size_t)usage.obj_count);
    printf("  str_count:      %10zu\n", (size_t)usage.str_count);
    printf("  obj_size:       %10zu bytes\n", (size_t)usage.obj_size);
    printf("  str_size:       %10zu bytes\n", (size_t)usage.str_size);
    printf("  gc_count:       %10zu\n", (size_t)usage.gc_count);
}

4.9 完整嵌入示例

// complete_embed.c — 完整的 QuickJS 嵌入示例
#include "quickjs-libc.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 工具函数:打印异常
static void print_exception(JSContext *ctx) {
    JSValue exception = JS_GetException(ctx);
    JSValue message = JS_GetPropertyStr(ctx, exception, "message");
    JSValue stack = JS_GetPropertyStr(ctx, exception, "stack");

    const char *msg = JS_ToCString(ctx, message);
    const char *stk = JS_ToCString(ctx, stack);

    fprintf(stderr, "Error: %s\n", msg);
    if (stk && strcmp(stk, "undefined") != 0) {
        fprintf(stderr, "Stack:\n%s\n", stk);
    }

    JS_FreeCString(ctx, msg);
    JS_FreeCString(ctx, stk);
    JS_FreeValue(ctx, stack);
    JS_FreeValue(ctx, message);
    JS_FreeValue(ctx, exception);
}

// 工具函数:安全执行代码
static JSValue safe_eval(JSContext *ctx, const char *code, const char *filename) {
    JSValue result = JS_Eval(ctx, code, strlen(code),
                              filename, JS_EVAL_TYPE_GLOBAL);
    if (JS_IsException(result)) {
        print_exception(ctx);
    }
    return result;
}

int main(int argc, char **argv) {
    int ret = 0;

    // 创建运行时和上下文
    JSRuntime *rt = JS_NewRuntime();
    JS_SetMemoryLimit(rt, 64 * 1024 * 1024);  // 64MB
    JS_SetMaxStackSize(rt, 512 * 1024);         // 512KB

    JSContext *ctx = JS_NewContext(rt);

    // 加载标准库
    js_init_module_std(ctx, "std");
    js_init_module_os(ctx, "os");

    // 执行初始化代码
    const char *init_code = R"(
        // 定义应用全局配置
        globalThis.AppConfig = {
            name: "MyApp",
            version: "1.0.0",
            debug: false
        };

        // 定义日志函数
        globalThis.log = function(...args) {
            if (AppConfig.debug) {
                console.log("[DEBUG]", ...args);
            }
        };
    )";

    JSValue init_result = safe_eval(ctx, init_code, "init.js");
    if (JS_IsException(init_result)) {
        ret = 1;
        goto cleanup;
    }
    JS_FreeValue(ctx, init_result);

    // 如果有命令行参数,执行指定文件
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            // 使用 std 模块加载文件
            char code[1024];
            snprintf(code, sizeof(code),
                "import * as std from 'std';\n"
                "const content = std.loadFile('%s');\n"
                "if (content === null) { throw new Error('Cannot read: %s'); }\n"
                "content;",
                argv[i], argv[i]);

            JSValue file_content = JS_Eval(ctx, code, strlen(code),
                                            argv[i], JS_EVAL_TYPE_MODULE);
            if (JS_IsException(file_content)) {
                print_exception(ctx);
                ret = 1;
                continue;
            }

            const char *content = JS_ToCString(ctx, file_content);
            if (content) {
                JSValue result = safe_eval(ctx, content, argv[i]);
                if (JS_IsException(result)) ret = 1;
                JS_FreeValue(ctx, result);
                JS_FreeCString(ctx, content);
            }
            JS_FreeValue(ctx, file_content);
        }
    } else {
        // 进入 REPL 模式
        printf("QuickJS Embedded REPL (type 'exit' to quit)\n");
        char line[4096];
        while (1) {
            printf("js> ");
            if (!fgets(line, sizeof(line), stdin)) break;
            if (strncmp(line, "exit", 4) == 0) break;

            JSValue result = safe_eval(ctx, line, "<repl>");
            if (!JS_IsException(result) && !JS_IsUndefined(result)) {
                // 使用 console.log 打印结果
                JSValue console = JS_GetPropertyStr(ctx,
                    JS_GetGlobalObject(ctx), "console");
                JSValue log_fn = JS_GetPropertyStr(ctx, console, "log");
                JS_Call(ctx, log_fn, console, 1, &result);
                JS_FreeValue(ctx, log_fn);
                JS_FreeValue(ctx, console);
            }
            JS_FreeValue(ctx, result);
        }
    }

cleanup:
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return ret;
}

4.10 C API 速查表

函数说明
JS_NewRuntime()创建运行时
JS_FreeRuntime(rt)释放运行时
JS_NewContext(rt)创建上下文
JS_FreeContext(ctx)释放上下文
JS_Eval(ctx, code, len, file, flags)执行 JS 代码
JS_GetGlobalObject(ctx)获取全局对象
JS_NewString(ctx, str)创建字符串
JS_NewInt32(ctx, val)创建 32 位整数
JS_NewFloat64(ctx, val)创建浮点数
JS_NewObject(ctx)创建空对象
JS_NewArray(ctx)创建空数组
JS_NewCFunction(ctx, fn, name, argc)创建 C 函数包装
JS_SetPropertyStr(ctx, obj, name, val)设置属性
JS_GetPropertyStr(ctx, obj, name)获取属性
JS_Call(ctx, fn, this, argc, argv)调用函数
JS_FreeValue(ctx, val)释放值
JS_DupValue(ctx, val)增加引用计数
JS_ToCString(ctx, val)转为 C 字符串
JS_FreeCString(ctx, str)释放 C 字符串
JS_IsException(val)检查是否异常
JS_GetException(ctx)获取当前异常
JS_NewClassID(id)分配类 ID
JS_NewClass(rt, id, def)注册类定义
JS_SetOpaque(obj, ptr)设置对象私有数据
JS_GetOpaque(val, id)获取对象私有数据

4.11 本章小结

要点说明
三层结构Runtime → Context → Value
JSValue统一的值表示,需要手动管理引用计数
JS_Eval执行 JavaScript 代码的核心函数
C 回调使用 JS_NewCFunction 注册 C 函数
自定义类使用 JSClassID + JSClassDef 定义类
内存管理引用计数为主,GC 处理循环引用

扩展阅读