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 |
JSValue | JavaScript 值的 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_NULL | 0 | null |
JS_TAG_UNDEFINED | 1 | undefined |
JS_TAG_BOOL | 2 | boolean |
JS_TAG_INT | 3 | number(整数) |
JS_TAG_FLOAT64 | 4 | number(浮点数) |
JS_TAG_STRING | -1 | string |
JS_TAG_OBJECT | -2 | object、function、array |
JS_TAG_BIG_INT | -3 | bigint |
JS_TAG_BIG_DECIMAL | -4 | bigdecimal |
JS_TAG_BIG_FLOAT | -5 | bigfloat |
JS_TAG_SYMBOL | -6 | symbol |
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_GLOBAL | 0 | 全局代码 |
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 处理循环引用 |
扩展阅读