15 个版本

0.2.3 2024年8月1日
0.2.2 2024年7月22日
0.2.1-alpha.12024年6月24日
0.1.12 2024年6月3日
0.1.10 2024年5月21日

#253 in Web编程

Download history 669/week @ 2024-05-13 280/week @ 2024-05-20 2/week @ 2024-05-27 188/week @ 2024-06-03 180/week @ 2024-06-10 101/week @ 2024-06-24 10/week @ 2024-07-01 139/week @ 2024-07-22 154/week @ 2024-07-29

每月303次下载

MIT许可协议

1.5MB
35K SLoC

Rust-JSC

Crates.io Docs.rs

Rust-JSC 是一个Rust库,它为JavaScriptCore引擎提供了一个高级绑定。它允许您从Rust应用程序中与JavaScript代码进行交互。

功能

  • JavaScriptCore引擎的高级绑定
  • 扩展API以与JavaScriptCore交互
  • 支持ES模块
  • 支持rust原生模块(合成模块)

安装

将以下行添加到您的 Cargo.toml 文件中

[dependencies]
rust_jsc = { version = "0.2.2" }

使用方法

评估脚本

use rust_jsc::JSContext;

let ctx = JSContext::new();
let result = ctx.evaluate_script("console.log('Hello, world!'); 'kedojs'", Some(0));
assert!(result.is_ok());

评估模块

use rust_jsc::JSContext;

let filename = "/path/filename.js";
let ctx = JSContext::new();
let result = ctx.evaluate_module(filename);
assert!(result.is_ok());

类型数组

use crate::{JSArrayBuffer, JSContext, JSTypedArray, JSTypedArrayType};

fn main() {
    let ctx = JSContext::new();
    let array = ctx
        .evaluate_script("const array = new Uint8Array([5, 4, 4, 5]); array", None)
        .unwrap();
    let array = JSTypedArray::from_value(array).unwrap();

    assert_eq!(array.array_type().unwrap(), JSTypedArrayType::Uint8Array);
    assert_eq!(array.len().unwrap(), 4);
    assert_eq!(array.byte_offset().unwrap(), 0);
    assert_eq!(array.byte_len().unwrap(), 4);
    assert_eq!(array.as_vec::<u8>().unwrap(), &[5, 4, 4, 5]);
}

数组

use rust_jsc::{JSArray, JSContext, JSValue};

let ctx = JSContext::new();
let array = JSArray::new_array(
    &ctx,
    &[
        JSValue::number(&ctx, 1.0),
        JSValue::number(&ctx, 2.0),
        JSValue::number(&ctx, 3.0),
     ]
).unwrap();
assert_eq!(array.as_string().unwrap(), "1,2,3");

回调

use rust_jsc::{JSContext, JSFunction, JSObject, JSValue};

#[callback]
fn log_info(
    ctx: JSContext,
    _function: JSObject,
    _this: JSObject,
    arguments: &[JSValue],
) -> JSResult<JSValue> {
    let message = arguments.get(0).unwrap().as_string().unwrap();
    println!("INFO: {}", message);

    Ok(JSValue::undefined(&ctx))
}

let ctx = JSContext::new();
let global_object = ctx.global_object();

let object = JSObject::new(&ctx);
let attributes = PropertyDescriptorBuilder::new()
    .writable(true)
    .configurable(true)
    .enumerable(true)
    .build();
let function = JSFunction::callback(&ctx, Some("log"), Some(log_info));
object
    .set_property("log", &function, attributes)
    .unwrap();

global_object
    .set_property("console", &object, attributes)
    .unwrap();

let result = ctx.evaluate_script("console.log('Hello, World!')", None);
assert!(result.is_ok());

合成模块

use rust_jsc::{
    callback, module_evaluate, module_fetch, module_import_meta, module_resolve,
    JSContext, JSFunction, JSObject, JSResult, JSString, JSStringRetain, JSValue, JSPromise,
    PropertyDescriptorBuilder, JSModuleLoader, PropertyDescriptor,
};

#[module_resolve]
fn module_loader_resolve(
    ctx: JSContext,
    key: JSValue,
    referrer: JSValue,
    script_fetcher: JSValue,
) -> JSStringRetain {
    JSStringRetain::from("@rust-jsc")
}

#[module_evaluate]
fn module_loader_evaluate(
    ctx: JSContext,
    key: JSValue,
) -> JSValue {

    // Module Loader Evaluate
    // is called only when evaluating Synthetic Modules
    let object = JSObject::new(&ctx);
    let keydata = JSValue::string(&ctx, "name");
    let value = JSValue::string(&ctx, "John Doe");
    object.set(&keydata, &value, PropertyDescriptor::default()).unwrap();

    object.into()
}

#[module_fetch]
fn module_loader_fetch(
    ctx: JSContext,
    key: JSValue,
    attributes_value: JSValue,
    script_fetcher: JSValue,
) -> JSStringRetain {
    // Module Loader Fetch
    // fetch the content from file or network
    JSStringRetain::from("let name = 'Kedojs'; export default name;")
}

#[module_import_meta]
fn module_loader_create_import_meta_properties(
    ctx: JSContext,
    key: JSValue,
    script_fetcher: JSValue,
) -> JSObject {
    let key_value = key.as_string().unwrap();

    let object = JSObject::new(&ctx);
    object.set_property("url", &key, Default::default()).unwrap();
    object
}

fn main() {
    let ctx = JSContext::new();
    let global_object = ctx.global_object();

    let module_loader = JSAPIModuleLoader {
        // Disable the builtin file system loader
        disableBuiltinFileSystemLoader: true,
        moduleLoaderResolve: Some(module_loader_resolve),
        moduleLoaderEvaluate: Some(module_loader_evaluate),
        moduleLoaderFetch: Some(module_loader_fetch),
        moduleLoaderCreateImportMetaProperties: Some(
            module_loader_create_import_meta_properties,
        ),
    };
    ctx.set_module_loader(module_loader);

    let result = ctx.evaluate_module("./test.js");
    assert!(result.is_ok());
}

支持的平台

下表显示了支持的平台

平台 架构 目标 支持
macOS x86_64 x86_64-apple-darwin
macOS aarch64 aarch64-apple-darwin
Linux x86_64 x86_64-unknown-linux-gnu
Linux aarch64 aarch64-unknown-linux-gnu 即将推出..
Linux x86_64 x86_64-unknown-linux-musl 即将推出..
Linux aarch64 aarch64-unknown-linux-musl 即将推出..
Windows x86_64 x86_64-pc-windows-msvc

常见问题解答

如何构建静态库?

默认情况下,此库将尝试从GitHub镜像下载静态库。如果您想自己构建静态库,可以克隆rust-jsc仓库并从Dockerfile构建Docker镜像。它将为您构建静态库并将它们复制到提供的路径。

DOCKER_BUILDKIT=1 docker build -o ./.libs -t $(IMAGE_NAME) .

此命令仅在Linux上有效。对于macOS,您应从Makefile运行以下命令来构建JavaScriptCore的静态库

make build-jsc

然后设置环境变量RUST_JSC_CUSTOM_BUILD_PATH为静态库的路径。

⚠️ 请注意,此库使用自定义版本的WebKit来生成绑定。这个版本的WebKit是原始WebKit的一个分支,包含一些补丁以支持esmodules和其他功能。

如何解决链接问题?

如果您遇到链接静态库的问题,请尝试设置以下环境变量

# For macOS
# Example path to the JavaScriptCore static libraries
DYLD_LIBRARY_PATH=/Users/${user}/Documents/Projects/WebKit/WebKitBuild/JSCOnly/Release/lib:$DYLD_LIBRARY_PATH
# For Linux
# Example path to the JavaScriptCore static libraries
LD_LIBRARY_PATH=/Users/${user}/Documents/Projects/WebKit/WebKitBuild/JSCOnly/Release/lib:$LD_LIBRARY_PATH

许可协议

本项目采用MIT许可协议 - 请参阅LICENSE文件以获取详细信息。

依赖项

~270-720KB
~17K SLoC