Compare commits
No commits in common. "master" and "improve-date-parsing" have entirely different histories.
master
...
improve-da
46 changed files with 5674 additions and 7925 deletions
|
@ -2,13 +2,6 @@
|
||||||
|
|
||||||
## Master branch
|
## Master branch
|
||||||
|
|
||||||
## v0.4.2 - 2024-03-23
|
|
||||||
|
|
||||||
* Update to QuickJS `2024-01-13`
|
|
||||||
* Update dependencies
|
|
||||||
|
|
||||||
## v0.4.1 - 2023-02-05
|
|
||||||
|
|
||||||
* Fixed use after free in `set_global` [#105](https://github.com/theduke/quickjs-rs/issues/105)
|
* Fixed use after free in `set_global` [#105](https://github.com/theduke/quickjs-rs/issues/105)
|
||||||
* `add_callback` can now take `JsValue` arguments [#109](https://github.com/theduke/quickjs-rs/issues/109)
|
* `add_callback` can now take `JsValue` arguments [#109](https://github.com/theduke/quickjs-rs/issues/109)
|
||||||
* Enable chrono feature by default
|
* Enable chrono feature by default
|
||||||
|
|
37
Cargo.toml
37
Cargo.toml
|
@ -1,39 +1,36 @@
|
||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
name = "quick-js-dtp"
|
name = "quick-js"
|
||||||
description = "QuickJS Javascript engine wrapper (with improved date parser)"
|
description = "QuickJS Javascript engine wrapper"
|
||||||
version = "0.4.2"
|
version = "0.4.2-alpha.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/Theta-Dev/quickjs-rs"
|
documentation = "https://docs.rs/quick-js"
|
||||||
|
repository = "https://github.com/theduke/quickjs-rs"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = [
|
authors = ["Christoph Herzog <chris@theduke.at>"]
|
||||||
"Christoph Herzog <chris@theduke.at>",
|
|
||||||
"ThetaDev <t.testboy@gmail.com>",
|
|
||||||
]
|
|
||||||
keywords = ["quickjs", "javascript", "js", "engine", "interpreter"]
|
keywords = ["quickjs", "javascript", "js", "engine", "interpreter"]
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "quick_js"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["chrono", "bigint", "log"]
|
features = [ "chrono", "bigint", "log" ]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["chrono"]
|
default = ["chrono"]
|
||||||
patch-bigint = ["libquickjs-dtp-sys/patch-bigint"]
|
patched = ["libquickjs-sys/patched"]
|
||||||
patch-dateparser = ["libquickjs-dtp-sys/patch-dateparser"]
|
bigint = ["num-bigint", "num-traits", "libquickjs-sys/patched"]
|
||||||
bigint = ["num-bigint", "num-traits", "libquickjs-dtp-sys/patch-bigint"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libquickjs-dtp-sys = { version = "0.10.0", path = "./libquickjs-sys" }
|
libquickjs-sys = { version = ">= 0.9.0, < 0.10.0", path = "./libquickjs-sys" }
|
||||||
chrono = { version = "0.4.7", optional = true }
|
chrono = { version = "0.4.7", optional = true }
|
||||||
num-bigint = { version = "0.4.0", optional = true }
|
num-bigint = { version = "0.2.2", optional = true }
|
||||||
num-traits = { version = "0.2.0", optional = true }
|
num-traits = { version = "0.2.0", optional = true }
|
||||||
log = { version = "0.4.8", optional = true }
|
log = { version = "0.4.8", optional = true }
|
||||||
once_cell = "1.2.0"
|
once_cell = "1.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = { version = "0.18.0", default-features = false }
|
rstest = "0.15.0"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["libquickjs-sys"]
|
members = [
|
||||||
|
"libquickjs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
21
README.md
21
README.md
|
@ -1,8 +1,8 @@
|
||||||
# quick-js-dtp
|
# quickjs-rs
|
||||||
|
|
||||||
[![Crates.io](https://img.shields.io/crates/v/quick-js-dtp.svg?maxAge=3600)](https://crates.io/crates/quick-js-dtp)
|
[![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-dtp/badge.svg)](https://docs.rs/quick-js-dtp)
|
[![docs.rs](https://docs.rs/quick-js/badge.svg)](https://docs.rs/quick-js)
|
||||||
[![CI](https://github.com/Theta-Dev/quickjs-rs/actions/workflows/main.yml/badge.svg)](https://github.com/Theta-Dev/quickjs-rs/actions/workflows/main.yml)
|
[![Build Status](https://github.com/theduke/quickjs-rs/workflows/CI/badge.svg)
|
||||||
|
|
||||||
A Rust wrapper for [QuickJS](https://bellard.org/quickjs/).
|
A Rust wrapper for [QuickJS](https://bellard.org/quickjs/).
|
||||||
|
|
||||||
|
@ -11,15 +11,11 @@ It is fast and supports the full ES2020 specification.
|
||||||
|
|
||||||
This crate allows you to easily run and integrate with Javascript code from Rust.
|
This crate allows you to easily run and integrate with Javascript code from Rust.
|
||||||
|
|
||||||
This is a fork of the original [quick-js](https://crates.io/crates/quick-js)
|
|
||||||
crate which includes a fully featured date parser, capable of parsing dates like
|
|
||||||
`Sat, 01-Jan-2000 00:00:00 PST`.
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quick-js-dtp = "0.4.1"
|
quick-js = "0.4.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -55,13 +51,10 @@ The crate supports the following features:
|
||||||
* `log`: allows forwarding `console.log` messages to the `log` crate.
|
* `log`: allows forwarding `console.log` messages to the `log` crate.
|
||||||
Note: must be enabled with `ContextBuilder::console(quick_js::console::LogConsole);`
|
Note: must be enabled with `ContextBuilder::console(quick_js::console::LogConsole);`
|
||||||
|
|
||||||
* `patch-bigint`
|
* `patched`
|
||||||
Enabled automatically for some other features, like `bigint`.
|
Enabled automatically for some other features, like `bigint`.
|
||||||
You should not need to enable this manually.
|
You should not need to enable this manually.
|
||||||
|
Applies QuickJS patches that can be found in `libquickjs-sys/embed/patches` directory.
|
||||||
* `patch-dateparser`
|
|
||||||
Enables the improved JS date parser that supports additional date formats like
|
|
||||||
`Sat, 01-Jan-2000 00:00:00 PST`
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
|
@ -4,7 +4,7 @@ pub fn main() {
|
||||||
let context = Context::new().unwrap();
|
let context = Context::new().unwrap();
|
||||||
|
|
||||||
let value = context.eval("1 + 2").unwrap();
|
let value = context.eval("1 + 2").unwrap();
|
||||||
println!("js: 1 + 2 = {value:?}");
|
println!("js: 1 + 2 = {:?}", value);
|
||||||
|
|
||||||
context
|
context
|
||||||
.add_callback("myCallback", |a: i32, b: i32| a + b * b)
|
.add_callback("myCallback", |a: i32, b: i32| a + b * b)
|
||||||
|
@ -18,5 +18,5 @@ pub fn main() {
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("js: callback = {value:?}");
|
println!("js: callback = {:?}", value);
|
||||||
}
|
}
|
||||||
|
|
5
justfile
5
justfile
|
@ -1,6 +1,6 @@
|
||||||
embed_dir := "./libquickjs-sys/embed/quickjs"
|
embed_dir := "./libquickjs-sys/embed/quickjs"
|
||||||
|
|
||||||
DOWNLOAD_URL := "https://bellard.org/quickjs/quickjs-2024-01-13.tar.xz"
|
DOWNLOAD_URL := "https://bellard.org/quickjs/quickjs-2021-03-27.tar.xz"
|
||||||
FEATURES := "--all-features"
|
FEATURES := "--all-features"
|
||||||
|
|
||||||
download-new:
|
download-new:
|
||||||
|
@ -55,4 +55,5 @@ valgrind:
|
||||||
echo "Checking for memory leaks..."
|
echo "Checking for memory leaks..."
|
||||||
cargo clean
|
cargo clean
|
||||||
cargo build --tests --all-features
|
cargo build --tests --all-features
|
||||||
find target/debug/deps -maxdepth 1 -type f -executable -not -name "*.so" | xargs valgrind --leak-check=full --error-exitcode=1 --gen-suppressions=yes --show-error-list=yes
|
find target/debug/deps -maxdepth 1 -type f -executable | xargs valgrind --leak-check=full --error-exitcode=1 --gen-suppressions=yes --show-error-list=yes
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
name = "libquickjs-dtp-sys"
|
name = "libquickjs-sys"
|
||||||
description = "QuickJS Javascript Engine FFI bindings (with improved date parser)"
|
description = "QuickJS Javascript Engine FFI bindings"
|
||||||
version = "0.10.0"
|
version = "0.9.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/Theta-Dev/quickjs-rs"
|
documentation = "https://docs.rs/quickjs-sys"
|
||||||
|
repository = "https://github.com/theduke/quickjs-rs"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Christoph Herzog <chris@theduke.at>", "ThetaDev <t.testboy@gmail.com>"]
|
authors = ["Christoph Herzog <chris@theduke.at>"]
|
||||||
categories = ["external-ffi-bindings"]
|
categories = ["external-ffi-bindings"]
|
||||||
keywords = ["quickjs"]
|
keywords = ["quickjs"]
|
||||||
|
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "libquickjs_sys"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bundled = ["cc", "copy_dir"]
|
bundled = ["cc", "copy_dir"]
|
||||||
patch-bigint = ["bundled"]
|
patched = ["bundled"]
|
||||||
patch-dateparser = ["bundled"]
|
|
||||||
|
|
||||||
default = ["bundled"]
|
default = ["bundled"]
|
||||||
|
|
||||||
system = ["bindgen"]
|
system = ["bindgen"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = { version = "0.69.0", optional = true }
|
bindgen = { version = "0.57.0", optional = true }
|
||||||
cc = { version = "1.0.66", optional = true }
|
cc = { version = "1.0.66", optional = true }
|
||||||
copy_dir = { version = "0.1.2", optional = true }
|
copy_dir = { version = "0.1.2", optional = true }
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
# libquickjs-dtp-sys
|
# libquickjs-sys
|
||||||
|
|
||||||
FFI Bindings for [quickjs](https://bellard.org/quickjs/), a Javascript engine.
|
FFI Bindings for [quickjs](https://bellard.org/quickjs/), a Javascript engine.
|
||||||
|
|
||||||
This is a fork of the original [libquickjs-sys](https://crates.io/crates/libquickjs-sys)
|
See the [quick](https://crates.io/crates/quickjs) crate for a high-level
|
||||||
crate which includes a fully featured date parser, capable of parsing dates like
|
|
||||||
`Sat, 01-Jan-2000 00:00:00 PST`.
|
|
||||||
|
|
||||||
See the [quick-js-dtp](https://crates.io/crates/quick-js-dtp) crate for a high-level
|
|
||||||
wrapper.
|
wrapper.
|
||||||
|
|
||||||
|
|
||||||
*Version 0.10.0*
|
*Version 0.9.0*
|
||||||
**Embedded VERSION: 2024-01-13**
|
**Embedded VERSION: 2021-03-27**
|
||||||
|
|
||||||
## Embedded vs system
|
## Embedded vs system
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,8 @@ fn main() {
|
||||||
copy_dir::copy_dir(embed_path.join("quickjs"), &code_dir)
|
copy_dir::copy_dir(embed_path.join("quickjs"), &code_dir)
|
||||||
.expect("Could not copy quickjs directory");
|
.expect("Could not copy quickjs directory");
|
||||||
|
|
||||||
#[cfg(feature = "patch-bigint")]
|
#[cfg(feature = "patched")]
|
||||||
apply_patch(&code_dir, "js-tobigint64-overflow.patch");
|
apply_patches(&code_dir);
|
||||||
|
|
||||||
#[cfg(feature = "patch-dateparser")]
|
|
||||||
apply_patch(&code_dir, "dateparser.patch");
|
|
||||||
|
|
||||||
std::fs::copy(
|
std::fs::copy(
|
||||||
embed_path.join("static-functions.c"),
|
embed_path.join("static-functions.c"),
|
||||||
|
@ -118,25 +115,27 @@ fn main() {
|
||||||
.expect("Could not copy bindings.rs");
|
.expect("Could not copy bindings.rs");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "patch-bigint", feature = "patch-dateparser"))]
|
#[cfg(feature = "patched")]
|
||||||
fn apply_patch(code_dir: &PathBuf, name: &str) {
|
fn apply_patches(code_dir: &PathBuf) {
|
||||||
eprintln!("Applying {name}");
|
use std::fs;
|
||||||
let patch_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
|
|
||||||
.join("embed")
|
|
||||||
.join("patches")
|
|
||||||
.join(name);
|
|
||||||
assert!(patch_path.exists(), "Could not open patch {name}");
|
|
||||||
|
|
||||||
let status = std::process::Command::new("patch")
|
eprintln!("Applying patches...");
|
||||||
.current_dir(code_dir)
|
let embed_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("embed");
|
||||||
.arg("-i")
|
let patches_path = embed_path.join("patches");
|
||||||
.arg(&patch_path)
|
for patch in fs::read_dir(patches_path).expect("Could not open patches directory") {
|
||||||
.spawn()
|
let patch = patch.expect("Could not open patch");
|
||||||
.expect("Could not apply patches")
|
eprintln!("Applying {:?}...", patch.file_name());
|
||||||
.wait()
|
let status = std::process::Command::new("patch")
|
||||||
.expect("Could not apply patches");
|
.current_dir(&code_dir)
|
||||||
assert!(
|
.arg("-i")
|
||||||
status.success(),
|
.arg(patch.path())
|
||||||
"Patch command returned non-zero exit code"
|
.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
|
@ -1,543 +0,0 @@
|
||||||
diff --git a/libquickjs-sys/embed/quickjs/quickjs.c b/libquickjs-sys/embed/quickjs/quickjs.c
|
|
||||||
index 4e58a98..93cf853 100644
|
|
||||||
--- a/libquickjs-sys/embed/quickjs/quickjs.c
|
|
||||||
+++ b/libquickjs-sys/embed/quickjs/quickjs.c
|
|
||||||
@@ -1459,6 +1459,10 @@ static inline int is_digit(int c) {
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
+static inline int is_space_like(char c) {
|
|
||||||
+ return c == ' ' || c == ',' || c == ':' || c == '-' || c == '/';
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
typedef struct JSClassShortDef {
|
|
||||||
JSAtom class_name;
|
|
||||||
JSClassFinalizer *finalizer;
|
|
||||||
@@ -49343,6 +49347,23 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
||||||
static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
|
||||||
static char const day_names[] = "SunMonTueWedThuFriSat";
|
|
||||||
|
|
||||||
+static const struct KnownZone {
|
|
||||||
+ char tzName[4];
|
|
||||||
+ int tzOffset;
|
|
||||||
+} known_zones[] = {
|
|
||||||
+ { "UTC", 0 },
|
|
||||||
+ { "UT", 0 },
|
|
||||||
+ { "GMT", 0 },
|
|
||||||
+ { "EST", -300 },
|
|
||||||
+ { "EDT", -240 },
|
|
||||||
+ { "CST", -360 },
|
|
||||||
+ { "CDT", -300 },
|
|
||||||
+ { "MST", -420 },
|
|
||||||
+ { "MDT", -360 },
|
|
||||||
+ { "PST", -480 },
|
|
||||||
+ { "PDT", -420 }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
|
|
||||||
double fields[9], int is_local, int force)
|
|
||||||
{
|
|
||||||
@@ -49707,14 +49728,44 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
|
|
||||||
return JS_NewFloat64(ctx, set_date_fields(fields, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
-static void string_skip_spaces(JSString *sp, int *pp) {
|
|
||||||
- while (*pp < sp->len && string_get(sp, *pp) == ' ')
|
|
||||||
+static void string_skip_spaces_and_comments(JSString *sp, int *pp) {
|
|
||||||
+ int nesting = 0;
|
|
||||||
+ while (*pp < sp->len) {
|
|
||||||
+ char ch = string_get(sp, *pp);
|
|
||||||
+ int nxt = *pp + 1;
|
|
||||||
+
|
|
||||||
+ // interpret - before a number as a sign rather than a comment char
|
|
||||||
+ if (ch == '-' && nesting == 0 && nxt < sp->len && is_digit(string_get(sp, nxt))) {
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ if (!is_space_like(ch)) {
|
|
||||||
+ if (ch == '(') {
|
|
||||||
+ nesting++;
|
|
||||||
+ } else if (ch == ')' && nesting > 0) {
|
|
||||||
+ nesting--;
|
|
||||||
+ } else if (nesting == 0) {
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
*pp += 1;
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
-static void string_skip_non_spaces(JSString *sp, int *pp) {
|
|
||||||
- while (*pp < sp->len && string_get(sp, *pp) != ' ')
|
|
||||||
- *pp += 1;
|
|
||||||
+static inline BOOL char_eq_ignorecase(char c1, char c2) {
|
|
||||||
+ if ((c1 >= 'A' && c1 <= 'Z') || (c1 >= 'a' && c1 <= 'z'))
|
|
||||||
+ return (c1 | 0x20) == (c2 | 0x20);
|
|
||||||
+ return c1 == c2;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static BOOL string_eq_ignorecase(JSString *s1, int p, const char *s2, int len) {
|
|
||||||
+ if (s1->len - p < len) return FALSE;
|
|
||||||
+
|
|
||||||
+ for (int i=0; i<len; i++) {
|
|
||||||
+ if (!char_eq_ignorecase(string_get(s1, p + i), s2[i]))
|
|
||||||
+ return FALSE;
|
|
||||||
+ }
|
|
||||||
+ return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse a numeric field with an optional sign if accept_sign is TRUE */
|
|
||||||
@@ -49752,11 +49803,7 @@ static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) {
|
|
||||||
p++;
|
|
||||||
|
|
||||||
res = string_get_digits(sp, &p, pval);
|
|
||||||
- if (res == 0 && sgn == '-') {
|
|
||||||
- if (*pval == 0)
|
|
||||||
- return -1; // reject negative zero
|
|
||||||
- *pval = -*pval;
|
|
||||||
- }
|
|
||||||
+ if (res == 0 && sgn == '-') *pval = -*pval;
|
|
||||||
*pp = p;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@@ -49805,6 +49852,34 @@ static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int string_get_num_timezone(JSString *sp, int *pp, int64_t *pval)
|
|
||||||
+{
|
|
||||||
+ int p = *pp;
|
|
||||||
+
|
|
||||||
+ int64_t o;
|
|
||||||
+ if (string_get_signed_digits(sp, &p, &o))
|
|
||||||
+ return -1;
|
|
||||||
+
|
|
||||||
+ if (o < -9959 || o > 9959) {
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ int sgn = (o < 0) ? -1 : 1;
|
|
||||||
+ o = abs((int32_t) o);
|
|
||||||
+
|
|
||||||
+ if (string_get(sp, p) != ':') {
|
|
||||||
+ *pval = ((o / 100) * 60 + (o % 100)) * sgn;
|
|
||||||
+ } else {
|
|
||||||
+ p++;
|
|
||||||
+ int64_t o2;
|
|
||||||
+ if (string_get_digits(sp, &p, &o2))
|
|
||||||
+ return -1;
|
|
||||||
+ *pval = (o * 60 + o2) * sgn;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ *pp = p;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
|
|
||||||
static int find_abbrev(JSString *sp, int p, const char *list, int count) {
|
|
||||||
int n, i;
|
|
||||||
@@ -49822,27 +49897,30 @@ static int find_abbrev(JSString *sp, int p, const char *list, int count) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static int string_get_month(JSString *sp, int *pp, int64_t *pval) {
|
|
||||||
- int n;
|
|
||||||
-
|
|
||||||
- string_skip_spaces(sp, pp);
|
|
||||||
- n = find_abbrev(sp, *pp, month_names, 12);
|
|
||||||
- if (n < 0)
|
|
||||||
- return -1;
|
|
||||||
-
|
|
||||||
- *pval = n;
|
|
||||||
- *pp += 3;
|
|
||||||
- return 0;
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
|
|
||||||
int argc, JSValueConst *argv)
|
|
||||||
{
|
|
||||||
- // parse(s)
|
|
||||||
+ // This parses a date in the form:
|
|
||||||
+ // Tuesday, 09-Nov-99 23:12:40 GMT
|
|
||||||
+ // or
|
|
||||||
+ // Sat, 01-Jan-2000 08:00:00 GMT
|
|
||||||
+ // or
|
|
||||||
+ // Sat, 01 Jan 2000 08:00:00 GMT
|
|
||||||
+ // or
|
|
||||||
+ // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
|
|
||||||
+ // ### non RFC formats, added for Javascript:
|
|
||||||
+ // [Wednesday] January 09 1999 23:12:40 GMT
|
|
||||||
+ // [Wednesday] January 09 23:12:40 GMT 1999
|
|
||||||
+ //
|
|
||||||
+ // We ignore the weekday.
|
|
||||||
+ //
|
|
||||||
+ // Date parser adapted from KJS' implementation
|
|
||||||
+ // Source: https://invent.kde.org/frameworks/kjs/-/blob/fd9252ec4b270ebb4a299129099bce97f00dac0e/src/kjs/date_object.cpp#L1153
|
|
||||||
+
|
|
||||||
JSValue s, rv;
|
|
||||||
int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };
|
|
||||||
double fields1[7];
|
|
||||||
- int64_t tz, hh, mm;
|
|
||||||
+ int64_t tz = 0, hh, mm;
|
|
||||||
double d;
|
|
||||||
int p, i, c, sgn, l;
|
|
||||||
JSString *sp;
|
|
||||||
@@ -49856,7 +49934,18 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
|
|
||||||
|
|
||||||
sp = JS_VALUE_GET_STRING(s);
|
|
||||||
p = 0;
|
|
||||||
- if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+
|
|
||||||
+ int end_of_digits = p;
|
|
||||||
+ if (string_get(sp, end_of_digits) == '+' || string_get(sp, end_of_digits) == '-') {
|
|
||||||
+ p++;
|
|
||||||
+ }
|
|
||||||
+ while (end_of_digits < sp->len && is_digit(string_get(sp, end_of_digits))) {
|
|
||||||
+ end_of_digits++;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if ((end_of_digits - p) > 0 &&
|
|
||||||
+ (string_get(sp, end_of_digits) == '-' || string_get(sp, end_of_digits) == 'T')) {
|
|
||||||
/* ISO format */
|
|
||||||
/* year field can be negative */
|
|
||||||
if (string_get_signed_digits(sp, &p, &fields[0]))
|
|
||||||
@@ -49866,23 +49955,28 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
|
|
||||||
if (p >= sp->len)
|
|
||||||
break;
|
|
||||||
switch(i) {
|
|
||||||
- case 1:
|
|
||||||
- case 2:
|
|
||||||
+ case 1: // Year
|
|
||||||
+ case 2: // Month
|
|
||||||
c = '-';
|
|
||||||
break;
|
|
||||||
- case 3:
|
|
||||||
+ case 3: // Day
|
|
||||||
c = 'T';
|
|
||||||
break;
|
|
||||||
- case 4:
|
|
||||||
- case 5:
|
|
||||||
+ case 4: // Hour
|
|
||||||
+ case 5: // Minute
|
|
||||||
c = ':';
|
|
||||||
break;
|
|
||||||
- case 6:
|
|
||||||
+ case 6: // Second
|
|
||||||
c = '.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
- if (string_get(sp, p) != c)
|
|
||||||
- break;
|
|
||||||
+ if (string_get(sp, p) != c) {
|
|
||||||
+ // 2000T08:00Z
|
|
||||||
+ if (i < 3 && string_get(sp, p) == 'T') {
|
|
||||||
+ i = 3;
|
|
||||||
+ }
|
|
||||||
+ else break;
|
|
||||||
+ }
|
|
||||||
p++;
|
|
||||||
if (i == 6) {
|
|
||||||
if (string_get_milliseconds(sp, &p, &fields[i]))
|
|
||||||
@@ -49892,6 +49986,9 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+ // Hour only is invalid
|
|
||||||
+ if (i == 4) goto done;
|
|
||||||
+
|
|
||||||
/* no time: UTC by default */
|
|
||||||
is_local = (i > 3);
|
|
||||||
fields[1] -= 1;
|
|
||||||
@@ -49929,68 +50026,250 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
- /* toString or toUTCString format */
|
|
||||||
- /* skip the day of the week */
|
|
||||||
- string_skip_non_spaces(sp, &p);
|
|
||||||
- string_skip_spaces(sp, &p);
|
|
||||||
- if (p >= sp->len)
|
|
||||||
+ // Check contents of first words if not number
|
|
||||||
+ int64_t month = -1;
|
|
||||||
+ int word_start = p;
|
|
||||||
+ char c = string_get(sp, p);
|
|
||||||
+
|
|
||||||
+ while (!is_digit(c)) {
|
|
||||||
+ if (c == ' ' || c == '(') {
|
|
||||||
+ if (p - word_start >= 3) {
|
|
||||||
+ month = find_abbrev(sp, word_start, month_names, 12);
|
|
||||||
+ }
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+ word_start = p;
|
|
||||||
+ } else {
|
|
||||||
+ p++;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ c = string_get(sp, p);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Missing delimiter between month and day (like "January29")?
|
|
||||||
+ if (month == -1 && word_start != p) {
|
|
||||||
+ month = find_abbrev(sp, word_start, month_names, 12);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+ if (sp->len <= p)
|
|
||||||
goto done;
|
|
||||||
- c = string_get(sp, p);
|
|
||||||
- if (c >= '0' && c <= '9') {
|
|
||||||
- /* day of month first */
|
|
||||||
- if (string_get_digits(sp, &p, &fields[2]))
|
|
||||||
+
|
|
||||||
+ // '09-Nov-99 23:12:40 GMT'
|
|
||||||
+ int64_t day = 0;
|
|
||||||
+ if (string_get_digits(sp, &p, &day))
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
+ int64_t year = -1;
|
|
||||||
+ if (day > 31) {
|
|
||||||
+ if (string_get(sp, p) != '/')
|
|
||||||
goto done;
|
|
||||||
- if (string_get_month(sp, &p, &fields[1]))
|
|
||||||
+ // looks like a YYYY/MM/DD date
|
|
||||||
+ p++;
|
|
||||||
+ if (sp->len <= p)
|
|
||||||
goto done;
|
|
||||||
- } else {
|
|
||||||
- /* month first */
|
|
||||||
- if (string_get_month(sp, &p, &fields[1]))
|
|
||||||
+
|
|
||||||
+ year = day;
|
|
||||||
+ if (string_get_digits(sp, &p, &month))
|
|
||||||
goto done;
|
|
||||||
- string_skip_spaces(sp, &p);
|
|
||||||
- if (string_get_digits(sp, &p, &fields[2]))
|
|
||||||
+ month--;
|
|
||||||
+
|
|
||||||
+ if (string_get(sp, p) != '/')
|
|
||||||
goto done;
|
|
||||||
- }
|
|
||||||
- /* year */
|
|
||||||
- string_skip_spaces(sp, &p);
|
|
||||||
- if (string_get_signed_digits(sp, &p, &fields[0]))
|
|
||||||
- goto done;
|
|
||||||
+ p++;
|
|
||||||
+ if (sp->len <= p)
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
+ if (string_get_digits(sp, &p, &day))
|
|
||||||
+ goto done;
|
|
||||||
+ } else if (string_get(sp, p) == '/' && month == -1) {
|
|
||||||
+ p++;
|
|
||||||
+ // This looks like a MM/DD/YYYY date, not an RFC date.
|
|
||||||
+ month = day - 1; // 0-based
|
|
||||||
+ if (string_get_digits(sp, &p, &day))
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
+ if (!is_space_like(string_get(sp, p)))
|
|
||||||
+ goto done;
|
|
||||||
+ p++;
|
|
||||||
+ if (sp->len <= p)
|
|
||||||
+ goto done;
|
|
||||||
+ } else {
|
|
||||||
+ if (string_get(sp, p) == '-') {
|
|
||||||
+ p++;
|
|
||||||
+ }
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
|
|
||||||
- /* hour, min, seconds */
|
|
||||||
- string_skip_spaces(sp, &p);
|
|
||||||
- for(i = 0; i < 3; i++) {
|
|
||||||
- if (i == 1 || i == 2) {
|
|
||||||
- if (p >= sp->len)
|
|
||||||
+ // Jan,2000,08:00:00 UT
|
|
||||||
+ if (month == -1) {
|
|
||||||
+ month = find_abbrev(sp, p, month_names, 12);
|
|
||||||
+ if (month == -1)
|
|
||||||
goto done;
|
|
||||||
- if (string_get(sp, p) != ':')
|
|
||||||
+
|
|
||||||
+ while (p < sp->len && !is_space_like(string_get(sp, p))) {
|
|
||||||
+ p++;
|
|
||||||
+ }
|
|
||||||
+ if (sp->len <= p)
|
|
||||||
goto done;
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
- if (string_get_digits(sp, &p, &fields[3 + i]))
|
|
||||||
- goto done;
|
|
||||||
}
|
|
||||||
- // XXX: parse optional milliseconds?
|
|
||||||
|
|
||||||
- /* parse the time zone offset if present: [+-]HHmm */
|
|
||||||
- is_local = FALSE;
|
|
||||||
- tz = 0;
|
|
||||||
- for (tz = 0; p < sp->len; p++) {
|
|
||||||
- sgn = string_get(sp, p);
|
|
||||||
- if (sgn == '+' || sgn == '-') {
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+
|
|
||||||
+ if (year < 0) {
|
|
||||||
+ // Year following, e.g. 01 Jan 2000 08:00:00 UT
|
|
||||||
+ // Time following, e.g. Jan 01 08:00:00 UT 2000
|
|
||||||
+ if (sp->len <= p + 2 || string_get(sp, p + 2) != ':') {
|
|
||||||
+ if (string_get_digits(sp, &p, &year))
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Don't fail if the time is missing.
|
|
||||||
+ int64_t hour = 0;
|
|
||||||
+ int64_t minute = 0;
|
|
||||||
+ int64_t second = 0;
|
|
||||||
+
|
|
||||||
+ if (sp->len > p) {
|
|
||||||
+ // ' 23:12:40 GMT'
|
|
||||||
+ if (string_get(sp, p) == ':') {
|
|
||||||
+ // There was no year; the number was the hour.
|
|
||||||
+ year = -1;
|
|
||||||
+ } else if (is_space_like(string_get(sp, p))) {
|
|
||||||
p++;
|
|
||||||
- if (string_get_fixed_width_digits(sp, &p, 2, &hh))
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Read a number? If not, this might be a timezone name.
|
|
||||||
+ if (!string_get_digits(sp, &p, &hour)) {
|
|
||||||
+ if (hour < 0 || hour > 23) {
|
|
||||||
goto done;
|
|
||||||
- if (string_get_fixed_width_digits(sp, &p, 2, &mm))
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (sp->len <= p)
|
|
||||||
goto done;
|
|
||||||
- tz = hh * 60 + mm;
|
|
||||||
- if (sgn == '-')
|
|
||||||
- tz = -tz;
|
|
||||||
- break;
|
|
||||||
+
|
|
||||||
+ // ':12:40 GMT'
|
|
||||||
+ if (string_get(sp, p) != ':') {
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+ p++;
|
|
||||||
+
|
|
||||||
+ if (string_get_digits(sp, &p, &minute))
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
+ // ':40 GMT'
|
|
||||||
+ // seconds are optional in rfc822 + rfc2822
|
|
||||||
+ if (string_get(sp, p) == ':') {
|
|
||||||
+ p++;
|
|
||||||
+
|
|
||||||
+ if (string_get_digits(sp, &p, &second))
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
+ // disallow trailing colon seconds
|
|
||||||
+ if (string_get(sp, p) == ':') {
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+ } else if (string_get(sp, p) != ' ') {
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+
|
|
||||||
+ if (string_eq_ignorecase(sp, p, "AM", 2)) {
|
|
||||||
+ if (hour > 12) {
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+ if (hour == 12) {
|
|
||||||
+ hour = 0;
|
|
||||||
+ }
|
|
||||||
+ p += 2;
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+ } else if (string_eq_ignorecase(sp, p, "PM", 2)) {
|
|
||||||
+ if (hour > 12) {
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+ if (hour != 12) {
|
|
||||||
+ hour += 12;
|
|
||||||
+ }
|
|
||||||
+ p += 2;
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ // Don't fail if the time zone is missing.
|
|
||||||
+ // Some websites omit the time zone
|
|
||||||
+ if (sp->len > p) {
|
|
||||||
+ if (string_get(sp, p) == '+' || string_get(sp, p) == '-') {
|
|
||||||
+ if (string_get_num_timezone(sp, &p, &tz))
|
|
||||||
+ goto done;
|
|
||||||
+ is_local = FALSE;
|
|
||||||
+ } else {
|
|
||||||
+ for (int i = 0; i < sizeof(known_zones) / sizeof(struct KnownZone); i++) {
|
|
||||||
+ if (string_eq_ignorecase(sp, p, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
|
|
||||||
+ tz = known_zones[i].tzOffset;
|
|
||||||
+ p += strlen(known_zones[i].tzName);
|
|
||||||
+ is_local = FALSE;
|
|
||||||
+
|
|
||||||
+ // TZ offset (GMT+0)
|
|
||||||
+ if (string_get(sp, p) == '+' || string_get(sp, p) == '-') {
|
|
||||||
+ int64_t o;
|
|
||||||
+ if (string_get_num_timezone(sp, &p, &o))
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
+ tz += o;
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+
|
|
||||||
+ if (sp->len > p && year == -1) {
|
|
||||||
+ if (string_get_digits(sp, &p, &year))
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ string_skip_spaces_and_comments(sp, &p);
|
|
||||||
+
|
|
||||||
+ // Trailing garbage
|
|
||||||
+ if (sp->len > p) {
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // Y2K: Handle 2 digit years.
|
|
||||||
+ if (year >= 0 && year < 100) {
|
|
||||||
+ if (year < 50) {
|
|
||||||
+ year += 2000;
|
|
||||||
+ } else {
|
|
||||||
+ year += 1900;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ fields[0] = year;
|
|
||||||
+ fields[1] = month;
|
|
||||||
+ fields[2] = day;
|
|
||||||
+ fields[3] = hour;
|
|
||||||
+ fields[4] = minute;
|
|
||||||
+ fields[5] = second;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ // Validate fields
|
|
||||||
+ if (fields[1] < 0 || fields[1] > 11 ||
|
|
||||||
+ fields[2] < 1 || fields[2] > 31 ||
|
|
||||||
+ fields[3] < 0 || fields[3] > 24 ||
|
|
||||||
+ fields[4] < 0 || fields[4] > 59 ||
|
|
||||||
+ fields[5] < 0 || fields[5] > 59 ||
|
|
||||||
+ fields[6] < 0 || fields[6] > 999 ||
|
|
||||||
+ fields[3] * 3600 * 1000 + fields[4] * 60000 + fields[5] * 1000 + fields[6] > 24 * 3600 * 1000
|
|
||||||
+ ) goto done;
|
|
||||||
+
|
|
||||||
for(i = 0; i < 7; i++)
|
|
||||||
fields1[i] = fields[i];
|
|
||||||
- d = set_date_fields(fields1, is_local) - tz * 60000;
|
|
||||||
+ d = set_date_fields(fields1, is_local) - (tz * 60000);
|
|
||||||
rv = JS_NewFloat64(ctx, d);
|
|
||||||
|
|
||||||
done:
|
|
|
@ -1,30 +1,3 @@
|
||||||
2024-01-13:
|
|
||||||
|
|
||||||
- top-level-await support in modules
|
|
||||||
- allow 'await' in the REPL
|
|
||||||
- added Array.prototype.{with,toReversed,toSpliced,toSorted} and
|
|
||||||
TypedArray.prototype.{with,toReversed,toSorted}
|
|
||||||
- added String.prototype.isWellFormed and String.prototype.toWellFormed
|
|
||||||
- added Object.groupBy and Map.groupBy
|
|
||||||
- added Promise.withResolvers
|
|
||||||
- class static block
|
|
||||||
- 'in' operator support for private fields
|
|
||||||
- optional chaining fixes
|
|
||||||
- added RegExp 'd' flag
|
|
||||||
- fixed RegExp zero length match logic
|
|
||||||
- fixed RegExp case insensitive flag
|
|
||||||
- added os.getpid() and os.now()
|
|
||||||
- added cosmopolitan build
|
|
||||||
- misc bug fixes
|
|
||||||
|
|
||||||
2023-12-09:
|
|
||||||
|
|
||||||
- added Object.hasOwn, {String|Array|TypedArray}.prototype.at,
|
|
||||||
{Array|TypedArray}.prototype.findLast{Index}
|
|
||||||
- BigInt support is enabled even if CONFIG_BIGNUM disabled
|
|
||||||
- updated to Unicode 15.0.0
|
|
||||||
- misc bug fixes
|
|
||||||
|
|
||||||
2021-03-27:
|
2021-03-27:
|
||||||
|
|
||||||
- faster Array.prototype.push and Array.prototype.unshift
|
- faster Array.prototype.push and Array.prototype.unshift
|
||||||
|
|
|
@ -33,20 +33,6 @@ CONFIG_LTO=y
|
||||||
#CONFIG_WERROR=y
|
#CONFIG_WERROR=y
|
||||||
# force 32 bit build for some utilities
|
# force 32 bit build for some utilities
|
||||||
#CONFIG_M32=y
|
#CONFIG_M32=y
|
||||||
# cosmopolitan build (see https://github.com/jart/cosmopolitan)
|
|
||||||
#CONFIG_COSMO=y
|
|
||||||
|
|
||||||
# installation directory
|
|
||||||
PREFIX?=/usr/local
|
|
||||||
|
|
||||||
# use the gprof profiler
|
|
||||||
#CONFIG_PROFILE=y
|
|
||||||
# use address sanitizer
|
|
||||||
#CONFIG_ASAN=y
|
|
||||||
# include the code for BigFloat/BigDecimal, math mode and faster large integers
|
|
||||||
CONFIG_BIGNUM=y
|
|
||||||
|
|
||||||
OBJDIR=.obj
|
|
||||||
|
|
||||||
ifdef CONFIG_DARWIN
|
ifdef CONFIG_DARWIN
|
||||||
# use clang instead of gcc
|
# use clang instead of gcc
|
||||||
|
@ -54,22 +40,33 @@ CONFIG_CLANG=y
|
||||||
CONFIG_DEFAULT_AR=y
|
CONFIG_DEFAULT_AR=y
|
||||||
endif
|
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
|
ifdef CONFIG_WIN32
|
||||||
ifdef CONFIG_M32
|
ifdef CONFIG_M32
|
||||||
CROSS_PREFIX?=i686-w64-mingw32-
|
CROSS_PREFIX=i686-w64-mingw32-
|
||||||
else
|
else
|
||||||
CROSS_PREFIX?=x86_64-w64-mingw32-
|
CROSS_PREFIX=x86_64-w64-mingw32-
|
||||||
endif
|
endif
|
||||||
EXE=.exe
|
EXE=.exe
|
||||||
else
|
else
|
||||||
CROSS_PREFIX?=
|
CROSS_PREFIX=
|
||||||
EXE=
|
EXE=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_CLANG
|
ifdef CONFIG_CLANG
|
||||||
HOST_CC=clang
|
HOST_CC=clang
|
||||||
CC=$(CROSS_PREFIX)clang
|
CC=$(CROSS_PREFIX)clang
|
||||||
CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
|
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
|
||||||
CFLAGS += -Wextra
|
CFLAGS += -Wextra
|
||||||
CFLAGS += -Wno-sign-compare
|
CFLAGS += -Wno-sign-compare
|
||||||
CFLAGS += -Wno-missing-field-initializers
|
CFLAGS += -Wno-missing-field-initializers
|
||||||
|
@ -87,18 +84,10 @@ ifdef CONFIG_CLANG
|
||||||
AR=$(CROSS_PREFIX)ar
|
AR=$(CROSS_PREFIX)ar
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
else ifdef CONFIG_COSMO
|
|
||||||
CONFIG_LTO=
|
|
||||||
HOST_CC=gcc
|
|
||||||
CC=cosmocc
|
|
||||||
# cosmocc does not correct support -MF
|
|
||||||
CFLAGS=-g -Wall #-MMD -MF $(OBJDIR)/$(@F).d
|
|
||||||
CFLAGS += -Wno-array-bounds -Wno-format-truncation
|
|
||||||
AR=cosmoar
|
|
||||||
else
|
else
|
||||||
HOST_CC=gcc
|
HOST_CC=gcc
|
||||||
CC=$(CROSS_PREFIX)gcc
|
CC=$(CROSS_PREFIX)gcc
|
||||||
CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
|
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
|
||||||
CFLAGS += -Wno-array-bounds -Wno-format-truncation
|
CFLAGS += -Wno-array-bounds -Wno-format-truncation
|
||||||
ifdef CONFIG_LTO
|
ifdef CONFIG_LTO
|
||||||
AR=$(CROSS_PREFIX)gcc-ar
|
AR=$(CROSS_PREFIX)gcc-ar
|
||||||
|
@ -107,7 +96,6 @@ else
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
STRIP=$(CROSS_PREFIX)strip
|
STRIP=$(CROSS_PREFIX)strip
|
||||||
CFLAGS+=-fwrapv # ensure that signed overflows behave as expected
|
|
||||||
ifdef CONFIG_WERROR
|
ifdef CONFIG_WERROR
|
||||||
CFLAGS+=-Werror
|
CFLAGS+=-Werror
|
||||||
endif
|
endif
|
||||||
|
@ -124,11 +112,7 @@ CFLAGS_DEBUG=$(CFLAGS) -O0
|
||||||
CFLAGS_SMALL=$(CFLAGS) -Os
|
CFLAGS_SMALL=$(CFLAGS) -Os
|
||||||
CFLAGS_OPT=$(CFLAGS) -O2
|
CFLAGS_OPT=$(CFLAGS) -O2
|
||||||
CFLAGS_NOLTO:=$(CFLAGS_OPT)
|
CFLAGS_NOLTO:=$(CFLAGS_OPT)
|
||||||
ifdef CONFIG_COSMO
|
LDFLAGS=-g
|
||||||
LDFLAGS+=-s # better to strip by default
|
|
||||||
else
|
|
||||||
LDFLAGS+=-g
|
|
||||||
endif
|
|
||||||
ifdef CONFIG_LTO
|
ifdef CONFIG_LTO
|
||||||
CFLAGS_SMALL+=-flto
|
CFLAGS_SMALL+=-flto
|
||||||
CFLAGS_OPT+=-flto
|
CFLAGS_OPT+=-flto
|
||||||
|
@ -148,12 +132,6 @@ else
|
||||||
LDEXPORT=-rdynamic
|
LDEXPORT=-rdynamic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef CONFIG_COSMO
|
|
||||||
ifndef CONFIG_DARWIN
|
|
||||||
CONFIG_SHARED_LIBS=y # building shared libraries is supported
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
PROGS=qjs$(EXE) qjsc$(EXE) run-test262
|
PROGS=qjs$(EXE) qjsc$(EXE) run-test262
|
||||||
ifneq ($(CROSS_PREFIX),)
|
ifneq ($(CROSS_PREFIX),)
|
||||||
QJSC_CC=gcc
|
QJSC_CC=gcc
|
||||||
|
@ -176,21 +154,23 @@ endif
|
||||||
|
|
||||||
# examples
|
# examples
|
||||||
ifeq ($(CROSS_PREFIX),)
|
ifeq ($(CROSS_PREFIX),)
|
||||||
PROGS+=examples/hello
|
ifdef CONFIG_ASAN
|
||||||
ifndef CONFIG_ASAN
|
PROGS+=
|
||||||
PROGS+=examples/hello_module
|
else
|
||||||
|
PROGS+=examples/hello examples/hello_module examples/test_fib
|
||||||
|
ifndef CONFIG_DARWIN
|
||||||
|
PROGS+=examples/fib.so examples/point.so
|
||||||
endif
|
endif
|
||||||
ifdef CONFIG_SHARED_LIBS
|
|
||||||
PROGS+=examples/test_fib examples/fib.so examples/point.so
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)
|
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 $(OBJDIR)/libbf.o
|
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)
|
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
|
||||||
ifdef CONFIG_BIGNUM
|
ifdef CONFIG_BIGNUM
|
||||||
|
QJS_LIB_OBJS+=$(OBJDIR)/libbf.o
|
||||||
QJS_OBJS+=$(OBJDIR)/qjscalc.o
|
QJS_OBJS+=$(OBJDIR)/qjscalc.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -221,11 +201,11 @@ $(QJSC): $(OBJDIR)/qjsc.host.o \
|
||||||
|
|
||||||
endif #CROSS_PREFIX
|
endif #CROSS_PREFIX
|
||||||
|
|
||||||
QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\"
|
QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
|
||||||
ifdef CONFIG_LTO
|
ifdef CONFIG_LTO
|
||||||
QJSC_DEFINES+=-DCONFIG_LTO
|
QJSC_DEFINES+=-DCONFIG_LTO
|
||||||
endif
|
endif
|
||||||
QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\"
|
QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
|
||||||
|
|
||||||
$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES)
|
$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES)
|
||||||
$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES)
|
$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES)
|
||||||
|
@ -318,17 +298,17 @@ clean:
|
||||||
rm -rf run-test262-debug run-test262-32
|
rm -rf run-test262-debug run-test262-32
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
mkdir -p "$(DESTDIR)$(PREFIX)/bin"
|
mkdir -p "$(DESTDIR)$(prefix)/bin"
|
||||||
$(STRIP) qjs qjsc
|
$(STRIP) qjs qjsc
|
||||||
install -m755 qjs qjsc "$(DESTDIR)$(PREFIX)/bin"
|
install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin"
|
||||||
ln -sf qjs "$(DESTDIR)$(PREFIX)/bin/qjscalc"
|
ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc"
|
||||||
mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs"
|
mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs"
|
||||||
install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
|
install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs"
|
||||||
ifdef CONFIG_LTO
|
ifdef CONFIG_LTO
|
||||||
install -m644 libquickjs.lto.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
|
install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs"
|
||||||
endif
|
endif
|
||||||
mkdir -p "$(DESTDIR)$(PREFIX)/include/quickjs"
|
mkdir -p "$(DESTDIR)$(prefix)/include/quickjs"
|
||||||
install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(PREFIX)/include/quickjs"
|
install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(prefix)/include/quickjs"
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# examples
|
# examples
|
||||||
|
@ -337,7 +317,10 @@ endif
|
||||||
HELLO_SRCS=examples/hello.js
|
HELLO_SRCS=examples/hello.js
|
||||||
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
|
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
|
||||||
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
|
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
|
||||||
-fno-date -fno-module-loader -fno-bigint
|
-fno-date -fno-module-loader
|
||||||
|
ifdef CONFIG_BIGNUM
|
||||||
|
HELLO_OPTS+=-fno-bigint
|
||||||
|
endif
|
||||||
|
|
||||||
hello.c: $(QJSC) $(HELLO_SRCS)
|
hello.c: $(QJSC) $(HELLO_SRCS)
|
||||||
$(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
|
$(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
|
||||||
|
@ -394,7 +377,7 @@ doc/%.html: doc/%.html.pre
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# tests
|
# tests
|
||||||
|
|
||||||
ifdef CONFIG_SHARED_LIBS
|
ifndef CONFIG_DARWIN
|
||||||
test: tests/bjson.so examples/point.so
|
test: tests/bjson.so examples/point.so
|
||||||
endif
|
endif
|
||||||
ifdef CONFIG_M32
|
ifdef CONFIG_M32
|
||||||
|
@ -408,7 +391,7 @@ test: qjs
|
||||||
./qjs tests/test_loop.js
|
./qjs tests/test_loop.js
|
||||||
./qjs tests/test_std.js
|
./qjs tests/test_std.js
|
||||||
./qjs tests/test_worker.js
|
./qjs tests/test_worker.js
|
||||||
ifdef CONFIG_SHARED_LIBS
|
ifndef CONFIG_DARWIN
|
||||||
ifdef CONFIG_BIGNUM
|
ifdef CONFIG_BIGNUM
|
||||||
./qjs --bignum tests/test_bjson.js
|
./qjs --bignum tests/test_bjson.js
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
Bugs:
|
||||||
|
- modules: better error handling with cyclic module references
|
||||||
|
|
||||||
Misc ideas:
|
Misc ideas:
|
||||||
- use custom printf to avoid compatibility issues with floating point numbers
|
- use custom printf to avoid compatibility issues with floating point numbers
|
||||||
- consistent naming for preprocessor defines
|
- consistent naming for preprocessor defines
|
||||||
|
@ -63,5 +66,5 @@ Optimization ideas:
|
||||||
Test262o: 0/11262 errors, 463 excluded
|
Test262o: 0/11262 errors, 463 excluded
|
||||||
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
|
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
|
||||||
|
|
||||||
Result: 10/76947 errors, 1497 excluded, 8117 skipped
|
Result: 35/75280 errors, 909 excluded, 585 skipped
|
||||||
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb
|
Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
2024-01-13
|
2021-03-27
|
||||||
|
|
|
@ -49,9 +49,6 @@
|
||||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */
|
|
||||||
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
|
|
||||||
|
|
||||||
typedef int BOOL;
|
typedef int BOOL;
|
||||||
|
|
||||||
#ifndef FALSE
|
#ifndef FALSE
|
||||||
|
|
|
@ -37,12 +37,10 @@
|
||||||
|
|
||||||
/* enable it to check the multiplication result */
|
/* enable it to check the multiplication result */
|
||||||
//#define USE_MUL_CHECK
|
//#define USE_MUL_CHECK
|
||||||
#ifdef CONFIG_BIGNUM
|
|
||||||
/* enable it to use FFT/NTT multiplication */
|
/* enable it to use FFT/NTT multiplication */
|
||||||
#define USE_FFT_MUL
|
#define USE_FFT_MUL
|
||||||
/* enable decimal floating point support */
|
/* enable decimal floating point support */
|
||||||
#define USE_BF_DEC
|
#define USE_BF_DEC
|
||||||
#endif
|
|
||||||
|
|
||||||
//#define inline __attribute__((always_inline))
|
//#define inline __attribute__((always_inline))
|
||||||
|
|
||||||
|
@ -166,21 +164,6 @@ static inline slimb_t sat_add(slimb_t a, slimb_t b)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift)
|
|
||||||
{
|
|
||||||
if (shift != 0)
|
|
||||||
low = (low >> shift) | (high << (LIMB_BITS - shift));
|
|
||||||
return low;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift)
|
|
||||||
{
|
|
||||||
if (shift != 0)
|
|
||||||
return (a1 << shift) | (a0 >> (LIMB_BITS - shift));
|
|
||||||
else
|
|
||||||
return a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define malloc(s) malloc_is_forbidden(s)
|
#define malloc(s) malloc_is_forbidden(s)
|
||||||
#define free(p) free_is_forbidden(p)
|
#define free(p) free_is_forbidden(p)
|
||||||
#define realloc(p, s) realloc_is_forbidden(p, s)
|
#define realloc(p, s) realloc_is_forbidden(p, s)
|
||||||
|
@ -253,7 +236,7 @@ int bf_set_ui(bf_t *r, uint64_t a)
|
||||||
a1 = a >> 32;
|
a1 = a >> 32;
|
||||||
shift = clz(a1);
|
shift = clz(a1);
|
||||||
r->tab[0] = a0 << shift;
|
r->tab[0] = a0 << shift;
|
||||||
r->tab[1] = shld(a1, a0, shift);
|
r->tab[1] = (a1 << shift) | (a0 >> (LIMB_BITS - shift));
|
||||||
r->expn = 2 * LIMB_BITS - shift;
|
r->expn = 2 * LIMB_BITS - shift;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1602,9 +1585,7 @@ int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
|
||||||
r = &tmp;
|
r = &tmp;
|
||||||
}
|
}
|
||||||
if (bf_resize(r, a_len + b_len)) {
|
if (bf_resize(r, a_len + b_len)) {
|
||||||
#ifdef USE_FFT_MUL
|
|
||||||
fail:
|
fail:
|
||||||
#endif
|
|
||||||
bf_set_nan(r);
|
bf_set_nan(r);
|
||||||
ret = BF_ST_MEM_ERROR;
|
ret = BF_ST_MEM_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -2301,14 +2282,11 @@ static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b,
|
||||||
bf_t a;
|
bf_t a;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
#ifdef USE_BF_DEC
|
|
||||||
if (a1 == 10 && b <= LIMB_DIGITS) {
|
if (a1 == 10 && b <= LIMB_DIGITS) {
|
||||||
/* use precomputed powers. We do not round at this point
|
/* use precomputed powers. We do not round at this point
|
||||||
because we expect the caller to do it */
|
because we expect the caller to do it */
|
||||||
ret = bf_set_ui(r, mp_pow_dec[b]);
|
ret = bf_set_ui(r, mp_pow_dec[b]);
|
||||||
} else
|
} else {
|
||||||
#endif
|
|
||||||
{
|
|
||||||
bf_init(r->ctx, &a);
|
bf_init(r->ctx, &a);
|
||||||
ret = bf_set_ui(&a, a1);
|
ret = bf_set_ui(&a, a1);
|
||||||
ret |= bf_pow_ui(r, &a, b, prec, flags);
|
ret |= bf_pow_ui(r, &a, b, prec, flags);
|
||||||
|
@ -5414,6 +5392,21 @@ int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
|
||||||
|
|
||||||
#endif /* LIMB_BITS != 64 */
|
#endif /* LIMB_BITS != 64 */
|
||||||
|
|
||||||
|
static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift)
|
||||||
|
{
|
||||||
|
if (shift != 0)
|
||||||
|
low = (low >> shift) | (high << (LIMB_BITS - shift));
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift)
|
||||||
|
{
|
||||||
|
if (shift != 0)
|
||||||
|
return (a1 << shift) | (a0 >> (LIMB_BITS - shift));
|
||||||
|
else
|
||||||
|
return a1;
|
||||||
|
}
|
||||||
|
|
||||||
#if LIMB_DIGITS == 19
|
#if LIMB_DIGITS == 19
|
||||||
|
|
||||||
/* WARNING: hardcoded for b = 1e19. It is assumed that:
|
/* WARNING: hardcoded for b = 1e19. It is assumed that:
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
|
#if INTPTR_MAX >= INT64_MAX
|
||||||
#define LIMB_LOG2_BITS 6
|
#define LIMB_LOG2_BITS 6
|
||||||
#else
|
#else
|
||||||
#define LIMB_LOG2_BITS 5
|
#define LIMB_LOG2_BITS 5
|
||||||
|
|
|
@ -50,7 +50,8 @@ DEF(range32, 3) /* variable length */
|
||||||
DEF(lookahead, 5)
|
DEF(lookahead, 5)
|
||||||
DEF(negative_lookahead, 5)
|
DEF(negative_lookahead, 5)
|
||||||
DEF(push_char_pos, 1) /* push the character position on the stack */
|
DEF(push_char_pos, 1) /* push the character position on the stack */
|
||||||
DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */
|
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(prev, 1) /* go to the previous char */
|
||||||
DEF(simple_greedy_quant, 17)
|
DEF(simple_greedy_quant, 17)
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@
|
||||||
/*
|
/*
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
|
- Add full unicode canonicalize rules for character ranges (not
|
||||||
|
really useful but needed for exact "ignorecase" compatibility).
|
||||||
|
|
||||||
- Add a lock step execution mode (=linear time execution guaranteed)
|
- Add a lock step execution mode (=linear time execution guaranteed)
|
||||||
when the regular expression is "simple" i.e. no backreference nor
|
when the regular expression is "simple" i.e. no backreference nor
|
||||||
complicated lookahead. The opcodes are designed for this execution
|
complicated lookahead. The opcodes are designed for this execution
|
||||||
|
@ -117,6 +120,33 @@ static int dbuf_insert(DynBuf *s, int pos, int len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* canonicalize with the specific JS regexp rules */
|
||||||
|
static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16)
|
||||||
|
{
|
||||||
|
uint32_t res[LRE_CC_RES_LEN_MAX];
|
||||||
|
int len;
|
||||||
|
if (is_utf16) {
|
||||||
|
if (likely(c < 128)) {
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
c = c - 'A' + 'a';
|
||||||
|
} else {
|
||||||
|
lre_case_conv(res, c, 2);
|
||||||
|
c = res[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (likely(c < 128)) {
|
||||||
|
if (c >= 'a' && c <= 'z')
|
||||||
|
c = c - 'a' + 'A';
|
||||||
|
} else {
|
||||||
|
/* legacy regexp: to upper case if single char >= 128 */
|
||||||
|
len = lre_case_conv(res, c, FALSE);
|
||||||
|
if (len == 1 && res[0] >= 128)
|
||||||
|
c = res[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
static const uint16_t char_range_d[] = {
|
static const uint16_t char_range_d[] = {
|
||||||
1,
|
1,
|
||||||
0x0030, 0x0039 + 1,
|
0x0030, 0x0039 + 1,
|
||||||
|
@ -215,6 +245,31 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cr_canonicalize(CharRange *cr)
|
||||||
|
{
|
||||||
|
CharRange a;
|
||||||
|
uint32_t pt[2];
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
cr_init(&a, cr->mem_opaque, lre_realloc);
|
||||||
|
pt[0] = 'a';
|
||||||
|
pt[1] = 'z' + 1;
|
||||||
|
ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
/* convert to upper case */
|
||||||
|
/* XXX: the generic unicode case would be much more complicated
|
||||||
|
and not really useful */
|
||||||
|
for(i = 0; i < a.len; i++) {
|
||||||
|
a.points[i] += 'A' - 'a';
|
||||||
|
}
|
||||||
|
/* Note: for simplicity we keep the lower case ranges */
|
||||||
|
ret = cr_union1(cr, a.points, a.len);
|
||||||
|
fail:
|
||||||
|
cr_free(&a);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DUMP_REOP
|
#ifdef DUMP_REOP
|
||||||
static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
|
static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
|
||||||
int buf_len)
|
int buf_len)
|
||||||
|
@ -280,6 +335,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
|
||||||
case REOP_loop:
|
case REOP_loop:
|
||||||
case REOP_lookahead:
|
case REOP_lookahead:
|
||||||
case REOP_negative_lookahead:
|
case REOP_negative_lookahead:
|
||||||
|
case REOP_bne_char_pos:
|
||||||
val = get_u32(buf + pos + 1);
|
val = get_u32(buf + pos + 1);
|
||||||
val += (pos + 5);
|
val += (pos + 5);
|
||||||
printf(" %u", val);
|
printf(" %u", val);
|
||||||
|
@ -866,7 +922,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s->ignore_case) {
|
if (s->ignore_case) {
|
||||||
if (cr_regexp_canonicalize(cr, s->is_utf16))
|
if (cr_canonicalize(cr))
|
||||||
goto memory_error;
|
goto memory_error;
|
||||||
}
|
}
|
||||||
if (invert) {
|
if (invert) {
|
||||||
|
@ -887,17 +943,22 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return:
|
/* Return:
|
||||||
- true if the opcodes may not advance the char pointer
|
1 if the opcodes in bc_buf[] always advance the character pointer.
|
||||||
- false if the opcodes always advance the char pointer
|
0 if the character pointer may not be advanced.
|
||||||
|
-1 if the code may depend on side effects of its previous execution (backreference)
|
||||||
*/
|
*/
|
||||||
static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
|
static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
|
||||||
{
|
{
|
||||||
int pos, opcode, len;
|
int pos, opcode, ret, len, i;
|
||||||
uint32_t val;
|
uint32_t val, last;
|
||||||
BOOL ret;
|
BOOL has_back_reference;
|
||||||
|
uint8_t capture_bitmap[CAPTURE_COUNT_MAX];
|
||||||
|
|
||||||
ret = TRUE;
|
ret = -2; /* not known yet */
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
has_back_reference = FALSE;
|
||||||
|
memset(capture_bitmap, 0, sizeof(capture_bitmap));
|
||||||
|
|
||||||
while (pos < bc_buf_len) {
|
while (pos < bc_buf_len) {
|
||||||
opcode = bc_buf[pos];
|
opcode = bc_buf[pos];
|
||||||
len = reopcode_info[opcode].size;
|
len = reopcode_info[opcode].size;
|
||||||
|
@ -915,7 +976,8 @@ static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
|
||||||
case REOP_dot:
|
case REOP_dot:
|
||||||
case REOP_any:
|
case REOP_any:
|
||||||
simple_char:
|
simple_char:
|
||||||
ret = FALSE;
|
if (ret == -2)
|
||||||
|
ret = 1;
|
||||||
break;
|
break;
|
||||||
case REOP_line_start:
|
case REOP_line_start:
|
||||||
case REOP_line_end:
|
case REOP_line_end:
|
||||||
|
@ -929,16 +991,41 @@ static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
|
||||||
break;
|
break;
|
||||||
case REOP_save_start:
|
case REOP_save_start:
|
||||||
case REOP_save_end:
|
case REOP_save_end:
|
||||||
|
val = bc_buf[pos + 1];
|
||||||
|
capture_bitmap[val] |= 1;
|
||||||
|
break;
|
||||||
case REOP_save_reset:
|
case REOP_save_reset:
|
||||||
|
{
|
||||||
|
val = bc_buf[pos + 1];
|
||||||
|
last = bc_buf[pos + 2];
|
||||||
|
while (val < last)
|
||||||
|
capture_bitmap[val++] |= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case REOP_back_reference:
|
case REOP_back_reference:
|
||||||
case REOP_backward_back_reference:
|
case REOP_backward_back_reference:
|
||||||
|
val = bc_buf[pos + 1];
|
||||||
|
capture_bitmap[val] |= 2;
|
||||||
|
has_back_reference = TRUE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* safe behvior: we cannot predict the outcome */
|
/* safe behvior: we cannot predict the outcome */
|
||||||
return TRUE;
|
if (ret == -2)
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
pos += len;
|
pos += len;
|
||||||
}
|
}
|
||||||
|
if (has_back_reference) {
|
||||||
|
/* check if there is back reference which references a capture
|
||||||
|
made in the some code */
|
||||||
|
for(i = 0; i < CAPTURE_COUNT_MAX; i++) {
|
||||||
|
if (capture_bitmap[i] == 3)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret == -2)
|
||||||
|
ret = 0;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -984,10 +1071,11 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* '*pp' is the first char after '<' */
|
/* '*pp' is the first char after '<' */
|
||||||
static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp)
|
static int re_parse_group_name(char *buf, int buf_size,
|
||||||
|
const uint8_t **pp, BOOL is_utf16)
|
||||||
{
|
{
|
||||||
const uint8_t *p, *p1;
|
const uint8_t *p;
|
||||||
uint32_t c, d;
|
uint32_t c;
|
||||||
char *q;
|
char *q;
|
||||||
|
|
||||||
p = *pp;
|
p = *pp;
|
||||||
|
@ -998,18 +1086,11 @@ static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp)
|
||||||
p++;
|
p++;
|
||||||
if (*p != 'u')
|
if (*p != 'u')
|
||||||
return -1;
|
return -1;
|
||||||
c = lre_parse_escape(&p, 2); // accept surrogate pairs
|
c = lre_parse_escape(&p, is_utf16 * 2);
|
||||||
} else if (c == '>') {
|
} else if (c == '>') {
|
||||||
break;
|
break;
|
||||||
} else if (c >= 128) {
|
} else if (c >= 128) {
|
||||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
|
||||||
d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
|
|
||||||
if (d >= 0xDC00 && d <= 0xDFFF) {
|
|
||||||
c = 0x10000 + 0x400 * (c - 0xD800) + (d - 0xDC00);
|
|
||||||
p = p1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
@ -1059,7 +1140,8 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures,
|
||||||
/* potential named capture */
|
/* potential named capture */
|
||||||
if (capture_name) {
|
if (capture_name) {
|
||||||
p += 3;
|
p += 3;
|
||||||
if (re_parse_group_name(name, sizeof(name), &p) == 0) {
|
if (re_parse_group_name(name, sizeof(name), &p,
|
||||||
|
s->is_utf16) == 0) {
|
||||||
if (!strcmp(name, capture_name))
|
if (!strcmp(name, capture_name))
|
||||||
return capture_index;
|
return capture_index;
|
||||||
}
|
}
|
||||||
|
@ -1232,7 +1314,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
||||||
} else if (p[2] == '<') {
|
} else if (p[2] == '<') {
|
||||||
p += 3;
|
p += 3;
|
||||||
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
|
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
|
||||||
&p)) {
|
&p, s->is_utf16)) {
|
||||||
return re_parse_error(s, "invalid group name");
|
return re_parse_error(s, "invalid group name");
|
||||||
}
|
}
|
||||||
if (find_group_name(s, s->u.tmp_buf) > 0) {
|
if (find_group_name(s, s->u.tmp_buf) > 0) {
|
||||||
|
@ -1296,7 +1378,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
||||||
}
|
}
|
||||||
p1 += 3;
|
p1 += 3;
|
||||||
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
|
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
|
||||||
&p1)) {
|
&p1, s->is_utf16)) {
|
||||||
if (s->is_utf16 || re_has_named_captures(s))
|
if (s->is_utf16 || re_has_named_captures(s))
|
||||||
return re_parse_error(s, "invalid group name");
|
return re_parse_error(s, "invalid group name");
|
||||||
else
|
else
|
||||||
|
@ -1509,12 +1591,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
||||||
|
|
||||||
if (dbuf_error(&s->byte_code))
|
if (dbuf_error(&s->byte_code))
|
||||||
goto out_of_memory;
|
goto out_of_memory;
|
||||||
/* the spec tells that if there is no advance when
|
add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,
|
||||||
running the atom after the first quant_min times,
|
s->byte_code.size - last_atom_start) == 0);
|
||||||
then there is no match. We remove this test when we
|
|
||||||
are sure the atom always advances the position. */
|
|
||||||
add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start,
|
|
||||||
s->byte_code.size - last_atom_start);
|
|
||||||
} else {
|
} else {
|
||||||
add_zero_advance_check = FALSE;
|
add_zero_advance_check = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -1534,34 +1612,38 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
||||||
}
|
}
|
||||||
if (quant_max == 0) {
|
if (quant_max == 0) {
|
||||||
s->byte_code.size = last_atom_start;
|
s->byte_code.size = last_atom_start;
|
||||||
} else if (quant_max == 1 || quant_max == INT32_MAX) {
|
} else if (quant_max == 1) {
|
||||||
BOOL has_goto = (quant_max == INT32_MAX);
|
if (dbuf_insert(&s->byte_code, last_atom_start, 5))
|
||||||
|
goto out_of_memory;
|
||||||
|
s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
|
||||||
|
greedy;
|
||||||
|
put_u32(s->byte_code.buf + last_atom_start + 1, len);
|
||||||
|
} else if (quant_max == INT32_MAX) {
|
||||||
if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))
|
if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))
|
||||||
goto out_of_memory;
|
goto out_of_memory;
|
||||||
s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
|
s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
|
||||||
greedy;
|
greedy;
|
||||||
put_u32(s->byte_code.buf + last_atom_start + 1,
|
put_u32(s->byte_code.buf + last_atom_start + 1,
|
||||||
len + 5 * has_goto + add_zero_advance_check * 2);
|
len + 5 + add_zero_advance_check);
|
||||||
if (add_zero_advance_check) {
|
if (add_zero_advance_check) {
|
||||||
|
/* avoid infinite loop by stoping the
|
||||||
|
recursion if no advance was made in the
|
||||||
|
atom (only works if the atom has no
|
||||||
|
side effect) */
|
||||||
s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;
|
s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;
|
||||||
re_emit_op(s, REOP_check_advance);
|
re_emit_goto(s, REOP_bne_char_pos, last_atom_start);
|
||||||
}
|
} else {
|
||||||
if (has_goto)
|
|
||||||
re_emit_goto(s, REOP_goto, last_atom_start);
|
re_emit_goto(s, REOP_goto, last_atom_start);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (dbuf_insert(&s->byte_code, last_atom_start, 10 + add_zero_advance_check))
|
if (dbuf_insert(&s->byte_code, last_atom_start, 10))
|
||||||
goto out_of_memory;
|
goto out_of_memory;
|
||||||
pos = last_atom_start;
|
pos = last_atom_start;
|
||||||
s->byte_code.buf[pos++] = REOP_push_i32;
|
s->byte_code.buf[pos++] = REOP_push_i32;
|
||||||
put_u32(s->byte_code.buf + pos, quant_max);
|
put_u32(s->byte_code.buf + pos, quant_max);
|
||||||
pos += 4;
|
pos += 4;
|
||||||
s->byte_code.buf[pos++] = REOP_split_goto_first + greedy;
|
s->byte_code.buf[pos++] = REOP_split_goto_first + greedy;
|
||||||
put_u32(s->byte_code.buf + pos, len + 5 + add_zero_advance_check * 2);
|
put_u32(s->byte_code.buf + pos, len + 5);
|
||||||
pos += 4;
|
|
||||||
if (add_zero_advance_check) {
|
|
||||||
s->byte_code.buf[pos++] = REOP_push_char_pos;
|
|
||||||
re_emit_op(s, REOP_check_advance);
|
|
||||||
}
|
|
||||||
re_emit_goto(s, REOP_loop, last_atom_start + 5);
|
re_emit_goto(s, REOP_loop, last_atom_start + 5);
|
||||||
re_emit_op(s, REOP_drop);
|
re_emit_op(s, REOP_drop);
|
||||||
}
|
}
|
||||||
|
@ -1585,25 +1667,22 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
||||||
if (quant_max == INT32_MAX) {
|
if (quant_max == INT32_MAX) {
|
||||||
pos = s->byte_code.size;
|
pos = s->byte_code.size;
|
||||||
re_emit_op_u32(s, REOP_split_goto_first + greedy,
|
re_emit_op_u32(s, REOP_split_goto_first + greedy,
|
||||||
len + 5 + add_zero_advance_check * 2);
|
len + 5 + add_zero_advance_check);
|
||||||
if (add_zero_advance_check)
|
if (add_zero_advance_check)
|
||||||
re_emit_op(s, REOP_push_char_pos);
|
re_emit_op(s, REOP_push_char_pos);
|
||||||
/* copy the atom */
|
/* copy the atom */
|
||||||
dbuf_put_self(&s->byte_code, last_atom_start, len);
|
dbuf_put_self(&s->byte_code, last_atom_start, len);
|
||||||
if (add_zero_advance_check)
|
if (add_zero_advance_check)
|
||||||
re_emit_op(s, REOP_check_advance);
|
re_emit_goto(s, REOP_bne_char_pos, pos);
|
||||||
re_emit_goto(s, REOP_goto, pos);
|
else
|
||||||
|
re_emit_goto(s, REOP_goto, pos);
|
||||||
} else if (quant_max > quant_min) {
|
} else if (quant_max > quant_min) {
|
||||||
re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min);
|
re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min);
|
||||||
pos = s->byte_code.size;
|
pos = s->byte_code.size;
|
||||||
re_emit_op_u32(s, REOP_split_goto_first + greedy,
|
re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5);
|
||||||
len + 5 + add_zero_advance_check * 2);
|
|
||||||
if (add_zero_advance_check)
|
|
||||||
re_emit_op(s, REOP_push_char_pos);
|
|
||||||
/* copy the atom */
|
/* copy the atom */
|
||||||
dbuf_put_self(&s->byte_code, last_atom_start, len);
|
dbuf_put_self(&s->byte_code, last_atom_start, len);
|
||||||
if (add_zero_advance_check)
|
|
||||||
re_emit_op(s, REOP_check_advance);
|
|
||||||
re_emit_goto(s, REOP_loop, pos);
|
re_emit_goto(s, REOP_loop, pos);
|
||||||
re_emit_op(s, REOP_drop);
|
re_emit_op(s, REOP_drop);
|
||||||
}
|
}
|
||||||
|
@ -1717,7 +1796,7 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REOP_drop:
|
case REOP_drop:
|
||||||
case REOP_check_advance:
|
case REOP_bne_char_pos:
|
||||||
assert(stack_size > 0);
|
assert(stack_size > 0);
|
||||||
stack_size--;
|
stack_size--;
|
||||||
break;
|
break;
|
||||||
|
@ -2213,9 +2292,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
||||||
case REOP_push_char_pos:
|
case REOP_push_char_pos:
|
||||||
stack[stack_len++] = (uintptr_t)cptr;
|
stack[stack_len++] = (uintptr_t)cptr;
|
||||||
break;
|
break;
|
||||||
case REOP_check_advance:
|
case REOP_bne_char_pos:
|
||||||
if (stack[--stack_len] == (uintptr_t)cptr)
|
val = get_u32(pc);
|
||||||
goto no_match;
|
pc += 4;
|
||||||
|
if (stack[--stack_len] != (uintptr_t)cptr)
|
||||||
|
pc += (int)val;
|
||||||
break;
|
break;
|
||||||
case REOP_word_boundary:
|
case REOP_word_boundary:
|
||||||
case REOP_not_word_boundary:
|
case REOP_not_word_boundary:
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
#define LRE_FLAG_DOTALL (1 << 3)
|
#define LRE_FLAG_DOTALL (1 << 3)
|
||||||
#define LRE_FLAG_UTF16 (1 << 4)
|
#define LRE_FLAG_UTF16 (1 << 4)
|
||||||
#define LRE_FLAG_STICKY (1 << 5)
|
#define LRE_FLAG_STICKY (1 << 5)
|
||||||
#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */
|
|
||||||
|
|
||||||
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
|
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -43,111 +43,11 @@ enum {
|
||||||
RUN_TYPE_UF_D1_EXT,
|
RUN_TYPE_UF_D1_EXT,
|
||||||
RUN_TYPE_U_EXT,
|
RUN_TYPE_U_EXT,
|
||||||
RUN_TYPE_LF_EXT,
|
RUN_TYPE_LF_EXT,
|
||||||
RUN_TYPE_UF_EXT2,
|
RUN_TYPE_U_EXT2,
|
||||||
RUN_TYPE_LF_EXT2,
|
RUN_TYPE_L_EXT2,
|
||||||
RUN_TYPE_UF_EXT3,
|
RUN_TYPE_U_EXT3,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int lre_case_conv1(uint32_t c, int conv_type)
|
|
||||||
{
|
|
||||||
uint32_t res[LRE_CC_RES_LEN_MAX];
|
|
||||||
lre_case_conv(res, c, conv_type);
|
|
||||||
return res[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* case conversion using the table entry 'idx' with value 'v' */
|
|
||||||
static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v)
|
|
||||||
{
|
|
||||||
uint32_t code, data, type, a, is_lower;
|
|
||||||
is_lower = (conv_type != 0);
|
|
||||||
type = (v >> (32 - 17 - 7 - 4)) & 0xf;
|
|
||||||
data = ((v & 0xf) << 8) | case_conv_table2[idx];
|
|
||||||
code = v >> (32 - 17);
|
|
||||||
switch(type) {
|
|
||||||
case RUN_TYPE_U:
|
|
||||||
case RUN_TYPE_L:
|
|
||||||
case RUN_TYPE_UF:
|
|
||||||
case RUN_TYPE_LF:
|
|
||||||
if (conv_type == (type & 1) ||
|
|
||||||
(type >= RUN_TYPE_UF && conv_type == 2)) {
|
|
||||||
c = c - code + (case_conv_table1[data] >> (32 - 17));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_UL:
|
|
||||||
a = c - code;
|
|
||||||
if ((a & 1) != (1 - is_lower))
|
|
||||||
break;
|
|
||||||
c = (a ^ 1) + code;
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_LSU:
|
|
||||||
a = c - code;
|
|
||||||
if (a == 1) {
|
|
||||||
c += 2 * is_lower - 1;
|
|
||||||
} else if (a == (1 - is_lower) * 2) {
|
|
||||||
c += (2 * is_lower - 1) * 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_U2L_399_EXT2:
|
|
||||||
if (!is_lower) {
|
|
||||||
res[0] = c - code + case_conv_ext[data >> 6];
|
|
||||||
res[1] = 0x399;
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
c = c - code + case_conv_ext[data & 0x3f];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_UF_D20:
|
|
||||||
if (conv_type == 1)
|
|
||||||
break;
|
|
||||||
c = data + (conv_type == 2) * 0x20;
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_UF_D1_EXT:
|
|
||||||
if (conv_type == 1)
|
|
||||||
break;
|
|
||||||
c = case_conv_ext[data] + (conv_type == 2);
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_U_EXT:
|
|
||||||
case RUN_TYPE_LF_EXT:
|
|
||||||
if (is_lower != (type - RUN_TYPE_U_EXT))
|
|
||||||
break;
|
|
||||||
c = case_conv_ext[data];
|
|
||||||
break;
|
|
||||||
case RUN_TYPE_LF_EXT2:
|
|
||||||
if (!is_lower)
|
|
||||||
break;
|
|
||||||
res[0] = c - code + case_conv_ext[data >> 6];
|
|
||||||
res[1] = case_conv_ext[data & 0x3f];
|
|
||||||
return 2;
|
|
||||||
case RUN_TYPE_UF_EXT2:
|
|
||||||
if (conv_type == 1)
|
|
||||||
break;
|
|
||||||
res[0] = c - code + case_conv_ext[data >> 6];
|
|
||||||
res[1] = case_conv_ext[data & 0x3f];
|
|
||||||
if (conv_type == 2) {
|
|
||||||
/* convert to lower */
|
|
||||||
res[0] = lre_case_conv1(res[0], 1);
|
|
||||||
res[1] = lre_case_conv1(res[1], 1);
|
|
||||||
}
|
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
case RUN_TYPE_UF_EXT3:
|
|
||||||
if (conv_type == 1)
|
|
||||||
break;
|
|
||||||
res[0] = case_conv_ext[data >> 8];
|
|
||||||
res[1] = case_conv_ext[(data >> 4) & 0xf];
|
|
||||||
res[2] = case_conv_ext[data & 0xf];
|
|
||||||
if (conv_type == 2) {
|
|
||||||
/* convert to lower */
|
|
||||||
res[0] = lre_case_conv1(res[0], 1);
|
|
||||||
res[1] = lre_case_conv1(res[1], 1);
|
|
||||||
res[2] = lre_case_conv1(res[2], 1);
|
|
||||||
}
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
res[0] = c;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conv_type:
|
/* conv_type:
|
||||||
0 = to upper
|
0 = to upper
|
||||||
1 = to lower
|
1 = to lower
|
||||||
|
@ -166,9 +66,10 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint32_t v, code, len;
|
uint32_t v, code, data, type, len, a, is_lower;
|
||||||
int idx, idx_min, idx_max;
|
int idx, idx_min, idx_max;
|
||||||
|
|
||||||
|
is_lower = (conv_type != 0);
|
||||||
idx_min = 0;
|
idx_min = 0;
|
||||||
idx_max = countof(case_conv_table1) - 1;
|
idx_max = countof(case_conv_table1) - 1;
|
||||||
while (idx_min <= idx_max) {
|
while (idx_min <= idx_max) {
|
||||||
|
@ -181,7 +82,74 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
|
||||||
} else if (c >= code + len) {
|
} else if (c >= code + len) {
|
||||||
idx_min = idx + 1;
|
idx_min = idx + 1;
|
||||||
} else {
|
} else {
|
||||||
return lre_case_conv_entry(res, c, conv_type, idx, v);
|
type = (v >> (32 - 17 - 7 - 4)) & 0xf;
|
||||||
|
data = ((v & 0xf) << 8) | case_conv_table2[idx];
|
||||||
|
switch(type) {
|
||||||
|
case RUN_TYPE_U:
|
||||||
|
case RUN_TYPE_L:
|
||||||
|
case RUN_TYPE_UF:
|
||||||
|
case RUN_TYPE_LF:
|
||||||
|
if (conv_type == (type & 1) ||
|
||||||
|
(type >= RUN_TYPE_UF && conv_type == 2)) {
|
||||||
|
c = c - code + (case_conv_table1[data] >> (32 - 17));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_UL:
|
||||||
|
a = c - code;
|
||||||
|
if ((a & 1) != (1 - is_lower))
|
||||||
|
break;
|
||||||
|
c = (a ^ 1) + code;
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_LSU:
|
||||||
|
a = c - code;
|
||||||
|
if (a == 1) {
|
||||||
|
c += 2 * is_lower - 1;
|
||||||
|
} else if (a == (1 - is_lower) * 2) {
|
||||||
|
c += (2 * is_lower - 1) * 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_U2L_399_EXT2:
|
||||||
|
if (!is_lower) {
|
||||||
|
res[0] = c - code + case_conv_ext[data >> 6];
|
||||||
|
res[1] = 0x399;
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
c = c - code + case_conv_ext[data & 0x3f];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_UF_D20:
|
||||||
|
if (conv_type == 1)
|
||||||
|
break;
|
||||||
|
c = data + (conv_type == 2) * 0x20;
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_UF_D1_EXT:
|
||||||
|
if (conv_type == 1)
|
||||||
|
break;
|
||||||
|
c = case_conv_ext[data] + (conv_type == 2);
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_U_EXT:
|
||||||
|
case RUN_TYPE_LF_EXT:
|
||||||
|
if (is_lower != (type - RUN_TYPE_U_EXT))
|
||||||
|
break;
|
||||||
|
c = case_conv_ext[data];
|
||||||
|
break;
|
||||||
|
case RUN_TYPE_U_EXT2:
|
||||||
|
case RUN_TYPE_L_EXT2:
|
||||||
|
if (conv_type != (type - RUN_TYPE_U_EXT2))
|
||||||
|
break;
|
||||||
|
res[0] = c - code + case_conv_ext[data >> 6];
|
||||||
|
res[1] = case_conv_ext[data & 0x3f];
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
case RUN_TYPE_U_EXT3:
|
||||||
|
if (conv_type != 0)
|
||||||
|
break;
|
||||||
|
res[0] = case_conv_ext[data >> 8];
|
||||||
|
res[1] = case_conv_ext[(data >> 4) & 0xf];
|
||||||
|
res[2] = case_conv_ext[data & 0xf];
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,77 +157,6 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, BOOL is_unicode)
|
|
||||||
{
|
|
||||||
uint32_t res[LRE_CC_RES_LEN_MAX];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (is_unicode) {
|
|
||||||
len = lre_case_conv_entry(res, c, 2, idx, v);
|
|
||||||
if (len == 1) {
|
|
||||||
c = res[0];
|
|
||||||
} else {
|
|
||||||
/* handle the few specific multi-character cases (see
|
|
||||||
unicode_gen.c:dump_case_folding_special_cases()) */
|
|
||||||
if (c == 0xfb06) {
|
|
||||||
c = 0xfb05;
|
|
||||||
} else if (c == 0x01fd3) {
|
|
||||||
c = 0x390;
|
|
||||||
} else if (c == 0x01fe3) {
|
|
||||||
c = 0x3b0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (likely(c < 128)) {
|
|
||||||
if (c >= 'a' && c <= 'z')
|
|
||||||
c = c - 'a' + 'A';
|
|
||||||
} else {
|
|
||||||
/* legacy regexp: to upper case if single char >= 128 */
|
|
||||||
len = lre_case_conv_entry(res, c, FALSE, idx, v);
|
|
||||||
if (len == 1 && res[0] >= 128)
|
|
||||||
c = res[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* JS regexp specific rules for case folding */
|
|
||||||
int lre_canonicalize(uint32_t c, BOOL is_unicode)
|
|
||||||
{
|
|
||||||
if (c < 128) {
|
|
||||||
/* fast case */
|
|
||||||
if (is_unicode) {
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
|
||||||
c = c - 'A' + 'a';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (c >= 'a' && c <= 'z') {
|
|
||||||
c = c - 'a' + 'A';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uint32_t v, code, len;
|
|
||||||
int idx, idx_min, idx_max;
|
|
||||||
|
|
||||||
idx_min = 0;
|
|
||||||
idx_max = countof(case_conv_table1) - 1;
|
|
||||||
while (idx_min <= idx_max) {
|
|
||||||
idx = (unsigned)(idx_max + idx_min) / 2;
|
|
||||||
v = case_conv_table1[idx];
|
|
||||||
code = v >> (32 - 17);
|
|
||||||
len = (v >> (32 - 17 - 7)) & 0x7f;
|
|
||||||
if (c < code) {
|
|
||||||
idx_max = idx - 1;
|
|
||||||
} else if (c >= code + len) {
|
|
||||||
idx_min = idx + 1;
|
|
||||||
} else {
|
|
||||||
return lre_case_folding_entry(c, idx, v, is_unicode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t get_le24(const uint8_t *ptr)
|
static uint32_t get_le24(const uint8_t *ptr)
|
||||||
{
|
{
|
||||||
#if defined(__x86__) || defined(__x86_64__)
|
#if defined(__x86__) || defined(__x86_64__)
|
||||||
|
@ -1282,11 +1179,11 @@ static int unicode_case1(CharRange *cr, int case_mask)
|
||||||
#define MR(x) (1 << RUN_TYPE_ ## x)
|
#define MR(x) (1 << RUN_TYPE_ ## x)
|
||||||
const uint32_t tab_run_mask[3] = {
|
const uint32_t tab_run_mask[3] = {
|
||||||
MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
|
MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
|
||||||
MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
|
MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3),
|
||||||
|
|
||||||
MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2),
|
MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2),
|
||||||
|
|
||||||
MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
|
MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT),
|
||||||
};
|
};
|
||||||
#undef MR
|
#undef MR
|
||||||
uint32_t mask, v, code, type, len, i, idx;
|
uint32_t mask, v, code, type, len, i, idx;
|
||||||
|
@ -1339,136 +1236,7 @@ static int unicode_case1(CharRange *cr, int case_mask)
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int point_cmp(const void *p1, const void *p2, void *arg)
|
|
||||||
{
|
|
||||||
uint32_t v1 = *(uint32_t *)p1;
|
|
||||||
uint32_t v2 = *(uint32_t *)p2;
|
|
||||||
return (v1 > v2) - (v1 < v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cr_sort_and_remove_overlap(CharRange *cr)
|
|
||||||
{
|
|
||||||
uint32_t start, end, start1, end1, i, j;
|
|
||||||
|
|
||||||
/* the resulting ranges are not necessarily sorted and may overlap */
|
|
||||||
rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL);
|
|
||||||
j = 0;
|
|
||||||
for(i = 0; i < cr->len; ) {
|
|
||||||
start = cr->points[i];
|
|
||||||
end = cr->points[i + 1];
|
|
||||||
i += 2;
|
|
||||||
while (i < cr->len) {
|
|
||||||
start1 = cr->points[i];
|
|
||||||
end1 = cr->points[i + 1];
|
|
||||||
if (start1 > end) {
|
|
||||||
/* |------|
|
|
||||||
* |-------| */
|
|
||||||
break;
|
|
||||||
} else if (end1 <= end) {
|
|
||||||
/* |------|
|
|
||||||
* |--| */
|
|
||||||
i += 2;
|
|
||||||
} else {
|
|
||||||
/* |------|
|
|
||||||
* |-------| */
|
|
||||||
end = end1;
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cr->points[j] = start;
|
|
||||||
cr->points[j + 1] = end;
|
|
||||||
j += 2;
|
|
||||||
}
|
|
||||||
cr->len = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* canonicalize a character set using the JS regex case folding rules
|
|
||||||
(see lre_canonicalize()) */
|
|
||||||
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode)
|
|
||||||
{
|
|
||||||
CharRange cr_inter, cr_mask, cr_result, cr_sub;
|
|
||||||
uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d;
|
|
||||||
|
|
||||||
cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func);
|
|
||||||
cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func);
|
|
||||||
cr_init(&cr_result, cr->mem_opaque, cr->realloc_func);
|
|
||||||
cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func);
|
|
||||||
|
|
||||||
if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U))
|
|
||||||
goto fail;
|
|
||||||
if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
if (cr_invert(&cr_mask))
|
|
||||||
goto fail;
|
|
||||||
if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/* cr_inter = cr & cr_mask */
|
|
||||||
/* cr_sub = cr & ~cr_mask */
|
|
||||||
|
|
||||||
/* use the case conversion table to compute the result */
|
|
||||||
d_start = -1;
|
|
||||||
d_end = -1;
|
|
||||||
idx = 0;
|
|
||||||
v = case_conv_table1[idx];
|
|
||||||
code = v >> (32 - 17);
|
|
||||||
len = (v >> (32 - 17 - 7)) & 0x7f;
|
|
||||||
for(i = 0; i < cr_inter.len; i += 2) {
|
|
||||||
start = cr_inter.points[i];
|
|
||||||
end = cr_inter.points[i + 1];
|
|
||||||
|
|
||||||
for(c = start; c < end; c++) {
|
|
||||||
for(;;) {
|
|
||||||
if (c >= code && c < code + len)
|
|
||||||
break;
|
|
||||||
idx++;
|
|
||||||
assert(idx < countof(case_conv_table1));
|
|
||||||
v = case_conv_table1[idx];
|
|
||||||
code = v >> (32 - 17);
|
|
||||||
len = (v >> (32 - 17 - 7)) & 0x7f;
|
|
||||||
}
|
|
||||||
d = lre_case_folding_entry(c, idx, v, is_unicode);
|
|
||||||
/* try to merge with the current interval */
|
|
||||||
if (d_start == -1) {
|
|
||||||
d_start = d;
|
|
||||||
d_end = d + 1;
|
|
||||||
} else if (d_end == d) {
|
|
||||||
d_end++;
|
|
||||||
} else {
|
|
||||||
cr_add_interval(&cr_result, d_start, d_end);
|
|
||||||
d_start = d;
|
|
||||||
d_end = d + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (d_start != -1) {
|
|
||||||
if (cr_add_interval(&cr_result, d_start, d_end))
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the resulting ranges are not necessarily sorted and may overlap */
|
|
||||||
cr_sort_and_remove_overlap(&cr_result);
|
|
||||||
|
|
||||||
/* or with the character not affected by the case folding */
|
|
||||||
cr->len = 0;
|
|
||||||
if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
cr_free(&cr_inter);
|
|
||||||
cr_free(&cr_mask);
|
|
||||||
cr_free(&cr_result);
|
|
||||||
cr_free(&cr_sub);
|
|
||||||
return 0;
|
|
||||||
fail:
|
|
||||||
cr_free(&cr_inter);
|
|
||||||
cr_free(&cr_mask);
|
|
||||||
cr_free(&cr_result);
|
|
||||||
cr_free(&cr_sub);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
POP_GC,
|
POP_GC,
|
||||||
POP_PROP,
|
POP_PROP,
|
||||||
|
|
|
@ -41,7 +41,6 @@ typedef enum {
|
||||||
} UnicodeNormalizationEnum;
|
} UnicodeNormalizationEnum;
|
||||||
|
|
||||||
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
|
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
|
||||||
int lre_canonicalize(uint32_t c, BOOL is_unicode);
|
|
||||||
LRE_BOOL lre_is_cased(uint32_t c);
|
LRE_BOOL lre_is_cased(uint32_t c);
|
||||||
LRE_BOOL lre_is_case_ignorable(uint32_t c);
|
LRE_BOOL lre_is_case_ignorable(uint32_t c);
|
||||||
|
|
||||||
|
@ -102,8 +101,6 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
|
||||||
|
|
||||||
int cr_invert(CharRange *cr);
|
int cr_invert(CharRange *cr);
|
||||||
|
|
||||||
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode);
|
|
||||||
|
|
||||||
#ifdef CONFIG_ALL_UNICODE
|
#ifdef CONFIG_ALL_UNICODE
|
||||||
|
|
||||||
LRE_BOOL lre_is_id_start(uint32_t c);
|
LRE_BOOL lre_is_id_start(uint32_t c);
|
||||||
|
|
|
@ -36,7 +36,8 @@ struct list_head {
|
||||||
#define LIST_HEAD_INIT(el) { &(el), &(el) }
|
#define LIST_HEAD_INIT(el) { &(el), &(el) }
|
||||||
|
|
||||||
/* return the pointer of type 'type *' containing 'el' as field 'member' */
|
/* return the pointer of type 'type *' containing 'el' as field 'member' */
|
||||||
#define list_entry(el, type, member) container_of(el, type, member)
|
#define list_entry(el, type, member) \
|
||||||
|
((type *)((uint8_t *)(el) - offsetof(type, member)))
|
||||||
|
|
||||||
static inline void init_list_head(struct list_head *head)
|
static inline void init_list_head(struct list_head *head)
|
||||||
{
|
{
|
||||||
|
|
|
@ -140,19 +140,19 @@ static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default memory allocation functions with memory limitation */
|
/* default memory allocation functions with memory limitation */
|
||||||
static size_t js_trace_malloc_usable_size(const void *ptr)
|
static inline size_t js_trace_malloc_usable_size(void *ptr)
|
||||||
{
|
{
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
return malloc_size(ptr);
|
return malloc_size(ptr);
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
return _msize((void *)ptr);
|
return _msize(ptr);
|
||||||
#elif defined(EMSCRIPTEN)
|
#elif defined(EMSCRIPTEN)
|
||||||
return 0;
|
return 0;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
return malloc_usable_size((void *)ptr);
|
return malloc_usable_size(ptr);
|
||||||
#else
|
#else
|
||||||
/* change this to `return 0;` if compilation fails */
|
/* change this to `return 0;` if compilation fails */
|
||||||
return malloc_usable_size((void *)ptr);
|
return malloc_usable_size(ptr);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +264,18 @@ static const JSMallocFunctions trace_mf = {
|
||||||
js_trace_malloc,
|
js_trace_malloc,
|
||||||
js_trace_free,
|
js_trace_free,
|
||||||
js_trace_realloc,
|
js_trace_realloc,
|
||||||
js_trace_malloc_usable_size,
|
#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"
|
#define PROG_NAME "qjs"
|
||||||
|
@ -443,10 +454,8 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_BIGNUM
|
|
||||||
if (load_jscalc)
|
if (load_jscalc)
|
||||||
bignum_ext = 1;
|
bignum_ext = 1;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (trace_memory) {
|
if (trace_memory) {
|
||||||
js_trace_malloc_init(&trace_data);
|
js_trace_malloc_init(&trace_data);
|
||||||
|
|
|
@ -76,7 +76,9 @@ static const FeatureEntry feature_list[] = {
|
||||||
{ "promise", "Promise" },
|
{ "promise", "Promise" },
|
||||||
#define FE_MODULE_LOADER 9
|
#define FE_MODULE_LOADER 9
|
||||||
{ "module-loader", NULL },
|
{ "module-loader", NULL },
|
||||||
|
#ifdef CONFIG_BIGNUM
|
||||||
{ "bigint", "BigInt" },
|
{ "bigint", "BigInt" },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
|
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
|
||||||
|
@ -330,7 +332,6 @@ static const char main_c_template1[] =
|
||||||
|
|
||||||
static const char main_c_template2[] =
|
static const char main_c_template2[] =
|
||||||
" js_std_loop(ctx);\n"
|
" js_std_loop(ctx);\n"
|
||||||
" js_std_free_handlers(rt);\n"
|
|
||||||
" JS_FreeContext(ctx);\n"
|
" JS_FreeContext(ctx);\n"
|
||||||
" JS_FreeRuntime(rt);\n"
|
" JS_FreeRuntime(rt);\n"
|
||||||
" return 0;\n"
|
" return 0;\n"
|
||||||
|
@ -344,8 +345,8 @@ void help(void)
|
||||||
"usage: " PROG_NAME " [options] [files]\n"
|
"usage: " PROG_NAME " [options] [files]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"options are:\n"
|
"options are:\n"
|
||||||
"-c only output bytecode to a C file\n"
|
"-c only output bytecode in a C file\n"
|
||||||
"-e output main() and bytecode to a C file (default = executable output)\n"
|
"-e output main() and bytecode in a C file (default = executable output)\n"
|
||||||
"-o output set the output filename\n"
|
"-o output set the output filename\n"
|
||||||
"-N cname set the C name of the generated data\n"
|
"-N cname set the C name of the generated data\n"
|
||||||
"-m compile as Javascript module (default=autodetect)\n"
|
"-m compile as Javascript module (default=autodetect)\n"
|
||||||
|
|
|
@ -82,7 +82,6 @@ DEF(length, "length")
|
||||||
DEF(fileName, "fileName")
|
DEF(fileName, "fileName")
|
||||||
DEF(lineNumber, "lineNumber")
|
DEF(lineNumber, "lineNumber")
|
||||||
DEF(message, "message")
|
DEF(message, "message")
|
||||||
DEF(cause, "cause")
|
|
||||||
DEF(errors, "errors")
|
DEF(errors, "errors")
|
||||||
DEF(stack, "stack")
|
DEF(stack, "stack")
|
||||||
DEF(name, "name")
|
DEF(name, "name")
|
||||||
|
@ -167,23 +166,22 @@ DEF(revoke, "revoke")
|
||||||
DEF(async, "async")
|
DEF(async, "async")
|
||||||
DEF(exec, "exec")
|
DEF(exec, "exec")
|
||||||
DEF(groups, "groups")
|
DEF(groups, "groups")
|
||||||
DEF(indices, "indices")
|
|
||||||
DEF(status, "status")
|
DEF(status, "status")
|
||||||
DEF(reason, "reason")
|
DEF(reason, "reason")
|
||||||
DEF(globalThis, "globalThis")
|
DEF(globalThis, "globalThis")
|
||||||
DEF(bigint, "bigint")
|
|
||||||
#ifdef CONFIG_BIGNUM
|
#ifdef CONFIG_BIGNUM
|
||||||
|
DEF(bigint, "bigint")
|
||||||
DEF(bigfloat, "bigfloat")
|
DEF(bigfloat, "bigfloat")
|
||||||
DEF(bigdecimal, "bigdecimal")
|
DEF(bigdecimal, "bigdecimal")
|
||||||
DEF(roundingMode, "roundingMode")
|
DEF(roundingMode, "roundingMode")
|
||||||
DEF(maximumSignificantDigits, "maximumSignificantDigits")
|
DEF(maximumSignificantDigits, "maximumSignificantDigits")
|
||||||
DEF(maximumFractionDigits, "maximumFractionDigits")
|
DEF(maximumFractionDigits, "maximumFractionDigits")
|
||||||
#endif
|
#endif
|
||||||
/* the following 3 atoms are only used with CONFIG_ATOMICS */
|
#ifdef CONFIG_ATOMICS
|
||||||
DEF(not_equal, "not-equal")
|
DEF(not_equal, "not-equal")
|
||||||
DEF(timed_out, "timed-out")
|
DEF(timed_out, "timed-out")
|
||||||
DEF(ok, "ok")
|
DEF(ok, "ok")
|
||||||
/* */
|
#endif
|
||||||
DEF(toJSON, "toJSON")
|
DEF(toJSON, "toJSON")
|
||||||
/* class names */
|
/* class names */
|
||||||
DEF(Object, "Object")
|
DEF(Object, "Object")
|
||||||
|
@ -211,13 +209,15 @@ DEF(Int16Array, "Int16Array")
|
||||||
DEF(Uint16Array, "Uint16Array")
|
DEF(Uint16Array, "Uint16Array")
|
||||||
DEF(Int32Array, "Int32Array")
|
DEF(Int32Array, "Int32Array")
|
||||||
DEF(Uint32Array, "Uint32Array")
|
DEF(Uint32Array, "Uint32Array")
|
||||||
|
#ifdef CONFIG_BIGNUM
|
||||||
DEF(BigInt64Array, "BigInt64Array")
|
DEF(BigInt64Array, "BigInt64Array")
|
||||||
DEF(BigUint64Array, "BigUint64Array")
|
DEF(BigUint64Array, "BigUint64Array")
|
||||||
|
#endif
|
||||||
DEF(Float32Array, "Float32Array")
|
DEF(Float32Array, "Float32Array")
|
||||||
DEF(Float64Array, "Float64Array")
|
DEF(Float64Array, "Float64Array")
|
||||||
DEF(DataView, "DataView")
|
DEF(DataView, "DataView")
|
||||||
DEF(BigInt, "BigInt")
|
|
||||||
#ifdef CONFIG_BIGNUM
|
#ifdef CONFIG_BIGNUM
|
||||||
|
DEF(BigInt, "BigInt")
|
||||||
DEF(BigFloat, "BigFloat")
|
DEF(BigFloat, "BigFloat")
|
||||||
DEF(BigFloatEnv, "BigFloatEnv")
|
DEF(BigFloatEnv, "BigFloatEnv")
|
||||||
DEF(BigDecimal, "BigDecimal")
|
DEF(BigDecimal, "BigDecimal")
|
||||||
|
|
|
@ -751,7 +751,6 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
|
||||||
JSValue ret;
|
JSValue ret;
|
||||||
JSValueConst options_obj;
|
JSValueConst options_obj;
|
||||||
BOOL backtrace_barrier = FALSE;
|
BOOL backtrace_barrier = FALSE;
|
||||||
BOOL is_async = FALSE;
|
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
|
@ -759,9 +758,6 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
|
||||||
if (get_bool_option(ctx, &backtrace_barrier, options_obj,
|
if (get_bool_option(ctx, &backtrace_barrier, options_obj,
|
||||||
"backtrace_barrier"))
|
"backtrace_barrier"))
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
if (get_bool_option(ctx, &is_async, options_obj,
|
|
||||||
"async"))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
str = JS_ToCStringLen(ctx, &len, argv[0]);
|
str = JS_ToCStringLen(ctx, &len, argv[0]);
|
||||||
|
@ -774,8 +770,6 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
|
||||||
flags = JS_EVAL_TYPE_GLOBAL;
|
flags = JS_EVAL_TYPE_GLOBAL;
|
||||||
if (backtrace_barrier)
|
if (backtrace_barrier)
|
||||||
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
|
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
|
||||||
if (is_async)
|
|
||||||
flags |= JS_EVAL_FLAG_ASYNC;
|
|
||||||
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
|
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
|
||||||
JS_FreeCString(ctx, str);
|
JS_FreeCString(ctx, str);
|
||||||
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
|
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
|
||||||
|
@ -1976,13 +1970,6 @@ static int64_t get_time_ms(void)
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
|
return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t get_time_ns(void)
|
|
||||||
{
|
|
||||||
struct timespec ts;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
/* more portable, but does not work if the date is updated */
|
/* more portable, but does not work if the date is updated */
|
||||||
static int64_t get_time_ms(void)
|
static int64_t get_time_ms(void)
|
||||||
|
@ -1991,21 +1978,8 @@ static int64_t get_time_ms(void)
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
|
return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t get_time_ns(void)
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
return (int64_t)tv.tv_sec * 1000000000 + (tv.tv_usec * 1000);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static JSValue js_os_now(JSContext *ctx, JSValue this_val,
|
|
||||||
int argc, JSValue *argv)
|
|
||||||
{
|
|
||||||
return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unlink_timer(JSRuntime *rt, JSOSTimer *th)
|
static void unlink_timer(JSRuntime *rt, JSOSTimer *th)
|
||||||
{
|
{
|
||||||
if (th->link.prev) {
|
if (th->link.prev) {
|
||||||
|
@ -2088,38 +2062,6 @@ static JSClassDef js_os_timer_class = {
|
||||||
.gc_mark = js_os_timer_mark,
|
.gc_mark = js_os_timer_mark,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* return a promise */
|
|
||||||
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
|
|
||||||
int argc, JSValueConst *argv)
|
|
||||||
{
|
|
||||||
JSRuntime *rt = JS_GetRuntime(ctx);
|
|
||||||
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
|
|
||||||
int64_t delay;
|
|
||||||
JSOSTimer *th;
|
|
||||||
JSValue promise, resolving_funcs[2];
|
|
||||||
|
|
||||||
if (JS_ToInt64(ctx, &delay, argv[0]))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
|
||||||
if (JS_IsException(promise))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
|
|
||||||
th = js_mallocz(ctx, sizeof(*th));
|
|
||||||
if (!th) {
|
|
||||||
JS_FreeValue(ctx, promise);
|
|
||||||
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
||||||
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
}
|
|
||||||
th->has_object = FALSE;
|
|
||||||
th->timeout = get_time_ms() + delay;
|
|
||||||
th->func = JS_DupValue(ctx, resolving_funcs[0]);
|
|
||||||
list_add_tail(&th->link, &ts->os_timers);
|
|
||||||
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
||||||
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void call_handler(JSContext *ctx, JSValueConst func)
|
static void call_handler(JSContext *ctx, JSValueConst func)
|
||||||
{
|
{
|
||||||
JSValue ret, func1;
|
JSValue ret, func1;
|
||||||
|
@ -3088,13 +3030,6 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* getpid() -> pid */
|
|
||||||
static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val,
|
|
||||||
int argc, JSValueConst *argv)
|
|
||||||
{
|
|
||||||
return JS_NewInt32(ctx, getpid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* waitpid(pid, block) -> [pid, status] */
|
/* waitpid(pid, block) -> [pid, status] */
|
||||||
static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val,
|
static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val,
|
||||||
int argc, JSValueConst *argv)
|
int argc, JSValueConst *argv)
|
||||||
|
@ -3339,7 +3274,6 @@ static void *worker_func(void *opaque)
|
||||||
JSRuntime *rt;
|
JSRuntime *rt;
|
||||||
JSThreadState *ts;
|
JSThreadState *ts;
|
||||||
JSContext *ctx;
|
JSContext *ctx;
|
||||||
JSValue promise;
|
|
||||||
|
|
||||||
rt = JS_NewRuntime();
|
rt = JS_NewRuntime();
|
||||||
if (rt == NULL) {
|
if (rt == NULL) {
|
||||||
|
@ -3366,11 +3300,8 @@ static void *worker_func(void *opaque)
|
||||||
|
|
||||||
js_std_add_helpers(ctx, -1, NULL);
|
js_std_add_helpers(ctx, -1, NULL);
|
||||||
|
|
||||||
promise = JS_LoadModule(ctx, args->basename, args->filename);
|
if (!JS_RunModule(ctx, args->basename, args->filename))
|
||||||
if (JS_IsException(promise))
|
|
||||||
js_std_dump_error(ctx);
|
js_std_dump_error(ctx);
|
||||||
/* XXX: check */
|
|
||||||
JS_FreeValue(ctx, promise);
|
|
||||||
free(args->filename);
|
free(args->filename);
|
||||||
free(args->basename);
|
free(args->basename);
|
||||||
free(args);
|
free(args);
|
||||||
|
@ -3690,10 +3621,8 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||||
OS_FLAG(SIGTTIN),
|
OS_FLAG(SIGTTIN),
|
||||||
OS_FLAG(SIGTTOU),
|
OS_FLAG(SIGTTOU),
|
||||||
#endif
|
#endif
|
||||||
JS_CFUNC_DEF("now", 0, js_os_now ),
|
|
||||||
JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
|
JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
|
||||||
JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
|
JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
|
||||||
JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ),
|
|
||||||
JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
|
JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
|
||||||
JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
|
JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
|
||||||
JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
|
JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
|
||||||
|
@ -3721,7 +3650,6 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||||
JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
|
JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
|
||||||
JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
|
JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
|
||||||
JS_CFUNC_DEF("exec", 1, js_os_exec ),
|
JS_CFUNC_DEF("exec", 1, js_os_exec ),
|
||||||
JS_CFUNC_DEF("getpid", 0, js_os_getpid ),
|
|
||||||
JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),
|
JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),
|
||||||
OS_FLAG(WNOHANG),
|
OS_FLAG(WNOHANG),
|
||||||
JS_CFUNC_DEF("pipe", 0, js_os_pipe ),
|
JS_CFUNC_DEF("pipe", 0, js_os_pipe ),
|
||||||
|
|
|
@ -172,7 +172,6 @@ DEF(set_loc_uninitialized, 3, 0, 0, loc)
|
||||||
DEF( get_loc_check, 3, 0, 1, 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, 3, 1, 0, loc) /* must come after get_loc_check */
|
||||||
DEF( put_loc_check_init, 3, 1, 0, loc)
|
DEF( put_loc_check_init, 3, 1, 0, loc)
|
||||||
DEF(get_loc_checkthis, 3, 0, 1, loc)
|
|
||||||
DEF(get_var_ref_check, 3, 0, 1, var_ref)
|
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, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
|
||||||
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
|
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
|
||||||
|
@ -183,7 +182,6 @@ DEF( goto, 5, 0, 0, label) /* must come after if_true */
|
||||||
DEF( catch, 5, 0, 1, label)
|
DEF( catch, 5, 0, 1, label)
|
||||||
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
|
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( ret, 1, 1, 0, none) /* used to return from the finally block */
|
||||||
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
|
|
||||||
|
|
||||||
DEF( to_object, 1, 1, 1, none)
|
DEF( to_object, 1, 1, 1, none)
|
||||||
//DEF( to_string, 1, 1, 1, none)
|
//DEF( to_string, 1, 1, 1, none)
|
||||||
|
@ -210,6 +208,7 @@ DEF( for_of_next, 2, 3, 5, u8)
|
||||||
DEF(iterator_check_object, 1, 1, 1, none)
|
DEF(iterator_check_object, 1, 1, 1, none)
|
||||||
DEF(iterator_get_value_done, 1, 1, 2, none)
|
DEF(iterator_get_value_done, 1, 1, 2, none)
|
||||||
DEF( iterator_close, 1, 3, 0, none)
|
DEF( iterator_close, 1, 3, 0, none)
|
||||||
|
DEF(iterator_close_return, 1, 4, 4, none)
|
||||||
DEF( iterator_next, 1, 4, 4, none)
|
DEF( iterator_next, 1, 4, 4, none)
|
||||||
DEF( iterator_call, 2, 4, 5, u8)
|
DEF( iterator_call, 2, 4, 5, u8)
|
||||||
DEF( initial_yield, 1, 0, 0, none)
|
DEF( initial_yield, 1, 0, 0, none)
|
||||||
|
@ -257,7 +256,6 @@ DEF( and, 1, 2, 1, none)
|
||||||
DEF( xor, 1, 2, 1, none)
|
DEF( xor, 1, 2, 1, none)
|
||||||
DEF( or, 1, 2, 1, none)
|
DEF( or, 1, 2, 1, none)
|
||||||
DEF(is_undefined_or_null, 1, 1, 1, none)
|
DEF(is_undefined_or_null, 1, 1, 1, none)
|
||||||
DEF( private_in, 1, 2, 1, none)
|
|
||||||
#ifdef CONFIG_BIGNUM
|
#ifdef CONFIG_BIGNUM
|
||||||
DEF( mul_pow10, 1, 2, 1, none)
|
DEF( mul_pow10, 1, 2, 1, none)
|
||||||
DEF( math_mod, 1, 2, 1, none)
|
DEF( math_mod, 1, 2, 1, none)
|
||||||
|
@ -272,8 +270,6 @@ 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( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
|
||||||
|
|
||||||
/* the following opcodes must be in the same order as the 'with_x' and
|
|
||||||
get_var_undef, get_var and put_var opcodes */
|
|
||||||
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
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_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_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||||
|
@ -281,13 +277,10 @@ def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase
|
||||||
def( scope_make_ref, 11, 0, 2, atom_label_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_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_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
|
||||||
def(scope_get_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */
|
|
||||||
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, 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_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, 2, 0, atom_u16) /* 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(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
|
|
||||||
def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */
|
|
||||||
def(get_array_el_opt_chain, 1, 2, 1, none) /* 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( 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 */
|
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -307,9 +307,6 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
||||||
/* don't include the stack frames before this eval in the Error() backtraces */
|
/* don't include the stack frames before this eval in the Error() backtraces */
|
||||||
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
||||||
/* allow top-level await in normal script. JS_Eval() returns a
|
|
||||||
promise. Only allowed with JS_EVAL_TYPE_GLOBAL */
|
|
||||||
#define JS_EVAL_FLAG_ASYNC (1 << 7)
|
|
||||||
|
|
||||||
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
|
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
|
||||||
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
|
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
|
||||||
|
@ -736,13 +733,13 @@ JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
|
||||||
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
||||||
uint32_t idx);
|
uint32_t idx);
|
||||||
|
|
||||||
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
|
||||||
JSAtom prop, JSValue val, JSValueConst this_obj,
|
JSAtom prop, JSValue val,
|
||||||
int flags);
|
int flags);
|
||||||
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
|
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
|
||||||
JSAtom prop, JSValue val)
|
JSAtom prop, JSValue val)
|
||||||
{
|
{
|
||||||
return JS_SetPropertyInternal(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW);
|
return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);
|
||||||
}
|
}
|
||||||
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
||||||
uint32_t idx, JSValue val);
|
uint32_t idx, JSValue val);
|
||||||
|
@ -834,15 +831,7 @@ typedef struct {
|
||||||
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
|
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
|
||||||
const JSSharedArrayBufferFunctions *sf);
|
const JSSharedArrayBufferFunctions *sf);
|
||||||
|
|
||||||
typedef enum JSPromiseStateEnum {
|
|
||||||
JS_PROMISE_PENDING,
|
|
||||||
JS_PROMISE_FULFILLED,
|
|
||||||
JS_PROMISE_REJECTED,
|
|
||||||
} JSPromiseStateEnum;
|
|
||||||
|
|
||||||
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
|
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
|
||||||
JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise);
|
|
||||||
JSValue JS_PromiseResult(JSContext *ctx, JSValue promise);
|
|
||||||
|
|
||||||
/* is_handled = TRUE means that the rejection is handled */
|
/* is_handled = TRUE means that the rejection is handled */
|
||||||
typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
|
typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
|
||||||
|
@ -913,8 +902,8 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
|
||||||
/* only exported for os.Worker() */
|
/* only exported for os.Worker() */
|
||||||
JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
|
JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
|
||||||
/* only exported for os.Worker() */
|
/* only exported for os.Worker() */
|
||||||
JSValue JS_LoadModule(JSContext *ctx, const char *basename,
|
JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
|
||||||
const char *filename);
|
const char *filename);
|
||||||
|
|
||||||
/* C function definition */
|
/* C function definition */
|
||||||
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
|
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
//#define DUMP_TABLE_SIZE
|
//#define DUMP_TABLE_SIZE
|
||||||
//#define DUMP_CC_TABLE
|
//#define DUMP_CC_TABLE
|
||||||
//#define DUMP_DECOMP_TABLE
|
//#define DUMP_DECOMP_TABLE
|
||||||
//#define DUMP_CASE_FOLDING_SPECIAL_CASES
|
|
||||||
|
|
||||||
/* Ideas:
|
/* Ideas:
|
||||||
- Generalize run length encoding + index for all tables
|
- Generalize run length encoding + index for all tables
|
||||||
|
@ -218,16 +217,15 @@ static const char *unicode_prop_short_name[] = {
|
||||||
#undef DEF
|
#undef DEF
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef UNICODE_PROP_LIST
|
#undef UNICODE_SPROP_LIST
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* case conv */
|
/* case conv */
|
||||||
uint8_t u_len;
|
uint8_t u_len;
|
||||||
uint8_t l_len;
|
uint8_t l_len;
|
||||||
uint8_t f_len;
|
int u_data[CC_LEN_MAX];
|
||||||
int u_data[CC_LEN_MAX]; /* to upper case */
|
int l_data[CC_LEN_MAX];
|
||||||
int l_data[CC_LEN_MAX]; /* to lower case */
|
int f_code;
|
||||||
int f_data[CC_LEN_MAX]; /* to case folding */
|
|
||||||
|
|
||||||
uint8_t combining_class;
|
uint8_t combining_class;
|
||||||
uint8_t is_compat:1;
|
uint8_t is_compat:1;
|
||||||
|
@ -501,7 +499,7 @@ void parse_case_folding(CCInfo *tab, const char *filename)
|
||||||
FILE *f;
|
FILE *f;
|
||||||
char line[1024];
|
char line[1024];
|
||||||
const char *p;
|
const char *p;
|
||||||
int code, status;
|
int code;
|
||||||
CCInfo *ci;
|
CCInfo *ci;
|
||||||
|
|
||||||
f = fopen(filename, "rb");
|
f = fopen(filename, "rb");
|
||||||
|
@ -532,28 +530,14 @@ void parse_case_folding(CCInfo *tab, const char *filename)
|
||||||
/* locale dependent casing */
|
/* locale dependent casing */
|
||||||
while (isspace(*p))
|
while (isspace(*p))
|
||||||
p++;
|
p++;
|
||||||
status = *p;
|
if (*p != 'C' && *p != 'S')
|
||||||
if (status != 'C' && status != 'S' && status != 'F')
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
p = get_field(line, 2);
|
p = get_field(line, 2);
|
||||||
assert(p != NULL);
|
assert(p != 0);
|
||||||
if (status == 'S') {
|
assert(ci->f_code == 0);
|
||||||
/* we always select the simple case folding and assume it
|
ci->f_code = strtoul(p, NULL, 16);
|
||||||
* comes after the full case folding case */
|
assert(ci->f_code != 0 && ci->f_code != code);
|
||||||
assert(ci->f_len >= 2);
|
|
||||||
ci->f_len = 0;
|
|
||||||
} else {
|
|
||||||
assert(ci->f_len == 0);
|
|
||||||
}
|
|
||||||
for(;;) {
|
|
||||||
while (isspace(*p))
|
|
||||||
p++;
|
|
||||||
if (*p == ';')
|
|
||||||
break;
|
|
||||||
assert(ci->l_len < CC_LEN_MAX);
|
|
||||||
ci->f_data[ci->f_len++] = strtoul(p, (char **)&p, 16);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
@ -880,21 +864,19 @@ void dump_cc_info(CCInfo *ci, int i)
|
||||||
for(j = 0; j < ci->l_len; j++)
|
for(j = 0; j < ci->l_len; j++)
|
||||||
printf(" %05x", ci->l_data[j]);
|
printf(" %05x", ci->l_data[j]);
|
||||||
}
|
}
|
||||||
if (ci->f_len != 0) {
|
if (ci->f_code != 0) {
|
||||||
printf(" F:");
|
printf(" F: %05x", ci->f_code);
|
||||||
for(j = 0; j < ci->f_len; j++)
|
|
||||||
printf(" %05x", ci->f_data[j]);
|
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_unicode_data(CCInfo *tab)
|
void dump_data(CCInfo *tab)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
CCInfo *ci;
|
CCInfo *ci;
|
||||||
for(i = 0; i <= CHARCODE_MAX; i++) {
|
for(i = 0; i <= CHARCODE_MAX; i++) {
|
||||||
ci = &tab[i];
|
ci = &tab[i];
|
||||||
if (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0) {
|
if (ci->u_len != 0 || ci->l_len != 0 || ci->f_code != 0) {
|
||||||
dump_cc_info(ci, i);
|
dump_cc_info(ci, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -904,8 +886,8 @@ BOOL is_complicated_case(const CCInfo *ci)
|
||||||
{
|
{
|
||||||
return (ci->u_len > 1 || ci->l_len > 1 ||
|
return (ci->u_len > 1 || ci->l_len > 1 ||
|
||||||
(ci->u_len > 0 && ci->l_len > 0) ||
|
(ci->u_len > 0 && ci->l_len > 0) ||
|
||||||
(ci->f_len != ci->l_len) ||
|
(ci->f_code != 0) != ci->l_len ||
|
||||||
(memcmp(ci->f_data, ci->l_data, ci->f_len * sizeof(ci->f_data[0])) != 0));
|
(ci->f_code != 0 && ci->l_data[0] != ci->f_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef USE_TEST
|
#ifndef USE_TEST
|
||||||
|
@ -921,9 +903,9 @@ enum {
|
||||||
RUN_TYPE_UF_D1_EXT,
|
RUN_TYPE_UF_D1_EXT,
|
||||||
RUN_TYPE_U_EXT,
|
RUN_TYPE_U_EXT,
|
||||||
RUN_TYPE_LF_EXT,
|
RUN_TYPE_LF_EXT,
|
||||||
RUN_TYPE_UF_EXT2,
|
RUN_TYPE_U_EXT2,
|
||||||
RUN_TYPE_LF_EXT2,
|
RUN_TYPE_L_EXT2,
|
||||||
RUN_TYPE_UF_EXT3,
|
RUN_TYPE_U_EXT3,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -939,9 +921,9 @@ const char *run_type_str[] = {
|
||||||
"UF_D1_EXT",
|
"UF_D1_EXT",
|
||||||
"U_EXT",
|
"U_EXT",
|
||||||
"LF_EXT",
|
"LF_EXT",
|
||||||
"UF_EXT2",
|
"U_EXT2",
|
||||||
"LF_EXT2",
|
"L_EXT2",
|
||||||
"UF_EXT3",
|
"U_EXT3",
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -954,13 +936,6 @@ typedef struct {
|
||||||
int data_index; /* 'data' coming from the table */
|
int data_index; /* 'data' coming from the table */
|
||||||
} TableEntry;
|
} TableEntry;
|
||||||
|
|
||||||
static int simple_to_lower(CCInfo *tab, int c)
|
|
||||||
{
|
|
||||||
if (tab[c].l_len != 1)
|
|
||||||
return c;
|
|
||||||
return tab[c].l_data[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* code (17), len (7), type (4) */
|
/* code (17), len (7), type (4) */
|
||||||
|
|
||||||
void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
|
@ -974,15 +949,15 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
te->code = code;
|
te->code = code;
|
||||||
|
|
||||||
if (ci->l_len == 1 && ci->l_data[0] == code + 2 &&
|
if (ci->l_len == 1 && ci->l_data[0] == code + 2 &&
|
||||||
ci->f_len == 1 && ci->f_data[0] == ci->l_data[0] &&
|
ci->f_code == ci->l_data[0] &&
|
||||||
ci->u_len == 0 &&
|
ci->u_len == 0 &&
|
||||||
|
|
||||||
ci1->l_len == 1 && ci1->l_data[0] == code + 2 &&
|
ci1->l_len == 1 && ci1->l_data[0] == code + 2 &&
|
||||||
ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0] &&
|
ci1->f_code == ci1->l_data[0] &&
|
||||||
ci1->u_len == 1 && ci1->u_data[0] == code &&
|
ci1->u_len == 1 && ci1->u_data[0] == code &&
|
||||||
|
|
||||||
ci2->l_len == 0 &&
|
ci2->l_len == 0 &&
|
||||||
ci2->f_len == 0 &&
|
ci2->f_code == 0 &&
|
||||||
ci2->u_len == 1 && ci2->u_data[0] == code) {
|
ci2->u_len == 1 && ci2->u_data[0] == code) {
|
||||||
te->len = 3;
|
te->len = 3;
|
||||||
te->data = 0;
|
te->data = 0;
|
||||||
|
@ -997,7 +972,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
if (ci1->u_len != 1 ||
|
if (ci1->u_len != 1 ||
|
||||||
ci1->u_data[0] != ci->u_data[0] + len ||
|
ci1->u_data[0] != ci->u_data[0] + len ||
|
||||||
ci1->l_len != 0 ||
|
ci1->l_len != 0 ||
|
||||||
ci1->f_len != 1 || ci1->f_data[0] != ci1->u_data[0])
|
ci1->f_code != ci1->u_data[0])
|
||||||
break;
|
break;
|
||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
|
@ -1008,25 +983,21 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ci->l_len == 0 &&
|
if (ci->u_len == 2 && ci->u_data[1] == 0x399 &&
|
||||||
ci->u_len == 2 && ci->u_data[1] == 0x399 &&
|
ci->f_code == 0 && ci->l_len == 0) {
|
||||||
ci->f_len == 2 && ci->f_data[1] == 0x3B9 &&
|
|
||||||
ci->f_data[0] == simple_to_lower(tab, ci->u_data[0])) {
|
|
||||||
len = 1;
|
len = 1;
|
||||||
while (code + len <= CHARCODE_MAX) {
|
while (code + len <= CHARCODE_MAX) {
|
||||||
ci1 = &tab[code + len];
|
ci1 = &tab[code + len];
|
||||||
if (!(ci1->u_len == 2 &&
|
if (!(ci1->u_len == 2 &&
|
||||||
ci1->u_data[1] == ci->u_data[1] &&
|
ci1->u_data[1] == 0x399 &&
|
||||||
ci1->u_data[0] == ci->u_data[0] + len &&
|
ci1->u_data[0] == ci->u_data[0] + len &&
|
||||||
ci1->f_len == 2 &&
|
ci1->f_code == 0 &&
|
||||||
ci1->f_data[1] == ci->f_data[1] &&
|
|
||||||
ci1->f_data[0] == ci->f_data[0] + len &&
|
|
||||||
ci1->l_len == 0))
|
ci1->l_len == 0))
|
||||||
break;
|
break;
|
||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
te->len = len;
|
te->len = len;
|
||||||
te->type = RUN_TYPE_UF_EXT2;
|
te->type = RUN_TYPE_U_EXT2;
|
||||||
te->ext_data[0] = ci->u_data[0];
|
te->ext_data[0] = ci->u_data[0];
|
||||||
te->ext_data[1] = ci->u_data[1];
|
te->ext_data[1] = ci->u_data[1];
|
||||||
te->ext_len = 2;
|
te->ext_len = 2;
|
||||||
|
@ -1034,8 +1005,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ci->u_len == 2 && ci->u_data[1] == 0x399 &&
|
if (ci->u_len == 2 && ci->u_data[1] == 0x399 &&
|
||||||
ci->l_len == 1 &&
|
ci->l_len == 1 && ci->f_code == ci->l_data[0]) {
|
||||||
ci->f_len == 1 && ci->f_data[0] == ci->l_data[0]) {
|
|
||||||
len = 1;
|
len = 1;
|
||||||
while (code + len <= CHARCODE_MAX) {
|
while (code + len <= CHARCODE_MAX) {
|
||||||
ci1 = &tab[code + len];
|
ci1 = &tab[code + len];
|
||||||
|
@ -1044,7 +1014,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
ci1->u_data[0] == ci->u_data[0] + len &&
|
ci1->u_data[0] == ci->u_data[0] + len &&
|
||||||
ci1->l_len == 1 &&
|
ci1->l_len == 1 &&
|
||||||
ci1->l_data[0] == ci->l_data[0] + len &&
|
ci1->l_data[0] == ci->l_data[0] + len &&
|
||||||
ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0]))
|
ci1->f_code == ci1->l_data[0]))
|
||||||
break;
|
break;
|
||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
|
@ -1056,13 +1026,13 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ci->l_len == 1 && ci->u_len == 0 && ci->f_len == 0) {
|
if (ci->l_len == 1 && ci->u_len == 0 && ci->f_code == 0) {
|
||||||
len = 1;
|
len = 1;
|
||||||
while (code + len <= CHARCODE_MAX) {
|
while (code + len <= CHARCODE_MAX) {
|
||||||
ci1 = &tab[code + len];
|
ci1 = &tab[code + len];
|
||||||
if (!(ci1->l_len == 1 &&
|
if (!(ci1->l_len == 1 &&
|
||||||
ci1->l_data[0] == ci->l_data[0] + len &&
|
ci1->l_data[0] == ci->l_data[0] + len &&
|
||||||
ci1->u_len == 0 && ci1->f_len == 0))
|
ci1->u_len == 0 && ci1->f_code == 0))
|
||||||
break;
|
break;
|
||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
|
@ -1075,39 +1045,32 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
|
||||||
if (ci->l_len == 0 &&
|
if (ci->l_len == 0 &&
|
||||||
ci->u_len == 1 &&
|
ci->u_len == 1 &&
|
||||||
ci->u_data[0] < 0x1000 &&
|
ci->u_data[0] < 0x1000 &&
|
||||||
ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 0x20) {
|
ci->f_code == ci->u_data[0] + 0x20) {
|
||||||
te->len = 1;
|
te->len = 1;
|
||||||
te->type = RUN_TYPE_UF_D20;
|
te->type = RUN_TYPE_UF_D20;
|
||||||
te->data = ci->u_data[0];
|
te->data = ci->u_data[0];
|
||||||
} else if (ci->l_len == 0 &&
|
} else if (ci->l_len == 0 &&
|
||||||
ci->u_len == 1 &&
|
ci->u_len == 1 &&
|
||||||
ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 1) {
|
ci->f_code == ci->u_data[0] + 1) {
|
||||||
te->len = 1;
|
te->len = 1;
|
||||||
te->type = RUN_TYPE_UF_D1_EXT;
|
te->type = RUN_TYPE_UF_D1_EXT;
|
||||||
te->ext_data[0] = ci->u_data[0];
|
te->ext_data[0] = ci->u_data[0];
|
||||||
te->ext_len = 1;
|
te->ext_len = 1;
|
||||||
} else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_len == 2 &&
|
} else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_code == 0) {
|
||||||
ci->l_data[0] == ci->f_data[0] &&
|
|
||||||
ci->l_data[1] == ci->f_data[1]) {
|
|
||||||
te->len = 1;
|
te->len = 1;
|
||||||
te->type = RUN_TYPE_LF_EXT2;
|
te->type = RUN_TYPE_L_EXT2;
|
||||||
te->ext_data[0] = ci->l_data[0];
|
te->ext_data[0] = ci->l_data[0];
|
||||||
te->ext_data[1] = ci->l_data[1];
|
te->ext_data[1] = ci->l_data[1];
|
||||||
te->ext_len = 2;
|
te->ext_len = 2;
|
||||||
} else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 2 &&
|
} else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_code == 0) {
|
||||||
ci->f_data[0] == simple_to_lower(tab, ci->u_data[0]) &&
|
|
||||||
ci->f_data[1] == simple_to_lower(tab, ci->u_data[1])) {
|
|
||||||
te->len = 1;
|
te->len = 1;
|
||||||
te->type = RUN_TYPE_UF_EXT2;
|
te->type = RUN_TYPE_U_EXT2;
|
||||||
te->ext_data[0] = ci->u_data[0];
|
te->ext_data[0] = ci->u_data[0];
|
||||||
te->ext_data[1] = ci->u_data[1];
|
te->ext_data[1] = ci->u_data[1];
|
||||||
te->ext_len = 2;
|
te->ext_len = 2;
|
||||||
} else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 3 &&
|
} else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_code == 0) {
|
||||||
ci->f_data[0] == simple_to_lower(tab, ci->u_data[0]) &&
|
|
||||||
ci->f_data[1] == simple_to_lower(tab, ci->u_data[1]) &&
|
|
||||||
ci->f_data[2] == simple_to_lower(tab, ci->u_data[2])) {
|
|
||||||
te->len = 1;
|
te->len = 1;
|
||||||
te->type = RUN_TYPE_UF_EXT3;
|
te->type = RUN_TYPE_U_EXT3;
|
||||||
te->ext_data[0] = ci->u_data[0];
|
te->ext_data[0] = ci->u_data[0];
|
||||||
te->ext_data[1] = ci->u_data[1];
|
te->ext_data[1] = ci->u_data[1];
|
||||||
te->ext_data[2] = ci->u_data[2];
|
te->ext_data[2] = ci->u_data[2];
|
||||||
|
@ -1225,7 +1188,7 @@ void build_conv_table(CCInfo *tab)
|
||||||
te = conv_table;
|
te = conv_table;
|
||||||
for(code = 0; code <= CHARCODE_MAX; code++) {
|
for(code = 0; code <= CHARCODE_MAX; code++) {
|
||||||
ci = &tab[code];
|
ci = &tab[code];
|
||||||
if (ci->u_len == 0 && ci->l_len == 0 && ci->f_len == 0)
|
if (ci->u_len == 0 && ci->l_len == 0 && ci->f_code == 0)
|
||||||
continue;
|
continue;
|
||||||
assert(te - conv_table < countof(conv_table));
|
assert(te - conv_table < countof(conv_table));
|
||||||
find_run_type(te, tab, code);
|
find_run_type(te, tab, code);
|
||||||
|
@ -1281,7 +1244,7 @@ void build_conv_table(CCInfo *tab)
|
||||||
/* find the data index for ext_data */
|
/* find the data index for ext_data */
|
||||||
for(i = 0; i < conv_table_len; i++) {
|
for(i = 0; i < conv_table_len; i++) {
|
||||||
te = &conv_table[i];
|
te = &conv_table[i];
|
||||||
if (te->type == RUN_TYPE_UF_EXT3) {
|
if (te->type == RUN_TYPE_U_EXT3) {
|
||||||
int p, v;
|
int p, v;
|
||||||
v = 0;
|
v = 0;
|
||||||
for(j = 0; j < 3; j++) {
|
for(j = 0; j < 3; j++) {
|
||||||
|
@ -1295,8 +1258,8 @@ void build_conv_table(CCInfo *tab)
|
||||||
|
|
||||||
for(i = 0; i < conv_table_len; i++) {
|
for(i = 0; i < conv_table_len; i++) {
|
||||||
te = &conv_table[i];
|
te = &conv_table[i];
|
||||||
if (te->type == RUN_TYPE_LF_EXT2 ||
|
if (te->type == RUN_TYPE_L_EXT2 ||
|
||||||
te->type == RUN_TYPE_UF_EXT2 ||
|
te->type == RUN_TYPE_U_EXT2 ||
|
||||||
te->type == RUN_TYPE_U2L_399_EXT2) {
|
te->type == RUN_TYPE_U2L_399_EXT2) {
|
||||||
int p, v;
|
int p, v;
|
||||||
v = 0;
|
v = 0;
|
||||||
|
@ -1359,54 +1322,6 @@ void dump_case_conv_table(FILE *f)
|
||||||
fprintf(f, "\n};\n\n");
|
fprintf(f, "\n};\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static CCInfo *global_tab;
|
|
||||||
|
|
||||||
static int sp_cc_cmp(const void *p1, const void *p2)
|
|
||||||
{
|
|
||||||
CCInfo *c1 = &global_tab[*(const int *)p1];
|
|
||||||
CCInfo *c2 = &global_tab[*(const int *)p2];
|
|
||||||
if (c1->f_len < c2->f_len) {
|
|
||||||
return -1;
|
|
||||||
} else if (c2->f_len < c1->f_len) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return memcmp(c1->f_data, c2->f_data, sizeof(c1->f_data[0]) * c1->f_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dump the case special cases (multi character results which are
|
|
||||||
identical and need specific handling in lre_canonicalize() */
|
|
||||||
void dump_case_folding_special_cases(CCInfo *tab)
|
|
||||||
{
|
|
||||||
int i, len, j;
|
|
||||||
int *perm;
|
|
||||||
|
|
||||||
perm = malloc(sizeof(perm[0]) * (CHARCODE_MAX + 1));
|
|
||||||
for(i = 0; i <= CHARCODE_MAX; i++)
|
|
||||||
perm[i] = i;
|
|
||||||
global_tab = tab;
|
|
||||||
qsort(perm, CHARCODE_MAX + 1, sizeof(perm[0]), sp_cc_cmp);
|
|
||||||
for(i = 0; i <= CHARCODE_MAX;) {
|
|
||||||
if (tab[perm[i]].f_len <= 1) {
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
len = 1;
|
|
||||||
while ((i + len) <= CHARCODE_MAX && !sp_cc_cmp(&perm[i], &perm[i + len]))
|
|
||||||
len++;
|
|
||||||
|
|
||||||
if (len > 1) {
|
|
||||||
for(j = i; j < i + len; j++)
|
|
||||||
dump_cc_info(&tab[perm[j]], perm[j]);
|
|
||||||
}
|
|
||||||
i += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(perm);
|
|
||||||
global_tab = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int tabcmp(const int *tab1, const int *tab2, int n)
|
int tabcmp(const int *tab1, const int *tab2, int n)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -1433,7 +1348,7 @@ void compute_internal_props(void)
|
||||||
|
|
||||||
for(i = 0; i <= CHARCODE_MAX; i++) {
|
for(i = 0; i <= CHARCODE_MAX; i++) {
|
||||||
CCInfo *ci = &unicode_db[i];
|
CCInfo *ci = &unicode_db[i];
|
||||||
has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0);
|
has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_code != 0);
|
||||||
if (has_ul) {
|
if (has_ul) {
|
||||||
assert(get_prop(i, PROP_Cased));
|
assert(get_prop(i, PROP_Cased));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1448,10 +1363,10 @@ void compute_internal_props(void)
|
||||||
set_prop(i, PROP_Changes_When_Titlecased1,
|
set_prop(i, PROP_Changes_When_Titlecased1,
|
||||||
get_prop(i, PROP_Changes_When_Titlecased) ^ (ci->u_len != 0));
|
get_prop(i, PROP_Changes_When_Titlecased) ^ (ci->u_len != 0));
|
||||||
set_prop(i, PROP_Changes_When_Casefolded1,
|
set_prop(i, PROP_Changes_When_Casefolded1,
|
||||||
get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_len != 0));
|
get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_code != 0));
|
||||||
/* XXX: reduce table size (438 bytes) */
|
/* XXX: reduce table size (438 bytes) */
|
||||||
set_prop(i, PROP_Changes_When_NFKC_Casefolded1,
|
set_prop(i, PROP_Changes_When_NFKC_Casefolded1,
|
||||||
get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_len != 0));
|
get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_code != 0));
|
||||||
#if 0
|
#if 0
|
||||||
/* TEST */
|
/* TEST */
|
||||||
#define M(x) (1U << GCAT_ ## x)
|
#define M(x) (1U << GCAT_ ## x)
|
||||||
|
@ -1882,10 +1797,8 @@ void check_case_conv(void)
|
||||||
ci->u_len = 1;
|
ci->u_len = 1;
|
||||||
ci->u_data[0] = code;
|
ci->u_data[0] = code;
|
||||||
}
|
}
|
||||||
if (ci->f_len == 0) {
|
if (ci->f_code == 0)
|
||||||
ci->f_len = 1;
|
ci->f_code = code;
|
||||||
ci->f_data[0] = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = 0;
|
error = 0;
|
||||||
l = check_conv(res, code, 0);
|
l = check_conv(res, code, 0);
|
||||||
|
@ -1899,7 +1812,7 @@ void check_case_conv(void)
|
||||||
error++;
|
error++;
|
||||||
}
|
}
|
||||||
l = check_conv(res, code, 2);
|
l = check_conv(res, code, 2);
|
||||||
if (l != ci->f_len || tabcmp((int *)res, ci->f_data, l)) {
|
if (l != 1 || res[0] != ci->f_code) {
|
||||||
printf("ERROR: F\n");
|
printf("ERROR: F\n");
|
||||||
error++;
|
error++;
|
||||||
}
|
}
|
||||||
|
@ -3094,12 +3007,11 @@ int main(int argc, char **argv)
|
||||||
unicode_db_path);
|
unicode_db_path);
|
||||||
parse_prop_list(filename);
|
parse_prop_list(filename);
|
||||||
|
|
||||||
// dump_unicode_data(unicode_db);
|
// dump_data(unicode_db);
|
||||||
build_conv_table(unicode_db);
|
|
||||||
|
|
||||||
#ifdef DUMP_CASE_FOLDING_SPECIAL_CASES
|
build_conv_table(unicode_db);
|
||||||
dump_case_folding_special_cases(unicode_db);
|
|
||||||
#endif
|
// dump_table();
|
||||||
|
|
||||||
if (!outfilename) {
|
if (!outfilename) {
|
||||||
#ifdef USE_TEST
|
#ifdef USE_TEST
|
||||||
|
|
|
@ -72,7 +72,6 @@ DEF(Coptic, "Copt,Qaac")
|
||||||
DEF(Cuneiform, "Xsux")
|
DEF(Cuneiform, "Xsux")
|
||||||
DEF(Cypriot, "Cprt")
|
DEF(Cypriot, "Cprt")
|
||||||
DEF(Cyrillic, "Cyrl")
|
DEF(Cyrillic, "Cyrl")
|
||||||
DEF(Cypro_Minoan, "Cpmn")
|
|
||||||
DEF(Deseret, "Dsrt")
|
DEF(Deseret, "Dsrt")
|
||||||
DEF(Devanagari, "Deva")
|
DEF(Devanagari, "Deva")
|
||||||
DEF(Dives_Akuru, "Diak")
|
DEF(Dives_Akuru, "Diak")
|
||||||
|
@ -105,7 +104,6 @@ DEF(Javanese, "Java")
|
||||||
DEF(Kaithi, "Kthi")
|
DEF(Kaithi, "Kthi")
|
||||||
DEF(Kannada, "Knda")
|
DEF(Kannada, "Knda")
|
||||||
DEF(Katakana, "Kana")
|
DEF(Katakana, "Kana")
|
||||||
DEF(Kawi, "Kawi")
|
|
||||||
DEF(Kayah_Li, "Kali")
|
DEF(Kayah_Li, "Kali")
|
||||||
DEF(Kharoshthi, "Khar")
|
DEF(Kharoshthi, "Khar")
|
||||||
DEF(Khmer, "Khmr")
|
DEF(Khmer, "Khmr")
|
||||||
|
@ -140,7 +138,6 @@ DEF(Mro, "Mroo")
|
||||||
DEF(Multani, "Mult")
|
DEF(Multani, "Mult")
|
||||||
DEF(Myanmar, "Mymr")
|
DEF(Myanmar, "Mymr")
|
||||||
DEF(Nabataean, "Nbat")
|
DEF(Nabataean, "Nbat")
|
||||||
DEF(Nag_Mundari, "Nagm")
|
|
||||||
DEF(Nandinagari, "Nand")
|
DEF(Nandinagari, "Nand")
|
||||||
DEF(New_Tai_Lue, "Talu")
|
DEF(New_Tai_Lue, "Talu")
|
||||||
DEF(Newa, "Newa")
|
DEF(Newa, "Newa")
|
||||||
|
@ -157,7 +154,6 @@ DEF(Old_Persian, "Xpeo")
|
||||||
DEF(Old_Sogdian, "Sogo")
|
DEF(Old_Sogdian, "Sogo")
|
||||||
DEF(Old_South_Arabian, "Sarb")
|
DEF(Old_South_Arabian, "Sarb")
|
||||||
DEF(Old_Turkic, "Orkh")
|
DEF(Old_Turkic, "Orkh")
|
||||||
DEF(Old_Uyghur, "Ougr")
|
|
||||||
DEF(Oriya, "Orya")
|
DEF(Oriya, "Orya")
|
||||||
DEF(Osage, "Osge")
|
DEF(Osage, "Osge")
|
||||||
DEF(Osmanya, "Osma")
|
DEF(Osmanya, "Osma")
|
||||||
|
@ -196,11 +192,8 @@ DEF(Thai, "Thai")
|
||||||
DEF(Tibetan, "Tibt")
|
DEF(Tibetan, "Tibt")
|
||||||
DEF(Tifinagh, "Tfng")
|
DEF(Tifinagh, "Tfng")
|
||||||
DEF(Tirhuta, "Tirh")
|
DEF(Tirhuta, "Tirh")
|
||||||
DEF(Tangsa, "Tnsa")
|
|
||||||
DEF(Toto, "Toto")
|
|
||||||
DEF(Ugaritic, "Ugar")
|
DEF(Ugaritic, "Ugar")
|
||||||
DEF(Vai, "Vaii")
|
DEF(Vai, "Vaii")
|
||||||
DEF(Vithkuqi, "Vith")
|
|
||||||
DEF(Wancho, "Wcho")
|
DEF(Wancho, "Wcho")
|
||||||
DEF(Warang_Citi, "Wara")
|
DEF(Warang_Citi, "Wara")
|
||||||
DEF(Yezidi, "Yezi")
|
DEF(Yezidi, "Yezi")
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool.
|
# Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool.
|
||||||
|
|
||||||
tag-prefix = "libquickjs-dtp-sys-"
|
tag-prefix = "libquickjs-sys-"
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(clippy::missing_safety_doc)]
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ mod tests {
|
||||||
let value = JS_Eval(
|
let value = JS_Eval(
|
||||||
ctx,
|
ctx,
|
||||||
code.as_ptr(),
|
code.as_ptr(),
|
||||||
code_str.len() - 1,
|
(code_str.len() - 1) as u64,
|
||||||
script.as_ptr(),
|
script.as_ptr(),
|
||||||
JS_EVAL_TYPE_GLOBAL as i32,
|
JS_EVAL_TYPE_GLOBAL as i32,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool.
|
# Configuration for the [cargo-release](https://github.com/sunng87/cargo-release) tool.
|
||||||
|
|
||||||
pre-release-replacements = [
|
pre-release-replacements = [
|
||||||
{file="README.md", search="quick-js-dtp = .*", replace="{{crate_name}} = \"{{version}}\""},
|
{file="README.md", search="quick-js = .*", replace="{{crate_name}} = \"{{version}}\""},
|
||||||
]
|
]
|
||||||
|
|
||||||
tag-prefix = "quick-js-dtp-"
|
tag-prefix = "quick-js-"
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub fn run_compiled_function<'a>(
|
||||||
|
|
||||||
context.ensure_no_excpetion().map_err(|e| {
|
context.ensure_no_excpetion().map_err(|e| {
|
||||||
if let ExecutionError::Internal(msg) = e {
|
if let ExecutionError::Internal(msg) = e {
|
||||||
ExecutionError::Internal(format!("Could not evaluate compiled function: {msg}"))
|
ExecutionError::Internal(format!("Could not evaluate compiled function: {}", msg))
|
||||||
} else {
|
} else {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ pub fn to_bytecode(context: &ContextWrapper, compiled_func: &JsCompiledFunction)
|
||||||
*compiled_func.as_value().as_inner(),
|
*compiled_func.as_value().as_inner(),
|
||||||
q::JS_WRITE_OBJ_BYTECODE as i32,
|
q::JS_WRITE_OBJ_BYTECODE as i32,
|
||||||
);
|
);
|
||||||
let slice = std::slice::from_raw_parts(raw, len);
|
let slice = std::slice::from_raw_parts(raw, len as usize);
|
||||||
let data = slice.to_vec();
|
let data = slice.to_vec();
|
||||||
q::js_free(context.context, raw as *mut c_void);
|
q::js_free(context.context, raw as *mut c_void);
|
||||||
data
|
data
|
||||||
|
@ -122,6 +122,7 @@ pub mod tests {
|
||||||
"test_func.es",
|
"test_func.es",
|
||||||
);
|
);
|
||||||
let func = func_res
|
let func = func_res
|
||||||
|
.ok()
|
||||||
.expect("func compile failed")
|
.expect("func compile failed")
|
||||||
.try_into_compiled_function()
|
.try_into_compiled_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -131,6 +132,7 @@ pub mod tests {
|
||||||
|
|
||||||
let func2_res = from_bytecode(&ctx, &bytecode);
|
let func2_res = from_bytecode(&ctx, &bytecode);
|
||||||
let func2 = func2_res
|
let func2 = func2_res
|
||||||
|
.ok()
|
||||||
.expect("could not read bytecode")
|
.expect("could not read bytecode")
|
||||||
.try_into_compiled_function()
|
.try_into_compiled_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -155,6 +157,7 @@ pub mod tests {
|
||||||
"test_func.es",
|
"test_func.es",
|
||||||
);
|
);
|
||||||
let func = func_res
|
let func = func_res
|
||||||
|
.ok()
|
||||||
.expect("func compile failed")
|
.expect("func compile failed")
|
||||||
.try_into_compiled_function()
|
.try_into_compiled_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -163,6 +166,7 @@ pub mod tests {
|
||||||
assert!(!bytecode.is_empty());
|
assert!(!bytecode.is_empty());
|
||||||
let func2_res = from_bytecode(&ctx, &bytecode);
|
let func2_res = from_bytecode(&ctx, &bytecode);
|
||||||
let func2 = func2_res
|
let func2 = func2_res
|
||||||
|
.ok()
|
||||||
.expect("could not read bytecode")
|
.expect("could not read bytecode")
|
||||||
.try_into_compiled_function()
|
.try_into_compiled_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -187,7 +191,7 @@ pub mod tests {
|
||||||
"{the changes of me compil1ng a're slim to 0-0}",
|
"{the changes of me compil1ng a're slim to 0-0}",
|
||||||
"test_func_fail.es",
|
"test_func_fail.es",
|
||||||
);
|
);
|
||||||
func_res.expect_err("func compiled unexpectedly");
|
func_res.err().expect("func compiled unexpectedly");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -196,6 +200,7 @@ pub mod tests {
|
||||||
|
|
||||||
let func_res = compile(&ctx, "let abcdef = 1;", "test_func_runfail.es");
|
let func_res = compile(&ctx, "let abcdef = 1;", "test_func_runfail.es");
|
||||||
let func = func_res
|
let func = func_res
|
||||||
|
.ok()
|
||||||
.expect("func compile failed")
|
.expect("func compile failed")
|
||||||
.try_into_compiled_function()
|
.try_into_compiled_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -227,7 +232,9 @@ pub mod tests {
|
||||||
|
|
||||||
assert_eq!(1, func2.as_value().get_ref_count());
|
assert_eq!(1, func2.as_value().get_ref_count());
|
||||||
|
|
||||||
let _run_res2 = run_compiled_function(&func2).expect_err("run 2 succeeded unexpectedly");
|
let _run_res2 = run_compiled_function(&func2)
|
||||||
|
.err()
|
||||||
|
.expect("run 2 succeeded unexpectedly");
|
||||||
|
|
||||||
assert_eq!(1, func2.as_value().get_ref_count());
|
assert_eq!(1, func2.as_value().get_ref_count());
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,13 +221,13 @@ pub(super) fn serialize_value(
|
||||||
q::JS_NewStringLen(
|
q::JS_NewStringLen(
|
||||||
context,
|
context,
|
||||||
bigint_string.as_ptr() as *const c_char,
|
bigint_string.as_ptr() as *const c_char,
|
||||||
bigint_string.len(),
|
bigint_string.len() as q::size_t,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let s = DroppableValue::new(s, |&mut s| unsafe {
|
let s = DroppableValue::new(s, |&mut s| unsafe {
|
||||||
q::JS_FreeValue(context, s);
|
q::JS_FreeValue(context, s);
|
||||||
});
|
});
|
||||||
if s.tag != TAG_STRING {
|
if (*s).tag != TAG_STRING {
|
||||||
return Err(ValueError::Internal(
|
return Err(ValueError::Internal(
|
||||||
"Could not construct String object needed to create BigInt object".into(),
|
"Could not construct String object needed to create BigInt object".into(),
|
||||||
));
|
));
|
||||||
|
@ -451,23 +451,20 @@ pub(super) fn deserialize_value(
|
||||||
q::JS_FreeValue(context, date_constructor);
|
q::JS_FreeValue(context, date_constructor);
|
||||||
};
|
};
|
||||||
|
|
||||||
let timestamp_ms = if timestamp_raw.tag == TAG_FLOAT64 {
|
let res = if timestamp_raw.tag == TAG_FLOAT64 {
|
||||||
(unsafe { timestamp_raw.u.float64 } as i64)
|
let f = unsafe { timestamp_raw.u.float64 } as i64;
|
||||||
|
let datetime = chrono::Utc.timestamp_millis(f);
|
||||||
|
Ok(JsValue::Date(datetime))
|
||||||
} else if timestamp_raw.tag == TAG_INT {
|
} else if timestamp_raw.tag == TAG_INT {
|
||||||
(unsafe { timestamp_raw.u.int32 } as i64)
|
let f = unsafe { timestamp_raw.u.int32 } as i64;
|
||||||
|
let datetime = chrono::Utc.timestamp_millis(f);
|
||||||
|
Ok(JsValue::Date(datetime))
|
||||||
} else {
|
} else {
|
||||||
return Err(ValueError::Internal(
|
Err(ValueError::Internal(
|
||||||
"Could not convert 'Date' instance to timestamp".into(),
|
"Could not convert 'Date' instance to timestamp".into(),
|
||||||
));
|
))
|
||||||
};
|
};
|
||||||
|
return res;
|
||||||
return chrono::Utc
|
|
||||||
.timestamp_millis_opt(timestamp_ms)
|
|
||||||
.single()
|
|
||||||
.map(JsValue::Date)
|
|
||||||
.ok_or(ValueError::Internal(
|
|
||||||
"Could not convert 'Date' instance to timestamp".into(),
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
unsafe { q::JS_FreeValue(context, date_constructor) };
|
unsafe { q::JS_FreeValue(context, date_constructor) };
|
||||||
}
|
}
|
||||||
|
@ -505,6 +502,9 @@ pub(super) fn deserialize_value(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x => Err(ValueError::Internal(format!("Unhandled JS_TAG value: {x}"))),
|
x => Err(ValueError::Internal(format!(
|
||||||
|
"Unhandled JS_TAG value: {}",
|
||||||
|
x
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ use crate::{
|
||||||
|
|
||||||
use value::{JsFunction, OwnedJsObject};
|
use value::{JsFunction, OwnedJsObject};
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub use value::{JsCompiledFunction, OwnedJsValue};
|
pub use value::{JsCompiledFunction, OwnedJsValue};
|
||||||
|
|
||||||
// JS_TAG_* constants from quickjs.
|
// JS_TAG_* constants from quickjs.
|
||||||
|
@ -270,11 +269,13 @@ impl<'a> OwnedObjectRef<'a> {
|
||||||
|
|
||||||
if raw.tag == TAG_EXCEPTION {
|
if raw.tag == TAG_EXCEPTION {
|
||||||
Err(ExecutionError::Internal(format!(
|
Err(ExecutionError::Internal(format!(
|
||||||
"Exception while getting property '{name}'"
|
"Exception while getting property '{}'",
|
||||||
|
name
|
||||||
)))
|
)))
|
||||||
} else if raw.tag == TAG_UNDEFINED {
|
} else if raw.tag == TAG_UNDEFINED {
|
||||||
Err(ExecutionError::Internal(format!(
|
Err(ExecutionError::Internal(format!(
|
||||||
"Property '{name}' not found"
|
"Property '{}' not found",
|
||||||
|
name
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(OwnedValueRef::new(self.value.context, raw))
|
Ok(OwnedValueRef::new(self.value.context, raw))
|
||||||
|
@ -680,10 +681,10 @@ impl ContextWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a global JS function that is backed by a Rust function or closure.
|
/// Add a global JS function that is backed by a Rust function or closure.
|
||||||
pub fn create_callback<F>(
|
pub fn create_callback<'a, F>(
|
||||||
&self,
|
&'a self,
|
||||||
callback: impl Callback<F> + 'static,
|
callback: impl Callback<F> + 'static,
|
||||||
) -> Result<JsFunction, ExecutionError> {
|
) -> Result<JsFunction<'a>, ExecutionError> {
|
||||||
let argcount = callback.argument_count() as i32;
|
let argcount = callback.argument_count() as i32;
|
||||||
|
|
||||||
let context = self.context;
|
let context = self.context;
|
||||||
|
@ -723,8 +724,8 @@ impl ContextWrapper {
|
||||||
Ok(f)
|
Ok(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_callback<F>(
|
pub fn add_callback<'a, F>(
|
||||||
&self,
|
&'a self,
|
||||||
name: &str,
|
name: &str,
|
||||||
callback: impl Callback<F> + 'static,
|
callback: impl Callback<F> + 'static,
|
||||||
) -> Result<(), ExecutionError> {
|
) -> Result<(), ExecutionError> {
|
||||||
|
|
|
@ -432,7 +432,8 @@ impl<'a> OwnedJsObject<'a> {
|
||||||
|
|
||||||
if tag.is_exception() {
|
if tag.is_exception() {
|
||||||
Err(ExecutionError::Internal(format!(
|
Err(ExecutionError::Internal(format!(
|
||||||
"Exception while getting property '{name}'"
|
"Exception while getting property '{}'",
|
||||||
|
name
|
||||||
)))
|
)))
|
||||||
} else if tag.is_undefined() {
|
} else if tag.is_undefined() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -443,7 +444,7 @@ impl<'a> OwnedJsObject<'a> {
|
||||||
|
|
||||||
pub fn property_require(&self, name: &str) -> Result<OwnedJsValue<'a>, ExecutionError> {
|
pub fn property_require(&self, name: &str) -> Result<OwnedJsValue<'a>, ExecutionError> {
|
||||||
self.property(name)?
|
self.property(name)?
|
||||||
.ok_or_else(|| ExecutionError::Internal(format!("Property '{name}' not found")))
|
.ok_or_else(|| ExecutionError::Internal(format!("Property '{}' not found", name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine if the object is a promise by checking the presence of
|
/// Determine if the object is a promise by checking the presence of
|
||||||
|
|
|
@ -104,7 +104,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
|
fn call(&self, args: Vec<JsValue>) -> Result<Result<JsValue, String>, ValueError> {
|
||||||
if !args.is_empty() {
|
if args.len() != 0 {
|
||||||
return Ok(Err(format!(
|
return Ok(Err(format!(
|
||||||
"Invalid argument count: Expected 0, got {}",
|
"Invalid argument count: Expected 0, got {}",
|
||||||
args.len(),
|
args.len(),
|
||||||
|
@ -124,7 +124,7 @@ impl_callback![
|
||||||
5: (A1, A2, A3, A4, A5,),
|
5: (A1, A2, A3, A4, A5,),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// A wrapper around [`Vec<JsValue>`], used for vararg callbacks.
|
/// A wrapper around Vec<JsValue>, used for vararg callbacks.
|
||||||
///
|
///
|
||||||
/// To create a callback with a variable number of arguments, a callback closure
|
/// To create a callback with a variable number of arguments, a callback closure
|
||||||
/// must take a single `Arguments` argument.
|
/// must take a single `Arguments` argument.
|
||||||
|
|
|
@ -28,7 +28,7 @@ impl std::fmt::Display for Level {
|
||||||
Warn => "warn",
|
Warn => "warn",
|
||||||
Error => "error",
|
Error => "error",
|
||||||
};
|
};
|
||||||
write!(f, "{v}")
|
write!(f, "{}", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ mod log {
|
||||||
.map(print_value)
|
.map(print_value)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
format!("[{parts}]")
|
format!("[{}]", parts)
|
||||||
}
|
}
|
||||||
JsValue::Object(map) => {
|
JsValue::Object(map) => {
|
||||||
let parts = map
|
let parts = map
|
||||||
|
@ -99,7 +99,7 @@ mod log {
|
||||||
.map(|(key, value)| format!("{}: {}", key, print_value(value)))
|
.map(|(key, value)| format!("{}: {}", key, print_value(value)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
format!("{{{parts}}}")
|
format!("{{{}}}", parts)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
JsValue::Date(v) => v.to_string(),
|
JsValue::Date(v) => v.to_string(),
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -32,7 +32,7 @@
|
||||||
//! "#).unwrap();
|
//! "#).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![allow(dead_code, clippy::missing_safety_doc)]
|
#![allow(dead_code)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
mod bindings;
|
mod bindings;
|
||||||
|
@ -52,7 +52,6 @@ pub use self::{
|
||||||
|
|
||||||
/// Error on Javascript execution.
|
/// Error on Javascript execution.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum ExecutionError {
|
pub enum ExecutionError {
|
||||||
/// Code to be executed contained zero-bytes.
|
/// Code to be executed contained zero-bytes.
|
||||||
InputWithZeroBytes,
|
InputWithZeroBytes,
|
||||||
|
@ -64,6 +63,8 @@ pub enum ExecutionError {
|
||||||
Exception(JsValue),
|
Exception(JsValue),
|
||||||
/// JS Runtime exceeded the memory limit.
|
/// JS Runtime exceeded the memory limit.
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
|
#[doc(hidden)]
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ExecutionError {
|
impl fmt::Display for ExecutionError {
|
||||||
|
@ -72,9 +73,10 @@ impl fmt::Display for ExecutionError {
|
||||||
match self {
|
match self {
|
||||||
InputWithZeroBytes => write!(f, "Invalid script input: code contains zero byte (\\0)"),
|
InputWithZeroBytes => write!(f, "Invalid script input: code contains zero byte (\\0)"),
|
||||||
Conversion(e) => e.fmt(f),
|
Conversion(e) => e.fmt(f),
|
||||||
Internal(e) => write!(f, "Internal error: {e}"),
|
Internal(e) => write!(f, "Internal error: {}", e),
|
||||||
Exception(e) => write!(f, "{e:?}"),
|
Exception(e) => write!(f, "{:?}", e),
|
||||||
OutOfMemory => write!(f, "Out of memory: runtime memory limit exceeded"),
|
OutOfMemory => write!(f, "Out of memory: runtime memory limit exceeded"),
|
||||||
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +91,6 @@ impl From<ValueError> for ExecutionError {
|
||||||
|
|
||||||
/// Error on context creation.
|
/// Error on context creation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum ContextError {
|
pub enum ContextError {
|
||||||
/// Runtime could not be created.
|
/// Runtime could not be created.
|
||||||
RuntimeCreationFailed,
|
RuntimeCreationFailed,
|
||||||
|
@ -97,6 +98,8 @@ pub enum ContextError {
|
||||||
ContextCreationFailed,
|
ContextCreationFailed,
|
||||||
/// Execution error while building.
|
/// Execution error while building.
|
||||||
Execution(ExecutionError),
|
Execution(ExecutionError),
|
||||||
|
#[doc(hidden)]
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ContextError {
|
impl fmt::Display for ContextError {
|
||||||
|
@ -106,6 +109,7 @@ impl fmt::Display for ContextError {
|
||||||
RuntimeCreationFailed => write!(f, "Could not create runtime"),
|
RuntimeCreationFailed => write!(f, "Could not create runtime"),
|
||||||
ContextCreationFailed => write!(f, "Could not create context"),
|
ContextCreationFailed => write!(f, "Could not create context"),
|
||||||
Execution(e) => e.fmt(f),
|
Execution(e) => e.fmt(f),
|
||||||
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
587
src/tests.rs
587
src/tests.rs
|
@ -1,4 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, convert::TryInto};
|
||||||
|
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -71,7 +73,11 @@ fn test_eval_pass() {
|
||||||
];
|
];
|
||||||
|
|
||||||
for (index, (code, res)) in obj_cases.into_iter().enumerate() {
|
for (index, (code, res)) in obj_cases.into_iter().enumerate() {
|
||||||
let full_code = format!("var v{index} = {code}; v{index}");
|
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(&full_code), res,);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +283,7 @@ fn test_callback() {
|
||||||
c.add_callback("cb1", |flag: bool| !flag).unwrap();
|
c.add_callback("cb1", |flag: bool| !flag).unwrap();
|
||||||
assert_eq!(c.eval("cb1(true)").unwrap(), JsValue::Bool(false),);
|
assert_eq!(c.eval("cb1(true)").unwrap(), JsValue::Bool(false),);
|
||||||
|
|
||||||
c.add_callback("concat2", |a: String, b: String| format!("{a}{b}"))
|
c.add_callback("concat2", |a: String, b: String| format!("{}{}", a, b))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.eval(r#"concat2("abc", "def")"#).unwrap(),
|
c.eval(r#"concat2("abc", "def")"#).unwrap(),
|
||||||
|
@ -480,7 +486,7 @@ fn chrono_serialize() {
|
||||||
let now_millis = now.timestamp_millis();
|
let now_millis = now.timestamp_millis();
|
||||||
|
|
||||||
let timestamp = c
|
let timestamp = c
|
||||||
.call_function("dateToTimestamp", vec![JsValue::Date(now)])
|
.call_function("dateToTimestamp", vec![JsValue::Date(now.clone())])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(timestamp, JsValue::Float(now_millis as f64));
|
assert_eq!(timestamp, JsValue::Float(now_millis as f64));
|
||||||
|
@ -494,7 +500,7 @@ fn chrono_deserialize() {
|
||||||
let c = build_context();
|
let c = build_context();
|
||||||
|
|
||||||
let value = c.eval(" new Date(1234567555) ").unwrap();
|
let value = c.eval(" new Date(1234567555) ").unwrap();
|
||||||
let datetime = chrono::Utc.timestamp_millis_opt(1234567555).unwrap();
|
let datetime = chrono::Utc.timestamp_millis(1234567555);
|
||||||
|
|
||||||
assert_eq!(value, JsValue::Date(datetime));
|
assert_eq!(value, JsValue::Date(datetime));
|
||||||
}
|
}
|
||||||
|
@ -506,8 +512,8 @@ fn chrono_roundtrip() {
|
||||||
|
|
||||||
c.eval(" function identity(x) { return x; } ").unwrap();
|
c.eval(" function identity(x) { return x; } ").unwrap();
|
||||||
let d = chrono::Utc::now();
|
let d = chrono::Utc::now();
|
||||||
let td = JsValue::Date(d);
|
let td = JsValue::Date(d.clone());
|
||||||
let td2 = c.call_function("identity", vec![td]).unwrap();
|
let td2 = c.call_function("identity", vec![td.clone()]).unwrap();
|
||||||
let d2 = if let JsValue::Date(x) = td2 {
|
let d2 = if let JsValue::Date(x) = td2 {
|
||||||
x
|
x
|
||||||
} else {
|
} else {
|
||||||
|
@ -578,308 +584,302 @@ fn test_bigint_serialize_bigint() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "patch-dateparser")]
|
#[test]
|
||||||
mod dateparser {
|
fn test_console() {
|
||||||
use super::*;
|
use console::Level;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use rstest::rstest;
|
let messages = Arc::new(Mutex::new(Vec::<(Level, Vec<JsValue>)>::new()));
|
||||||
|
|
||||||
#[test]
|
let m = messages.clone();
|
||||||
fn test_console() {
|
let c = Context::builder()
|
||||||
use console::Level;
|
.console(move |level: Level, args: Vec<JsValue>| {
|
||||||
use std::sync::{Arc, Mutex};
|
m.lock().unwrap().push((level, args));
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let messages = Arc::new(Mutex::new(Vec::<(Level, Vec<JsValue>)>::new()));
|
c.eval(
|
||||||
|
r#"
|
||||||
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.log("hi");
|
||||||
console.error(false);
|
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)]),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_global_setter() {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
ctx.set_global("a", "a").unwrap();
|
||||||
|
ctx.eval("a + 1").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test suite was taken from V8
|
||||||
|
// Source: https://github.com/v8/v8/blob/9433ad119aadfe10d60935029195c31f25ab8624/test/mjsunit/date-parse.js
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
// testCasesES5Misc
|
||||||
|
#[case("2000-01-01T08:00:00.000Z", 946713600000)]
|
||||||
|
#[case("2000-01-01T08:00:00Z", 946713600000)]
|
||||||
|
#[case("2000-01-01T08:00Z", 946713600000)]
|
||||||
|
#[case("2000-01T08:00:00.000Z", 946713600000)]
|
||||||
|
#[case("2000T08:00:00.000Z", 946713600000)]
|
||||||
|
#[case("2000T08:00Z", 946713600000)]
|
||||||
|
#[case("2000-01T00:00:00.000-08:00", 946713600000)]
|
||||||
|
#[case("2000-01T08:00:00.001Z", 946713600001)]
|
||||||
|
#[case("2000-01T08:00:00.099Z", 946713600099)]
|
||||||
|
#[case("2000-01T08:00:00.999Z", 946713600999)]
|
||||||
|
#[case("2000-01T00:00:00.001-08:00", 946713600001)]
|
||||||
|
#[case("2000-01-01T24:00Z", 946771200000)]
|
||||||
|
#[case("2000-01-01T24:00:00Z", 946771200000)]
|
||||||
|
#[case("2000-01-01T24:00:00.000Z", 946771200000)]
|
||||||
|
fn test_date_iso(#[case] input: &str, #[case] expect: i64) {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
let res = ctx
|
||||||
|
.eval(&format!(r#"new Date("{}").getTime()"#, input))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let m = messages.lock().unwrap();
|
let n: f64 = res.try_into().unwrap();
|
||||||
|
assert_eq!(n, expect as f64);
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(
|
#[rstest]
|
||||||
*m,
|
#[case("Sat, 01-Jan-2000 08:00:00 GMT", 946713600000)]
|
||||||
vec![
|
#[case("Sat, 01-Jan-2000 08:00:00 GMT+0", 946713600000)]
|
||||||
(Level::Log, vec![JsValue::from("hi")]),
|
#[case("Sat, 01-Jan-2000 08:00:00 GMT+00", 946713600000)]
|
||||||
(Level::Error, vec![JsValue::from(false)]),
|
#[case("Sat, 01-Jan-2000 08:00:00 GMT+000", 946713600000)]
|
||||||
]
|
#[case("Sat, 01-Jan-2000 08:00:00 GMT+0000", 946713600000)]
|
||||||
);
|
#[case("Sat, 01-Jan-2000 08:00:00 GMT+00:00", 946713600000)]
|
||||||
}
|
#[case("Sat, 01 Jan 2000 08:00:00 GMT", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 08:00:00 GMT", 946713600000)]
|
||||||
|
#[case("01 Jan 00 08:00 -0000", 946713600000)]
|
||||||
|
#[case("01 Jan 00 08:00 +0000", 946713600000)]
|
||||||
|
fn test_date_gmt(#[case] input: &str, #[case] expect: i64) {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
let res = ctx
|
||||||
|
.eval(&format!(r#"new Date("{}").getTime()"#, input))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[test]
|
let n: f64 = res.try_into().unwrap();
|
||||||
fn test_global_setter() {
|
assert_eq!(n, expect as f64);
|
||||||
let ctx = Context::new().unwrap();
|
}
|
||||||
ctx.set_global("a", "a").unwrap();
|
|
||||||
ctx.eval("a + 1").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test suite was taken from V8
|
#[rstest]
|
||||||
// Source: https://github.com/v8/v8/blob/9433ad119aadfe10d60935029195c31f25ab8624/test/mjsunit/date-parse.js
|
// EST (-5:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 03:00:00 UTC-0500", 946713600000)]
|
||||||
|
#[case("Sat, 01-Jan-2000 03:00:00 UTC-05:00", 946713600000)]
|
||||||
|
#[case("Sat, 01-Jan-2000 03:00:00 EST", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 03:00:00 EST", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 03:00:00 EST", 946713600000)]
|
||||||
|
#[case("01 Jan 00 03:00 -0500", 946713600000)]
|
||||||
|
// EDT (-4:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 04:00:00 EDT", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 04:00:00 EDT", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 04:00:00 EDT", 946713600000)]
|
||||||
|
#[case("01 Jan 00 04:00 -0400", 946713600000)]
|
||||||
|
// CST (-6:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 02:00:00 CST", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 02:00:00 CST", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 02:00:00 CST", 946713600000)]
|
||||||
|
#[case("01 Jan 00 02:00 -0600", 946713600000)]
|
||||||
|
// CDT (-5:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 03:00:00 CDT", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 03:00:00 CDT", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 03:00:00 CDT", 946713600000)]
|
||||||
|
#[case("01 Jan 00 03:00 -0500", 946713600000)]
|
||||||
|
// MST (-7:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 01:00:00 MST", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 01:00:00 MST", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 01:00:00 MST", 946713600000)]
|
||||||
|
#[case("01 Jan 00 01:00 -0700", 946713600000)]
|
||||||
|
// MDT (-6:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 02:00:00 MDT", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 02:00:00 MDT", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 02:00:00 MDT", 946713600000)]
|
||||||
|
#[case("01 Jan 00 02:00 -0600", 946713600000)]
|
||||||
|
// PST (-8:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 00:00:00 PST", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 00:00:00 PST", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 00:00:00 PST", 946713600000)]
|
||||||
|
#[case("01 Jan 00 00:00 -0800", 946713600000)]
|
||||||
|
#[case("Sat, 01-Jan-2000 PST", 946713600000)]
|
||||||
|
// PDT (-7:00)
|
||||||
|
#[case("Sat, 01-Jan-2000 01:00:00 PDT", 946713600000)]
|
||||||
|
#[case("Sat, 01 Jan 2000 01:00:00 PDT", 946713600000)]
|
||||||
|
#[case("Saturday, 01-Jan-00 01:00:00 PDT", 946713600000)]
|
||||||
|
#[case("01 Jan 00 01:00 -0700", 946713600000)]
|
||||||
|
fn test_date_tz(#[case] input: &str, #[case] expect: i64) {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
let res = ctx
|
||||||
|
.eval(&format!(r#"new Date("{}").getTime()"#, input))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
#[rstest]
|
let n: f64 = res.try_into().unwrap();
|
||||||
// testCasesES5Misc
|
assert_eq!(n, expect as f64);
|
||||||
#[case("2000-01-01T08:00:00.000Z", 946713600000)]
|
}
|
||||||
#[case("2000-01-01T08:00:00Z", 946713600000)]
|
|
||||||
#[case("2000-01-01T08:00Z", 946713600000)]
|
#[rstest]
|
||||||
#[case("2000-01T08:00:00.000Z", 946713600000)]
|
// Special handling for years in the [0, 100) range.
|
||||||
#[case("2000T08:00:00.000Z", 946713600000)]
|
#[case("Sat, 01 Jan 0 08:00:00 UT", 946713600000)] // year 2000
|
||||||
#[case("2000T08:00Z", 946713600000)]
|
#[case("Sat, 01 Jan 49 08:00:00 UT", 2493100800000)] // year 2049
|
||||||
#[case("2000-01T00:00:00.000-08:00", 946713600000)]
|
#[case("Sat, 01 Jan 50 08:00:00 UT", -631123200000)] // year 1950
|
||||||
#[case("2000-01T08:00:00.001Z", 946713600001)]
|
#[case("Sat, 01 Jan 99 08:00:00 UT", 915177600000)] // year 1999
|
||||||
#[case("2000-01T08:00:00.099Z", 946713600099)]
|
#[case("Sat, 01 Jan 100 08:00:00 UT", -59011430400000)] // year 100
|
||||||
#[case("2000-01T08:00:00.999Z", 946713600999)]
|
// Test PM after time.
|
||||||
#[case("2000-01T00:00:00.001-08:00", 946713600001)]
|
#[case("Sat, 01-Jan-2000 08:00 PM UT", 946756800000)]
|
||||||
#[case("2000-01-01T24:00Z", 946771200000)]
|
#[case("Sat, 01 Jan 2000 08:00 PM UT", 946756800000)]
|
||||||
#[case("2000-01-01T24:00:00Z", 946771200000)]
|
#[case("Jan 01 2000 08:00 PM UT", 946756800000)]
|
||||||
#[case("2000-01-01T24:00:00.000Z", 946771200000)]
|
#[case("Jan 01 08:00 PM UT 2000", 946756800000)]
|
||||||
fn test_date_iso(#[case] input: &str, #[case] expect: i64) {
|
#[case("Saturday, 01-Jan-00 08:00 PM UT", 946756800000)]
|
||||||
|
#[case("01 Jan 00 08:00 PM +0000", 946756800000)]
|
||||||
|
fn test_date_special(#[case] input: &str, #[case] expect: i64) {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
let res = ctx
|
||||||
|
.eval(&format!(r#"new Date("{}").getTime()"#, input))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let n: f64 = res.try_into().unwrap();
|
||||||
|
assert_eq!(n, expect as f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("2000-01-01TZ")]
|
||||||
|
#[case("2000-01-01T60Z")]
|
||||||
|
#[case("2000-01-01T60:60Z")]
|
||||||
|
#[case("2000-01-0108:00Z")]
|
||||||
|
#[case("2000-01-01T08Z")]
|
||||||
|
#[case("2000-01-01T24:01")]
|
||||||
|
#[case("2000-01-01T24:00:01")]
|
||||||
|
#[case("2000-01-01T24:00:00.001")]
|
||||||
|
#[case("2000-01-01T24:00:00.999Z")]
|
||||||
|
#[case("May 25 2008 1:30 (PM)) UTC")]
|
||||||
|
#[case("May 25 2008 1:30( )AM (PM)")]
|
||||||
|
#[case("a1")]
|
||||||
|
#[case("nasfdjklsfjoaifg1")]
|
||||||
|
#[case("x_2")]
|
||||||
|
#[case("May 25 2008 AAA (GMT)")]
|
||||||
|
fn test_date_invalid(#[case] input: &str) {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
let res = ctx
|
||||||
|
.eval(&format!(r#"new Date("{}").getTime()"#, input))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let n: f64 = res.try_into().unwrap();
|
||||||
|
assert!(n.is_nan(), "got: {}", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_date_ut() {
|
||||||
|
let test_cases_ut = vec![
|
||||||
|
"Sat, 01-Jan-2000 08:00:00 UT",
|
||||||
|
"Sat, 01 Jan 2000 08:00:00 UT",
|
||||||
|
"Jan 01 2000 08:00:00 UT",
|
||||||
|
"Jan 01 08:00:00 UT 2000",
|
||||||
|
"Saturday, 01-Jan-00 08:00:00 UT",
|
||||||
|
"01 Jan 00 08:00 +0000",
|
||||||
|
// Ignore weekdays.
|
||||||
|
"Mon, 01 Jan 2000 08:00:00 UT",
|
||||||
|
"Tue, 01 Jan 2000 08:00:00 UT",
|
||||||
|
// Ignore prefix that is not part of a date.
|
||||||
|
"[Saturday] Jan 01 08:00:00 UT 2000",
|
||||||
|
"Ignore all of this stuff because it is annoying 01 Jan 2000 08:00:00 UT",
|
||||||
|
"[Saturday] Jan 01 2000 08:00:00 UT",
|
||||||
|
"All of this stuff is really annnoying, so it will be ignored Jan 01 2000 08:00:00 UT",
|
||||||
|
// If the three first letters of the month is a
|
||||||
|
// month name we are happy - ignore the rest.
|
||||||
|
"Sat, 01-Janisamonth-2000 08:00:00 UT",
|
||||||
|
"Sat, 01 Janisamonth 2000 08:00:00 UT",
|
||||||
|
"Janisamonth 01 2000 08:00:00 UT",
|
||||||
|
"Janisamonth 01 08:00:00 UT 2000",
|
||||||
|
"Saturday, 01-Janisamonth-00 08:00:00 UT",
|
||||||
|
"01 Janisamonth 00 08:00 +0000",
|
||||||
|
// Allow missing space between month and day.
|
||||||
|
"Janisamonthandtherestisignored01 2000 08:00:00 UT",
|
||||||
|
"Jan01 2000 08:00:00 UT",
|
||||||
|
// Allow year/month/day format.
|
||||||
|
"Sat, 2000/01/01 08:00:00 UT",
|
||||||
|
// Allow month/day/year format.
|
||||||
|
"Sat, 01/01/2000 08:00:00 UT",
|
||||||
|
// Allow month/day year format.
|
||||||
|
"Sat, 01/01 2000 08:00:00 UT",
|
||||||
|
// Allow comma instead of space after day, month and year.
|
||||||
|
"Sat, 01,Jan,2000,08:00:00 UT",
|
||||||
|
// Seconds are optional.
|
||||||
|
"Sat, 01-Jan-2000 08:00 UT",
|
||||||
|
"Sat, 01 Jan 2000 08:00 UT",
|
||||||
|
"Jan 01 2000 08:00 UT",
|
||||||
|
"Jan 01 08:00 UT 2000",
|
||||||
|
"Saturday, 01-Jan-00 08:00 UT",
|
||||||
|
"01 Jan 00 08:00 +0000",
|
||||||
|
// Allow AM/PM after the time.
|
||||||
|
"Sat, 01-Jan-2000 08:00 AM UT",
|
||||||
|
"Sat, 01 Jan 2000 08:00 AM UT",
|
||||||
|
"Jan 01 2000 08:00 AM UT",
|
||||||
|
"Jan 01 08:00 AM UT 2000",
|
||||||
|
"Saturday, 01-Jan-00 08:00 AM UT",
|
||||||
|
"01 Jan 00 08:00 AM +0000",
|
||||||
|
// White space and stuff in parenthesis is
|
||||||
|
// apparently allowed in most places where white
|
||||||
|
// space is allowed.
|
||||||
|
" Sat, 01-Jan-2000 08:00:00 UT ",
|
||||||
|
" Sat, 01 Jan 2000 08:00:00 UT ",
|
||||||
|
" Saturday, 01-Jan-00 08:00:00 UT ",
|
||||||
|
" 01 Jan 00 08:00 +0000 ",
|
||||||
|
" ()(Sat, 01-Jan-2000) Sat, 01-Jan-2000 08:00:00 UT ",
|
||||||
|
" Sat()(Sat, 01-Jan-2000)01 Jan 2000 08:00:00 UT ",
|
||||||
|
" Sat,(02)01 Jan 2000 08:00:00 UT ",
|
||||||
|
" Sat, 01(02)Jan 2000 08:00:00 UT ",
|
||||||
|
" Sat, 01 Jan 2000 (2001)08:00:00 UT ",
|
||||||
|
" Sat, 01 Jan 2000 (01)08:00:00 UT ",
|
||||||
|
" Sat, 01 Jan 2000 (01:00:00)08:00:00 UT ",
|
||||||
|
" Sat, 01 Jan 2000 08:00:00 (CDT)UT ",
|
||||||
|
" Sat, 01 Jan 2000 08:00:00 UT((((CDT))))",
|
||||||
|
" Saturday, 01-Jan-00 ()(((asfd)))(Sat, 01-Jan-2000)08:00:00 UT ",
|
||||||
|
" 01 Jan 00 08:00 ()(((asdf)))(Sat, 01-Jan-2000)+0000 ",
|
||||||
|
" 01 Jan 00 08:00 +0000()((asfd)(Sat, 01-Jan-2000)) ",
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut passed = 0;
|
||||||
|
let mut failed = 0;
|
||||||
|
|
||||||
|
for case in test_cases_ut {
|
||||||
let ctx = Context::new().unwrap();
|
let ctx = Context::new().unwrap();
|
||||||
let res = ctx
|
let res = ctx
|
||||||
.eval(&format!(r#"new Date("{input}").getTime()"#))
|
.eval(&format!(r#"new Date("{}").getTime()"#, case))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let n: f64 = res.try_into().unwrap();
|
let n: f64 = res.try_into().unwrap();
|
||||||
assert_eq!(n, expect as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
if n == 946713600000.0 {
|
||||||
#[case("Sat, 01-Jan-2000 08:00:00 GMT", 946713600000)]
|
passed += 1;
|
||||||
#[case("Sat, 01-Jan-2000 08:00:00 GMT+0", 946713600000)]
|
} else {
|
||||||
#[case("Sat, 01-Jan-2000 08:00:00 GMT+00", 946713600000)]
|
println!("FAIL: `{}` - {}", case, n);
|
||||||
#[case("Sat, 01-Jan-2000 08:00:00 GMT+000", 946713600000)]
|
failed += 1;
|
||||||
#[case("Sat, 01-Jan-2000 08:00:00 GMT+0000", 946713600000)]
|
|
||||||
#[case("Sat, 01-Jan-2000 08:00:00 GMT+00:00", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 08:00:00 GMT", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 08:00:00 GMT", 946713600000)]
|
|
||||||
#[case("01 Jan 00 08:00 -0000", 946713600000)]
|
|
||||||
#[case("01 Jan 00 08:00 +0000", 946713600000)]
|
|
||||||
fn test_date_gmt(#[case] input: &str, #[case] expect: i64) {
|
|
||||||
let ctx = Context::new().unwrap();
|
|
||||||
let res = ctx
|
|
||||||
.eval(&format!(r#"new Date("{input}").getTime()"#))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n: f64 = res.try_into().unwrap();
|
|
||||||
assert_eq!(n, expect as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
// EST (-5:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 03:00:00 UTC-0500", 946713600000)]
|
|
||||||
#[case("Sat, 01-Jan-2000 03:00:00 UTC-05:00", 946713600000)]
|
|
||||||
#[case("Sat, 01-Jan-2000 03:00:00 EST", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 03:00:00 EST", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 03:00:00 EST", 946713600000)]
|
|
||||||
#[case("01 Jan 00 03:00 -0500", 946713600000)]
|
|
||||||
// EDT (-4:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 04:00:00 EDT", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 04:00:00 EDT", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 04:00:00 EDT", 946713600000)]
|
|
||||||
#[case("01 Jan 00 04:00 -0400", 946713600000)]
|
|
||||||
// CST (-6:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 02:00:00 CST", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 02:00:00 CST", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 02:00:00 CST", 946713600000)]
|
|
||||||
#[case("01 Jan 00 02:00 -0600", 946713600000)]
|
|
||||||
// CDT (-5:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 03:00:00 CDT", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 03:00:00 CDT", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 03:00:00 CDT", 946713600000)]
|
|
||||||
#[case("01 Jan 00 03:00 -0500", 946713600000)]
|
|
||||||
// MST (-7:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 01:00:00 MST", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 01:00:00 MST", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 01:00:00 MST", 946713600000)]
|
|
||||||
#[case("01 Jan 00 01:00 -0700", 946713600000)]
|
|
||||||
// MDT (-6:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 02:00:00 MDT", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 02:00:00 MDT", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 02:00:00 MDT", 946713600000)]
|
|
||||||
#[case("01 Jan 00 02:00 -0600", 946713600000)]
|
|
||||||
// PST (-8:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 00:00:00 PST", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 00:00:00 PST", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 00:00:00 PST", 946713600000)]
|
|
||||||
#[case("01 Jan 00 00:00 -0800", 946713600000)]
|
|
||||||
#[case("Sat, 01-Jan-2000 PST", 946713600000)]
|
|
||||||
// PDT (-7:00)
|
|
||||||
#[case("Sat, 01-Jan-2000 01:00:00 PDT", 946713600000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 01:00:00 PDT", 946713600000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 01:00:00 PDT", 946713600000)]
|
|
||||||
#[case("01 Jan 00 01:00 -0700", 946713600000)]
|
|
||||||
fn test_date_tz(#[case] input: &str, #[case] expect: i64) {
|
|
||||||
let ctx = Context::new().unwrap();
|
|
||||||
let res = ctx
|
|
||||||
.eval(&format!(r#"new Date("{input}").getTime()"#))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n: f64 = res.try_into().unwrap();
|
|
||||||
assert_eq!(n, expect as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
// Special handling for years in the [0, 100) range.
|
|
||||||
#[case("Sat, 01 Jan 0 08:00:00 UT", 946713600000)] // year 2000
|
|
||||||
#[case("Sat, 01 Jan 49 08:00:00 UT", 2493100800000)] // year 2049
|
|
||||||
#[case("Sat, 01 Jan 50 08:00:00 UT", -631123200000)] // year 1950
|
|
||||||
#[case("Sat, 01 Jan 99 08:00:00 UT", 915177600000)] // year 1999
|
|
||||||
#[case("Sat, 01 Jan 100 08:00:00 UT", -59011430400000)] // year 100
|
|
||||||
// Test PM after time.
|
|
||||||
#[case("Sat, 01-Jan-2000 08:00 PM UT", 946756800000)]
|
|
||||||
#[case("Sat, 01 Jan 2000 08:00 PM UT", 946756800000)]
|
|
||||||
#[case("Jan 01 2000 08:00 PM UT", 946756800000)]
|
|
||||||
#[case("Jan 01 08:00 PM UT 2000", 946756800000)]
|
|
||||||
#[case("Saturday, 01-Jan-00 08:00 PM UT", 946756800000)]
|
|
||||||
#[case("01 Jan 00 08:00 PM +0000", 946756800000)]
|
|
||||||
fn test_date_special(#[case] input: &str, #[case] expect: i64) {
|
|
||||||
let ctx = Context::new().unwrap();
|
|
||||||
let res = ctx
|
|
||||||
.eval(&format!(r#"new Date("{input}").getTime()"#))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n: f64 = res.try_into().unwrap();
|
|
||||||
assert_eq!(n, expect as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
#[case("2000-01-01TZ")]
|
|
||||||
#[case("2000-01-01T60Z")]
|
|
||||||
#[case("2000-01-01T60:60Z")]
|
|
||||||
#[case("2000-01-0108:00Z")]
|
|
||||||
#[case("2000-01-01T08Z")]
|
|
||||||
#[case("2000-01-01T24:01")]
|
|
||||||
#[case("2000-01-01T24:00:01")]
|
|
||||||
#[case("2000-01-01T24:00:00.001")]
|
|
||||||
#[case("2000-01-01T24:00:00.999Z")]
|
|
||||||
#[case("May 25 2008 1:30 (PM)) UTC")]
|
|
||||||
#[case("May 25 2008 1:30( )AM (PM)")]
|
|
||||||
#[case("a1")]
|
|
||||||
#[case("nasfdjklsfjoaifg1")]
|
|
||||||
#[case("x_2")]
|
|
||||||
#[case("May 25 2008 AAA (GMT)")]
|
|
||||||
fn test_date_invalid(#[case] input: &str) {
|
|
||||||
let ctx = Context::new().unwrap();
|
|
||||||
let res = ctx
|
|
||||||
.eval(&format!(r#"new Date("{input}").getTime()"#))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let n: f64 = res.try_into().unwrap();
|
|
||||||
assert!(n.is_nan(), "got: {n}");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_date_ut() {
|
|
||||||
let test_cases_ut = vec![
|
|
||||||
"Sat, 01-Jan-2000 08:00:00 UT",
|
|
||||||
"Sat, 01 Jan 2000 08:00:00 UT",
|
|
||||||
"Jan 01 2000 08:00:00 UT",
|
|
||||||
"Jan 01 08:00:00 UT 2000",
|
|
||||||
"Saturday, 01-Jan-00 08:00:00 UT",
|
|
||||||
"01 Jan 00 08:00 +0000",
|
|
||||||
// Ignore weekdays.
|
|
||||||
"Mon, 01 Jan 2000 08:00:00 UT",
|
|
||||||
"Tue, 01 Jan 2000 08:00:00 UT",
|
|
||||||
// Ignore prefix that is not part of a date.
|
|
||||||
"[Saturday] Jan 01 08:00:00 UT 2000",
|
|
||||||
"Ignore all of this stuff because it is annoying 01 Jan 2000 08:00:00 UT",
|
|
||||||
"[Saturday] Jan 01 2000 08:00:00 UT",
|
|
||||||
"All of this stuff is really annnoying, so it will be ignored Jan 01 2000 08:00:00 UT",
|
|
||||||
// If the three first letters of the month is a
|
|
||||||
// month name we are happy - ignore the rest.
|
|
||||||
"Sat, 01-Janisamonth-2000 08:00:00 UT",
|
|
||||||
"Sat, 01 Janisamonth 2000 08:00:00 UT",
|
|
||||||
"Janisamonth 01 2000 08:00:00 UT",
|
|
||||||
"Janisamonth 01 08:00:00 UT 2000",
|
|
||||||
"Saturday, 01-Janisamonth-00 08:00:00 UT",
|
|
||||||
"01 Janisamonth 00 08:00 +0000",
|
|
||||||
// Allow missing space between month and day.
|
|
||||||
"Janisamonthandtherestisignored01 2000 08:00:00 UT",
|
|
||||||
"Jan01 2000 08:00:00 UT",
|
|
||||||
// Allow year/month/day format.
|
|
||||||
"Sat, 2000/01/01 08:00:00 UT",
|
|
||||||
// Allow month/day/year format.
|
|
||||||
"Sat, 01/01/2000 08:00:00 UT",
|
|
||||||
// Allow month/day year format.
|
|
||||||
"Sat, 01/01 2000 08:00:00 UT",
|
|
||||||
// Allow comma instead of space after day, month and year.
|
|
||||||
"Sat, 01,Jan,2000,08:00:00 UT",
|
|
||||||
// Seconds are optional.
|
|
||||||
"Sat, 01-Jan-2000 08:00 UT",
|
|
||||||
"Sat, 01 Jan 2000 08:00 UT",
|
|
||||||
"Jan 01 2000 08:00 UT",
|
|
||||||
"Jan 01 08:00 UT 2000",
|
|
||||||
"Saturday, 01-Jan-00 08:00 UT",
|
|
||||||
"01 Jan 00 08:00 +0000",
|
|
||||||
// Allow AM/PM after the time.
|
|
||||||
"Sat, 01-Jan-2000 08:00 AM UT",
|
|
||||||
"Sat, 01 Jan 2000 08:00 AM UT",
|
|
||||||
"Jan 01 2000 08:00 AM UT",
|
|
||||||
"Jan 01 08:00 AM UT 2000",
|
|
||||||
"Saturday, 01-Jan-00 08:00 AM UT",
|
|
||||||
"01 Jan 00 08:00 AM +0000",
|
|
||||||
// White space and stuff in parenthesis is
|
|
||||||
// apparently allowed in most places where white
|
|
||||||
// space is allowed.
|
|
||||||
" Sat, 01-Jan-2000 08:00:00 UT ",
|
|
||||||
" Sat, 01 Jan 2000 08:00:00 UT ",
|
|
||||||
" Saturday, 01-Jan-00 08:00:00 UT ",
|
|
||||||
" 01 Jan 00 08:00 +0000 ",
|
|
||||||
" ()(Sat, 01-Jan-2000) Sat, 01-Jan-2000 08:00:00 UT ",
|
|
||||||
" Sat()(Sat, 01-Jan-2000)01 Jan 2000 08:00:00 UT ",
|
|
||||||
" Sat,(02)01 Jan 2000 08:00:00 UT ",
|
|
||||||
" Sat, 01(02)Jan 2000 08:00:00 UT ",
|
|
||||||
" Sat, 01 Jan 2000 (2001)08:00:00 UT ",
|
|
||||||
" Sat, 01 Jan 2000 (01)08:00:00 UT ",
|
|
||||||
" Sat, 01 Jan 2000 (01:00:00)08:00:00 UT ",
|
|
||||||
" Sat, 01 Jan 2000 08:00:00 (CDT)UT ",
|
|
||||||
" Sat, 01 Jan 2000 08:00:00 UT((((CDT))))",
|
|
||||||
" Saturday, 01-Jan-00 ()(((asfd)))(Sat, 01-Jan-2000)08:00:00 UT ",
|
|
||||||
" 01 Jan 00 08:00 ()(((asdf)))(Sat, 01-Jan-2000)+0000 ",
|
|
||||||
" 01 Jan 00 08:00 +0000()((asfd)(Sat, 01-Jan-2000)) ",
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut passed = 0;
|
|
||||||
let mut failed = 0;
|
|
||||||
|
|
||||||
for case in test_cases_ut {
|
|
||||||
let ctx = Context::new().unwrap();
|
|
||||||
let res = ctx
|
|
||||||
.eval(&format!(r#"new Date("{case}").getTime()"#))
|
|
||||||
.unwrap();
|
|
||||||
let n: f64 = res.try_into().unwrap();
|
|
||||||
|
|
||||||
if n == 946713600000.0 {
|
|
||||||
passed += 1;
|
|
||||||
} else {
|
|
||||||
println!("FAIL: `{case}` - {n}");
|
|
||||||
failed += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}/{} Passed", passed, passed + failed);
|
|
||||||
assert_eq!(failed, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
println!("{}/{} Passed", passed, passed + failed);
|
||||||
// Test if the JS interpreter can parse its own date format
|
assert_eq!(failed, 0);
|
||||||
// (Dates from 1970 to ~2070 with 150h steps.)
|
}
|
||||||
fn test_date_selfparse() {
|
|
||||||
let ctx = Context::new().unwrap();
|
#[test]
|
||||||
let res = ctx
|
// Test if the JS interpreter can parse its own date format
|
||||||
.eval(
|
// (Dates from 1970 to ~2070 with 150h steps.)
|
||||||
r#"
|
fn test_date_selfparse() {
|
||||||
|
let ctx = Context::new().unwrap();
|
||||||
|
let res = ctx
|
||||||
|
.eval(
|
||||||
|
r#"
|
||||||
test = () => {
|
test = () => {
|
||||||
for (var i = 0; i < 24 * 365 * 100; i += 150) {
|
for (var i = 0; i < 24 * 365 * 100; i += 150) {
|
||||||
var ms = i * (3600 * 1000);
|
var ms = i * (3600 * 1000);
|
||||||
|
@ -890,9 +890,8 @@ mod dateparser {
|
||||||
}
|
}
|
||||||
test();
|
test();
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res: bool = res.try_into().unwrap();
|
let res: bool = res.try_into().unwrap();
|
||||||
assert!(res);
|
assert!(res);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,8 @@ impl BigInt {
|
||||||
impl std::fmt::Display for BigInt {
|
impl std::fmt::Display for BigInt {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
BigIntOrI64::Int(i) => write!(f, "{i}"),
|
BigIntOrI64::Int(i) => write!(f, "{}", i),
|
||||||
BigIntOrI64::BigInt(ref i) => write!(f, "{i}"),
|
BigIntOrI64::BigInt(ref i) => write!(f, "{}", i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,7 +259,6 @@ where
|
||||||
|
|
||||||
/// Error during value conversion.
|
/// Error during value conversion.
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum ValueError {
|
pub enum ValueError {
|
||||||
/// Invalid non-utf8 string.
|
/// Invalid non-utf8 string.
|
||||||
InvalidString(std::str::Utf8Error),
|
InvalidString(std::str::Utf8Error),
|
||||||
|
@ -269,6 +268,8 @@ pub enum ValueError {
|
||||||
Internal(String),
|
Internal(String),
|
||||||
/// Received an unexpected type that could not be converted.
|
/// Received an unexpected type that could not be converted.
|
||||||
UnexpectedType,
|
UnexpectedType,
|
||||||
|
#[doc(hidden)]
|
||||||
|
__NonExhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this once either the Never type get's stabilized or the compiler
|
// TODO: remove this once either the Never type get's stabilized or the compiler
|
||||||
|
@ -283,10 +284,15 @@ impl fmt::Display for ValueError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use ValueError::*;
|
use ValueError::*;
|
||||||
match self {
|
match self {
|
||||||
InvalidString(e) => write!(f, "Value conversion failed - invalid non-utf8 string: {e}"),
|
InvalidString(e) => write!(
|
||||||
|
f,
|
||||||
|
"Value conversion failed - invalid non-utf8 string: {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
StringWithZeroBytes(_) => write!(f, "String contains \\0 bytes",),
|
StringWithZeroBytes(_) => write!(f, "String contains \\0 bytes",),
|
||||||
Internal(e) => write!(f, "Value conversion failed - internal error: {e}"),
|
Internal(e) => write!(f, "Value conversion failed - internal error: {}", e),
|
||||||
UnexpectedType => write!(f, "Could not convert - received unexpected type"),
|
UnexpectedType => write!(f, "Could not convert - received unexpected type"),
|
||||||
|
__NonExhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue