Rust QuickJS绑定库rquickjs-sys的使用:高性能JavaScript引擎集成与安全执行环境
Rust QuickJS绑定库rquickjs-sys的使用:高性能JavaScript引擎集成与安全执行环境
关于rquickjs-sys
rquickjs-sys是QuickJS JavaScript引擎的低级不安全原始绑定。QuickJS是一个轻量级的JavaScript引擎,由Fabrice Bellard开发。
注意:通常您不应该直接使用这个crate,而是使用rquickjs crate,它提供了高级安全绑定。
补丁
为了修复错误并支持一些未实现的功能,应用了一系列补丁。
热修复:
- 修复堆栈溢出检查(对Rust很重要)
- 为
JS_NewClassID
添加原子支持(对Rust很重要) - 无限处理(用
INFINITY
常量替换1.0 / 0.0
)
特殊补丁:
- 读取模块导出(
exports
功能) - 重置堆栈函数(
parallel
功能) - MSVC支持
安装
在项目目录中运行以下Cargo命令:
cargo add rquickjs-sys
或者在Cargo.toml中添加以下行:
rquickjs-sys = "0.9.0"
完整示例代码
以下是一个使用rquickjs-sys的完整示例,展示如何创建QuickJS运行时并执行JavaScript代码:
use rquickjs_sys::{JS_NewRuntime, JS_NewContext, JS_Eval, JS_FreeContext, JS_FFreeRuntime};
use std::ffi::CString;
use std::ptr;
// 完整示例:创建JS运行时、执行代码并处理结果
fn main() {
// 1. 创建运行时
let rt = unsafe { JS_NewRuntime() };
if rt.is_null() {
panic!("无法创建JS运行时");
}
// 2. 创建执行上下文
let ctx = unsafe { JS_NewContext(rt) };
if ctx.is_null() {
unsafe { JS_FreeRuntime(rt) };
panic!("无法创建JS上下文");
}
// 3. 准备要执行的JS代码
let js_code = CString::new("
function factorial(n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
factorial(5);
").unwrap();
// 4. 执行JS代码
let result = unsafe {
JS_Eval(
ctx,
js_code.as_ptr(),
js_code.as_bytes().len() as _,
b"script.js\0".as_ptr() as _,
0
)
};
// 5. 处理执行结果
if result.is_null() {
println!("JS代码执行出错");
} else {
// 将结果转换为整数
let mut result_value = 0;
unsafe { JS_ToInt32(ctx, &mut result_value, result) };
println!("JS执行结果: {}", result_value);
// 释放结果值
unsafe { JS_FreeValue(ctx, result) };
}
// 6. 清理资源
unsafe {
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
}
}
更高级的示例
下面是一个更完整的示例,展示了模块加载、异常处理和异步执行:
use rquickjs_sys::{
JS_NewRuntime, JS_NewContext, JS_Eval, JS_FreeContext, JS_FreeRuntime,
JS_GetGlobalObject, JS_SetPropertyStr, JS_NewInt32, JS_Call, JS_ToInt32,
JS_NewString, JS_GetPropertyStr, JS_FreeValue, JS_IsException, JS_GetException
};
use std::ffi::CString;
use std::ptr;
fn main() {
// 1. 初始化运行时和上下文
let rt = unsafe { JS_NewRuntime() };
let ctx = unsafe { JS_NewContext(rt) };
// 2. 设置全局变量
let global_obj = unsafe { JS_GetGlobalObject(ctx) };
let var_name = CString::new("appName").unwrap();
let var_value = unsafe { JS_NewString(ctx, b"MyRustApp\0".as_ptr() as _) };
unsafe { JS_SetPropertyStr(ctx, global_obj, var_name.as_ptr(), var_value) };
// 3. 定义JS函数
let func_code = CString::new(r#"
function calculate(data) {
if (!data) throw new Error("Invalid input");
return {
sum: data.a + data.b,
product: data.a * data.b
};
}
"#).unwrap();
// 4. 执行函数定义
unsafe {
JS_Eval(
ctx,
func_code.as_ptr(),
func_code.as_bytes().len() as _,
b"module.js\0".as_ptr() as _,
0
)
};
// 5. 准备调用参数
let func_name = CString::new("calculate").unwrap();
let func_val = unsafe { JS_GetPropertyStr(ctx, global_obj, func_name.as_ptr()) };
// 6. 创建参数对象
let arg_obj = unsafe { JS_NewObject(ctx) };
let prop_a = CString::new("a").unwrap();
let prop_b = CString::new("b").unwrap();
unsafe {
JS_SetPropertyStr(ctx, arg_obj, prop_a.as_ptr(), JS_NewInt32(ctx, 3));
JS_SetPropertyStr(ctx, arg_obj, prop_b.as_ptr(), JS_NewInt32(ctx, 4));
}
// 7. 调用函数
let args = [arg_obj];
let result = unsafe {
JS_Call(ctx, func_val, global_obj, args.len() as i32, args.as_ptr())
};
// 8. 处理结果或异常
if unsafe { JS_IsException(result) } {
let exception = unsafe { JS_GetException(ctx) };
let exception_str = unsafe { JS_NewString(ctx, b"Error\0".as_ptr() as _) };
println!("JS异常发生");
unsafe { JS_FreeValue(ctx, exception) };
} else {
// 从结果对象中提取属性
let sum_prop = CString::new("sum").unwrap();
let sum_val = unsafe { JS_GetPropertyStr(ctx, result, sum_prop.as_ptr()) };
let mut sum = 0;
unsafe { JS_ToInt32(ctx, &mut sum, sum_val) };
let product_prop = CString::new("product").unwrap();
let product_val = unsafe { JS_GetPropertyStr(ctx, result, product_prop.as_ptr()) };
let mut product = 0;
unsafe { JS_ToInt32(ctx, &mut product, product_val) };
println!("计算结果 - 和: {}, 积: {}", sum, product);
unsafe {
JS_FreeValue(ctx, sum_val);
JS_FreeValue(ctx, product_val);
}
}
// 9. 清理资源
unsafe {
JS_FreeValue(ctx, result);
JS_FreeValue(ctx, arg_obj);
JS_FreeValue(ctx, func_val);
JS_FreeValue(ctx, global_obj);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
}
}
最佳实践
- 使用
unsafe
块隔离所有QuickJS调用 - 确保所有创建的JS值都被正确释放
- 检查所有可能返回空指针的调用
- 考虑使用更高级的rquickjs包装库
- 实现错误处理机制处理JS异常
1 回复
Rust QuickJS绑定库rquickjs-sys使用指南
简介
rquickjs-sys是Rust语言对QuickJS JavaScript引擎的绑定库,提供了高性能的JavaScript执行环境集成能力。QuickJS是一个轻量级、可嵌入的JavaScript引擎,具有以下特点:
- 极小的内存占用和启动时间
- 几乎完整的ES2020支持
- 可选的内存安全限制
- 支持模块加载系统
基本使用方法
添加依赖
首先在Cargo.toml中添加依赖:
[dependencies]
rquickjs-sys = "0.4"
基本示例
use rquickjs_sys as q;
fn main() {
// 创建运行时
let rt = unsafe { q::JS_NewRuntime() };
// 创建上下文
let ctx = unsafe { q::JS_NewContext(rt) };
// 执行简单JavaScript代码
let code = "1 + 2";
let val = unsafe {
q::JS_Eval(
ctx,
code.as_ptr() as *const i8,
code.len(),
"script.js".as_ptr() as *const i8,
q::JS_EVAL_TYPE_GLOBAL as i32,
)
};
// 检查执行结果
if unsafe { q::JS_IsException(val) } != 0 {
// 处理错误
let ex = unsafe { q::JS_GetException(ctx) };
let mut len = 0;
let cstr = unsafe { q::JS_ToCStringLen(ctx, &mut len, ex) };
let err_str = unsafe { std::ffi::CStr::from_ptr(cstr) }.to_string_lossy();
println!("Error: {}", err_str);
unsafe { q::JS_F极CString(ctx, cstr) };
} else {
// 获取结果
let mut num = 0;
unsafe { q::JS_ToInt32(ctx, &mut num, val) };
println!("Result: {}", num);
}
// 清理资源
unsafe {
q::JS_FreeValue(ctx, val);
q::JS_FreeContext(ctx);
q::JS_FreeRuntime(rt);
}
}
完整示例代码
下面是一个结合了基本使用、内存限制和Rust-JS互操作的完整示例:
use rquickjs_sys as q;
use std::ffi::CString;
// 暴露给JS的Rust函数 - 计算斐波那契数列
extern "C" fn fibonacci(ctx: *mut q::JSContext, _this: q::JSValue, argc: i32, argv: *mut q::JSValue) -> q::JSValue {
if argc < 1 {
return unsafe { q::JS_ThrowTypeError(ctx, "Expected 1 argument".as_ptr() as *const i8) };
}
let mut n = 0;
unsafe { q::JS_ToInt32(ctx, &mut n, *argv) };
fn fib(n: i32) -> i32 {
match n {
0 => 0,
1 => 1,
_ => fib(n - 1) + fib(n - 2)
}
}
unsafe { q::JS_NewInt32(ctx, fib(n)) }
}
fn main() {
// 1. 初始化运行时和上下文
let rt = unsafe { q::JS_NewRuntime() };
let ctx = unsafe { q::JS_NewContext(rt) };
// 2. 设置内存限制为8MB
unsafe {
q::JS_SetMemoryLimit(rt, 8 * 1024 * 1024);
q::JS_SetGCThreshold(rt, 1 * 1024 * 1024);
}
// 3. 暴露Rust函数给JS
let global = unsafe { q::JS_GetGlobalObject(ctx) };
let func_name = CString::new("fib").unwrap();
let func = unsafe { q::JS_NewCFunction(ctx, Some(fibonacci), func_name.as_ptr(), 1) };
unsafe {
q::JS_SetPropertyStr(ctx, global, func_name.as_ptr(), func);
q::JS_FreeValue(ctx, global);
}
// 4. 执行JS代码
let js_code = r#"
function test() {
try {
console.log('Fib(10) =', fib(10));
console.log('Fib(20) =', fib(20));
// 测试错误处理
console.log('Result:', notDefinedFunction());
} catch (e) {
console.error('Caught error:', e);
}
}
test();
"#;
let val = unsafe {
q::JS_Eval(
ctx,
js_code.as_ptr() as *const i8,
js_code.len(),
"script.js".as_ptr() as *const i8,
q::JS_EVAL_TYPE_GLOBAL as i32,
)
};
// 5. 处理执行结果
if unsafe { q::JS_IsException(val) } != 0 {
let ex = unsafe { q::JS_GetException(ctx) };
let mut len = 0;
let cstr = unsafe { q::JS_ToCStringLen(ctx, &mut len, ex) };
let err_str = unsafe { std::ffi::CStr::from_ptr(cstr) }.to_string_lossy();
println!("JS Error: {}", err_str);
unsafe { q::JS_FreeCString(ctx, cstr) };
} else {
println!("JS executed successfully");
}
// 6. 清理资源
unsafe {
q::JS_FreeValue(ctx, val);
q::JS_FreeContext(ctx);
q::JS_FreeRuntime(rt);
}
}
安全注意事项
- 大部分rquickjs-sys函数都是不安全的,需要手动管理内存和生命周期
- 确保在释放运行时前释放所有上下文
- 处理JavaScript异常以避免内存泄漏
- 考虑使用更高级的封装库(如rquickjs)以获得更安全的API
性能优化建议
- 重用运行时和上下文对象
- 预编译常用脚本
- 合理设置内存限制
- 避免频繁的Rust-JavaScript边界跨越
总结
rquickjs-sys提供了Rust与QuickJS引擎的低级绑定,适合需要精细控制JavaScript执行环境的高级场景。对于更简单的用例,可以考虑基于rquickjs-sys构建的高级封装库。