├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── examples └── eval.rs ├── justfile ├── libquickjs-sys ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── build.rs ├── embed │ ├── bindings.rs │ ├── patches │ │ └── js-tobigint64-overflow.patch │ ├── quickjs │ │ ├── Changelog │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── TODO │ │ ├── VERSION │ │ ├── cutils.c │ │ ├── cutils.h │ │ ├── libbf.c │ │ ├── libbf.h │ │ ├── libregexp-opcode.h │ │ ├── libregexp.c │ │ ├── libregexp.h │ │ ├── libunicode-table.h │ │ ├── libunicode.c │ │ ├── libunicode.h │ │ ├── list.h │ │ ├── qjs.c │ │ ├── qjsc.c │ │ ├── quickjs-atom.h │ │ ├── quickjs-libc.c │ │ ├── quickjs-libc.h │ │ ├── quickjs-opcode.h │ │ ├── quickjs.c │ │ ├── quickjs.h │ │ ├── readme.txt │ │ ├── unicode_gen.c │ │ └── unicode_gen_def.h │ └── static-functions.c ├── release.toml ├── src │ ├── lib.rs │ └── static-functions.rs └── wrapper.h ├── release.toml ├── shell.nix └── src ├── bindings ├── compile.rs ├── convert.rs ├── droppable_value.rs ├── mod.rs ├── serialize.rs └── value.rs ├── callback.rs ├── console.rs ├── lib.rs ├── tests.rs └── value ├── bigint.rs └── mod.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: {} 8 | release: 9 | types: 10 | - created 11 | 12 | jobs: 13 | test: 14 | name: Test on ${{ matrix.os }} with features ${{ matrix.features }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: 19 | - ubuntu-latest 20 | - macOS-latest 21 | - windows-latest 22 | runs-on: ${{ matrix.os }} 23 | 24 | steps: 25 | - uses: actions/checkout@v1 26 | 27 | - name: Install stable toolchain 28 | uses: actions-rs/toolchain@v1 29 | with: 30 | profile: minimal 31 | toolchain: stable 32 | override: true 33 | components: rustfmt, clippy 34 | if: matrix.os != 'windows-latest' 35 | 36 | - name: Install stable toolchain (windows) 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | profile: minimal 40 | toolchain: stable 41 | target: x86_64-pc-windows-gnu 42 | override: true 43 | if: matrix.os == 'windows-latest' 44 | 45 | - name: Install `just` command runner 46 | uses: extractions/setup-just@v1 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | 50 | - name: Setup (debian) 51 | run: | 52 | echo "Install system tools" 53 | sudo apt-get update -y 54 | sudo apt-get install -y curl xz-utils build-essential gcc-multilib valgrind 55 | if: matrix.os == 'ubuntu-latest' 56 | 57 | - name: Setup (windows) 58 | run: | 59 | $env:PATH = "C:\msys64\mingw64\bin;C:\msys64\usr\bin;$env:PATH" 60 | echo "PATH=${env:PATH}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 61 | echo "CARGO_BUILD_TARGET=x86_64-pc-windows-gnu" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 62 | if: matrix.os == 'windows-latest' 63 | 64 | - name: Build (default features) 65 | env: 66 | FEATURES: "" 67 | run: | 68 | cargo clean 69 | just FEATURES="$FEATURES" build 70 | 71 | - name: Test (default features) 72 | env: 73 | FEATURES: "" 74 | run: | 75 | just FEATURES="$FEATURES" test 76 | 77 | - name: Build (--no-default-features) 78 | env: 79 | FEATURES: "--no-default-features" 80 | run: | 81 | cargo clean 82 | just FEATURES="$FEATURES" build 83 | 84 | - name: Test (--no-default-features) 85 | env: 86 | FEATURES: "--no-default-features" 87 | run: | 88 | just FEATURES="$FEATURES" test 89 | 90 | - name: Build (--all-features) 91 | env: 92 | FEATURES: "--all-features" 93 | run: | 94 | cargo clean 95 | just FEATURES="$FEATURES" build 96 | 97 | - name: Test (--all-features) 98 | env: 99 | FEATURES: "--all-features" 100 | run: | 101 | just FEATURES="$FEATURES" test 102 | 103 | - name: Lint 104 | run: just lint 105 | if: matrix.os == 'ubuntu-latest' 106 | 107 | - name: Check for leaks (Valgrind) 108 | run: just valgrind 109 | if: matrix.os == 'ubuntu-latest' 110 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | .vscode 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # quick-js - Changelog 2 | 3 | ## Master branch 4 | 5 | * Fixed use after free in `set_global` [#105](https://github.com/theduke/quickjs-rs/issues/105) 6 | * `add_callback` can now take `JsValue` arguments [#109](https://github.com/theduke/quickjs-rs/issues/109) 7 | * Enable chrono feature by default 8 | * Update to QuickJS 2021-03-27 9 | 10 | ## v0.4.0 - 2021-02-05 11 | 12 | * Bumped quickjs to `2020-11-08` 13 | * Added `Context::set_global` 14 | * Added `JsValue::Undefined` (breaking change) 15 | 16 | ## v0.3.4 - 2020-07-09 17 | 18 | * Bump quickjs to 2020-07-05 19 | 20 | ## v0.3.3 - 2020-05-27 21 | 22 | * Add Windows support 23 | (only with MSYS2 environment and `x86_64-pc-windows-gnu` target architecture) 24 | 25 | ## v0.3.2 - 2020-05-25 26 | 27 | * Updated quickjs to 2020-04-12 28 | 29 | ## v0.3.1 - 2020-03-24 30 | 31 | * Update quickjs to 2020-03-16 32 | * Add `TryFrom` impl for `HashMap` 33 | 34 | ## v0.3.0 - 2019-11-02 35 | 36 | ### Features 37 | 38 | * Add BigInt integration 39 | * Add logging system and optional `log` crate integration 40 | * Upgrade quickjs to 2019-10-27 41 | 42 | ### Breaking Changes 43 | 44 | * Made `Value` enum non-exhaustive 45 | * new Value::BigInt variant (with `bigint` feature) 46 | 47 | ## v0.2.3 - 2019-08-30 48 | 49 | * Properly free property keys after enumeration 50 | (Fixes memory leak when deserializing objects) 51 | 52 | ## v0.2.2 - 2019-08-13 53 | 54 | * Fix invalid millisecond conversion for JsValue::Date 55 | 56 | ## v0.2.1 - 2019-08-13 57 | 58 | * Impelemented deserializiation of objects to `JsValue::Object` 59 | * Added `chrono` integration via the `chrono` feature 60 | Adds a `JsValue::Date(DateTime)` variant that allows (de)serializing 61 | a JS `Date` 62 | * Implemented resolving promises in `eval`/`call_function` 63 | * Added `patched` feature for applying quickjs fixes 64 | * quickjs upgraded to `2019-08-10` release 65 | 66 | ## v0.2.0 - 2019-07-31 67 | 68 | * Added `memory_limit` customization 69 | * Added `Context::clear` method for resetting context 70 | * Callbacks now support more function signatures 71 | ( up to 5 arguments, `Result` return value) 72 | * Updated embedded quickjs bindings to version 2019-07-28. 73 | * Fixed a bug in callback logic 74 | 75 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2018" 3 | name = "quick-js" 4 | description = "QuickJS Javascript engine wrapper" 5 | version = "0.4.2-alpha.0" 6 | readme = "README.md" 7 | documentation = "https://docs.rs/quick-js" 8 | repository = "https://github.com/theduke/quickjs-rs" 9 | license = "MIT" 10 | authors = ["Christoph Herzog "] 11 | keywords = ["quickjs", "javascript", "js", "engine", "interpreter"] 12 | 13 | [package.metadata.docs.rs] 14 | features = [ "chrono", "bigint", "log" ] 15 | 16 | [features] 17 | default = ["chrono"] 18 | patched = ["libquickjs-sys/patched"] 19 | bigint = ["num-bigint", "num-traits", "libquickjs-sys/patched"] 20 | 21 | [dependencies] 22 | libquickjs-sys = { version = ">= 0.9.0, < 0.10.0", path = "./libquickjs-sys" } 23 | chrono = { version = "0.4.7", optional = true } 24 | num-bigint = { version = "0.2.2", optional = true } 25 | num-traits = { version = "0.2.0", optional = true } 26 | log = { version = "0.4.8", optional = true } 27 | once_cell = "1.2.0" 28 | 29 | [workspace] 30 | members = [ 31 | "libquickjs-sys", 32 | ] 33 | 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Christoph Herzog 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickjs-rs 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/quick-js.svg?maxAge=3600)](https://crates.io/crates/quick-js) 4 | [![docs.rs](https://docs.rs/quick-js/badge.svg)](https://docs.rs/quick-js) 5 | [![Build Status](https://github.com/theduke/quickjs-rs/workflows/CI/badge.svg) 6 | 7 | A Rust wrapper for [QuickJS](https://bellard.org/quickjs/). 8 | 9 | QuickJS is a new, small Javascript engine by Fabrice Bellard and Charlie Gordon. 10 | It is fast and supports the full ES2020 specification. 11 | 12 | This crate allows you to easily run and integrate with Javascript code from Rust. 13 | 14 | ## Quickstart 15 | 16 | ```toml 17 | [dependencies] 18 | quick-js = "0.4.1" 19 | ``` 20 | 21 | ```rust 22 | use quick_js::{Context, JsValue}; 23 | 24 | let context = Context::new().unwrap(); 25 | 26 | // Eval. 27 | 28 | let value = context.eval("1 + 2").unwrap(); 29 | assert_eq!(value, JsValue::Int(3)); 30 | 31 | let value = context.eval_as::(" var x = 100 + 250; x.toString() ").unwrap(); 32 | assert_eq!(&value, "350"); 33 | 34 | // Callbacks. 35 | 36 | context.add_callback("myCallback", |a: i32, b: i32| a + b).unwrap(); 37 | 38 | context.eval(r#" 39 | // x will equal 30 40 | var x = myCallback(10, 20); 41 | "#).unwrap(); 42 | ``` 43 | 44 | ## Optional Features 45 | 46 | The crate supports the following features: 47 | 48 | * `chrono`: chrono integration 49 | - adds a `JsValue::Date` variant that can be (de)serialized to/from a JS `Date` 50 | * `bigint`: arbitrary precision integer support via [num-bigint](https://github.com/rust-num/num-bigint) 51 | * `log`: allows forwarding `console.log` messages to the `log` crate. 52 | Note: must be enabled with `ContextBuilder::console(quick_js::console::LogConsole);` 53 | 54 | * `patched` 55 | Enabled automatically for some other features, like `bigint`. 56 | You should not need to enable this manually. 57 | Applies QuickJS patches that can be found in `libquickjs-sys/embed/patches` directory. 58 | 59 | 60 | ## Installation 61 | 62 | By default, quickjs is **bundled** with the `libquickjs-sys` crate and 63 | automatically compiled, assuming you have the appropriate dependencies. 64 | 65 | ### Windows Support 66 | 67 | Windows is only supported with the [MSYS2](https://www.msys2.org/) environment 68 | and `x86_64-pc-windows-gnu` target architecture. 69 | 70 | If you have MSYS2 installed and the MSYS `bin` directory in your path, you can 71 | compile quickjs with `cargo build --target="x86_64-pc-windows-gnu"`. 72 | 73 | The target can also be configured permanently via a 74 | [cargo config file](https://doc.rust-lang.org/cargo/reference/config.html) or 75 | the `CARGO_BUILD_TARGET` env var. 76 | 77 | ### System installation 78 | 79 | To use the system installation, without the bundled feature, first install the required 80 | dependencies, and then compile and install quickjs. 81 | 82 | ```bash 83 | # Debian/Ubuntu: apt-get install -y curl xz-utils build-essential gcc-multilib libclang-dev clang 84 | mkdir quickjs 85 | curl -L https://bellard.org/quickjs/quickjs-2019-07-09.tar.xz | tar xJv -C quickjs --strip-components 1 86 | cd quickjs 87 | sudo make install 88 | ``` 89 | 90 | You then need to disable the `bundled` feature in the `libquickjs-sys` crate to 91 | force using the system version. 92 | -------------------------------------------------------------------------------- /examples/eval.rs: -------------------------------------------------------------------------------- 1 | use quick_js::Context; 2 | 3 | pub fn main() { 4 | let context = Context::new().unwrap(); 5 | 6 | let value = context.eval("1 + 2").unwrap(); 7 | println!("js: 1 + 2 = {:?}", value); 8 | 9 | context 10 | .add_callback("myCallback", |a: i32, b: i32| a + b * b) 11 | .unwrap(); 12 | 13 | let value = context 14 | .eval( 15 | r#" 16 | var x = myCallback(10, 20); 17 | x; 18 | "#, 19 | ) 20 | .unwrap(); 21 | println!("js: callback = {:?}", value); 22 | } 23 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | embed_dir := "./libquickjs-sys/embed/quickjs" 2 | 3 | DOWNLOAD_URL := "https://bellard.org/quickjs/quickjs-2021-03-27.tar.xz" 4 | FEATURES := "--all-features" 5 | 6 | download-new: 7 | test -d {{embed_dir}} && rm -r {{embed_dir}} || echo "" 8 | mkdir {{embed_dir}} && \ 9 | curl -L {{DOWNLOAD_URL}} | tar xJv -C {{embed_dir}} --strip-components 1 10 | 11 | download-cleanup: 12 | rm -r "{{embed_dir}}/doc" "{{embed_dir}}/examples" "{{embed_dir}}/tests" 13 | find "{{embed_dir}}" -type f | grep -E "\.(pdf|html|js|texi|sh)$" | xargs rm 14 | find "{{embed_dir}}" -type f | grep test | xargs rm 15 | 16 | generate-bindings: 17 | (cd libquickjs-sys; bindgen wrapper.h -o embed/bindings.rs -- -I ./embed) 18 | # Update VERSION in README 19 | sed -i "s/**Embedded VERSION: .*/**Embedded VERSION: $(cat ./libquickjs-sys/embed/quickjs/VERSION)**/" ./libquickjs-sys/README.md 20 | 21 | update-quickjs: download-new generate-bindings download-cleanup 22 | 23 | 24 | debian-setup: 25 | echo "Installing dependencies..." 26 | sudo apt update && sudo apt-get install -y curl xz-utils build-essential gcc-multilib libclang-dev clang valgrind 27 | 28 | build: 29 | rustc --version 30 | cargo --version 31 | 32 | cargo build --verbose {{FEATURES}} 33 | 34 | test: 35 | rustc --version 36 | cargo --version 37 | 38 | # Limit test threads to 1 to show test name before execution. 39 | RUST_TEST_THREADS=1 cargo test --verbose {{FEATURES}} 40 | 41 | lint: 42 | rustc --version 43 | cargo --version 44 | cargo clippy --version 45 | 46 | echo "Linting!" 47 | rustup component add rustfmt clippy 48 | 49 | echo "Checking formatting..." 50 | cargo fmt -- --check 51 | echo "Checking clippy..." 52 | cargo clippy 53 | 54 | valgrind: 55 | echo "Checking for memory leaks..." 56 | cargo clean 57 | cargo build --tests --all-features 58 | find target/debug/deps -maxdepth 1 -type f -executable | xargs valgrind --leak-check=full --error-exitcode=1 --gen-suppressions=yes --show-error-list=yes 59 | 60 | -------------------------------------------------------------------------------- /libquickjs-sys/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # libquickjs_sys - Changelog 2 | 3 | 4 | ## v0.10.0 - 2021-08-09 5 | 6 | * Upgraded to quickjs version `2021-03-27` 7 | * Added `JS_ValueGetTag` 8 | 9 | ## v0.9.0 - 2021-02-04 10 | 11 | * Upgraded to quickjs version `2020-11-08` 12 | * Added wrappers to expose various QuickJS functions that are `inline static` 13 | * Always compile with -fPIC 14 | 15 | ## v0.8.0 - 2020-09-29 16 | 17 | Upgraded to quickjs version `2020-09-06`. 18 | 19 | * Added 20 | - JS_SetIsHTMLDDA 21 | - JS_GetScriptOrModuleName 22 | - JS_RunModule 23 | - Multiple new constants, including `JS_ATOM_NULL` 24 | 25 | JS_SetIsHTMLDDA 26 | 27 | ## v0.7.0 - 2020-07-09 28 | 29 | Upgraded to quickjs version `2020-07-05`. 30 | 31 | * Added 32 | - JS_ParseJSON2 33 | - JSSharedArrayBufferFunctions 34 | - JS_WriteObject2 35 | - JS_SetSharedArrayBufferFunctions 36 | - JS_WriteObject2 37 | - JS_SetSharedArrayBufferFunctions 38 | - JS_PARSE_JSON_EXT 39 | - JS_WRITE_OBJ_SAB 40 | - JS_WRITE_OBJ_REFERENCE 41 | - JS_READ_OBJ_SAB 42 | - JS_READ_OBJ_REFERENCE 43 | 44 | ## v0.6.0 - 2020-05-25 45 | 46 | Upgraded to quickjs version `2020-04-12`. 47 | 48 | * Lot's of changes from `usize` to `size_t`. 49 | 50 | ## v0.5.0 - 2020-03-24 51 | 52 | Upgraded to version `2020-03-16`: 53 | 54 | - Added functions `JS_GetRuntimeOpaque`, `JS_SetRuntimeOpaque` 55 | - Removed function `JS_NewInt64`, JS_ToInt64Ext 56 | 57 | ## v0.4.0 - 2019-11-02 58 | 59 | Upgraded to version `2019-09-18`: 60 | 61 | * Added `JS_ValueToAtom` 62 | * Added `JS_SetConstructor` 63 | * `JS_GetTypedArrayBuffer` 64 | 65 | Updated bindgen dependency to 0.51. 66 | 67 | ## v0.3.0 - 2019-08-13 68 | 69 | * Added `patched` feature for applying patches 70 | * Added patch stack-overflow-signed to fix stackoverflow due invalid cast 71 | 72 | * c_int changed to usize in JS_NewAtomLen/JS_NewStringLen 73 | * JS_ToCStringLen2 replaces JS_ToCStringLen 74 | * Added JS_GetOwnProperty(Names) functions 75 | 76 | ## v0.2.0 - 2019-07-31 77 | 78 | * Updated embedded bindings to version 2019-07-28 79 | - `JS_EVAL_FLAG_SHEBANG` constant was removed 80 | - `JS_NewPromiseCallback` was added 81 | -------------------------------------------------------------------------------- /libquickjs-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2018" 3 | name = "libquickjs-sys" 4 | description = "QuickJS Javascript Engine FFI bindings" 5 | version = "0.9.0" 6 | readme = "README.md" 7 | documentation = "https://docs.rs/quickjs-sys" 8 | repository = "https://github.com/theduke/quickjs-rs" 9 | license = "MIT" 10 | authors = ["Christoph Herzog "] 11 | categories = ["external-ffi-bindings"] 12 | keywords = ["quickjs"] 13 | 14 | build = "build.rs" 15 | 16 | [features] 17 | bundled = ["cc", "copy_dir"] 18 | patched = ["bundled"] 19 | default = ["bundled"] 20 | 21 | system = ["bindgen"] 22 | 23 | [build-dependencies] 24 | bindgen = { version = "0.57.0", optional = true } 25 | cc = { version = "1.0.66", optional = true } 26 | copy_dir = { version = "0.1.2", optional = true } 27 | -------------------------------------------------------------------------------- /libquickjs-sys/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Christoph Herzog 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /libquickjs-sys/README.md: -------------------------------------------------------------------------------- 1 | # libquickjs-sys 2 | 3 | FFI Bindings for [quickjs](https://bellard.org/quickjs/), a Javascript engine. 4 | 5 | See the [quick](https://crates.io/crates/quickjs) crate for a high-level 6 | wrapper. 7 | 8 | 9 | *Version 0.9.0* 10 | **Embedded VERSION: 2021-03-27** 11 | 12 | ## Embedded vs system 13 | 14 | By default, an embedded version of quickjs is used. 15 | 16 | If you want to use a version installed on your system, use: 17 | 18 | 19 | ```toml 20 | libquickjs-sys = { version = "...", default-features = false, features = ["system"] } 21 | ``` 22 | 23 | 24 | ## Updating the embedded bindings 25 | 26 | QuickJS sources and a pre-generated `bindings.rs` are included in the repo. 27 | 28 | They are used if the `embedded` feature is enabled. 29 | 30 | To updat the bindings, follow these steps: 31 | 32 | * (Install [just](https://github.com/casey/just)) 33 | * Update the download URL in ./justfile 34 | * run `just update-quickjs` 35 | -------------------------------------------------------------------------------- /libquickjs-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use std::env; 4 | 5 | fn exists(path: impl AsRef) -> bool { 6 | PathBuf::from(path.as_ref()).exists() 7 | } 8 | 9 | const LIB_NAME: &str = "quickjs"; 10 | 11 | #[cfg(all(not(feature = "system"), not(feature = "bundled")))] 12 | fn main() { 13 | panic!("Invalid config for crate libquickjs-sys: must enable either the 'bundled' or the 'system' feature"); 14 | } 15 | 16 | #[cfg(feature = "system")] 17 | extern crate bindgen; 18 | 19 | #[cfg(feature = "system")] 20 | fn main() { 21 | #[cfg(not(feature = "bindgen"))] 22 | panic!("Invalid configuration for libquickjs-sys: Must either enable the bundled or the bindgen feature"); 23 | 24 | #[cfg(feature = "patched")] 25 | panic!("Invalid configuration for libquickjs-sys: the patched feature is incompatible with the system feature"); 26 | 27 | let lib: std::borrow::Cow = if let Ok(lib) = env::var("QUICKJS_LIBRARY_PATH") { 28 | lib.into() 29 | } else if cfg!(unix) { 30 | if exists(format!("/usr/lib/quickjs/{}.a", LIB_NAME)) { 31 | "/usr/lib/quickjs".into() 32 | } else if exists("/usr/local/lib/quickjs") { 33 | "/usr/local/lib/quickjs".into() 34 | } else { 35 | panic!("quickjs library could not be found. Try setting the QUICKJS_LIBRARY_PATH env variable"); 36 | } 37 | } else { 38 | panic!("quickjs error: Windows is not supported yet"); 39 | }; 40 | 41 | // Instruct cargo to statically link quickjs. 42 | println!("cargo:rustc-link-search=native={}", lib); 43 | println!("cargo:rustc-link-lib=static={}", LIB_NAME); 44 | } 45 | 46 | #[cfg(feature = "bundled")] 47 | fn main() { 48 | let embed_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("embed"); 49 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 50 | 51 | let code_dir = out_path.join("quickjs"); 52 | if exists(&code_dir) { 53 | std::fs::remove_dir_all(&code_dir).unwrap(); 54 | } 55 | copy_dir::copy_dir(embed_path.join("quickjs"), &code_dir) 56 | .expect("Could not copy quickjs directory"); 57 | 58 | #[cfg(feature = "patched")] 59 | apply_patches(&code_dir); 60 | 61 | std::fs::copy( 62 | embed_path.join("static-functions.c"), 63 | code_dir.join("static-functions.c"), 64 | ) 65 | .expect("Could not copy static-functions.c"); 66 | 67 | eprintln!("Compiling quickjs..."); 68 | let quickjs_version = 69 | std::fs::read_to_string(code_dir.join("VERSION")).expect("failed to read quickjs version"); 70 | cc::Build::new() 71 | .files( 72 | [ 73 | "cutils.c", 74 | "libbf.c", 75 | "libregexp.c", 76 | "libunicode.c", 77 | "quickjs.c", 78 | // Custom wrappers. 79 | "static-functions.c", 80 | ] 81 | .iter() 82 | .map(|f| code_dir.join(f)), 83 | ) 84 | .define("_GNU_SOURCE", None) 85 | .define( 86 | "CONFIG_VERSION", 87 | format!("\"{}\"", quickjs_version.trim()).as_str(), 88 | ) 89 | .define("CONFIG_BIGNUM", None) 90 | // The below flags are used by the official Makefile. 91 | .flag_if_supported("-Wchar-subscripts") 92 | .flag_if_supported("-Wno-array-bounds") 93 | .flag_if_supported("-Wno-format-truncation") 94 | .flag_if_supported("-Wno-missing-field-initializers") 95 | .flag_if_supported("-Wno-sign-compare") 96 | .flag_if_supported("-Wno-unused-parameter") 97 | .flag_if_supported("-Wundef") 98 | .flag_if_supported("-Wuninitialized") 99 | .flag_if_supported("-Wunused") 100 | .flag_if_supported("-Wwrite-strings") 101 | .flag_if_supported("-funsigned-char") 102 | // Below flags are added to supress warnings that appear on some 103 | // platforms. 104 | .flag_if_supported("-Wno-cast-function-type") 105 | .flag_if_supported("-Wno-implicit-fallthrough") 106 | .flag_if_supported("-Wno-enum-conversion") 107 | // cc uses the OPT_LEVEL env var by default, but we hardcode it to -O2 108 | // since release builds use -O3 which might be problematic for quickjs, 109 | // and debug builds only happen once anyway so the optimization slowdown 110 | // is fine. 111 | .opt_level(2) 112 | .compile(LIB_NAME); 113 | 114 | std::fs::copy(embed_path.join("bindings.rs"), out_path.join("bindings.rs")) 115 | .expect("Could not copy bindings.rs"); 116 | } 117 | 118 | #[cfg(feature = "patched")] 119 | fn apply_patches(code_dir: &PathBuf) { 120 | use std::fs; 121 | 122 | eprintln!("Applying patches..."); 123 | let embed_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("embed"); 124 | let patches_path = embed_path.join("patches"); 125 | for patch in fs::read_dir(patches_path).expect("Could not open patches directory") { 126 | let patch = patch.expect("Could not open patch"); 127 | eprintln!("Applying {:?}...", patch.file_name()); 128 | let status = std::process::Command::new("patch") 129 | .current_dir(&code_dir) 130 | .arg("-i") 131 | .arg(patch.path()) 132 | .spawn() 133 | .expect("Could not apply patches") 134 | .wait() 135 | .expect("Could not apply patches"); 136 | assert!( 137 | status.success(), 138 | "Patch command returned non-zero exit code" 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/patches/js-tobigint64-overflow.patch: -------------------------------------------------------------------------------- 1 | diff --git libquickjs-sys/embed/quickjs/libbf.c libquickjs-sys/embed/quickjs/libbf.c 2 | index cbabf95..f0625f9 100644 3 | --- libquickjs-sys/embed/quickjs/libbf.c 4 | +++ libquickjs-sys/embed/quickjs/libbf.c 5 | @@ -2261,14 +2261,17 @@ int bf_get_int64(int64_t *pres, const bf_t *a, int flags) 6 | v = INT64_MAX; 7 | } 8 | } else { 9 | + ret = BF_ST_OVERFLOW; 10 | slimb_t bit_pos = a->len * LIMB_BITS - a->expn; 11 | v = get_bits(a->tab, a->len, bit_pos); 12 | #if LIMB_BITS == 32 13 | v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32; 14 | #endif 15 | - if (a->sign) 16 | + if (a->sign) { 17 | + if (a->expn == 64 && v == (uint64_t)INT64_MAX + 1) 18 | + ret = 0; // not overflow, but INT64_MIN 19 | v = -v; 20 | - ret = 0; 21 | + } 22 | } 23 | *pres = v; 24 | return ret; 25 | diff --git libquickjs-sys/embed/quickjs/quickjs.c libquickjs-sys/embed/quickjs/quickjs.c 26 | index 7bb20cb..ad5811c 100644 27 | --- libquickjs-sys/embed/quickjs/quickjs.c 28 | +++ libquickjs-sys/embed/quickjs/quickjs.c 29 | @@ -11244,15 +11244,16 @@ static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) 30 | static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) 31 | { 32 | bf_t a_s, *a; 33 | + int ret; 34 | 35 | a = JS_ToBigIntFree(ctx, &a_s, val); 36 | if (!a) { 37 | *pres = 0; 38 | return -1; 39 | } 40 | - bf_get_int64(pres, a, BF_GET_INT_MOD); 41 | + ret = bf_get_int64(pres, a, BF_GET_INT_MOD); 42 | JS_FreeBigInt(ctx, a, &a_s); 43 | - return 0; 44 | + return ret; 45 | } 46 | 47 | int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) 48 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/Changelog: -------------------------------------------------------------------------------- 1 | 2021-03-27: 2 | 3 | - faster Array.prototype.push and Array.prototype.unshift 4 | - added JS_UpdateStackTop() 5 | - fixed Windows console 6 | - misc bug fixes 7 | 8 | 2020-11-08: 9 | 10 | - improved function parameter initializers 11 | - added std.setenv(), std.unsetenv() and std.getenviron() 12 | - added JS_EvalThis() 13 | - misc bug fixes 14 | 15 | 2020-09-06: 16 | 17 | - added logical assignment operators 18 | - added IsHTMLDDA support 19 | - faster for-of loops 20 | - os.Worker now takes a module filename as parameter 21 | - qjsc: added -D option to compile dynamically loaded modules or workers 22 | - misc bug fixes 23 | 24 | 2020-07-05: 25 | 26 | - modified JS_GetPrototype() to return a live value 27 | - REPL: support unicode characters larger than 16 bits 28 | - added os.Worker 29 | - improved object serialization 30 | - added std.parseExtJSON 31 | - misc bug fixes 32 | 33 | 2020-04-12: 34 | 35 | - added cross realm support 36 | - added AggregateError and Promise.any 37 | - added env, uid and gid options in os.exec() 38 | - misc bug fixes 39 | 40 | 2020-03-16: 41 | 42 | - reworked error handling in std and os libraries: suppressed I/O 43 | exceptions in std FILE functions and return a positive errno value 44 | when it is explicit 45 | - output exception messages to stderr 46 | - added std.loadFile(), std.strerror(), std.FILE.prototype.tello() 47 | - added JS_GetRuntimeOpaque(), JS_SetRuntimeOpaque(), JS_NewUint32() 48 | - updated to Unicode 13.0.0 49 | - misc bug fixes 50 | 51 | 2020-01-19: 52 | 53 | - keep CONFIG_BIGNUM in the makefile 54 | - added os.chdir() 55 | - qjs: added -I option 56 | - more memory checks in the bignum operations 57 | - modified operator overloading semantics to be closer to the TC39 58 | proposal 59 | - suppressed "use bigint" mode. Simplified "use math" mode 60 | - BigDecimal: changed suffix from 'd' to 'm' 61 | - misc bug fixes 62 | 63 | 2020-01-05: 64 | 65 | - always compile the bignum code. Added '--bignum' option to qjs. 66 | - added BigDecimal 67 | - added String.prototype.replaceAll 68 | - misc bug fixes 69 | 70 | 2019-12-21: 71 | 72 | - added nullish coalescing operator (ES2020) 73 | - added optional chaining (ES2020) 74 | - removed recursions in garbage collector 75 | - test stack overflow in the parser 76 | - improved backtrace logic 77 | - added JS_SetHostPromiseRejectionTracker() 78 | - allow exotic constructors 79 | - improved c++ compatibility 80 | - misc bug fixes 81 | 82 | 2019-10-27: 83 | 84 | - added example of C class in a module (examples/test_point.js) 85 | - added JS_GetTypedArrayBuffer() 86 | - misc bug fixes 87 | 88 | 2019-09-18: 89 | 90 | - added os.exec and other system calls 91 | - exported JS_ValueToAtom() 92 | - qjsc: added 'qjsc_' prefix to the generated C identifiers 93 | - added cross-compilation support 94 | - misc bug fixes 95 | 96 | 2019-09-01: 97 | 98 | - added globalThis 99 | - documented JS_EVAL_FLAG_COMPILE_ONLY 100 | - added import.meta.url and import.meta.main 101 | - added 'debugger' statement 102 | - misc bug fixes 103 | 104 | 2019-08-18: 105 | 106 | - added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat, 107 | os.readlink, os.readdir, os.utimes, std.popen 108 | - module autodetection 109 | - added import.meta 110 | - misc bug fixes 111 | 112 | 2019-08-10: 113 | 114 | - added public class fields and private class fields, methods and 115 | accessors (TC39 proposal) 116 | - changed JS_ToCStringLen() prototype 117 | - qjsc: handle '-' in module names and modules with the same filename 118 | - added std.urlGet 119 | - exported JS_GetOwnPropertyNames() and JS_GetOwnProperty() 120 | - exported some bigint C functions 121 | - added support for eshost in run-test262 122 | - misc bug fixes 123 | 124 | 2019-07-28: 125 | 126 | - added dynamic import 127 | - added Promise.allSettled 128 | - added String.prototype.matchAll 129 | - added Object.fromEntries 130 | - reduced number of ticks in await 131 | - added BigInt support in Atomics 132 | - exported JS_NewPromiseCapability() 133 | - misc async function and async generator fixes 134 | - enabled hashbang support by default 135 | 136 | 2019-07-21: 137 | 138 | - updated test262 tests 139 | - updated to Unicode version 12.1.0 140 | - fixed missing Date object in qjsc 141 | - fixed multi-context creation 142 | - misc ES2020 related fixes 143 | - simplified power and division operators in bignum extension 144 | - fixed several crash conditions 145 | 146 | 2019-07-09: 147 | 148 | - first public release 149 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/LICENSE: -------------------------------------------------------------------------------- 1 | QuickJS Javascript Engine 2 | 3 | Copyright (c) 2017-2021 Fabrice Bellard 4 | Copyright (c) 2017-2021 Charlie Gordon 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # QuickJS Javascript Engine 3 | # 4 | # Copyright (c) 2017-2021 Fabrice Bellard 5 | # Copyright (c) 2017-2021 Charlie Gordon 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | ifeq ($(shell uname -s),Darwin) 26 | CONFIG_DARWIN=y 27 | endif 28 | # Windows cross compilation from Linux 29 | #CONFIG_WIN32=y 30 | # use link time optimization (smaller and faster executables but slower build) 31 | CONFIG_LTO=y 32 | # consider warnings as errors (for development) 33 | #CONFIG_WERROR=y 34 | # force 32 bit build for some utilities 35 | #CONFIG_M32=y 36 | 37 | ifdef CONFIG_DARWIN 38 | # use clang instead of gcc 39 | CONFIG_CLANG=y 40 | CONFIG_DEFAULT_AR=y 41 | endif 42 | 43 | # installation directory 44 | prefix=/usr/local 45 | 46 | # use the gprof profiler 47 | #CONFIG_PROFILE=y 48 | # use address sanitizer 49 | #CONFIG_ASAN=y 50 | # include the code for BigInt/BigFloat/BigDecimal and math mode 51 | CONFIG_BIGNUM=y 52 | 53 | OBJDIR=.obj 54 | 55 | ifdef CONFIG_WIN32 56 | ifdef CONFIG_M32 57 | CROSS_PREFIX=i686-w64-mingw32- 58 | else 59 | CROSS_PREFIX=x86_64-w64-mingw32- 60 | endif 61 | EXE=.exe 62 | else 63 | CROSS_PREFIX= 64 | EXE= 65 | endif 66 | ifdef CONFIG_CLANG 67 | HOST_CC=clang 68 | CC=$(CROSS_PREFIX)clang 69 | CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d 70 | CFLAGS += -Wextra 71 | CFLAGS += -Wno-sign-compare 72 | CFLAGS += -Wno-missing-field-initializers 73 | CFLAGS += -Wundef -Wuninitialized 74 | CFLAGS += -Wunused -Wno-unused-parameter 75 | CFLAGS += -Wwrite-strings 76 | CFLAGS += -Wchar-subscripts -funsigned-char 77 | CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d 78 | ifdef CONFIG_DEFAULT_AR 79 | AR=$(CROSS_PREFIX)ar 80 | else 81 | ifdef CONFIG_LTO 82 | AR=$(CROSS_PREFIX)llvm-ar 83 | else 84 | AR=$(CROSS_PREFIX)ar 85 | endif 86 | endif 87 | else 88 | HOST_CC=gcc 89 | CC=$(CROSS_PREFIX)gcc 90 | CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d 91 | CFLAGS += -Wno-array-bounds -Wno-format-truncation 92 | ifdef CONFIG_LTO 93 | AR=$(CROSS_PREFIX)gcc-ar 94 | else 95 | AR=$(CROSS_PREFIX)ar 96 | endif 97 | endif 98 | STRIP=$(CROSS_PREFIX)strip 99 | ifdef CONFIG_WERROR 100 | CFLAGS+=-Werror 101 | endif 102 | DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" 103 | ifdef CONFIG_BIGNUM 104 | DEFINES+=-DCONFIG_BIGNUM 105 | endif 106 | ifdef CONFIG_WIN32 107 | DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior 108 | endif 109 | 110 | CFLAGS+=$(DEFINES) 111 | CFLAGS_DEBUG=$(CFLAGS) -O0 112 | CFLAGS_SMALL=$(CFLAGS) -Os 113 | CFLAGS_OPT=$(CFLAGS) -O2 114 | CFLAGS_NOLTO:=$(CFLAGS_OPT) 115 | LDFLAGS=-g 116 | ifdef CONFIG_LTO 117 | CFLAGS_SMALL+=-flto 118 | CFLAGS_OPT+=-flto 119 | LDFLAGS+=-flto 120 | endif 121 | ifdef CONFIG_PROFILE 122 | CFLAGS+=-p 123 | LDFLAGS+=-p 124 | endif 125 | ifdef CONFIG_ASAN 126 | CFLAGS+=-fsanitize=address -fno-omit-frame-pointer 127 | LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer 128 | endif 129 | ifdef CONFIG_WIN32 130 | LDEXPORT= 131 | else 132 | LDEXPORT=-rdynamic 133 | endif 134 | 135 | PROGS=qjs$(EXE) qjsc$(EXE) run-test262 136 | ifneq ($(CROSS_PREFIX),) 137 | QJSC_CC=gcc 138 | QJSC=./host-qjsc 139 | PROGS+=$(QJSC) 140 | else 141 | QJSC_CC=$(CC) 142 | QJSC=./qjsc$(EXE) 143 | endif 144 | ifndef CONFIG_WIN32 145 | PROGS+=qjscalc 146 | endif 147 | ifdef CONFIG_M32 148 | PROGS+=qjs32 qjs32_s 149 | endif 150 | PROGS+=libquickjs.a 151 | ifdef CONFIG_LTO 152 | PROGS+=libquickjs.lto.a 153 | endif 154 | 155 | # examples 156 | ifeq ($(CROSS_PREFIX),) 157 | ifdef CONFIG_ASAN 158 | PROGS+= 159 | else 160 | PROGS+=examples/hello examples/hello_module examples/test_fib 161 | ifndef CONFIG_DARWIN 162 | PROGS+=examples/fib.so examples/point.so 163 | endif 164 | endif 165 | endif 166 | 167 | all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) 168 | 169 | QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o 170 | 171 | QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) 172 | ifdef CONFIG_BIGNUM 173 | QJS_LIB_OBJS+=$(OBJDIR)/libbf.o 174 | QJS_OBJS+=$(OBJDIR)/qjscalc.o 175 | endif 176 | 177 | HOST_LIBS=-lm -ldl -lpthread 178 | LIBS=-lm 179 | ifndef CONFIG_WIN32 180 | LIBS+=-ldl -lpthread 181 | endif 182 | LIBS+=$(EXTRA_LIBS) 183 | 184 | $(OBJDIR): 185 | mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests 186 | 187 | qjs$(EXE): $(QJS_OBJS) 188 | $(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) 189 | 190 | qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS)) 191 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 192 | 193 | qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) 194 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 195 | 196 | ifneq ($(CROSS_PREFIX),) 197 | 198 | $(QJSC): $(OBJDIR)/qjsc.host.o \ 199 | $(patsubst %.o, %.host.o, $(QJS_LIB_OBJS)) 200 | $(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS) 201 | 202 | endif #CROSS_PREFIX 203 | 204 | QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" 205 | ifdef CONFIG_LTO 206 | QJSC_DEFINES+=-DCONFIG_LTO 207 | endif 208 | QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" 209 | 210 | $(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES) 211 | $(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES) 212 | 213 | qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS)) 214 | $(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) 215 | 216 | qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS)) 217 | $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) 218 | @size $@ 219 | 220 | qjscalc: qjs 221 | ln -sf $< $@ 222 | 223 | ifdef CONFIG_LTO 224 | LTOEXT=.lto 225 | else 226 | LTOEXT= 227 | endif 228 | 229 | libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS) 230 | $(AR) rcs $@ $^ 231 | 232 | ifdef CONFIG_LTO 233 | libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) 234 | $(AR) rcs $@ $^ 235 | endif # CONFIG_LTO 236 | 237 | repl.c: $(QJSC) repl.js 238 | $(QJSC) -c -o $@ -m repl.js 239 | 240 | qjscalc.c: $(QJSC) qjscalc.js 241 | $(QJSC) -fbignum -c -o $@ qjscalc.js 242 | 243 | ifneq ($(wildcard unicode/UnicodeData.txt),) 244 | $(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \ 245 | $(OBJDIR)/libunicode.nolto.o: libunicode-table.h 246 | 247 | libunicode-table.h: unicode_gen 248 | ./unicode_gen unicode $@ 249 | endif 250 | 251 | run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS) 252 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 253 | 254 | run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) 255 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 256 | 257 | run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) 258 | $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) 259 | 260 | # object suffix order: nolto, [m32|m32s] 261 | 262 | $(OBJDIR)/%.o: %.c | $(OBJDIR) 263 | $(CC) $(CFLAGS_OPT) -c -o $@ $< 264 | 265 | $(OBJDIR)/%.host.o: %.c | $(OBJDIR) 266 | $(HOST_CC) $(CFLAGS_OPT) -c -o $@ $< 267 | 268 | $(OBJDIR)/%.pic.o: %.c | $(OBJDIR) 269 | $(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $< 270 | 271 | $(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) 272 | $(CC) $(CFLAGS_NOLTO) -c -o $@ $< 273 | 274 | $(OBJDIR)/%.m32.o: %.c | $(OBJDIR) 275 | $(CC) -m32 $(CFLAGS_OPT) -c -o $@ $< 276 | 277 | $(OBJDIR)/%.m32s.o: %.c | $(OBJDIR) 278 | $(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $< 279 | 280 | $(OBJDIR)/%.debug.o: %.c | $(OBJDIR) 281 | $(CC) $(CFLAGS_DEBUG) -c -o $@ $< 282 | 283 | $(OBJDIR)/%.check.o: %.c | $(OBJDIR) 284 | $(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $< 285 | 286 | regexp_test: libregexp.c libunicode.c cutils.c 287 | $(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS) 288 | 289 | unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h 290 | $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o 291 | 292 | clean: 293 | rm -f repl.c qjscalc.c out.c 294 | rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS) 295 | rm -f hello.c test_fib.c 296 | rm -f examples/*.so tests/*.so 297 | rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug 298 | rm -rf run-test262-debug run-test262-32 299 | 300 | install: all 301 | mkdir -p "$(DESTDIR)$(prefix)/bin" 302 | $(STRIP) qjs qjsc 303 | install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin" 304 | ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc" 305 | mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs" 306 | install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs" 307 | ifdef CONFIG_LTO 308 | install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs" 309 | endif 310 | mkdir -p "$(DESTDIR)$(prefix)/include/quickjs" 311 | install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(prefix)/include/quickjs" 312 | 313 | ############################################################################### 314 | # examples 315 | 316 | # example of static JS compilation 317 | HELLO_SRCS=examples/hello.js 318 | HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ 319 | -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ 320 | -fno-date -fno-module-loader 321 | ifdef CONFIG_BIGNUM 322 | HELLO_OPTS+=-fno-bigint 323 | endif 324 | 325 | hello.c: $(QJSC) $(HELLO_SRCS) 326 | $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) 327 | 328 | ifdef CONFIG_M32 329 | examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS)) 330 | $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) 331 | else 332 | examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS) 333 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 334 | endif 335 | 336 | # example of static JS compilation with modules 337 | HELLO_MODULE_SRCS=examples/hello_module.js 338 | HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ 339 | -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ 340 | -fno-date -m 341 | examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) 342 | $(QJSC) $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS) 343 | 344 | # use of an external C module (static compilation) 345 | 346 | test_fib.c: $(QJSC) examples/test_fib.js 347 | $(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js 348 | 349 | examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a 350 | $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) 351 | 352 | examples/fib.so: $(OBJDIR)/examples/fib.pic.o 353 | $(CC) $(LDFLAGS) -shared -o $@ $^ 354 | 355 | examples/point.so: $(OBJDIR)/examples/point.pic.o 356 | $(CC) $(LDFLAGS) -shared -o $@ $^ 357 | 358 | ############################################################################### 359 | # documentation 360 | 361 | DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html 362 | 363 | build_doc: $(DOCS) 364 | 365 | clean_doc: 366 | rm -f $(DOCS) 367 | 368 | doc/%.pdf: doc/%.texi 369 | texi2pdf --clean -o $@ -q $< 370 | 371 | doc/%.html.pre: doc/%.texi 372 | makeinfo --html --no-headers --no-split --number-sections -o $@ $< 373 | 374 | doc/%.html: doc/%.html.pre 375 | sed -e 's||\n|' < $< > $@ 376 | 377 | ############################################################################### 378 | # tests 379 | 380 | ifndef CONFIG_DARWIN 381 | test: tests/bjson.so examples/point.so 382 | endif 383 | ifdef CONFIG_M32 384 | test: qjs32 385 | endif 386 | 387 | test: qjs 388 | ./qjs tests/test_closure.js 389 | ./qjs tests/test_language.js 390 | ./qjs tests/test_builtin.js 391 | ./qjs tests/test_loop.js 392 | ./qjs tests/test_std.js 393 | ./qjs tests/test_worker.js 394 | ifndef CONFIG_DARWIN 395 | ifdef CONFIG_BIGNUM 396 | ./qjs --bignum tests/test_bjson.js 397 | else 398 | ./qjs tests/test_bjson.js 399 | endif 400 | ./qjs examples/test_point.js 401 | endif 402 | ifdef CONFIG_BIGNUM 403 | ./qjs --bignum tests/test_op_overloading.js 404 | ./qjs --bignum tests/test_bignum.js 405 | ./qjs --qjscalc tests/test_qjscalc.js 406 | endif 407 | ifdef CONFIG_M32 408 | ./qjs32 tests/test_closure.js 409 | ./qjs32 tests/test_language.js 410 | ./qjs32 tests/test_builtin.js 411 | ./qjs32 tests/test_loop.js 412 | ./qjs32 tests/test_std.js 413 | ./qjs32 tests/test_worker.js 414 | ifdef CONFIG_BIGNUM 415 | ./qjs32 --bignum tests/test_op_overloading.js 416 | ./qjs32 --bignum tests/test_bignum.js 417 | ./qjs32 --qjscalc tests/test_qjscalc.js 418 | endif 419 | endif 420 | 421 | stats: qjs qjs32 422 | ./qjs -qd 423 | ./qjs32 -qd 424 | 425 | microbench: qjs 426 | ./qjs tests/microbench.js 427 | 428 | microbench-32: qjs32 429 | ./qjs32 tests/microbench.js 430 | 431 | # ES5 tests (obsolete) 432 | test2o: run-test262 433 | time ./run-test262 -m -c test262o.conf 434 | 435 | test2o-32: run-test262-32 436 | time ./run-test262-32 -m -c test262o.conf 437 | 438 | test2o-update: run-test262 439 | ./run-test262 -u -c test262o.conf 440 | 441 | # Test262 tests 442 | test2-default: run-test262 443 | time ./run-test262 -m -c test262.conf 444 | 445 | test2: run-test262 446 | time ./run-test262 -m -c test262.conf -a 447 | 448 | test2-32: run-test262-32 449 | time ./run-test262-32 -m -c test262.conf -a 450 | 451 | test2-update: run-test262 452 | ./run-test262 -u -c test262.conf -a 453 | 454 | test2-check: run-test262 455 | time ./run-test262 -m -c test262.conf -E -a 456 | 457 | testall: all test microbench test2o test2 458 | 459 | testall-32: all test-32 microbench-32 test2o-32 test2-32 460 | 461 | testall-complete: testall testall-32 462 | 463 | bench-v8: qjs 464 | make -C tests/bench-v8 465 | ./qjs -d tests/bench-v8/combined.js 466 | 467 | tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o 468 | $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) 469 | 470 | -include $(wildcard $(OBJDIR)/*.d) 471 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/TODO: -------------------------------------------------------------------------------- 1 | Bugs: 2 | - modules: better error handling with cyclic module references 3 | 4 | Misc ideas: 5 | - use custom printf to avoid compatibility issues with floating point numbers 6 | - consistent naming for preprocessor defines 7 | - unify coding style and naming conventions 8 | - use names from the ECMA spec in library implementation 9 | - use byte code emitters with typed arguments (for clarity) 10 | - use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing 11 | and use the same wrappers in all phases 12 | - use more generic method for line numbers in resolve_variables and resolve_labels 13 | - use custom timezone support to avoid C library compatibility issues 14 | 15 | Memory: 16 | - use memory pools for objects, etc? 17 | - test border cases for max number of atoms, object properties, string length 18 | - add emergency malloc mode for out of memory exceptions. 19 | - test all DynBuf memory errors 20 | - test all js_realloc memory errors 21 | - improve JS_ComputeMemoryUsage() with more info 22 | 23 | Built-in standard library: 24 | - BSD sockets 25 | - modules: use realpath in module name normalizer and put it in quickjs-libc 26 | - modules: if no ".", use a well known module loading path ? 27 | - get rid of __loadScript, use more common name 28 | 29 | REPL: 30 | - debugger 31 | - readline: support MS Windows terminal 32 | - readline: handle dynamic terminal resizing 33 | - readline: handle double width unicode characters 34 | - multiline editing 35 | - runtime object and function inspectors 36 | - interactive object browser 37 | - use more generic approach to display evaluation results 38 | - improve directive handling: dispatch, colorize, completion... 39 | - save history 40 | - close all predefined methods in repl.js and jscalc.js 41 | 42 | Optimization ideas: 43 | - 64-bit atoms in 64-bit mode ? 44 | - 64-bit small bigint in 64-bit mode ? 45 | - reuse stack slots for disjoint scopes, if strip 46 | - add heuristic to avoid some cycles in closures 47 | - small String (0-2 charcodes) with immediate storage 48 | - perform static string concatenation at compile time 49 | - optimize string concatenation with ropes or miniropes? 50 | - add implicit numeric strings for Uint32 numbers? 51 | - optimize `s += a + b`, `s += a.b` and similar simple expressions 52 | - ensure string canonical representation and optimise comparisons and hashes? 53 | - remove JSObject.first_weak_ref, use bit+context based hashed array for weak references 54 | - property access optimization on the global object, functions, 55 | prototypes and special non extensible objects. 56 | - create object literals with the correct length by backpatching length argument 57 | - remove redundant set_loc_uninitialized/check_uninitialized opcodes 58 | - peephole optim: push_atom_value, to_propkey -> push_atom_value 59 | - peephole optim: put_loc x, get_loc_check x -> set_loc x 60 | - convert slow array to fast array when all properties != length are numeric 61 | - optimize destructuring assignments for global and local variables 62 | - implement some form of tail-call-optimization 63 | - optimize OP_apply 64 | - optimize f(...b) 65 | 66 | Test262o: 0/11262 errors, 463 excluded 67 | Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) 68 | 69 | Result: 35/75280 errors, 909 excluded, 585 skipped 70 | Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9 71 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/VERSION: -------------------------------------------------------------------------------- 1 | 2021-03-27 2 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/cutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C utilities 3 | * 4 | * Copyright (c) 2017 Fabrice Bellard 5 | * Copyright (c) 2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #ifndef CUTILS_H 26 | #define CUTILS_H 27 | 28 | #include 29 | #include 30 | 31 | /* set if CPU is big endian */ 32 | #undef WORDS_BIGENDIAN 33 | 34 | #define likely(x) __builtin_expect(!!(x), 1) 35 | #define unlikely(x) __builtin_expect(!!(x), 0) 36 | #define force_inline inline __attribute__((always_inline)) 37 | #define no_inline __attribute__((noinline)) 38 | #define __maybe_unused __attribute__((unused)) 39 | 40 | #define xglue(x, y) x ## y 41 | #define glue(x, y) xglue(x, y) 42 | #define stringify(s) tostring(s) 43 | #define tostring(s) #s 44 | 45 | #ifndef offsetof 46 | #define offsetof(type, field) ((size_t) &((type *)0)->field) 47 | #endif 48 | #ifndef countof 49 | #define countof(x) (sizeof(x) / sizeof((x)[0])) 50 | #endif 51 | 52 | typedef int BOOL; 53 | 54 | #ifndef FALSE 55 | enum { 56 | FALSE = 0, 57 | TRUE = 1, 58 | }; 59 | #endif 60 | 61 | void pstrcpy(char *buf, int buf_size, const char *str); 62 | char *pstrcat(char *buf, int buf_size, const char *s); 63 | int strstart(const char *str, const char *val, const char **ptr); 64 | int has_suffix(const char *str, const char *suffix); 65 | 66 | static inline int max_int(int a, int b) 67 | { 68 | if (a > b) 69 | return a; 70 | else 71 | return b; 72 | } 73 | 74 | static inline int min_int(int a, int b) 75 | { 76 | if (a < b) 77 | return a; 78 | else 79 | return b; 80 | } 81 | 82 | static inline uint32_t max_uint32(uint32_t a, uint32_t b) 83 | { 84 | if (a > b) 85 | return a; 86 | else 87 | return b; 88 | } 89 | 90 | static inline uint32_t min_uint32(uint32_t a, uint32_t b) 91 | { 92 | if (a < b) 93 | return a; 94 | else 95 | return b; 96 | } 97 | 98 | static inline int64_t max_int64(int64_t a, int64_t b) 99 | { 100 | if (a > b) 101 | return a; 102 | else 103 | return b; 104 | } 105 | 106 | static inline int64_t min_int64(int64_t a, int64_t b) 107 | { 108 | if (a < b) 109 | return a; 110 | else 111 | return b; 112 | } 113 | 114 | /* WARNING: undefined if a = 0 */ 115 | static inline int clz32(unsigned int a) 116 | { 117 | return __builtin_clz(a); 118 | } 119 | 120 | /* WARNING: undefined if a = 0 */ 121 | static inline int clz64(uint64_t a) 122 | { 123 | return __builtin_clzll(a); 124 | } 125 | 126 | /* WARNING: undefined if a = 0 */ 127 | static inline int ctz32(unsigned int a) 128 | { 129 | return __builtin_ctz(a); 130 | } 131 | 132 | /* WARNING: undefined if a = 0 */ 133 | static inline int ctz64(uint64_t a) 134 | { 135 | return __builtin_ctzll(a); 136 | } 137 | 138 | struct __attribute__((packed)) packed_u64 { 139 | uint64_t v; 140 | }; 141 | 142 | struct __attribute__((packed)) packed_u32 { 143 | uint32_t v; 144 | }; 145 | 146 | struct __attribute__((packed)) packed_u16 { 147 | uint16_t v; 148 | }; 149 | 150 | static inline uint64_t get_u64(const uint8_t *tab) 151 | { 152 | return ((const struct packed_u64 *)tab)->v; 153 | } 154 | 155 | static inline int64_t get_i64(const uint8_t *tab) 156 | { 157 | return (int64_t)((const struct packed_u64 *)tab)->v; 158 | } 159 | 160 | static inline void put_u64(uint8_t *tab, uint64_t val) 161 | { 162 | ((struct packed_u64 *)tab)->v = val; 163 | } 164 | 165 | static inline uint32_t get_u32(const uint8_t *tab) 166 | { 167 | return ((const struct packed_u32 *)tab)->v; 168 | } 169 | 170 | static inline int32_t get_i32(const uint8_t *tab) 171 | { 172 | return (int32_t)((const struct packed_u32 *)tab)->v; 173 | } 174 | 175 | static inline void put_u32(uint8_t *tab, uint32_t val) 176 | { 177 | ((struct packed_u32 *)tab)->v = val; 178 | } 179 | 180 | static inline uint32_t get_u16(const uint8_t *tab) 181 | { 182 | return ((const struct packed_u16 *)tab)->v; 183 | } 184 | 185 | static inline int32_t get_i16(const uint8_t *tab) 186 | { 187 | return (int16_t)((const struct packed_u16 *)tab)->v; 188 | } 189 | 190 | static inline void put_u16(uint8_t *tab, uint16_t val) 191 | { 192 | ((struct packed_u16 *)tab)->v = val; 193 | } 194 | 195 | static inline uint32_t get_u8(const uint8_t *tab) 196 | { 197 | return *tab; 198 | } 199 | 200 | static inline int32_t get_i8(const uint8_t *tab) 201 | { 202 | return (int8_t)*tab; 203 | } 204 | 205 | static inline void put_u8(uint8_t *tab, uint8_t val) 206 | { 207 | *tab = val; 208 | } 209 | 210 | static inline uint16_t bswap16(uint16_t x) 211 | { 212 | return (x >> 8) | (x << 8); 213 | } 214 | 215 | static inline uint32_t bswap32(uint32_t v) 216 | { 217 | return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | 218 | ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); 219 | } 220 | 221 | static inline uint64_t bswap64(uint64_t v) 222 | { 223 | return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | 224 | ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | 225 | ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | 226 | ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | 227 | ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | 228 | ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | 229 | ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | 230 | ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); 231 | } 232 | 233 | /* XXX: should take an extra argument to pass slack information to the caller */ 234 | typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); 235 | 236 | typedef struct DynBuf { 237 | uint8_t *buf; 238 | size_t size; 239 | size_t allocated_size; 240 | BOOL error; /* true if a memory allocation error occurred */ 241 | DynBufReallocFunc *realloc_func; 242 | void *opaque; /* for realloc_func */ 243 | } DynBuf; 244 | 245 | void dbuf_init(DynBuf *s); 246 | void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); 247 | int dbuf_realloc(DynBuf *s, size_t new_size); 248 | int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); 249 | int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); 250 | int dbuf_put_self(DynBuf *s, size_t offset, size_t len); 251 | int dbuf_putc(DynBuf *s, uint8_t c); 252 | int dbuf_putstr(DynBuf *s, const char *str); 253 | static inline int dbuf_put_u16(DynBuf *s, uint16_t val) 254 | { 255 | return dbuf_put(s, (uint8_t *)&val, 2); 256 | } 257 | static inline int dbuf_put_u32(DynBuf *s, uint32_t val) 258 | { 259 | return dbuf_put(s, (uint8_t *)&val, 4); 260 | } 261 | static inline int dbuf_put_u64(DynBuf *s, uint64_t val) 262 | { 263 | return dbuf_put(s, (uint8_t *)&val, 8); 264 | } 265 | int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, 266 | const char *fmt, ...); 267 | void dbuf_free(DynBuf *s); 268 | static inline BOOL dbuf_error(DynBuf *s) { 269 | return s->error; 270 | } 271 | static inline void dbuf_set_error(DynBuf *s) 272 | { 273 | s->error = TRUE; 274 | } 275 | 276 | #define UTF8_CHAR_LEN_MAX 6 277 | 278 | int unicode_to_utf8(uint8_t *buf, unsigned int c); 279 | int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); 280 | 281 | static inline int from_hex(int c) 282 | { 283 | if (c >= '0' && c <= '9') 284 | return c - '0'; 285 | else if (c >= 'A' && c <= 'F') 286 | return c - 'A' + 10; 287 | else if (c >= 'a' && c <= 'f') 288 | return c - 'a' + 10; 289 | else 290 | return -1; 291 | } 292 | 293 | void rqsort(void *base, size_t nmemb, size_t size, 294 | int (*cmp)(const void *, const void *, void *), 295 | void *arg); 296 | 297 | #endif /* CUTILS_H */ 298 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/libregexp-opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifdef DEF 26 | 27 | DEF(invalid, 1) /* never used */ 28 | DEF(char, 3) 29 | DEF(char32, 5) 30 | DEF(dot, 1) 31 | DEF(any, 1) /* same as dot but match any character including line terminator */ 32 | DEF(line_start, 1) 33 | DEF(line_end, 1) 34 | DEF(goto, 5) 35 | DEF(split_goto_first, 5) 36 | DEF(split_next_first, 5) 37 | DEF(match, 1) 38 | DEF(save_start, 2) /* save start position */ 39 | DEF(save_end, 2) /* save end position, must come after saved_start */ 40 | DEF(save_reset, 3) /* reset save positions */ 41 | DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ 42 | DEF(push_i32, 5) /* push integer on the stack */ 43 | DEF(drop, 1) 44 | DEF(word_boundary, 1) 45 | DEF(not_word_boundary, 1) 46 | DEF(back_reference, 2) 47 | DEF(backward_back_reference, 2) /* must come after back_reference */ 48 | DEF(range, 3) /* variable length */ 49 | DEF(range32, 3) /* variable length */ 50 | DEF(lookahead, 5) 51 | DEF(negative_lookahead, 5) 52 | DEF(push_char_pos, 1) /* push the character position on the stack */ 53 | DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character 54 | position */ 55 | DEF(prev, 1) /* go to the previous char */ 56 | DEF(simple_greedy_quant, 17) 57 | 58 | #endif /* DEF */ 59 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/libregexp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBREGEXP_H 25 | #define LIBREGEXP_H 26 | 27 | #include 28 | 29 | #include "libunicode.h" 30 | 31 | #define LRE_BOOL int /* for documentation purposes */ 32 | 33 | #define LRE_FLAG_GLOBAL (1 << 0) 34 | #define LRE_FLAG_IGNORECASE (1 << 1) 35 | #define LRE_FLAG_MULTILINE (1 << 2) 36 | #define LRE_FLAG_DOTALL (1 << 3) 37 | #define LRE_FLAG_UTF16 (1 << 4) 38 | #define LRE_FLAG_STICKY (1 << 5) 39 | 40 | #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ 41 | 42 | uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, 43 | const char *buf, size_t buf_len, int re_flags, 44 | void *opaque); 45 | int lre_get_capture_count(const uint8_t *bc_buf); 46 | int lre_get_flags(const uint8_t *bc_buf); 47 | const char *lre_get_groupnames(const uint8_t *bc_buf); 48 | int lre_exec(uint8_t **capture, 49 | const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, 50 | int cbuf_type, void *opaque); 51 | 52 | int lre_parse_escape(const uint8_t **pp, int allow_utf16); 53 | LRE_BOOL lre_is_space(int c); 54 | 55 | /* must be provided by the user */ 56 | LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); 57 | void *lre_realloc(void *opaque, void *ptr, size_t size); 58 | 59 | /* JS identifier test */ 60 | extern uint32_t const lre_id_start_table_ascii[4]; 61 | extern uint32_t const lre_id_continue_table_ascii[4]; 62 | 63 | static inline int lre_js_is_ident_first(int c) 64 | { 65 | if ((uint32_t)c < 128) { 66 | return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; 67 | } else { 68 | #ifdef CONFIG_ALL_UNICODE 69 | return lre_is_id_start(c); 70 | #else 71 | return !lre_is_space(c); 72 | #endif 73 | } 74 | } 75 | 76 | static inline int lre_js_is_ident_next(int c) 77 | { 78 | if ((uint32_t)c < 128) { 79 | return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; 80 | } else { 81 | /* ZWNJ and ZWJ are accepted in identifiers */ 82 | #ifdef CONFIG_ALL_UNICODE 83 | return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; 84 | #else 85 | return !lre_is_space(c) || c == 0x200C || c == 0x200D; 86 | #endif 87 | } 88 | } 89 | 90 | #undef LRE_BOOL 91 | 92 | #endif /* LIBREGEXP_H */ 93 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/libunicode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Unicode utilities 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBUNICODE_H 25 | #define LIBUNICODE_H 26 | 27 | #include 28 | 29 | #define LRE_BOOL int /* for documentation purposes */ 30 | 31 | /* define it to include all the unicode tables (40KB larger) */ 32 | #define CONFIG_ALL_UNICODE 33 | 34 | #define LRE_CC_RES_LEN_MAX 3 35 | 36 | typedef enum { 37 | UNICODE_NFC, 38 | UNICODE_NFD, 39 | UNICODE_NFKC, 40 | UNICODE_NFKD, 41 | } UnicodeNormalizationEnum; 42 | 43 | int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); 44 | LRE_BOOL lre_is_cased(uint32_t c); 45 | LRE_BOOL lre_is_case_ignorable(uint32_t c); 46 | 47 | /* char ranges */ 48 | 49 | typedef struct { 50 | int len; /* in points, always even */ 51 | int size; 52 | uint32_t *points; /* points sorted by increasing value */ 53 | void *mem_opaque; 54 | void *(*realloc_func)(void *opaque, void *ptr, size_t size); 55 | } CharRange; 56 | 57 | typedef enum { 58 | CR_OP_UNION, 59 | CR_OP_INTER, 60 | CR_OP_XOR, 61 | } CharRangeOpEnum; 62 | 63 | void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); 64 | void cr_free(CharRange *cr); 65 | int cr_realloc(CharRange *cr, int size); 66 | int cr_copy(CharRange *cr, const CharRange *cr1); 67 | 68 | static inline int cr_add_point(CharRange *cr, uint32_t v) 69 | { 70 | if (cr->len >= cr->size) { 71 | if (cr_realloc(cr, cr->len + 1)) 72 | return -1; 73 | } 74 | cr->points[cr->len++] = v; 75 | return 0; 76 | } 77 | 78 | static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) 79 | { 80 | if ((cr->len + 2) > cr->size) { 81 | if (cr_realloc(cr, cr->len + 2)) 82 | return -1; 83 | } 84 | cr->points[cr->len++] = c1; 85 | cr->points[cr->len++] = c2; 86 | return 0; 87 | } 88 | 89 | int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); 90 | 91 | static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) 92 | { 93 | uint32_t b_pt[2]; 94 | b_pt[0] = c1; 95 | b_pt[1] = c2 + 1; 96 | return cr_union1(cr, b_pt, 2); 97 | } 98 | 99 | int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, 100 | const uint32_t *b_pt, int b_len, int op); 101 | 102 | int cr_invert(CharRange *cr); 103 | 104 | #ifdef CONFIG_ALL_UNICODE 105 | 106 | LRE_BOOL lre_is_id_start(uint32_t c); 107 | LRE_BOOL lre_is_id_continue(uint32_t c); 108 | 109 | int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, 110 | UnicodeNormalizationEnum n_type, 111 | void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); 112 | 113 | /* Unicode character range functions */ 114 | 115 | int unicode_script(CharRange *cr, 116 | const char *script_name, LRE_BOOL is_ext); 117 | int unicode_general_category(CharRange *cr, const char *gc_name); 118 | int unicode_prop(CharRange *cr, const char *prop_name); 119 | 120 | #endif /* CONFIG_ALL_UNICODE */ 121 | 122 | #undef LRE_BOOL 123 | 124 | #endif /* LIBUNICODE_H */ 125 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux klist like system 3 | * 4 | * Copyright (c) 2016-2017 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIST_H 25 | #define LIST_H 26 | 27 | #ifndef NULL 28 | #include 29 | #endif 30 | 31 | struct list_head { 32 | struct list_head *prev; 33 | struct list_head *next; 34 | }; 35 | 36 | #define LIST_HEAD_INIT(el) { &(el), &(el) } 37 | 38 | /* return the pointer of type 'type *' containing 'el' as field 'member' */ 39 | #define list_entry(el, type, member) \ 40 | ((type *)((uint8_t *)(el) - offsetof(type, member))) 41 | 42 | static inline void init_list_head(struct list_head *head) 43 | { 44 | head->prev = head; 45 | head->next = head; 46 | } 47 | 48 | /* insert 'el' between 'prev' and 'next' */ 49 | static inline void __list_add(struct list_head *el, 50 | struct list_head *prev, struct list_head *next) 51 | { 52 | prev->next = el; 53 | el->prev = prev; 54 | el->next = next; 55 | next->prev = el; 56 | } 57 | 58 | /* add 'el' at the head of the list 'head' (= after element head) */ 59 | static inline void list_add(struct list_head *el, struct list_head *head) 60 | { 61 | __list_add(el, head, head->next); 62 | } 63 | 64 | /* add 'el' at the end of the list 'head' (= before element head) */ 65 | static inline void list_add_tail(struct list_head *el, struct list_head *head) 66 | { 67 | __list_add(el, head->prev, head); 68 | } 69 | 70 | static inline void list_del(struct list_head *el) 71 | { 72 | struct list_head *prev, *next; 73 | prev = el->prev; 74 | next = el->next; 75 | prev->next = next; 76 | next->prev = prev; 77 | el->prev = NULL; /* fail safe */ 78 | el->next = NULL; /* fail safe */ 79 | } 80 | 81 | static inline int list_empty(struct list_head *el) 82 | { 83 | return el->next == el; 84 | } 85 | 86 | #define list_for_each(el, head) \ 87 | for(el = (head)->next; el != (head); el = el->next) 88 | 89 | #define list_for_each_safe(el, el1, head) \ 90 | for(el = (head)->next, el1 = el->next; el != (head); \ 91 | el = el1, el1 = el->next) 92 | 93 | #define list_for_each_prev(el, head) \ 94 | for(el = (head)->prev; el != (head); el = el->prev) 95 | 96 | #define list_for_each_prev_safe(el, el1, head) \ 97 | for(el = (head)->prev, el1 = el->prev; el != (head); \ 98 | el = el1, el1 = el->prev) 99 | 100 | #endif /* LIST_H */ 101 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/qjs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS stand alone interpreter 3 | * 4 | * Copyright (c) 2017-2021 Fabrice Bellard 5 | * Copyright (c) 2017-2021 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #if defined(__APPLE__) 36 | #include 37 | #elif defined(__linux__) 38 | #include 39 | #endif 40 | 41 | #include "cutils.h" 42 | #include "quickjs-libc.h" 43 | 44 | extern const uint8_t qjsc_repl[]; 45 | extern const uint32_t qjsc_repl_size; 46 | #ifdef CONFIG_BIGNUM 47 | extern const uint8_t qjsc_qjscalc[]; 48 | extern const uint32_t qjsc_qjscalc_size; 49 | static int bignum_ext; 50 | #endif 51 | 52 | static int eval_buf(JSContext *ctx, const void *buf, int buf_len, 53 | const char *filename, int eval_flags) 54 | { 55 | JSValue val; 56 | int ret; 57 | 58 | if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { 59 | /* for the modules, we compile then run to be able to set 60 | import.meta */ 61 | val = JS_Eval(ctx, buf, buf_len, filename, 62 | eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); 63 | if (!JS_IsException(val)) { 64 | js_module_set_import_meta(ctx, val, TRUE, TRUE); 65 | val = JS_EvalFunction(ctx, val); 66 | } 67 | } else { 68 | val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); 69 | } 70 | if (JS_IsException(val)) { 71 | js_std_dump_error(ctx); 72 | ret = -1; 73 | } else { 74 | ret = 0; 75 | } 76 | JS_FreeValue(ctx, val); 77 | return ret; 78 | } 79 | 80 | static int eval_file(JSContext *ctx, const char *filename, int module) 81 | { 82 | uint8_t *buf; 83 | int ret, eval_flags; 84 | size_t buf_len; 85 | 86 | buf = js_load_file(ctx, &buf_len, filename); 87 | if (!buf) { 88 | perror(filename); 89 | exit(1); 90 | } 91 | 92 | if (module < 0) { 93 | module = (has_suffix(filename, ".mjs") || 94 | JS_DetectModule((const char *)buf, buf_len)); 95 | } 96 | if (module) 97 | eval_flags = JS_EVAL_TYPE_MODULE; 98 | else 99 | eval_flags = JS_EVAL_TYPE_GLOBAL; 100 | ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); 101 | js_free(ctx, buf); 102 | return ret; 103 | } 104 | 105 | /* also used to initialize the worker context */ 106 | static JSContext *JS_NewCustomContext(JSRuntime *rt) 107 | { 108 | JSContext *ctx; 109 | ctx = JS_NewContext(rt); 110 | if (!ctx) 111 | return NULL; 112 | #ifdef CONFIG_BIGNUM 113 | if (bignum_ext) { 114 | JS_AddIntrinsicBigFloat(ctx); 115 | JS_AddIntrinsicBigDecimal(ctx); 116 | JS_AddIntrinsicOperators(ctx); 117 | JS_EnableBignumExt(ctx, TRUE); 118 | } 119 | #endif 120 | /* system modules */ 121 | js_init_module_std(ctx, "std"); 122 | js_init_module_os(ctx, "os"); 123 | return ctx; 124 | } 125 | 126 | #if defined(__APPLE__) 127 | #define MALLOC_OVERHEAD 0 128 | #else 129 | #define MALLOC_OVERHEAD 8 130 | #endif 131 | 132 | struct trace_malloc_data { 133 | uint8_t *base; 134 | }; 135 | 136 | static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, 137 | struct trace_malloc_data *dp) 138 | { 139 | return ptr - dp->base; 140 | } 141 | 142 | /* default memory allocation functions with memory limitation */ 143 | static inline size_t js_trace_malloc_usable_size(void *ptr) 144 | { 145 | #if defined(__APPLE__) 146 | return malloc_size(ptr); 147 | #elif defined(_WIN32) 148 | return _msize(ptr); 149 | #elif defined(EMSCRIPTEN) 150 | return 0; 151 | #elif defined(__linux__) 152 | return malloc_usable_size(ptr); 153 | #else 154 | /* change this to `return 0;` if compilation fails */ 155 | return malloc_usable_size(ptr); 156 | #endif 157 | } 158 | 159 | static void 160 | #ifdef _WIN32 161 | /* mingw printf is used */ 162 | __attribute__((format(gnu_printf, 2, 3))) 163 | #else 164 | __attribute__((format(printf, 2, 3))) 165 | #endif 166 | js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) 167 | { 168 | va_list ap; 169 | int c; 170 | 171 | va_start(ap, fmt); 172 | while ((c = *fmt++) != '\0') { 173 | if (c == '%') { 174 | /* only handle %p and %zd */ 175 | if (*fmt == 'p') { 176 | uint8_t *ptr = va_arg(ap, void *); 177 | if (ptr == NULL) { 178 | printf("NULL"); 179 | } else { 180 | printf("H%+06lld.%zd", 181 | js_trace_malloc_ptr_offset(ptr, s->opaque), 182 | js_trace_malloc_usable_size(ptr)); 183 | } 184 | fmt++; 185 | continue; 186 | } 187 | if (fmt[0] == 'z' && fmt[1] == 'd') { 188 | size_t sz = va_arg(ap, size_t); 189 | printf("%zd", sz); 190 | fmt += 2; 191 | continue; 192 | } 193 | } 194 | putc(c, stdout); 195 | } 196 | va_end(ap); 197 | } 198 | 199 | static void js_trace_malloc_init(struct trace_malloc_data *s) 200 | { 201 | free(s->base = malloc(8)); 202 | } 203 | 204 | static void *js_trace_malloc(JSMallocState *s, size_t size) 205 | { 206 | void *ptr; 207 | 208 | /* Do not allocate zero bytes: behavior is platform dependent */ 209 | assert(size != 0); 210 | 211 | if (unlikely(s->malloc_size + size > s->malloc_limit)) 212 | return NULL; 213 | ptr = malloc(size); 214 | js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr); 215 | if (ptr) { 216 | s->malloc_count++; 217 | s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; 218 | } 219 | return ptr; 220 | } 221 | 222 | static void js_trace_free(JSMallocState *s, void *ptr) 223 | { 224 | if (!ptr) 225 | return; 226 | 227 | js_trace_malloc_printf(s, "F %p\n", ptr); 228 | s->malloc_count--; 229 | s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; 230 | free(ptr); 231 | } 232 | 233 | static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) 234 | { 235 | size_t old_size; 236 | 237 | if (!ptr) { 238 | if (size == 0) 239 | return NULL; 240 | return js_trace_malloc(s, size); 241 | } 242 | old_size = js_trace_malloc_usable_size(ptr); 243 | if (size == 0) { 244 | js_trace_malloc_printf(s, "R %zd %p\n", size, ptr); 245 | s->malloc_count--; 246 | s->malloc_size -= old_size + MALLOC_OVERHEAD; 247 | free(ptr); 248 | return NULL; 249 | } 250 | if (s->malloc_size + size - old_size > s->malloc_limit) 251 | return NULL; 252 | 253 | js_trace_malloc_printf(s, "R %zd %p", size, ptr); 254 | 255 | ptr = realloc(ptr, size); 256 | js_trace_malloc_printf(s, " -> %p\n", ptr); 257 | if (ptr) { 258 | s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size; 259 | } 260 | return ptr; 261 | } 262 | 263 | static const JSMallocFunctions trace_mf = { 264 | js_trace_malloc, 265 | js_trace_free, 266 | js_trace_realloc, 267 | #if defined(__APPLE__) 268 | malloc_size, 269 | #elif defined(_WIN32) 270 | (size_t (*)(const void *))_msize, 271 | #elif defined(EMSCRIPTEN) 272 | NULL, 273 | #elif defined(__linux__) 274 | (size_t (*)(const void *))malloc_usable_size, 275 | #else 276 | /* change this to `NULL,` if compilation fails */ 277 | malloc_usable_size, 278 | #endif 279 | }; 280 | 281 | #define PROG_NAME "qjs" 282 | 283 | void help(void) 284 | { 285 | printf("QuickJS version " CONFIG_VERSION "\n" 286 | "usage: " PROG_NAME " [options] [file [args]]\n" 287 | "-h --help list options\n" 288 | "-e --eval EXPR evaluate EXPR\n" 289 | "-i --interactive go to interactive mode\n" 290 | "-m --module load as ES6 module (default=autodetect)\n" 291 | " --script load as ES6 script (default=autodetect)\n" 292 | "-I --include file include an additional file\n" 293 | " --std make 'std' and 'os' available to the loaded script\n" 294 | #ifdef CONFIG_BIGNUM 295 | " --bignum enable the bignum extensions (BigFloat, BigDecimal)\n" 296 | " --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n" 297 | #endif 298 | "-T --trace trace memory allocation\n" 299 | "-d --dump dump the memory usage stats\n" 300 | " --memory-limit n limit the memory usage to 'n' bytes\n" 301 | " --stack-size n limit the stack size to 'n' bytes\n" 302 | " --unhandled-rejection dump unhandled promise rejections\n" 303 | "-q --quit just instantiate the interpreter and quit\n"); 304 | exit(1); 305 | } 306 | 307 | int main(int argc, char **argv) 308 | { 309 | JSRuntime *rt; 310 | JSContext *ctx; 311 | struct trace_malloc_data trace_data = { NULL }; 312 | int optind; 313 | char *expr = NULL; 314 | int interactive = 0; 315 | int dump_memory = 0; 316 | int trace_memory = 0; 317 | int empty_run = 0; 318 | int module = -1; 319 | int load_std = 0; 320 | int dump_unhandled_promise_rejection = 0; 321 | size_t memory_limit = 0; 322 | char *include_list[32]; 323 | int i, include_count = 0; 324 | #ifdef CONFIG_BIGNUM 325 | int load_jscalc; 326 | #endif 327 | size_t stack_size = 0; 328 | 329 | #ifdef CONFIG_BIGNUM 330 | /* load jscalc runtime if invoked as 'qjscalc' */ 331 | { 332 | const char *p, *exename; 333 | exename = argv[0]; 334 | p = strrchr(exename, '/'); 335 | if (p) 336 | exename = p + 1; 337 | load_jscalc = !strcmp(exename, "qjscalc"); 338 | } 339 | #endif 340 | 341 | /* cannot use getopt because we want to pass the command line to 342 | the script */ 343 | optind = 1; 344 | while (optind < argc && *argv[optind] == '-') { 345 | char *arg = argv[optind] + 1; 346 | const char *longopt = ""; 347 | /* a single - is not an option, it also stops argument scanning */ 348 | if (!*arg) 349 | break; 350 | optind++; 351 | if (*arg == '-') { 352 | longopt = arg + 1; 353 | arg += strlen(arg); 354 | /* -- stops argument scanning */ 355 | if (!*longopt) 356 | break; 357 | } 358 | for (; *arg || *longopt; longopt = "") { 359 | char opt = *arg; 360 | if (opt) 361 | arg++; 362 | if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { 363 | help(); 364 | continue; 365 | } 366 | if (opt == 'e' || !strcmp(longopt, "eval")) { 367 | if (*arg) { 368 | expr = arg; 369 | break; 370 | } 371 | if (optind < argc) { 372 | expr = argv[optind++]; 373 | break; 374 | } 375 | fprintf(stderr, "qjs: missing expression for -e\n"); 376 | exit(2); 377 | } 378 | if (opt == 'I' || !strcmp(longopt, "include")) { 379 | if (optind >= argc) { 380 | fprintf(stderr, "expecting filename"); 381 | exit(1); 382 | } 383 | if (include_count >= countof(include_list)) { 384 | fprintf(stderr, "too many included files"); 385 | exit(1); 386 | } 387 | include_list[include_count++] = argv[optind++]; 388 | continue; 389 | } 390 | if (opt == 'i' || !strcmp(longopt, "interactive")) { 391 | interactive++; 392 | continue; 393 | } 394 | if (opt == 'm' || !strcmp(longopt, "module")) { 395 | module = 1; 396 | continue; 397 | } 398 | if (!strcmp(longopt, "script")) { 399 | module = 0; 400 | continue; 401 | } 402 | if (opt == 'd' || !strcmp(longopt, "dump")) { 403 | dump_memory++; 404 | continue; 405 | } 406 | if (opt == 'T' || !strcmp(longopt, "trace")) { 407 | trace_memory++; 408 | continue; 409 | } 410 | if (!strcmp(longopt, "std")) { 411 | load_std = 1; 412 | continue; 413 | } 414 | if (!strcmp(longopt, "unhandled-rejection")) { 415 | dump_unhandled_promise_rejection = 1; 416 | continue; 417 | } 418 | #ifdef CONFIG_BIGNUM 419 | if (!strcmp(longopt, "bignum")) { 420 | bignum_ext = 1; 421 | continue; 422 | } 423 | if (!strcmp(longopt, "qjscalc")) { 424 | load_jscalc = 1; 425 | continue; 426 | } 427 | #endif 428 | if (opt == 'q' || !strcmp(longopt, "quit")) { 429 | empty_run++; 430 | continue; 431 | } 432 | if (!strcmp(longopt, "memory-limit")) { 433 | if (optind >= argc) { 434 | fprintf(stderr, "expecting memory limit"); 435 | exit(1); 436 | } 437 | memory_limit = (size_t)strtod(argv[optind++], NULL); 438 | continue; 439 | } 440 | if (!strcmp(longopt, "stack-size")) { 441 | if (optind >= argc) { 442 | fprintf(stderr, "expecting stack size"); 443 | exit(1); 444 | } 445 | stack_size = (size_t)strtod(argv[optind++], NULL); 446 | continue; 447 | } 448 | if (opt) { 449 | fprintf(stderr, "qjs: unknown option '-%c'\n", opt); 450 | } else { 451 | fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); 452 | } 453 | help(); 454 | } 455 | } 456 | 457 | if (load_jscalc) 458 | bignum_ext = 1; 459 | 460 | if (trace_memory) { 461 | js_trace_malloc_init(&trace_data); 462 | rt = JS_NewRuntime2(&trace_mf, &trace_data); 463 | } else { 464 | rt = JS_NewRuntime(); 465 | } 466 | if (!rt) { 467 | fprintf(stderr, "qjs: cannot allocate JS runtime\n"); 468 | exit(2); 469 | } 470 | if (memory_limit != 0) 471 | JS_SetMemoryLimit(rt, memory_limit); 472 | if (stack_size != 0) 473 | JS_SetMaxStackSize(rt, stack_size); 474 | js_std_set_worker_new_context_func(JS_NewCustomContext); 475 | js_std_init_handlers(rt); 476 | ctx = JS_NewCustomContext(rt); 477 | if (!ctx) { 478 | fprintf(stderr, "qjs: cannot allocate JS context\n"); 479 | exit(2); 480 | } 481 | 482 | /* loader for ES6 modules */ 483 | JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); 484 | 485 | if (dump_unhandled_promise_rejection) { 486 | JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, 487 | NULL); 488 | } 489 | 490 | if (!empty_run) { 491 | #ifdef CONFIG_BIGNUM 492 | if (load_jscalc) { 493 | js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0); 494 | } 495 | #endif 496 | js_std_add_helpers(ctx, argc - optind, argv + optind); 497 | 498 | /* make 'std' and 'os' visible to non module code */ 499 | if (load_std) { 500 | const char *str = "import * as std from 'std';\n" 501 | "import * as os from 'os';\n" 502 | "globalThis.std = std;\n" 503 | "globalThis.os = os;\n"; 504 | eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); 505 | } 506 | 507 | for(i = 0; i < include_count; i++) { 508 | if (eval_file(ctx, include_list[i], module)) 509 | goto fail; 510 | } 511 | 512 | if (expr) { 513 | if (eval_buf(ctx, expr, strlen(expr), "", 0)) 514 | goto fail; 515 | } else 516 | if (optind >= argc) { 517 | /* interactive mode */ 518 | interactive = 1; 519 | } else { 520 | const char *filename; 521 | filename = argv[optind]; 522 | if (eval_file(ctx, filename, module)) 523 | goto fail; 524 | } 525 | if (interactive) { 526 | js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); 527 | } 528 | js_std_loop(ctx); 529 | } 530 | 531 | if (dump_memory) { 532 | JSMemoryUsage stats; 533 | JS_ComputeMemoryUsage(rt, &stats); 534 | JS_DumpMemoryUsage(stdout, &stats, rt); 535 | } 536 | js_std_free_handlers(rt); 537 | JS_FreeContext(ctx); 538 | JS_FreeRuntime(rt); 539 | 540 | if (empty_run && dump_memory) { 541 | clock_t t[5]; 542 | double best[5]; 543 | int i, j; 544 | for (i = 0; i < 100; i++) { 545 | t[0] = clock(); 546 | rt = JS_NewRuntime(); 547 | t[1] = clock(); 548 | ctx = JS_NewContext(rt); 549 | t[2] = clock(); 550 | JS_FreeContext(ctx); 551 | t[3] = clock(); 552 | JS_FreeRuntime(rt); 553 | t[4] = clock(); 554 | for (j = 4; j > 0; j--) { 555 | double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; 556 | if (i == 0 || best[j] > ms) 557 | best[j] = ms; 558 | } 559 | } 560 | printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", 561 | best[1] + best[2] + best[3] + best[4], 562 | best[1], best[2], best[3], best[4]); 563 | } 564 | return 0; 565 | fail: 566 | js_std_free_handlers(rt); 567 | JS_FreeContext(ctx); 568 | JS_FreeRuntime(rt); 569 | return 1; 570 | } 571 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/quickjs-atom.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS atom definitions 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * Copyright (c) 2017-2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #ifdef DEF 27 | 28 | /* Note: first atoms are considered as keywords in the parser */ 29 | DEF(null, "null") /* must be first */ 30 | DEF(false, "false") 31 | DEF(true, "true") 32 | DEF(if, "if") 33 | DEF(else, "else") 34 | DEF(return, "return") 35 | DEF(var, "var") 36 | DEF(this, "this") 37 | DEF(delete, "delete") 38 | DEF(void, "void") 39 | DEF(typeof, "typeof") 40 | DEF(new, "new") 41 | DEF(in, "in") 42 | DEF(instanceof, "instanceof") 43 | DEF(do, "do") 44 | DEF(while, "while") 45 | DEF(for, "for") 46 | DEF(break, "break") 47 | DEF(continue, "continue") 48 | DEF(switch, "switch") 49 | DEF(case, "case") 50 | DEF(default, "default") 51 | DEF(throw, "throw") 52 | DEF(try, "try") 53 | DEF(catch, "catch") 54 | DEF(finally, "finally") 55 | DEF(function, "function") 56 | DEF(debugger, "debugger") 57 | DEF(with, "with") 58 | /* FutureReservedWord */ 59 | DEF(class, "class") 60 | DEF(const, "const") 61 | DEF(enum, "enum") 62 | DEF(export, "export") 63 | DEF(extends, "extends") 64 | DEF(import, "import") 65 | DEF(super, "super") 66 | /* FutureReservedWords when parsing strict mode code */ 67 | DEF(implements, "implements") 68 | DEF(interface, "interface") 69 | DEF(let, "let") 70 | DEF(package, "package") 71 | DEF(private, "private") 72 | DEF(protected, "protected") 73 | DEF(public, "public") 74 | DEF(static, "static") 75 | DEF(yield, "yield") 76 | DEF(await, "await") 77 | 78 | /* empty string */ 79 | DEF(empty_string, "") 80 | /* identifiers */ 81 | DEF(length, "length") 82 | DEF(fileName, "fileName") 83 | DEF(lineNumber, "lineNumber") 84 | DEF(message, "message") 85 | DEF(errors, "errors") 86 | DEF(stack, "stack") 87 | DEF(name, "name") 88 | DEF(toString, "toString") 89 | DEF(toLocaleString, "toLocaleString") 90 | DEF(valueOf, "valueOf") 91 | DEF(eval, "eval") 92 | DEF(prototype, "prototype") 93 | DEF(constructor, "constructor") 94 | DEF(configurable, "configurable") 95 | DEF(writable, "writable") 96 | DEF(enumerable, "enumerable") 97 | DEF(value, "value") 98 | DEF(get, "get") 99 | DEF(set, "set") 100 | DEF(of, "of") 101 | DEF(__proto__, "__proto__") 102 | DEF(undefined, "undefined") 103 | DEF(number, "number") 104 | DEF(boolean, "boolean") 105 | DEF(string, "string") 106 | DEF(object, "object") 107 | DEF(symbol, "symbol") 108 | DEF(integer, "integer") 109 | DEF(unknown, "unknown") 110 | DEF(arguments, "arguments") 111 | DEF(callee, "callee") 112 | DEF(caller, "caller") 113 | DEF(_eval_, "") 114 | DEF(_ret_, "") 115 | DEF(_var_, "") 116 | DEF(_arg_var_, "") 117 | DEF(_with_, "") 118 | DEF(lastIndex, "lastIndex") 119 | DEF(target, "target") 120 | DEF(index, "index") 121 | DEF(input, "input") 122 | DEF(defineProperties, "defineProperties") 123 | DEF(apply, "apply") 124 | DEF(join, "join") 125 | DEF(concat, "concat") 126 | DEF(split, "split") 127 | DEF(construct, "construct") 128 | DEF(getPrototypeOf, "getPrototypeOf") 129 | DEF(setPrototypeOf, "setPrototypeOf") 130 | DEF(isExtensible, "isExtensible") 131 | DEF(preventExtensions, "preventExtensions") 132 | DEF(has, "has") 133 | DEF(deleteProperty, "deleteProperty") 134 | DEF(defineProperty, "defineProperty") 135 | DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") 136 | DEF(ownKeys, "ownKeys") 137 | DEF(add, "add") 138 | DEF(done, "done") 139 | DEF(next, "next") 140 | DEF(values, "values") 141 | DEF(source, "source") 142 | DEF(flags, "flags") 143 | DEF(global, "global") 144 | DEF(unicode, "unicode") 145 | DEF(raw, "raw") 146 | DEF(new_target, "new.target") 147 | DEF(this_active_func, "this.active_func") 148 | DEF(home_object, "") 149 | DEF(computed_field, "") 150 | DEF(static_computed_field, "") /* must come after computed_fields */ 151 | DEF(class_fields_init, "") 152 | DEF(brand, "") 153 | DEF(hash_constructor, "#constructor") 154 | DEF(as, "as") 155 | DEF(from, "from") 156 | DEF(meta, "meta") 157 | DEF(_default_, "*default*") 158 | DEF(_star_, "*") 159 | DEF(Module, "Module") 160 | DEF(then, "then") 161 | DEF(resolve, "resolve") 162 | DEF(reject, "reject") 163 | DEF(promise, "promise") 164 | DEF(proxy, "proxy") 165 | DEF(revoke, "revoke") 166 | DEF(async, "async") 167 | DEF(exec, "exec") 168 | DEF(groups, "groups") 169 | DEF(status, "status") 170 | DEF(reason, "reason") 171 | DEF(globalThis, "globalThis") 172 | #ifdef CONFIG_BIGNUM 173 | DEF(bigint, "bigint") 174 | DEF(bigfloat, "bigfloat") 175 | DEF(bigdecimal, "bigdecimal") 176 | DEF(roundingMode, "roundingMode") 177 | DEF(maximumSignificantDigits, "maximumSignificantDigits") 178 | DEF(maximumFractionDigits, "maximumFractionDigits") 179 | #endif 180 | #ifdef CONFIG_ATOMICS 181 | DEF(not_equal, "not-equal") 182 | DEF(timed_out, "timed-out") 183 | DEF(ok, "ok") 184 | #endif 185 | DEF(toJSON, "toJSON") 186 | /* class names */ 187 | DEF(Object, "Object") 188 | DEF(Array, "Array") 189 | DEF(Error, "Error") 190 | DEF(Number, "Number") 191 | DEF(String, "String") 192 | DEF(Boolean, "Boolean") 193 | DEF(Symbol, "Symbol") 194 | DEF(Arguments, "Arguments") 195 | DEF(Math, "Math") 196 | DEF(JSON, "JSON") 197 | DEF(Date, "Date") 198 | DEF(Function, "Function") 199 | DEF(GeneratorFunction, "GeneratorFunction") 200 | DEF(ForInIterator, "ForInIterator") 201 | DEF(RegExp, "RegExp") 202 | DEF(ArrayBuffer, "ArrayBuffer") 203 | DEF(SharedArrayBuffer, "SharedArrayBuffer") 204 | /* must keep same order as class IDs for typed arrays */ 205 | DEF(Uint8ClampedArray, "Uint8ClampedArray") 206 | DEF(Int8Array, "Int8Array") 207 | DEF(Uint8Array, "Uint8Array") 208 | DEF(Int16Array, "Int16Array") 209 | DEF(Uint16Array, "Uint16Array") 210 | DEF(Int32Array, "Int32Array") 211 | DEF(Uint32Array, "Uint32Array") 212 | #ifdef CONFIG_BIGNUM 213 | DEF(BigInt64Array, "BigInt64Array") 214 | DEF(BigUint64Array, "BigUint64Array") 215 | #endif 216 | DEF(Float32Array, "Float32Array") 217 | DEF(Float64Array, "Float64Array") 218 | DEF(DataView, "DataView") 219 | #ifdef CONFIG_BIGNUM 220 | DEF(BigInt, "BigInt") 221 | DEF(BigFloat, "BigFloat") 222 | DEF(BigFloatEnv, "BigFloatEnv") 223 | DEF(BigDecimal, "BigDecimal") 224 | DEF(OperatorSet, "OperatorSet") 225 | DEF(Operators, "Operators") 226 | #endif 227 | DEF(Map, "Map") 228 | DEF(Set, "Set") /* Map + 1 */ 229 | DEF(WeakMap, "WeakMap") /* Map + 2 */ 230 | DEF(WeakSet, "WeakSet") /* Map + 3 */ 231 | DEF(Map_Iterator, "Map Iterator") 232 | DEF(Set_Iterator, "Set Iterator") 233 | DEF(Array_Iterator, "Array Iterator") 234 | DEF(String_Iterator, "String Iterator") 235 | DEF(RegExp_String_Iterator, "RegExp String Iterator") 236 | DEF(Generator, "Generator") 237 | DEF(Proxy, "Proxy") 238 | DEF(Promise, "Promise") 239 | DEF(PromiseResolveFunction, "PromiseResolveFunction") 240 | DEF(PromiseRejectFunction, "PromiseRejectFunction") 241 | DEF(AsyncFunction, "AsyncFunction") 242 | DEF(AsyncFunctionResolve, "AsyncFunctionResolve") 243 | DEF(AsyncFunctionReject, "AsyncFunctionReject") 244 | DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") 245 | DEF(AsyncGenerator, "AsyncGenerator") 246 | DEF(EvalError, "EvalError") 247 | DEF(RangeError, "RangeError") 248 | DEF(ReferenceError, "ReferenceError") 249 | DEF(SyntaxError, "SyntaxError") 250 | DEF(TypeError, "TypeError") 251 | DEF(URIError, "URIError") 252 | DEF(InternalError, "InternalError") 253 | /* private symbols */ 254 | DEF(Private_brand, "") 255 | /* symbols */ 256 | DEF(Symbol_toPrimitive, "Symbol.toPrimitive") 257 | DEF(Symbol_iterator, "Symbol.iterator") 258 | DEF(Symbol_match, "Symbol.match") 259 | DEF(Symbol_matchAll, "Symbol.matchAll") 260 | DEF(Symbol_replace, "Symbol.replace") 261 | DEF(Symbol_search, "Symbol.search") 262 | DEF(Symbol_split, "Symbol.split") 263 | DEF(Symbol_toStringTag, "Symbol.toStringTag") 264 | DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") 265 | DEF(Symbol_hasInstance, "Symbol.hasInstance") 266 | DEF(Symbol_species, "Symbol.species") 267 | DEF(Symbol_unscopables, "Symbol.unscopables") 268 | DEF(Symbol_asyncIterator, "Symbol.asyncIterator") 269 | #ifdef CONFIG_BIGNUM 270 | DEF(Symbol_operatorSet, "Symbol.operatorSet") 271 | #endif 272 | 273 | #endif /* DEF */ 274 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/quickjs-libc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS C library 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef QUICKJS_LIBC_H 25 | #define QUICKJS_LIBC_H 26 | 27 | #include 28 | #include 29 | 30 | #include "quickjs.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); 37 | JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); 38 | void js_std_add_helpers(JSContext *ctx, int argc, char **argv); 39 | void js_std_loop(JSContext *ctx); 40 | void js_std_init_handlers(JSRuntime *rt); 41 | void js_std_free_handlers(JSRuntime *rt); 42 | void js_std_dump_error(JSContext *ctx); 43 | uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); 44 | int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, 45 | JS_BOOL use_realpath, JS_BOOL is_main); 46 | JSModuleDef *js_module_loader(JSContext *ctx, 47 | const char *module_name, void *opaque); 48 | void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, 49 | int flags); 50 | void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, 51 | JSValueConst reason, 52 | JS_BOOL is_handled, void *opaque); 53 | void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); 54 | 55 | #ifdef __cplusplus 56 | } /* extern "C" { */ 57 | #endif 58 | 59 | #endif /* QUICKJS_LIBC_H */ 60 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/quickjs-opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS opcode definitions 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * Copyright (c) 2017-2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #ifdef FMT 27 | FMT(none) 28 | FMT(none_int) 29 | FMT(none_loc) 30 | FMT(none_arg) 31 | FMT(none_var_ref) 32 | FMT(u8) 33 | FMT(i8) 34 | FMT(loc8) 35 | FMT(const8) 36 | FMT(label8) 37 | FMT(u16) 38 | FMT(i16) 39 | FMT(label16) 40 | FMT(npop) 41 | FMT(npopx) 42 | FMT(npop_u16) 43 | FMT(loc) 44 | FMT(arg) 45 | FMT(var_ref) 46 | FMT(u32) 47 | FMT(i32) 48 | FMT(const) 49 | FMT(label) 50 | FMT(atom) 51 | FMT(atom_u8) 52 | FMT(atom_u16) 53 | FMT(atom_label_u8) 54 | FMT(atom_label_u16) 55 | FMT(label_u16) 56 | #undef FMT 57 | #endif /* FMT */ 58 | 59 | #ifdef DEF 60 | 61 | #ifndef def 62 | #define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) 63 | #endif 64 | 65 | DEF(invalid, 1, 0, 0, none) /* never emitted */ 66 | 67 | /* push values */ 68 | DEF( push_i32, 5, 0, 1, i32) 69 | DEF( push_const, 5, 0, 1, const) 70 | DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ 71 | DEF(push_atom_value, 5, 0, 1, atom) 72 | DEF( private_symbol, 5, 0, 1, atom) 73 | DEF( undefined, 1, 0, 1, none) 74 | DEF( null, 1, 0, 1, none) 75 | DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ 76 | DEF( push_false, 1, 0, 1, none) 77 | DEF( push_true, 1, 0, 1, none) 78 | DEF( object, 1, 0, 1, none) 79 | DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ 80 | DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ 81 | 82 | DEF( drop, 1, 1, 0, none) /* a -> */ 83 | DEF( nip, 1, 2, 1, none) /* a b -> b */ 84 | DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ 85 | DEF( dup, 1, 1, 2, none) /* a -> a a */ 86 | DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ 87 | DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ 88 | DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ 89 | DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ 90 | DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ 91 | DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ 92 | DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ 93 | DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ 94 | DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ 95 | DEF( swap, 1, 2, 2, none) /* a b -> b a */ 96 | DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ 97 | DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ 98 | DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ 99 | DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ 100 | DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ 101 | 102 | DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ 103 | DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ 104 | DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ 105 | DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ 106 | DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ 107 | DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ 108 | DEF( apply, 3, 3, 1, u16) 109 | DEF( return, 1, 1, 0, none) 110 | DEF( return_undef, 1, 0, 0, none) 111 | DEF(check_ctor_return, 1, 1, 2, none) 112 | DEF( check_ctor, 1, 0, 0, none) 113 | DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ 114 | DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ 115 | DEF( return_async, 1, 1, 0, none) 116 | DEF( throw, 1, 1, 0, none) 117 | DEF( throw_error, 6, 0, 0, atom_u8) 118 | DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ 119 | DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ 120 | DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a 121 | bytecode string */ 122 | DEF( get_super, 1, 1, 1, none) 123 | DEF( import, 1, 1, 1, none) /* dynamic module import */ 124 | 125 | DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ 126 | DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ 127 | DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ 128 | DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ 129 | DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ 130 | DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ 131 | 132 | DEF( get_ref_value, 1, 2, 3, none) 133 | DEF( put_ref_value, 1, 3, 0, none) 134 | 135 | DEF( define_var, 6, 0, 0, atom_u8) 136 | DEF(check_define_var, 6, 0, 0, atom_u8) 137 | DEF( define_func, 6, 1, 0, atom_u8) 138 | DEF( get_field, 5, 1, 1, atom) 139 | DEF( get_field2, 5, 1, 2, atom) 140 | DEF( put_field, 5, 2, 0, atom) 141 | DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ 142 | DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ 143 | DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ 144 | DEF( get_array_el, 1, 2, 1, none) 145 | DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ 146 | DEF( put_array_el, 1, 3, 0, none) 147 | DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ 148 | DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ 149 | DEF( define_field, 5, 2, 1, atom) 150 | DEF( set_name, 5, 1, 1, atom) 151 | DEF(set_name_computed, 1, 2, 2, none) 152 | DEF( set_proto, 1, 2, 1, none) 153 | DEF(set_home_object, 1, 2, 2, none) 154 | DEF(define_array_el, 1, 3, 2, none) 155 | DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ 156 | DEF(copy_data_properties, 2, 3, 3, u8) 157 | DEF( define_method, 6, 2, 1, atom_u8) 158 | DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ 159 | DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ 160 | DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ 161 | 162 | DEF( get_loc, 3, 0, 1, loc) 163 | DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ 164 | DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ 165 | DEF( get_arg, 3, 0, 1, arg) 166 | DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ 167 | DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ 168 | DEF( get_var_ref, 3, 0, 1, var_ref) 169 | DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ 170 | DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ 171 | DEF(set_loc_uninitialized, 3, 0, 0, loc) 172 | DEF( get_loc_check, 3, 0, 1, loc) 173 | DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ 174 | DEF( put_loc_check_init, 3, 1, 0, loc) 175 | DEF(get_var_ref_check, 3, 0, 1, var_ref) 176 | DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ 177 | DEF(put_var_ref_check_init, 3, 1, 0, var_ref) 178 | DEF( close_loc, 3, 0, 0, loc) 179 | DEF( if_false, 5, 1, 0, label) 180 | DEF( if_true, 5, 1, 0, label) /* must come after if_false */ 181 | DEF( goto, 5, 0, 0, label) /* must come after if_true */ 182 | DEF( catch, 5, 0, 1, label) 183 | DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ 184 | DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ 185 | 186 | DEF( to_object, 1, 1, 1, none) 187 | //DEF( to_string, 1, 1, 1, none) 188 | DEF( to_propkey, 1, 1, 1, none) 189 | DEF( to_propkey2, 1, 2, 2, none) 190 | 191 | DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 192 | DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ 193 | DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 194 | DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 195 | DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ 196 | DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) 197 | 198 | DEF( make_loc_ref, 7, 0, 2, atom_u16) 199 | DEF( make_arg_ref, 7, 0, 2, atom_u16) 200 | DEF(make_var_ref_ref, 7, 0, 2, atom_u16) 201 | DEF( make_var_ref, 5, 0, 2, atom) 202 | 203 | DEF( for_in_start, 1, 1, 1, none) 204 | DEF( for_of_start, 1, 1, 3, none) 205 | DEF(for_await_of_start, 1, 1, 3, none) 206 | DEF( for_in_next, 1, 1, 3, none) 207 | DEF( for_of_next, 2, 3, 5, u8) 208 | DEF(iterator_check_object, 1, 1, 1, none) 209 | DEF(iterator_get_value_done, 1, 1, 2, none) 210 | DEF( iterator_close, 1, 3, 0, none) 211 | DEF(iterator_close_return, 1, 4, 4, none) 212 | DEF( iterator_next, 1, 4, 4, none) 213 | DEF( iterator_call, 2, 4, 5, u8) 214 | DEF( initial_yield, 1, 0, 0, none) 215 | DEF( yield, 1, 1, 2, none) 216 | DEF( yield_star, 1, 1, 2, none) 217 | DEF(async_yield_star, 1, 1, 2, none) 218 | DEF( await, 1, 1, 1, none) 219 | 220 | /* arithmetic/logic operations */ 221 | DEF( neg, 1, 1, 1, none) 222 | DEF( plus, 1, 1, 1, none) 223 | DEF( dec, 1, 1, 1, none) 224 | DEF( inc, 1, 1, 1, none) 225 | DEF( post_dec, 1, 1, 2, none) 226 | DEF( post_inc, 1, 1, 2, none) 227 | DEF( dec_loc, 2, 0, 0, loc8) 228 | DEF( inc_loc, 2, 0, 0, loc8) 229 | DEF( add_loc, 2, 1, 0, loc8) 230 | DEF( not, 1, 1, 1, none) 231 | DEF( lnot, 1, 1, 1, none) 232 | DEF( typeof, 1, 1, 1, none) 233 | DEF( delete, 1, 2, 1, none) 234 | DEF( delete_var, 5, 0, 1, atom) 235 | 236 | DEF( mul, 1, 2, 1, none) 237 | DEF( div, 1, 2, 1, none) 238 | DEF( mod, 1, 2, 1, none) 239 | DEF( add, 1, 2, 1, none) 240 | DEF( sub, 1, 2, 1, none) 241 | DEF( pow, 1, 2, 1, none) 242 | DEF( shl, 1, 2, 1, none) 243 | DEF( sar, 1, 2, 1, none) 244 | DEF( shr, 1, 2, 1, none) 245 | DEF( lt, 1, 2, 1, none) 246 | DEF( lte, 1, 2, 1, none) 247 | DEF( gt, 1, 2, 1, none) 248 | DEF( gte, 1, 2, 1, none) 249 | DEF( instanceof, 1, 2, 1, none) 250 | DEF( in, 1, 2, 1, none) 251 | DEF( eq, 1, 2, 1, none) 252 | DEF( neq, 1, 2, 1, none) 253 | DEF( strict_eq, 1, 2, 1, none) 254 | DEF( strict_neq, 1, 2, 1, none) 255 | DEF( and, 1, 2, 1, none) 256 | DEF( xor, 1, 2, 1, none) 257 | DEF( or, 1, 2, 1, none) 258 | DEF(is_undefined_or_null, 1, 1, 1, none) 259 | #ifdef CONFIG_BIGNUM 260 | DEF( mul_pow10, 1, 2, 1, none) 261 | DEF( math_mod, 1, 2, 1, none) 262 | #endif 263 | /* must be the last non short and non temporary opcode */ 264 | DEF( nop, 1, 0, 0, none) 265 | 266 | /* temporary opcodes: never emitted in the final bytecode */ 267 | 268 | def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ 269 | def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ 270 | 271 | def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ 272 | 273 | def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ 274 | def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ 275 | def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ 276 | def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ 277 | def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ 278 | def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ 279 | def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ 280 | def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ 281 | def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ 282 | def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ 283 | 284 | def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ 285 | 286 | def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ 287 | 288 | #if SHORT_OPCODES 289 | DEF( push_minus1, 1, 0, 1, none_int) 290 | DEF( push_0, 1, 0, 1, none_int) 291 | DEF( push_1, 1, 0, 1, none_int) 292 | DEF( push_2, 1, 0, 1, none_int) 293 | DEF( push_3, 1, 0, 1, none_int) 294 | DEF( push_4, 1, 0, 1, none_int) 295 | DEF( push_5, 1, 0, 1, none_int) 296 | DEF( push_6, 1, 0, 1, none_int) 297 | DEF( push_7, 1, 0, 1, none_int) 298 | DEF( push_i8, 2, 0, 1, i8) 299 | DEF( push_i16, 3, 0, 1, i16) 300 | DEF( push_const8, 2, 0, 1, const8) 301 | DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ 302 | DEF(push_empty_string, 1, 0, 1, none) 303 | 304 | DEF( get_loc8, 2, 0, 1, loc8) 305 | DEF( put_loc8, 2, 1, 0, loc8) 306 | DEF( set_loc8, 2, 1, 1, loc8) 307 | 308 | DEF( get_loc0, 1, 0, 1, none_loc) 309 | DEF( get_loc1, 1, 0, 1, none_loc) 310 | DEF( get_loc2, 1, 0, 1, none_loc) 311 | DEF( get_loc3, 1, 0, 1, none_loc) 312 | DEF( put_loc0, 1, 1, 0, none_loc) 313 | DEF( put_loc1, 1, 1, 0, none_loc) 314 | DEF( put_loc2, 1, 1, 0, none_loc) 315 | DEF( put_loc3, 1, 1, 0, none_loc) 316 | DEF( set_loc0, 1, 1, 1, none_loc) 317 | DEF( set_loc1, 1, 1, 1, none_loc) 318 | DEF( set_loc2, 1, 1, 1, none_loc) 319 | DEF( set_loc3, 1, 1, 1, none_loc) 320 | DEF( get_arg0, 1, 0, 1, none_arg) 321 | DEF( get_arg1, 1, 0, 1, none_arg) 322 | DEF( get_arg2, 1, 0, 1, none_arg) 323 | DEF( get_arg3, 1, 0, 1, none_arg) 324 | DEF( put_arg0, 1, 1, 0, none_arg) 325 | DEF( put_arg1, 1, 1, 0, none_arg) 326 | DEF( put_arg2, 1, 1, 0, none_arg) 327 | DEF( put_arg3, 1, 1, 0, none_arg) 328 | DEF( set_arg0, 1, 1, 1, none_arg) 329 | DEF( set_arg1, 1, 1, 1, none_arg) 330 | DEF( set_arg2, 1, 1, 1, none_arg) 331 | DEF( set_arg3, 1, 1, 1, none_arg) 332 | DEF( get_var_ref0, 1, 0, 1, none_var_ref) 333 | DEF( get_var_ref1, 1, 0, 1, none_var_ref) 334 | DEF( get_var_ref2, 1, 0, 1, none_var_ref) 335 | DEF( get_var_ref3, 1, 0, 1, none_var_ref) 336 | DEF( put_var_ref0, 1, 1, 0, none_var_ref) 337 | DEF( put_var_ref1, 1, 1, 0, none_var_ref) 338 | DEF( put_var_ref2, 1, 1, 0, none_var_ref) 339 | DEF( put_var_ref3, 1, 1, 0, none_var_ref) 340 | DEF( set_var_ref0, 1, 1, 1, none_var_ref) 341 | DEF( set_var_ref1, 1, 1, 1, none_var_ref) 342 | DEF( set_var_ref2, 1, 1, 1, none_var_ref) 343 | DEF( set_var_ref3, 1, 1, 1, none_var_ref) 344 | 345 | DEF( get_length, 1, 1, 1, none) 346 | 347 | DEF( if_false8, 2, 1, 0, label8) 348 | DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ 349 | DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ 350 | DEF( goto16, 3, 0, 0, label16) 351 | 352 | DEF( call0, 1, 1, 1, npopx) 353 | DEF( call1, 1, 1, 1, npopx) 354 | DEF( call2, 1, 1, 1, npopx) 355 | DEF( call3, 1, 1, 1, npopx) 356 | 357 | DEF( is_undefined, 1, 1, 1, none) 358 | DEF( is_null, 1, 1, 1, none) 359 | DEF(typeof_is_undefined, 1, 1, 1, none) 360 | DEF( typeof_is_function, 1, 1, 1, none) 361 | #endif 362 | 363 | #undef DEF 364 | #undef def 365 | #endif /* DEF */ 366 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/readme.txt: -------------------------------------------------------------------------------- 1 | The main documentation is in doc/quickjs.pdf or doc/quickjs.html. 2 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/quickjs/unicode_gen_def.h: -------------------------------------------------------------------------------- 1 | #ifdef UNICODE_GENERAL_CATEGORY 2 | DEF(Cn, "Unassigned") /* must be zero */ 3 | DEF(Lu, "Uppercase_Letter") 4 | DEF(Ll, "Lowercase_Letter") 5 | DEF(Lt, "Titlecase_Letter") 6 | DEF(Lm, "Modifier_Letter") 7 | DEF(Lo, "Other_Letter") 8 | DEF(Mn, "Nonspacing_Mark") 9 | DEF(Mc, "Spacing_Mark") 10 | DEF(Me, "Enclosing_Mark") 11 | DEF(Nd, "Decimal_Number,digit") 12 | DEF(Nl, "Letter_Number") 13 | DEF(No, "Other_Number") 14 | DEF(Sm, "Math_Symbol") 15 | DEF(Sc, "Currency_Symbol") 16 | DEF(Sk, "Modifier_Symbol") 17 | DEF(So, "Other_Symbol") 18 | DEF(Pc, "Connector_Punctuation") 19 | DEF(Pd, "Dash_Punctuation") 20 | DEF(Ps, "Open_Punctuation") 21 | DEF(Pe, "Close_Punctuation") 22 | DEF(Pi, "Initial_Punctuation") 23 | DEF(Pf, "Final_Punctuation") 24 | DEF(Po, "Other_Punctuation") 25 | DEF(Zs, "Space_Separator") 26 | DEF(Zl, "Line_Separator") 27 | DEF(Zp, "Paragraph_Separator") 28 | DEF(Cc, "Control,cntrl") 29 | DEF(Cf, "Format") 30 | DEF(Cs, "Surrogate") 31 | DEF(Co, "Private_Use") 32 | /* synthetic properties */ 33 | DEF(LC, "Cased_Letter") 34 | DEF(L, "Letter") 35 | DEF(M, "Mark,Combining_Mark") 36 | DEF(N, "Number") 37 | DEF(S, "Symbol") 38 | DEF(P, "Punctuation,punct") 39 | DEF(Z, "Separator") 40 | DEF(C, "Other") 41 | #endif 42 | 43 | #ifdef UNICODE_SCRIPT 44 | /* scripts aliases names in PropertyValueAliases.txt */ 45 | DEF(Unknown, "Zzzz") 46 | DEF(Adlam, "Adlm") 47 | DEF(Ahom, "Ahom") 48 | DEF(Anatolian_Hieroglyphs, "Hluw") 49 | DEF(Arabic, "Arab") 50 | DEF(Armenian, "Armn") 51 | DEF(Avestan, "Avst") 52 | DEF(Balinese, "Bali") 53 | DEF(Bamum, "Bamu") 54 | DEF(Bassa_Vah, "Bass") 55 | DEF(Batak, "Batk") 56 | DEF(Bengali, "Beng") 57 | DEF(Bhaiksuki, "Bhks") 58 | DEF(Bopomofo, "Bopo") 59 | DEF(Brahmi, "Brah") 60 | DEF(Braille, "Brai") 61 | DEF(Buginese, "Bugi") 62 | DEF(Buhid, "Buhd") 63 | DEF(Canadian_Aboriginal, "Cans") 64 | DEF(Carian, "Cari") 65 | DEF(Caucasian_Albanian, "Aghb") 66 | DEF(Chakma, "Cakm") 67 | DEF(Cham, "Cham") 68 | DEF(Cherokee, "Cher") 69 | DEF(Chorasmian, "Chrs") 70 | DEF(Common, "Zyyy") 71 | DEF(Coptic, "Copt,Qaac") 72 | DEF(Cuneiform, "Xsux") 73 | DEF(Cypriot, "Cprt") 74 | DEF(Cyrillic, "Cyrl") 75 | DEF(Deseret, "Dsrt") 76 | DEF(Devanagari, "Deva") 77 | DEF(Dives_Akuru, "Diak") 78 | DEF(Dogra, "Dogr") 79 | DEF(Duployan, "Dupl") 80 | DEF(Egyptian_Hieroglyphs, "Egyp") 81 | DEF(Elbasan, "Elba") 82 | DEF(Elymaic, "Elym") 83 | DEF(Ethiopic, "Ethi") 84 | DEF(Georgian, "Geor") 85 | DEF(Glagolitic, "Glag") 86 | DEF(Gothic, "Goth") 87 | DEF(Grantha, "Gran") 88 | DEF(Greek, "Grek") 89 | DEF(Gujarati, "Gujr") 90 | DEF(Gunjala_Gondi, "Gong") 91 | DEF(Gurmukhi, "Guru") 92 | DEF(Han, "Hani") 93 | DEF(Hangul, "Hang") 94 | DEF(Hanifi_Rohingya, "Rohg") 95 | DEF(Hanunoo, "Hano") 96 | DEF(Hatran, "Hatr") 97 | DEF(Hebrew, "Hebr") 98 | DEF(Hiragana, "Hira") 99 | DEF(Imperial_Aramaic, "Armi") 100 | DEF(Inherited, "Zinh,Qaai") 101 | DEF(Inscriptional_Pahlavi, "Phli") 102 | DEF(Inscriptional_Parthian, "Prti") 103 | DEF(Javanese, "Java") 104 | DEF(Kaithi, "Kthi") 105 | DEF(Kannada, "Knda") 106 | DEF(Katakana, "Kana") 107 | DEF(Kayah_Li, "Kali") 108 | DEF(Kharoshthi, "Khar") 109 | DEF(Khmer, "Khmr") 110 | DEF(Khojki, "Khoj") 111 | DEF(Khitan_Small_Script, "Kits") 112 | DEF(Khudawadi, "Sind") 113 | DEF(Lao, "Laoo") 114 | DEF(Latin, "Latn") 115 | DEF(Lepcha, "Lepc") 116 | DEF(Limbu, "Limb") 117 | DEF(Linear_A, "Lina") 118 | DEF(Linear_B, "Linb") 119 | DEF(Lisu, "Lisu") 120 | DEF(Lycian, "Lyci") 121 | DEF(Lydian, "Lydi") 122 | DEF(Makasar, "Maka") 123 | DEF(Mahajani, "Mahj") 124 | DEF(Malayalam, "Mlym") 125 | DEF(Mandaic, "Mand") 126 | DEF(Manichaean, "Mani") 127 | DEF(Marchen, "Marc") 128 | DEF(Masaram_Gondi, "Gonm") 129 | DEF(Medefaidrin, "Medf") 130 | DEF(Meetei_Mayek, "Mtei") 131 | DEF(Mende_Kikakui, "Mend") 132 | DEF(Meroitic_Cursive, "Merc") 133 | DEF(Meroitic_Hieroglyphs, "Mero") 134 | DEF(Miao, "Plrd") 135 | DEF(Modi, "Modi") 136 | DEF(Mongolian, "Mong") 137 | DEF(Mro, "Mroo") 138 | DEF(Multani, "Mult") 139 | DEF(Myanmar, "Mymr") 140 | DEF(Nabataean, "Nbat") 141 | DEF(Nandinagari, "Nand") 142 | DEF(New_Tai_Lue, "Talu") 143 | DEF(Newa, "Newa") 144 | DEF(Nko, "Nkoo") 145 | DEF(Nushu, "Nshu") 146 | DEF(Nyiakeng_Puachue_Hmong, "Hmnp") 147 | DEF(Ogham, "Ogam") 148 | DEF(Ol_Chiki, "Olck") 149 | DEF(Old_Hungarian, "Hung") 150 | DEF(Old_Italic, "Ital") 151 | DEF(Old_North_Arabian, "Narb") 152 | DEF(Old_Permic, "Perm") 153 | DEF(Old_Persian, "Xpeo") 154 | DEF(Old_Sogdian, "Sogo") 155 | DEF(Old_South_Arabian, "Sarb") 156 | DEF(Old_Turkic, "Orkh") 157 | DEF(Oriya, "Orya") 158 | DEF(Osage, "Osge") 159 | DEF(Osmanya, "Osma") 160 | DEF(Pahawh_Hmong, "Hmng") 161 | DEF(Palmyrene, "Palm") 162 | DEF(Pau_Cin_Hau, "Pauc") 163 | DEF(Phags_Pa, "Phag") 164 | DEF(Phoenician, "Phnx") 165 | DEF(Psalter_Pahlavi, "Phlp") 166 | DEF(Rejang, "Rjng") 167 | DEF(Runic, "Runr") 168 | DEF(Samaritan, "Samr") 169 | DEF(Saurashtra, "Saur") 170 | DEF(Sharada, "Shrd") 171 | DEF(Shavian, "Shaw") 172 | DEF(Siddham, "Sidd") 173 | DEF(SignWriting, "Sgnw") 174 | DEF(Sinhala, "Sinh") 175 | DEF(Sogdian, "Sogd") 176 | DEF(Sora_Sompeng, "Sora") 177 | DEF(Soyombo, "Soyo") 178 | DEF(Sundanese, "Sund") 179 | DEF(Syloti_Nagri, "Sylo") 180 | DEF(Syriac, "Syrc") 181 | DEF(Tagalog, "Tglg") 182 | DEF(Tagbanwa, "Tagb") 183 | DEF(Tai_Le, "Tale") 184 | DEF(Tai_Tham, "Lana") 185 | DEF(Tai_Viet, "Tavt") 186 | DEF(Takri, "Takr") 187 | DEF(Tamil, "Taml") 188 | DEF(Tangut, "Tang") 189 | DEF(Telugu, "Telu") 190 | DEF(Thaana, "Thaa") 191 | DEF(Thai, "Thai") 192 | DEF(Tibetan, "Tibt") 193 | DEF(Tifinagh, "Tfng") 194 | DEF(Tirhuta, "Tirh") 195 | DEF(Ugaritic, "Ugar") 196 | DEF(Vai, "Vaii") 197 | DEF(Wancho, "Wcho") 198 | DEF(Warang_Citi, "Wara") 199 | DEF(Yezidi, "Yezi") 200 | DEF(Yi, "Yiii") 201 | DEF(Zanabazar_Square, "Zanb") 202 | #endif 203 | 204 | #ifdef UNICODE_PROP_LIST 205 | /* Prop list not exported to regexp */ 206 | DEF(Hyphen, "") 207 | DEF(Other_Math, "") 208 | DEF(Other_Alphabetic, "") 209 | DEF(Other_Lowercase, "") 210 | DEF(Other_Uppercase, "") 211 | DEF(Other_Grapheme_Extend, "") 212 | DEF(Other_Default_Ignorable_Code_Point, "") 213 | DEF(Other_ID_Start, "") 214 | DEF(Other_ID_Continue, "") 215 | DEF(Prepended_Concatenation_Mark, "") 216 | /* additional computed properties for smaller tables */ 217 | DEF(ID_Continue1, "") 218 | DEF(XID_Start1, "") 219 | DEF(XID_Continue1, "") 220 | DEF(Changes_When_Titlecased1, "") 221 | DEF(Changes_When_Casefolded1, "") 222 | DEF(Changes_When_NFKC_Casefolded1, "") 223 | 224 | /* Prop list exported to JS */ 225 | DEF(ASCII_Hex_Digit, "AHex") 226 | DEF(Bidi_Control, "Bidi_C") 227 | DEF(Dash, "") 228 | DEF(Deprecated, "Dep") 229 | DEF(Diacritic, "Dia") 230 | DEF(Extender, "Ext") 231 | DEF(Hex_Digit, "Hex") 232 | DEF(IDS_Binary_Operator, "IDSB") 233 | DEF(IDS_Trinary_Operator, "IDST") 234 | DEF(Ideographic, "Ideo") 235 | DEF(Join_Control, "Join_C") 236 | DEF(Logical_Order_Exception, "LOE") 237 | DEF(Noncharacter_Code_Point, "NChar") 238 | DEF(Pattern_Syntax, "Pat_Syn") 239 | DEF(Pattern_White_Space, "Pat_WS") 240 | DEF(Quotation_Mark, "QMark") 241 | DEF(Radical, "") 242 | DEF(Regional_Indicator, "RI") 243 | DEF(Sentence_Terminal, "STerm") 244 | DEF(Soft_Dotted, "SD") 245 | DEF(Terminal_Punctuation, "Term") 246 | DEF(Unified_Ideograph, "UIdeo") 247 | DEF(Variation_Selector, "VS") 248 | DEF(White_Space, "space") 249 | DEF(Bidi_Mirrored, "Bidi_M") 250 | DEF(Emoji, "") 251 | DEF(Emoji_Component, "EComp") 252 | DEF(Emoji_Modifier, "EMod") 253 | DEF(Emoji_Modifier_Base, "EBase") 254 | DEF(Emoji_Presentation, "EPres") 255 | DEF(Extended_Pictographic, "ExtPict") 256 | DEF(Default_Ignorable_Code_Point, "DI") 257 | DEF(ID_Start, "IDS") 258 | DEF(Case_Ignorable, "CI") 259 | 260 | /* other binary properties */ 261 | DEF(ASCII,"") 262 | DEF(Alphabetic, "Alpha") 263 | DEF(Any, "") 264 | DEF(Assigned,"") 265 | DEF(Cased, "") 266 | DEF(Changes_When_Casefolded, "CWCF") 267 | DEF(Changes_When_Casemapped, "CWCM") 268 | DEF(Changes_When_Lowercased, "CWL") 269 | DEF(Changes_When_NFKC_Casefolded, "CWKCF") 270 | DEF(Changes_When_Titlecased, "CWT") 271 | DEF(Changes_When_Uppercased, "CWU") 272 | DEF(Grapheme_Base, "Gr_Base") 273 | DEF(Grapheme_Extend, "Gr_Ext") 274 | DEF(ID_Continue, "IDC") 275 | DEF(Lowercase, "Lower") 276 | DEF(Math, "") 277 | DEF(Uppercase, "Upper") 278 | DEF(XID_Continue, "XIDC") 279 | DEF(XID_Start, "XIDS") 280 | 281 | /* internal tables with index */ 282 | DEF(Cased1, "") 283 | 284 | #endif 285 | -------------------------------------------------------------------------------- /libquickjs-sys/embed/static-functions.c: -------------------------------------------------------------------------------- 1 | #include "quickjs.h" 2 | 3 | // These are static inline functions in quickjs.h so bindgen does not pick 4 | // them up. 5 | // We use define simple wrapper functions to make them available to bindgen, 6 | // and therefore make them usable from Rust. 7 | 8 | int JS_ValueGetTag_real(JSValue v) { 9 | return JS_VALUE_GET_TAG(v); 10 | } 11 | 12 | void JS_FreeValue_real(JSContext *ctx, JSValue v) { 13 | JS_FreeValue(ctx, v); 14 | } 15 | 16 | void JS_FreeValueRT_real(JSRuntime *rt, JSValue v) { 17 | return JS_FreeValueRT(rt, v); 18 | } 19 | 20 | void JS_DupValue_real(JSContext *ctx, JSValue v) { 21 | JS_DupValue(ctx, v); 22 | } 23 | 24 | JSValue JS_DupValueRT_real(JSRuntime *rt, JSValueConst v) { 25 | return JS_DupValueRT(rt, v); 26 | } 27 | 28 | JSValue JS_NewFloat64_real(JSContext *ctx, double d) { 29 | return JS_NewFloat64(ctx, d); 30 | } 31 | 32 | JSValue JS_NewInt32_real(JSContext *ctx, int32_t val) { 33 | return JS_NewInt32(ctx, val); 34 | } 35 | 36 | JSValue JS_NewBool_real(JSContext *ctx, JS_BOOL val) { 37 | return JS_NewBool(ctx, val) ; 38 | } 39 | 40 | JS_BOOL JS_VALUE_IS_NAN_real(JSValue v) { 41 | return JS_VALUE_IS_NAN(v); 42 | } 43 | 44 | double JS_VALUE_GET_FLOAT64_real(JSValue v) { 45 | return JS_VALUE_GET_FLOAT64(v); 46 | } 47 | 48 | int JS_VALUE_GET_NORM_TAG_real(JSValue v) { 49 | return JS_VALUE_GET_NORM_TAG(v); 50 | } 51 | 52 | JS_BOOL JS_IsNumber_real(JSValueConst v) { 53 | return JS_IsNumber(v); 54 | } 55 | 56 | JS_BOOL JS_IsBigInt_real(JSContext *ctx, JSValueConst v) { 57 | return JS_IsBigInt(ctx, v); 58 | } 59 | 60 | JS_BOOL JS_IsBigFloat_real(JSValueConst v) { 61 | return JS_IsBigFloat(v); 62 | } 63 | 64 | JS_BOOL JS_IsBigDecimal_real(JSValueConst v) { 65 | return JS_IsBigDecimal(v); 66 | } 67 | 68 | JS_BOOL JS_IsBool_real(JSValueConst v) { 69 | return JS_IsBool(v); 70 | } 71 | 72 | JS_BOOL JS_IsNull_real(JSValueConst v) { 73 | return JS_IsNull(v); 74 | } 75 | 76 | JS_BOOL JS_IsUndefined_real(JSValueConst v) { 77 | return JS_IsUndefined(v); 78 | } 79 | 80 | JS_BOOL JS_IsException_real(JSValueConst v) { 81 | return JS_IsException(v); 82 | } 83 | 84 | JS_BOOL JS_IsUninitialized_real(JSValueConst v) { 85 | return JS_IsUninitialized(v); 86 | } 87 | 88 | JS_BOOL JS_IsString_real(JSValueConst v) { 89 | return JS_IsString(v); 90 | } 91 | 92 | JS_BOOL JS_IsSymbol_real(JSValueConst v) { 93 | return JS_IsSymbol(v); 94 | } 95 | 96 | JS_BOOL JS_IsObject_real(JSValueConst v) { 97 | return JS_IsObject(v); 98 | } 99 | 100 | int JS_ToUint32_real(JSContext *ctx, uint32_t *pres, JSValueConst val) { 101 | return JS_ToUint32(ctx, pres, val); 102 | } 103 | 104 | int JS_SetProperty_real(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val) { 105 | return JS_SetProperty(ctx, this_obj, prop, val); 106 | } 107 | 108 | JSValue JS_NewCFunction_real(JSContext *ctx, JSCFunction *func, const char *name,int length) { 109 | return JS_NewCFunction(ctx, func, name, length); 110 | } 111 | 112 | JSValue JS_NewCFunctionMagic_real(JSContext *ctx, JSCFunctionMagic *func, const char *name, int length, JSCFunctionEnum cproto, int magic) { 113 | return JS_NewCFunctionMagic(ctx, func, name, length, cproto, magic); 114 | } 115 | -------------------------------------------------------------------------------- /libquickjs-sys/release.toml: -------------------------------------------------------------------------------- 1 | # Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool. 2 | 3 | tag-prefix = "libquickjs-sys-" 4 | -------------------------------------------------------------------------------- /libquickjs-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! FFI Bindings for [quickjs](https://bellard.org/quickjs/), 2 | //! a Javascript engine. 3 | //! See the [quickjs](https://crates.io/crates/quickjs) crate for a high-level 4 | //! wrapper. 5 | 6 | #![allow(non_upper_case_globals)] 7 | #![allow(non_camel_case_types)] 8 | #![allow(non_snake_case)] 9 | 10 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 11 | 12 | // import the functions from static-functions.c 13 | 14 | include!("static-functions.rs"); 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use std::ffi::CStr; 19 | 20 | use super::*; 21 | 22 | // Small sanity test that starts the runtime and evaluates code. 23 | #[test] 24 | fn test_eval() { 25 | unsafe { 26 | let rt = JS_NewRuntime(); 27 | let ctx = JS_NewContext(rt); 28 | 29 | let code_str = "1 + 1\0"; 30 | let code = CStr::from_bytes_with_nul(code_str.as_bytes()).unwrap(); 31 | let script = CStr::from_bytes_with_nul("script\0".as_bytes()).unwrap(); 32 | 33 | let value = JS_Eval( 34 | ctx, 35 | code.as_ptr(), 36 | (code_str.len() - 1) as u64, 37 | script.as_ptr(), 38 | JS_EVAL_TYPE_GLOBAL as i32, 39 | ); 40 | assert_eq!(value.tag, 0); 41 | assert_eq!(value.u.int32, 2); 42 | 43 | JS_DupValue(ctx, value); 44 | JS_FreeValue(ctx, value); 45 | 46 | let ival = JS_NewInt32(ctx, 12); 47 | assert_eq!(ival.tag, 0); 48 | let fval = JS_NewFloat64(ctx, f64::MAX); 49 | assert_eq!(fval.tag, 7); 50 | let bval = JS_NewBool(ctx, true); 51 | assert_eq!(bval.tag, 1); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /libquickjs-sys/src/static-functions.rs: -------------------------------------------------------------------------------- 1 | 2 | extern "C" { 3 | fn JS_ValueGetTag_real(v: JSValue) -> i32; 4 | fn JS_DupValue_real(ctx: *mut JSContext, v: JSValue); 5 | fn JS_DupValueRT_real(rt: *mut JSRuntime, v: JSValue); 6 | fn JS_FreeValue_real(ctx: *mut JSContext, v: JSValue); 7 | fn JS_FreeValueRT_real(rt: *mut JSRuntime, v: JSValue); 8 | fn JS_NewBool_real(ctx: *mut JSContext, v: bool) -> JSValue; 9 | fn JS_NewInt32_real(ctx: *mut JSContext, v: i32) -> JSValue; 10 | fn JS_NewFloat64_real(ctx: *mut JSContext, v: f64) -> JSValue; 11 | fn JS_VALUE_IS_NAN_real(v: JSValue) -> bool; 12 | fn JS_VALUE_GET_FLOAT64_real(v: JSValue) -> f64; 13 | fn JS_VALUE_GET_NORM_TAG_real(v: JSValue) -> ::std::os::raw::c_int; 14 | fn JS_IsNumber_real(v: JSValue) -> bool; 15 | fn JS_IsBigInt_real(ctx: *mut JSContext, v: JSValue) -> bool; 16 | fn JS_IsBigFloat_real(v: JSValue) -> bool; 17 | fn JS_IsBigDecimal_real(v: JSValue) -> bool; 18 | fn JS_IsBool_real(v: JSValue) -> bool; 19 | fn JS_IsNull_real(v: JSValue) -> bool; 20 | fn JS_IsUndefined_real(v: JSValue) -> bool; 21 | fn JS_IsException_real(v: JSValue) -> bool; 22 | fn JS_IsUninitialized_real(v: JSValue) -> bool; 23 | fn JS_IsString_real(v: JSValue) -> bool; 24 | fn JS_IsSymbol_real(v: JSValue) -> bool; 25 | fn JS_IsObject_real(v: JSValue) -> bool; 26 | fn JS_ToUint32_real(ctx: *mut JSContext, pres: u32, val: JSValue) -> u32; 27 | fn JS_SetProperty_real(ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom, val: JSValue) -> ::std::os::raw::c_int; 28 | fn JS_NewCFunction_real(ctx: *mut JSContext, func: *mut JSCFunction, name: *const ::std::os::raw::c_char,length: ::std::os::raw::c_int) -> JSValue; 29 | fn JS_NewCFunctionMagic_real(ctx: *mut JSContext, func: *mut JSCFunctionMagic, name: *const ::std::os::raw::c_char, length: ::std::os::raw::c_int, cproto: JSCFunctionEnum, magic: ::std::os::raw::c_int) -> JSValue; 30 | } 31 | 32 | pub unsafe fn JS_ValueGetTag(v: JSValue) -> i32 { 33 | JS_ValueGetTag_real(v) 34 | } 35 | 36 | /// Increment the refcount of this value 37 | pub unsafe fn JS_DupValue(ctx: *mut JSContext, v: JSValue) { 38 | JS_DupValue_real(ctx, v); 39 | } 40 | 41 | /// Increment the refcount of this value 42 | pub unsafe fn JS_DupValueRT(rt: *mut JSRuntime, v: JSValue) { 43 | JS_DupValueRT_real(rt, v); 44 | } 45 | 46 | /// Decrement the refcount of this value 47 | pub unsafe fn JS_FreeValue(ctx: *mut JSContext, v: JSValue) { 48 | JS_FreeValue_real(ctx, v); 49 | } 50 | 51 | /// Decrement the refcount of this value 52 | pub unsafe fn JS_FreeValueRT(rt: *mut JSRuntime, v: JSValue) { 53 | JS_FreeValueRT_real(rt, v); 54 | } 55 | 56 | /// create a new boolean value 57 | pub unsafe fn JS_NewBool(ctx: *mut JSContext, v: bool) -> JSValue { 58 | JS_NewBool_real(ctx, v) 59 | } 60 | 61 | /// create a new int32 value 62 | pub unsafe fn JS_NewInt32(ctx: *mut JSContext, v: i32) -> JSValue { 63 | JS_NewInt32_real(ctx, v) 64 | } 65 | 66 | /// create a new f64 value, please note that if the passed f64 fits in a i32 this will return a value with flag 0 (i32) 67 | pub unsafe fn JS_NewFloat64(ctx: *mut JSContext, v: f64) -> JSValue { 68 | JS_NewFloat64_real(ctx, v) 69 | } 70 | 71 | /// check if a JSValue is a NaN value 72 | pub unsafe fn JS_VALUE_IS_NAN(v: JSValue) -> bool { 73 | JS_VALUE_IS_NAN_real(v) 74 | } 75 | 76 | /// get a f64 value from a JSValue 77 | pub unsafe fn JS_VALUE_GET_FLOAT64(v: JSValue) -> f64 { 78 | JS_VALUE_GET_FLOAT64_real(v) 79 | } 80 | 81 | /// same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing 82 | pub unsafe fn JS_VALUE_GET_NORM_TAG(v: JSValue) -> ::std::os::raw::c_int { 83 | JS_VALUE_GET_NORM_TAG_real(v) 84 | } 85 | 86 | /// check if a JSValue is a Number 87 | pub unsafe fn JS_IsNumber(v: JSValue) -> bool { 88 | JS_IsNumber_real(v) 89 | } 90 | 91 | /// check if a JSValue is a BigInt 92 | pub unsafe fn JS_IsBigInt(ctx: *mut JSContext, v: JSValue) -> bool { 93 | JS_IsBigInt_real(ctx,v) 94 | } 95 | 96 | /// check if a JSValue is a BigFloat 97 | pub unsafe fn JS_IsBigFloat(v: JSValue) -> bool { 98 | JS_IsBigFloat_real(v) 99 | } 100 | 101 | /// check if a JSValue is a BigDecimal 102 | pub unsafe fn JS_IsBigDecimal(v: JSValue) -> bool { 103 | JS_IsBigDecimal_real(v) 104 | } 105 | 106 | /// check if a JSValue is a Boolean 107 | pub unsafe fn JS_IsBool(v: JSValue) -> bool { 108 | JS_IsBool_real(v) 109 | } 110 | 111 | /// check if a JSValue is null 112 | pub unsafe fn JS_IsNull(v: JSValue) -> bool { 113 | JS_IsNull_real(v) 114 | } 115 | 116 | /// check if a JSValue is Undefined 117 | pub unsafe fn JS_IsUndefined(v: JSValue) -> bool { 118 | JS_IsUndefined_real(v) 119 | } 120 | 121 | /// check if a JSValue is an Exception 122 | pub unsafe fn JS_IsException(v: JSValue) -> bool{ 123 | JS_IsException_real(v) 124 | } 125 | 126 | /// check if a JSValue is initialized 127 | pub unsafe fn JS_IsUninitialized(v: JSValue) -> bool { 128 | JS_IsUninitialized_real(v) 129 | } 130 | 131 | /// check if a JSValue is a String 132 | pub unsafe fn JS_IsString(v: JSValue) -> bool { 133 | JS_IsString_real(v) 134 | } 135 | 136 | /// check if a JSValue is a Symbol 137 | pub unsafe fn JS_IsSymbol(v: JSValue) -> bool { 138 | JS_IsSymbol_real(v) 139 | } 140 | 141 | /// check if a JSValue is an Object 142 | pub unsafe fn JS_IsObject(v: JSValue) -> bool { 143 | JS_IsObject_real(v) 144 | } 145 | 146 | /// get a u32 value from a JSValue 147 | pub unsafe fn JS_ToUint32(ctx: *mut JSContext, pres: u32, val: JSValue) -> u32 { 148 | JS_ToUint32_real(ctx, pres, val) 149 | } 150 | 151 | /// set a property of an object identified by a JSAtom 152 | pub unsafe fn JS_SetProperty(ctx: *mut JSContext, this_obj: JSValue, prop: JSAtom, val: JSValue) -> ::std::os::raw::c_int { 153 | JS_SetProperty_real(ctx, this_obj, prop, val) 154 | } 155 | 156 | /// create a new Function based on a JSCFunction 157 | pub unsafe fn JS_NewCFunction(ctx: *mut JSContext, func: *mut JSCFunction, name: *const ::std::os::raw::c_char,length: ::std::os::raw::c_int) -> JSValue { 158 | JS_NewCFunction_real(ctx, func, name, length) 159 | } 160 | 161 | /// create a new Function based on a JSCFunction 162 | pub unsafe fn JS_NewCFunctionMagic(ctx: *mut JSContext, func: *mut JSCFunctionMagic, name: *const ::std::os::raw::c_char, length: ::std::os::raw::c_int, cproto: JSCFunctionEnum, magic: ::std::os::raw::c_int) -> JSValue { 163 | JS_NewCFunctionMagic_real(ctx, func, name, length, cproto, magic) 164 | } 165 | -------------------------------------------------------------------------------- /libquickjs-sys/wrapper.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | # Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool. 2 | 3 | pre-release-replacements = [ 4 | {file="README.md", search="quick-js = .*", replace="{{crate_name}} = \"{{version}}\""}, 5 | ] 6 | 7 | tag-prefix = "quick-js-" 8 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | 3 | pkgs.mkShell { 4 | buildInputs = with pkgs; [ 5 | gcc 6 | just 7 | rust-bindgen 8 | cargo 9 | cargo-release 10 | ]; 11 | } 12 | -------------------------------------------------------------------------------- /src/bindings/compile.rs: -------------------------------------------------------------------------------- 1 | //! Utils to compile script to bytecode and run script from bytecode 2 | 3 | use crate::ExecutionError; 4 | use libquickjs_sys as q; 5 | use std::os::raw::c_void; 6 | 7 | use super::{make_cstring, value::JsCompiledFunction, ContextWrapper, OwnedJsValue}; 8 | 9 | /// compile a script, will result in a JSValueRef with tag JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE. 10 | /// It can be executed with run_compiled_function(). 11 | pub fn compile<'a>( 12 | context: &'a ContextWrapper, 13 | script: &str, 14 | file_name: &str, 15 | ) -> Result, ExecutionError> { 16 | let filename_c = make_cstring(file_name)?; 17 | let code_c = make_cstring(script)?; 18 | 19 | let value = unsafe { 20 | let v = q::JS_Eval( 21 | context.context, 22 | code_c.as_ptr(), 23 | script.len() as _, 24 | filename_c.as_ptr(), 25 | q::JS_EVAL_FLAG_COMPILE_ONLY as i32, 26 | ); 27 | OwnedJsValue::new(context, v) 28 | }; 29 | 30 | // check for error 31 | context.ensure_no_excpetion()?; 32 | Ok(value) 33 | } 34 | 35 | /// run a compiled function, see compile for an example 36 | pub fn run_compiled_function<'a>( 37 | func: &'a JsCompiledFunction, 38 | ) -> Result, ExecutionError> { 39 | let context = func.as_value().context(); 40 | let value = unsafe { 41 | // NOTE: JS_EvalFunction takes ownership. 42 | // We clone the func and extract the inner JsValue. 43 | let f = func.clone().into_value().extract(); 44 | let v = q::JS_EvalFunction(context.context, f); 45 | OwnedJsValue::new(context, v) 46 | }; 47 | 48 | context.ensure_no_excpetion().map_err(|e| { 49 | if let ExecutionError::Internal(msg) = e { 50 | ExecutionError::Internal(format!("Could not evaluate compiled function: {}", msg)) 51 | } else { 52 | e 53 | } 54 | })?; 55 | 56 | Ok(value) 57 | } 58 | 59 | /// write a function to bytecode 60 | pub fn to_bytecode(context: &ContextWrapper, compiled_func: &JsCompiledFunction) -> Vec { 61 | unsafe { 62 | let mut len = 0; 63 | let raw = q::JS_WriteObject( 64 | context.context, 65 | &mut len, 66 | *compiled_func.as_value().as_inner(), 67 | q::JS_WRITE_OBJ_BYTECODE as i32, 68 | ); 69 | let slice = std::slice::from_raw_parts(raw, len as usize); 70 | let data = slice.to_vec(); 71 | q::js_free(context.context, raw as *mut c_void); 72 | data 73 | } 74 | } 75 | 76 | /// read a compiled function from bytecode, see to_bytecode for an example 77 | pub fn from_bytecode<'a>( 78 | context: &'a ContextWrapper, 79 | bytecode: &[u8], 80 | ) -> Result, ExecutionError> { 81 | assert!(!bytecode.is_empty()); 82 | { 83 | let len = bytecode.len(); 84 | let buf = bytecode.as_ptr(); 85 | let raw = unsafe { 86 | q::JS_ReadObject( 87 | context.context, 88 | buf, 89 | len as _, 90 | q::JS_READ_OBJ_BYTECODE as i32, 91 | ) 92 | }; 93 | 94 | let func_ref = OwnedJsValue::new(context, raw); 95 | if func_ref.is_exception() { 96 | let ex_opt = context.get_exception(); 97 | if let Some(ex) = ex_opt { 98 | Err(ex) 99 | } else { 100 | Err(ExecutionError::Internal( 101 | "from_bytecode failed and could not get exception".to_string(), 102 | )) 103 | } 104 | } else { 105 | Ok(func_ref) 106 | } 107 | } 108 | } 109 | 110 | #[cfg(test)] 111 | pub mod tests { 112 | use super::*; 113 | use crate::JsValue; 114 | 115 | #[test] 116 | fn test_compile_function() { 117 | let ctx = ContextWrapper::new(None).unwrap(); 118 | 119 | let func_res = compile( 120 | &ctx, 121 | "{let a_tb3 = 7; let b_tb3 = 5; a_tb3 * b_tb3;}", 122 | "test_func.es", 123 | ); 124 | let func = func_res 125 | .ok() 126 | .expect("func compile failed") 127 | .try_into_compiled_function() 128 | .unwrap(); 129 | let bytecode: Vec = to_bytecode(&ctx, &func); 130 | drop(func); 131 | assert!(!bytecode.is_empty()); 132 | 133 | let func2_res = from_bytecode(&ctx, &bytecode); 134 | let func2 = func2_res 135 | .ok() 136 | .expect("could not read bytecode") 137 | .try_into_compiled_function() 138 | .unwrap(); 139 | let run_res = run_compiled_function(&func2); 140 | match run_res { 141 | Ok(res) => { 142 | assert_eq!(res.to_value().unwrap(), JsValue::Int(7 * 5)); 143 | } 144 | Err(e) => { 145 | panic!("run failed1: {}", e); 146 | } 147 | } 148 | } 149 | 150 | #[test] 151 | fn test_load_and_eval_compiled_function() { 152 | let ctx = ContextWrapper::new(None).unwrap(); 153 | 154 | let func_res = compile( 155 | &ctx, 156 | "{let a_tb4 = 7; let b_tb4 = 5; a_tb4 * b_tb4;}", 157 | "test_func.es", 158 | ); 159 | let func = func_res 160 | .ok() 161 | .expect("func compile failed") 162 | .try_into_compiled_function() 163 | .unwrap(); 164 | let bytecode: Vec = to_bytecode(&ctx, &func); 165 | drop(func); 166 | assert!(!bytecode.is_empty()); 167 | let func2_res = from_bytecode(&ctx, &bytecode); 168 | let func2 = func2_res 169 | .ok() 170 | .expect("could not read bytecode") 171 | .try_into_compiled_function() 172 | .unwrap(); 173 | let run_res = run_compiled_function(&func2); 174 | 175 | match run_res { 176 | Ok(res) => { 177 | assert_eq!(res.to_value().unwrap(), JsValue::Int(7 * 5)); 178 | } 179 | Err(e) => { 180 | panic!("run failed: {}", e); 181 | } 182 | } 183 | } 184 | 185 | #[test] 186 | fn test_load_compiled_function_fail() { 187 | let ctx = ContextWrapper::new(None).unwrap(); 188 | 189 | let func_res = compile( 190 | &ctx, 191 | "{the changes of me compil1ng a're slim to 0-0}", 192 | "test_func_fail.es", 193 | ); 194 | func_res.err().expect("func compiled unexpectedly"); 195 | } 196 | 197 | #[test] 198 | fn test_compiled_func_bad_eval() { 199 | let ctx = ContextWrapper::new(None).unwrap(); 200 | 201 | let func_res = compile(&ctx, "let abcdef = 1;", "test_func_runfail.es"); 202 | let func = func_res 203 | .ok() 204 | .expect("func compile failed") 205 | .try_into_compiled_function() 206 | .unwrap(); 207 | assert_eq!(1, func.as_value().get_ref_count()); 208 | 209 | let bytecode: Vec = to_bytecode(&ctx, &func); 210 | 211 | assert_eq!(1, func.as_value().get_ref_count()); 212 | 213 | drop(func); 214 | 215 | assert!(!bytecode.is_empty()); 216 | 217 | let func2_res = from_bytecode(&ctx, &bytecode); 218 | let func2 = func2_res 219 | .ok() 220 | .expect("could not read bytecode") 221 | .try_into_compiled_function() 222 | .unwrap(); 223 | 224 | //should fail the second time you run this because abcdef is already defined 225 | 226 | assert_eq!(1, func2.as_value().get_ref_count()); 227 | 228 | let run_res1 = run_compiled_function(&func2) 229 | .ok() 230 | .expect("run 1 failed unexpectedly"); 231 | drop(run_res1); 232 | 233 | assert_eq!(1, func2.as_value().get_ref_count()); 234 | 235 | let _run_res2 = run_compiled_function(&func2) 236 | .err() 237 | .expect("run 2 succeeded unexpectedly"); 238 | 239 | assert_eq!(1, func2.as_value().get_ref_count()); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/bindings/convert.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, os::raw::c_char}; 2 | 3 | use libquickjs_sys as q; 4 | 5 | use crate::{JsValue, ValueError}; 6 | 7 | use super::{droppable_value::DroppableValue, make_cstring}; 8 | 9 | use super::{ 10 | TAG_BOOL, TAG_EXCEPTION, TAG_FLOAT64, TAG_INT, TAG_NULL, TAG_OBJECT, TAG_STRING, TAG_UNDEFINED, 11 | }; 12 | 13 | #[cfg(feature = "bigint")] 14 | use { 15 | super::TAG_BIG_INT, 16 | crate::value::bigint::{BigInt, BigIntOrI64}, 17 | }; 18 | 19 | #[cfg(feature = "chrono")] 20 | fn js_date_constructor(context: *mut q::JSContext) -> q::JSValue { 21 | let global = unsafe { q::JS_GetGlobalObject(context) }; 22 | assert_eq!(global.tag, TAG_OBJECT); 23 | 24 | let date_constructor = unsafe { 25 | q::JS_GetPropertyStr( 26 | context, 27 | global, 28 | std::ffi::CStr::from_bytes_with_nul(b"Date\0") 29 | .unwrap() 30 | .as_ptr(), 31 | ) 32 | }; 33 | assert_eq!(date_constructor.tag, TAG_OBJECT); 34 | unsafe { q::JS_FreeValue(context, global) }; 35 | date_constructor 36 | } 37 | 38 | #[cfg(feature = "bigint")] 39 | fn js_create_bigint_function(context: *mut q::JSContext) -> q::JSValue { 40 | let global = unsafe { q::JS_GetGlobalObject(context) }; 41 | assert_eq!(global.tag, TAG_OBJECT); 42 | 43 | let bigint_function = unsafe { 44 | q::JS_GetPropertyStr( 45 | context, 46 | global, 47 | std::ffi::CStr::from_bytes_with_nul(b"BigInt\0") 48 | .unwrap() 49 | .as_ptr(), 50 | ) 51 | }; 52 | assert_eq!(bigint_function.tag, TAG_OBJECT); 53 | unsafe { q::JS_FreeValue(context, global) }; 54 | bigint_function 55 | } 56 | 57 | /// Serialize a Rust value into a quickjs runtime value. 58 | pub(super) fn serialize_value( 59 | context: *mut q::JSContext, 60 | value: JsValue, 61 | ) -> Result { 62 | let v = match value { 63 | JsValue::Undefined => q::JSValue { 64 | u: q::JSValueUnion { int32: 0 }, 65 | tag: TAG_UNDEFINED, 66 | }, 67 | JsValue::Null => q::JSValue { 68 | u: q::JSValueUnion { int32: 0 }, 69 | tag: TAG_NULL, 70 | }, 71 | JsValue::Bool(flag) => q::JSValue { 72 | u: q::JSValueUnion { 73 | int32: if flag { 1 } else { 0 }, 74 | }, 75 | tag: TAG_BOOL, 76 | }, 77 | JsValue::Int(val) => q::JSValue { 78 | u: q::JSValueUnion { int32: val }, 79 | tag: TAG_INT, 80 | }, 81 | JsValue::Float(val) => q::JSValue { 82 | u: q::JSValueUnion { float64: val }, 83 | tag: TAG_FLOAT64, 84 | }, 85 | JsValue::String(val) => { 86 | let qval = unsafe { 87 | q::JS_NewStringLen(context, val.as_ptr() as *const c_char, val.len() as _) 88 | }; 89 | 90 | if qval.tag == TAG_EXCEPTION { 91 | return Err(ValueError::Internal( 92 | "Could not create string in runtime".into(), 93 | )); 94 | } 95 | 96 | qval 97 | } 98 | JsValue::Array(values) => { 99 | // Allocate a new array in the runtime. 100 | let arr = unsafe { q::JS_NewArray(context) }; 101 | if arr.tag == TAG_EXCEPTION { 102 | return Err(ValueError::Internal( 103 | "Could not create array in runtime".into(), 104 | )); 105 | } 106 | 107 | for (index, value) in values.into_iter().enumerate() { 108 | let qvalue = match serialize_value(context, value) { 109 | Ok(qval) => qval, 110 | Err(e) => { 111 | // Make sure to free the array if a individual element 112 | // fails. 113 | 114 | unsafe { 115 | q::JS_FreeValue(context, arr); 116 | } 117 | 118 | return Err(e); 119 | } 120 | }; 121 | 122 | let ret = unsafe { 123 | q::JS_DefinePropertyValueUint32( 124 | context, 125 | arr, 126 | index as u32, 127 | qvalue, 128 | q::JS_PROP_C_W_E as i32, 129 | ) 130 | }; 131 | if ret < 0 { 132 | // Make sure to free the array if a individual 133 | // element fails. 134 | unsafe { 135 | q::JS_FreeValue(context, arr); 136 | } 137 | return Err(ValueError::Internal( 138 | "Could not append element to array".into(), 139 | )); 140 | } 141 | } 142 | arr 143 | } 144 | JsValue::Object(map) => { 145 | let obj = unsafe { q::JS_NewObject(context) }; 146 | if obj.tag == TAG_EXCEPTION { 147 | return Err(ValueError::Internal("Could not create object".into())); 148 | } 149 | 150 | for (key, value) in map { 151 | let ckey = make_cstring(key)?; 152 | 153 | let qvalue = serialize_value(context, value).map_err(|e| { 154 | // Free the object if a property failed. 155 | unsafe { 156 | q::JS_FreeValue(context, obj); 157 | } 158 | e 159 | })?; 160 | 161 | let ret = unsafe { 162 | q::JS_DefinePropertyValueStr( 163 | context, 164 | obj, 165 | ckey.as_ptr(), 166 | qvalue, 167 | q::JS_PROP_C_W_E as i32, 168 | ) 169 | }; 170 | if ret < 0 { 171 | // Free the object if a property failed. 172 | unsafe { 173 | q::JS_FreeValue(context, obj); 174 | } 175 | return Err(ValueError::Internal( 176 | "Could not add add property to object".into(), 177 | )); 178 | } 179 | } 180 | 181 | obj 182 | } 183 | #[cfg(feature = "chrono")] 184 | JsValue::Date(datetime) => { 185 | let date_constructor = js_date_constructor(context); 186 | 187 | let f = datetime.timestamp_millis() as f64; 188 | 189 | let timestamp = q::JSValue { 190 | u: q::JSValueUnion { float64: f }, 191 | tag: TAG_FLOAT64, 192 | }; 193 | 194 | let mut args = vec![timestamp]; 195 | 196 | let value = unsafe { 197 | q::JS_CallConstructor( 198 | context, 199 | date_constructor, 200 | args.len() as i32, 201 | args.as_mut_ptr(), 202 | ) 203 | }; 204 | unsafe { 205 | q::JS_FreeValue(context, date_constructor); 206 | } 207 | 208 | if value.tag != TAG_OBJECT { 209 | return Err(ValueError::Internal( 210 | "Could not construct Date object".into(), 211 | )); 212 | } 213 | value 214 | } 215 | #[cfg(feature = "bigint")] 216 | JsValue::BigInt(int) => match int.inner { 217 | BigIntOrI64::Int(int) => unsafe { q::JS_NewBigInt64(context, int) }, 218 | BigIntOrI64::BigInt(bigint) => { 219 | let bigint_string = bigint.to_str_radix(10); 220 | let s = unsafe { 221 | q::JS_NewStringLen( 222 | context, 223 | bigint_string.as_ptr() as *const c_char, 224 | bigint_string.len() as q::size_t, 225 | ) 226 | }; 227 | let s = DroppableValue::new(s, |&mut s| unsafe { 228 | q::JS_FreeValue(context, s); 229 | }); 230 | if (*s).tag != TAG_STRING { 231 | return Err(ValueError::Internal( 232 | "Could not construct String object needed to create BigInt object".into(), 233 | )); 234 | } 235 | 236 | let mut args = vec![*s]; 237 | 238 | let bigint_function = js_create_bigint_function(context); 239 | let bigint_function = 240 | DroppableValue::new(bigint_function, |&mut bigint_function| unsafe { 241 | q::JS_FreeValue(context, bigint_function); 242 | }); 243 | let js_bigint = unsafe { 244 | q::JS_Call( 245 | context, 246 | *bigint_function, 247 | q::JSValue { 248 | u: q::JSValueUnion { int32: 0 }, 249 | tag: TAG_NULL, 250 | }, 251 | 1, 252 | args.as_mut_ptr(), 253 | ) 254 | }; 255 | 256 | if js_bigint.tag != TAG_BIG_INT { 257 | return Err(ValueError::Internal( 258 | "Could not construct BigInt object".into(), 259 | )); 260 | } 261 | 262 | js_bigint 263 | } 264 | }, 265 | JsValue::__NonExhaustive => unreachable!(), 266 | }; 267 | Ok(v) 268 | } 269 | 270 | fn deserialize_array( 271 | context: *mut q::JSContext, 272 | raw_value: &q::JSValue, 273 | ) -> Result { 274 | assert_eq!(raw_value.tag, TAG_OBJECT); 275 | 276 | let length_name = make_cstring("length")?; 277 | 278 | let len_raw = unsafe { q::JS_GetPropertyStr(context, *raw_value, length_name.as_ptr()) }; 279 | 280 | let len_res = deserialize_value(context, &len_raw); 281 | unsafe { q::JS_FreeValue(context, len_raw) }; 282 | let len = match len_res? { 283 | JsValue::Int(x) => x, 284 | _ => { 285 | return Err(ValueError::Internal( 286 | "Could not determine array length".into(), 287 | )); 288 | } 289 | }; 290 | 291 | let mut values = Vec::new(); 292 | for index in 0..(len as usize) { 293 | let value_raw = unsafe { q::JS_GetPropertyUint32(context, *raw_value, index as u32) }; 294 | if value_raw.tag == TAG_EXCEPTION { 295 | return Err(ValueError::Internal("Could not build array".into())); 296 | } 297 | let value_res = deserialize_value(context, &value_raw); 298 | unsafe { q::JS_FreeValue(context, value_raw) }; 299 | 300 | let value = value_res?; 301 | values.push(value); 302 | } 303 | 304 | Ok(JsValue::Array(values)) 305 | } 306 | 307 | fn deserialize_object(context: *mut q::JSContext, obj: &q::JSValue) -> Result { 308 | assert_eq!(obj.tag, TAG_OBJECT); 309 | 310 | let mut properties: *mut q::JSPropertyEnum = std::ptr::null_mut(); 311 | let mut count: u32 = 0; 312 | 313 | let flags = (q::JS_GPN_STRING_MASK | q::JS_GPN_SYMBOL_MASK | q::JS_GPN_ENUM_ONLY) as i32; 314 | let ret = 315 | unsafe { q::JS_GetOwnPropertyNames(context, &mut properties, &mut count, *obj, flags) }; 316 | if ret != 0 { 317 | return Err(ValueError::Internal( 318 | "Could not get object properties".into(), 319 | )); 320 | } 321 | 322 | // TODO: refactor into a more Rust-idiomatic iterator wrapper. 323 | let properties = DroppableValue::new(properties, |&mut properties| { 324 | for index in 0..count { 325 | let prop = unsafe { properties.offset(index as isize) }; 326 | unsafe { 327 | q::JS_FreeAtom(context, (*prop).atom); 328 | } 329 | } 330 | unsafe { 331 | q::js_free(context, properties as *mut std::ffi::c_void); 332 | } 333 | }); 334 | 335 | let mut map = HashMap::new(); 336 | for index in 0..count { 337 | let prop = unsafe { (*properties).offset(index as isize) }; 338 | let raw_value = unsafe { q::JS_GetPropertyInternal(context, *obj, (*prop).atom, *obj, 0) }; 339 | if raw_value.tag == TAG_EXCEPTION { 340 | return Err(ValueError::Internal("Could not get object property".into())); 341 | } 342 | 343 | let value_res = deserialize_value(context, &raw_value); 344 | unsafe { 345 | q::JS_FreeValue(context, raw_value); 346 | } 347 | let value = value_res?; 348 | 349 | let key_value = unsafe { q::JS_AtomToString(context, (*prop).atom) }; 350 | if key_value.tag == TAG_EXCEPTION { 351 | return Err(ValueError::Internal( 352 | "Could not get object property name".into(), 353 | )); 354 | } 355 | 356 | let key_res = deserialize_value(context, &key_value); 357 | unsafe { 358 | q::JS_FreeValue(context, key_value); 359 | } 360 | let key = match key_res? { 361 | JsValue::String(s) => s, 362 | _ => { 363 | return Err(ValueError::Internal("Could not get property name".into())); 364 | } 365 | }; 366 | map.insert(key, value); 367 | } 368 | 369 | Ok(JsValue::Object(map)) 370 | } 371 | 372 | pub(super) fn deserialize_value( 373 | context: *mut q::JSContext, 374 | value: &q::JSValue, 375 | ) -> Result { 376 | let r = value; 377 | 378 | match r.tag { 379 | // Int. 380 | TAG_INT => { 381 | let val = unsafe { r.u.int32 }; 382 | Ok(JsValue::Int(val)) 383 | } 384 | // Bool. 385 | TAG_BOOL => { 386 | let raw = unsafe { r.u.int32 }; 387 | let val = raw > 0; 388 | Ok(JsValue::Bool(val)) 389 | } 390 | // Null. 391 | TAG_NULL => Ok(JsValue::Null), 392 | // Undefined. 393 | TAG_UNDEFINED => Ok(JsValue::Undefined), 394 | // Float. 395 | TAG_FLOAT64 => { 396 | let val = unsafe { r.u.float64 }; 397 | Ok(JsValue::Float(val)) 398 | } 399 | // String. 400 | TAG_STRING => { 401 | let ptr = unsafe { q::JS_ToCStringLen2(context, std::ptr::null_mut(), *r, 0) }; 402 | 403 | if ptr.is_null() { 404 | return Err(ValueError::Internal( 405 | "Could not convert string: got a null pointer".into(), 406 | )); 407 | } 408 | 409 | let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; 410 | 411 | let s = cstr 412 | .to_str() 413 | .map_err(ValueError::InvalidString)? 414 | .to_string(); 415 | 416 | // Free the c string. 417 | unsafe { q::JS_FreeCString(context, ptr) }; 418 | 419 | Ok(JsValue::String(s)) 420 | } 421 | // Object. 422 | TAG_OBJECT => { 423 | let is_array = unsafe { q::JS_IsArray(context, *r) } > 0; 424 | if is_array { 425 | deserialize_array(context, r) 426 | } else { 427 | #[cfg(feature = "chrono")] 428 | { 429 | use chrono::offset::TimeZone; 430 | 431 | let date_constructor = js_date_constructor(context); 432 | let is_date = unsafe { q::JS_IsInstanceOf(context, *r, date_constructor) > 0 }; 433 | 434 | if is_date { 435 | let getter = unsafe { 436 | q::JS_GetPropertyStr( 437 | context, 438 | *r, 439 | std::ffi::CStr::from_bytes_with_nul(b"getTime\0") 440 | .unwrap() 441 | .as_ptr(), 442 | ) 443 | }; 444 | assert_eq!(getter.tag, TAG_OBJECT); 445 | 446 | let timestamp_raw = 447 | unsafe { q::JS_Call(context, getter, *r, 0, std::ptr::null_mut()) }; 448 | 449 | unsafe { 450 | q::JS_FreeValue(context, getter); 451 | q::JS_FreeValue(context, date_constructor); 452 | }; 453 | 454 | let res = if timestamp_raw.tag == TAG_FLOAT64 { 455 | let f = unsafe { timestamp_raw.u.float64 } as i64; 456 | let datetime = chrono::Utc.timestamp_millis(f); 457 | Ok(JsValue::Date(datetime)) 458 | } else if timestamp_raw.tag == TAG_INT { 459 | let f = unsafe { timestamp_raw.u.int32 } as i64; 460 | let datetime = chrono::Utc.timestamp_millis(f); 461 | Ok(JsValue::Date(datetime)) 462 | } else { 463 | Err(ValueError::Internal( 464 | "Could not convert 'Date' instance to timestamp".into(), 465 | )) 466 | }; 467 | return res; 468 | } else { 469 | unsafe { q::JS_FreeValue(context, date_constructor) }; 470 | } 471 | } 472 | 473 | deserialize_object(context, r) 474 | } 475 | } 476 | // BigInt 477 | #[cfg(feature = "bigint")] 478 | TAG_BIG_INT => { 479 | let mut int: i64 = 0; 480 | let ret = unsafe { q::JS_ToBigInt64(context, &mut int, *r) }; 481 | if ret == 0 { 482 | Ok(JsValue::BigInt(BigInt { 483 | inner: BigIntOrI64::Int(int), 484 | })) 485 | } else { 486 | let ptr = unsafe { q::JS_ToCStringLen2(context, std::ptr::null_mut(), *r, 0) }; 487 | 488 | if ptr.is_null() { 489 | return Err(ValueError::Internal( 490 | "Could not convert BigInt to string: got a null pointer".into(), 491 | )); 492 | } 493 | 494 | let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; 495 | let bigint = num_bigint::BigInt::parse_bytes(cstr.to_bytes(), 10).unwrap(); 496 | 497 | // Free the c string. 498 | unsafe { q::JS_FreeCString(context, ptr) }; 499 | 500 | Ok(JsValue::BigInt(BigInt { 501 | inner: BigIntOrI64::BigInt(bigint), 502 | })) 503 | } 504 | } 505 | x => Err(ValueError::Internal(format!( 506 | "Unhandled JS_TAG value: {}", 507 | x 508 | ))), 509 | } 510 | } 511 | -------------------------------------------------------------------------------- /src/bindings/droppable_value.rs: -------------------------------------------------------------------------------- 1 | /// A small wrapper that frees resources that have to be freed 2 | /// automatically when they go out of scope. 3 | pub struct DroppableValue 4 | where 5 | F: FnMut(&mut T), 6 | { 7 | value: T, 8 | drop_fn: F, 9 | } 10 | 11 | impl DroppableValue 12 | where 13 | F: FnMut(&mut T), 14 | { 15 | pub fn new(value: T, drop_fn: F) -> Self { 16 | Self { value, drop_fn } 17 | } 18 | } 19 | 20 | impl Drop for DroppableValue 21 | where 22 | F: FnMut(&mut T), 23 | { 24 | fn drop(&mut self) { 25 | (self.drop_fn)(&mut self.value); 26 | } 27 | } 28 | 29 | impl std::ops::Deref for DroppableValue 30 | where 31 | F: FnMut(&mut T), 32 | { 33 | type Target = T; 34 | 35 | fn deref(&self) -> &T { 36 | &self.value 37 | } 38 | } 39 | 40 | impl std::ops::DerefMut for DroppableValue 41 | where 42 | F: FnMut(&mut T), 43 | { 44 | fn deref_mut(&mut self) -> &mut T { 45 | &mut self.value 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/bindings/serialize.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theduke/quickjs-rs/941b3610e7d8e95de99369c9935a79fbbab0a7d3/src/bindings/serialize.rs -------------------------------------------------------------------------------- /src/callback.rs: -------------------------------------------------------------------------------- 1 | use std::{convert::TryFrom, marker::PhantomData, panic::RefUnwindSafe}; 2 | 3 | use crate::value::{JsValue, ValueError}; 4 | 5 | pub trait IntoCallbackResult { 6 | fn into_callback_res(self) -> Result; 7 | } 8 | 9 | impl> IntoCallbackResult for T { 10 | fn into_callback_res(self) -> Result { 11 | Ok(self.into()) 12 | } 13 | } 14 | 15 | impl, E: std::fmt::Display> IntoCallbackResult for Result { 16 | fn into_callback_res(self) -> Result { 17 | match self { 18 | Ok(v) => Ok(v.into()), 19 | Err(e) => Err(e.to_string()), 20 | } 21 | } 22 | } 23 | 24 | /// The Callback trait is implemented for functions/closures that can be 25 | /// used as callbacks in the JS runtime. 26 | pub trait Callback: RefUnwindSafe { 27 | /// Returns the number of required Javascript arguments. 28 | fn argument_count(&self) -> usize; 29 | 30 | /// Execute the callback. 31 | /// 32 | /// Should return: 33 | /// - Err(_) if the JS values could not be converted 34 | /// - Ok(Err(_)) if an error ocurred while processing. 35 | /// The given error will be raised as a JS exception. 36 | /// - Ok(Ok(result)) when execution succeeded. 37 | fn call(&self, args: Vec) -> Result, ValueError>; 38 | } 39 | 40 | macro_rules! impl_callback { 41 | (@call $len:literal $self:ident $args:ident ) => { 42 | $self() 43 | }; 44 | 45 | (@call $len:literal $self:ident $args:ident $( $arg:ident ),* ) => { 46 | { 47 | let mut iter = $args.into_iter(); 48 | $self( 49 | $( 50 | $arg::try_from(iter.next().unwrap())?, 51 | )* 52 | ) 53 | } 54 | }; 55 | 56 | [ $( $len:literal : ( $( $arg:ident, )* ), )* ] => { 57 | $( 58 | 59 | impl< 60 | $( $arg, )* 61 | E, 62 | R, 63 | F, 64 | > Callback> for F 70 | where 71 | $( $arg: TryFrom, )* 72 | ValueError: From, 73 | R: IntoCallbackResult, 74 | F: Fn( $( $arg, )* ) -> R + Sized + RefUnwindSafe, 75 | { 76 | fn argument_count(&self) -> usize { 77 | $len 78 | } 79 | 80 | fn call(&self, args: Vec) -> Result, ValueError> { 81 | if args.len() != $len { 82 | return Ok(Err(format!( 83 | "Invalid argument count: Expected {}, got {}", 84 | self.argument_count(), 85 | args.len() 86 | ))); 87 | } 88 | 89 | let res = impl_callback!(@call $len self args $($arg),* ); 90 | Ok(res.into_callback_res()) 91 | } 92 | } 93 | )* 94 | }; 95 | } 96 | 97 | impl Callback> for F 98 | where 99 | R: IntoCallbackResult, 100 | F: Fn() -> R + Sized + RefUnwindSafe, 101 | { 102 | fn argument_count(&self) -> usize { 103 | 0 104 | } 105 | 106 | fn call(&self, args: Vec) -> Result, ValueError> { 107 | if args.len() != 0 { 108 | return Ok(Err(format!( 109 | "Invalid argument count: Expected 0, got {}", 110 | args.len(), 111 | ))); 112 | } 113 | 114 | let res = self(); 115 | Ok(res.into_callback_res()) 116 | } 117 | } 118 | 119 | impl_callback![ 120 | 1: (A1,), 121 | 2: (A1, A2,), 122 | 3: (A1, A2, A3,), 123 | 4: (A1, A2, A3, A4,), 124 | 5: (A1, A2, A3, A4, A5,), 125 | ]; 126 | 127 | /// A wrapper around Vec, used for vararg callbacks. 128 | /// 129 | /// To create a callback with a variable number of arguments, a callback closure 130 | /// must take a single `Arguments` argument. 131 | pub struct Arguments(Vec); 132 | 133 | impl Arguments { 134 | /// Unpack the arguments into a Vec. 135 | pub fn into_vec(self) -> Vec { 136 | self.0 137 | } 138 | } 139 | 140 | impl Callback> for F 141 | where 142 | F: Fn(Arguments) + Sized + RefUnwindSafe, 143 | { 144 | fn argument_count(&self) -> usize { 145 | 0 146 | } 147 | 148 | fn call(&self, args: Vec) -> Result, ValueError> { 149 | (self)(Arguments(args)); 150 | Ok(Ok(JsValue::Undefined)) 151 | } 152 | } 153 | 154 | impl Callback> for F 155 | where 156 | R: IntoCallbackResult, 157 | F: Fn(Arguments) -> R + Sized + RefUnwindSafe, 158 | { 159 | fn argument_count(&self) -> usize { 160 | 0 161 | } 162 | 163 | fn call(&self, args: Vec) -> Result, ValueError> { 164 | let res = (self)(Arguments(args)); 165 | Ok(res.into_callback_res()) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | //! Javascript console integration. 2 | //! See the [ConsoleBackend] trait for more info. 3 | 4 | use super::JsValue; 5 | 6 | /// Log level of a log message sent via the console. 7 | /// These levels represent the different functions defined in the spec: 8 | /// 9 | #[allow(missing_docs)] 10 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 11 | pub enum Level { 12 | Trace, 13 | Debug, 14 | Log, 15 | Info, 16 | Warn, 17 | Error, 18 | } 19 | 20 | impl std::fmt::Display for Level { 21 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 22 | use Level::*; 23 | let v = match self { 24 | Trace => "trace", 25 | Debug => "debug", 26 | Log => "log", 27 | Info => "info", 28 | Warn => "warn", 29 | Error => "error", 30 | }; 31 | write!(f, "{}", v) 32 | } 33 | } 34 | 35 | /// A console backend that handles console messages sent from JS via 36 | /// console.{log,debug,trace,...} functions. 37 | /// 38 | /// A backend has to be registered via the `ContextBuilder::console` method. 39 | /// 40 | /// A backend that forwads to the `log` crate is available with the `log` feature. 41 | /// 42 | /// Note that any closure of type `Fn(Level, Vec)` implements this trait. 43 | /// 44 | /// A very simple logger that just prints to stderr could look like this: 45 | /// 46 | /// ```rust 47 | /// use quick_js::{Context, JsValue, console::Level}; 48 | /// 49 | /// Context::builder() 50 | /// .console(|level: Level, args: Vec| { 51 | /// eprintln!("{}: {:?}", level, args); 52 | /// }) 53 | /// .build() 54 | /// # .unwrap(); 55 | /// ``` 56 | /// 57 | pub trait ConsoleBackend: std::panic::RefUnwindSafe + 'static { 58 | /// Handle a log message. 59 | fn log(&self, level: Level, values: Vec); 60 | } 61 | 62 | impl ConsoleBackend for F 63 | where 64 | F: Fn(Level, Vec) + std::panic::RefUnwindSafe + 'static, 65 | { 66 | fn log(&self, level: Level, values: Vec) { 67 | (self)(level, values); 68 | } 69 | } 70 | 71 | #[cfg(feature = "log")] 72 | mod log { 73 | use super::{JsValue, Level}; 74 | 75 | /// A console implementation that logs messages via the `log` crate. 76 | /// 77 | /// Only available with the `log` feature. 78 | pub struct LogConsole; 79 | 80 | fn print_value(value: JsValue) -> String { 81 | match value { 82 | JsValue::Undefined => "undefined".to_string(), 83 | JsValue::Null => "null".to_string(), 84 | JsValue::Bool(v) => v.to_string(), 85 | JsValue::Int(v) => v.to_string(), 86 | JsValue::Float(v) => v.to_string(), 87 | JsValue::String(v) => v, 88 | JsValue::Array(values) => { 89 | let parts = values 90 | .into_iter() 91 | .map(print_value) 92 | .collect::>() 93 | .join(", "); 94 | format!("[{}]", parts) 95 | } 96 | JsValue::Object(map) => { 97 | let parts = map 98 | .into_iter() 99 | .map(|(key, value)| format!("{}: {}", key, print_value(value))) 100 | .collect::>() 101 | .join(", "); 102 | format!("{{{}}}", parts) 103 | } 104 | #[cfg(feature = "chrono")] 105 | JsValue::Date(v) => v.to_string(), 106 | #[cfg(feature = "bigint")] 107 | JsValue::BigInt(v) => v.to_string(), 108 | JsValue::__NonExhaustive => unreachable!(), 109 | } 110 | } 111 | 112 | impl super::ConsoleBackend for LogConsole { 113 | fn log(&self, level: Level, values: Vec) { 114 | if values.is_empty() { 115 | return; 116 | } 117 | let log_level = match level { 118 | Level::Trace => log::Level::Trace, 119 | Level::Debug => log::Level::Debug, 120 | Level::Log => log::Level::Info, 121 | Level::Info => log::Level::Info, 122 | Level::Warn => log::Level::Warn, 123 | Level::Error => log::Level::Error, 124 | }; 125 | 126 | let msg = values 127 | .into_iter() 128 | .map(print_value) 129 | .collect::>() 130 | .join(" "); 131 | 132 | log::log!(log_level, "{}", msg); 133 | } 134 | } 135 | } 136 | 137 | #[cfg(feature = "log")] 138 | pub use self::log::LogConsole; 139 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! quick-js is a a Rust wrapper for [QuickJS](https://bellard.org/quickjs/), a new Javascript 2 | //! engine by Fabrice Bellard. 3 | //! 4 | //! It enables easy and straight-forward execution of modern Javascript from Rust. 5 | //! 6 | //! ## Limitations 7 | //! 8 | //! * Building on Windows requires the `x86_64-pc-windows-gnu` toolchain 9 | //! 10 | //! ## Quickstart: 11 | //! 12 | //! ```rust 13 | //! use quick_js::{Context, JsValue}; 14 | //! 15 | //! let context = Context::new().unwrap(); 16 | //! 17 | //! // Eval. 18 | //! 19 | //! let value = context.eval("1 + 2").unwrap(); 20 | //! assert_eq!(value, JsValue::Int(3)); 21 | //! 22 | //! let value = context.eval_as::(" var x = 100 + 250; x.toString() ").unwrap(); 23 | //! assert_eq!(&value, "350"); 24 | //! 25 | //! // Callbacks. 26 | //! 27 | //! context.add_callback("myCallback", |a: i32, b: i32| a + b).unwrap(); 28 | //! 29 | //! context.eval(r#" 30 | //! // x will equal 30 31 | //! var x = myCallback(10, 20); 32 | //! "#).unwrap(); 33 | //! ``` 34 | 35 | #![deny(missing_docs)] 36 | 37 | mod bindings; 38 | mod callback; 39 | pub mod console; 40 | mod value; 41 | 42 | #[cfg(test)] 43 | mod tests; 44 | 45 | use std::{convert::TryFrom, error, fmt}; 46 | 47 | pub use self::{ 48 | callback::{Arguments, Callback}, 49 | value::*, 50 | }; 51 | 52 | /// Error on Javascript execution. 53 | #[derive(PartialEq, Debug)] 54 | pub enum ExecutionError { 55 | /// Code to be executed contained zero-bytes. 56 | InputWithZeroBytes, 57 | /// Value conversion failed. (either input arguments or result value). 58 | Conversion(ValueError), 59 | /// Internal error. 60 | Internal(String), 61 | /// JS Exception was thrown. 62 | Exception(JsValue), 63 | /// JS Runtime exceeded the memory limit. 64 | OutOfMemory, 65 | #[doc(hidden)] 66 | __NonExhaustive, 67 | } 68 | 69 | impl fmt::Display for ExecutionError { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | use ExecutionError::*; 72 | match self { 73 | InputWithZeroBytes => write!(f, "Invalid script input: code contains zero byte (\\0)"), 74 | Conversion(e) => e.fmt(f), 75 | Internal(e) => write!(f, "Internal error: {}", e), 76 | Exception(e) => write!(f, "{:?}", e), 77 | OutOfMemory => write!(f, "Out of memory: runtime memory limit exceeded"), 78 | __NonExhaustive => unreachable!(), 79 | } 80 | } 81 | } 82 | 83 | impl error::Error for ExecutionError {} 84 | 85 | impl From for ExecutionError { 86 | fn from(v: ValueError) -> Self { 87 | ExecutionError::Conversion(v) 88 | } 89 | } 90 | 91 | /// Error on context creation. 92 | #[derive(Debug)] 93 | pub enum ContextError { 94 | /// Runtime could not be created. 95 | RuntimeCreationFailed, 96 | /// Context could not be created. 97 | ContextCreationFailed, 98 | /// Execution error while building. 99 | Execution(ExecutionError), 100 | #[doc(hidden)] 101 | __NonExhaustive, 102 | } 103 | 104 | impl fmt::Display for ContextError { 105 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 106 | use ContextError::*; 107 | match self { 108 | RuntimeCreationFailed => write!(f, "Could not create runtime"), 109 | ContextCreationFailed => write!(f, "Could not create context"), 110 | Execution(e) => e.fmt(f), 111 | __NonExhaustive => unreachable!(), 112 | } 113 | } 114 | } 115 | 116 | impl error::Error for ContextError {} 117 | 118 | /// A builder for [Context](Context). 119 | /// 120 | /// Create with [Context::builder](Context::builder). 121 | pub struct ContextBuilder { 122 | memory_limit: Option, 123 | console_backend: Option>, 124 | } 125 | 126 | impl ContextBuilder { 127 | fn new() -> Self { 128 | Self { 129 | memory_limit: None, 130 | console_backend: None, 131 | } 132 | } 133 | 134 | /// Sets the memory limit of the Javascript runtime (in bytes). 135 | /// 136 | /// If the limit is exceeded, methods like `eval` will return 137 | /// a `Err(ExecutionError::Exception(JsValue::Null))` 138 | // TODO: investigate why we don't get a proper exception message here. 139 | pub fn memory_limit(self, max_bytes: usize) -> Self { 140 | let mut s = self; 141 | s.memory_limit = Some(max_bytes); 142 | s 143 | } 144 | 145 | /// Set a console handler that will proxy `console.{log,trace,debug,...}` 146 | /// calls. 147 | /// 148 | /// The given argument must implement the [console::ConsoleBackend] trait. 149 | /// 150 | /// A very simple logger could look like this: 151 | pub fn console(mut self, backend: B) -> Self 152 | where 153 | B: console::ConsoleBackend, 154 | { 155 | self.console_backend = Some(Box::new(backend)); 156 | self 157 | } 158 | 159 | /// Finalize the builder and build a JS Context. 160 | pub fn build(self) -> Result { 161 | let wrapper = bindings::ContextWrapper::new(self.memory_limit)?; 162 | if let Some(be) = self.console_backend { 163 | wrapper.set_console(be).map_err(ContextError::Execution)?; 164 | } 165 | Ok(Context::from_wrapper(wrapper)) 166 | } 167 | } 168 | 169 | /// Context is a wrapper around a QuickJS Javascript context. 170 | /// It is the primary way to interact with the runtime. 171 | /// 172 | /// For each `Context` instance a new instance of QuickJS 173 | /// runtime is created. It means that it is safe to use 174 | /// different contexts in different threads, but each 175 | /// `Context` instance must be used only from a single thread. 176 | pub struct Context { 177 | wrapper: bindings::ContextWrapper, 178 | } 179 | 180 | impl Context { 181 | fn from_wrapper(wrapper: bindings::ContextWrapper) -> Self { 182 | Self { wrapper } 183 | } 184 | 185 | /// Create a `ContextBuilder` that allows customization of JS Runtime settings. 186 | /// 187 | /// For details, see the methods on `ContextBuilder`. 188 | /// 189 | /// ```rust 190 | /// let _context = quick_js::Context::builder() 191 | /// .memory_limit(100_000) 192 | /// .build() 193 | /// .unwrap(); 194 | /// ``` 195 | pub fn builder() -> ContextBuilder { 196 | ContextBuilder::new() 197 | } 198 | 199 | /// Create a new Javascript context with default settings. 200 | pub fn new() -> Result { 201 | let wrapper = bindings::ContextWrapper::new(None)?; 202 | Ok(Self::from_wrapper(wrapper)) 203 | } 204 | 205 | /// Reset the Javascript engine. 206 | /// 207 | /// All state and callbacks will be removed. 208 | pub fn reset(self) -> Result { 209 | let wrapper = self.wrapper.reset()?; 210 | Ok(Self { wrapper }) 211 | } 212 | 213 | /// Evaluates Javascript code and returns the value of the final expression. 214 | /// 215 | /// **Promises**: 216 | /// If the evaluated code returns a Promise, the event loop 217 | /// will be executed until the promise is finished. The final value of 218 | /// the promise will be returned, or a `ExecutionError::Exception` if the 219 | /// promise failed. 220 | /// 221 | /// ```rust 222 | /// use quick_js::{Context, JsValue}; 223 | /// let context = Context::new().unwrap(); 224 | /// 225 | /// let value = context.eval(" 1 + 2 + 3 "); 226 | /// assert_eq!( 227 | /// value, 228 | /// Ok(JsValue::Int(6)), 229 | /// ); 230 | /// 231 | /// let value = context.eval(r#" 232 | /// function f() { return 55 * 3; } 233 | /// let y = f(); 234 | /// var x = y.toString() + "!" 235 | /// x 236 | /// "#); 237 | /// assert_eq!( 238 | /// value, 239 | /// Ok(JsValue::String("165!".to_string())), 240 | /// ); 241 | /// ``` 242 | pub fn eval(&self, code: &str) -> Result { 243 | let value_raw = self.wrapper.eval(code)?; 244 | let value = value_raw.to_value()?; 245 | Ok(value) 246 | } 247 | 248 | /// Evaluates Javascript code and returns the value of the final expression 249 | /// as a Rust type. 250 | /// 251 | /// **Promises**: 252 | /// If the evaluated code returns a Promise, the event loop 253 | /// will be executed until the promise is finished. The final value of 254 | /// the promise will be returned, or a `ExecutionError::Exception` if the 255 | /// promise failed. 256 | /// 257 | /// ```rust 258 | /// use quick_js::{Context}; 259 | /// let context = Context::new().unwrap(); 260 | /// 261 | /// let res = context.eval_as::(" 100 > 10 "); 262 | /// assert_eq!( 263 | /// res, 264 | /// Ok(true), 265 | /// ); 266 | /// 267 | /// let value: i32 = context.eval_as(" 10 + 10 ").unwrap(); 268 | /// assert_eq!( 269 | /// value, 270 | /// 20, 271 | /// ); 272 | /// ``` 273 | pub fn eval_as(&self, code: &str) -> Result 274 | where 275 | R: TryFrom, 276 | R::Error: Into, 277 | { 278 | let value_raw = self.wrapper.eval(code)?; 279 | let value = value_raw.to_value()?; 280 | let ret = R::try_from(value).map_err(|e| e.into())?; 281 | Ok(ret) 282 | } 283 | 284 | /// Set a global variable. 285 | /// 286 | /// ```rust 287 | /// use quick_js::{Context, JsValue}; 288 | /// let context = Context::new().unwrap(); 289 | /// 290 | /// context.set_global("someGlobalVariable", 42).unwrap(); 291 | /// let value = context.eval_as::("someGlobalVariable").unwrap(); 292 | /// assert_eq!( 293 | /// value, 294 | /// 42, 295 | /// ); 296 | /// ``` 297 | pub fn set_global(&self, name: &str, value: V) -> Result<(), ExecutionError> 298 | where 299 | V: Into, 300 | { 301 | let global = self.wrapper.global()?; 302 | let v = self.wrapper.serialize_value(value.into())?; 303 | global.set_property(name, v)?; 304 | Ok(()) 305 | } 306 | 307 | /// Call a global function in the Javascript namespace. 308 | /// 309 | /// **Promises**: 310 | /// If the evaluated code returns a Promise, the event loop 311 | /// will be executed until the promise is finished. The final value of 312 | /// the promise will be returned, or a `ExecutionError::Exception` if the 313 | /// promise failed. 314 | /// 315 | /// ```rust 316 | /// use quick_js::{Context, JsValue}; 317 | /// let context = Context::new().unwrap(); 318 | /// 319 | /// let res = context.call_function("encodeURIComponent", vec!["a=b"]); 320 | /// assert_eq!( 321 | /// res, 322 | /// Ok(JsValue::String("a%3Db".to_string())), 323 | /// ); 324 | /// ``` 325 | pub fn call_function( 326 | &self, 327 | function_name: &str, 328 | args: impl IntoIterator>, 329 | ) -> Result { 330 | let qargs = args 331 | .into_iter() 332 | .map(|arg| self.wrapper.serialize_value(arg.into())) 333 | .collect::, _>>()?; 334 | 335 | let global = self.wrapper.global()?; 336 | let func = global 337 | .property_require(function_name)? 338 | .try_into_function()?; 339 | let v = self.wrapper.call_function(func, qargs)?.to_value()?; 340 | Ok(v) 341 | } 342 | 343 | /// Add a global JS function that is backed by a Rust function or closure. 344 | /// 345 | /// The callback must satisfy several requirements: 346 | /// * accepts 0 - 5 arguments 347 | /// * each argument must be convertible from a JsValue 348 | /// * must return a value 349 | /// * the return value must either: 350 | /// - be convertible to JsValue 351 | /// - be a Result where T is convertible to JsValue 352 | /// if Err(e) is returned, a Javascript exception will be raised 353 | /// 354 | /// ```rust 355 | /// use quick_js::{Context, JsValue}; 356 | /// let context = Context::new().unwrap(); 357 | /// 358 | /// // Register a closue as a callback under the "add" name. 359 | /// // The 'add' function can now be called from Javascript code. 360 | /// context.add_callback("add", |a: i32, b: i32| { a + b }).unwrap(); 361 | /// 362 | /// // Now we try out the 'add' function via eval. 363 | /// let output = context.eval_as::(" add( 3 , 4 ) ").unwrap(); 364 | /// assert_eq!( 365 | /// output, 366 | /// 7, 367 | /// ); 368 | /// ``` 369 | pub fn add_callback( 370 | &self, 371 | name: &str, 372 | callback: impl Callback + 'static, 373 | ) -> Result<(), ExecutionError> { 374 | self.wrapper.add_callback(name, callback) 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::*; 4 | 5 | // #[test] 6 | // fn test_global_properties() { 7 | // let c = Context::new().unwrap(); 8 | 9 | // assert_eq!( 10 | // c.global_property("lala"), 11 | // Err(ExecutionError::Exception( 12 | // "Global object does not have property 'lala'".into() 13 | // )) 14 | // ); 15 | 16 | // c.set_global_property("testprop", true).unwrap(); 17 | // assert_eq!( 18 | // c.global_property("testprop").unwrap(), 19 | // JsValue::Bool(true), 20 | // ); 21 | // } 22 | 23 | #[test] 24 | fn test_eval_pass() { 25 | use std::iter::FromIterator; 26 | 27 | let c = Context::new().unwrap(); 28 | 29 | let cases = vec![ 30 | ("undefined", Ok(JsValue::Undefined)), 31 | ("null", Ok(JsValue::Null)), 32 | ("true", Ok(JsValue::Bool(true))), 33 | ("2 > 10", Ok(JsValue::Bool(false))), 34 | ("1", Ok(JsValue::Int(1))), 35 | ("1 + 1", Ok(JsValue::Int(2))), 36 | ("1.1", Ok(JsValue::Float(1.1))), 37 | ("2.2 * 2 + 5", Ok(JsValue::Float(9.4))), 38 | ("\"abc\"", Ok(JsValue::String("abc".into()))), 39 | ( 40 | "[1,2]", 41 | Ok(JsValue::Array(vec![JsValue::Int(1), JsValue::Int(2)])), 42 | ), 43 | ]; 44 | 45 | for (code, res) in cases.into_iter() { 46 | assert_eq!(c.eval(code), res,); 47 | } 48 | 49 | let obj_cases = vec![ 50 | ( 51 | r#" {"a": null, "b": undefined} "#, 52 | Ok(JsValue::Object(HashMap::from_iter(vec![ 53 | ("a".to_string(), JsValue::Null), 54 | ("b".to_string(), JsValue::Undefined), 55 | ]))), 56 | ), 57 | ( 58 | r#" {a: 1, b: true, c: {c1: false}} "#, 59 | Ok(JsValue::Object(HashMap::from_iter(vec![ 60 | ("a".to_string(), JsValue::Int(1)), 61 | ("b".to_string(), JsValue::Bool(true)), 62 | ( 63 | "c".to_string(), 64 | JsValue::Object(HashMap::from_iter(vec![( 65 | "c1".to_string(), 66 | JsValue::Bool(false), 67 | )])), 68 | ), 69 | ]))), 70 | ), 71 | ]; 72 | 73 | for (index, (code, res)) in obj_cases.into_iter().enumerate() { 74 | let full_code = format!( 75 | "var v{index} = {code}; v{index}", 76 | index = index, 77 | code = code 78 | ); 79 | assert_eq!(c.eval(&full_code), res,); 80 | } 81 | 82 | assert_eq!(c.eval_as::("true").unwrap(), true,); 83 | assert_eq!(c.eval_as::("1 + 2").unwrap(), 3,); 84 | 85 | let value: String = c.eval_as("var x = 44; x.toString()").unwrap(); 86 | assert_eq!(&value, "44"); 87 | 88 | #[cfg(feature = "bigint")] 89 | assert_eq!( 90 | c.eval_as::("1n << 100n").unwrap(), 91 | num_bigint::BigInt::from(1i128 << 100) 92 | ); 93 | 94 | #[cfg(feature = "bigint")] 95 | assert_eq!(c.eval_as::("1 << 30").unwrap(), 1i64 << 30); 96 | 97 | #[cfg(feature = "bigint")] 98 | assert_eq!(c.eval_as::("1n << 100n").unwrap(), 1u128 << 100); 99 | } 100 | 101 | #[test] 102 | fn test_eval_syntax_error() { 103 | let c = Context::new().unwrap(); 104 | assert_eq!( 105 | c.eval( 106 | r#" 107 | !!!! 108 | "# 109 | ), 110 | Err(ExecutionError::Exception( 111 | "SyntaxError: unexpected token in expression: \'\'".into() 112 | )) 113 | ); 114 | } 115 | 116 | #[test] 117 | fn test_eval_exception() { 118 | let c = Context::new().unwrap(); 119 | assert_eq!( 120 | c.eval( 121 | r#" 122 | function f() { 123 | throw new Error("My Error"); 124 | } 125 | f(); 126 | "# 127 | ), 128 | Err(ExecutionError::Exception("Error: My Error".into(),)) 129 | ); 130 | } 131 | 132 | #[test] 133 | fn eval_async() { 134 | let c = Context::new().unwrap(); 135 | 136 | let value = c 137 | .eval( 138 | r#" 139 | new Promise((resolve, _) => { 140 | resolve(33); 141 | }) 142 | "#, 143 | ) 144 | .unwrap(); 145 | assert_eq!(value, JsValue::Int(33)); 146 | 147 | let res = c.eval( 148 | r#" 149 | new Promise((_resolve, reject) => { 150 | reject("Failed..."); 151 | }) 152 | "#, 153 | ); 154 | assert_eq!( 155 | res, 156 | Err(ExecutionError::Exception(JsValue::String( 157 | "Failed...".into() 158 | ))) 159 | ); 160 | } 161 | 162 | #[test] 163 | fn test_set_global() { 164 | let context = Context::new().unwrap(); 165 | context.set_global("someGlobalVariable", 42).unwrap(); 166 | let value = context.eval_as::("someGlobalVariable").unwrap(); 167 | assert_eq!(value, 42,); 168 | } 169 | 170 | #[test] 171 | fn test_call() { 172 | let c = Context::new().unwrap(); 173 | 174 | assert_eq!( 175 | c.call_function("parseInt", vec!["22"]).unwrap(), 176 | JsValue::Int(22), 177 | ); 178 | 179 | c.eval( 180 | r#" 181 | function add(a, b) { 182 | return a + b; 183 | } 184 | "#, 185 | ) 186 | .unwrap(); 187 | assert_eq!( 188 | c.call_function("add", vec![5, 7]).unwrap(), 189 | JsValue::Int(12), 190 | ); 191 | 192 | c.eval( 193 | r#" 194 | function sumArray(arr) { 195 | let sum = 0; 196 | for (const value of arr) { 197 | sum += value; 198 | } 199 | return sum; 200 | } 201 | "#, 202 | ) 203 | .unwrap(); 204 | assert_eq!( 205 | c.call_function("sumArray", vec![vec![1, 2, 3]]).unwrap(), 206 | JsValue::Int(6), 207 | ); 208 | 209 | c.eval( 210 | r#" 211 | function addObject(obj) { 212 | let sum = 0; 213 | for (const key of Object.keys(obj)) { 214 | sum += obj[key]; 215 | } 216 | return sum; 217 | } 218 | "#, 219 | ) 220 | .unwrap(); 221 | let mut obj = std::collections::HashMap::::new(); 222 | obj.insert("a".into(), 10); 223 | obj.insert("b".into(), 20); 224 | obj.insert("c".into(), 30); 225 | assert_eq!( 226 | c.call_function("addObject", vec![obj]).unwrap(), 227 | JsValue::Int(60), 228 | ); 229 | } 230 | 231 | #[test] 232 | fn test_call_large_string() { 233 | let c = Context::new().unwrap(); 234 | c.eval(" function strLen(s) { return s.length; } ").unwrap(); 235 | 236 | let s = " ".repeat(200_000); 237 | let v = c.call_function("strLen", vec![s]).unwrap(); 238 | assert_eq!(v, JsValue::Int(200_000)); 239 | } 240 | 241 | #[test] 242 | fn call_async() { 243 | let c = Context::new().unwrap(); 244 | 245 | c.eval( 246 | r#" 247 | function asyncOk() { 248 | return new Promise((resolve, _) => { 249 | resolve(33); 250 | }); 251 | } 252 | 253 | function asyncErr() { 254 | return new Promise((_resolve, reject) => { 255 | reject("Failed..."); 256 | }); 257 | } 258 | "#, 259 | ) 260 | .unwrap(); 261 | 262 | let value = c.call_function("asyncOk", vec![true]).unwrap(); 263 | assert_eq!(value, JsValue::Int(33)); 264 | 265 | let res = c.call_function("asyncErr", vec![true]); 266 | assert_eq!( 267 | res, 268 | Err(ExecutionError::Exception(JsValue::String( 269 | "Failed...".into() 270 | ))) 271 | ); 272 | } 273 | 274 | #[test] 275 | fn test_callback() { 276 | let c = Context::new().unwrap(); 277 | 278 | c.add_callback("no_arguments", || true).unwrap(); 279 | assert_eq!(c.eval_as::("no_arguments()").unwrap(), true); 280 | 281 | c.add_callback("cb1", |flag: bool| !flag).unwrap(); 282 | assert_eq!(c.eval("cb1(true)").unwrap(), JsValue::Bool(false),); 283 | 284 | c.add_callback("concat2", |a: String, b: String| format!("{}{}", a, b)) 285 | .unwrap(); 286 | assert_eq!( 287 | c.eval(r#"concat2("abc", "def")"#).unwrap(), 288 | JsValue::String("abcdef".into()), 289 | ); 290 | 291 | c.add_callback("add2", |a: i32, b: i32| -> i32 { a + b }) 292 | .unwrap(); 293 | assert_eq!(c.eval("add2(5, 11)").unwrap(), JsValue::Int(16),); 294 | 295 | c.add_callback("sum", |items: Vec| -> i32 { items.iter().sum() }) 296 | .unwrap(); 297 | assert_eq!(c.eval("sum([1, 2, 3, 4, 5, 6])").unwrap(), JsValue::Int(21),); 298 | 299 | c.add_callback("identity", |value: JsValue| -> JsValue { value }) 300 | .unwrap(); 301 | { 302 | let v = JsValue::from(22); 303 | assert_eq!(c.eval("identity(22)").unwrap(), v); 304 | } 305 | } 306 | 307 | #[test] 308 | fn test_callback_argn_variants() { 309 | macro_rules! callback_argn_tests { 310 | [ 311 | $( 312 | $len:literal : ( $( $argn:ident : $argv:literal ),* ), 313 | )* 314 | ] => { 315 | $( 316 | { 317 | // Test plain return type. 318 | let name = format!("cb{}", $len); 319 | let c = Context::new().unwrap(); 320 | c.add_callback(&name, | $( $argn : i32 ),*| -> i32 { 321 | $( $argn + )* 0 322 | }).unwrap(); 323 | 324 | let code = format!("{}( {} )", name, "1,".repeat($len)); 325 | let v = c.eval(&code).unwrap(); 326 | assert_eq!(v, JsValue::Int($len)); 327 | 328 | // Test Result return type with OK(_) returns. 329 | let name = format!("cbres{}", $len); 330 | c.add_callback(&name, | $( $argn : i32 ),*| -> Result { 331 | Ok($( $argn + )* 0) 332 | }).unwrap(); 333 | 334 | let code = format!("{}( {} )", name, "1,".repeat($len)); 335 | let v = c.eval(&code).unwrap(); 336 | assert_eq!(v, JsValue::Int($len)); 337 | 338 | // Test Result return type with Err(_) returns. 339 | let name = format!("cbreserr{}", $len); 340 | c.add_callback(&name, #[allow(unused_variables)] | $( $argn : i32 ),*| -> Result { 341 | Err("error".into()) 342 | }).unwrap(); 343 | 344 | let code = format!("{}( {} )", name, "1,".repeat($len)); 345 | let res = c.eval(&code); 346 | assert_eq!(res, Err(ExecutionError::Exception("error".into()))); 347 | } 348 | )* 349 | } 350 | } 351 | 352 | callback_argn_tests![ 353 | 1: (a : 1), 354 | ] 355 | } 356 | 357 | #[test] 358 | fn test_callback_varargs() { 359 | let c = Context::new().unwrap(); 360 | 361 | // No return. 362 | c.add_callback("cb", |args: Arguments| { 363 | let args = args.into_vec(); 364 | assert_eq!( 365 | args, 366 | vec![ 367 | JsValue::String("hello".into()), 368 | JsValue::Bool(true), 369 | JsValue::from(100), 370 | ] 371 | ); 372 | }) 373 | .unwrap(); 374 | assert_eq!( 375 | c.eval_as::("cb('hello', true, 100) === undefined") 376 | .unwrap(), 377 | true 378 | ); 379 | 380 | // With return. 381 | c.add_callback("cb2", |args: Arguments| -> u32 { 382 | let args = args.into_vec(); 383 | assert_eq!( 384 | args, 385 | vec![JsValue::from(1), JsValue::from(10), JsValue::from(100),] 386 | ); 387 | 111 388 | }) 389 | .unwrap(); 390 | c.eval( 391 | r#" 392 | var x = cb2(1, 10, 100); 393 | if (x !== 111) { 394 | throw new Error('Expected 111, got ' + x); 395 | } 396 | "#, 397 | ) 398 | .unwrap(); 399 | } 400 | 401 | #[test] 402 | fn test_callback_invalid_argcount() { 403 | let c = Context::new().unwrap(); 404 | 405 | c.add_callback("cb", |a: i32, b: i32| a + b).unwrap(); 406 | 407 | assert_eq!( 408 | c.eval(" cb(5) "), 409 | Err(ExecutionError::Exception( 410 | "Invalid argument count: Expected 2, got 1".into() 411 | )), 412 | ); 413 | } 414 | 415 | #[test] 416 | fn memory_limit_exceeded() { 417 | let c = Context::builder().memory_limit(100_000).build().unwrap(); 418 | assert_eq!( 419 | c.eval(" 'abc'.repeat(200_000) "), 420 | Err(ExecutionError::OutOfMemory), 421 | ); 422 | } 423 | 424 | #[test] 425 | fn context_reset() { 426 | let c = Context::new().unwrap(); 427 | c.eval(" var x = 123; ").unwrap(); 428 | c.add_callback("myCallback", || true).unwrap(); 429 | 430 | let c2 = c.reset().unwrap(); 431 | 432 | // Check it still works. 433 | assert_eq!( 434 | c2.eval_as::(" 'abc'.repeat(2) ").unwrap(), 435 | "abcabc".to_string(), 436 | ); 437 | 438 | // Check old state is gone. 439 | let err_msg = c2.eval(" x ").unwrap_err().to_string(); 440 | assert!(err_msg.contains("ReferenceError")); 441 | 442 | // Check callback is gone. 443 | let err_msg = c2.eval(" myCallback() ").unwrap_err().to_string(); 444 | assert!(err_msg.contains("ReferenceError")); 445 | } 446 | 447 | #[inline(never)] 448 | fn build_context() -> Context { 449 | let ctx = Context::new().unwrap(); 450 | let name = "cb".to_string(); 451 | ctx.add_callback(&name, |a: String| a.repeat(2)).unwrap(); 452 | 453 | let code = " function f(value) { return cb(value); } ".to_string(); 454 | ctx.eval(&code).unwrap(); 455 | 456 | ctx 457 | } 458 | 459 | #[test] 460 | fn moved_context() { 461 | let c = build_context(); 462 | let v = c.call_function("f", vec!["test"]).unwrap(); 463 | assert_eq!(v, "testtest".into()); 464 | 465 | let v = c.eval(" f('la') ").unwrap(); 466 | assert_eq!(v, "lala".into()); 467 | } 468 | 469 | #[cfg(feature = "chrono")] 470 | #[test] 471 | fn chrono_serialize() { 472 | let c = build_context(); 473 | 474 | c.eval( 475 | " 476 | function dateToTimestamp(date) { 477 | return date.getTime(); 478 | } 479 | ", 480 | ) 481 | .unwrap(); 482 | 483 | let now = chrono::Utc::now(); 484 | let now_millis = now.timestamp_millis(); 485 | 486 | let timestamp = c 487 | .call_function("dateToTimestamp", vec![JsValue::Date(now.clone())]) 488 | .unwrap(); 489 | 490 | assert_eq!(timestamp, JsValue::Float(now_millis as f64)); 491 | } 492 | 493 | #[cfg(feature = "chrono")] 494 | #[test] 495 | fn chrono_deserialize() { 496 | use chrono::offset::TimeZone; 497 | 498 | let c = build_context(); 499 | 500 | let value = c.eval(" new Date(1234567555) ").unwrap(); 501 | let datetime = chrono::Utc.timestamp_millis(1234567555); 502 | 503 | assert_eq!(value, JsValue::Date(datetime)); 504 | } 505 | 506 | #[cfg(feature = "chrono")] 507 | #[test] 508 | fn chrono_roundtrip() { 509 | let c = build_context(); 510 | 511 | c.eval(" function identity(x) { return x; } ").unwrap(); 512 | let d = chrono::Utc::now(); 513 | let td = JsValue::Date(d.clone()); 514 | let td2 = c.call_function("identity", vec![td.clone()]).unwrap(); 515 | let d2 = if let JsValue::Date(x) = td2 { 516 | x 517 | } else { 518 | panic!("expected date") 519 | }; 520 | 521 | assert_eq!(d.timestamp_millis(), d2.timestamp_millis()); 522 | } 523 | 524 | #[cfg(feature = "bigint")] 525 | #[test] 526 | fn test_bigint_deserialize_i64() { 527 | for i in vec![0, std::i64::MAX, std::i64::MIN] { 528 | let c = Context::new().unwrap(); 529 | let value = c.eval(&format!("{}n", i)).unwrap(); 530 | assert_eq!(value, JsValue::BigInt(i.into())); 531 | } 532 | } 533 | 534 | #[cfg(feature = "bigint")] 535 | #[test] 536 | fn test_bigint_deserialize_bigint() { 537 | for i in vec![ 538 | std::i64::MAX as i128 + 1, 539 | std::i64::MIN as i128 - 1, 540 | std::i128::MAX, 541 | std::i128::MIN, 542 | ] { 543 | let c = Context::new().unwrap(); 544 | let value = c.eval(&format!("{}n", i)).unwrap(); 545 | let expected = num_bigint::BigInt::from(i); 546 | assert_eq!(value, JsValue::BigInt(expected.into())); 547 | } 548 | } 549 | 550 | #[cfg(feature = "bigint")] 551 | #[test] 552 | fn test_bigint_serialize_i64() { 553 | for i in vec![0, std::i64::MAX, std::i64::MIN] { 554 | let c = Context::new().unwrap(); 555 | c.eval(&format!(" function isEqual(x) {{ return x === {}n }} ", i)) 556 | .unwrap(); 557 | assert_eq!( 558 | c.call_function("isEqual", vec![JsValue::BigInt(i.into())]) 559 | .unwrap(), 560 | JsValue::Bool(true) 561 | ); 562 | } 563 | } 564 | 565 | #[cfg(feature = "bigint")] 566 | #[test] 567 | fn test_bigint_serialize_bigint() { 568 | for i in vec![ 569 | std::i64::MAX as i128 + 1, 570 | std::i64::MIN as i128 - 1, 571 | std::i128::MAX, 572 | std::i128::MIN, 573 | ] { 574 | let c = Context::new().unwrap(); 575 | c.eval(&format!(" function isEqual(x) {{ return x === {}n }} ", i)) 576 | .unwrap(); 577 | let value = JsValue::BigInt(num_bigint::BigInt::from(i).into()); 578 | assert_eq!( 579 | c.call_function("isEqual", vec![value]).unwrap(), 580 | JsValue::Bool(true) 581 | ); 582 | } 583 | } 584 | 585 | #[test] 586 | fn test_console() { 587 | use console::Level; 588 | use std::sync::{Arc, Mutex}; 589 | 590 | let messages = Arc::new(Mutex::new(Vec::<(Level, Vec)>::new())); 591 | 592 | let m = messages.clone(); 593 | let c = Context::builder() 594 | .console(move |level: Level, args: Vec| { 595 | m.lock().unwrap().push((level, args)); 596 | }) 597 | .build() 598 | .unwrap(); 599 | 600 | c.eval( 601 | r#" 602 | console.log("hi"); 603 | console.error(false); 604 | "#, 605 | ) 606 | .unwrap(); 607 | 608 | let m = messages.lock().unwrap(); 609 | 610 | assert_eq!( 611 | *m, 612 | vec![ 613 | (Level::Log, vec![JsValue::from("hi")]), 614 | (Level::Error, vec![JsValue::from(false)]), 615 | ] 616 | ); 617 | } 618 | 619 | #[test] 620 | fn test_global_setter() { 621 | let ctx = Context::new().unwrap(); 622 | ctx.set_global("a", "a").unwrap(); 623 | ctx.eval("a + 1").unwrap(); 624 | } 625 | -------------------------------------------------------------------------------- /src/value/bigint.rs: -------------------------------------------------------------------------------- 1 | use num_traits::cast::ToPrimitive; 2 | 3 | #[derive(Clone, Debug)] 4 | pub enum BigIntOrI64 { 5 | Int(i64), 6 | BigInt(num_bigint::BigInt), 7 | } 8 | 9 | impl PartialEq for BigIntOrI64 { 10 | fn eq(&self, other: &Self) -> bool { 11 | use BigIntOrI64::*; 12 | match (&self, &other) { 13 | (Int(i), Int(j)) => i == j, 14 | (Int(i), BigInt(b)) | (BigInt(b), Int(i)) => b == &num_bigint::BigInt::from(*i), 15 | (BigInt(a), BigInt(b)) => a == b, 16 | } 17 | } 18 | } 19 | 20 | impl Eq for BigIntOrI64 {} 21 | 22 | /// A value holding JavaScript 23 | /// [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) type 24 | #[derive(Clone, Debug, PartialEq, Eq)] 25 | pub struct BigInt { 26 | pub(crate) inner: BigIntOrI64, 27 | } 28 | 29 | impl BigInt { 30 | /// Return `Some` if value fits into `i64` and `None` otherwise 31 | pub fn as_i64(&self) -> Option { 32 | match &self.inner { 33 | BigIntOrI64::Int(int) => Some(*int), 34 | BigIntOrI64::BigInt(bigint) => bigint.to_i64(), 35 | } 36 | } 37 | /// Convert value into `num_bigint::BigInt` 38 | pub fn into_bigint(self) -> num_bigint::BigInt { 39 | match self.inner { 40 | BigIntOrI64::Int(int) => int.into(), 41 | BigIntOrI64::BigInt(bigint) => bigint, 42 | } 43 | } 44 | } 45 | 46 | impl std::fmt::Display for BigInt { 47 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 48 | match self.inner { 49 | BigIntOrI64::Int(i) => write!(f, "{}", i), 50 | BigIntOrI64::BigInt(ref i) => write!(f, "{}", i), 51 | } 52 | } 53 | } 54 | 55 | impl From for BigInt { 56 | fn from(int: i64) -> Self { 57 | BigInt { 58 | inner: BigIntOrI64::Int(int), 59 | } 60 | } 61 | } 62 | 63 | impl From for BigInt { 64 | fn from(bigint: num_bigint::BigInt) -> Self { 65 | BigInt { 66 | inner: BigIntOrI64::BigInt(bigint), 67 | } 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod tests { 73 | #[allow(unused_imports)] 74 | use super::*; 75 | 76 | #[test] 77 | fn test_bigint_as_i64() { 78 | let value = BigInt { 79 | inner: BigIntOrI64::Int(1234i64), 80 | }; 81 | assert_eq!(value.as_i64(), Some(1234i64)); 82 | } 83 | 84 | #[test] 85 | fn test_bigint_as_i64_overflow() { 86 | let value = BigInt { 87 | inner: BigIntOrI64::BigInt(num_bigint::BigInt::from(std::i128::MAX)), 88 | }; 89 | assert_eq!(value.as_i64(), None); 90 | } 91 | 92 | #[test] 93 | fn test_bigint_into_bigint() { 94 | for i in vec![ 95 | 0 as i128, 96 | std::i64::MAX as i128, 97 | std::i64::MIN as i128, 98 | std::i128::MAX, 99 | std::i128::MIN, 100 | ] { 101 | let value = BigInt { 102 | inner: BigIntOrI64::BigInt(num_bigint::BigInt::from(i)), 103 | }; 104 | assert_eq!(value.into_bigint(), num_bigint::BigInt::from(i)); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/value/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "bigint")] 2 | pub(crate) mod bigint; 3 | 4 | use std::convert::{TryFrom, TryInto}; 5 | use std::{collections::HashMap, error, fmt}; 6 | 7 | #[cfg(feature = "bigint")] 8 | pub use bigint::BigInt; 9 | 10 | /// A value that can be (de)serialized to/from the quickjs runtime. 11 | #[derive(PartialEq, Clone, Debug)] 12 | #[allow(missing_docs)] 13 | pub enum JsValue { 14 | Undefined, 15 | Null, 16 | Bool(bool), 17 | Int(i32), 18 | Float(f64), 19 | String(String), 20 | Array(Vec), 21 | Object(HashMap), 22 | /// chrono::Datetime / JS Date integration. 23 | /// Only available with the optional `chrono` feature. 24 | #[cfg(feature = "chrono")] 25 | Date(chrono::DateTime), 26 | /// num_bigint::BigInt / JS BigInt integration 27 | /// Only available with the optional `bigint` feature 28 | #[cfg(feature = "bigint")] 29 | BigInt(crate::BigInt), 30 | #[doc(hidden)] 31 | __NonExhaustive, 32 | } 33 | 34 | impl JsValue { 35 | /// Cast value to a str. 36 | /// 37 | /// Returns `Some(&str)` if value is a `JsValue::String`, None otherwise. 38 | pub fn as_str(&self) -> Option<&str> { 39 | match self { 40 | JsValue::String(ref s) => Some(s.as_str()), 41 | _ => None, 42 | } 43 | } 44 | 45 | /// Convert to `String`. 46 | pub fn into_string(self) -> Option { 47 | match self { 48 | JsValue::String(s) => Some(s), 49 | _ => None, 50 | } 51 | } 52 | } 53 | 54 | macro_rules! value_impl_from { 55 | ( 56 | ( 57 | $( $t1:ty => $var1:ident, )* 58 | ) 59 | ( 60 | $( $t2:ty => |$exprname:ident| $expr:expr => $var2:ident, )* 61 | ) 62 | ) => { 63 | $( 64 | impl From<$t1> for JsValue { 65 | fn from(value: $t1) -> Self { 66 | JsValue::$var1(value) 67 | } 68 | } 69 | 70 | impl std::convert::TryFrom for $t1 { 71 | type Error = ValueError; 72 | 73 | fn try_from(value: JsValue) -> Result { 74 | match value { 75 | JsValue::$var1(inner) => Ok(inner), 76 | _ => Err(ValueError::UnexpectedType) 77 | } 78 | 79 | } 80 | } 81 | )* 82 | $( 83 | impl From<$t2> for JsValue { 84 | fn from(value: $t2) -> Self { 85 | let $exprname = value; 86 | let inner = $expr; 87 | JsValue::$var2(inner) 88 | } 89 | } 90 | )* 91 | } 92 | } 93 | 94 | value_impl_from! { 95 | ( 96 | bool => Bool, 97 | i32 => Int, 98 | f64 => Float, 99 | String => String, 100 | ) 101 | ( 102 | i8 => |x| i32::from(x) => Int, 103 | i16 => |x| i32::from(x) => Int, 104 | u8 => |x| i32::from(x) => Int, 105 | u16 => |x| i32::from(x) => Int, 106 | u32 => |x| f64::from(x) => Float, 107 | ) 108 | } 109 | 110 | #[cfg(feature = "bigint")] 111 | value_impl_from! { 112 | () 113 | ( 114 | i64 => |x| x.into() => BigInt, 115 | u64 => |x| num_bigint::BigInt::from(x).into() => BigInt, 116 | i128 => |x| num_bigint::BigInt::from(x).into() => BigInt, 117 | u128 => |x| num_bigint::BigInt::from(x).into() => BigInt, 118 | num_bigint::BigInt => |x| x.into() => BigInt, 119 | ) 120 | } 121 | 122 | #[cfg(feature = "bigint")] 123 | impl std::convert::TryFrom for i64 { 124 | type Error = ValueError; 125 | 126 | fn try_from(value: JsValue) -> Result { 127 | match value { 128 | JsValue::Int(int) => Ok(int as i64), 129 | JsValue::BigInt(bigint) => bigint.as_i64().ok_or(ValueError::UnexpectedType), 130 | _ => Err(ValueError::UnexpectedType), 131 | } 132 | } 133 | } 134 | 135 | #[cfg(feature = "bigint")] 136 | macro_rules! value_bigint_impl_tryfrom { 137 | ( 138 | ($($t:ty => $to_type:ident, )*) 139 | ) => { 140 | $( 141 | impl std::convert::TryFrom for $t { 142 | type Error = ValueError; 143 | 144 | fn try_from(value: JsValue) -> Result { 145 | use num_traits::ToPrimitive; 146 | 147 | match value { 148 | JsValue::Int(int) => Ok(int as $t), 149 | JsValue::BigInt(bigint) => bigint 150 | .into_bigint() 151 | .$to_type() 152 | .ok_or(ValueError::UnexpectedType), 153 | _ => Err(ValueError::UnexpectedType), 154 | } 155 | } 156 | } 157 | )* 158 | } 159 | } 160 | 161 | #[cfg(feature = "bigint")] 162 | value_bigint_impl_tryfrom! { 163 | ( 164 | u64 => to_u64, 165 | i128 => to_i128, 166 | u128 => to_u128, 167 | ) 168 | } 169 | 170 | #[cfg(feature = "bigint")] 171 | impl std::convert::TryFrom for num_bigint::BigInt { 172 | type Error = ValueError; 173 | 174 | fn try_from(value: JsValue) -> Result { 175 | match value { 176 | JsValue::Int(int) => Ok(num_bigint::BigInt::from(int)), 177 | JsValue::BigInt(bigint) => Ok(bigint.into_bigint()), 178 | _ => Err(ValueError::UnexpectedType), 179 | } 180 | } 181 | } 182 | 183 | impl From> for JsValue 184 | where 185 | T: Into, 186 | { 187 | fn from(values: Vec) -> Self { 188 | let items = values.into_iter().map(|x| x.into()).collect(); 189 | JsValue::Array(items) 190 | } 191 | } 192 | 193 | impl TryFrom for Vec 194 | where 195 | T: TryFrom, 196 | { 197 | type Error = ValueError; 198 | 199 | fn try_from(value: JsValue) -> Result { 200 | match value { 201 | JsValue::Array(items) => items 202 | .into_iter() 203 | .map(|item| item.try_into().map_err(|_| ValueError::UnexpectedType)) 204 | .collect(), 205 | _ => Err(ValueError::UnexpectedType), 206 | } 207 | } 208 | } 209 | 210 | impl<'a> From<&'a str> for JsValue { 211 | fn from(val: &'a str) -> Self { 212 | JsValue::String(val.into()) 213 | } 214 | } 215 | 216 | impl From> for JsValue 217 | where 218 | T: Into, 219 | { 220 | fn from(opt: Option) -> Self { 221 | if let Some(value) = opt { 222 | value.into() 223 | } else { 224 | JsValue::Null 225 | } 226 | } 227 | } 228 | 229 | impl From> for JsValue 230 | where 231 | K: Into, 232 | V: Into, 233 | { 234 | fn from(map: HashMap) -> Self { 235 | let new_map = map.into_iter().map(|(k, v)| (k.into(), v.into())).collect(); 236 | JsValue::Object(new_map) 237 | } 238 | } 239 | 240 | impl TryFrom for HashMap 241 | where 242 | V: TryFrom, 243 | { 244 | type Error = ValueError; 245 | 246 | fn try_from(value: JsValue) -> Result { 247 | match value { 248 | JsValue::Object(object) => object 249 | .into_iter() 250 | .map(|(k, v)| match v.try_into() { 251 | Ok(v) => Ok((k, v)), 252 | Err(_) => Err(ValueError::UnexpectedType), 253 | }) 254 | .collect(), 255 | _ => Err(ValueError::UnexpectedType), 256 | } 257 | } 258 | } 259 | 260 | /// Error during value conversion. 261 | #[derive(PartialEq, Eq, Debug)] 262 | pub enum ValueError { 263 | /// Invalid non-utf8 string. 264 | InvalidString(std::str::Utf8Error), 265 | /// Encountered string with \0 bytes. 266 | StringWithZeroBytes(std::ffi::NulError), 267 | /// Internal error. 268 | Internal(String), 269 | /// Received an unexpected type that could not be converted. 270 | UnexpectedType, 271 | #[doc(hidden)] 272 | __NonExhaustive, 273 | } 274 | 275 | // TODO: remove this once either the Never type get's stabilized or the compiler 276 | // can properly handle Infallible. 277 | impl From for ValueError { 278 | fn from(_: std::convert::Infallible) -> Self { 279 | unreachable!() 280 | } 281 | } 282 | 283 | impl fmt::Display for ValueError { 284 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 285 | use ValueError::*; 286 | match self { 287 | InvalidString(e) => write!( 288 | f, 289 | "Value conversion failed - invalid non-utf8 string: {}", 290 | e 291 | ), 292 | StringWithZeroBytes(_) => write!(f, "String contains \\0 bytes",), 293 | Internal(e) => write!(f, "Value conversion failed - internal error: {}", e), 294 | UnexpectedType => write!(f, "Could not convert - received unexpected type"), 295 | __NonExhaustive => unreachable!(), 296 | } 297 | } 298 | } 299 | 300 | impl error::Error for ValueError {} 301 | 302 | #[cfg(test)] 303 | mod tests { 304 | #[allow(unused_imports)] 305 | use super::*; 306 | 307 | #[cfg(feature = "bigint")] 308 | #[test] 309 | fn test_bigint_from_i64() { 310 | let int = 1234i64; 311 | let value = JsValue::from(int); 312 | if let JsValue::BigInt(value) = value { 313 | assert_eq!(value.as_i64(), Some(int)); 314 | } else { 315 | panic!("Expected JsValue::BigInt"); 316 | } 317 | } 318 | 319 | #[cfg(feature = "bigint")] 320 | #[test] 321 | fn test_bigint_from_bigint() { 322 | let bigint = num_bigint::BigInt::from(std::i128::MAX); 323 | let value = JsValue::from(bigint.clone()); 324 | if let JsValue::BigInt(value) = value { 325 | assert_eq!(value.into_bigint(), bigint); 326 | } else { 327 | panic!("Expected JsValue::BigInt"); 328 | } 329 | } 330 | 331 | #[cfg(feature = "bigint")] 332 | #[test] 333 | fn test_bigint_i64_bigint_eq() { 334 | let value_i64 = JsValue::BigInt(1234i64.into()); 335 | let value_bigint = JsValue::BigInt(num_bigint::BigInt::from(1234i64).into()); 336 | assert_eq!(value_i64, value_bigint); 337 | } 338 | } 339 | --------------------------------------------------------------------------------