├── webapi ├── c-api │ ├── binding.cpp │ └── binding.hpp ├── src │ ├── readable_stream.rs │ └── lib.rs ├── Cargo.toml └── build.rs ├── rust-toolchain ├── rustfmt.toml ├── tyr ├── src │ ├── web │ │ ├── mod.rs │ │ └── btoa.rs │ ├── wellknown_property_name.rs │ ├── async_callback.rs │ ├── timer.rs │ ├── main.rs │ └── console.rs └── Cargo.toml ├── .yarnrc.yml ├── aarch64.cmake ├── .prettierignore ├── renovate.json ├── specs ├── console.js ├── react │ └── server-rendering.js └── timer.js ├── linux-x64-musl.Dockerfile ├── .rustfmt.toml ├── .gitmodules ├── .editorconfig ├── .taplo.toml ├── jsc-sys ├── Cargo.toml ├── c-api │ ├── binding.hpp │ └── binding.cpp ├── src │ ├── lib.rs │ └── binding.rs └── build.rs ├── jsc ├── Cargo.toml └── src │ ├── value.rs │ ├── error.rs │ ├── object.rs │ ├── class.rs │ └── lib.rs ├── linux-x64.Dockerfile ├── node-api ├── Cargo.toml └── src │ └── lib.rs ├── Cargo.toml ├── .cargo └── config.toml ├── linux-aarch64.Dockerfile ├── tsconfig.json ├── xtask ├── Cargo.toml └── src │ ├── main.rs │ ├── release_jsc.rs │ └── build.rs ├── .gitignore ├── package.json ├── README.md ├── .github └── workflows │ ├── docker.yaml │ ├── build-test.yaml │ └── release-jsc.yaml └── .eslintrc.yaml /webapi/c-api/binding.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webapi/c-api/binding.hpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2022-07-11 -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 2 | -------------------------------------------------------------------------------- /tyr/src/web/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod btoa; 2 | -------------------------------------------------------------------------------- /webapi/src/readable_stream.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /webapi/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod readable_stream; 2 | 3 | pub use readable_stream::*; 4 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: .yarn/releases/yarn-3.3.0.cjs 2 | nodeLinker: node-modules 3 | -------------------------------------------------------------------------------- /aarch64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME "Linux") 2 | set(CMAKE_SYSTEM_PROCESSOR "aarch64") 3 | set(CMAKE_CROSSCOMPILING "TRUE") -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | target 3 | dist 4 | node_modules 5 | icu 6 | WebKit 7 | jsc 8 | jsc-sys 9 | tyr 10 | xtask -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /specs/console.js: -------------------------------------------------------------------------------- 1 | console.log('hello') 2 | console.log(Symbol('foo')) 3 | console.log([1, '2', { foo: 'bar' }]) 4 | console.log({ foo: 1, bar: '2', baz: Math.PI }) 5 | -------------------------------------------------------------------------------- /linux-x64-musl.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine 2 | 3 | ENV RUSTFLAGS="" 4 | 5 | RUN apk add --update --no-cache lld ruby perl openssl-dev 6 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 2 | hard_tabs = false 3 | format_strings = true 4 | wrap_comments = true 5 | 6 | imports_granularity = "Crate" 7 | group_imports = "StdExternalCrate" 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "WebKit"] 2 | path = WebKit 3 | url = git@github.com:WebKit/WebKit.git 4 | [submodule "icu"] 5 | path = icu 6 | url = git@github.com:unicode-org/icu.git 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | exclude = ["**/node_modules/**", "**/target/**"] 2 | 3 | [[rule]] 4 | include = ["**/Cargo.toml"] 5 | keys = ["dependencies", "*-dependencies"] 6 | 7 | [rule.formatting] 8 | reorder_keys = true -------------------------------------------------------------------------------- /jsc-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jsc-sys" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | compiler_builtins = "0.1" 8 | 9 | [build-dependencies] 10 | build-support = { path = "../build-support" } 11 | -------------------------------------------------------------------------------- /jsc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jsc-safe" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1" 8 | bitflags = "1" 9 | jsc-sys = { path = "../jsc-sys", version = "0.0.0" } 10 | thiserror = "1" 11 | -------------------------------------------------------------------------------- /linux-x64.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian 2 | 3 | RUN apt-get update && \ 4 | apt-get install libc++-14-dev libc++abi-14-dev ruby -y --fix-missing --no-install-recommends && \ 5 | rm -rf /var/lib/apt/lists/* -------------------------------------------------------------------------------- /tyr/src/wellknown_property_name.rs: -------------------------------------------------------------------------------- 1 | use jsc_safe::sys::{jsc_string_from_static_rust_str, JSStringRef}; 2 | 3 | thread_local! { 4 | pub static LENGTH: JSStringRef = unsafe { jsc_string_from_static_rust_str("length\0".as_ptr() as *const _) }; 5 | } 6 | -------------------------------------------------------------------------------- /node-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "node-api" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | jsc-sys = { path = "../jsc-sys" } 10 | -------------------------------------------------------------------------------- /specs/react/server-rendering.js: -------------------------------------------------------------------------------- 1 | import { renderToReadableStream } from 'react-dom/server' 2 | 3 | function App() { 4 | return
Hello World
5 | } 6 | 7 | const readableStream = await renderToReadableStream() 8 | 9 | console.info(readableStream.allReady) 10 | -------------------------------------------------------------------------------- /specs/timer.js: -------------------------------------------------------------------------------- 1 | setTimeout(() => { 2 | console.log('hello') 3 | }) 4 | 5 | setTimeout(() => { 6 | console.log('hello after 1s') 7 | }, 1000) 8 | 9 | const i = setTimeout(() => { 10 | console.log('hello after 1s should be cancelled') 11 | }, 1000) 12 | 13 | clearTimeout(i) 14 | -------------------------------------------------------------------------------- /node-api/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | use jsc_sys::{JSContextRef, JSValueMakeUndefined, JSValueRef}; 4 | 5 | pub type napi_env = JSContextRef; 6 | pub type napi_value = JSValueRef; 7 | 8 | pub fn napi_get_undefined(env: napi_env) -> napi_value { 9 | unsafe { JSValueMakeUndefined(env) } 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "build-support", 4 | "jsc", 5 | "jsc-sys", 6 | "tyr", 7 | "node-api", 8 | "xtask", 9 | "webapi", 10 | ] 11 | 12 | [profile.release] 13 | lto = true 14 | codegen-units = 1 15 | 16 | [workspace.metadata] 17 | icu = { tag = "release-71-1" } 18 | WebKit = { tag = "webkitgtk-2.37.1" } 19 | -------------------------------------------------------------------------------- /webapi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webapi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | jsc-sys = { path = "../jsc-sys" } 10 | 11 | [build-dependencies] 12 | build-support = { path = "../build-support" } 13 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | 4 | [target.aarch64-unknown-linux-gnu] 5 | rustflags = [ 6 | "-C", "link-args=-fuse-ld=lld", 7 | "-C", "link-args=--target=aarch64-unknown-linux-gnu", 8 | "-C", "link-args=-march=armv8-a", 9 | "-C", "link-args=--sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot", 10 | ] 11 | -------------------------------------------------------------------------------- /linux-aarch64.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 2 | 3 | ADD ./lib/llvm-14 /usr/aarch64-unknown-linux-gnu/lib/llvm-14 4 | 5 | RUN apt-get update && \ 6 | apt-get install libssl-dev libc++-14-dev libc++abi-14-dev pkg-config ruby -y --fix-missing --no-install-recommends && \ 7 | cp -r /usr/aarch64-unknown-linux-gnu/lib/gcc /usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib/ && \ 8 | rm -rf /var/lib/apt/lists/* -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "importHelpers": false, 8 | "noEmitHelpers": false, 9 | "skipLibCheck": true, 10 | "allowJs": true, 11 | "outDir": "./dist" 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | "jsc", 16 | "jsc-sys", 17 | "icu", 18 | "WebKit", 19 | "tyr", 20 | ".yarn", 21 | "target" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tyr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tyr" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | base64-simd = "0.7" 11 | clap = { version = "3", features = ["derive"] } 12 | encoding_rs = "0.8" 13 | jsc-safe = { path = "../jsc", version = "0.0.0" } 14 | once_cell = "1" 15 | serde = { version = "1", features = ["derive"] } 16 | serde_json = "1" 17 | tokio = { version = "1", features = ["full"] } 18 | 19 | [target.'cfg(not(all(target_os = "linux", target_env = "musl", target_arch = "aarch64")))'.dependencies] 20 | mimalloc-rust = "0.2" 21 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | default = ["rustls-tls"] 10 | native-tls = ["octorust/native-tls", "reqwest/default-tls"] 11 | rustls-tls = ["octorust/rustls-tls", "reqwest/rustls-tls"] 12 | 13 | [dependencies] 14 | clap = { version = "3", features = ["derive"] } 15 | num_cpus = "1" 16 | octorust = "0.2" 17 | reqwest = { version = "0.11", default-features = false, features = [ 18 | "json", 19 | "multipart", 20 | ] } 21 | serde = { version = "1", features = ["derive"] } 22 | tar = "0.4" 23 | tokio = "1" 24 | toml = { version = "0.5" } 25 | -------------------------------------------------------------------------------- /jsc/src/value.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use jsc_sys::{ 4 | JSGlobalContextRef, JSValueIsArray, JSValueIsDate, JSValueIsEqual, JSValueIsUndefined, JSValueRef, 5 | }; 6 | 7 | pub trait JsValue { 8 | fn ctx(&self) -> JSGlobalContextRef; 9 | fn raw(&self) -> JSValueRef; 10 | 11 | fn is_undefined(&self) -> bool { 12 | unsafe { JSValueIsUndefined(self.ctx(), self.raw()) } 13 | } 14 | 15 | fn is_array(&self) -> bool { 16 | unsafe { JSValueIsArray(self.ctx(), self.raw()) } 17 | } 18 | 19 | fn is_date(&self) -> bool { 20 | unsafe { JSValueIsDate(self.ctx(), self.raw()) } 21 | } 22 | 23 | fn is_equal(&self, other: &T) -> bool { 24 | let mut exception = ptr::null(); 25 | unsafe { JSValueIsEqual(self.ctx(), self.raw(), other.raw(), &mut exception) } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{self, Parser, Subcommand}; 2 | 3 | mod build; 4 | mod release_jsc; 5 | 6 | use build::build; 7 | use release_jsc::{download, release}; 8 | 9 | #[derive(Parser)] 10 | #[clap(author, version, about = "Tyr cargo tasks", long_about = None)] 11 | #[clap(propagate_version = true)] 12 | struct Cli { 13 | #[clap(subcommand)] 14 | command: Commands, 15 | } 16 | 17 | #[derive(Subcommand)] 18 | enum Commands { 19 | Build, 20 | Release { 21 | #[clap(short, long)] 22 | target: String, 23 | }, 24 | Download { 25 | #[clap(short, long)] 26 | target: String, 27 | }, 28 | } 29 | 30 | #[tokio::main] 31 | async fn main() { 32 | let cli = Cli::parse(); 33 | 34 | match &cli.command { 35 | Commands::Build => { 36 | build(); 37 | } 38 | &Commands::Release { ref target } => { 39 | release(target).await; 40 | } 41 | &Commands::Download { ref target } => { 42 | download(target).await; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /jsc/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::NulError, 3 | fmt::{Display, Formatter}, 4 | io, 5 | }; 6 | 7 | use jsc_sys::JSValueRef; 8 | use thiserror::Error; 9 | 10 | #[derive(Error, Debug)] 11 | pub enum JscError { 12 | #[error("Convert to `CString` failed")] 13 | NulError, 14 | #[error("{0}")] 15 | JSCException(JSCException), 16 | #[error("{0}")] 17 | IO(io::Error), 18 | } 19 | 20 | impl From for JscError { 21 | fn from(_: NulError) -> Self { 22 | Self::NulError 23 | } 24 | } 25 | 26 | impl From for JscError { 27 | fn from(err: io::Error) -> Self { 28 | Self::IO(err) 29 | } 30 | } 31 | 32 | impl From for JscError { 33 | fn from(err: JSValueRef) -> Self { 34 | Self::JSCException(JSCException { exception: err }) 35 | } 36 | } 37 | 38 | #[derive(Debug)] 39 | pub struct JSCException { 40 | pub exception: JSValueRef, 41 | } 42 | 43 | impl Display for JSCException { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 45 | write!(f, "JSCException") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | # Created by https://www.toptal.com/developers/gitignore/api/macos 4 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos 5 | 6 | ### macOS ### 7 | # General 8 | .DS_Store 9 | .AppleDouble 10 | .LSOverride 11 | 12 | # Icon must end with two \r 13 | Icon 14 | 15 | 16 | # Thumbnails 17 | ._* 18 | 19 | # Files that might appear in the root of a volume 20 | .DocumentRevisions-V100 21 | .fseventsd 22 | .Spotlight-V100 23 | .TemporaryItems 24 | .Trashes 25 | .VolumeIcon.icns 26 | .com.apple.timemachine.donotpresent 27 | 28 | # Directories potentially created on remote AFP share 29 | .AppleDB 30 | .AppleDesktop 31 | Network Trash Folder 32 | Temporary Items 33 | .apdisk 34 | 35 | ### macOS Patch ### 36 | # iCloud generated files 37 | *.icloud 38 | 39 | # End of https://www.toptal.com/developers/gitignore/api/macos 40 | 41 | .vscode 42 | icu/icu4c/source/include 43 | lib 44 | icu-linux-aarch64 45 | .pnp.* 46 | .yarn/* 47 | !.yarn/patches 48 | !.yarn/plugins 49 | !.yarn/releases 50 | !.yarn/sdks 51 | !.yarn/versions 52 | node_modules 53 | 54 | webapi/webcore-build -------------------------------------------------------------------------------- /tyr/src/async_callback.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use jsc_safe::sys::*; 4 | 5 | pub struct AsyncCallback { 6 | func: JSValueRef, 7 | this: JSValueRef, 8 | args: *const JSValueRef, 9 | len: usize, 10 | exception: *mut JSValueRef, 11 | } 12 | 13 | impl Display for AsyncCallback { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | write!(f, "AsyncCallback") 16 | } 17 | } 18 | 19 | unsafe impl Send for AsyncCallback {} 20 | 21 | impl AsyncCallback { 22 | pub fn new( 23 | func: JSValueRef, 24 | this: JSValueRef, 25 | args: *const JSValueRef, 26 | len: usize, 27 | exception: *mut JSValueRef, 28 | ) -> Self { 29 | Self { 30 | func, 31 | this, 32 | args, 33 | len, 34 | exception, 35 | } 36 | } 37 | 38 | pub fn call(self, ctx: JSGlobalContextRef) -> JSValueRef { 39 | unsafe { 40 | JSObjectCallAsFunction( 41 | ctx, 42 | self.func as *mut _, 43 | self.this as *mut _, 44 | self.len, 45 | self.args, 46 | self.exception, 47 | ) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /webapi/build.rs: -------------------------------------------------------------------------------- 1 | extern crate build_support; 2 | 3 | use std::{env, fs}; 4 | 5 | fn main() -> Result<(), std::io::Error> { 6 | let current_dir = env::current_dir().unwrap(); 7 | let root_dir = current_dir.parent().unwrap(); 8 | let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); 9 | let mut build = build_support::create_cc(); 10 | build 11 | .include(root_dir.join("WebKit/WebKitBuild/bmalloc/Headers")) 12 | .include(root_dir.join("WebKit/WebKitBuild/WTF/Headers")) 13 | .include(root_dir.join("WebKit/WebKitBuild/JavaScriptCore/Headers")) 14 | .include(root_dir.join("WebKit/WebKitBuild/JavaScriptCore/PrivateHeaders")) 15 | .include(root_dir.join("WebKit/WebKitBuild/JavaScriptCore/PrivateHeaders/JavaScriptCore")) 16 | .include(root_dir.join("WebKit/WebKitBuild/JavaScriptCore/DerivedSources")) 17 | .include(current_dir.join("c-api")); 18 | if target_os == "macos" { 19 | build.flag("-DJSC_API_AVAILABLE(...)="); 20 | } 21 | for file in fs::read_dir(current_dir.join("c-api")).expect("Read c-api dir failed") { 22 | let f = file?; 23 | if f.file_type()?.is_file() && f.path().extension().and_then(|e| e.to_str()) == Some("cpp") { 24 | build.file(f.path()); 25 | } 26 | } 27 | build.compile("webcore"); 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /jsc-sys/c-api/binding.hpp: -------------------------------------------------------------------------------- 1 | #if defined(_MSC_VER) 2 | #define NOMINMAX // Prevent windows.h from defining min and max macros. 3 | #include 4 | #endif 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | typedef struct WTFStringImpl WTFStringImpl; 20 | typedef struct JSContext JSContext; 21 | 22 | struct WTFString 23 | { 24 | WTFStringImpl *inner; 25 | bool is_utf8; 26 | const unsigned char *characters8; 27 | const char16_t *characters16; 28 | uint32_t length; 29 | }; 30 | 31 | extern "C" 32 | { 33 | bool jsc_value_is_int(JSValueRef value); 34 | int32_t jsc_value_as_int(JSValueRef value); 35 | WTFString jsc_string_to_wtf_string(JSStringRef s); 36 | WTFString jsc_symbol_desc_string(JSValueRef value); 37 | JSStringRef jsc_string_from_static_rust_str(const char *str); 38 | void jsc_wtf_string_release(WTFStringImpl *inner); 39 | } 40 | -------------------------------------------------------------------------------- /jsc/src/object.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | 3 | use std::{ffi::CString, ptr}; 4 | 5 | use bitflags::bitflags; 6 | use jsc_sys::{ 7 | JSGlobalContextRef, JSObjectRef, JSObjectSetProperty, JSStringCreateWithUTF8CString, JSValueRef, 8 | }; 9 | 10 | use crate::JsValue; 11 | 12 | bitflags! { 13 | pub struct PropertyAttributes: u32 { 14 | const None = 0; 15 | const ReadOnly = 2; 16 | const DontEnum = 4; 17 | const DontDelete = 8; 18 | } 19 | } 20 | 21 | pub struct Object { 22 | pub(crate) ctx: JSGlobalContextRef, 23 | pub(crate) inner: JSObjectRef, 24 | } 25 | 26 | impl JsValue for Object { 27 | fn ctx(&self) -> JSGlobalContextRef { 28 | self.ctx 29 | } 30 | 31 | fn raw(&self) -> JSValueRef { 32 | self.inner 33 | } 34 | } 35 | 36 | impl Object { 37 | pub fn set_property>, T: JsValue>( 38 | &mut self, 39 | property: N, 40 | value: &T, 41 | attr: PropertyAttributes, 42 | ) -> Result<(), JSValueRef> { 43 | let property = CString::new(property).unwrap(); 44 | let mut exception = ptr::null(); 45 | unsafe { 46 | let property_js = JSStringCreateWithUTF8CString(property.as_ptr()); 47 | JSObjectSetProperty( 48 | self.ctx, 49 | self.inner, 50 | property_js, 51 | value.raw(), 52 | attr.bits, 53 | &mut exception, 54 | ); 55 | }; 56 | if exception.is_null() { 57 | Ok(()) 58 | } else { 59 | Err(exception) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsc-rs", 3 | "private": true, 4 | "packageManager": "yarn@3.3.0", 5 | "type": "module", 6 | "devDependencies": { 7 | "@taplo/cli": "0.5.2", 8 | "@types/react": "18.0.25", 9 | "@types/react-dom": "18.0.8", 10 | "@typescript-eslint/eslint-plugin": "5.44.0", 11 | "@typescript-eslint/parser": "5.44.0", 12 | "esbuild": "0.15.16", 13 | "eslint": "8.28.0", 14 | "eslint-config-prettier": "8.5.0", 15 | "eslint-plugin-import": "2.26.0", 16 | "husky": "8.0.2", 17 | "lint-staged": "13.0.4", 18 | "npm-run-all": "4.1.5", 19 | "prettier": "2.8.0", 20 | "typescript": "4.9.3" 21 | }, 22 | "lint-staged": { 23 | "*.@(js|ts)": [ 24 | "eslint --quiet --fix" 25 | ], 26 | "*.@(js|ts|json|md|yml|yaml)": [ 27 | "prettier --write" 28 | ], 29 | "*.toml": [ 30 | "taplo format" 31 | ], 32 | "*.rs": [ 33 | "cargo fmt --" 34 | ] 35 | }, 36 | "scripts": { 37 | "format": "run-p format:prettier format:rs format:toml", 38 | "format:prettier": "prettier --config ./package.json -w .", 39 | "format:rs": "cargo fmt --all", 40 | "format:toml": "taplo format", 41 | "lint": "eslint . --ext js,jsx,ts,tsx -c ./.eslintrc.yaml", 42 | "postinstall": "husky install" 43 | }, 44 | "husky": { 45 | "hooks": { 46 | "pre-commit": "lint-staged && cargo fmt --all" 47 | } 48 | }, 49 | "prettier": { 50 | "printWidth": 80, 51 | "semi": false, 52 | "singleQuote": true, 53 | "trailingComma": "all", 54 | "arrowParens": "always" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jsc-sys/c-api/binding.cpp: -------------------------------------------------------------------------------- 1 | #include "binding.hpp" 2 | 3 | inline WTFString toWTFString(WTF::String s) 4 | { 5 | auto is_utf8 = s.is8Bit(); 6 | auto characters8 = is_utf8 ? s.characters8() : nullptr; 7 | auto characters16 = is_utf8 ? nullptr : s.characters16(); 8 | auto length = s.length(); 9 | auto inner = reinterpret_cast(s.releaseImpl().leakRef()); 10 | WTFString result{ 11 | inner, 12 | is_utf8, 13 | characters8, 14 | characters16, 15 | length, 16 | }; 17 | return result; 18 | } 19 | 20 | extern "C" 21 | { 22 | bool jsc_value_is_int(JSValueRef value) 23 | { 24 | auto jsValue = toJS(value); 25 | return jsValue.isAnyInt(); 26 | } 27 | 28 | int32_t jsc_value_as_int(JSValueRef value) 29 | { 30 | auto jsValue = toJS(value); 31 | return jsValue.asInt32(); 32 | } 33 | 34 | WTFString jsc_symbol_desc_string(JSValueRef value) 35 | { 36 | auto jsValue = toJS(value); 37 | auto desc = asSymbol(jsValue)->descriptiveString(); 38 | return toWTFString(desc); 39 | } 40 | 41 | WTFString jsc_string_to_wtf_string(JSStringRef s) 42 | { 43 | auto ss = reinterpret_cast(s); 44 | auto wtf_string = ss->string(); 45 | return toWTFString(wtf_string); 46 | } 47 | 48 | JSStringRef jsc_string_from_static_rust_str(const char *str) 49 | { 50 | auto wtf_string = WTF::String::fromUTF8(str); 51 | return OpaqueJSString::tryCreate(wtf_string).leakRef(); 52 | } 53 | 54 | void jsc_wtf_string_release(WTFStringImpl *inner) 55 | { 56 | WTF::StringImpl::destroy(reinterpret_cast(inner)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Týr 2 | 3 | Tyr is a simple runtime for JavaScript and TypeScript that uses JavaScriptCore and is built in Rust. 4 | 5 | [![JavaScriptCore Version](https://img.shields.io/badge/JavaScriptCore-webkitgtk%2F2.37.1-hotpink)](https://github.com/WebKit/WebKit/releases/tag/webkitgtk-2.37.1) 6 | [![ICU Version](https://img.shields.io/badge/ICU-71.1-green)](https://github.com/unicode-org/icu/releases/tag/release-71-1) 7 | 8 | ## Support Matrix 9 | 10 | | Operating System | Architectures | Versions | Notes | Status | 11 | | ---------------- | ------------- | -------------------------- | ------------------------------------- | ----------- | 12 | | Linux | x86_64 | glibc >= 2.17 | e.g. Ubuntu 14.04, Debian 9, CentOS 7 | ✅ | 13 | | Linux | arm64 | glibc >= 2.17 | e.g. Ubuntu 14.04, Debian 9, CentOS 7 | ✅ | 14 | | Linux | x86_64 | musl >= 1.1.19 | e.g. Alpine 3.8 | ✅ | 15 | | Linux | arm64 | musl >= 1.1.19 | e.g. Alpine 3.8 | coming soon | 16 | | Linux | armv7 | glibc >= 2.28 | e.g. Ubuntu 18.04, Debian 10 | coming soon | 17 | | macOS | x64 | \>= 10.15 | | ✅ | 18 | | macOS | arm64 | \>= 11 | | ✅ | 19 | | Windows | x64 | \>= Windows 10/Server 2016 | | ✅ | 20 | -------------------------------------------------------------------------------- /jsc-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(target_os = "windows"))] 2 | extern crate compiler_builtins; 3 | 4 | use std::{fmt::Display, os::raw::c_char, slice}; 5 | 6 | mod binding; 7 | 8 | pub use binding::*; 9 | 10 | #[repr(C)] 11 | #[derive(Debug, Copy, Clone)] 12 | pub struct WTFStringImpl { 13 | _unused: [u8; 0], 14 | } 15 | 16 | #[repr(C)] 17 | #[derive(Debug, Clone)] 18 | pub struct WTString { 19 | inner: *mut WTFStringImpl, 20 | pub is_utf8: bool, 21 | pub characters8: *const u8, 22 | pub characters16: *const u16, 23 | pub length: u32, 24 | } 25 | 26 | impl Display for WTString { 27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 28 | if self.is_utf8 { 29 | write!( 30 | f, 31 | "{}", 32 | String::from_utf8_lossy(unsafe { 33 | slice::from_raw_parts(self.characters8, self.length as usize) 34 | }) 35 | ) 36 | } else { 37 | write!( 38 | f, 39 | "{}", 40 | String::from_utf16_lossy(unsafe { 41 | slice::from_raw_parts(self.characters16, self.length as usize) 42 | }) 43 | ) 44 | } 45 | } 46 | } 47 | 48 | impl Drop for WTString { 49 | fn drop(&mut self) { 50 | unsafe { 51 | jsc_wtf_string_release(self.inner); 52 | } 53 | } 54 | } 55 | 56 | extern "C" { 57 | pub fn jsc_value_is_int(value: JSValueRef) -> bool; 58 | pub fn jsc_value_as_int(value: JSValueRef) -> i32; 59 | pub fn jsc_string_to_wtf_string(string: JSStringRef) -> WTString; 60 | /// The created String will be leaked in runtime 61 | /// It's used for create Object Property attached to GlobalObject 62 | pub fn jsc_string_from_static_rust_str(string: *const c_char) -> JSStringRef; 63 | pub fn jsc_symbol_desc_string(symbol: JSValueRef) -> WTString; 64 | fn jsc_wtf_string_release(inner: *mut WTFStringImpl); 65 | } 66 | -------------------------------------------------------------------------------- /tyr/src/web/btoa.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | 3 | use encoding_rs::mem::convert_utf16_to_str; 4 | use jsc_safe::sys::*; 5 | 6 | pub unsafe extern "C" fn btoa( 7 | ctx: JSContextRef, 8 | _function: JSObjectRef, 9 | _this: JSObjectRef, 10 | argument_count: usize, 11 | arguments: *const JSValueRef, 12 | exception: *mut JSValueRef, 13 | ) -> JSValueRef { 14 | if argument_count == 0 { 15 | return JSValueMakeUndefined(ctx); 16 | } 17 | let args = slice::from_raw_parts(arguments, argument_count); 18 | let string_to_encode = args[0]; 19 | if !JSValueIsString(ctx, string_to_encode) { 20 | return JSValueMakeUndefined(ctx); 21 | } 22 | let string_to_encode = JSValueToStringCopy(ctx, string_to_encode, exception); 23 | let wtf_string = jsc_string_to_wtf_string(string_to_encode); 24 | let len = wtf_string.length as usize; 25 | let (str_to_encode, container_to_drop) = if wtf_string.is_utf8 { 26 | ( 27 | slice::from_raw_parts(wtf_string.characters8, len), 28 | String::new(), 29 | ) 30 | } else { 31 | let mut output = String::from_utf8_unchecked(vec![0; len * 3]); 32 | let new_length = convert_utf16_to_str( 33 | slice::from_raw_parts(wtf_string.characters16, len), 34 | &mut output, 35 | ); 36 | (slice::from_raw_parts(output.as_ptr(), new_length), output) 37 | }; 38 | let b64 = base64_simd::Base64::URL_SAFE; 39 | let mut output = vec![0; len * 3]; 40 | let output_buf = base64_simd::OutBuf::new(output.as_mut_slice()); 41 | let output_ptr = { 42 | if let Ok(o) = b64.encode(str_to_encode, output_buf) { 43 | let l = o.len(); 44 | let o = slice::from_raw_parts_mut(o.as_mut_ptr(), l + 1); 45 | o[l] = b'\0'; 46 | output.as_ptr() 47 | } else { 48 | std::ptr::null_mut() 49 | } 50 | }; 51 | if output_ptr.is_null() { 52 | return JSValueMakeUndefined(ctx); 53 | } 54 | drop(container_to_drop); 55 | JSValueMakeString(ctx, JSStringCreateWithUTF8CString(output_ptr as *const _)) 56 | } 57 | -------------------------------------------------------------------------------- /jsc/src/class.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CString, os::raw::c_char, ptr}; 2 | 3 | use jsc_sys::{JSClassCreate, JSClassDefinition, JSClassRef, JSObjectMake}; 4 | 5 | use crate::{Context, JscError, Object}; 6 | 7 | #[repr(u32)] 8 | pub enum ClassAttribute { 9 | None = jsc_sys::kJSClassAttributeNone, 10 | NoAutomaticPrototype = jsc_sys::kJSClassAttributeNoAutomaticPrototype, 11 | } 12 | 13 | pub struct ClassDefinition { 14 | inner: JSClassDefinition, 15 | } 16 | 17 | impl Default for ClassDefinition { 18 | fn default() -> Self { 19 | Self { 20 | inner: JSClassDefinition { 21 | version: 0, 22 | attributes: ClassAttribute::None as u32, 23 | className: ptr::null(), 24 | parentClass: ptr::null_mut(), 25 | staticValues: ptr::null(), 26 | staticFunctions: ptr::null(), 27 | initialize: None, 28 | finalize: None, 29 | hasProperty: None, 30 | getProperty: None, 31 | setProperty: None, 32 | deleteProperty: None, 33 | getPropertyNames: None, 34 | callAsFunction: None, 35 | callAsConstructor: None, 36 | hasInstance: None, 37 | convertToType: None, 38 | }, 39 | } 40 | } 41 | } 42 | 43 | impl ClassDefinition { 44 | pub fn with_name>>(mut self, name: S) -> Result { 45 | let c_name = CString::new(name)?; 46 | self.inner.className = c_name.as_ptr(); 47 | Ok(Self { inner: self.inner }) 48 | } 49 | 50 | pub fn with_c_name(mut self, c_name: *const c_char) -> Self { 51 | self.inner.className = c_name; 52 | Self { inner: self.inner } 53 | } 54 | 55 | pub fn with_attribute(mut self, attribute: ClassAttribute) -> Self { 56 | self.inner.attributes = attribute as u32; 57 | Self { inner: self.inner } 58 | } 59 | 60 | pub fn into_class(self) -> Class { 61 | Class { 62 | inner: unsafe { JSClassCreate(&self.inner as *const JSClassDefinition) }, 63 | } 64 | } 65 | } 66 | 67 | pub struct Class { 68 | inner: JSClassRef, 69 | } 70 | 71 | impl Class { 72 | pub fn make_object(self, ctx: &Context) -> Object { 73 | unsafe { JSObjectMake(ctx.inner, self.inner, ptr::null_mut()) }; 74 | Object { 75 | ctx: ctx.inner, 76 | inner: unsafe { JSObjectMake(ctx.inner, self.inner, ptr::null_mut()) }, 77 | } 78 | } 79 | 80 | pub fn raw(&self) -> JSClassRef { 81 | self.inner 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: Docker nightly build 2 | 3 | on: 4 | schedule: 5 | - cron: '0 1 * * 1' 6 | 7 | jobs: 8 | build_image: 9 | name: Build nodejs-rust:lts 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Setup QEMU 16 | uses: docker/setup-qemu-action@v2 17 | 18 | - name: Setup Docker Buildx 19 | uses: docker/setup-buildx-action@v2 20 | 21 | - name: Login to GitHub Container Registry 22 | uses: docker/login-action@v2 23 | with: 24 | registry: ghcr.io 25 | username: Brooooooklyn 26 | password: ${{ secrets.GH_TOKEN }} 27 | logout: false 28 | 29 | - name: Build and push Linux x64 30 | uses: docker/build-push-action@v3 31 | with: 32 | file: linux-x64.Dockerfile 33 | platforms: linux/amd64 34 | context: . 35 | push: true 36 | tags: ghcr.io/brooooooklyn/jsc-rs/linux-builder:x64 37 | 38 | - name: Build and push Linux x64 musl 39 | uses: docker/build-push-action@v3 40 | with: 41 | file: linux-x64-musl.Dockerfile 42 | platforms: linux/amd64 43 | context: . 44 | push: true 45 | tags: ghcr.io/brooooooklyn/jsc-rs/linux-builder:x64-musl 46 | 47 | - name: Install latest libc++-dev for cross build 48 | uses: addnab/docker-run-action@v3 49 | with: 50 | image: ghcr.io/napi-rs/napi-rs/nodejs:aarch64-16 51 | options: '--user 0:0 -e GITHUB_TOKEN -v ${{ github.workspace }}/lib/llvm-14:/usr/lib/llvm-14' 52 | run: >- 53 | apt-get update && 54 | apt-get install -y wget && 55 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && 56 | echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main" >> /etc/apt/sources.list && 57 | echo "deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main" >> /etc/apt/sources.list && 58 | apt-get update && 59 | apt-get install libc++-14-dev libc++abi-14-dev -y --fix-missing --no-install-recommends 60 | - name: Build and push Linux aarch64 61 | uses: docker/build-push-action@v3 62 | with: 63 | file: linux-aarch64.Dockerfile 64 | context: . 65 | platforms: linux/amd64 66 | push: true 67 | tags: ghcr.io/brooooooklyn/jsc-rs/linux-builder:aarch64 68 | -------------------------------------------------------------------------------- /tyr/src/timer.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | ptr, slice, 4 | sync::{ 5 | atomic::{AtomicU32, Ordering}, 6 | Mutex, 7 | }, 8 | time::Duration, 9 | }; 10 | 11 | use jsc_safe::sys::*; 12 | use once_cell::sync::Lazy; 13 | use tokio::{task::JoinHandle, time::sleep}; 14 | 15 | use crate::async_callback::AsyncCallback; 16 | 17 | static TIMER_ID: AtomicU32 = AtomicU32::new(0); 18 | static TIMER_MAP: Lazy>>> = Lazy::new(|| Default::default()); 19 | 20 | pub unsafe extern "C" fn set_timeout( 21 | ctx: JSContextRef, 22 | _function: JSObjectRef, 23 | _this: JSObjectRef, 24 | argument_count: usize, 25 | arguments: *const JSValueRef, 26 | exception: *mut JSValueRef, 27 | ) -> JSValueRef { 28 | crate::queue_async_task(); 29 | let args = slice::from_raw_parts(arguments, argument_count); 30 | let callback = args[0]; 31 | let duration_time = if let Some(dur) = args.get(1) { 32 | JSValueToNumber(ctx, *dur, exception) as i32 33 | } else { 34 | 4 35 | }; 36 | let duration = if duration_time < 0 { 37 | Duration::from_millis(0) 38 | } else { 39 | Duration::from_millis(duration_time as u64) 40 | }; 41 | let timer_id = TIMER_ID.fetch_add(1, Ordering::Relaxed); 42 | let callback = AsyncCallback::new( 43 | callback, 44 | JSValueMakeUndefined(ctx), 45 | if argument_count == 1 { 46 | ptr::null() 47 | } else { 48 | args[1..].as_ptr() 49 | }, 50 | if argument_count == 1 { 51 | 0 52 | } else { 53 | argument_count - 2 54 | }, 55 | exception, 56 | ); 57 | let jh = crate::GLOBAL_RUNTIME.spawn(async move { 58 | sleep(duration).await; 59 | let s = crate::GLOBAL_SENDER.get_unchecked(); 60 | if !s.is_closed() { 61 | if let Err(err) = s.send(callback) { 62 | eprintln!("{err}"); 63 | } 64 | } 65 | }); 66 | let mut timer_map_lock_guard = TIMER_MAP.lock().unwrap(); 67 | timer_map_lock_guard.insert(timer_id, jh); 68 | JSValueMakeNumber(ctx, timer_id as f64) 69 | } 70 | 71 | pub unsafe extern "C" fn clear_timeout( 72 | ctx: JSContextRef, 73 | _function: JSObjectRef, 74 | _this: JSObjectRef, 75 | argument_count: usize, 76 | arguments: *const JSValueRef, 77 | _exception: *mut JSValueRef, 78 | ) -> JSValueRef { 79 | let args = slice::from_raw_parts(arguments, argument_count); 80 | let timer_id = args[0]; 81 | let timer_id = jsc_value_as_int(timer_id) as u32; 82 | let timer_map_lock_guard = TIMER_MAP.lock().unwrap(); 83 | if let Some(jh) = timer_map_lock_guard.get(&timer_id) { 84 | jh.abort(); 85 | crate::dequeue_async_task(); 86 | } 87 | drop(timer_map_lock_guard); 88 | JSValueMakeUndefined(ctx) 89 | } 90 | -------------------------------------------------------------------------------- /jsc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CString, ptr}; 2 | 3 | use jsc_sys::{ 4 | JSContextGetGlobalObject, JSContextGroupCreate, JSContextGroupRef, JSContextGroupRelease, 5 | JSEvaluateScript, JSGarbageCollect, JSGlobalContextCreateInGroup, JSGlobalContextRef, 6 | JSGlobalContextRelease, JSObjectCallAsFunctionCallback, JSObjectMakeFunctionWithCallback, 7 | JSStringCreateWithUTF8CString, JSStringRef, JSValueRef, 8 | }; 9 | 10 | mod class; 11 | mod error; 12 | mod object; 13 | mod value; 14 | 15 | pub use class::*; 16 | pub use error::*; 17 | pub use jsc_sys as sys; 18 | pub use object::*; 19 | pub use value::*; 20 | 21 | pub struct Context { 22 | group: JSContextGroupRef, 23 | pub(crate) inner: JSGlobalContextRef, 24 | } 25 | 26 | impl Context { 27 | pub fn new() -> Self { 28 | Self::create(None) 29 | } 30 | 31 | pub fn with_global_class(class: Class) -> Self { 32 | Self::create(Some(class)) 33 | } 34 | 35 | fn create(global_class: Option) -> Self { 36 | let group = unsafe { JSContextGroupCreate() }; 37 | let inner = unsafe { 38 | JSGlobalContextCreateInGroup( 39 | group, 40 | global_class.map(|c| c.raw()).unwrap_or(ptr::null_mut()), 41 | ) 42 | }; 43 | Context { group, inner } 44 | } 45 | 46 | pub fn global(&self) -> Object { 47 | Object { 48 | ctx: self.inner, 49 | inner: unsafe { JSContextGetGlobalObject(self.inner) }, 50 | } 51 | } 52 | 53 | pub fn create_function>>( 54 | &self, 55 | name: N, 56 | callback: JSObjectCallAsFunctionCallback, 57 | ) -> Result { 58 | let js_name = self.create_string(name); 59 | Ok(Object { 60 | inner: unsafe { JSObjectMakeFunctionWithCallback(self.inner, js_name, callback) }, 61 | ctx: self.inner, 62 | }) 63 | } 64 | 65 | pub fn gc(&mut self) { 66 | unsafe { JSGarbageCollect(self.inner) }; 67 | } 68 | 69 | pub fn eval(&self, script: String) -> Result { 70 | let script = self.create_string(script); 71 | let mut exception = ptr::null(); 72 | let output = unsafe { 73 | JSEvaluateScript( 74 | self.inner, 75 | script, 76 | JSContextGetGlobalObject(self.inner), 77 | ptr::null_mut(), 78 | 0, 79 | &mut exception, 80 | ) 81 | }; 82 | if exception.is_null() { 83 | Ok(output) 84 | } else { 85 | Ok(exception) 86 | } 87 | } 88 | 89 | pub fn raw(&self) -> JSGlobalContextRef { 90 | self.inner 91 | } 92 | 93 | #[inline] 94 | fn create_string>>(&self, string: T) -> JSStringRef { 95 | let c_string = CString::new(string).expect("Source string contains invalid UTF-8"); 96 | unsafe { JSStringCreateWithUTF8CString(c_string.as_ptr()) } 97 | } 98 | } 99 | 100 | impl Drop for Context { 101 | fn drop(&mut self) { 102 | unsafe { 103 | JSGlobalContextRelease(self.inner); 104 | JSContextGroupRelease(self.group); 105 | }; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tyr/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | 3 | use std::{ 4 | fs, 5 | os::raw::c_char, 6 | process, ptr, 7 | sync::atomic::{AtomicU32, Ordering}, 8 | }; 9 | 10 | use async_callback::AsyncCallback; 11 | use clap::{self, Parser}; 12 | use jsc_safe::{ClassAttribute, ClassDefinition, Context, JscError, PropertyAttributes}; 13 | use once_cell::sync::{Lazy, OnceCell}; 14 | use tokio::{ 15 | runtime::{Builder, Runtime}, 16 | sync::mpsc::{unbounded_channel, UnboundedSender}, 17 | }; 18 | 19 | #[cfg(all(not(all(target_os = "linux", target_env = "musl", target_arch = "aarch64")),))] 20 | #[global_allocator] 21 | static ALLOC: mimalloc_rust::GlobalMiMalloc = mimalloc_rust::GlobalMiMalloc; 22 | 23 | pub(crate) static GLOBAL_SENDER: OnceCell> = OnceCell::new(); 24 | pub(crate) static GLOBAL_RUNTIME: Lazy = 25 | Lazy::new(|| Builder::new_multi_thread().enable_all().build().unwrap()); 26 | static ASYNC_TASK_QUEUE_SIZE: AtomicU32 = AtomicU32::new(0); 27 | 28 | mod async_callback; 29 | mod console; 30 | mod timer; 31 | mod web; 32 | mod wellknown_property_name; 33 | 34 | #[derive(Parser)] 35 | #[clap(author, version, about = "tyr", long_about = "Tyr JavaScript runtime")] 36 | #[clap(propagate_version = true)] 37 | struct Tyr { 38 | /// JavaScript file to run 39 | entry: String, 40 | } 41 | 42 | fn main() { 43 | let global_class = ClassDefinition::default() 44 | .with_c_name("globalThis\0".as_ptr() as *const c_char) 45 | .with_attribute(ClassAttribute::NoAutomaticPrototype) 46 | .into_class(); 47 | let ctx = Context::with_global_class(global_class); 48 | let _ = GLOBAL_RUNTIME.enter(); 49 | let (sender, mut receiver) = unbounded_channel::(); 50 | GLOBAL_SENDER.get_or_init(move || sender); 51 | match create_runtime(&ctx) { 52 | Err(JscError::JSCException(exception)) => { 53 | let js_error = unsafe { 54 | console::js_value_to_console( 55 | ctx.raw(), 56 | exception.exception, 57 | &mut Default::default(), 58 | ptr::null_mut(), 59 | ) 60 | }; 61 | eprintln!("{js_error}"); 62 | process::exit(1); 63 | } 64 | Err(e) => { 65 | eprintln!("{e}"); 66 | process::exit(1); 67 | } 68 | _ => {} 69 | } 70 | if ASYNC_TASK_QUEUE_SIZE.load(Ordering::Relaxed) > 0 { 71 | GLOBAL_RUNTIME.block_on(async move { 72 | while let Some(cb) = receiver.recv().await { 73 | cb.call(ctx.raw()); 74 | if ASYNC_TASK_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed) == 1 { 75 | break; 76 | }; 77 | } 78 | }); 79 | } 80 | } 81 | 82 | fn create_runtime(ctx: &Context) -> Result<(), JscError> { 83 | let mut global = ctx.global(); 84 | let console = console::create(&ctx)?; 85 | let btoa = ctx.create_function("btoa", Some(web::btoa::btoa))?; 86 | let set_timeout = ctx.create_function("setTimeout", Some(timer::set_timeout))?; 87 | let clear_timeout = ctx.create_function("clearTimeout", Some(timer::clear_timeout))?; 88 | global.set_property("console", &console, PropertyAttributes::DontDelete)?; 89 | global.set_property("btoa", &btoa, PropertyAttributes::DontDelete)?; 90 | global.set_property("setTimeout", &set_timeout, PropertyAttributes::DontDelete)?; 91 | global.set_property( 92 | "clearTimeout", 93 | &clear_timeout, 94 | PropertyAttributes::DontDelete, 95 | )?; 96 | let tyr = Tyr::parse(); 97 | let script = fs::read_to_string(tyr.entry)?; 98 | ctx.eval(script)?; 99 | Ok(()) 100 | } 101 | 102 | #[inline(always)] 103 | pub(crate) fn c_str(s: &str) -> *const c_char { 104 | s.as_ptr() as *const c_char 105 | } 106 | 107 | #[inline] 108 | pub(crate) fn queue_async_task() -> u32 { 109 | ASYNC_TASK_QUEUE_SIZE.fetch_add(1, Ordering::Relaxed) 110 | } 111 | 112 | #[inline] 113 | pub(crate) fn dequeue_async_task() -> u32 { 114 | ASYNC_TASK_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed) 115 | } 116 | -------------------------------------------------------------------------------- /jsc-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate build_support; 2 | 3 | use std::{env, process}; 4 | 5 | static LOW_LEVEL_INTERPRETER_LIB: &str = "libLowLevelInterpreterLib.a"; 6 | 7 | fn main() { 8 | println!("cargo:rerun-if-changed=c-api/binding.cpp"); 9 | println!("cargo:rerun-if-changed=c-api/binding.hpp"); 10 | let mut build = build_support::create_cc(); 11 | let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); 12 | let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 13 | let is_windows = target_os == "windows"; 14 | let out_dir = env::var("OUT_DIR").unwrap(); 15 | println!("cargo:rustc-link-search={}", &out_dir); 16 | let current_dir = env::current_dir().expect("get current_dir failed"); 17 | let webkit_output_dir = current_dir.parent().unwrap().join("WebKit/WebKitBuild"); 18 | let jsc_lib_dir = env::var("JSC_LIB_DIR").unwrap_or_else(|_| { 19 | { 20 | if is_windows { 21 | "lib64" 22 | } else { 23 | "lib" 24 | } 25 | } 26 | .to_owned() 27 | }); 28 | let jsc_lib_dir_path = webkit_output_dir.join(&jsc_lib_dir); 29 | // WebKit/WebKitBuild/lib/libJavaScriptCore.a 30 | println!("cargo:rustc-link-search={}", jsc_lib_dir_path.display()); 31 | build 32 | .file("c-api/binding.cpp") 33 | .include("c-api") 34 | .include( 35 | webkit_output_dir 36 | .join("JavaScriptCore") 37 | .to_str() 38 | .unwrap() 39 | .replace(r#"\"#, "/"), 40 | ) 41 | .include( 42 | webkit_output_dir 43 | .join("JavaScriptCore/Headers") 44 | .to_str() 45 | .unwrap() 46 | .replace(r#"\"#, "/"), 47 | ) 48 | .include( 49 | webkit_output_dir 50 | .join("WTF/Headers") 51 | .to_str() 52 | .unwrap() 53 | .replace(r#"\"#, "/"), 54 | ) 55 | .include( 56 | webkit_output_dir 57 | .join("bmalloc/Headers") 58 | .to_str() 59 | .unwrap() 60 | .replace(r#"\"#, "/"), 61 | ); 62 | let icu_header_dir = current_dir 63 | .parent() 64 | .unwrap() 65 | .join("icu/icu4c/include") 66 | .to_str() 67 | .unwrap() 68 | .to_owned(); 69 | if is_windows { 70 | build.flag(&format!("-I{}", &icu_header_dir)); 71 | println!( 72 | "cargo:rustc-link-search={}", 73 | current_dir 74 | .parent() 75 | .unwrap() 76 | .join("icu/icu4c/lib") 77 | .to_str() 78 | .unwrap() 79 | ); 80 | println!("cargo:rustc-link-lib=static=sicudt"); 81 | println!("cargo:rustc-link-lib=static=sicuuc"); 82 | println!("cargo:rustc-link-lib=static=sicuin"); 83 | println!("cargo:rustc-link-lib=winmm"); 84 | println!("cargo:rustc-link-lib=shell32"); 85 | println!("cargo:rustc-link-lib=static=JavaScriptCore"); 86 | println!("cargo:rustc-link-lib=static=WTF"); 87 | println!("cargo:rustc-link-lib=static=jscc"); 88 | } else { 89 | if target_os == "macos" { 90 | let xcrun_output = process::Command::new("xcrun") 91 | .args(&["-sdk", "macosx", "--show-sdk-path"]) 92 | .output() 93 | .expect("failed to get macos sdk path") 94 | .stdout; 95 | let xcode_sdk_path = String::from_utf8_lossy(xcrun_output.as_slice()) 96 | .trim() 97 | .to_owned(); 98 | 99 | build.flag("-DJSC_API_AVAILABLE(...)="); 100 | 101 | if jsc_lib_dir_path.join(LOW_LEVEL_INTERPRETER_LIB).exists() { 102 | println!("cargo:rustc-link-lib=LowLevelInterpreterLib"); 103 | } 104 | println!("cargo:rustc-link-search={}", xcode_sdk_path); 105 | println!("cargo:rustc-link-lib=icucore"); 106 | } else if target_os == "linux" { 107 | build.flag(&format!("-I{}", &icu_header_dir)); 108 | match target_arch.as_str() { 109 | "x86_64" => { 110 | println!( 111 | "cargo:rustc-link-search={}", 112 | current_dir 113 | .parent() 114 | .unwrap() 115 | .join("icu/icu4c/lib") 116 | .to_str() 117 | .unwrap() 118 | ); 119 | } 120 | "aarch64" => { 121 | println!( 122 | "cargo:rustc-link-search={}", 123 | current_dir 124 | .parent() 125 | .unwrap() 126 | .join("icu-linux-aarch64/lib") 127 | .to_str() 128 | .unwrap() 129 | ); 130 | build.flag("-DUSE_SYSTEM_MALLOC=1"); 131 | } 132 | _ => { 133 | panic!("Unsupported arch {target_arch}"); 134 | } 135 | } 136 | println!("cargo:rustc-link-lib=static=icudata"); 137 | println!("cargo:rustc-link-lib=static=icuuc"); 138 | println!("cargo:rustc-link-lib=static=icui18n"); 139 | } 140 | if jsc_lib_dir_path.join("libbmalloc.a").exists() { 141 | println!("cargo:rustc-link-lib=static=bmalloc"); 142 | } 143 | println!("cargo:rustc-link-lib=static=WTF"); 144 | println!("cargo:rustc-link-lib=static=JavaScriptCore"); 145 | println!("cargo:rustc-link-lib=static=jscc"); 146 | } 147 | build.compile("jscc"); 148 | } 149 | -------------------------------------------------------------------------------- /tyr/src/console.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashSet, ptr, slice}; 2 | 3 | use jsc_safe::{ 4 | sys::*, ClassAttribute, ClassDefinition, Context, JscError, Object, PropertyAttributes, 5 | }; 6 | 7 | use crate::wellknown_property_name; 8 | 9 | pub fn create(ctx: &Context) -> Result { 10 | let mut console = ClassDefinition::default() 11 | .with_c_name(crate::c_str("Console\0")) 12 | .with_attribute(ClassAttribute::NoAutomaticPrototype) 13 | .into_class() 14 | .make_object(&ctx); 15 | let log_fn = ctx.create_function("log", Some(log))?; 16 | let info_fn = ctx.create_function("info", Some(log))?; 17 | let warn_fn = ctx.create_function("warn", Some(log))?; 18 | let error_fn = ctx.create_function("error", Some(log))?; 19 | console.set_property("log", &log_fn, PropertyAttributes::None)?; 20 | console.set_property("info", &info_fn, PropertyAttributes::None)?; 21 | console.set_property("warn", &warn_fn, PropertyAttributes::None)?; 22 | console.set_property("error", &error_fn, PropertyAttributes::None)?; 23 | Ok(console) 24 | } 25 | 26 | pub unsafe extern "C" fn log( 27 | ctx: JSContextRef, 28 | _function: JSObjectRef, 29 | _this: JSObjectRef, 30 | argument_count: usize, 31 | arguments: *const JSValueRef, 32 | exception: *mut JSValueRef, 33 | ) -> JSValueRef { 34 | let args = slice::from_raw_parts(arguments, argument_count); 35 | let mut reference_set = HashSet::default(); 36 | args.iter().for_each(|arg| { 37 | let serde_value = js_value_to_console(ctx, *arg, &mut reference_set, exception); 38 | match &serde_value { 39 | serde_json::Value::String(s) => println!("{s}"), 40 | serde_json::Value::Null => println!("null"), 41 | serde_json::Value::Bool(b) => println!("{b}"), 42 | serde_json::Value::Number(n) => println!("{n}"), 43 | serde_json::Value::Array(_) | serde_json::Value::Object(_) => { 44 | println!("{}", serde_json::to_string_pretty(&serde_value).unwrap()) 45 | } 46 | } 47 | }); 48 | JSValueMakeUndefined(ctx) 49 | } 50 | 51 | pub unsafe fn js_value_to_console( 52 | ctx: JSContextRef, 53 | value: JSValueRef, 54 | reference_set: &mut HashSet, 55 | exception: *mut JSValueRef, 56 | ) -> serde_json::Value { 57 | let js_type = JSValueGetType(ctx, value); 58 | match js_type { 59 | 0 => serde_json::Value::String("undefined".to_owned()), 60 | 1 => serde_json::Value::Null, 61 | 2 => serde_json::Value::Bool(JSValueToBoolean(ctx, value)), 62 | 3 => serde_json::Value::Number(if jsc_value_is_int(value) { 63 | serde_json::Number::from(JSValueToNumber(ctx, value, ptr::null_mut()) as i64) 64 | } else { 65 | serde_json::Number::from_f64(JSValueToNumber(ctx, value, ptr::null_mut())).unwrap() 66 | }), 67 | 4 => { 68 | let js_string = JSValueToStringCopy(ctx, value, ptr::null_mut()); 69 | let string_length = JSStringGetLength(js_string); 70 | let utf16_ptr = JSStringGetCharactersPtr(js_string); 71 | serde_json::Value::String(String::from_utf16_lossy(slice::from_raw_parts( 72 | utf16_ptr, 73 | string_length, 74 | ))) 75 | } 76 | 5 => { 77 | reference_set.insert(value); 78 | if JSValueIsArray(ctx, value) { 79 | let length = JSObjectGetProperty( 80 | ctx, 81 | value as *mut _, 82 | wellknown_property_name::LENGTH.with(|s| *s), 83 | exception, 84 | ); 85 | let len = JSValueToNumber(ctx, length, exception) as usize; 86 | let mut array = Vec::with_capacity(len); 87 | for index in 0..len { 88 | let element = JSObjectGetPropertyAtIndex(ctx, value as *mut _, index as u32, exception); 89 | let element_value = js_value_to_console(ctx, element, reference_set, exception); 90 | reference_set.insert(element); 91 | reference_set.insert(value); 92 | array.push(element_value); 93 | } 94 | return serde_json::Value::Array(array); 95 | } 96 | let names_array = JSObjectCopyPropertyNames(ctx, value as *mut _); 97 | let names_array_length = JSPropertyNameArrayGetCount(names_array); 98 | let mut value_to_display = serde_json::Value::Object(serde_json::Map::default()); 99 | for index in 0..names_array_length { 100 | let name_string = JSPropertyNameArrayGetNameAtIndex(names_array, index); 101 | let name_wtf = jsc_string_to_wtf_string(name_string); 102 | let name = format!("{name_wtf}"); 103 | let value = JSObjectGetProperty(ctx, value as *mut _, name_string, ptr::null_mut()); 104 | if reference_set.contains(&value) { 105 | value_to_display[name] = serde_json::Value::String("".to_owned()); 106 | } else { 107 | reference_set.insert(value); 108 | value_to_display[name] = js_value_to_console(ctx, value, reference_set, exception); 109 | } 110 | } 111 | value_to_display 112 | } 113 | 6 => { 114 | let desc = jsc_symbol_desc_string(value); 115 | serde_json::Value::String(format!("{desc}")) 116 | } 117 | _ => unreachable!(), 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | parser: '@typescript-eslint/parser' 2 | 3 | parserOptions: 4 | ecmaFeatures: 5 | jsx: true 6 | ecmaVersion: latest 7 | sourceType: module 8 | project: './tsconfig.json' 9 | 10 | ignorePatterns: 11 | - .yarn 12 | - target 13 | - dist 14 | - node_modules 15 | - icu 16 | - WebKit 17 | - jsc 18 | - jsc-sys 19 | - tyr 20 | - xtask 21 | - specs 22 | 23 | env: 24 | browser: false 25 | es6: true 26 | node: true 27 | 28 | plugins: 29 | - import 30 | - '@typescript-eslint' 31 | 32 | extends: 33 | - eslint:recommended 34 | 35 | globals: 36 | NodeJS: 'readonly' 37 | 38 | rules: 39 | # covered by @typescript-eslint 40 | 'space-before-function-paren': 0 41 | 'no-useless-constructor': 0 42 | 'no-unused-vars': 0 43 | 'no-case-declarations': 0 44 | 'no-dupe-class-members': 0 45 | 46 | 'comma-dangle': ['error', 'only-multiline'] 47 | 'eqeqeq': [2, 'always', { 'null': 'ignore' }] 48 | 'no-undef': 2 49 | 'no-console': [2, { allow: ['error', 'warn', 'info', 'assert'] }] 50 | 'no-const-assign': 2 51 | 'no-duplicate-imports': 2 52 | 'no-restricted-syntax': 53 | [ 54 | 2, 55 | { 56 | 'selector': 'BinaryExpression[operator=/(==|===|!=|!==)/][left.raw=true], BinaryExpression[operator=/(==|===|!=|!==)/][right.raw=true]', 57 | 'message': Don't compare for equality against boolean literals, 58 | }, 59 | ] 60 | 'no-use-before-define': [2, { 'functions': false, 'classes': false }] 61 | 'no-var': 2 62 | 'one-var-declaration-per-line': 2 63 | 'prefer-const': 2 64 | 65 | 'import/first': 2 66 | 'import/newline-after-import': 2 67 | 'import/order': 68 | [ 69 | 2, 70 | { 71 | 'newlines-between': 'always', 72 | 'alphabetize': { 'order': 'asc', 'caseInsensitive': true }, 73 | 'pathGroups': 74 | [ 75 | { 76 | 'pattern': '@vercel/**', 77 | 'group': 'internal', 78 | 'position': 'before', 79 | }, 80 | ], 81 | }, 82 | ] 83 | 84 | '@typescript-eslint/adjacent-overload-signatures': 2 85 | 86 | '@typescript-eslint/await-thenable': 2 87 | 88 | '@typescript-eslint/consistent-type-assertions': 2 89 | 90 | '@typescript-eslint/ban-types': 91 | [ 92 | 'error', 93 | { 94 | 'types': 95 | { 96 | 'String': { 'message': 'Use string instead', 'fixWith': 'string' }, 97 | 'Number': { 'message': 'Use number instead', 'fixWith': 'number' }, 98 | 'Boolean': 99 | { 'message': 'Use boolean instead', 'fixWith': 'boolean' }, 100 | 'Function': { 'message': 'Use explicit type instead' }, 101 | }, 102 | }, 103 | ] 104 | 105 | '@typescript-eslint/explicit-member-accessibility': 106 | [ 107 | 'error', 108 | { 109 | accessibility: 'explicit', 110 | overrides: 111 | { 112 | accessors: 'no-public', 113 | constructors: 'no-public', 114 | methods: 'no-public', 115 | properties: 'no-public', 116 | parameterProperties: 'explicit', 117 | }, 118 | }, 119 | ] 120 | 121 | '@typescript-eslint/method-signature-style': 2 122 | 123 | '@typescript-eslint/no-floating-promises': 2 124 | 125 | '@typescript-eslint/no-implied-eval': 2 126 | 127 | '@typescript-eslint/no-for-in-array': 2 128 | 129 | '@typescript-eslint/no-inferrable-types': 2 130 | 131 | '@typescript-eslint/no-invalid-void-type': 2 132 | 133 | '@typescript-eslint/no-misused-new': 2 134 | 135 | '@typescript-eslint/no-misused-promises': 2 136 | 137 | '@typescript-eslint/no-namespace': 2 138 | 139 | '@typescript-eslint/no-non-null-asserted-optional-chain': 2 140 | 141 | '@typescript-eslint/no-throw-literal': 2 142 | 143 | '@typescript-eslint/no-unnecessary-boolean-literal-compare': 2 144 | 145 | '@typescript-eslint/prefer-for-of': 2 146 | 147 | '@typescript-eslint/prefer-nullish-coalescing': 2 148 | 149 | '@typescript-eslint/switch-exhaustiveness-check': 2 150 | 151 | '@typescript-eslint/prefer-optional-chain': 2 152 | 153 | '@typescript-eslint/prefer-readonly': 2 154 | 155 | '@typescript-eslint/prefer-string-starts-ends-with': 0 156 | 157 | '@typescript-eslint/no-array-constructor': 2 158 | 159 | '@typescript-eslint/require-await': 2 160 | 161 | '@typescript-eslint/return-await': 2 162 | 163 | '@typescript-eslint/ban-ts-comment': 164 | [ 165 | 2, 166 | { 167 | 'ts-expect-error': false, 168 | 'ts-ignore': true, 169 | 'ts-nocheck': true, 170 | 'ts-check': false, 171 | }, 172 | ] 173 | 174 | '@typescript-eslint/naming-convention': 175 | [ 176 | 2, 177 | { 178 | selector: 'memberLike', 179 | format: ['camelCase', 'PascalCase'], 180 | modifiers: ['private'], 181 | leadingUnderscore: 'forbid', 182 | }, 183 | ] 184 | 185 | '@typescript-eslint/no-unused-vars': 186 | [ 187 | 2, 188 | { 189 | varsIgnorePattern: '^_', 190 | argsIgnorePattern: '^_', 191 | ignoreRestSiblings: true, 192 | }, 193 | ] 194 | '@typescript-eslint/member-ordering': 195 | [ 196 | 2, 197 | { 198 | default: 199 | [ 200 | 'public-static-field', 201 | 'protected-static-field', 202 | 'private-static-field', 203 | 'public-static-method', 204 | 'protected-static-method', 205 | 'private-static-method', 206 | 'public-instance-field', 207 | 'protected-instance-field', 208 | 'private-instance-field', 209 | 'public-constructor', 210 | 'protected-constructor', 211 | 'private-constructor', 212 | 'public-instance-method', 213 | 'protected-instance-method', 214 | 'private-instance-method', 215 | ], 216 | }, 217 | ] 218 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yaml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | tags-ignore: 7 | - '**' 8 | pull_request: 9 | 10 | env: 11 | MACOSX_DEPLOYMENT_TARGET: '10.15' 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | settings: 19 | - os: macos-latest 20 | target: x86_64-apple-darwin 21 | - os: macos-latest 22 | target: aarch64-apple-darwin 23 | - os: ubuntu-latest 24 | target: x86_64-unknown-linux-gnu 25 | docker: ghcr.io/brooooooklyn/jsc-rs/linux-builder:x64 26 | - os: ubuntu-latest 27 | target: aarch64-unknown-linux-gnu 28 | docker: ghcr.io/brooooooklyn/jsc-rs/linux-builder:aarch64 29 | - os: ubuntu-latest 30 | target: x86_64-unknown-linux-musl 31 | docker: ghcr.io/brooooooklyn/jsc-rs/linux-builder:x64-musl 32 | - os: windows-latest 33 | target: x86_64-pc-windows-msvc 34 | runs-on: ${{ matrix.settings.os }} 35 | name: Build ${{ matrix.settings.target }} 36 | steps: 37 | - uses: actions/checkout@v3 38 | 39 | - name: Install Rust 40 | uses: actions-rs/toolchain@v1 41 | with: 42 | target: ${{ matrix.os.target }} 43 | toolchain: stable 44 | if: ${{ !matrix.settings.docker }} 45 | 46 | - name: Cache cargo registry 47 | uses: actions/cache@v3 48 | with: 49 | path: | 50 | ~/.cargo/registry/index/ 51 | ~/.cargo/registry/cache/ 52 | ~/.cargo/git/db/ 53 | .cargo-cache/ 54 | target 55 | key: ${{ matrix.settings.target }}-cargo-cache 56 | 57 | - uses: maxim-lobanov/setup-xcode@v1 58 | with: 59 | xcode-version: latest 60 | if: matrix.settings.os == 'macos-latest' 61 | 62 | - name: Download static release 63 | run: | 64 | cargo xtask download --target ${{ matrix.settings.target }} 65 | ls -R WebKit 66 | ls -R icu 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | 70 | - name: Build JavaScriptCore 71 | if: matrix.settings.os == 'macos-latest' 72 | run: | 73 | cargo build --bin tyr --release 74 | cp target/release/tyr ./tyr-bin 75 | 76 | - name: Build JavaScriptCore 77 | if: matrix.settings.os == 'windows-latest' 78 | run: | 79 | cargo build --bin tyr --release 80 | cp target/release/tyr.exe ./tyr-bin.exe 81 | 82 | - name: Build JavaScriptCore in docker 83 | uses: addnab/docker-run-action@v3 84 | if: ${{ matrix.settings.docker }} 85 | with: 86 | image: ${{ matrix.settings.docker }} 87 | options: '--user 0:0 -e GITHUB_TOKEN -v ${{ github.workspace }}/.cargo-cache/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}/.cargo-cache/registry/cache:/usr/local/cargo/cache -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}:/build -w /build' 88 | run: >- 89 | export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang && 90 | rustup toolchain install $(cat ./rust-toolchain) && 91 | rustup target add ${{ matrix.settings.target }} && 92 | cargo build --bin tyr --target ${{ matrix.settings.target }} --release && 93 | cp target/${{ matrix.settings.target }}/release/tyr ./tyr-bin 94 | 95 | - name: Upload artifact 96 | uses: actions/upload-artifact@v3 97 | with: 98 | name: bin-${{ matrix.settings.target }} 99 | path: tyr-bin 100 | if-no-files-found: error 101 | if: matrix.settings.os != 'windows-latest' 102 | 103 | - name: Upload artifact 104 | uses: actions/upload-artifact@v3 105 | with: 106 | name: bin-${{ matrix.settings.target }} 107 | path: tyr-bin.exe 108 | if-no-files-found: error 109 | if: matrix.settings.os == 'windows-latest' 110 | 111 | tests: 112 | runs-on: ${{ matrix.settings.os }} 113 | name: Test ${{ matrix.settings.target }} 114 | needs: 115 | - build 116 | strategy: 117 | fail-fast: false 118 | matrix: 119 | settings: 120 | - os: macos-latest 121 | target: x86_64-apple-darwin 122 | - os: ubuntu-latest 123 | target: x86_64-unknown-linux-gnu 124 | - os: windows-latest 125 | target: x86_64-pc-windows-msvc 126 | steps: 127 | - uses: actions/checkout@v3 128 | - name: Download binary 129 | uses: actions/download-artifact@v3 130 | with: 131 | name: bin-${{ matrix.settings.target }} 132 | 133 | - name: Run tests 134 | if: matrix.settings.os != 'windows-latest' 135 | run: | 136 | chmod 777 tyr-bin 137 | ./tyr-bin specs/console.js 138 | 139 | - name: Run tests 140 | if: matrix.settings.os == 'windows-latest' 141 | run: | 142 | icacls tyr-bin.exe 143 | ./tyr-bin.exe specs/console.js 144 | 145 | test-linux-centos-7-x64: 146 | runs-on: ubuntu-latest 147 | name: Test bindings on Linux-x64-glibc-2.17 148 | needs: 149 | - build 150 | container: 151 | image: centos:7 152 | steps: 153 | - uses: actions/checkout@v3 154 | - name: Download binary 155 | uses: actions/download-artifact@v3 156 | with: 157 | name: bin-x86_64-unknown-linux-gnu 158 | 159 | - name: Run tests 160 | run: | 161 | chmod 777 tyr-bin 162 | ./tyr-bin specs/console.js 163 | 164 | test-linux-centos-7-aarch64: 165 | runs-on: ubuntu-latest 166 | name: Test bindings on Linux-aarch64-glibc-2.17 167 | needs: 168 | - build 169 | steps: 170 | - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset 171 | 172 | - uses: actions/checkout@v3 173 | 174 | - name: Download binary 175 | uses: actions/download-artifact@v3 176 | with: 177 | name: bin-aarch64-unknown-linux-gnu 178 | 179 | - name: Test bindings 180 | uses: addnab/docker-run-action@v3 181 | with: 182 | image: multiarch/centos:aarch64-clean 183 | options: -v ${{ github.workspace }}:/build -w /build 184 | run: | 185 | chmod 777 tyr-bin 186 | ./tyr-bin specs/console.js 187 | -------------------------------------------------------------------------------- /.github/workflows/release-jsc.yaml: -------------------------------------------------------------------------------- 1 | name: Release JavaScriptCore and ICU 2 | 3 | on: 4 | push: 5 | branches: [release] 6 | tags-ignore: 7 | - '**' 8 | env: 9 | MACOSX_DEPLOYMENT_TARGET: '10.15' 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | settings: 17 | - os: macos-latest 18 | target: x86_64-apple-darwin 19 | - os: macos-latest 20 | target: aarch64-apple-darwin 21 | - os: ubuntu-latest 22 | target: x86_64-unknown-linux-gnu 23 | docker: ghcr.io/brooooooklyn/jsc-rs/linux-builder:x64 24 | - os: ubuntu-latest 25 | target: aarch64-unknown-linux-gnu 26 | docker: ghcr.io/brooooooklyn/jsc-rs/linux-builder:aarch64 27 | - os: windows-latest 28 | target: x86_64-pc-windows-msvc 29 | runs-on: ${{ matrix.settings.os }} 30 | name: Build ${{ matrix.settings.target }} 31 | steps: 32 | - uses: actions/checkout@v3 33 | with: 34 | submodules: true 35 | 36 | - name: Install Rust 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | target: ${{ matrix.os.target }} 40 | if: ${{ !matrix.settings.docker }} 41 | 42 | - name: Cache cargo registry 43 | uses: actions/cache@v3 44 | with: 45 | path: | 46 | ~/.cargo/registry 47 | ~/.cargo/git 48 | target 49 | key: ${{ matrix.settings.target }}-cargo-cache 50 | 51 | - name: Install tools on macOS 52 | run: brew install ninja 53 | if: matrix.settings.os == 'macos-latest' 54 | 55 | - uses: maxim-lobanov/setup-xcode@v1 56 | with: 57 | xcode-version: latest 58 | if: matrix.settings.os == 'macos-latest' 59 | 60 | - name: Install tools on Windows 61 | run: | 62 | choco install ninja -y 63 | which clang-cl 64 | C:/msys64/usr/bin/bash -lc "pacman -Syuu --noconfirm" 65 | C:/msys64/usr/bin/bash -lc "pacman -S make --noconfirm" 66 | if: matrix.settings.os == 'windows-latest' 67 | 68 | - name: Set MSYS path 69 | run: echo "c:/msys64/usr/bin" >> $GITHUB_PATH 70 | shell: bash 71 | if: matrix.settings.os == 'windows-latest' 72 | 73 | - name: Build JavaScriptCore 74 | shell: cmd 75 | run: | 76 | ls -R c:/msys64/usr/bin 77 | call "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" x64 -vcvars_ver=14.29.30133 78 | sh -c "which cl" 79 | cargo xtask build 80 | cat icu/icu4c/source/config.log 81 | cargo build --bin tyr --release 82 | cp target/release/tyr.exe ./tyr-bin.exe 83 | cargo xtask release --target ${{ matrix.settings.target }} 84 | if: matrix.settings.os == 'windows-latest' 85 | env: 86 | GNU_MAKE_PATH: 'c:/msys64/usr/bin/make.exe' 87 | GNU_SH_PATH: 'c:/msys64/usr/bin/sh.exe' 88 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 89 | 90 | - name: Build JavaScriptCore 91 | run: | 92 | cargo xtask build 93 | cargo build --bin tyr --release 94 | cp target/release/tyr ./tyr-bin 95 | cargo xtask release --target ${{ matrix.settings.target }} 96 | if: matrix.settings.os == 'macos-latest' 97 | env: 98 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 99 | 100 | - name: Build JavaScriptCore in docker 101 | uses: addnab/docker-run-action@v3 102 | if: ${{ matrix.settings.docker }} 103 | env: 104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 105 | with: 106 | image: ${{ matrix.settings.docker }} 107 | options: '--user 0:0 -e GITHUB_TOKEN -v ${{ github.workspace }}:/build -w /build' 108 | run: >- 109 | pwd && 110 | rustup toolchain install $(cat ./rust-toolchain) && 111 | rustup target add ${{ matrix.settings.target }} && 112 | rm -f /usr/lib/llvm-14/lib/libc++abi.so && 113 | rm -f /usr/aarch64-unknown-linux-gnu/lib/llvm-14/lib/libc++abi.so && 114 | cargo run --bin xtask --target x86_64-unknown-linux-gnu -- build && 115 | ls -R WebKit/WebKitBuild/lib && 116 | export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang && 117 | cargo build --target ${{ matrix.settings.target }} --bin tyr --release && 118 | cp target/${{ matrix.settings.target }}/release/tyr ./tyr-bin && 119 | cargo run --bin xtask --target x86_64-unknown-linux-gnu -- release --target ${{ matrix.settings.target }} 120 | 121 | - name: Cat CMake error log 122 | if: ${{ failure() }} 123 | run: cat ./WebKit/WebKitBuild/CMakeFiles/CMakeError.log 124 | 125 | build-linux-musl: 126 | name: Build ${{ matrix.target }} 127 | runs-on: ubuntu-latest 128 | strategy: 129 | fail-fast: false 130 | matrix: 131 | target: [x86_64-unknown-linux-musl] 132 | steps: 133 | - uses: actions/checkout@v3 134 | with: 135 | submodules: true 136 | 137 | - name: Cache cargo registry 138 | uses: actions/cache@v3 139 | with: 140 | path: | 141 | ~/.cargo/registry 142 | ~/.cargo/git 143 | target 144 | key: ${{ matrix.target }}-cargo-cache 145 | 146 | - name: Build JavaScriptCore in docker 147 | uses: addnab/docker-run-action@v3 148 | env: 149 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 150 | with: 151 | image: ghcr.io/brooooooklyn/jsc-rs/linux-builder:x64-musl 152 | options: '--user 0:0 -e GITHUB_TOKEN -v ${{ github.workspace }}:/build -w /build' 153 | run: >- 154 | pwd && 155 | rustup toolchain install $(cat ./rust-toolchain) && 156 | rustup target add ${{ matrix.target }} && 157 | apk add lld openssl-dev ruby perl && 158 | unset RUSTFLAGS && 159 | cargo run --bin xtask --target x86_64-unknown-linux-musl -- build && 160 | ls -R WebKit/WebKitBuild/lib && 161 | cargo build --target ${{ matrix.target }} --bin tyr --release && 162 | cp target/${{ matrix.target }}/release/tyr ./tyr-bin && 163 | cargo run --bin xtask --target x86_64-unknown-linux-musl -- release --target ${{ matrix.target }} 164 | 165 | - name: Cat CMake error log 166 | if: ${{ failure() }} 167 | run: cat ./WebKit/WebKitBuild/CMakeFiles/CMakeError.log 168 | -------------------------------------------------------------------------------- /xtask/src/release_jsc.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | 3 | use octorust::{auth::Credentials, types::ReposCreateReleaseRequest, Client}; 4 | use serde::Deserialize; 5 | use tar::{Archive, Builder}; 6 | 7 | static ICU_DIR_TO_TAR: &[&str] = &[ 8 | "icu/icu4c/include", 9 | "icu/icu4c/lib", 10 | "icu-linux-aarch64/lib", 11 | ]; 12 | 13 | static WEBKIT_DIR_TO_TAR: &[&str] = &[ 14 | #[cfg(not(target_os = "windows"))] 15 | "WebKit/WebKitBuild/bmalloc", 16 | "WebKit/WebKitBuild/JavaScriptCore", 17 | #[cfg(target_os = "macos")] 18 | "WebKit/WebKitBuild/ICU/Headers/unicode", 19 | #[cfg(not(target_os = "windows"))] 20 | "WebKit/WebKitBuild/lib", 21 | #[cfg(target_os = "windows")] 22 | "WebKit/WebKitBuild/lib64", 23 | "WebKit/WebKitBuild/WTF", 24 | ]; 25 | 26 | static REPO_OWNER: &str = "Brooooooklyn"; 27 | static REPO_NAME: &str = "jsc-rs"; 28 | 29 | #[derive(Debug, Deserialize)] 30 | struct RootConfig { 31 | workspace: Workspace, 32 | } 33 | 34 | #[derive(Debug, Deserialize)] 35 | struct Workspace { 36 | metadata: ExternalDependencies, 37 | } 38 | 39 | #[derive(Debug, Deserialize)] 40 | struct ExternalDependencies { 41 | icu: ExternalVersion, 42 | #[serde(rename = "WebKit")] 43 | webkit: ExternalVersion, 44 | } 45 | 46 | #[derive(Debug, Deserialize)] 47 | struct ExternalVersion { 48 | tag: String, 49 | } 50 | 51 | pub async fn release(target: &str) { 52 | let current_path = env::current_dir().expect("Get current dir failed"); 53 | let mut webkit_tar = Builder::new(Vec::new()); 54 | let mut icu_tar = Builder::new(Vec::new()); 55 | if !cfg!(target_os = "macos") { 56 | for dir in ICU_DIR_TO_TAR { 57 | let p = current_path.join(dir); 58 | if p.exists() { 59 | icu_tar 60 | .append_dir_all(dir, p) 61 | .expect(&format!("Append {} failed", dir)); 62 | } 63 | } 64 | } 65 | for dir in WEBKIT_DIR_TO_TAR { 66 | let src_dir = current_path.join(dir); 67 | if src_dir.exists() { 68 | webkit_tar 69 | .append_dir_all(dir, src_dir) 70 | .expect(&format!("Append {} failed", dir)); 71 | } 72 | } 73 | let client = create_client(); 74 | let root_config = read_root_config(); 75 | if !cfg!(target_os = "macos") { 76 | upload_release( 77 | &client, 78 | &root_config.workspace.metadata.icu.tag, 79 | target, 80 | icu_tar.into_inner().expect("Get icu tar failed"), 81 | ) 82 | .await; 83 | } 84 | upload_release( 85 | &client, 86 | &root_config.workspace.metadata.webkit.tag, 87 | target, 88 | webkit_tar.into_inner().expect("Get icu tar failed"), 89 | ) 90 | .await; 91 | } 92 | 93 | async fn upload_release(client: &Client, tag: &str, target: &str, tar: Vec) { 94 | let release = if let Ok(release) = client 95 | .repos() 96 | .get_release_by_tag(REPO_OWNER, REPO_NAME, tag) 97 | .await 98 | { 99 | release 100 | } else { 101 | client 102 | .repos() 103 | .create_release( 104 | REPO_OWNER, 105 | REPO_NAME, 106 | &ReposCreateReleaseRequest { 107 | name: tag.to_owned(), 108 | body: format!("Static linked lib and c headers for **{}**", tag), 109 | draft: None, 110 | discussion_category_name: String::new(), 111 | prerelease: None, 112 | tag_name: tag.to_owned(), 113 | target_commitish: String::new(), 114 | }, 115 | ) 116 | .await 117 | .expect("Create release failed") 118 | }; 119 | let assets = client 120 | .repos() 121 | .list_release_assets(REPO_OWNER, REPO_NAME, release.id, 20, 1) 122 | .await 123 | .expect("Get assets list from release failed"); 124 | for asset in assets.iter() { 125 | if asset.name == format!("{}.tar", target) { 126 | client 127 | .repos() 128 | .delete_release_asset(REPO_OWNER, REPO_NAME, asset.id) 129 | .await 130 | .expect("Delete release asset failed"); 131 | } 132 | } 133 | let mut ttl = 0; 134 | while ttl < 5 { 135 | if let Ok(_) = reqwest::Client::new() 136 | .post(&format!( 137 | "https://uploads.github.com/repos/{}/{}/releases/{}/assets?name={}", 138 | REPO_OWNER, 139 | REPO_NAME, 140 | release.id, 141 | format!("{}.tar", target) 142 | )) 143 | .body(tar.clone()) 144 | .header("Content-Type", "application/x-tar") 145 | .header( 146 | "Authorization", 147 | &format!( 148 | "token {}", 149 | env::var("GITHUB_TOKEN").expect("No GITHUB_TOKEN provided") 150 | ), 151 | ) 152 | .send() 153 | .await 154 | { 155 | break; 156 | } else { 157 | ttl += 1; 158 | } 159 | } 160 | assert_ne!(ttl, 5, "Upload release failed"); 161 | } 162 | 163 | pub async fn download(target: &str) { 164 | let root_config = read_root_config(); 165 | let client = create_client(); 166 | if !cfg!(target_os = "macos") { 167 | download_and_unpack_asset(&client, &root_config.workspace.metadata.icu.tag, target).await; 168 | } 169 | download_and_unpack_asset(&client, &root_config.workspace.metadata.webkit.tag, target).await; 170 | } 171 | 172 | fn create_client() -> Client { 173 | Client::new( 174 | "jsc-rs Release bot", 175 | Credentials::Token(env::var("GITHUB_TOKEN").expect("No GITHUB_TOKEN provided")), 176 | ) 177 | .expect("Initialize octorust Client failed") 178 | } 179 | 180 | fn read_root_config() -> RootConfig { 181 | toml::from_slice( 182 | fs::read("Cargo.toml") 183 | .expect("Read Cargo.toml failed") 184 | .as_slice(), 185 | ) 186 | .expect("Parse root Cargo.toml failed") 187 | } 188 | 189 | async fn download_and_unpack_asset(client: &Client, tag: &str, target: &str) { 190 | let release = client 191 | .repos() 192 | .get_release_by_tag(REPO_OWNER, REPO_NAME, &tag) 193 | .await 194 | .expect("Get release failed"); 195 | let assets = client 196 | .repos() 197 | .list_release_assets(REPO_OWNER, REPO_NAME, release.id, 20, 1) 198 | .await 199 | .expect("List release assets failed"); 200 | let asset = assets 201 | .iter() 202 | .find(|asset| asset.name == format!("{}.tar", target)) 203 | .expect("Can't find asset"); 204 | let mut ttl = 0; 205 | let mut tar_content = None; 206 | while ttl < 5 { 207 | if let Ok(body) = reqwest::ClientBuilder::new() 208 | .redirect(reqwest::redirect::Policy::limited(10)) 209 | .build() 210 | .expect("Build reqwest client failed") 211 | .get(&asset.browser_download_url) 212 | .send() 213 | .await 214 | .expect("Get asset failed") 215 | .bytes() 216 | .await 217 | { 218 | tar_content = Some(body); 219 | break; 220 | } else { 221 | ttl += 1; 222 | } 223 | } 224 | let mut tar = Archive::new(tar_content.as_ref().unwrap().as_ref()); 225 | tar 226 | .unpack(env::current_dir().expect("Get current dir failed")) 227 | .expect("Unpack tar failed"); 228 | } 229 | -------------------------------------------------------------------------------- /xtask/src/build.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use std::{ 4 | env, fs, 5 | path::PathBuf, 6 | process::{Command, Stdio}, 7 | }; 8 | 9 | static AARCH64_LINUX_GNU_LD_FLAG: &str = 10 | "-L/usr/aarch64-unknown-linux-gnu/lib/llvm-14/lib -L/usr/aarch64-unknown-linux-gnu/lib \ 11 | -L/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib \ 12 | -L/usr/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/4.8.5"; 13 | 14 | pub fn build() { 15 | let current_dir = env::current_dir().expect("get current_dir failed"); 16 | let cmake_build_dir = current_dir.join("WebKit/WebKitBuild"); 17 | fs::create_dir_all(&cmake_build_dir).expect("Create WebKitBuild dir failed"); 18 | let icu4c_dir = current_dir.join("icu/icu4c/source"); 19 | #[cfg(target_os = "macos")] 20 | { 21 | build_osx(cmake_build_dir); 22 | } 23 | #[cfg(target_os = "linux")] 24 | { 25 | build_linux(cmake_build_dir, icu4c_dir); 26 | } 27 | #[cfg(target_os = "windows")] 28 | { 29 | build_windows(cmake_build_dir, icu4c_dir); 30 | } 31 | } 32 | 33 | #[cfg(target_os = "macos")] 34 | fn build_osx(cmake_build_dir: PathBuf) { 35 | build_js_core( 36 | cmake_build_dir, 37 | JSCoreBuildConfig { 38 | use_pthread_permission_api: true, 39 | set_system_cc: false, 40 | ..Default::default() 41 | }, 42 | ) 43 | } 44 | 45 | #[cfg(target_os = "linux")] 46 | fn build_linux(cmake_build_dir: PathBuf, icu4c_dir: PathBuf) { 47 | let is_cross_aarch64_gnu = 48 | env::var("CARGO_BUILD_TARGET") == Ok("aarch64-unknown-linux-gnu".to_owned()); 49 | let is_musl = cfg!(target_env = "musl"); 50 | build_icu(icu4c_dir.clone(), is_cross_aarch64_gnu); 51 | let cross_flag = if is_cross_aarch64_gnu { 52 | "-I/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/include \ 53 | --sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot" 54 | } else { 55 | "" 56 | }; 57 | let libcpp_flag = if is_cross_aarch64_gnu { 58 | format!( 59 | "-march=armv8-a --target=aarch64-unknown-linux-gnu \ 60 | --sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot" 61 | ) 62 | } else if is_musl { 63 | String::new() 64 | } else { 65 | "-I/usr/lib/llvm-14/include/c++/v1 -L/usr/lib/llvm-14/lib".to_owned() 66 | }; 67 | let static_link_cpp_flag = if is_musl { "" } else { "-stdlib=libc++" }; 68 | let use_lld_flag = if is_musl { 69 | "-fuse-ld=lld" 70 | } else { 71 | "-fuse-ld=lld" 72 | }; 73 | let aarch64_gnu_cross_flag = if is_cross_aarch64_gnu { 74 | AARCH64_LINUX_GNU_LD_FLAG 75 | } else { 76 | "" 77 | }; 78 | build_js_core( 79 | cmake_build_dir, 80 | JSCoreBuildConfig { 81 | use_pthread_permission_api: false, 82 | set_system_cc: true, 83 | self_build_icu: true, 84 | extra_cxx_flag: format!( 85 | "{use_lld_flag} {static_link_cpp_flag} -I{} {libcpp_flag}", 86 | icu4c_dir 87 | .parent() 88 | .unwrap() 89 | .join("include") 90 | .to_str() 91 | .unwrap(), 92 | ), 93 | extra_c_flag: format!("{use_lld_flag} {libcpp_flag} {aarch64_gnu_cross_flag}"), 94 | ..Default::default() 95 | }, 96 | ); 97 | } 98 | 99 | #[cfg(target_os = "windows")] 100 | fn build_windows(cmake_build_dir: PathBuf, icu4c_dir: PathBuf) { 101 | build_icu(icu4c_dir.clone(), false); 102 | let include_flags = format!( 103 | "-I{}", 104 | icu4c_dir 105 | .parent() 106 | .unwrap() 107 | .join("include") 108 | .to_str() 109 | .unwrap() 110 | .replace(r#"\"#, "/"), 111 | ); 112 | build_js_core( 113 | cmake_build_dir, 114 | JSCoreBuildConfig { 115 | use_pthread_permission_api: false, 116 | set_system_cc: true, 117 | self_build_icu: true, 118 | extra_cxx_flag: include_flags.clone(), 119 | extra_c_flag: include_flags.clone(), 120 | ..Default::default() 121 | }, 122 | ) 123 | } 124 | 125 | #[derive(Debug, Default)] 126 | struct JSCoreBuildConfig { 127 | use_pthread_permission_api: bool, 128 | set_system_cc: bool, 129 | self_build_icu: bool, 130 | extra_c_flag: String, 131 | extra_cxx_flag: String, 132 | } 133 | 134 | fn build_icu(icu4c_dir: PathBuf, is_cross_aarch64_gnu: bool) { 135 | let is_musl = cfg!(target_env = "musl"); 136 | if is_cross_aarch64_gnu { 137 | build_icu(icu4c_dir.clone(), false); 138 | } 139 | let sh_bin = if cfg!(target_os = "windows") { 140 | env::var("GNU_SH_PATH").unwrap_or_else(|_| "C:/msys64/usr/bin/sh.exe".to_string()) 141 | } else { 142 | "sh".to_owned() 143 | }; 144 | let mut icu4c_config = Command::new(&sh_bin); 145 | let cross_build_dir = env::current_dir().unwrap().join(ICU_AARCH64_DIR); 146 | static ICU_AARCH64_DIR: &str = "icu-linux-aarch64"; 147 | #[cfg(target_os = "windows")] 148 | { 149 | icu4c_config.arg("--noprofile").arg("--norc"); 150 | } 151 | icu4c_config.arg("-c"); 152 | icu4c_config.arg(&format!( 153 | "{} {} --enable-static=yes --enable-shared=no --with-data-packaging=static --prefix={} {}", 154 | icu4c_dir 155 | .join(if is_cross_aarch64_gnu { 156 | "configure" 157 | } else { 158 | "runConfigureICU" 159 | }) 160 | .to_str() 161 | .unwrap() 162 | .replace(r#"\"#, "/"), 163 | if is_cross_aarch64_gnu { 164 | "" 165 | } else if cfg!(target_os = "linux") { 166 | "Linux" 167 | } else if cfg!(target_os = "windows") { 168 | "MSYS/MSVC" 169 | } else { 170 | panic!("Unsupported OS") 171 | }, 172 | icu4c_dir 173 | .parent() 174 | .unwrap() 175 | .to_str() 176 | .unwrap() 177 | .replace(r#"\"#, "/"), 178 | if cfg!(target_os = "windows") { 179 | format!( 180 | "--enable-extras=no --enable-tests=no --enable-tools=no --enable-samples=no \ 181 | --build=x86_64-msvc-mingw64 --host=x86_64-msvc-mingw64" 182 | ) 183 | } else if is_cross_aarch64_gnu { 184 | fs::create_dir_all(ICU_AARCH64_DIR).expect("Create cross build dir faild"); 185 | format!( 186 | "--host=x86_64-pc-linux --build=aarch64-pc-linux --with-cross-build={}", 187 | icu4c_dir.display() 188 | ) 189 | } else { 190 | String::new() 191 | } 192 | )); 193 | 194 | if !cfg!(target_os = "windows") { 195 | icu4c_config.env("CC", "clang").env("CXX", "clang++"); 196 | } 197 | let cross_flag; 198 | let cross_ld_flag; 199 | if is_cross_aarch64_gnu { 200 | cross_flag = "-fuse-ld=lld -march=armv8-a --target=aarch64-unknown-linux-gnu \ 201 | -I/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/include \ 202 | --sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot"; 203 | cross_ld_flag = AARCH64_LINUX_GNU_LD_FLAG; 204 | } else { 205 | cross_flag = ""; 206 | cross_ld_flag = ""; 207 | }; 208 | if is_cross_aarch64_gnu { 209 | icu4c_config.current_dir(cross_build_dir.clone()); 210 | } else { 211 | icu4c_config.current_dir(icu4c_dir.clone()); 212 | } 213 | icu4c_config 214 | .env( 215 | "CFLAGS", 216 | if cfg!(target_os = "windows") { 217 | "-Gy -MD" 218 | } else { 219 | cross_flag 220 | }, 221 | ) 222 | .env( 223 | "CXXFLAGS", 224 | if cfg!(target_os = "windows") { 225 | "/std:c++20 -Gy -MD".to_owned() 226 | } else if is_cross_aarch64_gnu { 227 | format!("{cross_flag} -std=c++20 -stdlib=libc++") 228 | } else if is_musl { 229 | "-fuse-ld=lld -std=c++20".to_owned() 230 | } else { 231 | "-fuse-ld=lld -std=c++20 -stdlib=libc++ -I/usr/lib/llvm-14/include/c++/v1".to_owned() 232 | }, 233 | ) 234 | .env( 235 | "LDFLAGS", 236 | if cfg!(target_os = "windows") { 237 | String::new() 238 | } else if is_cross_aarch64_gnu { 239 | format!( 240 | "-L/usr/aarch64-unknown-linux-gnu/lib/llvm-14/lib {}", 241 | cross_ld_flag 242 | ) 243 | } else { 244 | "-L/usr/lib/llvm-14/lib".to_owned() 245 | }, 246 | ) 247 | .stderr(Stdio::inherit()) 248 | .stdin(Stdio::inherit()) 249 | .stdout(Stdio::inherit()); 250 | assert_command_success(icu4c_config, "config icu4c failed"); 251 | let cpus = num_cpus::get(); 252 | let make_program = if cfg!(target_os = "windows") { 253 | env::var("GNU_MAKE_PATH").unwrap_or("C:/msys64/usr/bin/make.exe".to_string()) 254 | } else { 255 | "make".to_owned() 256 | }; 257 | let mut make_icu4c = Command::new(&make_program); 258 | make_icu4c 259 | .arg("-j") 260 | .arg(&format!("{}", cpus)) 261 | .current_dir(if is_cross_aarch64_gnu { 262 | cross_build_dir.clone() 263 | } else { 264 | icu4c_dir.clone() 265 | }) 266 | .stderr(Stdio::inherit()) 267 | .stdin(Stdio::inherit()) 268 | .stdout(Stdio::inherit()); 269 | assert_command_success(make_icu4c, "build icu4c failed"); 270 | let mut install_icu4c_command = Command::new(&make_program); 271 | install_icu4c_command 272 | .arg("install") 273 | .current_dir(icu4c_dir.clone()) 274 | .stderr(Stdio::inherit()) 275 | .stdin(Stdio::inherit()) 276 | .stdout(Stdio::inherit()); 277 | assert_command_success(install_icu4c_command, "install icu4c failed"); 278 | } 279 | 280 | fn build_js_core(cmake_build_dir: PathBuf, config: JSCoreBuildConfig) { 281 | let is_cross_aarch64_gnu = 282 | env::var("CARGO_BUILD_TARGET") == Ok("aarch64-unknown-linux-gnu".to_owned()); 283 | let use_pthread_permission_api_flag = if config.use_pthread_permission_api { 284 | "-DUSE_PTHREAD_JIT_PERMISSIONS_API=1" 285 | } else { 286 | "" 287 | }; 288 | let macos_deploy_target_flag = if cfg!(target_os = "macos") { 289 | if cfg!(target_arch = "x86_64") { 290 | "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15" 291 | } else { 292 | "-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0" 293 | } 294 | } else { 295 | "" 296 | }; 297 | let cross_build_flag = if is_cross_aarch64_gnu { 298 | let cmake_toolchain_file = env::current_dir() 299 | .unwrap() 300 | .join("aarch64.cmake") 301 | .to_str() 302 | .unwrap() 303 | .to_owned(); 304 | format!( 305 | r#"-DCMAKE_TOOLCHAIN_FILE={cmake_toolchain_file} -DCMAKE_ADDR2LINE=/usr/bin/llvm-addr2line-14 -DCMAKE_AR=/usr/bin/llvm-ar-14 -DUSE_SYSTEM_MALLOC=ON -DCMAKE_LINKER=lld -DCMAKE_MODULE_LINKER_FLAGS="{AARCH64_LINUX_GNU_LD_FLAG}" -DCMAKE_EXE_LINKER_FLAGS="{AARCH64_LINUX_GNU_LD_FLAG}""# 306 | ) 307 | } else { 308 | String::new() 309 | }; 310 | let mut cmake_config = Command::new("sh"); 311 | let extra_c_flag = &config.extra_c_flag; 312 | let extra_cxx_flag = &config.extra_cxx_flag; 313 | let c_flags = format!("{use_pthread_permission_api_flag} {extra_c_flag}") 314 | .trim() 315 | .to_owned(); 316 | let cxx20_flag = if cfg!(target_os = "windows") { 317 | "" 318 | } else { 319 | "-std=c++20" 320 | }; 321 | let cxx_flags = format!("{use_pthread_permission_api_flag} {cxx20_flag} {extra_cxx_flag}") 322 | .trim() 323 | .to_owned(); 324 | let icu_flag = if config.self_build_icu { 325 | format!( 326 | "-DICU_INCLUDE_DIR={} -DCMAKE_LIBRARY_PATH={}", 327 | env::current_dir() 328 | .unwrap() 329 | .join("icu/icu4c/include") 330 | .to_str() 331 | .unwrap() 332 | .replace(r#"\"#, "/"), 333 | env::current_dir() 334 | .unwrap() 335 | .join(if is_cross_aarch64_gnu { 336 | "icu-linux-aarch64/lib" 337 | } else { 338 | "icu/icu4c/lib" 339 | }) 340 | .to_str() 341 | .unwrap() 342 | .replace(r#"\"#, "/") 343 | ) 344 | } else { 345 | "".to_owned() 346 | }; 347 | let icu_uc_in_flag = if cfg!(target_os = "windows") { 348 | format!( 349 | "-DICU_UC_LIBRARY_RELEASE={} -DICU_I18N_LIBRARY_RELEASE={}", 350 | env::current_dir() 351 | .unwrap() 352 | .join("icu/icu4c/lib/sicuuc.lib") 353 | .to_str() 354 | .unwrap() 355 | .replace(r#"\"#, "/"), 356 | env::current_dir() 357 | .unwrap() 358 | .join("icu/icu4c/lib/sicuin.lib") 359 | .to_str() 360 | .unwrap() 361 | .replace(r#"\"#, "/") 362 | ) 363 | } else { 364 | String::new() 365 | }; 366 | let enable_ftl_jit = if cfg!(target_os = "windows") { 367 | "OFF" 368 | } else { 369 | "ON" 370 | }; 371 | cmake_config 372 | .arg("-c") 373 | .arg( 374 | format!( 375 | r#"cmake .. \ 376 | -DPORT="JSCOnly" \ 377 | -DENABLE_STATIC_JSC=ON \ 378 | -DUSE_THIN_ARCHIVES=OFF \ 379 | -DCMAKE_BUILD_TYPE=Release \ 380 | -DENABLE_FTL_JIT={enable_ftl_jit} \ 381 | -DENABLE_JIT=ON \ 382 | -DCMAKE_C_FLAGS="{c_flags}" \ 383 | -DCMAKE_CXX_FLAGS="{cxx_flags}" \ 384 | -G Ninja {icu_uc_in_flag} {macos_deploy_target_flag} {icu_flag} {cross_build_flag} 385 | "#, 386 | ) 387 | .trim() 388 | .replace("\\\n", ""), 389 | ) 390 | .current_dir(cmake_build_dir.clone()) 391 | .stderr(Stdio::inherit()) 392 | .stdin(Stdio::inherit()) 393 | .stdout(Stdio::inherit()); 394 | #[allow(unused)] 395 | let icu4c_source = env::current_dir().unwrap().join(if is_cross_aarch64_gnu { 396 | "icu-linux-aarch64" 397 | } else { 398 | "icu/icu4c" 399 | }); 400 | #[cfg(target_os = "macos")] 401 | { 402 | #[cfg(target_arch = "aarch64")] 403 | cmake_config.env("MACOSX_DEPLOYMENT_TARGET", "11.0"); 404 | #[cfg(target_arch = "x86_64")] 405 | cmake_config.env("MACOSX_DEPLOYMENT_TARGET", "10.15"); 406 | } 407 | cmake_config.env("CMAKE_LIBRARY_PATH", icu4c_source.to_str().unwrap()); 408 | if config.set_system_cc { 409 | if !cfg!(target_os = "windows") { 410 | cmake_config.env("CC", "clang").env("CXX", "clang++"); 411 | } 412 | } 413 | println!("{:?}", &cmake_config); 414 | assert_command_success(cmake_config, "cmake config failed"); 415 | let mut cmake_build = Command::new("cmake"); 416 | cmake_build 417 | .args(&["--build", ".", "--config", "Release", "--", "jsc"]) 418 | .current_dir(cmake_build_dir.clone()) 419 | .stderr(Stdio::inherit()) 420 | .stdin(Stdio::inherit()) 421 | .stdout(Stdio::inherit()); 422 | if config.set_system_cc { 423 | if !cfg!(target_os = "windows") { 424 | cmake_build.env("CC", "clang"); 425 | cmake_build.env("CXX", "clang++"); 426 | } 427 | } 428 | assert_command_success(cmake_build, "Build JavaScriptCore failed"); 429 | } 430 | 431 | fn assert_command_success(mut command: Command, msg: &str) { 432 | println!("Run command: {:?}", &command); 433 | assert!(command.output().expect(msg).status.success(), "{}", msg); 434 | } 435 | -------------------------------------------------------------------------------- /jsc-sys/src/binding.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | 5 | pub const JSC_OBJC_API_ENABLED: u32 = 0; 6 | #[repr(C)] 7 | #[derive(Debug, Copy, Clone)] 8 | pub struct OpaqueJSContextGroup { 9 | _unused: [u8; 0], 10 | } 11 | pub type JSContextGroupRef = *const OpaqueJSContextGroup; 12 | #[repr(C)] 13 | #[derive(Debug, Copy, Clone)] 14 | pub struct OpaqueJSContext { 15 | _unused: [u8; 0], 16 | } 17 | pub type JSContextRef = *const OpaqueJSContext; 18 | pub type JSGlobalContextRef = *mut OpaqueJSContext; 19 | #[repr(C)] 20 | #[derive(Debug, Copy, Clone)] 21 | pub struct OpaqueJSString { 22 | _unused: [u8; 0], 23 | } 24 | pub type JSStringRef = *mut OpaqueJSString; 25 | #[repr(C)] 26 | #[derive(Debug, Copy, Clone)] 27 | pub struct OpaqueJSClass { 28 | _unused: [u8; 0], 29 | } 30 | pub type JSClassRef = *mut OpaqueJSClass; 31 | #[repr(C)] 32 | #[derive(Debug, Copy, Clone)] 33 | pub struct OpaqueJSPropertyNameArray { 34 | _unused: [u8; 0], 35 | } 36 | pub type JSPropertyNameArrayRef = *mut OpaqueJSPropertyNameArray; 37 | #[repr(C)] 38 | #[derive(Debug, Copy, Clone)] 39 | pub struct OpaqueJSPropertyNameAccumulator { 40 | _unused: [u8; 0], 41 | } 42 | pub type JSPropertyNameAccumulatorRef = *mut OpaqueJSPropertyNameAccumulator; 43 | pub type JSTypedArrayBytesDeallocator = ::std::option::Option< 44 | unsafe extern "C" fn( 45 | bytes: *mut ::std::os::raw::c_void, 46 | deallocatorContext: *mut ::std::os::raw::c_void, 47 | ), 48 | >; 49 | #[repr(C)] 50 | #[derive(Debug, Copy, Clone)] 51 | pub struct OpaqueJSValue { 52 | _unused: [u8; 0], 53 | } 54 | pub type JSValueRef = *const OpaqueJSValue; 55 | pub type JSObjectRef = *mut OpaqueJSValue; 56 | extern "C" { 57 | pub fn JSEvaluateScript( 58 | ctx: JSContextRef, 59 | script: JSStringRef, 60 | thisObject: JSObjectRef, 61 | sourceURL: JSStringRef, 62 | startingLineNumber: ::std::os::raw::c_int, 63 | exception: *mut JSValueRef, 64 | ) -> JSValueRef; 65 | } 66 | extern "C" { 67 | pub fn JSCheckScriptSyntax( 68 | ctx: JSContextRef, 69 | script: JSStringRef, 70 | sourceURL: JSStringRef, 71 | startingLineNumber: ::std::os::raw::c_int, 72 | exception: *mut JSValueRef, 73 | ) -> bool; 74 | } 75 | extern "C" { 76 | pub fn JSGarbageCollect(ctx: JSContextRef); 77 | } 78 | pub type __darwin_size_t = ::std::os::raw::c_ulong; 79 | pub const JSType_kJSTypeUndefined: JSType = 0; 80 | pub const JSType_kJSTypeNull: JSType = 1; 81 | pub const JSType_kJSTypeBoolean: JSType = 2; 82 | pub const JSType_kJSTypeNumber: JSType = 3; 83 | pub const JSType_kJSTypeString: JSType = 4; 84 | pub const JSType_kJSTypeObject: JSType = 5; 85 | pub const JSType_kJSTypeSymbol: JSType = 6; 86 | pub type JSType = u32; 87 | pub const JSTypedArrayType_kJSTypedArrayTypeInt8Array: JSTypedArrayType = 0; 88 | pub const JSTypedArrayType_kJSTypedArrayTypeInt16Array: JSTypedArrayType = 1; 89 | pub const JSTypedArrayType_kJSTypedArrayTypeInt32Array: JSTypedArrayType = 2; 90 | pub const JSTypedArrayType_kJSTypedArrayTypeUint8Array: JSTypedArrayType = 3; 91 | pub const JSTypedArrayType_kJSTypedArrayTypeUint8ClampedArray: JSTypedArrayType = 4; 92 | pub const JSTypedArrayType_kJSTypedArrayTypeUint16Array: JSTypedArrayType = 5; 93 | pub const JSTypedArrayType_kJSTypedArrayTypeUint32Array: JSTypedArrayType = 6; 94 | pub const JSTypedArrayType_kJSTypedArrayTypeFloat32Array: JSTypedArrayType = 7; 95 | pub const JSTypedArrayType_kJSTypedArrayTypeFloat64Array: JSTypedArrayType = 8; 96 | pub const JSTypedArrayType_kJSTypedArrayTypeArrayBuffer: JSTypedArrayType = 9; 97 | pub const JSTypedArrayType_kJSTypedArrayTypeNone: JSTypedArrayType = 10; 98 | pub const JSTypedArrayType_kJSTypedArrayTypeBigInt64Array: JSTypedArrayType = 11; 99 | pub const JSTypedArrayType_kJSTypedArrayTypeBigUint64Array: JSTypedArrayType = 12; 100 | pub type JSTypedArrayType = u32; 101 | extern "C" { 102 | pub fn JSValueGetType(ctx: JSContextRef, value: JSValueRef) -> JSType; 103 | } 104 | extern "C" { 105 | pub fn JSValueIsUndefined(ctx: JSContextRef, value: JSValueRef) -> bool; 106 | } 107 | extern "C" { 108 | pub fn JSValueIsNull(ctx: JSContextRef, value: JSValueRef) -> bool; 109 | } 110 | extern "C" { 111 | pub fn JSValueIsBoolean(ctx: JSContextRef, value: JSValueRef) -> bool; 112 | } 113 | extern "C" { 114 | pub fn JSValueIsNumber(ctx: JSContextRef, value: JSValueRef) -> bool; 115 | } 116 | extern "C" { 117 | pub fn JSValueIsString(ctx: JSContextRef, value: JSValueRef) -> bool; 118 | } 119 | extern "C" { 120 | pub fn JSValueIsSymbol(ctx: JSContextRef, value: JSValueRef) -> bool; 121 | } 122 | extern "C" { 123 | pub fn JSValueIsObject(ctx: JSContextRef, value: JSValueRef) -> bool; 124 | } 125 | extern "C" { 126 | pub fn JSValueIsObjectOfClass(ctx: JSContextRef, value: JSValueRef, jsClass: JSClassRef) -> bool; 127 | } 128 | extern "C" { 129 | pub fn JSValueIsArray(ctx: JSContextRef, value: JSValueRef) -> bool; 130 | } 131 | extern "C" { 132 | pub fn JSValueIsDate(ctx: JSContextRef, value: JSValueRef) -> bool; 133 | } 134 | extern "C" { 135 | pub fn JSValueGetTypedArrayType( 136 | ctx: JSContextRef, 137 | value: JSValueRef, 138 | exception: *mut JSValueRef, 139 | ) -> JSTypedArrayType; 140 | } 141 | extern "C" { 142 | pub fn JSValueIsEqual( 143 | ctx: JSContextRef, 144 | a: JSValueRef, 145 | b: JSValueRef, 146 | exception: *mut JSValueRef, 147 | ) -> bool; 148 | } 149 | extern "C" { 150 | pub fn JSValueIsStrictEqual(ctx: JSContextRef, a: JSValueRef, b: JSValueRef) -> bool; 151 | } 152 | extern "C" { 153 | pub fn JSValueIsInstanceOfConstructor( 154 | ctx: JSContextRef, 155 | value: JSValueRef, 156 | constructor: JSObjectRef, 157 | exception: *mut JSValueRef, 158 | ) -> bool; 159 | } 160 | extern "C" { 161 | pub fn JSValueMakeUndefined(ctx: JSContextRef) -> JSValueRef; 162 | } 163 | extern "C" { 164 | pub fn JSValueMakeNull(ctx: JSContextRef) -> JSValueRef; 165 | } 166 | extern "C" { 167 | pub fn JSValueMakeBoolean(ctx: JSContextRef, boolean: bool) -> JSValueRef; 168 | } 169 | extern "C" { 170 | pub fn JSValueMakeNumber(ctx: JSContextRef, number: f64) -> JSValueRef; 171 | } 172 | extern "C" { 173 | pub fn JSValueMakeString(ctx: JSContextRef, string: JSStringRef) -> JSValueRef; 174 | } 175 | extern "C" { 176 | pub fn JSValueMakeSymbol(ctx: JSContextRef, description: JSStringRef) -> JSValueRef; 177 | } 178 | extern "C" { 179 | pub fn JSValueMakeFromJSONString(ctx: JSContextRef, string: JSStringRef) -> JSValueRef; 180 | } 181 | extern "C" { 182 | pub fn JSValueCreateJSONString( 183 | ctx: JSContextRef, 184 | value: JSValueRef, 185 | indent: ::std::os::raw::c_uint, 186 | exception: *mut JSValueRef, 187 | ) -> JSStringRef; 188 | } 189 | extern "C" { 190 | pub fn JSValueToBoolean(ctx: JSContextRef, value: JSValueRef) -> bool; 191 | } 192 | extern "C" { 193 | pub fn JSValueToNumber(ctx: JSContextRef, value: JSValueRef, exception: *mut JSValueRef) -> f64; 194 | } 195 | extern "C" { 196 | pub fn JSValueToStringCopy( 197 | ctx: JSContextRef, 198 | value: JSValueRef, 199 | exception: *mut JSValueRef, 200 | ) -> JSStringRef; 201 | } 202 | extern "C" { 203 | pub fn JSValueToObject( 204 | ctx: JSContextRef, 205 | value: JSValueRef, 206 | exception: *mut JSValueRef, 207 | ) -> JSObjectRef; 208 | } 209 | extern "C" { 210 | pub fn JSValueProtect(ctx: JSContextRef, value: JSValueRef); 211 | } 212 | extern "C" { 213 | pub fn JSValueUnprotect(ctx: JSContextRef, value: JSValueRef); 214 | } 215 | pub const kJSPropertyAttributeNone: _bindgen_ty_63 = 0; 216 | pub const kJSPropertyAttributeReadOnly: _bindgen_ty_63 = 2; 217 | pub const kJSPropertyAttributeDontEnum: _bindgen_ty_63 = 4; 218 | pub const kJSPropertyAttributeDontDelete: _bindgen_ty_63 = 8; 219 | pub type _bindgen_ty_63 = u32; 220 | pub type JSPropertyAttributes = ::std::os::raw::c_uint; 221 | pub const kJSClassAttributeNone: _bindgen_ty_64 = 0; 222 | pub const kJSClassAttributeNoAutomaticPrototype: _bindgen_ty_64 = 2; 223 | pub type _bindgen_ty_64 = u32; 224 | pub type JSClassAttributes = ::std::os::raw::c_uint; 225 | pub type JSObjectInitializeCallback = 226 | ::std::option::Option; 227 | pub type JSObjectFinalizeCallback = 228 | ::std::option::Option; 229 | pub type JSObjectHasPropertyCallback = ::std::option::Option< 230 | unsafe extern "C" fn(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef) -> bool, 231 | >; 232 | pub type JSObjectGetPropertyCallback = ::std::option::Option< 233 | unsafe extern "C" fn( 234 | ctx: JSContextRef, 235 | object: JSObjectRef, 236 | propertyName: JSStringRef, 237 | exception: *mut JSValueRef, 238 | ) -> JSValueRef, 239 | >; 240 | pub type JSObjectSetPropertyCallback = ::std::option::Option< 241 | unsafe extern "C" fn( 242 | ctx: JSContextRef, 243 | object: JSObjectRef, 244 | propertyName: JSStringRef, 245 | value: JSValueRef, 246 | exception: *mut JSValueRef, 247 | ) -> bool, 248 | >; 249 | pub type JSObjectDeletePropertyCallback = ::std::option::Option< 250 | unsafe extern "C" fn( 251 | ctx: JSContextRef, 252 | object: JSObjectRef, 253 | propertyName: JSStringRef, 254 | exception: *mut JSValueRef, 255 | ) -> bool, 256 | >; 257 | pub type JSObjectGetPropertyNamesCallback = ::std::option::Option< 258 | unsafe extern "C" fn( 259 | ctx: JSContextRef, 260 | object: JSObjectRef, 261 | propertyNames: JSPropertyNameAccumulatorRef, 262 | ), 263 | >; 264 | pub type JSObjectCallAsFunctionCallback = ::std::option::Option< 265 | unsafe extern "C" fn( 266 | ctx: JSContextRef, 267 | function: JSObjectRef, 268 | thisObject: JSObjectRef, 269 | argumentCount: usize, 270 | arguments: *const JSValueRef, 271 | exception: *mut JSValueRef, 272 | ) -> JSValueRef, 273 | >; 274 | pub type JSObjectCallAsConstructorCallback = ::std::option::Option< 275 | unsafe extern "C" fn( 276 | ctx: JSContextRef, 277 | constructor: JSObjectRef, 278 | argumentCount: usize, 279 | arguments: *const JSValueRef, 280 | exception: *mut JSValueRef, 281 | ) -> JSObjectRef, 282 | >; 283 | pub type JSObjectHasInstanceCallback = ::std::option::Option< 284 | unsafe extern "C" fn( 285 | ctx: JSContextRef, 286 | constructor: JSObjectRef, 287 | possibleInstance: JSValueRef, 288 | exception: *mut JSValueRef, 289 | ) -> bool, 290 | >; 291 | pub type JSObjectConvertToTypeCallback = ::std::option::Option< 292 | unsafe extern "C" fn( 293 | ctx: JSContextRef, 294 | object: JSObjectRef, 295 | type_: JSType, 296 | exception: *mut JSValueRef, 297 | ) -> JSValueRef, 298 | >; 299 | #[repr(C)] 300 | #[derive(Debug, Copy, Clone)] 301 | pub struct JSStaticValue { 302 | pub name: *const ::std::os::raw::c_char, 303 | pub getProperty: JSObjectGetPropertyCallback, 304 | pub setProperty: JSObjectSetPropertyCallback, 305 | pub attributes: JSPropertyAttributes, 306 | } 307 | #[repr(C)] 308 | #[derive(Debug, Copy, Clone)] 309 | pub struct JSStaticFunction { 310 | pub name: *const ::std::os::raw::c_char, 311 | pub callAsFunction: JSObjectCallAsFunctionCallback, 312 | pub attributes: JSPropertyAttributes, 313 | } 314 | #[repr(C)] 315 | #[derive(Debug, Copy, Clone)] 316 | pub struct JSClassDefinition { 317 | pub version: ::std::os::raw::c_int, 318 | pub attributes: JSClassAttributes, 319 | pub className: *const ::std::os::raw::c_char, 320 | pub parentClass: JSClassRef, 321 | pub staticValues: *const JSStaticValue, 322 | pub staticFunctions: *const JSStaticFunction, 323 | pub initialize: JSObjectInitializeCallback, 324 | pub finalize: JSObjectFinalizeCallback, 325 | pub hasProperty: JSObjectHasPropertyCallback, 326 | pub getProperty: JSObjectGetPropertyCallback, 327 | pub setProperty: JSObjectSetPropertyCallback, 328 | pub deleteProperty: JSObjectDeletePropertyCallback, 329 | pub getPropertyNames: JSObjectGetPropertyNamesCallback, 330 | pub callAsFunction: JSObjectCallAsFunctionCallback, 331 | pub callAsConstructor: JSObjectCallAsConstructorCallback, 332 | pub hasInstance: JSObjectHasInstanceCallback, 333 | pub convertToType: JSObjectConvertToTypeCallback, 334 | } 335 | extern "C" { 336 | pub static kJSClassDefinitionEmpty: JSClassDefinition; 337 | } 338 | extern "C" { 339 | pub fn JSClassCreate(definition: *const JSClassDefinition) -> JSClassRef; 340 | } 341 | extern "C" { 342 | pub fn JSClassRetain(jsClass: JSClassRef) -> JSClassRef; 343 | } 344 | extern "C" { 345 | pub fn JSClassRelease(jsClass: JSClassRef); 346 | } 347 | extern "C" { 348 | pub fn JSObjectMake( 349 | ctx: JSContextRef, 350 | jsClass: JSClassRef, 351 | data: *mut ::std::os::raw::c_void, 352 | ) -> JSObjectRef; 353 | } 354 | extern "C" { 355 | pub fn JSObjectMakeFunctionWithCallback( 356 | ctx: JSContextRef, 357 | name: JSStringRef, 358 | callAsFunction: JSObjectCallAsFunctionCallback, 359 | ) -> JSObjectRef; 360 | } 361 | extern "C" { 362 | pub fn JSObjectMakeConstructor( 363 | ctx: JSContextRef, 364 | jsClass: JSClassRef, 365 | callAsConstructor: JSObjectCallAsConstructorCallback, 366 | ) -> JSObjectRef; 367 | } 368 | extern "C" { 369 | pub fn JSObjectMakeArray( 370 | ctx: JSContextRef, 371 | argumentCount: usize, 372 | arguments: *const JSValueRef, 373 | exception: *mut JSValueRef, 374 | ) -> JSObjectRef; 375 | } 376 | extern "C" { 377 | pub fn JSObjectMakeDate( 378 | ctx: JSContextRef, 379 | argumentCount: usize, 380 | arguments: *const JSValueRef, 381 | exception: *mut JSValueRef, 382 | ) -> JSObjectRef; 383 | } 384 | extern "C" { 385 | pub fn JSObjectMakeError( 386 | ctx: JSContextRef, 387 | argumentCount: usize, 388 | arguments: *const JSValueRef, 389 | exception: *mut JSValueRef, 390 | ) -> JSObjectRef; 391 | } 392 | extern "C" { 393 | pub fn JSObjectMakeRegExp( 394 | ctx: JSContextRef, 395 | argumentCount: usize, 396 | arguments: *const JSValueRef, 397 | exception: *mut JSValueRef, 398 | ) -> JSObjectRef; 399 | } 400 | extern "C" { 401 | pub fn JSObjectMakeDeferredPromise( 402 | ctx: JSContextRef, 403 | resolve: *mut JSObjectRef, 404 | reject: *mut JSObjectRef, 405 | exception: *mut JSValueRef, 406 | ) -> JSObjectRef; 407 | } 408 | extern "C" { 409 | pub fn JSObjectMakeFunction( 410 | ctx: JSContextRef, 411 | name: JSStringRef, 412 | parameterCount: ::std::os::raw::c_uint, 413 | parameterNames: *const JSStringRef, 414 | body: JSStringRef, 415 | sourceURL: JSStringRef, 416 | startingLineNumber: ::std::os::raw::c_int, 417 | exception: *mut JSValueRef, 418 | ) -> JSObjectRef; 419 | } 420 | extern "C" { 421 | pub fn JSObjectGetPrototype(ctx: JSContextRef, object: JSObjectRef) -> JSValueRef; 422 | } 423 | extern "C" { 424 | pub fn JSObjectSetPrototype(ctx: JSContextRef, object: JSObjectRef, value: JSValueRef); 425 | } 426 | extern "C" { 427 | pub fn JSObjectHasProperty( 428 | ctx: JSContextRef, 429 | object: JSObjectRef, 430 | propertyName: JSStringRef, 431 | ) -> bool; 432 | } 433 | extern "C" { 434 | pub fn JSObjectGetProperty( 435 | ctx: JSContextRef, 436 | object: JSObjectRef, 437 | propertyName: JSStringRef, 438 | exception: *mut JSValueRef, 439 | ) -> JSValueRef; 440 | } 441 | extern "C" { 442 | pub fn JSObjectSetProperty( 443 | ctx: JSContextRef, 444 | object: JSObjectRef, 445 | propertyName: JSStringRef, 446 | value: JSValueRef, 447 | attributes: JSPropertyAttributes, 448 | exception: *mut JSValueRef, 449 | ); 450 | } 451 | extern "C" { 452 | pub fn JSObjectDeleteProperty( 453 | ctx: JSContextRef, 454 | object: JSObjectRef, 455 | propertyName: JSStringRef, 456 | exception: *mut JSValueRef, 457 | ) -> bool; 458 | } 459 | extern "C" { 460 | pub fn JSObjectHasPropertyForKey( 461 | ctx: JSContextRef, 462 | object: JSObjectRef, 463 | propertyKey: JSValueRef, 464 | exception: *mut JSValueRef, 465 | ) -> bool; 466 | } 467 | extern "C" { 468 | pub fn JSObjectGetPropertyForKey( 469 | ctx: JSContextRef, 470 | object: JSObjectRef, 471 | propertyKey: JSValueRef, 472 | exception: *mut JSValueRef, 473 | ) -> JSValueRef; 474 | } 475 | extern "C" { 476 | pub fn JSObjectSetPropertyForKey( 477 | ctx: JSContextRef, 478 | object: JSObjectRef, 479 | propertyKey: JSValueRef, 480 | value: JSValueRef, 481 | attributes: JSPropertyAttributes, 482 | exception: *mut JSValueRef, 483 | ); 484 | } 485 | extern "C" { 486 | pub fn JSObjectDeletePropertyForKey( 487 | ctx: JSContextRef, 488 | object: JSObjectRef, 489 | propertyKey: JSValueRef, 490 | exception: *mut JSValueRef, 491 | ) -> bool; 492 | } 493 | extern "C" { 494 | pub fn JSObjectGetPropertyAtIndex( 495 | ctx: JSContextRef, 496 | object: JSObjectRef, 497 | propertyIndex: ::std::os::raw::c_uint, 498 | exception: *mut JSValueRef, 499 | ) -> JSValueRef; 500 | } 501 | extern "C" { 502 | pub fn JSObjectSetPropertyAtIndex( 503 | ctx: JSContextRef, 504 | object: JSObjectRef, 505 | propertyIndex: ::std::os::raw::c_uint, 506 | value: JSValueRef, 507 | exception: *mut JSValueRef, 508 | ); 509 | } 510 | extern "C" { 511 | pub fn JSObjectGetPrivate(object: JSObjectRef) -> *mut ::std::os::raw::c_void; 512 | } 513 | extern "C" { 514 | pub fn JSObjectSetPrivate(object: JSObjectRef, data: *mut ::std::os::raw::c_void) -> bool; 515 | } 516 | extern "C" { 517 | pub fn JSObjectIsFunction(ctx: JSContextRef, object: JSObjectRef) -> bool; 518 | } 519 | extern "C" { 520 | pub fn JSObjectCallAsFunction( 521 | ctx: JSContextRef, 522 | object: JSObjectRef, 523 | thisObject: JSObjectRef, 524 | argumentCount: usize, 525 | arguments: *const JSValueRef, 526 | exception: *mut JSValueRef, 527 | ) -> JSValueRef; 528 | } 529 | extern "C" { 530 | pub fn JSObjectIsConstructor(ctx: JSContextRef, object: JSObjectRef) -> bool; 531 | } 532 | extern "C" { 533 | pub fn JSObjectCallAsConstructor( 534 | ctx: JSContextRef, 535 | object: JSObjectRef, 536 | argumentCount: usize, 537 | arguments: *const JSValueRef, 538 | exception: *mut JSValueRef, 539 | ) -> JSObjectRef; 540 | } 541 | extern "C" { 542 | pub fn JSObjectCopyPropertyNames( 543 | ctx: JSContextRef, 544 | object: JSObjectRef, 545 | ) -> JSPropertyNameArrayRef; 546 | } 547 | extern "C" { 548 | pub fn JSPropertyNameArrayRetain(array: JSPropertyNameArrayRef) -> JSPropertyNameArrayRef; 549 | } 550 | extern "C" { 551 | pub fn JSPropertyNameArrayRelease(array: JSPropertyNameArrayRef); 552 | } 553 | extern "C" { 554 | pub fn JSPropertyNameArrayGetCount(array: JSPropertyNameArrayRef) -> usize; 555 | } 556 | extern "C" { 557 | pub fn JSPropertyNameArrayGetNameAtIndex( 558 | array: JSPropertyNameArrayRef, 559 | index: usize, 560 | ) -> JSStringRef; 561 | } 562 | extern "C" { 563 | pub fn JSPropertyNameAccumulatorAddName( 564 | accumulator: JSPropertyNameAccumulatorRef, 565 | propertyName: JSStringRef, 566 | ); 567 | } 568 | extern "C" { 569 | pub fn JSContextGroupCreate() -> JSContextGroupRef; 570 | } 571 | extern "C" { 572 | pub fn JSContextGroupRetain(group: JSContextGroupRef) -> JSContextGroupRef; 573 | } 574 | extern "C" { 575 | pub fn JSContextGroupRelease(group: JSContextGroupRef); 576 | } 577 | extern "C" { 578 | pub fn JSGlobalContextCreate(globalObjectClass: JSClassRef) -> JSGlobalContextRef; 579 | } 580 | extern "C" { 581 | pub fn JSGlobalContextCreateInGroup( 582 | group: JSContextGroupRef, 583 | globalObjectClass: JSClassRef, 584 | ) -> JSGlobalContextRef; 585 | } 586 | extern "C" { 587 | pub fn JSGlobalContextRetain(ctx: JSGlobalContextRef) -> JSGlobalContextRef; 588 | } 589 | extern "C" { 590 | pub fn JSGlobalContextRelease(ctx: JSGlobalContextRef); 591 | } 592 | extern "C" { 593 | pub fn JSContextGetGlobalObject(ctx: JSContextRef) -> JSObjectRef; 594 | } 595 | extern "C" { 596 | pub fn JSContextGetGroup(ctx: JSContextRef) -> JSContextGroupRef; 597 | } 598 | extern "C" { 599 | pub fn JSContextGetGlobalContext(ctx: JSContextRef) -> JSGlobalContextRef; 600 | } 601 | extern "C" { 602 | pub fn JSGlobalContextCopyName(ctx: JSGlobalContextRef) -> JSStringRef; 603 | } 604 | extern "C" { 605 | pub fn JSGlobalContextSetName(ctx: JSGlobalContextRef, name: JSStringRef); 606 | } 607 | pub type JSChar = ::std::os::raw::c_ushort; 608 | extern "C" { 609 | pub fn JSStringCreateWithCharacters(chars: *const JSChar, numChars: usize) -> JSStringRef; 610 | } 611 | extern "C" { 612 | pub fn JSStringCreateWithUTF8CString(string: *const ::std::os::raw::c_char) -> JSStringRef; 613 | } 614 | extern "C" { 615 | pub fn JSStringRetain(string: JSStringRef) -> JSStringRef; 616 | } 617 | extern "C" { 618 | pub fn JSStringRelease(string: JSStringRef); 619 | } 620 | extern "C" { 621 | pub fn JSStringGetLength(string: JSStringRef) -> usize; 622 | } 623 | extern "C" { 624 | pub fn JSStringGetCharactersPtr(string: JSStringRef) -> *const JSChar; 625 | } 626 | extern "C" { 627 | pub fn JSStringGetMaximumUTF8CStringSize(string: JSStringRef) -> usize; 628 | } 629 | extern "C" { 630 | pub fn JSStringGetUTF8CString( 631 | string: JSStringRef, 632 | buffer: *mut ::std::os::raw::c_char, 633 | bufferSize: usize, 634 | ) -> usize; 635 | } 636 | extern "C" { 637 | pub fn JSStringIsEqual(a: JSStringRef, b: JSStringRef) -> bool; 638 | } 639 | extern "C" { 640 | pub fn JSStringIsEqualToUTF8CString(a: JSStringRef, b: *const ::std::os::raw::c_char) -> bool; 641 | } 642 | extern "C" { 643 | pub fn JSObjectMakeTypedArray( 644 | ctx: JSContextRef, 645 | arrayType: JSTypedArrayType, 646 | length: usize, 647 | exception: *mut JSValueRef, 648 | ) -> JSObjectRef; 649 | } 650 | extern "C" { 651 | pub fn JSObjectMakeTypedArrayWithBytesNoCopy( 652 | ctx: JSContextRef, 653 | arrayType: JSTypedArrayType, 654 | bytes: *mut ::std::os::raw::c_void, 655 | byteLength: usize, 656 | bytesDeallocator: JSTypedArrayBytesDeallocator, 657 | deallocatorContext: *mut ::std::os::raw::c_void, 658 | exception: *mut JSValueRef, 659 | ) -> JSObjectRef; 660 | } 661 | extern "C" { 662 | pub fn JSObjectMakeTypedArrayWithArrayBuffer( 663 | ctx: JSContextRef, 664 | arrayType: JSTypedArrayType, 665 | buffer: JSObjectRef, 666 | exception: *mut JSValueRef, 667 | ) -> JSObjectRef; 668 | } 669 | extern "C" { 670 | pub fn JSObjectMakeTypedArrayWithArrayBufferAndOffset( 671 | ctx: JSContextRef, 672 | arrayType: JSTypedArrayType, 673 | buffer: JSObjectRef, 674 | byteOffset: usize, 675 | length: usize, 676 | exception: *mut JSValueRef, 677 | ) -> JSObjectRef; 678 | } 679 | extern "C" { 680 | pub fn JSObjectGetTypedArrayBytesPtr( 681 | ctx: JSContextRef, 682 | object: JSObjectRef, 683 | exception: *mut JSValueRef, 684 | ) -> *mut ::std::os::raw::c_void; 685 | } 686 | extern "C" { 687 | pub fn JSObjectGetTypedArrayLength( 688 | ctx: JSContextRef, 689 | object: JSObjectRef, 690 | exception: *mut JSValueRef, 691 | ) -> usize; 692 | } 693 | extern "C" { 694 | pub fn JSObjectGetTypedArrayByteLength( 695 | ctx: JSContextRef, 696 | object: JSObjectRef, 697 | exception: *mut JSValueRef, 698 | ) -> usize; 699 | } 700 | extern "C" { 701 | pub fn JSObjectGetTypedArrayByteOffset( 702 | ctx: JSContextRef, 703 | object: JSObjectRef, 704 | exception: *mut JSValueRef, 705 | ) -> usize; 706 | } 707 | extern "C" { 708 | pub fn JSObjectGetTypedArrayBuffer( 709 | ctx: JSContextRef, 710 | object: JSObjectRef, 711 | exception: *mut JSValueRef, 712 | ) -> JSObjectRef; 713 | } 714 | extern "C" { 715 | pub fn JSObjectMakeArrayBufferWithBytesNoCopy( 716 | ctx: JSContextRef, 717 | bytes: *mut ::std::os::raw::c_void, 718 | byteLength: usize, 719 | bytesDeallocator: JSTypedArrayBytesDeallocator, 720 | deallocatorContext: *mut ::std::os::raw::c_void, 721 | exception: *mut JSValueRef, 722 | ) -> JSObjectRef; 723 | } 724 | extern "C" { 725 | pub fn JSObjectGetArrayBufferBytesPtr( 726 | ctx: JSContextRef, 727 | object: JSObjectRef, 728 | exception: *mut JSValueRef, 729 | ) -> *mut ::std::os::raw::c_void; 730 | } 731 | extern "C" { 732 | pub fn JSObjectGetArrayBufferByteLength( 733 | ctx: JSContextRef, 734 | object: JSObjectRef, 735 | exception: *mut JSValueRef, 736 | ) -> usize; 737 | } 738 | --------------------------------------------------------------------------------