feat: can run qjs

This commit is contained in:
2020-11-07 16:12:21 +08:00
parent 5110732561
commit 56b44d1a8f
60 changed files with 91718 additions and 16 deletions

View File

@@ -65,6 +65,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "copy_dir"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4281031634644843bd2f5aa9c48cf98fc48d6b083bd90bb11becf10deaf8b0"
dependencies = [
"walkdir",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
@@ -84,7 +93,7 @@ checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users",
"winapi",
"winapi 0.3.9",
]
[[package]]
@@ -119,6 +128,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -131,6 +150,14 @@ version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "libquickjs-sys"
version = "0.7.0"
dependencies = [
"cc",
"copy_dir",
]
[[package]]
name = "log"
version = "0.4.11"
@@ -155,6 +182,14 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-js"
version = "0.3.5-alpha.0"
dependencies = [
"libquickjs-sys",
"once_cell",
]
[[package]]
name = "quote"
version = "1.0.7"
@@ -193,7 +228,7 @@ dependencies = [
"spin",
"untrusted",
"web-sys",
"winapi",
"winapi 0.3.9",
]
[[package]]
@@ -282,7 +317,7 @@ checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
dependencies = [
"byteorder",
"dirs",
"winapi",
"winapi 0.3.9",
]
[[package]]
@@ -292,7 +327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@@ -312,12 +347,23 @@ name = "virt_enclave"
version = "0.1.0"
dependencies = [
"hex",
"quick-js",
"ring",
"rust_util",
"serde",
"serde_json",
]
[[package]]
name = "walkdir"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780"
dependencies = [
"kernel32-sys",
"winapi 0.2.8",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@@ -388,6 +434,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -398,6 +450,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"

View File

@@ -7,8 +7,10 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quick-js = { path= "external/quickjs-rs" }
ring = "0.16"
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rust_util = "0.6"

View File

@@ -0,0 +1,109 @@
name: CI
on:
push:
branches:
- master
pull_request: {}
release:
types:
- created
jobs:
test:
name: Test on ${{ matrix.os }} with features ${{ matrix.features }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macOS-latest
- windows-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
if: matrix.os != 'windows-latest'
- name: Install stable toolchain (windows)
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
target: x86_64-pc-windows-gnu
override: true
if: matrix.os == 'windows-latest'
- name: Install `just` command runner
uses: extractions/setup-just@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup (debian)
run: |
echo "Install system tools"
sudo apt-get update -y
sudo apt-get install -y curl xz-utils build-essential gcc-multilib valgrind
if: matrix.os == 'ubuntu-latest'
- name: Setup (windows)
run: |
$env:PATH = "C:\msys64\mingw64\bin;C:\msys64\usr\bin;$env:PATH"
echo "::set-env name=PATH::${env:PATH}"
echo "::set-env name=CARGO_BUILD_TARGET::x86_64-pc-windows-gnu"
if: matrix.os == 'windows-latest'
- name: Build (default features)
env:
FEATURES: ""
run: |
cargo clean
just FEATURES="$FEATURES" build
- name: Test (default features)
env:
FEATURES: ""
run: |
just FEATURES="$FEATURES" test
- name: Build (--no-default-features)
env:
FEATURES: "--no-default-features"
run: |
cargo clean
just FEATURES="$FEATURES" build
- name: Test (--no-default-features)
env:
FEATURES: "--no-default-features"
run: |
just FEATURES="$FEATURES" test
- name: Build (--all-features)
env:
FEATURES: "--all-features"
run: |
cargo clean
just FEATURES="$FEATURES" build
- name: Test (--all-features)
env:
FEATURES: "--all-features"
run: |
just FEATURES="$FEATURES" test
- name: Lint
run: just lint
if: matrix.os == 'ubuntu-latest'
- name: Check for leaks (Valgrind)
run: just valgrind
if: matrix.os == 'ubuntu-latest' && matrix.features == '--all-features'

View File

@@ -0,0 +1,2 @@
/target
/Cargo.lock

View File

@@ -0,0 +1,63 @@
# quick-js - Changelog
## Master branch
## v0.3.4 - 2020-07-09
* Bump quickjs to 2020-07-05
## v0.3.3 - 2020-05-27
* Add Windows support
(only with MSYS2 environment and `x86_64-pc-windows-gnu` target architecture)
## v0.3.2 - 2020-05-25
* Updated quickjs to 2020-04-12
## v0.3.1 - 2020-03-24
* Update quickjs to 2020-03-16
* Add `TryFrom<JsValue>` impl for `HashMap<String, X>`
## v0.3.0 - 2019-11-02
### Features
* Add BigInt integration
* Add logging system and optional `log` crate integration
* Upgrade quickjs to 2019-10-27
### Breaking Changes
* Made `Value` enum non-exhaustive
* new Value::BigInt variant (with `bigint` feature)
## v0.2.3 - 2019-08-30
* Properly free property keys after enumeration
(Fixes memory leak when deserializing objects)
## v0.2.2 - 2019-08-13
* Fix invalid millisecond conversion for JsValue::Date
## v0.2.1 - 2019-08-13
* Impelemented deserializiation of objects to `JsValue::Object`
* Added `chrono` integration via the `chrono` feature
Adds a `JsValue::Date(DateTime<Utc>)` variant that allows (de)serializing
a JS `Date`
* Implemented resolving promises in `eval`/`call_function`
* Added `patched` feature for applying quickjs fixes
* quickjs upgraded to `2019-08-10` release
## v0.2.0 - 2019-07-31
* Added `memory_limit` customization
* Added `Context::clear` method for resetting context
* Callbacks now support more function signatures
( up to 5 arguments, `Result<T, E>` return value)
* Updated embedded quickjs bindings to version 2019-07-28.
* Fixed a bug in callback logic

View File

@@ -0,0 +1,32 @@
[package]
edition = "2018"
name = "quick-js"
description = "QuickJS Javascript engine wrapper"
version = "0.3.5-alpha.0"
readme = "README.md"
documentation = "https://docs.rs/quick-js"
repository = "https://github.com/theduke/quickjs-rs"
license = "MIT"
authors = ["Christoph Herzog <chris@theduke.at>"]
keywords = ["quickjs", "javascript", "js", "engine", "interpreter"]
[package.metadata.docs.rs]
features = [ "chrono", "bigint", "log" ]
[features]
patched = ["libquickjs-sys/patched"]
bigint = ["num-bigint", "num-traits", "libquickjs-sys/patched"]
[dependencies]
libquickjs-sys = { version = "> 0.3.0, < 0.8.0", path = "./libquickjs-sys" }
chrono = { version = "0.4.7", optional = true }
num-bigint = { version = "0.2.2", optional = true }
num-traits = { version = "0.2.0", optional = true }
log = { version = "0.4.8", optional = true }
once_cell = "1.2.0"
[workspace]
members = [
"libquickjs-sys",
]

View File

@@ -0,0 +1,19 @@
Copyright (c) 2019 Christoph Herzog <christoph.herzog@theduke.at>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,91 @@
# quickjs-rs
[![Crates.io](https://img.shields.io/crates/v/quick-js.svg?maxAge=3600)](https://crates.io/crates/quick-js)
[![docs.rs](https://docs.rs/quick-js/badge.svg)](https://docs.rs/quick-js)
[![Build Status](https://github.com/theduke/quickjs-rs/workflows/CI/badge.svg)
A Rust wrapper for [QuickJS](https://bellard.org/quickjs/).
QuickJS is a new, small Javascript engine by Fabrice Bellard and Charlie Gordon.
It is fast and supports the full ES2020 specification.
This crate allows you to easily run and integrate with Javascript code from Rust.
## Quickstart
```toml
[dependencies]
quick-js = "0.3.3"
```
```rust
use quick_js::{Context, JsValue};
let context = Context::new().unwrap();
// Eval.
let value = context.eval("1 + 2").unwrap();
assert_eq!(value, JsValue::Int(3));
let value = context.eval_as::<String>(" var x = 100 + 250; x.toString() ").unwrap();
assert_eq!(&value, "350");
// Callbacks.
context.add_callback("myCallback", |a: i32, b: i32| a + b).unwrap();
context.eval(r#"
// x will equal 30
var x = myCallback(10, 20);
"#).unwrap();
```
## Optional Features
The crate supports the following features:
* `chrono`: chrono integration
- adds a `JsValue::Date` variant that can be (de)serialized to/from a JS `Date`
* `bigint`: arbitrary precision integer support via [num-bigint](https://github.com/rust-num/num-bigint)
* `log`: allows forwarding `console.log` messages to the `log` crate.
Note: must be enabled with `ContextBuilder::console(quick_js::console::LogConsole);`
* `patched`
Enabled automatically for some other features, like `bigint`.
You should not need to enable this manually.
Applies QuickJS patches that can be found in `libquickjs-sys/embed/patches` directory.
## Installation
By default, quickjs is **bundled** with the `libquickjs-sys` crate and
automatically compiled, assuming you have the appropriate dependencies.
### Windows Support
Windows is only supported with the [MSYS2](https://www.msys2.org/) environment
and `x86_64-pc-windows-gnu` target architecture.
If you have MSYS2 installed and the MSYS `bin` directory in your path, you can
compile quickjs with `cargo build --target="x86_64-pc-windows-gnu"`.
The target can also be configured permanently via a
[cargo config file](https://doc.rust-lang.org/cargo/reference/config.html) or
the `CARGO_BUILD_TARGET` env var.
### System installation
To use the system installation, without the bundled feature, first install the required
dependencies, and then compile and install quickjs.
```bash
# Debian/Ubuntu: apt-get install -y curl xz-utils build-essential gcc-multilib libclang-dev clang
mkdir quickjs
curl -L https://bellard.org/quickjs/quickjs-2019-07-09.tar.xz | tar xJv -C quickjs --strip-components 1
cd quickjs
sudo make install
```
You then need to disable the `bundled` feature in the `libquickjs-sys` crate to
force using the system version.

View File

@@ -0,0 +1,22 @@
use quick_js::Context;
pub fn main() {
let context = Context::new().unwrap();
let value = context.eval("1 + 2").unwrap();
println!("js: 1 + 2 = {:?}", value);
context
.add_callback("myCallback", |a: i32, b: i32| a + b * b)
.unwrap();
let value = context
.eval(
r#"
var x = myCallback(10, 20);
x;
"#,
)
.unwrap();
println!("js: callback = {:?}", value);
}

View File

@@ -0,0 +1,56 @@
embed_dir := "./libquickjs-sys/embed/quickjs"
DOWNLOAD_URL := "https://bellard.org/quickjs/quickjs-2020-07-05.tar.xz"
FEATURES := "--all-features"
download-new:
test -d {{embed_dir}} && rm -r {{embed_dir}} || echo ""
mkdir {{embed_dir}} && \
curl -L {{DOWNLOAD_URL}} | tar xJv -C {{embed_dir}} --strip-components 1
download-cleanup:
rm -r "{{embed_dir}}/doc" "{{embed_dir}}/examples" "{{embed_dir}}/tests"
find "{{embed_dir}}" -type f | grep -E "\.(pdf|html|js|texi|sh)$" | xargs rm
find "{{embed_dir}}" -type f | grep test | xargs rm
generate-bindings:
(cd libquickjs-sys; bindgen wrapper.h -o embed/bindings.rs -- -I ./embed)
# Update VERSION in README
sed -i "s/**Embedded VERSION: .*/**Embedded VERSION: $(cat ./libquickjs-sys/embed/quickjs/VERSION)**/" ./libquickjs-sys/README.md
update-quickjs: download-new generate-bindings download-cleanup
debian-setup:
echo "Installing dependencies..."
sudo apt update && sudo apt-get install -y curl xz-utils build-essential gcc-multilib libclang-dev clang valgrind
build:
rustc --version
cargo --version
cargo build --verbose {{FEATURES}}
test:
rustc --version
cargo --version
# Limit test threads to 1 to show test name before execution.
RUST_TEST_THREADS=1 cargo test --verbose {{FEATURES}}
lint:
rustc --version
cargo --version
cargo clippy --version
echo "Linting!"
rustup component add rustfmt clippy
echo "Checking formatting..."
cargo fmt -- --check
echo "Checking clippy..."
cargo clippy
valgrind:
echo "Checking for memory leaks..."
find target/debug -maxdepth 1 -type f -executable | xargs valgrind --leak-check=full --error-exitcode=1

View File

@@ -0,0 +1,56 @@
# libquickjs_sys - Changelog
## v0.7.0 - 2020-07-09
Upgraded to quickjs version `2020-07-05`.
* Added
- JS_ParseJSON2
- JSSharedArrayBufferFunctions
- JS_WriteObject2
- JS_SetSharedArrayBufferFunctions
- JS_WriteObject2
- JS_SetSharedArrayBufferFunctions
- JS_PARSE_JSON_EXT
- JS_WRITE_OBJ_SAB
- JS_WRITE_OBJ_REFERENCE
- JS_READ_OBJ_SAB
- JS_READ_OBJ_REFERENCE
## v0.6.0 - 2020-05-25
Upgraded to quickjs version `2020-04-12`.
* Lot's of changes from `usize` to `size_t`.
## v0.5.0 - 2020-03-24
Upgraded to version `2020-03-16`:
- Added functions `JS_GetRuntimeOpaque`, `JS_SetRuntimeOpaque`
- Removed function `JS_NewInt64`, JS_ToInt64Ext
## v0.4.0 - 2019-11-02
Upgraded to version `2019-09-18`:
* Added `JS_ValueToAtom`
* Added `JS_SetConstructor`
* `JS_GetTypedArrayBuffer`
Updated bindgen dependency to 0.51.
## v0.3.0 - 2019-08-13
* Added `patched` feature for applying patches
* Added patch stack-overflow-signed to fix stackoverflow due invalid cast
* c_int changed to usize in JS_NewAtomLen/JS_NewStringLen
* JS_ToCStringLen2 replaces JS_ToCStringLen
* Added JS_GetOwnProperty(Names) functions
## v0.2.0 - 2019-07-31
* Updated embedded bindings to version 2019-07-28
- `JS_EVAL_FLAG_SHEBANG` constant was removed
- `JS_NewPromiseCallback` was added

View File

@@ -0,0 +1,26 @@
[package]
edition = "2018"
name = "libquickjs-sys"
description = "QuickJS Javascript Engine FFI bindings"
version = "0.7.0"
readme = "README.md"
documentation = "https://docs.rs/quickjs-sys"
repository = "https://github.com/theduke/quickjs-rs"
license = "MIT"
authors = ["Christoph Herzog <chris@theduke.at>"]
categories = ["external-ffi-bindings"]
keywords = ["quickjs"]
build = "build.rs"
[features]
bundled = ["cc", "copy_dir"]
patched = ["bundled"]
default = ["bundled"]
system = ["bindgen"]
[build-dependencies]
bindgen = { version = "0.54.0", optional = true }
cc = { version = "1.0", optional = true }
copy_dir = { version = "0.1.2", optional = true }

View File

@@ -0,0 +1,19 @@
Copyright (c) 2019 Christoph Herzog <christoph.herzog@theduke.at>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,32 @@
# libquickjs-sys
FFI Bindings for [quickjs](https://bellard.org/quickjs/), a Javascript engine.
See the [quick](https://crates.io/crates/quickjs) crate for a high-level
wrapper.
**Embedded VERSION: 2020-07-05**
## Embedded vs system
By default, an embedded version of quickjs is used.
If you want to use a version installed on your system, use:
```toml
libquickjs-sys = { version = "...", default-features = false, features = ["system"] }
```
## Updating the embedded bindings
QuickJS sources and a pre-generated `bindings.rs` are included in the repo.
They are used if the `embedded` feature is enabled.
To updat the bindings, follow these steps:
* (Install [just](https://github.com/casey/just))
* Update the download URL in ./justfile
* run `just quickjs-update`

View File

@@ -0,0 +1,144 @@
use std::path::{Path, PathBuf};
use std::env;
fn exists(path: impl AsRef<Path>) -> bool {
PathBuf::from(path.as_ref()).exists()
}
const LIB_NAME: &str = "quickjs";
#[cfg(all(not(feature = "system"), not(feature = "bundled")))]
fn main() {
panic!("Invalid config for crate libquickjs-sys: must enable either the 'bundled' or the 'system' feature");
}
#[cfg(feature = "system")]
extern crate bindgen;
#[cfg(feature = "system")]
fn main() {
#[cfg(not(feature = "bindgen"))]
panic!("Invalid configuration for libquickjs-sys: Must either enable the bundled or the bindgen feature");
#[cfg(feature = "patched")]
panic!("Invalid configuration for libquickjs-sys: the patched feature is incompatible with the system feature");
let lib: std::borrow::Cow<str> = if let Ok(lib) = env::var("QUICKJS_LIBRARY_PATH") {
lib.into()
} else if cfg!(unix) {
if exists(format!("/usr/lib/quickjs/{}.a", LIB_NAME)) {
"/usr/lib/quickjs".into()
} else if exists("/usr/local/lib/quickjs") {
"/usr/local/lib/quickjs".into()
} else {
panic!("quickjs library could not be found. Try setting the QUICKJS_LIBRARY_PATH env variable");
}
} else {
panic!("quickjs error: Windows is not supported yet");
};
// Generate bindings.
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.generate()
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
// Instruct cargo to statically link quickjs.
println!("cargo:rustc-link-search=native={}", lib);
println!("cargo:rustc-link-lib=static={}", LIB_NAME);
}
#[cfg(feature = "bundled")]
fn main() {
let embed_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("embed");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let code_dir = out_path.join("quickjs");
if exists(&code_dir) {
std::fs::remove_dir_all(&code_dir).unwrap();
}
copy_dir::copy_dir(embed_path.join("quickjs"), &code_dir)
.expect("Could not copy quickjs directory");
#[cfg(feature = "patched")]
apply_patches(&code_dir);
eprintln!("Compiling quickjs...");
let quickjs_version =
std::fs::read_to_string(code_dir.join("VERSION")).expect("failed to read quickjs version");
cc::Build::new()
.files(
[
"cutils.c",
"libbf.c",
"libregexp.c",
"libunicode.c",
"quickjs.c",
]
.iter()
.map(|f| code_dir.join(f)),
)
.define("_GNU_SOURCE", None)
.define(
"CONFIG_VERSION",
format!("\"{}\"", quickjs_version.trim()).as_str(),
)
.define("CONFIG_BIGNUM", None)
// The below flags are used by the official Makefile.
.flag_if_supported("-Wchar-subscripts")
.flag_if_supported("-Wno-array-bounds")
.flag_if_supported("-Wno-format-truncation")
.flag_if_supported("-Wno-missing-field-initializers")
.flag_if_supported("-Wno-sign-compare")
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wundef")
.flag_if_supported("-Wuninitialized")
.flag_if_supported("-Wunused")
.flag_if_supported("-Wwrite-strings")
.flag_if_supported("-funsigned-char")
// Below flags are added to supress warnings that appear on some
// platforms.
.flag_if_supported("-Wno-cast-function-type")
.flag_if_supported("-Wno-implicit-fallthrough")
// cc uses the OPT_LEVEL env var by default, but we hardcode it to -O2
// since release builds use -O3 which might be problematic for quickjs,
// and debug builds only happen once anyway so the optimization slowdown
// is fine.
.opt_level(2)
.compile(LIB_NAME);
std::fs::copy(embed_path.join("bindings.rs"), out_path.join("bindings.rs"))
.expect("Could not copy bindings.rs");
}
#[cfg(feature = "patched")]
fn apply_patches(code_dir: &PathBuf) {
use std::fs;
eprintln!("Applying patches...");
let embed_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("embed");
let patches_path = embed_path.join("patches");
for patch in fs::read_dir(patches_path).expect("Could not open patches directory") {
let patch = patch.expect("Could not open patch");
eprintln!("Applying {:?}...", patch.file_name());
let status = std::process::Command::new("patch")
.current_dir(&code_dir)
.arg("-i")
.arg(fs::canonicalize(patch.path()).expect("Cannot canonicalize patch path"))
.spawn()
.expect("Could not apply patches")
.wait()
.expect("Could not apply patches");
assert!(
status.success(),
"Patch command returned non-zero exit code"
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
diff --git libquickjs-sys/embed/quickjs/libbf.c libquickjs-sys/embed/quickjs/libbf.c
index cbabf95..f0625f9 100644
--- libquickjs-sys/embed/quickjs/libbf.c
+++ libquickjs-sys/embed/quickjs/libbf.c
@@ -2261,14 +2261,17 @@ int bf_get_int64(int64_t *pres, const bf_t *a, int flags)
v = INT64_MAX;
}
} else {
+ ret = BF_ST_OVERFLOW;
slimb_t bit_pos = a->len * LIMB_BITS - a->expn;
v = get_bits(a->tab, a->len, bit_pos);
#if LIMB_BITS == 32
v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32;
#endif
- if (a->sign)
+ if (a->sign) {
+ if (a->expn == 64 && v == (uint64_t)INT64_MAX + 1)
+ ret = 0; // not overflow, but INT64_MIN
v = -v;
- ret = 0;
+ }
}
*pres = v;
return ret;
diff --git libquickjs-sys/embed/quickjs/quickjs.c libquickjs-sys/embed/quickjs/quickjs.c
index 7bb20cb..ad5811c 100644
--- libquickjs-sys/embed/quickjs/quickjs.c
+++ libquickjs-sys/embed/quickjs/quickjs.c
@@ -11244,15 +11244,16 @@ static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
{
bf_t a_s, *a;
+ int ret;
a = JS_ToBigIntFree(ctx, &a_s, val);
if (!a) {
*pres = 0;
return -1;
}
- bf_get_int64(pres, a, BF_GET_INT_MOD);
+ ret = bf_get_int64(pres, a, BF_GET_INT_MOD);
JS_FreeBigInt(ctx, a, &a_s);
- return 0;
+ return ret;
}
int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)

View File

@@ -0,0 +1,15 @@
diff -urN quickjs-2019-07-28/quickjs.c quickjs-2019-07-28-stack-overflow-signed/quickjs.c
--- quickjs-2019-07-28/quickjs.c 2019-07-28 15:03:03.000000000 +0000
+++ quickjs-2019-07-28-stack-overflow-signed/quickjs.c 2019-08-09 20:00:03.666846091 +0000
@@ -1732,9 +1732,9 @@
static inline BOOL js_check_stack_overflow(JSContext *rt, size_t alloca_size)
{
- size_t size;
+ ptrdiff_t size;
size = rt->stack_top - js_get_stack_pointer();
- return unlikely((size + alloca_size) > rt->stack_size);
+ return unlikely((size + (ptrdiff_t)alloca_size) > (ptrdiff_t)rt->stack_size);
}
#endif

View File

@@ -0,0 +1,125 @@
2020-07-05:
- modified JS_GetPrototype() to return a live value
- REPL: support unicode characters larger than 16 bits
- added os.Worker
- improved object serialization
- added std.parseExtJSON
- misc bug fixes
2020-04-12:
- added cross realm support
- added AggregateError and Promise.any
- added env, uid and gid options in os.exec()
- misc bug fixes
2020-03-16:
- reworked error handling in std and os libraries: suppressed I/O
exceptions in std FILE functions and return a positive errno value
when it is explicit
- output exception messages to stderr
- added std.loadFile(), std.strerror(), std.FILE.prototype.tello()
- added JS_GetRuntimeOpaque(), JS_SetRuntimeOpaque(), JS_NewUint32()
- updated to Unicode 13.0.0
- misc bug fixes
2020-01-19:
- keep CONFIG_BIGNUM in the makefile
- added os.chdir()
- qjs: added -I option
- more memory checks in the bignum operations
- modified operator overloading semantics to be closer to the TC39
proposal
- suppressed "use bigint" mode. Simplified "use math" mode
- BigDecimal: changed suffix from 'd' to 'm'
- misc bug fixes
2020-01-05:
- always compile the bignum code. Added '--bignum' option to qjs.
- added BigDecimal
- added String.prototype.replaceAll
- misc bug fixes
2019-12-21:
- added nullish coalescing operator (ES2020)
- added optional chaining (ES2020)
- removed recursions in garbage collector
- test stack overflow in the parser
- improved backtrace logic
- added JS_SetHostPromiseRejectionTracker()
- allow exotic constructors
- improved c++ compatibility
- misc bug fixes
2019-10-27:
- added example of C class in a module (examples/test_point.js)
- added JS_GetTypedArrayBuffer()
- misc bug fixes
2019-09-18:
- added os.exec and other system calls
- exported JS_ValueToAtom()
- qjsc: added 'qjsc_' prefix to the generated C identifiers
- added cross-compilation support
- misc bug fixes
2019-09-01:
- added globalThis
- documented JS_EVAL_FLAG_COMPILE_ONLY
- added import.meta.url and import.meta.main
- added 'debugger' statement
- misc bug fixes
2019-08-18:
- added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat,
os.readlink, os.readdir, os.utimes, std.popen
- module autodetection
- added import.meta
- misc bug fixes
2019-08-10:
- added public class fields and private class fields, methods and
accessors (TC39 proposal)
- changed JS_ToCStringLen() prototype
- qjsc: handle '-' in module names and modules with the same filename
- added std.urlGet
- exported JS_GetOwnPropertyNames() and JS_GetOwnProperty()
- exported some bigint C functions
- added support for eshost in run-test262
- misc bug fixes
2019-07-28:
- added dynamic import
- added Promise.allSettled
- added String.prototype.matchAll
- added Object.fromEntries
- reduced number of ticks in await
- added BigInt support in Atomics
- exported JS_NewPromiseCapability()
- misc async function and async generator fixes
- enabled hashbang support by default
2019-07-21:
- updated test262 tests
- updated to Unicode version 12.1.0
- fixed missing Date object in qjsc
- fixed multi-context creation
- misc ES2020 related fixes
- simplified power and division operators in bignum extension
- fixed several crash conditions
2019-07-09:
- first public release

View File

@@ -0,0 +1,468 @@
#
# QuickJS Javascript Engine
#
# Copyright (c) 2017-2020 Fabrice Bellard
# Copyright (c) 2017-2020 Charlie Gordon
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
ifeq ($(shell uname -s),Darwin)
CONFIG_DARWIN=y
endif
# Windows cross compilation from Linux
#CONFIG_WIN32=y
# use link time optimization (smaller and faster executables but slower build)
CONFIG_LTO=y
# consider warnings as errors (for development)
#CONFIG_WERROR=y
# force 32 bit build for some utilities
#CONFIG_M32=y
ifdef CONFIG_DARWIN
# use clang instead of gcc
CONFIG_CLANG=y
CONFIG_DEFAULT_AR=y
endif
# installation directory
prefix=/usr/local
# use the gprof profiler
#CONFIG_PROFILE=y
# use address sanitizer
#CONFIG_ASAN=y
# include the code for BigInt/BigFloat/BigDecimal and math mode
CONFIG_BIGNUM=y
OBJDIR=.obj
ifdef CONFIG_WIN32
CROSS_PREFIX=i686-w64-mingw32-
EXE=.exe
else
CROSS_PREFIX=
EXE=
endif
ifdef CONFIG_CLANG
HOST_CC=clang
CC=$(CROSS_PREFIX)clang
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wextra
CFLAGS += -Wno-sign-compare
CFLAGS += -Wno-missing-field-initializers
CFLAGS += -Wundef -Wuninitialized
CFLAGS += -Wunused -Wno-unused-parameter
CFLAGS += -Wwrite-strings
CFLAGS += -Wchar-subscripts -funsigned-char
CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d
ifdef CONFIG_DEFAULT_AR
AR=$(CROSS_PREFIX)ar
else
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)llvm-ar
else
AR=$(CROSS_PREFIX)ar
endif
endif
else
HOST_CC=gcc
CC=$(CROSS_PREFIX)gcc
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wno-array-bounds -Wno-format-truncation
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)gcc-ar
else
AR=$(CROSS_PREFIX)ar
endif
endif
STRIP=$(CROSS_PREFIX)strip
ifdef CONFIG_WERROR
CFLAGS+=-Werror
endif
DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
ifdef CONFIG_BIGNUM
DEFINES+=-DCONFIG_BIGNUM
endif
ifdef CONFIG_WIN32
DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior
endif
CFLAGS+=$(DEFINES)
CFLAGS_DEBUG=$(CFLAGS) -O0
CFLAGS_SMALL=$(CFLAGS) -Os
CFLAGS_OPT=$(CFLAGS) -O2
CFLAGS_NOLTO:=$(CFLAGS_OPT)
LDFLAGS=-g
ifdef CONFIG_LTO
CFLAGS_SMALL+=-flto
CFLAGS_OPT+=-flto
LDFLAGS+=-flto
endif
ifdef CONFIG_PROFILE
CFLAGS+=-p
LDFLAGS+=-p
endif
ifdef CONFIG_ASAN
CFLAGS+=-fsanitize=address -fno-omit-frame-pointer
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer
endif
ifdef CONFIG_WIN32
LDEXPORT=
else
LDEXPORT=-rdynamic
endif
PROGS=qjs$(EXE) qjsc$(EXE) run-test262
ifneq ($(CROSS_PREFIX),)
QJSC_CC=gcc
QJSC=./host-qjsc
PROGS+=$(QJSC)
else
QJSC_CC=$(CC)
QJSC=./qjsc$(EXE)
endif
ifndef CONFIG_WIN32
PROGS+=qjscalc
endif
ifdef CONFIG_M32
PROGS+=qjs32 qjs32_s
endif
PROGS+=libquickjs.a
ifdef CONFIG_LTO
PROGS+=libquickjs.lto.a
endif
# examples
ifeq ($(CROSS_PREFIX),)
ifdef CONFIG_ASAN
PROGS+=
else
PROGS+=examples/hello examples/hello_module examples/test_fib
ifndef CONFIG_DARWIN
PROGS+=examples/fib.so examples/point.so
endif
endif
endif
all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)
QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
ifdef CONFIG_BIGNUM
QJS_LIB_OBJS+=$(OBJDIR)/libbf.o
QJS_OBJS+=$(OBJDIR)/qjscalc.o
endif
HOST_LIBS=-lm -ldl -lpthread
LIBS=-lm
ifndef CONFIG_WIN32
LIBS+=-ldl -lpthread
endif
$(OBJDIR):
mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests
qjs$(EXE): $(QJS_OBJS)
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
ifneq ($(CROSS_PREFIX),)
$(QJSC): $(OBJDIR)/qjsc.host.o \
$(patsubst %.o, %.host.o, $(QJS_LIB_OBJS))
$(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS)
endif #CROSS_PREFIX
QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
ifdef CONFIG_LTO
QJSC_DEFINES+=-DCONFIG_LTO
endif
QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES)
$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES)
qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
@size $@
qjscalc: qjs
ln -sf $< $@
ifdef CONFIG_LTO
LTOEXT=.lto
else
LTOEXT=
endif
libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS)
$(AR) rcs $@ $^
ifdef CONFIG_LTO
libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^
endif # CONFIG_LTO
repl.c: $(QJSC) repl.js
$(QJSC) -c -o $@ -m repl.js
qjscalc.c: $(QJSC) qjscalc.js
$(QJSC) -fbignum -c -o $@ qjscalc.js
ifneq ($(wildcard unicode/UnicodeData.txt),)
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \
$(OBJDIR)/libunicode.nolto.o: libunicode-table.h
libunicode-table.h: unicode_gen
./unicode_gen unicode $@
endif
run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
# object suffix order: nolto, [m32|m32s]
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.host.o: %.c | $(OBJDIR)
$(HOST_CC) $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.pic.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $<
$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_NOLTO) -c -o $@ $<
$(OBJDIR)/%.m32.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $<
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_DEBUG) -c -o $@ $<
$(OBJDIR)/%.check.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<
regexp_test: libregexp.c libunicode.c cutils.c
$(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS)
jscompress: jscompress.c
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ jscompress.c
unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h
$(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o
clean:
rm -f repl.c qjscalc.c out.c
rm -f *.a *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS)
rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug
rm -rf run-test262-debug run-test262-32
install: all
mkdir -p "$(DESTDIR)$(prefix)/bin"
$(STRIP) qjs qjsc
install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin"
ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs"
ifdef CONFIG_LTO
install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs"
endif
mkdir -p "$(DESTDIR)$(prefix)/include/quickjs"
install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(prefix)/include/quickjs"
###############################################################################
# examples
# example of static JS compilation
HELLO_SRCS=examples/hello.js
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
-fno-date -fno-module-loader
ifdef CONFIG_BIGNUM
HELLO_OPTS+=-fno-bigint
endif
hello.c: $(QJSC) $(HELLO_SRCS)
$(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
ifdef CONFIG_M32
examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
else
examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
endif
# example of static JS compilation with modules
HELLO_MODULE_SRCS=examples/hello_module.js
HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
-fno-date -m
examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS)
$(QJSC) $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS)
# use of an external C module (static compilation)
test_fib.c: $(QJSC) examples/test_fib.js
$(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js
examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
examples/fib.so: $(OBJDIR)/examples/fib.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^
examples/point.so: $(OBJDIR)/examples/point.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^
###############################################################################
# documentation
DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html
build_doc: $(DOCS)
clean_doc:
rm -f $(DOCS)
doc/%.pdf: doc/%.texi
texi2pdf --clean -o $@ -q $<
doc/%.html.pre: doc/%.texi
makeinfo --html --no-headers --no-split --number-sections -o $@ $<
doc/%.html: doc/%.html.pre
sed -e 's|</style>|</style>\n<meta name="viewport" content="width=device-width, initial-scale=1.0">|' < $< > $@
###############################################################################
# tests
ifndef CONFIG_DARWIN
test: tests/bjson.so examples/point.so
endif
ifdef CONFIG_M32
test: qjs32
endif
test: qjs
./qjs tests/test_closure.js
./qjs tests/test_language.js
./qjs tests/test_builtin.js
./qjs tests/test_loop.js
./qjs tests/test_std.js
./qjs tests/test_worker.js
ifndef CONFIG_DARWIN
ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_bjson.js
else
./qjs tests/test_bjson.js
endif
./qjs examples/test_point.js
endif
ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_op_overloading.js
./qjs --bignum tests/test_bignum.js
./qjs --qjscalc tests/test_qjscalc.js
endif
ifdef CONFIG_M32
./qjs32 tests/test_closure.js
./qjs32 tests/test_language.js
./qjs32 tests/test_builtin.js
./qjs32 tests/test_loop.js
./qjs32 tests/test_std.js
./qjs32 tests/test_worker.js
ifdef CONFIG_BIGNUM
./qjs32 --bignum tests/test_op_overloading.js
./qjs32 --bignum tests/test_bignum.js
./qjs32 --qjscalc tests/test_qjscalc.js
endif
endif
stats: qjs qjs32
./qjs -qd
./qjs32 -qd
microbench: qjs
./qjs tests/microbench.js
microbench-32: qjs32
./qjs32 tests/microbench.js
# ES5 tests (obsolete)
test2o: run-test262
time ./run-test262 -m -c test262o.conf
test2o-32: run-test262-32
time ./run-test262-32 -m -c test262o.conf
test2o-update: run-test262
./run-test262 -u -c test262o.conf
# Test262 tests
test2-default: run-test262
time ./run-test262 -m -c test262.conf
test2: run-test262
time ./run-test262 -m -c test262.conf -a
test2-32: run-test262-32
time ./run-test262-32 -m -c test262.conf -a
test2-update: run-test262
./run-test262 -u -c test262.conf -a
test2-check: run-test262
time ./run-test262 -m -c test262.conf -E -a
testall: all test microbench test2o test2
testall-32: all test-32 microbench-32 test2o-32 test2-32
testall-complete: testall testall-32
bench-v8: qjs
make -C tests/bench-v8
./qjs -d tests/bench-v8/combined.js
tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
-include $(wildcard $(OBJDIR)/*.d)

View File

@@ -0,0 +1,78 @@
Misc:
- use realpath in module name normalizer and put it in quickjs-libc
- use custom printf to avoid C library compatibility issues
- rename CONFIG_ALL_UNICODE, CONFIG_BIGNUM, CONFIG_ATOMICS, CONFIG_CHECK_JSVALUE ?
- unify coding style and naming conventions
- use names from the ECMA spec in library implementation
- modules: if no ".", use a well known module loading path ?
- use JSHoistedDef only for global variables (JSHoistedDef.var_name != JS_ATOM_NULL)
- add index in JSVarDef and is_arg flag to merge args and vars in JSFunctionDef
- replace most JSVarDef flags with var_type enumeration
- use byte code emitters with typed arguments (for clarity)
- use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing
and use the same wrappers in all phases
- use more generic method for line numbers in resolve_variables and resolve_labels
- use custom timezone support to avoid C library compatibility issues
Memory:
- test border cases for max number of atoms, object properties, string length
- add emergency malloc mode for out of memory exceptions.
- test all DynBuf memory errors
- test all js_realloc memory errors
- bignum: handle memory errors
- use memory pools for objects, etc?
- improve JS_ComputeMemoryUsage() with more info
Optimizations:
- 64-bit atoms in 64-bit mode ?
- use auto-init properties for more global objects
- reuse stack slots for disjoint scopes, if strip
- optimize `for of` iterator for built-in array objects
- add heuristic to avoid some cycles in closures
- small String (0-2 charcodes) with immediate storage
- perform static string concatenation at compile time
- optimize string concatenation with ropes or miniropes?
- add implicit numeric strings for Uint32 numbers?
- optimize `s += a + b`, `s += a.b` and similar simple expressions
- ensure string canonical representation and optimise comparisons and hashes?
- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references
- optimize function storage with length and name accessors?
- property access optimization on the global object, functions,
prototypes and special non extensible objects.
- create object literals with the correct length by backpatching length argument
- remove redundant set_loc_uninitialized/check_uninitialized opcodes
- peephole optim: push_atom_value, to_propkey -> push_atom_value
- peephole optim: put_loc x, get_loc_check x -> set_loc x
- comparative performance benchmark
- use variable name when throwing uninitialized exception if available
- convert slow array to fast array when all properties != length are numeric
- optimize destructuring assignments for global and local variables
- implement some form of tail-call-optimization
- optimize OP_apply
- optimize f(...b)
Extensions:
- support more features in [features] section
- add built-in preprocessor in compiler, get rid of jscompress
handle #if, #ifdef, #line, limited support for #define
- get rid of __loadScript, use more common name
- BSD sockets
REPL:
- debugger
- readline: support MS Windows terminal
- readline: handle dynamic terminal resizing
- readline: handle double width unicode characters
- multiline editing
- runtime object and function inspectors
- interactive object browser
- use more generic approach to display evaluation results
- improve directive handling: dispatch, colorize, completion...
- save history
- close all predefined methods in repl.js and jscalc.js
Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Test262: 30/71095 errors, 870 excluded, 549 skipped
Test262 commit: 281eb10b2844929a7c0ac04527f5b42ce56509fd

View File

@@ -0,0 +1 @@
2020-07-05

View File

@@ -0,0 +1,620 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "cutils.h"
void pstrcpy(char *buf, int buf_size, const char *str)
{
int c;
char *q = buf;
if (buf_size <= 0)
return;
for(;;) {
c = *str++;
if (c == 0 || q >= buf + buf_size - 1)
break;
*q++ = c;
}
*q = '\0';
}
/* strcat and truncate. */
char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
int has_suffix(const char *str, const char *suffix)
{
size_t len = strlen(str);
size_t slen = strlen(suffix);
return (len >= slen && !memcmp(str + len - slen, suffix, slen));
}
/* Dynamic buffer package */
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
{
return realloc(ptr, size);
}
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
{
memset(s, 0, sizeof(*s));
if (!realloc_func)
realloc_func = dbuf_default_realloc;
s->opaque = opaque;
s->realloc_func = realloc_func;
}
void dbuf_init(DynBuf *s)
{
dbuf_init2(s, NULL, NULL);
}
/* return < 0 if error */
int dbuf_realloc(DynBuf *s, size_t new_size)
{
size_t size;
uint8_t *new_buf;
if (new_size > s->allocated_size) {
if (s->error)
return -1;
size = s->allocated_size * 3 / 2;
if (size > new_size)
new_size = size;
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
if (!new_buf) {
s->error = TRUE;
return -1;
}
s->buf = new_buf;
s->allocated_size = new_size;
}
return 0;
}
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
{
size_t end;
end = offset + len;
if (dbuf_realloc(s, end))
return -1;
memcpy(s->buf + offset, data, len);
if (end > s->size)
s->size = end;
return 0;
}
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy(s->buf + s->size, data, len);
s->size += len;
return 0;
}
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy(s->buf + s->size, s->buf + offset, len);
s->size += len;
return 0;
}
int dbuf_putc(DynBuf *s, uint8_t c)
{
return dbuf_put(s, &c, 1);
}
int dbuf_putstr(DynBuf *s, const char *str)
{
return dbuf_put(s, (const uint8_t *)str, strlen(str));
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...)
{
va_list ap;
char buf[128];
int len;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len < sizeof(buf)) {
/* fast case */
return dbuf_put(s, (uint8_t *)buf, len);
} else {
if (dbuf_realloc(s, s->size + len + 1))
return -1;
va_start(ap, fmt);
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
fmt, ap);
va_end(ap);
s->size += len;
}
return 0;
}
void dbuf_free(DynBuf *s)
{
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
is called twice */
if (s->buf) {
s->realloc_func(s->opaque, s->buf, 0);
}
memset(s, 0, sizeof(*s));
}
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
are output. */
int unicode_to_utf8(uint8_t *buf, unsigned int c)
{
uint8_t *q = buf;
if (c < 0x80) {
*q++ = c;
} else {
if (c < 0x800) {
*q++ = (c >> 6) | 0xc0;
} else {
if (c < 0x10000) {
*q++ = (c >> 12) | 0xe0;
} else {
if (c < 0x00200000) {
*q++ = (c >> 18) | 0xf0;
} else {
if (c < 0x04000000) {
*q++ = (c >> 24) | 0xf8;
} else if (c < 0x80000000) {
*q++ = (c >> 30) | 0xfc;
*q++ = ((c >> 24) & 0x3f) | 0x80;
} else {
return 0;
}
*q++ = ((c >> 18) & 0x3f) | 0x80;
}
*q++ = ((c >> 12) & 0x3f) | 0x80;
}
*q++ = ((c >> 6) & 0x3f) | 0x80;
}
*q++ = (c & 0x3f) | 0x80;
}
return q - buf;
}
static const unsigned int utf8_min_code[5] = {
0x80, 0x800, 0x10000, 0x00200000, 0x04000000,
};
static const unsigned char utf8_first_code_mask[5] = {
0x1f, 0xf, 0x7, 0x3, 0x1,
};
/* return -1 if error. *pp is not updated in this case. max_len must
be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
{
int l, c, b, i;
c = *p++;
if (c < 0x80) {
*pp = p;
return c;
}
switch(c) {
case 0xc0 ... 0xdf:
l = 1;
break;
case 0xe0 ... 0xef:
l = 2;
break;
case 0xf0 ... 0xf7:
l = 3;
break;
case 0xf8 ... 0xfb:
l = 4;
break;
case 0xfc ... 0xfd:
l = 5;
break;
default:
return -1;
}
/* check that we have enough characters */
if (l > (max_len - 1))
return -1;
c &= utf8_first_code_mask[l - 1];
for(i = 0; i < l; i++) {
b = *p++;
if (b < 0x80 || b >= 0xc0)
return -1;
c = (c << 6) | (b & 0x3f);
}
if (c < utf8_min_code[l - 1])
return -1;
*pp = p;
return c;
}
#if 0
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
static void *rqsort_arg;
static int (*rqsort_cmp)(const void *, const void *, void *);
static int rqsort_cmp2(const void *p1, const void *p2)
{
return rqsort_cmp(p1, p2, rqsort_arg);
}
/* not reentrant, but not needed with emscripten */
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg)
{
rqsort_arg = arg;
rqsort_cmp = cmp;
qsort(base, nmemb, size, rqsort_cmp2);
}
#endif
#else
typedef void (*exchange_f)(void *a, void *b, size_t size);
typedef int (*cmp_f)(const void *, const void *, void *opaque);
static void exchange_bytes(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
while (size-- != 0) {
uint8_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_byte(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
uint8_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int16s(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
for (size /= sizeof(uint16_t); size-- != 0;) {
uint16_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int16(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
uint16_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int32s(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
for (size /= sizeof(uint32_t); size-- != 0;) {
uint32_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int32(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
uint32_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int64s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t); size-- != 0;) {
uint64_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int64(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int128s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
}
static void exchange_one_int128(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
static inline exchange_f exchange_func(const void *base, size_t size) {
switch (((uintptr_t)base | (uintptr_t)size) & 15) {
case 0:
if (size == sizeof(uint64_t) * 2)
return exchange_one_int128;
else
return exchange_int128s;
case 8:
if (size == sizeof(uint64_t))
return exchange_one_int64;
else
return exchange_int64s;
case 4:
case 12:
if (size == sizeof(uint32_t))
return exchange_one_int32;
else
return exchange_int32s;
case 2:
case 6:
case 10:
case 14:
if (size == sizeof(uint16_t))
return exchange_one_int16;
else
return exchange_int16s;
default:
if (size == 1)
return exchange_one_byte;
else
return exchange_bytes;
}
}
static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
uint8_t *basep = (uint8_t *)base;
size_t i, n, c, r;
exchange_f swap = exchange_func(base, size);
if (nmemb > 1) {
i = (nmemb / 2) * size;
n = nmemb * size;
while (i > 0) {
i -= size;
for (r = i; (c = r * 2 + size) < n; r = c) {
if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
for (i = n - size; i > 0; i -= size) {
swap(basep, basep + i, size);
for (r = 0; (c = r * 2 + size) < i; r = c) {
if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
}
}
static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)
{
return cmp(a, b, opaque) < 0 ?
(cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :
(cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));
}
/* pointer based version with local stack and insertion sort threshhold */
void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;
uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;
size_t m4, i, lt, gt, span, span2;
int c, depth;
exchange_f swap = exchange_func(base, size);
exchange_f swap_block = exchange_func(base, size | 128);
if (nmemb < 2 || size <= 0)
return;
sp->base = (uint8_t *)base;
sp->count = nmemb;
sp->depth = 0;
sp++;
while (sp > stack) {
sp--;
ptr = sp->base;
nmemb = sp->count;
depth = sp->depth;
while (nmemb > 6) {
if (++depth > 50) {
/* depth check to ensure worst case logarithmic time */
heapsortx(ptr, nmemb, size, cmp, opaque);
nmemb = 0;
break;
}
/* select median of 3 from 1/4, 1/2, 3/4 positions */
/* should use median of 5 or 9? */
m4 = (nmemb >> 2) * size;
m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);
swap(ptr, m, size); /* move the pivot to the start or the array */
i = lt = 1;
pi = plt = ptr + size;
gt = nmemb;
pj = pgt = top = ptr + nmemb * size;
for (;;) {
while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {
if (c == 0) {
swap(plt, pi, size);
lt++;
plt += size;
}
i++;
pi += size;
}
while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {
if (c == 0) {
gt--;
pgt -= size;
swap(pgt, pj, size);
}
}
if (pi >= pj)
break;
swap(pi, pj, size);
i++;
pi += size;
}
/* array has 4 parts:
* from 0 to lt excluded: elements identical to pivot
* from lt to pi excluded: elements smaller than pivot
* from pi to gt excluded: elements greater than pivot
* from gt to n excluded: elements identical to pivot
*/
/* move elements identical to pivot in the middle of the array: */
/* swap values in ranges [0..lt[ and [i-lt..i[
swapping the smallest span between lt and i-lt is sufficient
*/
span = plt - ptr;
span2 = pi - plt;
lt = i - lt;
if (span > span2)
span = span2;
swap_block(ptr, pi - span, span);
/* swap values in ranges [gt..top[ and [i..top-(top-gt)[
swapping the smallest span between top-gt and gt-i is sufficient
*/
span = top - pgt;
span2 = pgt - pi;
pgt = top - span2;
gt = nmemb - (gt - i);
if (span > span2)
span = span2;
swap_block(pi, top - span, span);
/* now array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* stack the larger segment and keep processing the smaller one
to minimize stack use for pathological distributions */
if (lt > nmemb - gt) {
sp->base = ptr;
sp->count = lt;
sp->depth = depth;
sp++;
ptr = pgt;
nmemb -= gt;
} else {
sp->base = pgt;
sp->count = nmemb - gt;
sp->depth = depth;
sp++;
nmemb = lt;
}
}
/* Use insertion sort for small fragments */
for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {
for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)
swap(pj, pj - size, size);
}
}
}
#endif

View File

@@ -0,0 +1,297 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CUTILS_H
#define CUTILS_H
#include <stdlib.h>
#include <inttypes.h>
/* set if CPU is big endian */
#undef WORDS_BIGENDIAN
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define __maybe_unused __attribute__((unused))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif
typedef int BOOL;
#ifndef FALSE
enum {
FALSE = 0,
TRUE = 1,
};
#endif
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
{
if (a > b)
return a;
else
return b;
}
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
{
if (a < b)
return a;
else
return b;
}
static inline int64_t max_int64(int64_t a, int64_t b)
{
if (a > b)
return a;
else
return b;
}
static inline int64_t min_int64(int64_t a, int64_t b)
{
if (a < b)
return a;
else
return b;
}
/* WARNING: undefined if a = 0 */
static inline int clz32(unsigned int a)
{
return __builtin_clz(a);
}
/* WARNING: undefined if a = 0 */
static inline int clz64(uint64_t a)
{
return __builtin_clzll(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz32(unsigned int a)
{
return __builtin_ctz(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz64(uint64_t a)
{
return __builtin_ctzll(a);
}
struct __attribute__((packed)) packed_u64 {
uint64_t v;
};
struct __attribute__((packed)) packed_u32 {
uint32_t v;
};
struct __attribute__((packed)) packed_u16 {
uint16_t v;
};
static inline uint64_t get_u64(const uint8_t *tab)
{
return ((const struct packed_u64 *)tab)->v;
}
static inline int64_t get_i64(const uint8_t *tab)
{
return (int64_t)((const struct packed_u64 *)tab)->v;
}
static inline void put_u64(uint8_t *tab, uint64_t val)
{
((struct packed_u64 *)tab)->v = val;
}
static inline uint32_t get_u32(const uint8_t *tab)
{
return ((const struct packed_u32 *)tab)->v;
}
static inline int32_t get_i32(const uint8_t *tab)
{
return (int32_t)((const struct packed_u32 *)tab)->v;
}
static inline void put_u32(uint8_t *tab, uint32_t val)
{
((struct packed_u32 *)tab)->v = val;
}
static inline uint32_t get_u16(const uint8_t *tab)
{
return ((const struct packed_u16 *)tab)->v;
}
static inline int32_t get_i16(const uint8_t *tab)
{
return (int16_t)((const struct packed_u16 *)tab)->v;
}
static inline void put_u16(uint8_t *tab, uint16_t val)
{
((struct packed_u16 *)tab)->v = val;
}
static inline uint32_t get_u8(const uint8_t *tab)
{
return *tab;
}
static inline int32_t get_i8(const uint8_t *tab)
{
return (int8_t)*tab;
}
static inline void put_u8(uint8_t *tab, uint8_t val)
{
*tab = val;
}
static inline uint16_t bswap16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
static inline uint32_t bswap32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
static inline uint64_t bswap64(uint64_t v)
{
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
}
/* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
typedef struct DynBuf {
uint8_t *buf;
size_t size;
size_t allocated_size;
BOOL error; /* true if a memory allocation error occurred */
DynBufReallocFunc *realloc_func;
void *opaque; /* for realloc_func */
} DynBuf;
void dbuf_init(DynBuf *s);
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
int dbuf_realloc(DynBuf *s, size_t new_size);
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
int dbuf_putc(DynBuf *s, uint8_t c);
int dbuf_putstr(DynBuf *s, const char *str);
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
{
return dbuf_put(s, (uint8_t *)&val, 2);
}
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
{
return dbuf_put(s, (uint8_t *)&val, 4);
}
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
{
return dbuf_put(s, (uint8_t *)&val, 8);
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...);
void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
}
static inline void dbuf_set_error(DynBuf *s)
{
s->error = TRUE;
}
#define UTF8_CHAR_LEN_MAX 6
int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg);
#endif /* CUTILS_H */

View File

@@ -0,0 +1,918 @@
/*
* Javascript Compressor
*
* Copyright (c) 2008-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include "cutils.h"
typedef struct JSToken {
int tok;
char buf[20];
char *str;
int len;
int size;
int line_num; /* line number for start of token */
int lines; /* number of embedded linefeeds in token */
} JSToken;
enum {
TOK_EOF = 256,
TOK_IDENT,
TOK_STR1,
TOK_STR2,
TOK_STR3,
TOK_NUM,
TOK_COM,
TOK_LCOM,
};
void tok_reset(JSToken *tt)
{
if (tt->str != tt->buf) {
free(tt->str);
tt->str = tt->buf;
tt->size = sizeof(tt->buf);
}
tt->len = 0;
}
void tok_add_ch(JSToken *tt, int c)
{
if (tt->len + 1 > tt->size) {
tt->size *= 2;
if (tt->str == tt->buf) {
tt->str = malloc(tt->size);
memcpy(tt->str, tt->buf, tt->len);
} else {
tt->str = realloc(tt->str, tt->size);
}
}
tt->str[tt->len++] = c;
}
FILE *infile;
const char *filename;
int output_line_num;
int line_num;
int ch;
JSToken tokc;
int skip_mask;
#define DEFINE_MAX 20
char *define_tab[DEFINE_MAX];
int define_len;
void error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (filename) {
fprintf(stderr, "%s:%d: ", filename, line_num);
} else {
fprintf(stderr, "jscompress: ");
}
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
void define_symbol(const char *def)
{
int i;
for (i = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i]))
return;
}
if (define_len >= DEFINE_MAX)
error("too many defines");
define_tab[define_len++] = strdup(def);
}
void undefine_symbol(const char *def)
{
int i, j;
for (i = j = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i])) {
free(define_tab[i]);
} else {
define_tab[j++] = define_tab[i];
}
}
define_len = j;
}
const char *find_symbol(const char *def)
{
int i;
for (i = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i]))
return "1";
}
return NULL;
}
void next(void);
void nextch(void)
{
ch = fgetc(infile);
if (ch == '\n')
line_num++;
}
int skip_blanks(void)
{
for (;;) {
next();
if (tokc.tok != ' ' && tokc.tok != '\t' &&
tokc.tok != TOK_COM && tokc.tok != TOK_LCOM)
return tokc.tok;
}
}
void parse_directive(void)
{
int ifdef, mask = skip_mask;
/* simplistic preprocessor:
#define / #undef / #ifdef / #ifndef / #else / #endif
no symbol substitution.
*/
skip_mask = 0; /* disable skipping to parse preprocessor line */
nextch();
if (skip_blanks() != TOK_IDENT)
error("expected preprocessing directive after #");
if (!strcmp(tokc.str, "define")) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #define");
define_symbol(tokc.str);
} else if (!strcmp(tokc.str, "undef")) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #undef");
undefine_symbol(tokc.str);
} else if ((ifdef = 1, !strcmp(tokc.str, "ifdef")) ||
(ifdef = 0, !strcmp(tokc.str, "ifndef"))) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #ifdef/#ifndef");
mask = (mask << 2) | 2 | ifdef;
if (find_symbol(tokc.str))
mask ^= 1;
} else if (!strcmp(tokc.str, "else")) {
if (!(mask & 2))
error("#else without a #if");
mask ^= 1;
} else if (!strcmp(tokc.str, "endif")) {
if (!(mask & 2))
error("#endif without a #if");
mask >>= 2;
} else {
error("unsupported preprocessing directive");
}
if (skip_blanks() != '\n')
error("extra characters on preprocessing line");
skip_mask = mask;
}
/* return -1 if invalid char */
static int hex_to_num(int ch)
{
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else if (ch >= '0' && ch <= '9')
return ch - '0';
else
return -1;
}
void next(void)
{
again:
tok_reset(&tokc);
tokc.line_num = line_num;
tokc.lines = 0;
switch(ch) {
case EOF:
tokc.tok = TOK_EOF;
if (skip_mask)
error("missing #endif");
break;
case 'a' ... 'z':
case 'A' ... 'Z':
case '_':
case '$':
tok_add_ch(&tokc, ch);
nextch();
while ((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
(ch == '_' || ch == '$')) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_IDENT;
break;
case '.':
nextch();
if (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, '.');
goto has_dot;
}
tokc.tok = '.';
break;
case '0':
tok_add_ch(&tokc, ch);
nextch();
if (ch == 'x' || ch == 'X') {
/* hexa */
tok_add_ch(&tokc, ch);
nextch();
while ((ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F') ||
(ch >= '0' && ch <= '9')) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_NUM;
break;
}
goto has_digit;
case '1' ... '9':
tok_add_ch(&tokc, ch);
nextch();
has_digit:
/* decimal */
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
if (ch == '.') {
tok_add_ch(&tokc, ch);
nextch();
has_dot:
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
}
if (ch == 'e' || ch == 'E') {
tok_add_ch(&tokc, ch);
nextch();
if (ch == '+' || ch == '-') {
tok_add_ch(&tokc, ch);
nextch();
}
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_NUM;
break;
case '`':
{
nextch();
while (ch != '`' && ch != EOF) {
if (ch == '\\') {
tok_add_ch(&tokc, ch);
nextch();
if (ch == EOF) {
error("unexpected char after '\\'");
}
tok_add_ch(&tokc, ch);
} else {
tok_add_ch(&tokc, ch);
nextch();
}
}
nextch();
tok_add_ch(&tokc, 0);
tokc.tok = TOK_STR3;
}
break;
case '\"':
case '\'':
{
int n, i, c, hex_digit_count;
int quote_ch;
quote_ch = ch;
nextch();
while (ch != quote_ch && ch != EOF) {
if (ch == '\\') {
nextch();
switch(ch) {
case 'n':
tok_add_ch(&tokc, '\n');
nextch();
break;
case 'r':
tok_add_ch(&tokc, '\r');
nextch();
break;
case 't':
tok_add_ch(&tokc, '\t');
nextch();
break;
case 'v':
tok_add_ch(&tokc, '\v');
nextch();
break;
case '\"':
case '\'':
case '\\':
tok_add_ch(&tokc, ch);
nextch();
break;
case '0' ... '7':
n = 0;
while (ch >= '0' && ch <= '7') {
n = n * 8 + (ch - '0');
nextch();
}
tok_add_ch(&tokc, n);
break;
case 'x':
case 'u':
if (ch == 'x')
hex_digit_count = 2;
else
hex_digit_count = 4;
nextch();
n = 0;
for(i = 0; i < hex_digit_count; i++) {
c = hex_to_num(ch);
if (c < 0)
error("unexpected char after '\\x'");
n = n * 16 + c;
nextch();
}
if (n >= 256)
error("unicode is currently unsupported");
tok_add_ch(&tokc, n);
break;
default:
error("unexpected char after '\\'");
}
} else {
/* XXX: should refuse embedded newlines */
tok_add_ch(&tokc, ch);
nextch();
}
}
nextch();
tok_add_ch(&tokc, 0);
if (quote_ch == '\'')
tokc.tok = TOK_STR1;
else
tokc.tok = TOK_STR2;
}
break;
case '/':
nextch();
if (ch == '/') {
tok_add_ch(&tokc, '/');
tok_add_ch(&tokc, ch);
nextch();
while (ch != '\n' && ch != EOF) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_LCOM;
} else if (ch == '*') {
int last;
tok_add_ch(&tokc, '/');
tok_add_ch(&tokc, ch);
last = 0;
for(;;) {
nextch();
if (ch == EOF)
error("unterminated comment");
if (ch == '\n')
tokc.lines++;
tok_add_ch(&tokc, ch);
if (last == '*' && ch == '/')
break;
last = ch;
}
nextch();
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_COM;
} else {
tokc.tok = '/';
}
break;
case '#':
parse_directive();
goto again;
case '\n':
/* adjust line number */
tokc.line_num--;
tokc.lines++;
/* fall thru */
default:
tokc.tok = ch;
nextch();
break;
}
if (skip_mask & 1)
goto again;
}
void print_tok(FILE *f, JSToken *tt)
{
/* keep output lines in sync with input lines */
while (output_line_num < tt->line_num) {
putc('\n', f);
output_line_num++;
}
switch(tt->tok) {
case TOK_IDENT:
case TOK_COM:
case TOK_LCOM:
fprintf(f, "%s", tt->str);
break;
case TOK_NUM:
{
unsigned long a;
char *p;
a = strtoul(tt->str, &p, 0);
if (*p == '\0' && a <= 0x7fffffff) {
/* must be an integer */
fprintf(f, "%d", (int)a);
} else {
fprintf(f, "%s", tt->str);
}
}
break;
case TOK_STR3:
fprintf(f, "`%s`", tt->str);
break;
case TOK_STR1:
case TOK_STR2:
{
int i, c, quote_ch;
if (tt->tok == TOK_STR1)
quote_ch = '\'';
else
quote_ch = '\"';
fprintf(f, "%c", quote_ch);
for(i = 0; i < tt->len - 1; i++) {
c = (uint8_t)tt->str[i];
switch(c) {
case '\r':
fprintf(f, "\\r");
break;
case '\n':
fprintf(f, "\\n");
break;
case '\t':
fprintf(f, "\\t");
break;
case '\v':
fprintf(f, "\\v");
break;
case '\"':
case '\'':
if (c == quote_ch)
fprintf(f, "\\%c", c);
else
fprintf(f, "%c", c);
break;
case '\\':
fprintf(f, "\\\\");
break;
default:
/* XXX: no utf-8 support! */
if (c >= 32 && c <= 255) {
fprintf(f, "%c", c);
} else if (c <= 255)
fprintf(f, "\\x%02x", c);
else
fprintf(f, "\\u%04x", c);
break;
}
}
fprintf(f, "%c", quote_ch);
}
break;
default:
if (tokc.tok >= 256)
error("unsupported token in print_tok: %d", tt->tok);
fprintf(f, "%c", tt->tok);
break;
}
output_line_num += tt->lines;
}
/* check if token pasting could occur */
static BOOL compat_token(int c1, int c2)
{
if ((c1 == TOK_IDENT || c1 == TOK_NUM) &&
(c2 == TOK_IDENT || c2 == TOK_NUM))
return FALSE;
if ((c1 == c2 && strchr("+-<>&|=*/.", c1))
|| (c2 == '=' && strchr("+-<>&|!*/^%", c1))
|| (c1 == '=' && c2 == '>')
|| (c1 == '/' && c2 == '*')
|| (c1 == '.' && c2 == TOK_NUM)
|| (c1 == TOK_NUM && c2 == '.'))
return FALSE;
return TRUE;
}
void js_compress(const char *filename, const char *outfilename,
BOOL do_strip, BOOL keep_header)
{
FILE *outfile;
int ltok, seen_space;
line_num = 1;
infile = fopen(filename, "rb");
if (!infile) {
perror(filename);
exit(1);
}
output_line_num = 1;
outfile = fopen(outfilename, "wb");
if (!outfile) {
perror(outfilename);
exit(1);
}
nextch();
next();
ltok = 0;
seen_space = 0;
if (do_strip) {
if (keep_header) {
while (tokc.tok == ' ' ||
tokc.tok == '\n' ||
tokc.tok == '\t' ||
tokc.tok == '\v' ||
tokc.tok == '\b' ||
tokc.tok == '\f') {
seen_space = 1;
next();
}
if (tokc.tok == TOK_COM) {
print_tok(outfile, &tokc);
//fprintf(outfile, "\n");
ltok = tokc.tok;
seen_space = 0;
next();
}
}
for(;;) {
if (tokc.tok == TOK_EOF)
break;
if (tokc.tok == ' ' ||
tokc.tok == '\r' ||
tokc.tok == '\t' ||
tokc.tok == '\v' ||
tokc.tok == '\b' ||
tokc.tok == '\f' ||
tokc.tok == TOK_LCOM ||
tokc.tok == TOK_COM) {
/* don't print spaces or comments */
seen_space = 1;
} else if (tokc.tok == TOK_STR3) {
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
} else if (tokc.tok == TOK_STR1 || tokc.tok == TOK_STR2) {
int count, i;
/* find the optimal quote char */
count = 0;
for(i = 0; i < tokc.len; i++) {
if (tokc.str[i] == '\'')
count++;
else if (tokc.str[i] == '\"')
count--;
}
if (count > 0)
tokc.tok = TOK_STR2;
else if (count < 0)
tokc.tok = TOK_STR1;
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
} else {
if (seen_space && !compat_token(ltok, tokc.tok)) {
fprintf(outfile, " ");
}
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
}
next();
}
} else {
/* just handle preprocessing */
while (tokc.tok != TOK_EOF) {
print_tok(outfile, &tokc);
next();
}
}
fclose(outfile);
fclose(infile);
}
#define HASH_SIZE 30011
#define MATCH_LEN_MIN 3
#define MATCH_LEN_MAX (4 + 63)
#define DIST_MAX 65535
static int find_longest_match(int *pdist, const uint8_t *src, int src_len,
const int *hash_next, int cur_pos)
{
int pos, i, match_len, match_pos, pos_min, len_max;
len_max = min_int(src_len - cur_pos, MATCH_LEN_MAX);
match_len = 0;
match_pos = 0;
pos_min = max_int(cur_pos - DIST_MAX - 1, 0);
pos = hash_next[cur_pos];
while (pos >= pos_min) {
for(i = 0; i < len_max; i++) {
if (src[cur_pos + i] != src[pos + i])
break;
}
if (i > match_len) {
match_len = i;
match_pos = pos;
}
pos = hash_next[pos];
}
*pdist = cur_pos - match_pos - 1;
return match_len;
}
int lz_compress(uint8_t **pdst, const uint8_t *src, int src_len)
{
int *hash_table, *hash_next;
uint32_t h, v;
int i, dist, len, len1, dist1;
uint8_t *dst, *q;
/* build the hash table */
hash_table = malloc(sizeof(hash_table[0]) * HASH_SIZE);
for(i = 0; i < HASH_SIZE; i++)
hash_table[i] = -1;
hash_next = malloc(sizeof(hash_next[0]) * src_len);
for(i = 0; i < src_len; i++)
hash_next[i] = -1;
for(i = 0; i < src_len - MATCH_LEN_MIN + 1; i++) {
h = ((src[i] << 16) | (src[i + 1] << 8) | src[i + 2]) % HASH_SIZE;
hash_next[i] = hash_table[h];
hash_table[h] = i;
}
for(;i < src_len; i++) {
hash_next[i] = -1;
}
free(hash_table);
dst = malloc(src_len + 4); /* never larger than the source */
q = dst;
*q++ = src_len >> 24;
*q++ = src_len >> 16;
*q++ = src_len >> 8;
*q++ = src_len >> 0;
/* compress */
i = 0;
while (i < src_len) {
if (src[i] >= 128)
return -1;
len = find_longest_match(&dist, src, src_len, hash_next, i);
if (len >= MATCH_LEN_MIN) {
/* heuristic: see if better length just after */
len1 = find_longest_match(&dist1, src, src_len, hash_next, i + 1);
if (len1 > len)
goto no_match;
}
if (len < MATCH_LEN_MIN) {
no_match:
*q++ = src[i];
i++;
} else if (len <= (3 + 15) && dist < (1 << 10)) {
v = 0x8000 | ((len - 3) << 10) | dist;
*q++ = v >> 8;
*q++ = v;
i += len;
} else if (len >= 4 && len <= (4 + 63) && dist < (1 << 16)) {
v = 0xc00000 | ((len - 4) << 16) | dist;
*q++ = v >> 16;
*q++ = v >> 8;
*q++ = v;
i += len;
} else {
goto no_match;
}
}
free(hash_next);
*pdst = dst;
return q - dst;
}
static int load_file(uint8_t **pbuf, const char *filename)
{
FILE *f;
uint8_t *buf;
int buf_len;
f = fopen(filename, "rb");
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
buf_len = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(buf_len + 1);
fread(buf, 1, buf_len, f);
buf[buf_len] = '\0';
fclose(f);
*pbuf = buf;
return buf_len;
}
static void save_file(const char *filename, const uint8_t *buf, int buf_len)
{
FILE *f;
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fwrite(buf, 1, buf_len, f);
fclose(f);
}
static void save_c_source(const char *filename, const uint8_t *buf, int buf_len,
const char *var_name)
{
FILE *f;
int i;
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fprintf(f, "/* This file is automatically generated - do not edit */\n\n");
fprintf(f, "const uint8_t %s[] = {\n", var_name);
for(i = 0; i < buf_len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if ((i % 8) == 7 || (i == buf_len - 1))
fprintf(f, "\n");
}
fprintf(f, "};\n");
fclose(f);
}
#define DEFAULT_OUTPUT_FILENAME "out.js"
void help(void)
{
printf("jscompress version 1.0 Copyright (c) 2008-2018 Fabrice Bellard\n"
"usage: jscompress [options] filename\n"
"Javascript compressor\n"
"\n"
"-h print this help\n"
"-n do not compress spaces\n"
"-H keep the first comment\n"
"-c compress to file\n"
"-C name compress to C source ('name' is the variable name)\n"
"-D symbol define preprocessor symbol\n"
"-U symbol undefine preprocessor symbol\n"
"-o outfile set the output filename (default=%s)\n",
DEFAULT_OUTPUT_FILENAME);
exit(1);
}
int main(int argc, char **argv)
{
int c, do_strip, keep_header, compress;
const char *out_filename, *c_var, *fname;
char tmpfilename[1024];
do_strip = 1;
keep_header = 0;
out_filename = DEFAULT_OUTPUT_FILENAME;
compress = 0;
c_var = NULL;
for(;;) {
c = getopt(argc, argv, "hno:HcC:D:U:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
break;
case 'n':
do_strip = 0;
break;
case 'o':
out_filename = optarg;
break;
case 'H':
keep_header = 1;
break;
case 'c':
compress = 1;
break;
case 'C':
c_var = optarg;
compress = 1;
break;
case 'D':
define_symbol(optarg);
break;
case 'U':
undefine_symbol(optarg);
break;
}
}
if (optind >= argc)
help();
filename = argv[optind++];
if (compress) {
#if defined(__ANDROID__)
/* XXX: use another directory ? */
snprintf(tmpfilename, sizeof(tmpfilename), "out.%d", getpid());
#else
snprintf(tmpfilename, sizeof(tmpfilename), "/tmp/out.%d", getpid());
#endif
fname = tmpfilename;
} else {
fname = out_filename;
}
js_compress(filename, fname, do_strip, keep_header);
if (compress) {
uint8_t *buf1, *buf2;
int buf1_len, buf2_len;
buf1_len = load_file(&buf1, fname);
unlink(fname);
buf2_len = lz_compress(&buf2, buf1, buf1_len);
if (buf2_len < 0) {
fprintf(stderr, "Could not compress file (UTF8 chars are forbidden)\n");
exit(1);
}
if (c_var) {
save_c_source(out_filename, buf2, buf2_len, c_var);
} else {
save_file(out_filename, buf2, buf2_len);
}
free(buf1);
free(buf2);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,535 @@
/*
* Tiny arbitrary precision floating point library
*
* Copyright (c) 2017-2020 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBBF_H
#define LIBBF_H
#include <stddef.h>
#include <stdint.h>
#if defined(__x86_64__)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5
#endif
#define LIMB_BITS (1 << LIMB_LOG2_BITS)
#if LIMB_BITS == 64
typedef __int128 int128_t;
typedef unsigned __int128 uint128_t;
typedef int64_t slimb_t;
typedef uint64_t limb_t;
typedef uint128_t dlimb_t;
#define BF_RAW_EXP_MIN INT64_MIN
#define BF_RAW_EXP_MAX INT64_MAX
#define LIMB_DIGITS 19
#define BF_DEC_BASE UINT64_C(10000000000000000000)
#else
typedef int32_t slimb_t;
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
#define BF_RAW_EXP_MIN INT32_MIN
#define BF_RAW_EXP_MAX INT32_MAX
#define LIMB_DIGITS 9
#define BF_DEC_BASE 1000000000U
#endif
/* in bits */
/* minimum number of bits for the exponent */
#define BF_EXP_BITS_MIN 3
/* maximum number of bits for the exponent */
#define BF_EXP_BITS_MAX (LIMB_BITS - 3)
/* extended range for exponent, used internally */
#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1)
/* minimum possible precision */
#define BF_PREC_MIN 2
/* minimum possible precision */
#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2)
/* some operations support infinite precision */
#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */
#if LIMB_BITS == 64
#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197))
#else
#define BF_CHKSUM_MOD 975620677U
#endif
#define BF_EXP_ZERO BF_RAW_EXP_MIN
#define BF_EXP_INF (BF_RAW_EXP_MAX - 1)
#define BF_EXP_NAN BF_RAW_EXP_MAX
/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0,
+/-infinity is represented with expn = BF_EXP_INF and len = 0,
NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored)
*/
typedef struct {
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bf_t;
typedef struct {
/* must be kept identical to bf_t */
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bfdec_t;
typedef enum {
BF_RNDN, /* round to nearest, ties to even */
BF_RNDZ, /* round to zero */
BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */
BF_RNDU, /* round to +inf */
BF_RNDNA, /* round to nearest, ties away from zero */
BF_RNDA, /* round away from zero */
BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU,
inexact flag is always set) */
} bf_rnd_t;
/* allow subnormal numbers. Only available if the number of exponent
bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */
#define BF_FLAG_SUBNORMAL (1 << 3)
/* 'prec' is the precision after the radix point instead of the whole
mantissa. Can only be used with bf_round() and
bfdec_[add|sub|mul|div|sqrt|round](). */
#define BF_FLAG_RADPNT_PREC (1 << 4)
#define BF_RND_MASK 0x7
#define BF_EXP_BITS_SHIFT 5
#define BF_EXP_BITS_MASK 0x3f
/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */
#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)
/* contains the rounding mode and number of exponents bits */
typedef uint32_t bf_flags_t;
typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size);
typedef struct {
bf_t val;
limb_t prec;
} BFConstCache;
typedef struct bf_context_t {
void *realloc_opaque;
bf_realloc_func_t *realloc_func;
BFConstCache log2_cache;
BFConstCache pi_cache;
struct BFNTTState *ntt_state;
} bf_context_t;
static inline int bf_get_exp_bits(bf_flags_t flags)
{
int e;
e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK;
if (e == BF_EXP_BITS_MASK)
return BF_EXP_BITS_MAX + 1;
else
return BF_EXP_BITS_MAX - e;
}
static inline bf_flags_t bf_set_exp_bits(int n)
{
return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT;
}
/* returned status */
#define BF_ST_INVALID_OP (1 << 0)
#define BF_ST_DIVIDE_ZERO (1 << 1)
#define BF_ST_OVERFLOW (1 << 2)
#define BF_ST_UNDERFLOW (1 << 3)
#define BF_ST_INEXACT (1 << 4)
/* indicate that a memory allocation error occured. NaN is returned */
#define BF_ST_MEM_ERROR (1 << 5)
#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
static inline slimb_t bf_max(slimb_t a, slimb_t b)
{
if (a > b)
return a;
else
return b;
}
static inline slimb_t bf_min(slimb_t a, slimb_t b)
{
if (a < b)
return a;
else
return b;
}
void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
void *realloc_opaque);
void bf_context_end(bf_context_t *s);
/* free memory allocated for the bf cache data */
void bf_clear_cache(bf_context_t *s);
static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size)
{
return s->realloc_func(s->realloc_opaque, ptr, size);
}
/* 'size' must be != 0 */
static inline void *bf_malloc(bf_context_t *s, size_t size)
{
return bf_realloc(s, NULL, size);
}
static inline void bf_free(bf_context_t *s, void *ptr)
{
/* must test ptr otherwise equivalent to malloc(0) */
if (ptr)
bf_realloc(s, ptr, 0);
}
void bf_init(bf_context_t *s, bf_t *r);
static inline void bf_delete(bf_t *r)
{
bf_context_t *s = r->ctx;
/* we accept to delete a zeroed bf_t structure */
if (s && r->tab) {
bf_realloc(s, r->tab, 0);
}
}
static inline void bf_neg(bf_t *r)
{
r->sign ^= 1;
}
static inline int bf_is_finite(const bf_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bf_is_nan(const bf_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bf_is_zero(const bf_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bf_memcpy(bf_t *r, const bf_t *a)
{
*r = *a;
}
int bf_set_ui(bf_t *r, uint64_t a);
int bf_set_si(bf_t *r, int64_t a);
void bf_set_nan(bf_t *r);
void bf_set_zero(bf_t *r, int is_neg);
void bf_set_inf(bf_t *r, int is_neg);
int bf_set(bf_t *r, const bf_t *a);
void bf_move(bf_t *r, bf_t *a);
int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode);
int bf_set_float64(bf_t *a, double d);
int bf_cmpu(const bf_t *a, const bf_t *b);
int bf_cmp_full(const bf_t *a, const bf_t *b);
int bf_cmp(const bf_t *a, const bf_t *b);
static inline int bf_cmp_eq(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) == 0;
}
static inline int bf_cmp_le(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) <= 0;
}
static inline int bf_cmp_lt(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) < 0;
}
int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags);
int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
#define BF_DIVREM_EUCLIDIAN BF_RNDF
int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
/* round to integer with infinite precision */
int bf_rint(bf_t *r, int rnd_mode);
int bf_round(bf_t *r, limb_t prec, bf_flags_t flags);
int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a);
int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
slimb_t bf_get_exp_min(const bf_t *a);
int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b);
/* additional flags for bf_atof */
/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */
#define BF_ATOF_NO_HEX (1 << 16)
/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */
#define BF_ATOF_BIN_OCT (1 << 17)
/* Do not parse NaN or Inf */
#define BF_ATOF_NO_NAN_INF (1 << 18)
/* return the exponent separately */
#define BF_ATOF_EXPONENT (1 << 19)
int bf_atof(bf_t *a, const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
/* this version accepts prec = BF_PREC_INF and returns the radix
exponent */
int bf_atof2(bf_t *r, slimb_t *pexponent,
const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
slimb_t expn, limb_t prec, bf_flags_t flags);
/* Conversion of floating point number to string. Return a null
terminated string or NULL if memory error. *plen contains its
length if plen != NULL. The exponent letter is "e" for base 10,
"p" for bases 2, 8, 16 with a binary exponent and "@" for the other
bases. */
#define BF_FTOA_FORMAT_MASK (3 << 16)
/* fixed format: prec significant digits rounded with (flags &
BF_RND_MASK). Exponential notation is used if too many zeros are
needed.*/
#define BF_FTOA_FORMAT_FIXED (0 << 16)
/* fractional format: prec digits after the decimal point rounded with
(flags & BF_RND_MASK) */
#define BF_FTOA_FORMAT_FRAC (1 << 16)
/* free format:
For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum
number of digits to represent 'a'. The precision and the rounding
mode are ignored.
For the non binary radices with bf_ftoa(): use as many digits as
necessary so that bf_atof() return the same number when using
precision 'prec', rounding to nearest and the subnormal
configuration of 'flags'. The result is meaningful only if 'a' is
already rounded to 'prec' bits. If the subnormal flag is set, the
exponent in 'flags' must also be set to the desired exponent range.
*/
#define BF_FTOA_FORMAT_FREE (2 << 16)
/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits
(takes more computation time). Identical to BF_FTOA_FORMAT_FREE for
binary radices with bf_ftoa() and for bfdec_ftoa(). */
#define BF_FTOA_FORMAT_FREE_MIN (3 << 16)
/* force exponential notation for fixed or free format */
#define BF_FTOA_FORCE_EXP (1 << 20)
/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for
base 2 if non zero value */
#define BF_FTOA_ADD_PREFIX (1 << 21)
/* return "Infinity" instead of "Inf" and add a "+" for positive
exponents */
#define BF_FTOA_JS_QUIRKS (1 << 22)
char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
bf_flags_t flags);
/* modulo 2^n instead of saturation. NaN and infinity return 0 */
#define BF_GET_INT_MOD (1 << 0)
int bf_get_int32(int *pres, const bf_t *a, int flags);
int bf_get_int64(int64_t *pres, const bf_t *a, int flags);
int bf_get_uint64(uint64_t *pres, const bf_t *a);
/* the following functions are exported for testing only. */
void mp_print_str(const char *str, const limb_t *tab, limb_t n);
void bf_print_str(const char *str, const bf_t *a);
int bf_resize(bf_t *r, limb_t len);
int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len);
int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags);
int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k);
slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
int is_ceil1);
int mp_mul(bf_context_t *s, limb_t *result,
const limb_t *op1, limb_t op1_size,
const limb_t *op2, limb_t op2_size);
limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
limb_t n, limb_t carry);
limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n);
int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n);
int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n);
limb_t bf_isqrt(limb_t a);
/* transcendental functions */
int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */
int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags);
int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
limb_t prec, bf_flags_t flags);
int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
/* decimal floating point */
static inline void bfdec_init(bf_context_t *s, bfdec_t *r)
{
bf_init(s, (bf_t *)r);
}
static inline void bfdec_delete(bfdec_t *r)
{
bf_delete((bf_t *)r);
}
static inline void bfdec_neg(bfdec_t *r)
{
r->sign ^= 1;
}
static inline int bfdec_is_finite(const bfdec_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bfdec_is_nan(const bfdec_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bfdec_is_zero(const bfdec_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a)
{
bf_memcpy((bf_t *)r, (const bf_t *)a);
}
int bfdec_set_ui(bfdec_t *r, uint64_t a);
int bfdec_set_si(bfdec_t *r, int64_t a);
static inline void bfdec_set_nan(bfdec_t *r)
{
bf_set_nan((bf_t *)r);
}
static inline void bfdec_set_zero(bfdec_t *r, int is_neg)
{
bf_set_zero((bf_t *)r, is_neg);
}
static inline void bfdec_set_inf(bfdec_t *r, int is_neg)
{
bf_set_inf((bf_t *)r, is_neg);
}
static inline int bfdec_set(bfdec_t *r, const bfdec_t *a)
{
return bf_set((bf_t *)r, (bf_t *)a);
}
static inline void bfdec_move(bfdec_t *r, bfdec_t *a)
{
bf_move((bf_t *)r, (bf_t *)a);
}
static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmpu((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp_full((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) == 0;
}
static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) <= 0;
}
static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) < 0;
}
int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bfdec_rint(bfdec_t *r, int rnd_mode);
int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags);
int bfdec_get_int32(int *pres, const bfdec_t *a);
int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b);
char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
limb_t prec, bf_flags_t flags);
/* the following functions are exported for testing only. */
extern const limb_t mp_pow_dec[LIMB_DIGITS + 1];
void bfdec_print_str(const char *str, const bfdec_t *a);
static inline int bfdec_resize(bfdec_t *r, limb_t len)
{
return bf_resize((bf_t *)r, len);
}
int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags);
#endif /* LIBBF_H */

View File

@@ -0,0 +1,58 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DEF
DEF(invalid, 1) /* never used */
DEF(char, 3)
DEF(char32, 5)
DEF(dot, 1)
DEF(any, 1) /* same as dot but match any character including line terminator */
DEF(line_start, 1)
DEF(line_end, 1)
DEF(goto, 5)
DEF(split_goto_first, 5)
DEF(split_next_first, 5)
DEF(match, 1)
DEF(save_start, 2) /* save start position */
DEF(save_end, 2) /* save end position, must come after saved_start */
DEF(save_reset, 3) /* reset save positions */
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
DEF(push_i32, 5) /* push integer on the stack */
DEF(drop, 1)
DEF(word_boundary, 1)
DEF(not_word_boundary, 1)
DEF(back_reference, 2)
DEF(backward_back_reference, 2) /* must come after back_reference */
DEF(range, 3) /* variable length */
DEF(range32, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character
position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)
#endif /* DEF */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBREGEXP_H
#define LIBREGEXP_H
#include <stddef.h>
#include "libunicode.h"
#define LRE_BOOL int /* for documentation purposes */
#define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1)
#define LRE_FLAG_MULTILINE (1 << 2)
#define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UTF16 (1 << 4)
#define LRE_FLAG_STICKY (1 << 5)
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
LRE_BOOL lre_is_space(int c);
/* must be provided by the user */
LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size);
void *lre_realloc(void *opaque, void *ptr, size_t size);
/* JS identifier test */
extern uint32_t const lre_id_start_table_ascii[4];
extern uint32_t const lre_id_continue_table_ascii[4];
static inline int lre_js_is_ident_first(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space(c);
#endif
}
}
static inline int lre_js_is_ident_next(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
#else
return !lre_is_space(c) || c == 0x200C || c == 0x200D;
#endif
}
}
#undef LRE_BOOL
#endif /* LIBREGEXP_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
/*
* Unicode utilities
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBUNICODE_H
#define LIBUNICODE_H
#include <inttypes.h>
#define LRE_BOOL int /* for documentation purposes */
/* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
LRE_BOOL lre_is_cased(uint32_t c);
LRE_BOOL lre_is_case_ignorable(uint32_t c);
/* char ranges */
typedef struct {
int len; /* in points, always even */
int size;
uint32_t *points; /* points sorted by increasing value */
void *mem_opaque;
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
} CharRange;
typedef enum {
CR_OP_UNION,
CR_OP_INTER,
CR_OP_XOR,
} CharRangeOpEnum;
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
void cr_free(CharRange *cr);
int cr_realloc(CharRange *cr, int size);
int cr_copy(CharRange *cr, const CharRange *cr1);
static inline int cr_add_point(CharRange *cr, uint32_t v)
{
if (cr->len >= cr->size) {
if (cr_realloc(cr, cr->len + 1))
return -1;
}
cr->points[cr->len++] = v;
return 0;
}
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
if ((cr->len + 2) > cr->size) {
if (cr_realloc(cr, cr->len + 2))
return -1;
}
cr->points[cr->len++] = c1;
cr->points[cr->len++] = c2;
return 0;
}
int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
uint32_t b_pt[2];
b_pt[0] = c1;
b_pt[1] = c2 + 1;
return cr_union1(cr, b_pt, 2);
}
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
const uint32_t *b_pt, int b_len, int op);
int cr_invert(CharRange *cr);
#ifdef CONFIG_ALL_UNICODE
LRE_BOOL lre_is_id_start(uint32_t c);
LRE_BOOL lre_is_id_continue(uint32_t c);
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type,
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
/* Unicode character range functions */
int unicode_script(CharRange *cr,
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name);
#endif /* CONFIG_ALL_UNICODE */
#undef LRE_BOOL
#endif /* LIBUNICODE_H */

View File

@@ -0,0 +1,100 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) \
((type *)((uint8_t *)(el) - offsetof(type, member)))
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

View File

@@ -0,0 +1,551 @@
/*
* QuickJS stand alone interpreter
*
* Copyright (c) 2017-2020 Fabrice Bellard
* Copyright (c) 2017-2020 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#if defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__linux__)
#include <malloc.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size;
#ifdef CONFIG_BIGNUM
extern const uint8_t qjsc_qjscalc[];
extern const uint32_t qjsc_qjscalc_size;
#endif
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
const char *filename, int eval_flags)
{
JSValue val;
int ret;
if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) {
/* for the modules, we compile then run to be able to set
import.meta */
val = JS_Eval(ctx, buf, buf_len, filename,
eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(val)) {
js_module_set_import_meta(ctx, val, TRUE, TRUE);
val = JS_EvalFunction(ctx, val);
}
} else {
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
}
if (JS_IsException(val)) {
js_std_dump_error(ctx);
ret = -1;
} else {
ret = 0;
}
JS_FreeValue(ctx, val);
return ret;
}
static int eval_file(JSContext *ctx, const char *filename, int module)
{
uint8_t *buf;
int ret, eval_flags;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
perror(filename);
exit(1);
}
if (module < 0) {
module = (has_suffix(filename, ".mjs") ||
JS_DetectModule((const char *)buf, buf_len));
}
if (module)
eval_flags = JS_EVAL_TYPE_MODULE;
else
eval_flags = JS_EVAL_TYPE_GLOBAL;
ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
js_free(ctx, buf);
return ret;
}
#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
#define MALLOC_OVERHEAD 8
#endif
struct trace_malloc_data {
uint8_t *base;
};
static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
struct trace_malloc_data *dp)
{
return ptr - dp->base;
}
/* default memory allocation functions with memory limitation */
static inline size_t js_trace_malloc_usable_size(void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize(ptr);
#elif defined(EMSCRIPTEN)
return 0;
#elif defined(__linux__)
return malloc_usable_size(ptr);
#else
/* change this to `return 0;` if compilation fails */
return malloc_usable_size(ptr);
#endif
}
static void __attribute__((format(printf, 2, 3)))
js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
{
va_list ap;
int c;
va_start(ap, fmt);
while ((c = *fmt++) != '\0') {
if (c == '%') {
/* only handle %p and %zd */
if (*fmt == 'p') {
uint8_t *ptr = va_arg(ap, void *);
if (ptr == NULL) {
printf("NULL");
} else {
printf("H%+06lld.%zd",
js_trace_malloc_ptr_offset(ptr, s->opaque),
js_trace_malloc_usable_size(ptr));
}
fmt++;
continue;
}
if (fmt[0] == 'z' && fmt[1] == 'd') {
size_t sz = va_arg(ap, size_t);
printf("%zd", sz);
fmt += 2;
continue;
}
}
putc(c, stdout);
}
va_end(ap);
}
static void js_trace_malloc_init(struct trace_malloc_data *s)
{
free(s->base = malloc(8));
}
static void *js_trace_malloc(JSMallocState *s, size_t size)
{
void *ptr;
/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);
if (unlikely(s->malloc_size + size > s->malloc_limit))
return NULL;
ptr = malloc(size);
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
if (ptr) {
s->malloc_count++;
s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
}
return ptr;
}
static void js_trace_free(JSMallocState *s, void *ptr)
{
if (!ptr)
return;
js_trace_malloc_printf(s, "F %p\n", ptr);
s->malloc_count--;
s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
free(ptr);
}
static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
{
size_t old_size;
if (!ptr) {
if (size == 0)
return NULL;
return js_trace_malloc(s, size);
}
old_size = js_trace_malloc_usable_size(ptr);
if (size == 0) {
js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
s->malloc_count--;
s->malloc_size -= old_size + MALLOC_OVERHEAD;
free(ptr);
return NULL;
}
if (s->malloc_size + size - old_size > s->malloc_limit)
return NULL;
js_trace_malloc_printf(s, "R %zd %p", size, ptr);
ptr = realloc(ptr, size);
js_trace_malloc_printf(s, " -> %p\n", ptr);
if (ptr) {
s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
}
return ptr;
}
static const JSMallocFunctions trace_mf = {
js_trace_malloc,
js_trace_free,
js_trace_realloc,
#if defined(__APPLE__)
malloc_size,
#elif defined(_WIN32)
(size_t (*)(const void *))_msize,
#elif defined(EMSCRIPTEN)
NULL,
#elif defined(__linux__)
(size_t (*)(const void *))malloc_usable_size,
#else
/* change this to `NULL,` if compilation fails */
malloc_usable_size,
#endif
};
#define PROG_NAME "qjs"
void help(void)
{
printf("QuickJS version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [file [args]]\n"
"-h --help list options\n"
"-e --eval EXPR evaluate EXPR\n"
"-i --interactive go to interactive mode\n"
"-m --module load as ES6 module (default=autodetect)\n"
" --script load as ES6 script (default=autodetect)\n"
"-I --include file include an additional file\n"
" --std make 'std' and 'os' available to the loaded script\n"
#ifdef CONFIG_BIGNUM
" --bignum enable the bignum extensions (BigFloat, BigDecimal)\n"
" --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n"
#endif
"-T --trace trace memory allocation\n"
"-d --dump dump the memory usage stats\n"
" --memory-limit n limit the memory usage to 'n' bytes\n"
" --stack-size n limit the stack size to 'n' bytes\n"
" --unhandled-rejection dump unhandled promise rejections\n"
"-q --quit just instantiate the interpreter and quit\n");
exit(1);
}
int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
struct trace_malloc_data trace_data = { NULL };
int optind;
char *expr = NULL;
int interactive = 0;
int dump_memory = 0;
int trace_memory = 0;
int empty_run = 0;
int module = -1;
int load_std = 0;
int dump_unhandled_promise_rejection = 0;
size_t memory_limit = 0;
char *include_list[32];
int i, include_count = 0;
#ifdef CONFIG_BIGNUM
int load_jscalc, bignum_ext = 0;
#endif
size_t stack_size = 0;
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
const char *p, *exename;
exename = argv[0];
p = strrchr(exename, '/');
if (p)
exename = p + 1;
load_jscalc = !strcmp(exename, "qjscalc");
}
#endif
/* cannot use getopt because we want to pass the command line to
the script */
optind = 1;
while (optind < argc && *argv[optind] == '-') {
char *arg = argv[optind] + 1;
const char *longopt = "";
/* a single - is not an option, it also stops argument scanning */
if (!*arg)
break;
optind++;
if (*arg == '-') {
longopt = arg + 1;
arg += strlen(arg);
/* -- stops argument scanning */
if (!*longopt)
break;
}
for (; *arg || *longopt; longopt = "") {
char opt = *arg;
if (opt)
arg++;
if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
help();
continue;
}
if (opt == 'e' || !strcmp(longopt, "eval")) {
if (*arg) {
expr = arg;
break;
}
if (optind < argc) {
expr = argv[optind++];
break;
}
fprintf(stderr, "qjs: missing expression for -e\n");
exit(2);
}
if (opt == 'I' || !strcmp(longopt, "include")) {
if (optind >= argc) {
fprintf(stderr, "expecting filename");
exit(1);
}
if (include_count >= countof(include_list)) {
fprintf(stderr, "too many included files");
exit(1);
}
include_list[include_count++] = argv[optind++];
continue;
}
if (opt == 'i' || !strcmp(longopt, "interactive")) {
interactive++;
continue;
}
if (opt == 'm' || !strcmp(longopt, "module")) {
module = 1;
continue;
}
if (!strcmp(longopt, "script")) {
module = 0;
continue;
}
if (opt == 'd' || !strcmp(longopt, "dump")) {
dump_memory++;
continue;
}
if (opt == 'T' || !strcmp(longopt, "trace")) {
trace_memory++;
continue;
}
if (!strcmp(longopt, "std")) {
load_std = 1;
continue;
}
if (!strcmp(longopt, "unhandled-rejection")) {
dump_unhandled_promise_rejection = 1;
continue;
}
#ifdef CONFIG_BIGNUM
if (!strcmp(longopt, "bignum")) {
bignum_ext = 1;
continue;
}
if (!strcmp(longopt, "qjscalc")) {
load_jscalc = 1;
continue;
}
#endif
if (opt == 'q' || !strcmp(longopt, "quit")) {
empty_run++;
continue;
}
if (!strcmp(longopt, "memory-limit")) {
if (optind >= argc) {
fprintf(stderr, "expecting memory limit");
exit(1);
}
memory_limit = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (!strcmp(longopt, "stack-size")) {
if (optind >= argc) {
fprintf(stderr, "expecting stack size");
exit(1);
}
stack_size = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (opt) {
fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
} else {
fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
}
help();
}
}
if (trace_memory) {
js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data);
} else {
rt = JS_NewRuntime();
}
if (!rt) {
fprintf(stderr, "qjs: cannot allocate JS runtime\n");
exit(2);
}
if (memory_limit != 0)
JS_SetMemoryLimit(rt, memory_limit);
if (stack_size != 0)
JS_SetMaxStackSize(rt, stack_size);
js_std_init_handlers(rt);
ctx = JS_NewContext(rt);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
exit(2);
}
#ifdef CONFIG_BIGNUM
if (bignum_ext || load_jscalc) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_AddIntrinsicOperators(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
if (dump_unhandled_promise_rejection) {
JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
NULL);
}
if (!empty_run) {
#ifdef CONFIG_BIGNUM
if (load_jscalc) {
js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0);
}
#endif
js_std_add_helpers(ctx, argc - optind, argv + optind);
/* system modules */
js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os");
/* make 'std' and 'os' visible to non module code */
if (load_std) {
const char *str = "import * as std from 'std';\n"
"import * as os from 'os';\n"
"globalThis.std = std;\n"
"globalThis.os = os;\n";
eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
}
for(i = 0; i < include_count; i++) {
if (eval_file(ctx, include_list[i], module))
goto fail;
}
if (expr) {
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
goto fail;
} else
if (optind >= argc) {
/* interactive mode */
interactive = 1;
} else {
const char *filename;
filename = argv[optind];
if (eval_file(ctx, filename, module))
goto fail;
}
if (interactive) {
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
}
js_std_loop(ctx);
}
if (dump_memory) {
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
JS_DumpMemoryUsage(stdout, &stats, rt);
}
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
if (empty_run && dump_memory) {
clock_t t[5];
double best[5];
int i, j;
for (i = 0; i < 100; i++) {
t[0] = clock();
rt = JS_NewRuntime();
t[1] = clock();
ctx = JS_NewContext(rt);
t[2] = clock();
JS_FreeContext(ctx);
t[3] = clock();
JS_FreeRuntime(rt);
t[4] = clock();
for (j = 4; j > 0; j--) {
double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC;
if (i == 0 || best[j] > ms)
best[j] = ms;
}
}
printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n",
best[1] + best[2] + best[3] + best[4],
best[1], best[2], best[3], best[4]);
}
return 0;
fail:
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 1;
}

View File

@@ -0,0 +1,728 @@
/*
* QuickJS command line compiler
*
* Copyright (c) 2018-2020 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
typedef struct {
char *name;
char *short_name;
int flags;
} namelist_entry_t;
typedef struct namelist_t {
namelist_entry_t *array;
int count;
int size;
} namelist_t;
typedef struct {
const char *option_name;
const char *init_name;
} FeatureEntry;
static namelist_t cname_list;
static namelist_t cmodule_list;
static namelist_t init_module_list;
static uint64_t feature_bitmap;
static FILE *outfile;
static BOOL byte_swap;
static BOOL dynamic_export;
static const char *c_ident_prefix = "qjsc_";
#define FE_ALL (-1)
static const FeatureEntry feature_list[] = {
{ "date", "Date" },
{ "eval", "Eval" },
{ "string-normalize", "StringNormalize" },
{ "regexp", "RegExp" },
{ "json", "JSON" },
{ "proxy", "Proxy" },
{ "map", "MapSet" },
{ "typedarray", "TypedArrays" },
{ "promise", "Promise" },
#define FE_MODULE_LOADER 9
{ "module-loader", NULL },
#ifdef CONFIG_BIGNUM
{ "bigint", "BigInt" },
#endif
};
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
int flags)
{
namelist_entry_t *e;
if (lp->count == lp->size) {
size_t newsize = lp->size + (lp->size >> 1) + 4;
namelist_entry_t *a =
realloc(lp->array, sizeof(lp->array[0]) * newsize);
/* XXX: check for realloc failure */
lp->array = a;
lp->size = newsize;
}
e = &lp->array[lp->count++];
e->name = strdup(name);
if (short_name)
e->short_name = strdup(short_name);
else
e->short_name = NULL;
e->flags = flags;
}
void namelist_free(namelist_t *lp)
{
while (lp->count > 0) {
namelist_entry_t *e = &lp->array[--lp->count];
free(e->name);
free(e->short_name);
}
free(lp->array);
lp->array = NULL;
lp->size = 0;
}
namelist_entry_t *namelist_find(namelist_t *lp, const char *name)
{
int i;
for(i = 0; i < lp->count; i++) {
namelist_entry_t *e = &lp->array[i];
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
static void get_c_name(char *buf, size_t buf_size, const char *file)
{
const char *p, *r;
size_t len, i;
int c;
char *q;
p = strrchr(file, '/');
if (!p)
p = file;
else
p++;
r = strrchr(p, '.');
if (!r)
len = strlen(p);
else
len = r - p;
pstrcpy(buf, buf_size, c_ident_prefix);
q = buf + strlen(buf);
for(i = 0; i < len; i++) {
c = p[i];
if (!((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z'))) {
c = '_';
}
if ((q - buf) < buf_size - 1)
*q++ = c;
}
*q = '\0';
}
static void dump_hex(FILE *f, const uint8_t *buf, size_t len)
{
size_t i, col;
col = 0;
for(i = 0; i < len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if (++col == 8) {
fprintf(f, "\n");
col = 0;
}
}
if (col != 0)
fprintf(f, "\n");
}
static void output_object_code(JSContext *ctx,
FILE *fo, JSValueConst obj, const char *c_name,
BOOL load_only)
{
uint8_t *out_buf;
size_t out_buf_len;
int flags;
flags = JS_WRITE_OBJ_BYTECODE;
if (byte_swap)
flags |= JS_WRITE_OBJ_BSWAP;
out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
if (!out_buf) {
js_std_dump_error(ctx);
exit(1);
}
namelist_add(&cname_list, c_name, NULL, load_only);
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
c_name, (unsigned int)out_buf_len);
fprintf(fo, "const uint8_t %s[%u] = {\n",
c_name, (unsigned int)out_buf_len);
dump_hex(fo, out_buf, out_buf_len);
fprintf(fo, "};\n\n");
js_free(ctx, out_buf);
}
static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m)
{
/* should never be called when compiling JS code */
abort();
}
static void find_unique_cname(char *cname, size_t cname_size)
{
char cname1[1024];
int suffix_num;
size_t len, max_len;
assert(cname_size >= 32);
/* find a C name not matching an existing module C name by
adding a numeric suffix */
len = strlen(cname);
max_len = cname_size - 16;
if (len > max_len)
cname[max_len] = '\0';
suffix_num = 1;
for(;;) {
snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num);
if (!namelist_find(&cname_list, cname1))
break;
suffix_num++;
}
pstrcpy(cname, cname_size, cname1);
}
JSModuleDef *jsc_module_loader(JSContext *ctx,
const char *module_name, void *opaque)
{
JSModuleDef *m;
namelist_entry_t *e;
/* check if it is a declared C or system module */
e = namelist_find(&cmodule_list, module_name);
if (e) {
/* add in the static init module list */
namelist_add(&init_module_list, e->name, e->short_name, 0);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
} else if (has_suffix(module_name, ".so")) {
fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
/* the resulting executable will export its symbols for the
dynamic library */
dynamic_export = TRUE;
} else {
size_t buf_len;
uint8_t *buf;
JSValue func_val;
char cname[1024];
buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name);
return NULL;
}
/* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
js_free(ctx, buf);
if (JS_IsException(func_val))
return NULL;
get_c_name(cname, sizeof(cname), module_name);
if (namelist_find(&cname_list, cname)) {
find_unique_cname(cname, sizeof(cname));
}
output_object_code(ctx, outfile, func_val, cname, TRUE);
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
}
return m;
}
static void compile_file(JSContext *ctx, FILE *fo,
const char *filename,
const char *c_name1,
int module)
{
uint8_t *buf;
char c_name[1024];
int eval_flags;
JSValue obj;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
fprintf(stderr, "Could not load '%s'\n", filename);
exit(1);
}
eval_flags = JS_EVAL_FLAG_COMPILE_ONLY;
if (module < 0) {
module = (has_suffix(filename, ".mjs") ||
JS_DetectModule((const char *)buf, buf_len));
}
if (module)
eval_flags |= JS_EVAL_TYPE_MODULE;
else
eval_flags |= JS_EVAL_TYPE_GLOBAL;
obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags);
if (JS_IsException(obj)) {
js_std_dump_error(ctx);
exit(1);
}
js_free(ctx, buf);
if (c_name1) {
pstrcpy(c_name, sizeof(c_name), c_name1);
} else {
get_c_name(c_name, sizeof(c_name), filename);
}
output_object_code(ctx, fo, obj, c_name, FALSE);
JS_FreeValue(ctx, obj);
}
static const char main_c_template1[] =
"int main(int argc, char **argv)\n"
"{\n"
" JSRuntime *rt;\n"
" JSContext *ctx;\n"
" rt = JS_NewRuntime();\n"
" js_std_init_handlers(rt);\n"
;
static const char main_c_template2[] =
" js_std_loop(ctx);\n"
" JS_FreeContext(ctx);\n"
" JS_FreeRuntime(rt);\n"
" return 0;\n"
"}\n";
#define PROG_NAME "qjsc"
void help(void)
{
printf("QuickJS Compiler version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [files]\n"
"\n"
"options are:\n"
"-c only output bytecode in a C file\n"
"-e output main() and bytecode in a C file (default = executable output)\n"
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module (default=autodetect)\n"
"-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n"
"-S n set the maximum stack size to 'n' bytes (default=%d)\n",
JS_DEFAULT_STACK_SIZE);
#ifdef CONFIG_LTO
{
int i;
printf("-flto use link time optimization\n");
printf("-fbignum enable bignum extensions\n");
printf("-fno-[");
for(i = 0; i < countof(feature_list); i++) {
if (i != 0)
printf("|");
printf("%s", feature_list[i].option_name);
}
printf("]\n"
" disable selected language features (smaller code size)\n");
}
#endif
exit(1);
}
#if defined(CONFIG_CC) && !defined(_WIN32)
int exec_cmd(char **argv)
{
int pid, status, ret;
pid = fork();
if (pid == 0) {
execvp(argv[0], argv);
exit(1);
}
for(;;) {
ret = waitpid(pid, &status, 0);
if (ret == pid && WIFEXITED(status))
break;
}
return WEXITSTATUS(status);
}
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
const char *argv[64];
const char **arg, *bn_suffix, *lto_suffix;
char libjsname[1024];
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
int ret;
/* get the directory of the executable */
pstrcpy(exe_dir, sizeof(exe_dir), exename);
p = strrchr(exe_dir, '/');
if (p) {
*p = '\0';
} else {
pstrcpy(exe_dir, sizeof(exe_dir), ".");
}
/* if 'quickjs.h' is present at the same path as the executable, we
use it as include and lib directory */
snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir);
if (access(buf, R_OK) == 0) {
pstrcpy(inc_dir, sizeof(inc_dir), exe_dir);
pstrcpy(lib_dir, sizeof(lib_dir), exe_dir);
} else {
snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
}
lto_suffix = "";
bn_suffix = "";
arg = argv;
*arg++ = CONFIG_CC;
*arg++ = "-O2";
#ifdef CONFIG_LTO
if (use_lto) {
*arg++ = "-flto";
lto_suffix = ".lto";
}
#endif
/* XXX: use the executable path to find the includes files and
libraries */
*arg++ = "-D";
*arg++ = "_GNU_SOURCE";
*arg++ = "-I";
*arg++ = inc_dir;
*arg++ = "-o";
*arg++ = out_filename;
if (dynamic_export)
*arg++ = "-rdynamic";
*arg++ = cfilename;
snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a",
lib_dir, bn_suffix, lto_suffix);
*arg++ = libjsname;
*arg++ = "-lm";
*arg++ = "-ldl";
*arg++ = "-lpthread";
*arg = NULL;
if (verbose) {
for(arg = argv; *arg != NULL; arg++)
printf("%s ", *arg);
printf("\n");
}
ret = exec_cmd((char **)argv);
unlink(cfilename);
return ret;
}
#else
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
fprintf(stderr, "Executable output is not supported for this target\n");
exit(1);
return 0;
}
#endif
typedef enum {
OUTPUT_C,
OUTPUT_C_MAIN,
OUTPUT_EXECUTABLE,
} OutputTypeEnum;
int main(int argc, char **argv)
{
int c, i, verbose;
const char *out_filename, *cname;
char cfilename[1024];
FILE *fo;
JSRuntime *rt;
JSContext *ctx;
BOOL use_lto;
int module;
OutputTypeEnum output_type;
size_t stack_size;
#ifdef CONFIG_BIGNUM
BOOL bignum_ext = FALSE;
#endif
out_filename = NULL;
output_type = OUTPUT_EXECUTABLE;
cname = NULL;
feature_bitmap = FE_ALL;
module = -1;
byte_swap = FALSE;
verbose = 0;
use_lto = FALSE;
stack_size = 0;
/* add system modules */
namelist_add(&cmodule_list, "std", "std", 0);
namelist_add(&cmodule_list, "os", "os", 0);
for(;;) {
c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
case 'o':
out_filename = optarg;
break;
case 'c':
output_type = OUTPUT_C;
break;
case 'e':
output_type = OUTPUT_C_MAIN;
break;
case 'N':
cname = optarg;
break;
case 'f':
{
const char *p;
p = optarg;
if (!strcmp(optarg, "lto")) {
use_lto = TRUE;
} else if (strstart(p, "no-", &p)) {
use_lto = TRUE;
for(i = 0; i < countof(feature_list); i++) {
if (!strcmp(p, feature_list[i].option_name)) {
feature_bitmap &= ~((uint64_t)1 << i);
break;
}
}
if (i == countof(feature_list))
goto bad_feature;
} else
#ifdef CONFIG_BIGNUM
if (!strcmp(optarg, "bignum")) {
bignum_ext = TRUE;
} else
#endif
{
bad_feature:
fprintf(stderr, "unsupported feature: %s\n", optarg);
exit(1);
}
}
break;
case 'm':
module = 1;
break;
case 'M':
{
char *p;
char path[1024];
char cname[1024];
pstrcpy(path, sizeof(path), optarg);
p = strchr(path, ',');
if (p) {
*p = '\0';
pstrcpy(cname, sizeof(cname), p + 1);
} else {
get_c_name(cname, sizeof(cname), path);
}
namelist_add(&cmodule_list, path, cname, 0);
}
break;
case 'x':
byte_swap = TRUE;
break;
case 'v':
verbose++;
break;
case 'p':
c_ident_prefix = optarg;
break;
case 'S':
stack_size = (size_t)strtod(optarg, NULL);
break;
default:
break;
}
}
if (optind >= argc)
help();
if (!out_filename) {
if (output_type == OUTPUT_EXECUTABLE) {
out_filename = "a.out";
} else {
out_filename = "out.c";
}
}
if (output_type == OUTPUT_EXECUTABLE) {
#if defined(_WIN32) || defined(__ANDROID__)
/* XXX: find a /tmp directory ? */
snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid());
#else
snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid());
#endif
} else {
pstrcpy(cfilename, sizeof(cfilename), out_filename);
}
fo = fopen(cfilename, "w");
if (!fo) {
perror(cfilename);
exit(1);
}
outfile = fo;
rt = JS_NewRuntime();
ctx = JS_NewContext(rt);
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_AddIntrinsicOperators(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
"\n"
);
if (output_type != OUTPUT_C) {
fprintf(fo, "#include \"quickjs-libc.h\"\n"
"\n"
);
} else {
fprintf(fo, "#include <inttypes.h>\n"
"\n"
);
}
for(i = optind; i < argc; i++) {
const char *filename = argv[i];
compile_file(ctx, fo, filename, cname, module);
cname = NULL;
}
if (output_type != OUTPUT_C) {
fputs(main_c_template1, fo);
fprintf(fo, " ctx = JS_NewContextRaw(rt);\n");
if (stack_size != 0) {
fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n",
(unsigned int)stack_size);
}
/* add the module loader if necessary */
if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");
}
/* add the basic objects */
fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n");
for(i = 0; i < countof(feature_list); i++) {
if ((feature_bitmap & ((uint64_t)1 << i)) &&
feature_list[i].init_name) {
fprintf(fo, " JS_AddIntrinsic%s(ctx);\n",
feature_list[i].init_name);
}
}
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
fprintf(fo,
" JS_AddIntrinsicBigFloat(ctx);\n"
" JS_AddIntrinsicBigDecimal(ctx);\n"
" JS_AddIntrinsicOperators(ctx);\n"
" JS_EnableBignumExt(ctx, 1);\n");
}
#endif
fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n");
for(i = 0; i < init_module_list.count; i++) {
namelist_entry_t *e = &init_module_list.array[i];
/* initialize the static C modules */
fprintf(fo,
" {\n"
" extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
" js_init_module_%s(ctx, \"%s\");\n"
" }\n",
e->short_name, e->short_name, e->name);
}
for(i = 0; i < cname_list.count; i++) {
namelist_entry_t *e = &cname_list.array[i];
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, %s);\n",
e->name, e->name,
e->flags ? "1" : "0");
}
fputs(main_c_template2, fo);
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
fclose(fo);
if (output_type == OUTPUT_EXECUTABLE) {
return output_executable(out_filename, cfilename, use_lto, verbose,
argv[0]);
}
namelist_free(&cname_list);
namelist_free(&cmodule_list);
namelist_free(&init_module_list);
return 0;
}

View File

@@ -0,0 +1,272 @@
/*
* QuickJS atom definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DEF
/* Note: first atoms are considered as keywords in the parser */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
DEF(await, "await")
/* empty string */
DEF(empty_string, "")
/* identifiers */
DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(message, "message")
DEF(errors, "errors")
DEF(stack, "stack")
DEF(name, "name")
DEF(toString, "toString")
DEF(toLocaleString, "toLocaleString")
DEF(valueOf, "valueOf")
DEF(eval, "eval")
DEF(prototype, "prototype")
DEF(constructor, "constructor")
DEF(configurable, "configurable")
DEF(writable, "writable")
DEF(enumerable, "enumerable")
DEF(value, "value")
DEF(get, "get")
DEF(set, "set")
DEF(of, "of")
DEF(__proto__, "__proto__")
DEF(undefined, "undefined")
DEF(number, "number")
DEF(boolean, "boolean")
DEF(string, "string")
DEF(object, "object")
DEF(symbol, "symbol")
DEF(integer, "integer")
DEF(unknown, "unknown")
DEF(arguments, "arguments")
DEF(callee, "callee")
DEF(caller, "caller")
DEF(_eval_, "<eval>")
DEF(_ret_, "<ret>")
DEF(_var_, "<var>")
DEF(_with_, "<with>")
DEF(lastIndex, "lastIndex")
DEF(target, "target")
DEF(index, "index")
DEF(input, "input")
DEF(defineProperties, "defineProperties")
DEF(apply, "apply")
DEF(join, "join")
DEF(concat, "concat")
DEF(split, "split")
DEF(construct, "construct")
DEF(getPrototypeOf, "getPrototypeOf")
DEF(setPrototypeOf, "setPrototypeOf")
DEF(isExtensible, "isExtensible")
DEF(preventExtensions, "preventExtensions")
DEF(has, "has")
DEF(deleteProperty, "deleteProperty")
DEF(defineProperty, "defineProperty")
DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
DEF(ownKeys, "ownKeys")
DEF(add, "add")
DEF(done, "done")
DEF(next, "next")
DEF(values, "values")
DEF(source, "source")
DEF(flags, "flags")
DEF(global, "global")
DEF(unicode, "unicode")
DEF(raw, "raw")
DEF(new_target, "new.target")
DEF(this_active_func, "this.active_func")
DEF(home_object, "<home_object>")
DEF(computed_field, "<computed_field>")
DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
DEF(class_fields_init, "<class_fields_init>")
DEF(brand, "<brand>")
DEF(hash_constructor, "#constructor")
DEF(as, "as")
DEF(from, "from")
DEF(meta, "meta")
DEF(_default_, "*default*")
DEF(_star_, "*")
DEF(Module, "Module")
DEF(then, "then")
DEF(resolve, "resolve")
DEF(reject, "reject")
DEF(promise, "promise")
DEF(proxy, "proxy")
DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
DEF(status, "status")
DEF(reason, "reason")
DEF(globalThis, "globalThis")
#ifdef CONFIG_BIGNUM
DEF(bigint, "bigint")
DEF(bigfloat, "bigfloat")
DEF(bigdecimal, "bigdecimal")
DEF(roundingMode, "roundingMode")
DEF(maximumSignificantDigits, "maximumSignificantDigits")
DEF(maximumFractionDigits, "maximumFractionDigits")
#endif
#ifdef CONFIG_ATOMICS
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
#endif
DEF(toJSON, "toJSON")
/* class names */
DEF(Object, "Object")
DEF(Array, "Array")
DEF(Error, "Error")
DEF(Number, "Number")
DEF(String, "String")
DEF(Boolean, "Boolean")
DEF(Symbol, "Symbol")
DEF(Arguments, "Arguments")
DEF(Math, "Math")
DEF(JSON, "JSON")
DEF(Date, "Date")
DEF(Function, "Function")
DEF(GeneratorFunction, "GeneratorFunction")
DEF(ForInIterator, "ForInIterator")
DEF(RegExp, "RegExp")
DEF(ArrayBuffer, "ArrayBuffer")
DEF(SharedArrayBuffer, "SharedArrayBuffer")
/* must keep same order as class IDs for typed arrays */
DEF(Uint8ClampedArray, "Uint8ClampedArray")
DEF(Int8Array, "Int8Array")
DEF(Uint8Array, "Uint8Array")
DEF(Int16Array, "Int16Array")
DEF(Uint16Array, "Uint16Array")
DEF(Int32Array, "Int32Array")
DEF(Uint32Array, "Uint32Array")
#ifdef CONFIG_BIGNUM
DEF(BigInt64Array, "BigInt64Array")
DEF(BigUint64Array, "BigUint64Array")
#endif
DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView")
#ifdef CONFIG_BIGNUM
DEF(BigInt, "BigInt")
DEF(BigFloat, "BigFloat")
DEF(BigFloatEnv, "BigFloatEnv")
DEF(BigDecimal, "BigDecimal")
DEF(OperatorSet, "OperatorSet")
DEF(Operators, "Operators")
#endif
DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */
DEF(WeakSet, "WeakSet") /* Map + 3 */
DEF(Map_Iterator, "Map Iterator")
DEF(Set_Iterator, "Set Iterator")
DEF(Array_Iterator, "Array Iterator")
DEF(String_Iterator, "String Iterator")
DEF(RegExp_String_Iterator, "RegExp String Iterator")
DEF(Generator, "Generator")
DEF(Proxy, "Proxy")
DEF(Promise, "Promise")
DEF(PromiseResolveFunction, "PromiseResolveFunction")
DEF(PromiseRejectFunction, "PromiseRejectFunction")
DEF(AsyncFunction, "AsyncFunction")
DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
DEF(AsyncFunctionReject, "AsyncFunctionReject")
DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
DEF(AsyncGenerator, "AsyncGenerator")
DEF(EvalError, "EvalError")
DEF(RangeError, "RangeError")
DEF(ReferenceError, "ReferenceError")
DEF(SyntaxError, "SyntaxError")
DEF(TypeError, "TypeError")
DEF(URIError, "URIError")
DEF(InternalError, "InternalError")
/* private symbols */
DEF(Private_brand, "<brand>")
/* symbols */
DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
DEF(Symbol_iterator, "Symbol.iterator")
DEF(Symbol_match, "Symbol.match")
DEF(Symbol_matchAll, "Symbol.matchAll")
DEF(Symbol_replace, "Symbol.replace")
DEF(Symbol_search, "Symbol.search")
DEF(Symbol_split, "Symbol.split")
DEF(Symbol_toStringTag, "Symbol.toStringTag")
DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#ifdef CONFIG_BIGNUM
DEF(Symbol_operatorSet, "Symbol.operatorSet")
#endif
#endif /* DEF */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
/*
* QuickJS C library
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef QUICKJS_LIBC_H
#define QUICKJS_LIBC_H
#include <stdio.h>
#include <stdlib.h>
#include "quickjs.h"
#ifdef __cplusplus
extern "C" {
#endif
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
void js_std_loop(JSContext *ctx);
void js_std_init_handlers(JSRuntime *rt);
void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx);
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
JS_BOOL use_realpath, JS_BOOL is_main);
JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque);
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* QUICKJS_LIBC_H */

View File

@@ -0,0 +1,367 @@
/*
* QuickJS opcode definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(npop_u16)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(atom)
FMT(atom_u8)
FMT(atom_u16)
FMT(atom_label_u8)
FMT(atom_label_u16)
FMT(label_u16)
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
DEF(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_i32, 5, 0, 1, i32)
DEF( push_const, 5, 0, 1, const)
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
DEF(push_atom_value, 5, 0, 1, atom)
DEF( private_symbol, 5, 0, 1, atom)
DEF( undefined, 1, 0, 1, none)
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 1, 0, 1, none)
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( apply, 3, 3, 1, u16)
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none)
DEF( check_ctor, 1, 0, 0, none)
DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
DEF( return_async, 1, 1, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( throw_var, 6, 0, 0, atom_u8)
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
DEF( get_super, 1, 1, 1, none)
DEF( import, 1, 1, 1, none) /* dynamic module import */
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
DEF( get_ref_value, 1, 2, 3, none)
DEF( put_ref_value, 1, 3, 0, none)
DEF( define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 0, atom_u8)
DEF( define_func, 6, 1, 0, atom_u8)
DEF( get_field, 5, 1, 1, atom)
DEF( get_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
DEF( get_array_el, 1, 2, 1, none)
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( put_array_el, 1, 3, 0, none)
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
DEF( define_field, 5, 2, 1, atom)
DEF( set_name, 5, 1, 1, atom)
DEF(set_name_computed, 1, 2, 2, none)
DEF( set_proto, 1, 2, 1, none)
DEF(set_home_object, 1, 2, 2, none)
DEF(define_array_el, 1, 3, 2, none)
DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
DEF(copy_data_properties, 2, 3, 3, u8)
DEF( define_method, 6, 2, 1, atom_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF( close_loc, 3, 0, 0, loc)
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( to_object, 1, 1, 1, none)
//DEF( to_string, 1, 1, 1, none)
DEF( to_propkey, 1, 1, 1, none)
DEF( to_propkey2, 1, 2, 2, none)
DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)
DEF( make_loc_ref, 7, 0, 2, atom_u16)
DEF( make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF( make_var_ref, 5, 0, 2, atom)
DEF( for_in_start, 1, 1, 1, none)
DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8)
DEF(for_await_of_next, 1, 3, 4, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF( iterator_close, 1, 3, 0, none)
DEF(iterator_close_return, 1, 4, 4, none)
DEF(async_iterator_close, 1, 3, 2, none)
DEF(async_iterator_next, 1, 4, 4, none)
DEF(async_iterator_get, 2, 4, 5, u8)
DEF( initial_yield, 1, 0, 0, none)
DEF( yield, 1, 1, 2, none)
DEF( yield_star, 1, 2, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF( await, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( dec_loc, 2, 0, 0, loc8)
DEF( inc_loc, 2, 0, 0, loc8)
DEF( add_loc, 2, 1, 0, loc8)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( typeof, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, atom)
DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
#ifdef CONFIG_BIGNUM
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
#endif
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)
/* temporary opcodes: never emitted in the final bytecode */
def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8)
DEF( set_loc8, 2, 1, 1, loc8)
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc)
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( set_loc0, 1, 1, 1, none_loc)
DEF( set_loc1, 1, 1, 1, none_loc)
DEF( set_loc2, 1, 1, 1, none_loc)
DEF( set_loc3, 1, 1, 1, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg)
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
DEF( set_arg0, 1, 1, 1, none_arg)
DEF( set_arg1, 1, 1, 1, none_arg)
DEF( set_arg2, 1, 1, 1, none_arg)
DEF( set_arg3, 1, 1, 1, none_arg)
DEF( get_var_ref0, 1, 0, 1, none_var_ref)
DEF( get_var_ref1, 1, 0, 1, none_var_ref)
DEF( get_var_ref2, 1, 0, 1, none_var_ref)
DEF( get_var_ref3, 1, 0, 1, none_var_ref)
DEF( put_var_ref0, 1, 1, 0, none_var_ref)
DEF( put_var_ref1, 1, 1, 0, none_var_ref)
DEF( put_var_ref2, 1, 1, 0, none_var_ref)
DEF( put_var_ref3, 1, 1, 0, none_var_ref)
DEF( set_var_ref0, 1, 1, 1, none_var_ref)
DEF( set_var_ref1, 1, 1, 1, none_var_ref)
DEF( set_var_ref2, 1, 1, 1, none_var_ref)
DEF( set_var_ref3, 1, 1, 1, none_var_ref)
DEF( get_length, 1, 1, 1, none)
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
DEF( is_undefined, 1, 1, 1, none)
DEF( is_null, 1, 1, 1, none)
DEF( is_function, 1, 1, 1, none)
#endif
#undef DEF
#undef def
#endif /* DEF */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
The main documentation is in doc/quickjs.pdf or doc/quickjs.html.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
#ifdef UNICODE_GENERAL_CATEGORY
DEF(Cn, "Unassigned") /* must be zero */
DEF(Lu, "Uppercase_Letter")
DEF(Ll, "Lowercase_Letter")
DEF(Lt, "Titlecase_Letter")
DEF(Lm, "Modifier_Letter")
DEF(Lo, "Other_Letter")
DEF(Mn, "Nonspacing_Mark")
DEF(Mc, "Spacing_Mark")
DEF(Me, "Enclosing_Mark")
DEF(Nd, "Decimal_Number,digit")
DEF(Nl, "Letter_Number")
DEF(No, "Other_Number")
DEF(Sm, "Math_Symbol")
DEF(Sc, "Currency_Symbol")
DEF(Sk, "Modifier_Symbol")
DEF(So, "Other_Symbol")
DEF(Pc, "Connector_Punctuation")
DEF(Pd, "Dash_Punctuation")
DEF(Ps, "Open_Punctuation")
DEF(Pe, "Close_Punctuation")
DEF(Pi, "Initial_Punctuation")
DEF(Pf, "Final_Punctuation")
DEF(Po, "Other_Punctuation")
DEF(Zs, "Space_Separator")
DEF(Zl, "Line_Separator")
DEF(Zp, "Paragraph_Separator")
DEF(Cc, "Control,cntrl")
DEF(Cf, "Format")
DEF(Cs, "Surrogate")
DEF(Co, "Private_Use")
/* synthetic properties */
DEF(LC, "Cased_Letter")
DEF(L, "Letter")
DEF(M, "Mark,Combining_Mark")
DEF(N, "Number")
DEF(S, "Symbol")
DEF(P, "Punctuation,punct")
DEF(Z, "Separator")
DEF(C, "Other")
#endif
#ifdef UNICODE_SCRIPT
/* scripts aliases names in PropertyValueAliases.txt */
DEF(Unknown, "Zzzz")
DEF(Adlam, "Adlm")
DEF(Ahom, "Ahom")
DEF(Anatolian_Hieroglyphs, "Hluw")
DEF(Arabic, "Arab")
DEF(Armenian, "Armn")
DEF(Avestan, "Avst")
DEF(Balinese, "Bali")
DEF(Bamum, "Bamu")
DEF(Bassa_Vah, "Bass")
DEF(Batak, "Batk")
DEF(Bengali, "Beng")
DEF(Bhaiksuki, "Bhks")
DEF(Bopomofo, "Bopo")
DEF(Brahmi, "Brah")
DEF(Braille, "Brai")
DEF(Buginese, "Bugi")
DEF(Buhid, "Buhd")
DEF(Canadian_Aboriginal, "Cans")
DEF(Carian, "Cari")
DEF(Caucasian_Albanian, "Aghb")
DEF(Chakma, "Cakm")
DEF(Cham, "Cham")
DEF(Cherokee, "Cher")
DEF(Chorasmian, "Chrs")
DEF(Common, "Zyyy")
DEF(Coptic, "Copt,Qaac")
DEF(Cuneiform, "Xsux")
DEF(Cypriot, "Cprt")
DEF(Cyrillic, "Cyrl")
DEF(Deseret, "Dsrt")
DEF(Devanagari, "Deva")
DEF(Dives_Akuru, "Diak")
DEF(Dogra, "Dogr")
DEF(Duployan, "Dupl")
DEF(Egyptian_Hieroglyphs, "Egyp")
DEF(Elbasan, "Elba")
DEF(Elymaic, "Elym")
DEF(Ethiopic, "Ethi")
DEF(Georgian, "Geor")
DEF(Glagolitic, "Glag")
DEF(Gothic, "Goth")
DEF(Grantha, "Gran")
DEF(Greek, "Grek")
DEF(Gujarati, "Gujr")
DEF(Gunjala_Gondi, "Gong")
DEF(Gurmukhi, "Guru")
DEF(Han, "Hani")
DEF(Hangul, "Hang")
DEF(Hanifi_Rohingya, "Rohg")
DEF(Hanunoo, "Hano")
DEF(Hatran, "Hatr")
DEF(Hebrew, "Hebr")
DEF(Hiragana, "Hira")
DEF(Imperial_Aramaic, "Armi")
DEF(Inherited, "Zinh,Qaai")
DEF(Inscriptional_Pahlavi, "Phli")
DEF(Inscriptional_Parthian, "Prti")
DEF(Javanese, "Java")
DEF(Kaithi, "Kthi")
DEF(Kannada, "Knda")
DEF(Katakana, "Kana")
DEF(Kayah_Li, "Kali")
DEF(Kharoshthi, "Khar")
DEF(Khmer, "Khmr")
DEF(Khojki, "Khoj")
DEF(Khitan_Small_Script, "Kits")
DEF(Khudawadi, "Sind")
DEF(Lao, "Laoo")
DEF(Latin, "Latn")
DEF(Lepcha, "Lepc")
DEF(Limbu, "Limb")
DEF(Linear_A, "Lina")
DEF(Linear_B, "Linb")
DEF(Lisu, "Lisu")
DEF(Lycian, "Lyci")
DEF(Lydian, "Lydi")
DEF(Makasar, "Maka")
DEF(Mahajani, "Mahj")
DEF(Malayalam, "Mlym")
DEF(Mandaic, "Mand")
DEF(Manichaean, "Mani")
DEF(Marchen, "Marc")
DEF(Masaram_Gondi, "Gonm")
DEF(Medefaidrin, "Medf")
DEF(Meetei_Mayek, "Mtei")
DEF(Mende_Kikakui, "Mend")
DEF(Meroitic_Cursive, "Merc")
DEF(Meroitic_Hieroglyphs, "Mero")
DEF(Miao, "Plrd")
DEF(Modi, "Modi")
DEF(Mongolian, "Mong")
DEF(Mro, "Mroo")
DEF(Multani, "Mult")
DEF(Myanmar, "Mymr")
DEF(Nabataean, "Nbat")
DEF(Nandinagari, "Nand")
DEF(New_Tai_Lue, "Talu")
DEF(Newa, "Newa")
DEF(Nko, "Nkoo")
DEF(Nushu, "Nshu")
DEF(Nyiakeng_Puachue_Hmong, "Hmnp")
DEF(Ogham, "Ogam")
DEF(Ol_Chiki, "Olck")
DEF(Old_Hungarian, "Hung")
DEF(Old_Italic, "Ital")
DEF(Old_North_Arabian, "Narb")
DEF(Old_Permic, "Perm")
DEF(Old_Persian, "Xpeo")
DEF(Old_Sogdian, "Sogo")
DEF(Old_South_Arabian, "Sarb")
DEF(Old_Turkic, "Orkh")
DEF(Oriya, "Orya")
DEF(Osage, "Osge")
DEF(Osmanya, "Osma")
DEF(Pahawh_Hmong, "Hmng")
DEF(Palmyrene, "Palm")
DEF(Pau_Cin_Hau, "Pauc")
DEF(Phags_Pa, "Phag")
DEF(Phoenician, "Phnx")
DEF(Psalter_Pahlavi, "Phlp")
DEF(Rejang, "Rjng")
DEF(Runic, "Runr")
DEF(Samaritan, "Samr")
DEF(Saurashtra, "Saur")
DEF(Sharada, "Shrd")
DEF(Shavian, "Shaw")
DEF(Siddham, "Sidd")
DEF(SignWriting, "Sgnw")
DEF(Sinhala, "Sinh")
DEF(Sogdian, "Sogd")
DEF(Sora_Sompeng, "Sora")
DEF(Soyombo, "Soyo")
DEF(Sundanese, "Sund")
DEF(Syloti_Nagri, "Sylo")
DEF(Syriac, "Syrc")
DEF(Tagalog, "Tglg")
DEF(Tagbanwa, "Tagb")
DEF(Tai_Le, "Tale")
DEF(Tai_Tham, "Lana")
DEF(Tai_Viet, "Tavt")
DEF(Takri, "Takr")
DEF(Tamil, "Taml")
DEF(Tangut, "Tang")
DEF(Telugu, "Telu")
DEF(Thaana, "Thaa")
DEF(Thai, "Thai")
DEF(Tibetan, "Tibt")
DEF(Tifinagh, "Tfng")
DEF(Tirhuta, "Tirh")
DEF(Ugaritic, "Ugar")
DEF(Vai, "Vaii")
DEF(Wancho, "Wcho")
DEF(Warang_Citi, "Wara")
DEF(Yezidi, "Yezi")
DEF(Yi, "Yiii")
DEF(Zanabazar_Square, "Zanb")
#endif
#ifdef UNICODE_PROP_LIST
/* Prop list not exported to regexp */
DEF(Hyphen, "")
DEF(Other_Math, "")
DEF(Other_Alphabetic, "")
DEF(Other_Lowercase, "")
DEF(Other_Uppercase, "")
DEF(Other_Grapheme_Extend, "")
DEF(Other_Default_Ignorable_Code_Point, "")
DEF(Other_ID_Start, "")
DEF(Other_ID_Continue, "")
DEF(Prepended_Concatenation_Mark, "")
/* additional computed properties for smaller tables */
DEF(ID_Continue1, "")
DEF(XID_Start1, "")
DEF(XID_Continue1, "")
DEF(Changes_When_Titlecased1, "")
DEF(Changes_When_Casefolded1, "")
DEF(Changes_When_NFKC_Casefolded1, "")
/* Prop list exported to JS */
DEF(ASCII_Hex_Digit, "AHex")
DEF(Bidi_Control, "Bidi_C")
DEF(Dash, "")
DEF(Deprecated, "Dep")
DEF(Diacritic, "Dia")
DEF(Extender, "Ext")
DEF(Hex_Digit, "Hex")
DEF(IDS_Binary_Operator, "IDSB")
DEF(IDS_Trinary_Operator, "IDST")
DEF(Ideographic, "Ideo")
DEF(Join_Control, "Join_C")
DEF(Logical_Order_Exception, "LOE")
DEF(Noncharacter_Code_Point, "NChar")
DEF(Pattern_Syntax, "Pat_Syn")
DEF(Pattern_White_Space, "Pat_WS")
DEF(Quotation_Mark, "QMark")
DEF(Radical, "")
DEF(Regional_Indicator, "RI")
DEF(Sentence_Terminal, "STerm")
DEF(Soft_Dotted, "SD")
DEF(Terminal_Punctuation, "Term")
DEF(Unified_Ideograph, "UIdeo")
DEF(Variation_Selector, "VS")
DEF(White_Space, "space")
DEF(Bidi_Mirrored, "Bidi_M")
DEF(Emoji, "")
DEF(Emoji_Component, "EComp")
DEF(Emoji_Modifier, "EMod")
DEF(Emoji_Modifier_Base, "EBase")
DEF(Emoji_Presentation, "EPres")
DEF(Extended_Pictographic, "ExtPict")
DEF(Default_Ignorable_Code_Point, "DI")
DEF(ID_Start, "IDS")
DEF(Case_Ignorable, "CI")
/* other binary properties */
DEF(ASCII,"")
DEF(Alphabetic, "Alpha")
DEF(Any, "")
DEF(Assigned,"")
DEF(Cased, "")
DEF(Changes_When_Casefolded, "CWCF")
DEF(Changes_When_Casemapped, "CWCM")
DEF(Changes_When_Lowercased, "CWL")
DEF(Changes_When_NFKC_Casefolded, "CWKCF")
DEF(Changes_When_Titlecased, "CWT")
DEF(Changes_When_Uppercased, "CWU")
DEF(Grapheme_Base, "Gr_Base")
DEF(Grapheme_Extend, "Gr_Ext")
DEF(ID_Continue, "IDC")
DEF(Lowercase, "Lower")
DEF(Math, "")
DEF(Uppercase, "Upper")
DEF(XID_Continue, "XIDC")
DEF(XID_Start, "XIDS")
/* internal tables with index */
DEF(Cased1, "")
#endif

View File

@@ -0,0 +1,3 @@
# Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool.
tag-prefix = "libquickjs-sys-"

View File

@@ -0,0 +1,40 @@
//! FFI Bindings for [quickjs](https://bellard.org/quickjs/),
//! a Javascript engine.
//! See the [quickjs](https://crates.io/crates/quickjs) crate for a high-level
//! wrapper.
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[cfg(test)]
mod tests {
use std::ffi::CStr;
use super::*;
// Small sanity test that starts the runtime and evaluates code.
#[test]
fn test_eval() {
unsafe {
let rt = JS_NewRuntime();
let ctx = JS_NewContext(rt);
let code_str = "1 + 1\0";
let code = CStr::from_bytes_with_nul(code_str.as_bytes()).unwrap();
let script = CStr::from_bytes_with_nul("script\0".as_bytes()).unwrap();
let value = JS_Eval(
ctx,
code.as_ptr(),
code_str.len() - 1,
script.as_ptr(),
JS_EVAL_TYPE_GLOBAL as i32,
);
assert_eq!(value.tag, 0);
assert_eq!(value.u.int32, 2);
}
}
}

View File

@@ -0,0 +1,2 @@
#include <quickjs/quickjs.h>

View File

@@ -0,0 +1,7 @@
# Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool.
pre-release-replacements = [
{file="README.md", search="quick-js = .*", replace="{{crate_name}} = \"{{version}}\""},
]
tag-prefix = "quick-js-"

View File

@@ -0,0 +1,11 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
gcc
just
rust-bindgen
cargo
cargo-release
];
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,297 @@
use std::{convert::TryFrom, marker::PhantomData, panic::RefUnwindSafe};
use crate::value::{JsValue, ValueError};
pub trait IntoCallbackResult {
fn into_callback_res(self) -> Result<JsValue, String>;
}
impl<T: Into<JsValue>> IntoCallbackResult for T {
fn into_callback_res(self) -> Result<JsValue, String> {
Ok(self.into())
}
}
impl<T: Into<JsValue>, E: std::fmt::Display> IntoCallbackResult for Result<T, E> {
fn into_callback_res(self) -> Result<JsValue, String> {
match self {
Ok(v) => Ok(v.into()),
Err(e) => Err(e.to_string()),
}
}
}
/// The Callback trait is implemented for functions/closures that can be
/// used as callbacks in the JS runtime.
pub trait Callback<F>: RefUnwindSafe {
/// The number of JS arguments required.
fn argument_count(&self) -> usize;
/// Execute the callback.
///
/// Should return:
/// - Err(_) if the JS values could not be converted
/// - Ok(Err(_)) if an error ocurred while processing.
/// The given error will be raised as a JS exception.
/// - Ok(Ok(result)) when execution succeeded.
fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError>;
}
macro_rules! impl_callback {
(@call $len:literal $self:ident $args:ident ) => {
$self()
};
(@call $len:literal $self:ident $args:ident $( $arg:ident ),* ) => {
{
let mut iter = $args.into_iter();
$self(
$(
$arg::try_from(iter.next().unwrap())?,
)*
)
}
};
[ $( $len:literal : ( $( $arg:ident, )* ), )* ] => {
$(
impl<
$( $arg, )*
R,
F,
> Callback<PhantomData<(
$( &$arg, )*
&R,
&F,
)>> for F
where
$( $arg: TryFrom<JsValue, Error = ValueError>, )*
R: IntoCallbackResult,
F: Fn( $( $arg, )* ) -> R + Sized + RefUnwindSafe,
{
fn argument_count(&self) -> usize {
$len
}
fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
if args.len() != $len {
return Ok(Err(format!(
"Invalid argument count: Expected {}, got {}",
self.argument_count(),
args.len()
)));
}
let res = impl_callback!(@call $len self args $($arg),* );
Ok(res.into_callback_res())
}
}
)*
};
}
impl_callback![
0: (),
1: (A1,),
2: (A1, A2,),
3: (A1, A2, A3,),
4: (A1, A2, A3, A4,),
5: (A1, A2, A3, A4, A5,),
];
/// A wrapper around Vec<JsValue>, used for vararg callbacks.
///
/// To create a callback with a variable number of arguments, a callback closure
/// must take a single `Arguments` argument.
pub struct Arguments(Vec<JsValue>);
impl Arguments {
/// Unpack the arguments into a Vec.
pub fn into_vec(self) -> Vec<JsValue> {
self.0
}
}
impl<F> Callback<PhantomData<(&Arguments, &F)>> for F
where
F: Fn(Arguments) + Sized + RefUnwindSafe,
{
fn argument_count(&self) -> usize {
0
}
fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
(self)(Arguments(args));
Ok(Ok(JsValue::Null))
}
}
impl<F, R> Callback<PhantomData<(&Arguments, &F, &R)>> for F
where
R: IntoCallbackResult,
F: Fn(Arguments) -> R + Sized + RefUnwindSafe,
{
fn argument_count(&self) -> usize {
0
}
fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
let res = (self)(Arguments(args));
Ok(res.into_callback_res())
}
}
// Implement Callback for Fn() -> R functions.
//impl<R, F> Callback<PhantomData<(&R, &F)>> for F
//where
//R: Into<JsValue>,
//F: Fn() -> R + Sized + RefUnwindSafe,
//{
//fn argument_count(&self) -> usize {
//0
//}
//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
//if !args.is_empty() {
//return Ok(Err(format!(
//"Invalid argument count: Expected 0, got {}",
//args.len()
//)));
//}
//let res = self().into();
//Ok(Ok(res))
//}
//}
// Implement Callback for Fn(A) -> R functions.
//impl<A1, R, F> Callback<PhantomData<(&A1, &R, &F)>> for F
//where
//A1: TryFrom<JsValue, Error = ValueError>,
//R: Into<JsValue>,
//F: Fn(A1) -> R + Sized + RefUnwindSafe,
//{
//fn argument_count(&self) -> usize {
//1
//}
//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
//if args.len() != 1 {
//return Ok(Err(format!(
//"Invalid argument count: Expected 1, got {}",
//args.len()
//)));
//}
//let arg_raw = args.into_iter().next().expect("Invalid argument count");
//let arg = A1::try_from(arg_raw)?;
//let res = self(arg).into();
//Ok(Ok(res))
//}
//}
//// Implement Callback for Fn(A1, A2) -> R functions.
//impl<A1, A2, R, F> Callback<PhantomData<(&A1, &A2, &R, &F)>> for F
//where
//A1: TryFrom<JsValue, Error = ValueError>,
//A2: TryFrom<JsValue, Error = ValueError>,
//R: Into<JsValue>,
//F: Fn(A1, A2) -> R + Sized + RefUnwindSafe,
//{
//fn argument_count(&self) -> usize {
//2
//}
//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
//if args.len() != 2 {
//return Ok(Err(format!(
//"Invalid argument count: Expected 2, got {}",
//args.len()
//)));
//}
//let mut iter = args.into_iter();
//let arg1_raw = iter.next().expect("Invalid argument count");
//let arg1 = A1::try_from(arg1_raw)?;
//let arg2_raw = iter.next().expect("Invalid argument count");
//let arg2 = A2::try_from(arg2_raw)?;
//let res = self(arg1, arg2).into();
//Ok(Ok(res))
//}
//}
// Implement Callback for Fn(A1, A2, A3) -> R functions.
//impl<A1, A2, A3, R, F> Callback<PhantomData<(&A1, &A2, &A3, &R, &F)>> for F
//where
//A1: TryFrom<JsValue, Error = ValueError>,
//A2: TryFrom<JsValue, Error = ValueError>,
//A3: TryFrom<JsValue, Error = ValueError>,
//R: Into<JsValue>,
//F: Fn(A1, A2, A3) -> R + Sized + RefUnwindSafe,
//{
//fn argument_count(&self) -> usize {
//3
//}
//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
//if args.len() != self.argument_count() {
//return Ok(Err(format!(
//"Invalid argument count: Expected 3, got {}",
//args.len()
//)));
//}
//let mut iter = args.into_iter();
//let arg1_raw = iter.next().expect("Invalid argument count");
//let arg1 = A1::try_from(arg1_raw)?;
//let arg2_raw = iter.next().expect("Invalid argument count");
//let arg2 = A2::try_from(arg2_raw)?;
//let arg3_raw = iter.next().expect("Invalid argument count");
//let arg3 = A3::try_from(arg3_raw)?;
//let res = self(arg1, arg2, arg3).into();
//Ok(Ok(res))
//}
//}
//// Implement Callback for Fn(A1, A2, A3, A4) -> R functions.
//impl<A1, A2, A3, A4, R, F> Callback<PhantomData<(&A1, &A2, &A3, &A4, &R, &F)>> for F
//where
//A1: TryFrom<JsValue, Error = ValueError>,
//A2: TryFrom<JsValue, Error = ValueError>,
//A3: TryFrom<JsValue, Error = ValueError>,
//A4: TryFrom<JsValue, Error = ValueError>,
//R: Into<JsValue>,
//F: Fn(A1, A2, A3) -> R + Sized + RefUnwindSafe,
//{
//fn argument_count(&self) -> usize {
//4
//}
//fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
//if args.len() != self.argument_count() {
//return Ok(Err(format!(
//"Invalid argument count: Expected 3, got {}",
//args.len()
//)));
//}
//let mut iter = args.into_iter();
//let arg1_raw = iter.next().expect("Invalid argument count");
//let arg1 = A1::try_from(arg1_raw)?;
//let arg2_raw = iter.next().expect("Invalid argument count");
//let arg2 = A2::try_from(arg2_raw)?;
//let arg3_raw = iter.next().expect("Invalid argument count");
//let arg3 = A3::try_from(arg3_raw)?;
//let res = self(arg1, arg2, arg3).into();
//Ok(Ok(res))
//}
//}
// RESULT variants.

View File

@@ -0,0 +1,137 @@
//! Javascript console integration.
//! See the [ConsoleBackend] trait for more info.
use super::JsValue;
/// Log level of a log message sent via the console.
/// These levels represent the different functions defined in the spec:
/// https://s3.amazonaws.com/temp.michaelfbryan.com/callbacks/index.html
#[allow(missing_docs)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Level {
Trace,
Debug,
Log,
Info,
Warn,
Error,
}
impl std::fmt::Display for Level {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Level::*;
let v = match self {
Trace => "trace",
Debug => "debug",
Log => "log",
Info => "info",
Warn => "warn",
Error => "error",
};
write!(f, "{}", v)
}
}
/// A console backend that handles console messages sent from JS via
/// console.{log,debug,trace,...} functions.
///
/// A backend has to be registered via the `ContextBuilder::console` method.
///
/// A backend that forwads to the `log` crate is available with the `log` feature.
///
/// Note that any closure of type `Fn(Level, Vec<JsValue>)` implements this trait.
///
/// A very simple logger that just prints to stderr could look like this:
///
/// ```rust
/// use quick_js::{Context, JsValue, console::Level};
///
/// Context::builder()
/// .console(|level: Level, args: Vec<JsValue>| {
/// eprintln!("{}: {:?}", level, args);
/// })
/// .build()
/// # .unwrap();
/// ```
///
pub trait ConsoleBackend: std::panic::RefUnwindSafe + 'static {
/// Handle a log message.
fn log(&self, level: Level, values: Vec<JsValue>);
}
impl<F> ConsoleBackend for F
where
F: Fn(Level, Vec<JsValue>) + std::panic::RefUnwindSafe + 'static,
{
fn log(&self, level: Level, values: Vec<JsValue>) {
(self)(level, values);
}
}
#[cfg(feature = "log")]
mod log {
use super::{JsValue, Level};
/// A console implementation that logs messages via the `log` crate.
///
/// Only available with the `log` feature.
pub struct LogConsole;
fn print_value(value: JsValue) -> String {
match value {
JsValue::Null => "null".to_string(),
JsValue::Bool(v) => v.to_string(),
JsValue::Int(v) => v.to_string(),
JsValue::Float(v) => v.to_string(),
JsValue::String(v) => v,
JsValue::Array(values) => {
let parts = values
.into_iter()
.map(print_value)
.collect::<Vec<_>>()
.join(", ");
format!("[{}]", parts)
}
JsValue::Object(map) => {
let parts = map
.into_iter()
.map(|(key, value)| format!("{}: {}", key, print_value(value)))
.collect::<Vec<_>>()
.join(", ");
format!("{{{}}}", parts)
}
#[cfg(feature = "chrono")]
JsValue::Date(v) => v.to_string(),
#[cfg(feature = "bigint")]
JsValue::BigInt(v) => v.to_string(),
JsValue::__NonExhaustive => unreachable!(),
}
}
impl super::ConsoleBackend for LogConsole {
fn log(&self, level: Level, values: Vec<JsValue>) {
if values.is_empty() {
return;
}
let log_level = match level {
Level::Trace => log::Level::Trace,
Level::Debug => log::Level::Debug,
Level::Log => log::Level::Info,
Level::Info => log::Level::Info,
Level::Warn => log::Level::Warn,
Level::Error => log::Level::Error,
};
let msg = values
.into_iter()
.map(print_value)
.collect::<Vec<_>>()
.join(" ");
log::log!(log_level, "{}", msg);
}
}
}
#[cfg(feature = "log")]
pub use self::log::LogConsole;

View File

@@ -0,0 +1,47 @@
/// A small wrapper that frees resources that have to be freed
/// automatically when they go out of scope.
pub struct DroppableValue<T, F>
where
F: FnMut(&mut T),
{
value: T,
drop_fn: F,
}
impl<T, F> DroppableValue<T, F>
where
F: FnMut(&mut T),
{
pub fn new(value: T, drop_fn: F) -> Self {
Self { value, drop_fn }
}
}
impl<T, F> Drop for DroppableValue<T, F>
where
F: FnMut(&mut T),
{
fn drop(&mut self) {
(self.drop_fn)(&mut self.value);
}
}
impl<T, F> std::ops::Deref for DroppableValue<T, F>
where
F: FnMut(&mut T),
{
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T, F> std::ops::DerefMut for DroppableValue<T, F>
where
F: FnMut(&mut T),
{
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}

View File

@@ -0,0 +1,949 @@
//! quick-js is a a Rust wrapper for [QuickJS](https://bellard.org/quickjs/), a new Javascript
//! engine by Fabrice Bellard.
//!
//! It enables easy and straight-forward execution of modern Javascript from Rust.
//!
//! ## Limitations
//!
//! * Windows is not supported yet
//!
//! ## Quickstart:
//!
//! ```rust
//! use quick_js::{Context, JsValue};
//!
//! let context = Context::new().unwrap();
//!
//! // Eval.
//!
//! let value = context.eval("1 + 2").unwrap();
//! assert_eq!(value, JsValue::Int(3));
//!
//! let value = context.eval_as::<String>(" var x = 100 + 250; x.toString() ").unwrap();
//! assert_eq!(&value, "350");
//!
//! // Callbacks.
//!
//! context.add_callback("myCallback", |a: i32, b: i32| a + b).unwrap();
//!
//! context.eval(r#"
//! // x will equal 30
//! var x = myCallback(10, 20);
//! "#).unwrap();
//! ```
#![deny(missing_docs)]
mod bindings;
mod callback;
pub mod console;
mod droppable_value;
mod value;
use std::{convert::TryFrom, error, fmt};
pub use callback::{Arguments, Callback};
pub use value::*;
/// Error on Javascript execution.
#[derive(PartialEq, Debug)]
pub enum ExecutionError {
/// Code to be executed contained zero-bytes.
InputWithZeroBytes,
/// Value conversion failed. (either input arguments or result value).
Conversion(ValueError),
/// Internal error.
Internal(String),
/// JS Exception was thrown.
Exception(JsValue),
/// JS Runtime exceeded the memory limit.
OutOfMemory,
#[doc(hidden)]
__NonExhaustive,
}
impl fmt::Display for ExecutionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ExecutionError::*;
match self {
InputWithZeroBytes => write!(f, "Invalid script input: code contains zero byte (\\0)"),
Conversion(e) => e.fmt(f),
Internal(e) => write!(f, "Internal error: {}", e),
Exception(e) => write!(f, "{:?}", e),
OutOfMemory => write!(f, "Out of memory: runtime memory limit exceeded"),
__NonExhaustive => unreachable!(),
}
}
}
impl error::Error for ExecutionError {}
impl From<ValueError> for ExecutionError {
fn from(v: ValueError) -> Self {
ExecutionError::Conversion(v)
}
}
/// Error on context creation.
#[derive(Debug)]
pub enum ContextError {
/// Runtime could not be created.
RuntimeCreationFailed,
/// Context could not be created.
ContextCreationFailed,
/// Execution error while building.
Execution(ExecutionError),
#[doc(hidden)]
__NonExhaustive,
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ContextError::*;
match self {
RuntimeCreationFailed => write!(f, "Could not create runtime"),
ContextCreationFailed => write!(f, "Could not create context"),
Execution(e) => e.fmt(f),
__NonExhaustive => unreachable!(),
}
}
}
impl error::Error for ContextError {}
/// A builder for [Context](Context).
///
/// Create with [Context::builder](Context::builder).
pub struct ContextBuilder {
memory_limit: Option<usize>,
console_backend: Option<Box<dyn console::ConsoleBackend>>,
}
impl ContextBuilder {
fn new() -> Self {
Self {
memory_limit: None,
console_backend: None,
}
}
/// Sets the memory limit of the Javascript runtime (in bytes).
///
/// If the limit is exceeded, methods like `eval` will return
/// a `Err(ExecutionError::Exception(JsValue::Null))`
// TODO: investigate why we don't get a proper exception message here.
pub fn memory_limit(self, max_bytes: usize) -> Self {
let mut s = self;
s.memory_limit = Some(max_bytes);
s
}
/// Set a console handler that will proxy `console.{log,trace,debug,...}`
/// calls.
///
/// The given argument must implement the [ConsoleBackend] trait.
///
/// A very simple logger could look like this:
pub fn console<B>(mut self, backend: B) -> Self
where
B: console::ConsoleBackend,
{
self.console_backend = Some(Box::new(backend));
self
}
/// Finalize the builder and build a JS Context.
pub fn build(self) -> Result<Context, ContextError> {
let wrapper = bindings::ContextWrapper::new(self.memory_limit)?;
if let Some(be) = self.console_backend {
wrapper.set_console(be).map_err(ContextError::Execution)?;
}
Ok(Context::from_wrapper(wrapper))
}
}
/// Context is a wrapper around a QuickJS Javascript context.
/// It is the primary way to interact with the runtime.
///
/// For each `Context` instance a new instance of QuickJS
/// runtime is created. It means that it is safe to use
/// different contexts in different threads, but each
/// `Context` instance must be used only from a single thread.
pub struct Context {
wrapper: bindings::ContextWrapper,
}
impl Context {
fn from_wrapper(wrapper: bindings::ContextWrapper) -> Self {
Self { wrapper }
}
/// Create a `ContextBuilder` that allows customization of JS Runtime settings.
///
/// For details, see the methods on `ContextBuilder`.
///
/// ```rust
/// let _context = quick_js::Context::builder()
/// .memory_limit(100_000)
/// .build()
/// .unwrap();
/// ```
pub fn builder() -> ContextBuilder {
ContextBuilder::new()
}
/// Create a new Javascript context with default settings.
pub fn new() -> Result<Self, ContextError> {
let wrapper = bindings::ContextWrapper::new(None)?;
Ok(Self::from_wrapper(wrapper))
}
/// Reset the Javascript engine.
///
/// All state and callbacks will be removed.
pub fn reset(self) -> Result<Self, ContextError> {
let wrapper = self.wrapper.reset()?;
Ok(Self { wrapper })
}
/// Evaluates Javascript code and returns the value of the final expression.
///
/// **Promises**:
/// If the evaluated code returns a Promise, the event loop
/// will be executed until the promise is finished. The final value of
/// the promise will be returned, or a `ExecutionError::Exception` if the
/// promise failed.
///
/// ```rust
/// use quick_js::{Context, JsValue};
/// let context = Context::new().unwrap();
///
/// let value = context.eval(" 1 + 2 + 3 ");
/// assert_eq!(
/// value,
/// Ok(JsValue::Int(6)),
/// );
///
/// let value = context.eval(r#"
/// function f() { return 55 * 3; }
/// let y = f();
/// var x = y.toString() + "!"
/// x
/// "#);
/// assert_eq!(
/// value,
/// Ok(JsValue::String("165!".to_string())),
/// );
/// ```
pub fn eval(&self, code: &str) -> Result<JsValue, ExecutionError> {
let value_raw = self.wrapper.eval(code)?;
let value = value_raw.to_value()?;
Ok(value)
}
/// Evaluates Javascript code and returns the value of the final expression
/// as a Rust type.
///
/// **Promises**:
/// If the evaluated code returns a Promise, the event loop
/// will be executed until the promise is finished. The final value of
/// the promise will be returned, or a `ExecutionError::Exception` if the
/// promise failed.
///
/// ```rust
/// use quick_js::{Context};
/// let context = Context::new().unwrap();
///
/// let res = context.eval_as::<bool>(" 100 > 10 ");
/// assert_eq!(
/// res,
/// Ok(true),
/// );
///
/// let value: i32 = context.eval_as(" 10 + 10 ").unwrap();
/// assert_eq!(
/// value,
/// 20,
/// );
/// ```
pub fn eval_as<R>(&self, code: &str) -> Result<R, ExecutionError>
where
R: TryFrom<JsValue>,
R::Error: Into<ValueError>,
{
let value_raw = self.wrapper.eval(code)?;
let value = value_raw.to_value()?;
let ret = R::try_from(value).map_err(|e| e.into())?;
Ok(ret)
}
/// Call a global function in the Javascript namespace.
///
/// **Promises**:
/// If the evaluated code returns a Promise, the event loop
/// will be executed until the promise is finished. The final value of
/// the promise will be returned, or a `ExecutionError::Exception` if the
/// promise failed.
///
/// ```rust
/// use quick_js::{Context, JsValue};
/// let context = Context::new().unwrap();
///
/// let res = context.call_function("encodeURIComponent", vec!["a=b"]);
/// assert_eq!(
/// res,
/// Ok(JsValue::String("a%3Db".to_string())),
/// );
/// ```
pub fn call_function(
&self,
function_name: &str,
args: impl IntoIterator<Item = impl Into<JsValue>>,
) -> Result<JsValue, ExecutionError> {
let qargs = args
.into_iter()
.map(|arg| self.wrapper.serialize_value(arg.into()))
.collect::<Result<Vec<_>, _>>()?;
let global = self.wrapper.global()?;
let func_obj = global.property(function_name)?;
if !func_obj.is_object() {
return Err(ExecutionError::Internal(format!(
"Could not find function '{}' in global scope: does not exist, or not an object",
function_name
)));
}
let value = self.wrapper.call_function(func_obj, qargs)?.to_value()?;
Ok(value)
}
/// Add a global JS function that is backed by a Rust function or closure.
///
/// The callback must satisfy several requirements:
/// * accepts 0 - 5 arguments
/// * each argument must be convertible from a JsValue
/// * must return a value
/// * the return value must either:
/// - be convertible to JsValue
/// - be a Result<T, E> where T is convertible to JsValue
/// if Err(e) is returned, a Javascript exception will be raised
///
/// ```rust
/// use quick_js::{Context, JsValue};
/// let context = Context::new().unwrap();
///
/// // Register a closue as a callback under the "add" name.
/// // The 'add' function can now be called from Javascript code.
/// context.add_callback("add", |a: i32, b: i32| { a + b }).unwrap();
///
/// // Now we try out the 'add' function via eval.
/// let output = context.eval_as::<i32>(" add( 3 , 4 ) ").unwrap();
/// assert_eq!(
/// output,
/// 7,
/// );
/// ```
pub fn add_callback<F>(
&self,
name: &str,
callback: impl Callback<F> + 'static,
) -> Result<(), ExecutionError> {
self.wrapper.add_callback(name, callback)
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
// #[test]
// fn test_global_properties() {
// let c = Context::new().unwrap();
// assert_eq!(
// c.global_property("lala"),
// Err(ExecutionError::Exception(
// "Global object does not have property 'lala'".into()
// ))
// );
// c.set_global_property("testprop", true).unwrap();
// assert_eq!(
// c.global_property("testprop").unwrap(),
// JsValue::Bool(true),
// );
// }
#[test]
fn test_eval_pass() {
use std::iter::FromIterator;
let c = Context::new().unwrap();
let cases = vec![
("null", Ok(JsValue::Null)),
("true", Ok(JsValue::Bool(true))),
("2 > 10", Ok(JsValue::Bool(false))),
("1", Ok(JsValue::Int(1))),
("1 + 1", Ok(JsValue::Int(2))),
("1.1", Ok(JsValue::Float(1.1))),
("2.2 * 2 + 5", Ok(JsValue::Float(9.4))),
("\"abc\"", Ok(JsValue::String("abc".into()))),
(
"[1,2]",
Ok(JsValue::Array(vec![JsValue::Int(1), JsValue::Int(2)])),
),
];
for (code, res) in cases.into_iter() {
assert_eq!(c.eval(code), res,);
}
let obj_cases = vec![
(
r#" {"a": null} "#,
Ok(JsValue::Object(HashMap::from_iter(vec![(
"a".to_string(),
JsValue::Null,
)]))),
),
(
r#" {a: 1, b: true, c: {c1: false}} "#,
Ok(JsValue::Object(HashMap::from_iter(vec![
("a".to_string(), JsValue::Int(1)),
("b".to_string(), JsValue::Bool(true)),
(
"c".to_string(),
JsValue::Object(HashMap::from_iter(vec![(
"c1".to_string(),
JsValue::Bool(false),
)])),
),
]))),
),
];
for (index, (code, res)) in obj_cases.into_iter().enumerate() {
let full_code = format!(
"var v{index} = {code}; v{index}",
index = index,
code = code
);
assert_eq!(c.eval(&full_code), res,);
}
assert_eq!(c.eval_as::<bool>("true").unwrap(), true,);
assert_eq!(c.eval_as::<i32>("1 + 2").unwrap(), 3,);
let value: String = c.eval_as("var x = 44; x.toString()").unwrap();
assert_eq!(&value, "44");
#[cfg(feature = "bigint")]
assert_eq!(
c.eval_as::<num_bigint::BigInt>("1n << 100n").unwrap(),
num_bigint::BigInt::from(1i128 << 100)
);
#[cfg(feature = "bigint")]
assert_eq!(c.eval_as::<i64>("1 << 30").unwrap(), 1i64 << 30);
#[cfg(feature = "bigint")]
assert_eq!(c.eval_as::<u128>("1n << 100n").unwrap(), 1u128 << 100);
}
#[test]
fn test_eval_syntax_error() {
let c = Context::new().unwrap();
assert_eq!(
c.eval(
r#"
!!!!
"#
),
Err(ExecutionError::Exception(
"SyntaxError: unexpected token in expression: \'\'".into()
))
);
}
#[test]
fn test_eval_exception() {
let c = Context::new().unwrap();
assert_eq!(
c.eval(
r#"
function f() {
throw new Error("My Error");
}
f();
"#
),
Err(ExecutionError::Exception("Error: My Error".into(),))
);
}
#[test]
fn eval_async() {
let c = Context::new().unwrap();
let value = c
.eval(
r#"
new Promise((resolve, _) => {
resolve(33);
})
"#,
)
.unwrap();
assert_eq!(value, JsValue::Int(33));
let res = c.eval(
r#"
new Promise((_resolve, reject) => {
reject("Failed...");
})
"#,
);
assert_eq!(
res,
Err(ExecutionError::Exception(JsValue::String(
"Failed...".into()
)))
);
}
#[test]
fn test_call() {
let c = Context::new().unwrap();
assert_eq!(
c.call_function("parseInt", vec!["22"]).unwrap(),
JsValue::Int(22),
);
c.eval(
r#"
function add(a, b) {
return a + b;
}
"#,
)
.unwrap();
assert_eq!(
c.call_function("add", vec![5, 7]).unwrap(),
JsValue::Int(12),
);
c.eval(
r#"
function sumArray(arr) {
let sum = 0;
for (const value of arr) {
sum += value;
}
return sum;
}
"#,
)
.unwrap();
assert_eq!(
c.call_function("sumArray", vec![vec![1, 2, 3]]).unwrap(),
JsValue::Int(6),
);
c.eval(
r#"
function addObject(obj) {
let sum = 0;
for (const key of Object.keys(obj)) {
sum += obj[key];
}
return sum;
}
"#,
)
.unwrap();
let mut obj = std::collections::HashMap::<String, i32>::new();
obj.insert("a".into(), 10);
obj.insert("b".into(), 20);
obj.insert("c".into(), 30);
assert_eq!(
c.call_function("addObject", vec![obj]).unwrap(),
JsValue::Int(60),
);
}
#[test]
fn test_call_large_string() {
let c = Context::new().unwrap();
c.eval(" function strLen(s) { return s.length; } ").unwrap();
let s = " ".repeat(200_000);
let v = c.call_function("strLen", vec![s]).unwrap();
assert_eq!(v, JsValue::Int(200_000));
}
#[test]
fn call_async() {
let c = Context::new().unwrap();
c.eval(
r#"
function asyncOk() {
return new Promise((resolve, _) => {
resolve(33);
});
}
function asyncErr() {
return new Promise((_resolve, reject) => {
reject("Failed...");
});
}
"#,
)
.unwrap();
let value = c.call_function("asyncOk", vec![true]).unwrap();
assert_eq!(value, JsValue::Int(33));
let res = c.call_function("asyncErr", vec![true]);
assert_eq!(
res,
Err(ExecutionError::Exception(JsValue::String(
"Failed...".into()
)))
);
}
#[test]
fn test_callback() {
let c = Context::new().unwrap();
c.add_callback("cb1", |flag: bool| !flag).unwrap();
assert_eq!(c.eval("cb1(true)").unwrap(), JsValue::Bool(false),);
c.add_callback("concat2", |a: String, b: String| format!("{}{}", a, b))
.unwrap();
assert_eq!(
c.eval(r#"concat2("abc", "def")"#).unwrap(),
JsValue::String("abcdef".into()),
);
c.add_callback("add2", |a: i32, b: i32| -> i32 { a + b })
.unwrap();
assert_eq!(c.eval("add2(5, 11)").unwrap(), JsValue::Int(16),);
}
#[test]
fn test_callback_argn_variants() {
macro_rules! callback_argn_tests {
[
$(
$len:literal : ( $( $argn:ident : $argv:literal ),* ),
)*
] => {
$(
{
// Test plain return type.
let name = format!("cb{}", $len);
let c = Context::new().unwrap();
c.add_callback(&name, | $( $argn : i32 ),*| -> i32 {
$( $argn + )* 0
}).unwrap();
let code = format!("{}( {} )", name, "1,".repeat($len));
let v = c.eval(&code).unwrap();
assert_eq!(v, JsValue::Int($len));
// Test Result<T, E> return type with OK(_) returns.
let name = format!("cbres{}", $len);
c.add_callback(&name, | $( $argn : i32 ),*| -> Result<i32, String> {
Ok($( $argn + )* 0)
}).unwrap();
let code = format!("{}( {} )", name, "1,".repeat($len));
let v = c.eval(&code).unwrap();
assert_eq!(v, JsValue::Int($len));
// Test Result<T, E> return type with Err(_) returns.
let name = format!("cbreserr{}", $len);
c.add_callback(&name, #[allow(unused_variables)] | $( $argn : i32 ),*| -> Result<i32, String> {
Err("error".into())
}).unwrap();
let code = format!("{}( {} )", name, "1,".repeat($len));
let res = c.eval(&code);
assert_eq!(res, Err(ExecutionError::Exception("error".into())));
}
)*
}
}
callback_argn_tests![
1: (a : 1),
]
}
#[test]
fn test_callback_varargs() {
let c = Context::new().unwrap();
// No return.
c.add_callback("cb", |args: Arguments| {
let args = args.into_vec();
assert_eq!(
args,
vec![
JsValue::String("hello".into()),
JsValue::Bool(true),
JsValue::from(100),
]
);
})
.unwrap();
c.eval(" cb('hello', true, 100) ").unwrap();
// With return.
c.add_callback("cb2", |args: Arguments| -> u32 {
let args = args.into_vec();
assert_eq!(
args,
vec![JsValue::from(1), JsValue::from(10), JsValue::from(100),]
);
111
})
.unwrap();
c.eval(
r#"
var x = cb2(1, 10, 100);
if (x !== 111) {
throw new Error('Expected 111, got ' + x);
}
"#,
)
.unwrap();
}
#[test]
fn test_callback_invalid_argcount() {
let c = Context::new().unwrap();
c.add_callback("cb", |a: i32, b: i32| a + b).unwrap();
assert_eq!(
c.eval(" cb(5) "),
Err(ExecutionError::Exception(
"Invalid argument count: Expected 2, got 1".into()
)),
);
}
#[test]
fn memory_limit_exceeded() {
let c = Context::builder().memory_limit(100_000).build().unwrap();
assert_eq!(
c.eval(" 'abc'.repeat(200_000) "),
Err(ExecutionError::OutOfMemory),
);
}
#[test]
fn context_reset() {
let c = Context::new().unwrap();
c.eval(" var x = 123; ").unwrap();
c.add_callback("myCallback", || true).unwrap();
let c2 = c.reset().unwrap();
// Check it still works.
assert_eq!(
c2.eval_as::<String>(" 'abc'.repeat(2) ").unwrap(),
"abcabc".to_string(),
);
// Check old state is gone.
let err_msg = c2.eval(" x ").unwrap_err().to_string();
assert!(err_msg.contains("ReferenceError"));
// Check callback is gone.
let err_msg = c2.eval(" myCallback() ").unwrap_err().to_string();
assert!(err_msg.contains("ReferenceError"));
}
#[inline(never)]
fn build_context() -> Context {
let ctx = Context::new().unwrap();
let name = "cb".to_string();
ctx.add_callback(&name, |a: String| a.repeat(2)).unwrap();
let code = " function f(value) { return cb(value); } ".to_string();
ctx.eval(&code).unwrap();
ctx
}
#[test]
fn moved_context() {
let c = build_context();
let v = c.call_function("f", vec!["test"]).unwrap();
assert_eq!(v, "testtest".into());
let v = c.eval(" f('la') ").unwrap();
assert_eq!(v, "lala".into());
}
#[cfg(feature = "chrono")]
#[test]
fn chrono_serialize() {
let c = build_context();
c.eval(
"
function dateToTimestamp(date) {
return date.getTime();
}
",
)
.unwrap();
let now = chrono::Utc::now();
let now_millis = now.timestamp_millis();
let timestamp = c
.call_function("dateToTimestamp", vec![JsValue::Date(now.clone())])
.unwrap();
assert_eq!(timestamp, JsValue::Float(now_millis as f64));
}
#[cfg(feature = "chrono")]
#[test]
fn chrono_deserialize() {
use chrono::offset::TimeZone;
let c = build_context();
let value = c.eval(" new Date(1234567555) ").unwrap();
let datetime = chrono::Utc.timestamp_millis(1234567555);
assert_eq!(value, JsValue::Date(datetime));
}
#[cfg(feature = "chrono")]
#[test]
fn chrono_roundtrip() {
let c = build_context();
c.eval(" function identity(x) { return x; } ").unwrap();
let d = chrono::Utc::now();
let td = JsValue::Date(d.clone());
let td2 = c.call_function("identity", vec![td.clone()]).unwrap();
let d2 = if let JsValue::Date(x) = td2 {
x
} else {
panic!("expected date")
};
assert_eq!(d.timestamp_millis(), d2.timestamp_millis());
}
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_deserialize_i64() {
for i in vec![0, std::i64::MAX, std::i64::MIN] {
let c = Context::new().unwrap();
let value = c.eval(&format!("{}n", i)).unwrap();
assert_eq!(value, JsValue::BigInt(i.into()));
}
}
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_deserialize_bigint() {
for i in vec![
std::i64::MAX as i128 + 1,
std::i64::MIN as i128 - 1,
std::i128::MAX,
std::i128::MIN,
] {
let c = Context::new().unwrap();
let value = c.eval(&format!("{}n", i)).unwrap();
let expected = num_bigint::BigInt::from(i);
assert_eq!(value, JsValue::BigInt(expected.into()));
}
}
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_serialize_i64() {
for i in vec![0, std::i64::MAX, std::i64::MIN] {
let c = Context::new().unwrap();
c.eval(&format!(" function isEqual(x) {{ return x === {}n }} ", i))
.unwrap();
assert_eq!(
c.call_function("isEqual", vec![JsValue::BigInt(i.into())])
.unwrap(),
JsValue::Bool(true)
);
}
}
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_serialize_bigint() {
for i in vec![
std::i64::MAX as i128 + 1,
std::i64::MIN as i128 - 1,
std::i128::MAX,
std::i128::MIN,
] {
let c = Context::new().unwrap();
c.eval(&format!(" function isEqual(x) {{ return x === {}n }} ", i))
.unwrap();
let value = JsValue::BigInt(num_bigint::BigInt::from(i).into());
assert_eq!(
c.call_function("isEqual", vec![value]).unwrap(),
JsValue::Bool(true)
);
}
}
#[test]
fn test_console() {
use console::Level;
use std::sync::{Arc, Mutex};
let messages = Arc::new(Mutex::new(Vec::<(Level, Vec<JsValue>)>::new()));
let m = messages.clone();
let c = Context::builder()
.console(move |level: Level, args: Vec<JsValue>| {
m.lock().unwrap().push((level, args));
})
.build()
.unwrap();
c.eval(
r#"
console.log("hi");
console.error(false);
"#,
)
.unwrap();
let m = messages.lock().unwrap();
assert_eq!(
*m,
vec![
(Level::Log, vec![JsValue::from("hi")]),
(Level::Error, vec![JsValue::from(false)]),
]
);
}
}

View File

@@ -0,0 +1,107 @@
use num_traits::cast::ToPrimitive;
#[derive(Clone, Debug)]
pub enum BigIntOrI64 {
Int(i64),
BigInt(num_bigint::BigInt),
}
impl PartialEq for BigIntOrI64 {
fn eq(&self, other: &Self) -> bool {
use BigIntOrI64::*;
match (&self, &other) {
(Int(i), Int(j)) => i == j,
(Int(i), BigInt(b)) | (BigInt(b), Int(i)) => b == &num_bigint::BigInt::from(*i),
(BigInt(a), BigInt(b)) => a == b,
}
}
}
impl Eq for BigIntOrI64 {}
/// A value holding JavaScript
/// [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) type
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BigInt {
pub(crate) inner: BigIntOrI64,
}
impl BigInt {
/// Return `Some` if value fits into `i64` and `None` otherwise
pub fn as_i64(&self) -> Option<i64> {
match &self.inner {
BigIntOrI64::Int(int) => Some(*int),
BigIntOrI64::BigInt(bigint) => bigint.to_i64(),
}
}
/// Convert value into `num_bigint::BigInt`
pub fn into_bigint(self) -> num_bigint::BigInt {
match self.inner {
BigIntOrI64::Int(int) => int.into(),
BigIntOrI64::BigInt(bigint) => bigint,
}
}
}
impl std::fmt::Display for BigInt {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.inner {
BigIntOrI64::Int(i) => write!(f, "{}", i),
BigIntOrI64::BigInt(ref i) => write!(f, "{}", i),
}
}
}
impl From<i64> for BigInt {
fn from(int: i64) -> Self {
BigInt {
inner: BigIntOrI64::Int(int),
}
}
}
impl From<num_bigint::BigInt> for BigInt {
fn from(bigint: num_bigint::BigInt) -> Self {
BigInt {
inner: BigIntOrI64::BigInt(bigint),
}
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_bigint_as_i64() {
let value = BigInt {
inner: BigIntOrI64::Int(1234i64),
};
assert_eq!(value.as_i64(), Some(1234i64));
}
#[test]
fn test_bigint_as_i64_overflow() {
let value = BigInt {
inner: BigIntOrI64::BigInt(num_bigint::BigInt::from(std::i128::MAX)),
};
assert_eq!(value.as_i64(), None);
}
#[test]
fn test_bigint_into_bigint() {
for i in vec![
0 as i128,
std::i64::MAX as i128,
std::i64::MIN as i128,
std::i128::MAX,
std::i128::MIN,
] {
let value = BigInt {
inner: BigIntOrI64::BigInt(num_bigint::BigInt::from(i)),
};
assert_eq!(value.into_bigint(), num_bigint::BigInt::from(i));
}
}
}

View File

@@ -0,0 +1,320 @@
#[cfg(feature = "bigint")]
pub(crate) mod bigint;
use std::convert::{TryFrom, TryInto};
use std::{collections::HashMap, error, fmt};
#[cfg(feature = "bigint")]
pub use bigint::BigInt;
/// A value that can be (de)serialized to/from the quickjs runtime.
#[derive(PartialEq, Clone, Debug)]
#[allow(missing_docs)]
pub enum JsValue {
Null,
Bool(bool),
Int(i32),
Float(f64),
String(String),
Array(Vec<JsValue>),
Object(HashMap<String, JsValue>),
/// chrono::Datetime<Utc> / JS Date integration.
/// Only available with the optional `chrono` feature.
#[cfg(feature = "chrono")]
Date(chrono::DateTime<chrono::Utc>),
/// num_bigint::BigInt / JS BigInt integration
/// Only available with the optional `bigint` feature
#[cfg(feature = "bigint")]
BigInt(crate::BigInt),
#[doc(hidden)]
__NonExhaustive,
}
impl JsValue {
/// Cast value to a str.
///
/// Returns `Some(&str)` if value is a `JsValue::String`, None otherwise.
pub fn as_str(&self) -> Option<&str> {
match self {
JsValue::String(ref s) => Some(s.as_str()),
_ => None,
}
}
/// Convert to `String`.
pub fn into_string(self) -> Option<String> {
match self {
JsValue::String(s) => Some(s),
_ => None,
}
}
}
macro_rules! value_impl_from {
(
(
$( $t1:ty => $var1:ident, )*
)
(
$( $t2:ty => |$exprname:ident| $expr:expr => $var2:ident, )*
)
) => {
$(
impl From<$t1> for JsValue {
fn from(value: $t1) -> Self {
JsValue::$var1(value)
}
}
impl std::convert::TryFrom<JsValue> for $t1 {
type Error = ValueError;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
match value {
JsValue::$var1(inner) => Ok(inner),
_ => Err(ValueError::UnexpectedType)
}
}
}
)*
$(
impl From<$t2> for JsValue {
fn from(value: $t2) -> Self {
let $exprname = value;
let inner = $expr;
JsValue::$var2(inner)
}
}
)*
}
}
value_impl_from! {
(
bool => Bool,
i32 => Int,
f64 => Float,
String => String,
)
(
i8 => |x| i32::from(x) => Int,
i16 => |x| i32::from(x) => Int,
u8 => |x| i32::from(x) => Int,
u16 => |x| i32::from(x) => Int,
u32 => |x| f64::from(x) => Float,
)
}
#[cfg(feature = "bigint")]
value_impl_from! {
()
(
i64 => |x| x.into() => BigInt,
u64 => |x| num_bigint::BigInt::from(x).into() => BigInt,
i128 => |x| num_bigint::BigInt::from(x).into() => BigInt,
u128 => |x| num_bigint::BigInt::from(x).into() => BigInt,
num_bigint::BigInt => |x| x.into() => BigInt,
)
}
#[cfg(feature = "bigint")]
impl std::convert::TryFrom<JsValue> for i64 {
type Error = ValueError;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
match value {
JsValue::Int(int) => Ok(int as i64),
JsValue::BigInt(bigint) => bigint.as_i64().ok_or(ValueError::UnexpectedType),
_ => Err(ValueError::UnexpectedType),
}
}
}
#[cfg(feature = "bigint")]
macro_rules! value_bigint_impl_tryfrom {
(
($($t:ty => $to_type:ident, )*)
) => {
$(
impl std::convert::TryFrom<JsValue> for $t {
type Error = ValueError;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
use num_traits::ToPrimitive;
match value {
JsValue::Int(int) => Ok(int as $t),
JsValue::BigInt(bigint) => bigint
.into_bigint()
.$to_type()
.ok_or(ValueError::UnexpectedType),
_ => Err(ValueError::UnexpectedType),
}
}
}
)*
}
}
#[cfg(feature = "bigint")]
value_bigint_impl_tryfrom! {
(
u64 => to_u64,
i128 => to_i128,
u128 => to_u128,
)
}
#[cfg(feature = "bigint")]
impl std::convert::TryFrom<JsValue> for num_bigint::BigInt {
type Error = ValueError;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
match value {
JsValue::Int(int) => Ok(num_bigint::BigInt::from(int)),
JsValue::BigInt(bigint) => Ok(bigint.into_bigint()),
_ => Err(ValueError::UnexpectedType),
}
}
}
impl<T> From<Vec<T>> for JsValue
where
T: Into<JsValue>,
{
fn from(values: Vec<T>) -> Self {
let items = values.into_iter().map(|x| x.into()).collect();
JsValue::Array(items)
}
}
impl<'a> From<&'a str> for JsValue {
fn from(val: &'a str) -> Self {
JsValue::String(val.into())
}
}
impl<T> From<Option<T>> for JsValue
where
T: Into<JsValue>,
{
fn from(opt: Option<T>) -> Self {
if let Some(value) = opt {
value.into()
} else {
JsValue::Null
}
}
}
impl<K, V> From<HashMap<K, V>> for JsValue
where
K: Into<String>,
V: Into<JsValue>,
{
fn from(map: HashMap<K, V>) -> Self {
let new_map = map.into_iter().map(|(k, v)| (k.into(), v.into())).collect();
JsValue::Object(new_map)
}
}
impl<V> TryFrom<JsValue> for HashMap<String, V>
where
V: TryFrom<JsValue>,
{
type Error = ValueError;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
match value {
JsValue::Object(object) => object
.into_iter()
.map(|(k, v)| match v.try_into() {
Ok(v) => Ok((k, v)),
Err(_) => Err(ValueError::UnexpectedType),
})
.collect(),
_ => Err(ValueError::UnexpectedType),
}
}
}
/// Error during value conversion.
#[derive(PartialEq, Eq, Debug)]
pub enum ValueError {
/// Invalid non-utf8 string.
InvalidString(std::str::Utf8Error),
/// Encountered string with \0 bytes.
StringWithZeroBytes(std::ffi::NulError),
/// Internal error.
Internal(String),
/// Received an unexpected type that could not be converted.
UnexpectedType,
#[doc(hidden)]
__NonExhaustive,
}
// TODO: remove this once either the Never type get's stabilized or the compiler
// can properly handle Infallible.
impl From<std::convert::Infallible> for ValueError {
fn from(_: std::convert::Infallible) -> Self {
unreachable!()
}
}
impl fmt::Display for ValueError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ValueError::*;
match self {
InvalidString(e) => write!(
f,
"Value conversion failed - invalid non-utf8 string: {}",
e
),
StringWithZeroBytes(_) => write!(f, "String contains \\0 bytes",),
Internal(e) => write!(f, "Value conversion failed - internal error: {}", e),
UnexpectedType => write!(f, "Could not convert - received unexpected type"),
__NonExhaustive => unreachable!(),
}
}
}
impl error::Error for ValueError {}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_from_i64() {
let int = 1234i64;
let value = JsValue::from(int);
if let JsValue::BigInt(value) = value {
assert_eq!(value.as_i64(), Some(int));
} else {
panic!("Expected JsValue::BigInt");
}
}
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_from_bigint() {
let bigint = num_bigint::BigInt::from(std::i128::MAX);
let value = JsValue::from(bigint.clone());
if let JsValue::BigInt(value) = value {
assert_eq!(value.into_bigint(), bigint);
} else {
panic!("Expected JsValue::BigInt");
}
}
#[cfg(feature = "bigint")]
#[test]
fn test_bigint_i64_bigint_eq() {
let value_i64 = JsValue::BigInt(1234i64.into());
let value_bigint = JsValue::BigInt(num_bigint::BigInt::from(1234i64).into());
assert_eq!(value_i64, value_bigint);
}
}

View File

@@ -1,5 +1,46 @@
use std::collections::HashMap;
mod qjs;
mod sig;
pub use qjs::*;
pub use sig::*; // TODO
fn main() {
println!("Hello, world!");
let context = QuickJSContext::new().unwrap();
let mut map = HashMap::new();
map.insert("name", "hatter");
let script = r##"
function __EXPORT(f) { eval('__PUBLIC_' + f + '=' + f); }
function getName() {
console.info("=========================");
for (var i = 0; i < arguments.length; i++) {
console.info(i, ' ----> ', arguments[i]);
}
return 'hatter';
}
function helloAb(a, b) {
console.info("Hello: ", a);
console.info("Hello: ", b);
}
function main(p) {
console.log(p);
return 1;
}
__EXPORT('getName');
__EXPORT('helloAb');
"##;
context.init(script).unwrap();
let r = context.run_js(&map);
println!("{:?}", r);
let r = context.call_fn("getName", "[1, 'hatter', 'jiang']");
println!("{:?}", r);
let r = context.call_fn("helloAb", "['hatter', 'jiang']");
println!("{:?}", r);
}

View File

@@ -0,0 +1,65 @@
use quick_js::Context;
use quick_js::JsValue;
use quick_js::console::Level;
use quick_js::console::ConsoleBackend;
use serde::{ Serialize, Deserialize };
use rust_util::XResult;
pub struct QuickJSContext {
context: Context,
}
impl QuickJSContext {
pub fn new() -> XResult<Self> {
Ok(Self{
context: build_default_context()?,
})
}
pub fn init(&self, js: &str) -> XResult<JsValue> {
self.context.eval(js).map_err(|e| e.into())
}
pub fn run_js<T>(&self, p: &T) -> XResult<JsValue> where T: ?Sized + Serialize {
let p_json = match serde_json::to_string(p) {
Err(e) => return Err(e.into()),
Ok(p) => p,
};
self.context.eval(&format!("main({})", p_json)).map_err(|e| e.into())
}
pub fn call_fn(&self, fun: &str, params: &str) -> XResult<JsValue> {
if !params.trim().starts_with("[") {
return Err(rust_util::new_box_error("Params is not JSON array!"));
}
let f: String = fun.chars().map(|c| if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' {
c
} else {
'_'
}).collect();
// TODO check JSON valid
// let v: Option<Vec<EmptyObject>> = serde_json::from_str(params).ok();
// if v.is_none() {
// return Err(rust_util::new_box_error("Params is not valid JSON array!"));
// }
self.context.eval(&format!("__PUBLIC_{}.apply(null, {})", f, params)).map_err(|e| e.into())
}
}
#[derive(Serialize, Deserialize)]
struct EmptyObject {}
struct ConsoleBackendImpl;
impl ConsoleBackend for ConsoleBackendImpl {
fn log(&self, level: Level, values: Vec<JsValue>) {
println!("[{:>5}] - {:?}", format!("{:?}", level).to_uppercase(), values);
}
}
fn build_default_context() -> XResult<Context> {
Context::builder().memory_limit(16 * 1024 * 1024)
.console(ConsoleBackendImpl{})
.build()
.map_err(|e| e.into())
}

View File

@@ -3,8 +3,8 @@ use std::io::{ Write, Read };
use serde::{ Deserialize, Serialize };
use ring::{
signature::{ KeyPair, Ed25519KeyPair, UnparsedPublicKey, ED25519 },
hmac, rand, error::Unspecified,
digest,
rand,
// hmac, digest, error::Unspecified,
};
use rust_util::XResult;
@@ -14,7 +14,7 @@ pub struct SigningKeyPair {
}
impl SigningKeyPair {
fn new() -> Self {
pub fn new() -> Self {
let rng = rand::SystemRandom::new();
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); // TODO ...
SigningKeyPair{
@@ -22,19 +22,19 @@ impl SigningKeyPair {
}
}
fn parse(&self) -> Ed25519KeyPair {
pub fn key_pair(&self) -> Ed25519KeyPair {
Ed25519KeyPair::from_pkcs8(&self.key_pair).unwrap() // TODO ...
}
fn public_key(&self) -> Vec<u8> {
self.parse().public_key().as_ref().to_vec()
pub fn public_key(&self) -> Vec<u8> {
self.key_pair().public_key().as_ref().to_vec()
}
fn unparsed_public_key(&self) -> UnparsedPublicKey<Vec<u8>> {
pub fn unparsed_public_key(&self) -> UnparsedPublicKey<Vec<u8>> {
UnparsedPublicKey::new(&ED25519, self.public_key())
}
fn read_from_file(file: &str) -> XResult<Self> {
pub fn read_from_file(file: &str) -> XResult<Self> {
match File::open(file) {
Err(e) => Err(rust_util::new_box_ioerror(&format!("Read from file failed: {}", e))),
Ok(mut f) => {
@@ -53,7 +53,7 @@ impl SigningKeyPair {
}
}
fn write_to_file(&self, file: &str) -> XResult<()> {
pub fn write_to_file(&self, file: &str) -> XResult<()> {
if File::open(file).is_ok() {
return Err(rust_util::new_box_ioerror(&format!("File exists: {}", file)));
}
@@ -90,7 +90,7 @@ impl SignedMessage {
}
pub fn sign(&mut self, key_pair: &SigningKeyPair) {
let sig = key_pair.parse().sign(&self.msg);
let sig = key_pair.key_pair().sign(&self.msg);
self.sig = Some(sig.as_ref().to_vec());
}
@@ -113,4 +113,4 @@ fn test_sign() {
);
signed_message.sign(&signing_key_pair);
assert!(signed_message.verify(&signing_key_pair.public_key()))
}
}