Compare commits

...

2 commits

Author SHA1 Message Date
31956cfd75
fix: update dateparser patch 2024-03-23 01:32:06 +01:00
a3b3d3bc51
chore: update quickjs to 2024-01-13 2024-03-23 01:21:50 +01:00
32 changed files with 6768 additions and 5171 deletions

View file

@ -2,6 +2,11 @@
## 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)

View file

@ -2,7 +2,7 @@
edition = "2021"
name = "quick-js-dtp"
description = "QuickJS Javascript engine wrapper (with improved date parser)"
version = "0.4.1"
version = "0.4.2"
readme = "README.md"
repository = "https://github.com/Theta-Dev/quickjs-rs"
license = "MIT"
@ -25,7 +25,7 @@ patch-dateparser = ["libquickjs-dtp-sys/patch-dateparser"]
bigint = ["num-bigint", "num-traits", "libquickjs-dtp-sys/patch-bigint"]
[dependencies]
libquickjs-dtp-sys = { version = ">= 0.9.0, < 0.10.0", path = "./libquickjs-sys" }
libquickjs-dtp-sys = { version = "0.10.0", path = "./libquickjs-sys" }
chrono = { version = "0.4.7", optional = true }
num-bigint = { version = "0.4.0", optional = true }
num-traits = { version = "0.2.0", optional = true }
@ -33,7 +33,7 @@ log = { version = "0.4.8", optional = true }
once_cell = "1.2.0"
[dev-dependencies]
rstest = { version = "0.16.0", default-features = false }
rstest = { version = "0.18.0", default-features = false }
[workspace]
members = ["libquickjs-sys"]

View file

@ -1,6 +1,6 @@
embed_dir := "./libquickjs-sys/embed/quickjs"
DOWNLOAD_URL := "https://bellard.org/quickjs/quickjs-2021-03-27.tar.xz"
DOWNLOAD_URL := "https://bellard.org/quickjs/quickjs-2024-01-13.tar.xz"
FEATURES := "--all-features"
download-new:

View file

@ -2,7 +2,7 @@
edition = "2021"
name = "libquickjs-dtp-sys"
description = "QuickJS Javascript Engine FFI bindings (with improved date parser)"
version = "0.9.0"
version = "0.10.0"
readme = "README.md"
repository = "https://github.com/Theta-Dev/quickjs-rs"
license = "MIT"
@ -24,6 +24,6 @@ default = ["bundled"]
system = ["bindgen"]
[build-dependencies]
bindgen = { version = "0.63.0", optional = true }
bindgen = { version = "0.69.0", optional = true }
cc = { version = "1.0.66", optional = true }
copy_dir = { version = "0.1.2", optional = true }

View file

@ -10,8 +10,8 @@ See the [quick-js-dtp](https://crates.io/crates/quick-js-dtp) crate for a high-l
wrapper.
*Version 0.9.0*
**Embedded VERSION: 2021-03-27**
*Version 0.10.0*
**Embedded VERSION: 2024-01-13**
## Embedded vs system

View file

@ -168,6 +168,7 @@ pub const JS_EVAL_FLAG_STRICT: u32 = 8;
pub const JS_EVAL_FLAG_STRIP: u32 = 16;
pub const JS_EVAL_FLAG_COMPILE_ONLY: u32 = 32;
pub const JS_EVAL_FLAG_BACKTRACE_BARRIER: u32 = 64;
pub const JS_EVAL_FLAG_ASYNC: u32 = 128;
pub const JS_ATOM_NULL: u32 = 0;
pub const JS_CALL_FLAG_CONSTRUCTOR: u32 = 1;
pub const JS_GPN_STRING_MASK: u32 = 1;
@ -2678,9 +2679,10 @@ extern "C" {
extern "C" {
pub fn JS_SetPropertyInternal(
ctx: *mut JSContext,
this_obj: JSValue,
obj: JSValue,
prop: JSAtom,
val: JSValue,
this_obj: JSValue,
flags: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
@ -3026,9 +3028,19 @@ extern "C" {
sf: *const JSSharedArrayBufferFunctions,
);
}
pub const JSPromiseStateEnum_JS_PROMISE_PENDING: JSPromiseStateEnum = 0;
pub const JSPromiseStateEnum_JS_PROMISE_FULFILLED: JSPromiseStateEnum = 1;
pub const JSPromiseStateEnum_JS_PROMISE_REJECTED: JSPromiseStateEnum = 2;
pub type JSPromiseStateEnum = ::std::os::raw::c_uint;
extern "C" {
pub fn JS_NewPromiseCapability(ctx: *mut JSContext, resolving_funcs: *mut JSValue) -> JSValue;
}
extern "C" {
pub fn JS_PromiseState(ctx: *mut JSContext, promise: JSValue) -> JSPromiseStateEnum;
}
extern "C" {
pub fn JS_PromiseResult(ctx: *mut JSContext, promise: JSValue) -> JSValue;
}
pub type JSHostPromiseRejectionTracker = ::std::option::Option<
unsafe extern "C" fn(
ctx: *mut JSContext,
@ -3161,11 +3173,11 @@ extern "C" {
) -> JSAtom;
}
extern "C" {
pub fn JS_RunModule(
pub fn JS_LoadModule(
ctx: *mut JSContext,
basename: *const ::std::os::raw::c_char,
filename: *const ::std::os::raw::c_char,
) -> *mut JSModuleDef;
) -> JSValue;
}
pub const JSCFunctionEnum_JS_CFUNC_generic: JSCFunctionEnum = 0;
pub const JSCFunctionEnum_JS_CFUNC_generic_magic: JSCFunctionEnum = 1;

View file

@ -1,19 +1,19 @@
diff --git a/libquickjs-sys/embed/quickjs/quickjs.c b/libquickjs-sys/embed/quickjs/quickjs.c
index 48aeffc..a3325b3 100644
index 4e58a98..93cf853 100644
--- a/libquickjs-sys/embed/quickjs/quickjs.c
+++ b/libquickjs-sys/embed/quickjs/quickjs.c
@@ -1435,6 +1435,10 @@ static inline int is_digit(int 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 == '-';
+ return c == ' ' || c == ',' || c == ':' || c == '-' || c == '/';
+}
+
typedef struct JSClassShortDef {
JSAtom class_name;
JSClassFinalizer *finalizer;
@@ -47929,6 +47933,23 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
@@ -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";
@ -37,7 +37,7 @@ index 48aeffc..a3325b3 100644
static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
double fields[9], int is_local, int force)
{
@@ -48294,14 +48315,46 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
@@ -49707,14 +49728,44 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
return JS_NewFloat64(ctx, set_date_fields(fields, 0));
}
@ -50,7 +50,7 @@ index 48aeffc..a3325b3 100644
+ int nxt = *pp + 1;
+
+ // interpret - before a number as a sign rather than a comment char
+ if (ch == '-' && nxt < sp->len && is_digit(string_get(sp, nxt))) {
+ if (ch == '-' && nesting == 0 && nxt < sp->len && is_digit(string_get(sp, nxt))) {
+ break;
+ }
+ if (!is_space_like(ch)) {
@ -70,12 +70,10 @@ index 48aeffc..a3325b3 100644
-static void string_skip_non_spaces(JSString *sp, int *pp) {
- while (*pp < sp->len && string_get(sp, *pp) != ' ')
- *pp += 1;
+static BOOL char_eq_ignorecase(char c1, char c2) {
+ if (c1 == c2) return TRUE;
+
+ if (c1 >= 'A' && c1 <= 'Z' && c2 == c1 + 32) return TRUE;
+ if (c1 >= 'a' && c1 <= 'z' && c2 == c1 - 32) return TRUE;
+ return FALSE;
+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) {
@ -89,7 +87,55 @@ index 48aeffc..a3325b3 100644
}
/* parse a numeric field with an optional sign if accept_sign is TRUE */
@@ -48406,27 +48459,30 @@ static int find_abbrev(JSString *sp, int p, const char *list, int count) {
@@ -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;
}
@ -135,7 +181,74 @@ index 48aeffc..a3325b3 100644
double d;
int p, i, c, sgn, l;
JSString *sp;
@@ -48513,68 +48569,268 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
@@ -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 {
@ -154,7 +267,7 @@ index 48aeffc..a3325b3 100644
+ if (p - word_start >= 3) {
+ month = find_abbrev(sp, word_start, month_names, 12);
+ }
+ string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
+ word_start = p;
+ } else {
+ p++;
@ -168,7 +281,7 @@ index 48aeffc..a3325b3 100644
+ month = find_abbrev(sp, word_start, month_names, 12);
+ }
+
+ string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
+ if (sp->len <= p)
goto done;
- c = string_get(sp, p);
@ -181,7 +294,7 @@ index 48aeffc..a3325b3 100644
+ if (string_get_digits(sp, &p, &day))
+ goto done;
+
+ int64_t year = 0;
+ int64_t year = -1;
+ if (day > 31) {
+ if (string_get(sp, p) != '/')
goto done;
@ -221,7 +334,7 @@ index 48aeffc..a3325b3 100644
+ if (string_get_digits(sp, &p, &day))
+ goto done;
+
+ if (string_get(sp, p) != '/')
+ if (!is_space_like(string_get(sp, p)))
+ goto done;
+ p++;
+ if (sp->len <= p)
@ -230,40 +343,29 @@ index 48aeffc..a3325b3 100644
+ if (string_get(sp, p) == '-') {
+ p++;
+ }
+ string_skip_spaces_and_comments(sp, &p); // and comments
+
+ 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;
goto done;
- if (string_get(sp, p) != ':')
+
+ while (p < sp->len && string_get(sp, p) != '-' && string_get(sp, p) != ' ') {
+ while (p < sp->len && !is_space_like(string_get(sp, p))) {
+ p++;
+ }
+ if (sp->len <= p)
goto done;
- if (string_get(sp, p) != ':')
+
+ // '-99 23:12:40 GMT'
+ if (string_get(sp, p) != '-' && string_get(sp, p) != '/' && string_get(sp, p) != ' ') {
goto done;
+ }
p++;
}
- if (string_get_digits(sp, &p, &fields[3 + i]))
+
+ if (month < 0 || month > 11) {
goto done;
+ }
- goto done;
}
- // XXX: parse optional milliseconds?
@ -273,11 +375,14 @@ index 48aeffc..a3325b3 100644
- for (tz = 0; p < sp->len; p++) {
- sgn = string_get(sp, p);
- if (sgn == '+' || sgn == '-') {
+ if (year <= 0) {
+ 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;
+ if (year <= 0) {
+ goto done;
+ }
+ }
+
@ -294,9 +399,7 @@ index 48aeffc..a3325b3 100644
+ } 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); // and comments
+ } else {
+ goto done;
+ string_skip_spaces_and_comments(sp, &p);
+ }
+
+ // Read a number? If not, this might be a timezone name.
@ -322,10 +425,6 @@ index 48aeffc..a3325b3 100644
+ if (string_get_digits(sp, &p, &minute))
+ goto done;
+
+ if (minute < 0 || minute > 59) {
+ goto done;
+ }
+
+ // ':40 GMT'
+ // seconds are optional in rfc822 + rfc2822
+ if (string_get(sp, p) == ':') {
@ -334,10 +433,6 @@ index 48aeffc..a3325b3 100644
+ if (string_get_digits(sp, &p, &second))
+ goto done;
+
+ if (second < 0 || second > 59) {
+ goto done;
+ }
+
+ // disallow trailing colon seconds
+ if (string_get(sp, p) == ':') {
+ goto done;
@ -346,7 +441,7 @@ index 48aeffc..a3325b3 100644
+ goto done;
+ }
+
+ string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
+
+ if (string_eq_ignorecase(sp, p, "AM", 2)) {
+ if (hour > 12) {
@ -356,7 +451,7 @@ index 48aeffc..a3325b3 100644
+ hour = 0;
+ }
+ p += 2;
+ string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
+ } else if (string_eq_ignorecase(sp, p, "PM", 2)) {
+ if (hour > 12) {
+ goto done;
@ -365,35 +460,17 @@ index 48aeffc..a3325b3 100644
+ hour += 12;
+ }
+ p += 2;
+ string_skip_spaces_and_comments(sp, &p); // and comments
+ }
+ }
+ 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) == '-') {
+ int64_t o;
+ if (string_get_digits(sp, &p, &o))
+ if (string_get_num_timezone(sp, &p, &tz))
+ goto done;
+
+ if (o < -9959 || o > 9959) {
+ goto done;
+ }
+
+ int sgn = (o < 0) ? -1 : 1;
+ o = abs((int32_t) o);
+
+ if (string_get(sp, p) != ':') {
+ tz = ((o / 100) * 60 + (o % 100)) * sgn;
+ } else {
+ p++;
+ int64_t o2;
+ if (string_get_digits(sp, &p, &o2))
+ goto done;
+ tz = (o * 60 + o2) * sgn;
+ }
+ is_local = FALSE;
+ } else {
+ for (int i = 0; i < sizeof(known_zones) / sizeof(struct KnownZone); i++) {
@ -401,6 +478,15 @@ index 48aeffc..a3325b3 100644
+ 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;
+ }
+ }
@ -427,8 +513,8 @@ index 48aeffc..a3325b3 100644
+ year += 2000;
+ } else {
+ year += 1900;
}
}
+ }
+ }
+
+ fields[0] = year;
+ fields[1] = month;
@ -438,521 +524,6 @@ index 48aeffc..a3325b3 100644
+ fields[5] = second;
}
+
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:
--
2.39.1
From f52e54d69ca1b62c9c855f360881ca8e4886ed1a Mon Sep 17 00:00:00 2001
From: ThetaDev <t.testboy@gmail.com>
Date: Sat, 13 Aug 2022 01:39:24 +0200
Subject: [PATCH 2/5] 2 small fixes
---
libquickjs-sys/embed/quickjs/quickjs.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/libquickjs-sys/embed/quickjs/quickjs.c b/libquickjs-sys/embed/quickjs/quickjs.c
index a3325b3..8ff9f4e 100644
--- a/libquickjs-sys/embed/quickjs/quickjs.c
+++ b/libquickjs-sys/embed/quickjs/quickjs.c
@@ -48322,7 +48322,7 @@ static void string_skip_spaces_and_comments(JSString *sp, int *pp) {
int nxt = *pp + 1;
// interpret - before a number as a sign rather than a comment char
- if (ch == '-' && nxt < sp->len && is_digit(string_get(sp, nxt))) {
+ if (ch == '-' && nesting == 0 && nxt < sp->len && is_digit(string_get(sp, nxt))) {
break;
}
if (!is_space_like(ch)) {
@@ -48496,6 +48496,8 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
sp = JS_VALUE_GET_STRING(s);
p = 0;
+ string_skip_spaces_and_comments(sp, &p);
+
if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
/* ISO format */
/* year field can be negative */
@@ -48579,7 +48581,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (p - word_start >= 3) {
month = find_abbrev(sp, word_start, month_names, 12);
}
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
word_start = p;
} else {
p++;
@@ -48593,7 +48595,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
month = find_abbrev(sp, word_start, month_names, 12);
}
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
if (sp->len <= p)
goto done;
@@ -48640,7 +48642,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (string_get(sp, p) == '-') {
p++;
}
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
if (string_get(sp, p) == ',') {
p++;
@@ -48669,6 +48671,8 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
}
}
+ string_skip_spaces_and_comments(sp, &p);
+
if (year <= 0) {
if (string_get_digits(sp, &p, &year))
goto done;
@@ -48689,7 +48693,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
year = -1;
} else if (is_space_like(string_get(sp, p))) {
p++;
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
} else {
goto done;
}
@@ -48736,7 +48740,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
goto done;
}
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
if (string_eq_ignorecase(sp, p, "AM", 2)) {
if (hour > 12) {
@@ -48746,7 +48750,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
hour = 0;
}
p += 2;
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
} else if (string_eq_ignorecase(sp, p, "PM", 2)) {
if (hour > 12) {
goto done;
@@ -48755,7 +48759,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
hour += 12;
}
p += 2;
- string_skip_spaces_and_comments(sp, &p); // and comments
+ string_skip_spaces_and_comments(sp, &p);
}
}
}
--
2.39.1
From ad17c000b6664c6f9ef2ecedbcaade19fc41c1eb Mon Sep 17 00:00:00 2001
From: ThetaDev <t.testboy@gmail.com>
Date: Sat, 13 Aug 2022 10:11:06 +0200
Subject: [PATCH 3/5] fix y2k issue
---
libquickjs-sys/embed/quickjs/quickjs.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libquickjs-sys/embed/quickjs/quickjs.c b/libquickjs-sys/embed/quickjs/quickjs.c
index 8ff9f4e..e7c71b0 100644
--- a/libquickjs-sys/embed/quickjs/quickjs.c
+++ b/libquickjs-sys/embed/quickjs/quickjs.c
@@ -48604,7 +48604,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (string_get_digits(sp, &p, &day))
goto done;
- int64_t year = 0;
+ int64_t year = -1;
if (day > 31) {
if (string_get(sp, p) != '/')
goto done;
@@ -48673,10 +48673,10 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
string_skip_spaces_and_comments(sp, &p);
- if (year <= 0) {
+ if (year < 0) {
if (string_get_digits(sp, &p, &year))
goto done;
- if (year <= 0) {
+ if (year < 0) {
goto done;
}
}
--
2.39.1
From 333283c377422f62a5e72605814c34b74f0f875d Mon Sep 17 00:00:00 2001
From: ThetaDev <t.testboy@gmail.com>
Date: Sat, 13 Aug 2022 15:02:04 +0200
Subject: [PATCH 4/5] fix more date formats, now passes all tests
---
libquickjs-sys/embed/quickjs/quickjs.c | 84 +++++++++++++++++---------
1 file changed, 56 insertions(+), 28 deletions(-)
diff --git a/libquickjs-sys/embed/quickjs/quickjs.c b/libquickjs-sys/embed/quickjs/quickjs.c
index e7c71b0..91b34c7 100644
--- a/libquickjs-sys/embed/quickjs/quickjs.c
+++ b/libquickjs-sys/embed/quickjs/quickjs.c
@@ -1436,7 +1436,7 @@ static inline int is_digit(int c) {
}
static inline int is_space_like(char c) {
- return c == ' ' || c == ',' || c == ':' || c == '-';
+ return c == ' ' || c == ',' || c == ':' || c == '-' || c == '/';
}
typedef struct JSClassShortDef {
@@ -48339,12 +48339,10 @@ static void string_skip_spaces_and_comments(JSString *sp, int *pp) {
}
}
-static BOOL char_eq_ignorecase(char c1, char c2) {
- if (c1 == c2) return TRUE;
-
- if (c1 >= 'A' && c1 <= 'Z' && c2 == c1 + 32) return TRUE;
- if (c1 >= 'a' && c1 <= 'z' && c2 == c1 - 32) return TRUE;
- return FALSE;
+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) {
@@ -48498,7 +48496,16 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
p = 0;
string_skip_spaces_and_comments(sp, &p);
- if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
+ 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]))
@@ -48523,8 +48530,13 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
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]))
@@ -48633,7 +48645,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (string_get_digits(sp, &p, &day))
goto done;
- if (string_get(sp, p) != '/')
+ if (!is_space_like(string_get(sp, p)))
goto done;
p++;
if (sp->len <= p)
@@ -48644,25 +48656,17 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
}
string_skip_spaces_and_comments(sp, &p);
- if (string_get(sp, p) == ',') {
- p++;
- }
-
+ // Jan,2000,08:00:00 UT
if (month == -1) {
month = find_abbrev(sp, p, month_names, 12);
if (month == -1)
goto done;
- while (p < sp->len && string_get(sp, p) != '-' && string_get(sp, p) != ' ') {
+ while (p < sp->len && !is_space_like(string_get(sp, p))) {
p++;
}
if (sp->len <= p)
goto done;
-
- // '-99 23:12:40 GMT'
- if (string_get(sp, p) != '-' && string_get(sp, p) != '/' && string_get(sp, p) != ' ') {
- goto done;
- }
p++;
}
@@ -48674,10 +48678,11 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
string_skip_spaces_and_comments(sp, &p);
if (year < 0) {
- if (string_get_digits(sp, &p, &year))
- goto done;
- if (year < 0) {
- goto done;
+ // 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;
}
}
@@ -48694,8 +48699,6 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
} else if (is_space_like(string_get(sp, p))) {
p++;
string_skip_spaces_and_comments(sp, &p);
- } else {
- goto done;
}
// Read a number? If not, this might be a timezone name.
@@ -48769,7 +48772,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (sp->len > p) {
if (string_get(sp, p) == '+' || string_get(sp, p) == '-') {
int64_t o;
- if (string_get_digits(sp, &p, &o))
+ if (string_get_signed_digits(sp, &p, &o))
goto done;
if (o < -9959 || o > 9959) {
@@ -48795,6 +48798,31 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
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_signed_digits(sp, &p, &o))
+ goto done;
+
+ if (o < -9959 || o > 9959) {
+ goto done;
+ }
+
+ int sgn = (o < 0) ? -1 : 1;
+ o = abs((int32_t) o);
+
+ if (string_get(sp, p) != ':') {
+ tz += ((o / 100) * 60 + (o % 100)) * sgn;
+ } else {
+ p++;
+ int64_t o2;
+ if (string_get_digits(sp, &p, &o2))
+ goto done;
+ tz += (o * 60 + o2) * sgn;
+ }
+ }
+
break;
}
}
--
2.39.1
From 749be29936abc5c9d1d1dedf863a70d7bafaa795 Mon Sep 17 00:00:00 2001
From: ThetaDev <t.testboy@gmail.com>
Date: Sat, 13 Aug 2022 18:07:09 +0200
Subject: [PATCH 5/5] added date validation
---
libquickjs-sys/embed/quickjs/quickjs.c | 105 ++++++++++++-------------
1 file changed, 50 insertions(+), 55 deletions(-)
diff --git a/libquickjs-sys/embed/quickjs/quickjs.c b/libquickjs-sys/embed/quickjs/quickjs.c
index 91b34c7..68f0120 100644
--- a/libquickjs-sys/embed/quickjs/quickjs.c
+++ b/libquickjs-sys/embed/quickjs/quickjs.c
@@ -48440,6 +48440,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;
@@ -48515,18 +48543,18 @@ 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;
}
@@ -48546,6 +48574,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;
@@ -48669,10 +48700,6 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
goto done;
p++;
}
-
- if (month < 0 || month > 11) {
- goto done;
- }
}
string_skip_spaces_and_comments(sp, &p);
@@ -48718,10 +48745,6 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (string_get_digits(sp, &p, &minute))
goto done;
-
- if (minute < 0 || minute > 59) {
- goto done;
- }
// ':40 GMT'
// seconds are optional in rfc822 + rfc2822
@@ -48730,10 +48753,6 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
if (string_get_digits(sp, &p, &second))
goto done;
-
- if (second < 0 || second > 59) {
- goto done;
- }
// disallow trailing colon seconds
if (string_get(sp, p) == ':') {
@@ -48771,26 +48790,8 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
// Some websites omit the time zone
if (sp->len > p) {
if (string_get(sp, p) == '+' || string_get(sp, p) == '-') {
- int64_t o;
- if (string_get_signed_digits(sp, &p, &o))
- goto done;
-
- if (o < -9959 || o > 9959) {
+ if (string_get_num_timezone(sp, &p, &tz))
goto done;
- }
-
- int sgn = (o < 0) ? -1 : 1;
- o = abs((int32_t) o);
-
- if (string_get(sp, p) != ':') {
- tz = ((o / 100) * 60 + (o % 100)) * sgn;
- } else {
- p++;
- int64_t o2;
- if (string_get_digits(sp, &p, &o2))
- goto done;
- tz = (o * 60 + o2) * sgn;
- }
is_local = FALSE;
} else {
for (int i = 0; i < sizeof(known_zones) / sizeof(struct KnownZone); i++) {
@@ -48802,27 +48803,11 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
// TZ offset (GMT+0)
if (string_get(sp, p) == '+' || string_get(sp, p) == '-') {
int64_t o;
- if (string_get_signed_digits(sp, &p, &o))
+ if (string_get_num_timezone(sp, &p, &o))
goto done;
- if (o < -9959 || o > 9959) {
- goto done;
- }
-
- int sgn = (o < 0) ? -1 : 1;
- o = abs((int32_t) o);
-
- if (string_get(sp, p) != ':') {
- tz += ((o / 100) * 60 + (o % 100)) * sgn;
- } else {
- p++;
- int64_t o2;
- if (string_get_digits(sp, &p, &o2))
- goto done;
- tz += (o * 60 + o2) * sgn;
- }
+ tz += o;
}
-
break;
}
}
@@ -48860,6 +48845,16 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
fields[5] = second;
}
+ // Validate fields
+ if (fields[1] < 0 || fields[1] > 11 ||
+ fields[2] < 1 || fields[2] > 31 ||
@ -965,7 +536,8 @@ index 91b34c7..68f0120 100644
+
for(i = 0; i < 7; i++)
fields1[i] = fields[i];
d = set_date_fields(fields1, is_local) - (tz * 60000);
--
2.39.1
- d = set_date_fields(fields1, is_local) - tz * 60000;
+ d = set_date_fields(fields1, is_local) - (tz * 60000);
rv = JS_NewFloat64(ctx, d);
done:

View file

@ -1,3 +1,30 @@
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:
- faster Array.prototype.push and Array.prototype.unshift

View file

@ -33,6 +33,20 @@ CONFIG_LTO=y
#CONFIG_WERROR=y
# force 32 bit build for some utilities
#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
# use clang instead of gcc
@ -40,33 +54,22 @@ CONFIG_CLANG=y
CONFIG_DEFAULT_AR=y
endif
# installation directory
prefix=/usr/local
# use the gprof profiler
#CONFIG_PROFILE=y
# use address sanitizer
#CONFIG_ASAN=y
# include the code for BigInt/BigFloat/BigDecimal and math mode
CONFIG_BIGNUM=y
OBJDIR=.obj
ifdef CONFIG_WIN32
ifdef CONFIG_M32
CROSS_PREFIX=i686-w64-mingw32-
CROSS_PREFIX?=i686-w64-mingw32-
else
CROSS_PREFIX=x86_64-w64-mingw32-
CROSS_PREFIX?=x86_64-w64-mingw32-
endif
EXE=.exe
else
CROSS_PREFIX=
CROSS_PREFIX?=
EXE=
endif
ifdef CONFIG_CLANG
HOST_CC=clang
CC=$(CROSS_PREFIX)clang
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wextra
CFLAGS += -Wno-sign-compare
CFLAGS += -Wno-missing-field-initializers
@ -84,10 +87,18 @@ ifdef CONFIG_CLANG
AR=$(CROSS_PREFIX)ar
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
HOST_CC=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
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)gcc-ar
@ -96,6 +107,7 @@ else
endif
endif
STRIP=$(CROSS_PREFIX)strip
CFLAGS+=-fwrapv # ensure that signed overflows behave as expected
ifdef CONFIG_WERROR
CFLAGS+=-Werror
endif
@ -112,7 +124,11 @@ CFLAGS_DEBUG=$(CFLAGS) -O0
CFLAGS_SMALL=$(CFLAGS) -Os
CFLAGS_OPT=$(CFLAGS) -O2
CFLAGS_NOLTO:=$(CFLAGS_OPT)
LDFLAGS=-g
ifdef CONFIG_COSMO
LDFLAGS+=-s # better to strip by default
else
LDFLAGS+=-g
endif
ifdef CONFIG_LTO
CFLAGS_SMALL+=-flto
CFLAGS_OPT+=-flto
@ -132,6 +148,12 @@ else
LDEXPORT=-rdynamic
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
ifneq ($(CROSS_PREFIX),)
QJSC_CC=gcc
@ -154,23 +176,21 @@ endif
# examples
ifeq ($(CROSS_PREFIX),)
ifdef CONFIG_ASAN
PROGS+=
else
PROGS+=examples/hello examples/hello_module examples/test_fib
ifndef CONFIG_DARWIN
PROGS+=examples/fib.so examples/point.so
PROGS+=examples/hello
ifndef CONFIG_ASAN
PROGS+=examples/hello_module
endif
ifdef CONFIG_SHARED_LIBS
PROGS+=examples/test_fib examples/fib.so examples/point.so
endif
endif
all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)
QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o
QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o $(OBJDIR)/libbf.o
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
ifdef CONFIG_BIGNUM
QJS_LIB_OBJS+=$(OBJDIR)/libbf.o
QJS_OBJS+=$(OBJDIR)/qjscalc.o
endif
@ -201,11 +221,11 @@ $(QJSC): $(OBJDIR)/qjsc.host.o \
endif #CROSS_PREFIX
QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\"
ifdef CONFIG_LTO
QJSC_DEFINES+=-DCONFIG_LTO
endif
QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\"
$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES)
$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES)
@ -298,17 +318,17 @@ clean:
rm -rf run-test262-debug run-test262-32
install: all
mkdir -p "$(DESTDIR)$(prefix)/bin"
mkdir -p "$(DESTDIR)$(PREFIX)/bin"
$(STRIP) qjs qjsc
install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin"
ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs"
install -m755 qjs qjsc "$(DESTDIR)$(PREFIX)/bin"
ln -sf qjs "$(DESTDIR)$(PREFIX)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
ifdef CONFIG_LTO
install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs"
install -m644 libquickjs.lto.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
endif
mkdir -p "$(DESTDIR)$(prefix)/include/quickjs"
install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(prefix)/include/quickjs"
mkdir -p "$(DESTDIR)$(PREFIX)/include/quickjs"
install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(PREFIX)/include/quickjs"
###############################################################################
# examples
@ -317,10 +337,7 @@ endif
HELLO_SRCS=examples/hello.js
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
-fno-date -fno-module-loader
ifdef CONFIG_BIGNUM
HELLO_OPTS+=-fno-bigint
endif
-fno-date -fno-module-loader -fno-bigint
hello.c: $(QJSC) $(HELLO_SRCS)
$(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
@ -377,7 +394,7 @@ doc/%.html: doc/%.html.pre
###############################################################################
# tests
ifndef CONFIG_DARWIN
ifdef CONFIG_SHARED_LIBS
test: tests/bjson.so examples/point.so
endif
ifdef CONFIG_M32
@ -391,7 +408,7 @@ test: qjs
./qjs tests/test_loop.js
./qjs tests/test_std.js
./qjs tests/test_worker.js
ifndef CONFIG_DARWIN
ifdef CONFIG_SHARED_LIBS
ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_bjson.js
else

View file

@ -1,6 +1,3 @@
Bugs:
- modules: better error handling with cyclic module references
Misc ideas:
- use custom printf to avoid compatibility issues with floating point numbers
- consistent naming for preprocessor defines
@ -66,5 +63,5 @@ Optimization ideas:
Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Result: 35/75280 errors, 909 excluded, 585 skipped
Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9
Result: 10/76947 errors, 1497 excluded, 8117 skipped
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb

View file

@ -1 +1 @@
2021-03-27
2024-01-13

View file

@ -49,6 +49,9 @@
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#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;
#ifndef FALSE

View file

@ -37,10 +37,12 @@
/* enable it to check the multiplication result */
//#define USE_MUL_CHECK
#ifdef CONFIG_BIGNUM
/* enable it to use FFT/NTT multiplication */
#define USE_FFT_MUL
/* enable decimal floating point support */
#define USE_BF_DEC
#endif
//#define inline __attribute__((always_inline))
@ -164,6 +166,21 @@ static inline slimb_t sat_add(slimb_t a, slimb_t b)
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 free(p) free_is_forbidden(p)
#define realloc(p, s) realloc_is_forbidden(p, s)
@ -236,7 +253,7 @@ int bf_set_ui(bf_t *r, uint64_t a)
a1 = a >> 32;
shift = clz(a1);
r->tab[0] = a0 << shift;
r->tab[1] = (a1 << shift) | (a0 >> (LIMB_BITS - shift));
r->tab[1] = shld(a1, a0, shift);
r->expn = 2 * LIMB_BITS - shift;
}
#endif
@ -1585,7 +1602,9 @@ int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
r = &tmp;
}
if (bf_resize(r, a_len + b_len)) {
#ifdef USE_FFT_MUL
fail:
#endif
bf_set_nan(r);
ret = BF_ST_MEM_ERROR;
goto done;
@ -2282,11 +2301,14 @@ static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b,
bf_t a;
int ret;
#ifdef USE_BF_DEC
if (a1 == 10 && b <= LIMB_DIGITS) {
/* use precomputed powers. We do not round at this point
because we expect the caller to do it */
ret = bf_set_ui(r, mp_pow_dec[b]);
} else {
} else
#endif
{
bf_init(r->ctx, &a);
ret = bf_set_ui(&a, a1);
ret |= bf_pow_ui(r, &a, b, prec, flags);
@ -5392,21 +5414,6 @@ int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
#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
/* WARNING: hardcoded for b = 1e19. It is assumed that:

View file

@ -27,7 +27,7 @@
#include <stddef.h>
#include <stdint.h>
#if INTPTR_MAX >= INT64_MAX
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5

View file

@ -50,8 +50,7 @@ DEF(range32, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character
position */
DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)

View file

@ -34,9 +34,6 @@
/*
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)
when the regular expression is "simple" i.e. no backreference nor
complicated lookahead. The opcodes are designed for this execution
@ -120,33 +117,6 @@ static int dbuf_insert(DynBuf *s, int pos, int len)
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[] = {
1,
0x0030, 0x0039 + 1,
@ -245,31 +215,6 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)
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
static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
int buf_len)
@ -335,7 +280,6 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
case REOP_loop:
case REOP_lookahead:
case REOP_negative_lookahead:
case REOP_bne_char_pos:
val = get_u32(buf + pos + 1);
val += (pos + 5);
printf(" %u", val);
@ -922,7 +866,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
}
}
if (s->ignore_case) {
if (cr_canonicalize(cr))
if (cr_regexp_canonicalize(cr, s->is_utf16))
goto memory_error;
}
if (invert) {
@ -943,22 +887,17 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
}
/* Return:
1 if the opcodes in bc_buf[] always advance the character pointer.
0 if the character pointer may not be advanced.
-1 if the code may depend on side effects of its previous execution (backreference)
- true if the opcodes may not advance the char pointer
- false if the opcodes always advance the char pointer
*/
static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
{
int pos, opcode, ret, len, i;
uint32_t val, last;
BOOL has_back_reference;
uint8_t capture_bitmap[CAPTURE_COUNT_MAX];
int pos, opcode, len;
uint32_t val;
BOOL ret;
ret = -2; /* not known yet */
ret = TRUE;
pos = 0;
has_back_reference = FALSE;
memset(capture_bitmap, 0, sizeof(capture_bitmap));
while (pos < bc_buf_len) {
opcode = bc_buf[pos];
len = reopcode_info[opcode].size;
@ -976,8 +915,7 @@ static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
case REOP_dot:
case REOP_any:
simple_char:
if (ret == -2)
ret = 1;
ret = FALSE;
break;
case REOP_line_start:
case REOP_line_end:
@ -991,41 +929,16 @@ static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
break;
case REOP_save_start:
case REOP_save_end:
val = bc_buf[pos + 1];
capture_bitmap[val] |= 1;
break;
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_backward_back_reference:
val = bc_buf[pos + 1];
capture_bitmap[val] |= 2;
has_back_reference = TRUE;
break;
default:
/* safe behvior: we cannot predict the outcome */
if (ret == -2)
ret = 0;
break;
return TRUE;
}
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;
}
@ -1071,11 +984,10 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)
}
/* '*pp' is the first char after '<' */
static int re_parse_group_name(char *buf, int buf_size,
const uint8_t **pp, BOOL is_utf16)
static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp)
{
const uint8_t *p;
uint32_t c;
const uint8_t *p, *p1;
uint32_t c, d;
char *q;
p = *pp;
@ -1086,11 +998,18 @@ static int re_parse_group_name(char *buf, int buf_size,
p++;
if (*p != 'u')
return -1;
c = lre_parse_escape(&p, is_utf16 * 2);
c = lre_parse_escape(&p, 2); // accept surrogate pairs
} else if (c == '>') {
break;
} else if (c >= 128) {
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 {
p++;
}
@ -1140,8 +1059,7 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures,
/* potential named capture */
if (capture_name) {
p += 3;
if (re_parse_group_name(name, sizeof(name), &p,
s->is_utf16) == 0) {
if (re_parse_group_name(name, sizeof(name), &p) == 0) {
if (!strcmp(name, capture_name))
return capture_index;
}
@ -1314,7 +1232,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
} else if (p[2] == '<') {
p += 3;
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
&p, s->is_utf16)) {
&p)) {
return re_parse_error(s, "invalid group name");
}
if (find_group_name(s, s->u.tmp_buf) > 0) {
@ -1378,7 +1296,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
}
p1 += 3;
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
&p1, s->is_utf16)) {
&p1)) {
if (s->is_utf16 || re_has_named_captures(s))
return re_parse_error(s, "invalid group name");
else
@ -1591,8 +1509,12 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (dbuf_error(&s->byte_code))
goto out_of_memory;
add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start) == 0);
/* the spec tells that if there is no advance when
running the atom after the first quant_min times,
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 {
add_zero_advance_check = FALSE;
}
@ -1612,38 +1534,34 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
}
if (quant_max == 0) {
s->byte_code.size = last_atom_start;
} else if (quant_max == 1) {
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) {
} else if (quant_max == 1 || quant_max == INT32_MAX) {
BOOL has_goto = (quant_max == INT32_MAX);
if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))
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 + 5 + add_zero_advance_check);
len + 5 * has_goto + add_zero_advance_check * 2);
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;
re_emit_goto(s, REOP_bne_char_pos, last_atom_start);
} else {
re_emit_goto(s, REOP_goto, last_atom_start);
re_emit_op(s, REOP_check_advance);
}
if (has_goto)
re_emit_goto(s, REOP_goto, last_atom_start);
} else {
if (dbuf_insert(&s->byte_code, last_atom_start, 10))
if (dbuf_insert(&s->byte_code, last_atom_start, 10 + add_zero_advance_check))
goto out_of_memory;
pos = last_atom_start;
s->byte_code.buf[pos++] = REOP_push_i32;
put_u32(s->byte_code.buf + pos, quant_max);
pos += 4;
s->byte_code.buf[pos++] = REOP_split_goto_first + greedy;
put_u32(s->byte_code.buf + pos, len + 5);
put_u32(s->byte_code.buf + pos, len + 5 + add_zero_advance_check * 2);
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_op(s, REOP_drop);
}
@ -1667,22 +1585,25 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (quant_max == INT32_MAX) {
pos = s->byte_code.size;
re_emit_op_u32(s, REOP_split_goto_first + greedy,
len + 5 + add_zero_advance_check);
len + 5 + add_zero_advance_check * 2);
if (add_zero_advance_check)
re_emit_op(s, REOP_push_char_pos);
/* copy the atom */
dbuf_put_self(&s->byte_code, last_atom_start, len);
if (add_zero_advance_check)
re_emit_goto(s, REOP_bne_char_pos, pos);
else
re_emit_op(s, REOP_check_advance);
re_emit_goto(s, REOP_goto, pos);
} else if (quant_max > quant_min) {
re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min);
pos = s->byte_code.size;
re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5);
re_emit_op_u32(s, REOP_split_goto_first + greedy,
len + 5 + add_zero_advance_check * 2);
if (add_zero_advance_check)
re_emit_op(s, REOP_push_char_pos);
/* copy the atom */
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_op(s, REOP_drop);
}
@ -1796,7 +1717,7 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)
}
break;
case REOP_drop:
case REOP_bne_char_pos:
case REOP_check_advance:
assert(stack_size > 0);
stack_size--;
break;
@ -2292,11 +2213,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
case REOP_push_char_pos:
stack[stack_len++] = (uintptr_t)cptr;
break;
case REOP_bne_char_pos:
val = get_u32(pc);
pc += 4;
if (stack[--stack_len] != (uintptr_t)cptr)
pc += (int)val;
case REOP_check_advance:
if (stack[--stack_len] == (uintptr_t)cptr)
goto no_match;
break;
case REOP_word_boundary:
case REOP_not_word_boundary:

View file

@ -36,6 +36,7 @@
#define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UTF16 (1 << 4)
#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 */

File diff suppressed because it is too large Load diff

View file

@ -43,47 +43,26 @@ enum {
RUN_TYPE_UF_D1_EXT,
RUN_TYPE_U_EXT,
RUN_TYPE_LF_EXT,
RUN_TYPE_U_EXT2,
RUN_TYPE_L_EXT2,
RUN_TYPE_U_EXT3,
RUN_TYPE_UF_EXT2,
RUN_TYPE_LF_EXT2,
RUN_TYPE_UF_EXT3,
};
/* conv_type:
0 = to upper
1 = to lower
2 = case folding (= to lower with modifications)
*/
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
static int lre_case_conv1(uint32_t c, int conv_type)
{
if (c < 128) {
if (conv_type) {
if (c >= 'A' && c <= 'Z') {
c = c - 'A' + 'a';
uint32_t res[LRE_CC_RES_LEN_MAX];
lre_case_conv(res, c, conv_type);
return res[0];
}
} else {
if (c >= 'a' && c <= 'z') {
c = c - 'a' + 'A';
}
}
} else {
uint32_t v, code, data, type, len, a, is_lower;
int idx, idx_min, idx_max;
/* 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);
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 {
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:
@ -133,23 +112,76 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
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))
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_U_EXT3:
if (conv_type != 0)
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;
}
break;
res[0] = c;
return 1;
}
/* conv_type:
0 = to upper
1 = to lower
2 = case folding (= to lower with modifications)
*/
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
{
if (c < 128) {
if (conv_type) {
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_conv_entry(res, c, conv_type, idx, v);
}
}
}
@ -157,6 +189,77 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
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)
{
#if defined(__x86__) || defined(__x86_64__)
@ -1179,11 +1282,11 @@ static int unicode_case1(CharRange *cr, int case_mask)
#define MR(x) (1 << RUN_TYPE_ ## x)
const uint32_t tab_run_mask[3] = {
MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3),
MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2),
MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2),
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),
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),
};
#undef MR
uint32_t mask, v, code, type, len, i, idx;
@ -1237,6 +1340,135 @@ static int unicode_case1(CharRange *cr, int case_mask)
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 {
POP_GC,
POP_PROP,

View file

@ -41,6 +41,7 @@ typedef enum {
} UnicodeNormalizationEnum;
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_case_ignorable(uint32_t c);
@ -101,6 +102,8 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
int cr_invert(CharRange *cr);
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode);
#ifdef CONFIG_ALL_UNICODE
LRE_BOOL lre_is_id_start(uint32_t c);

View file

@ -36,8 +36,7 @@ struct list_head {
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) \
((type *)((uint8_t *)(el) - offsetof(type, member)))
#define list_entry(el, type, member) container_of(el, type, member)
static inline void init_list_head(struct list_head *head)
{

View file

@ -140,19 +140,19 @@ static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
}
/* default memory allocation functions with memory limitation */
static inline size_t js_trace_malloc_usable_size(void *ptr)
static size_t js_trace_malloc_usable_size(const void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize(ptr);
return _msize((void *)ptr);
#elif defined(EMSCRIPTEN)
return 0;
#elif defined(__linux__)
return malloc_usable_size(ptr);
return malloc_usable_size((void *)ptr);
#else
/* change this to `return 0;` if compilation fails */
return malloc_usable_size(ptr);
return malloc_usable_size((void *)ptr);
#endif
}
@ -264,18 +264,7 @@ static const JSMallocFunctions trace_mf = {
js_trace_malloc,
js_trace_free,
js_trace_realloc,
#if defined(__APPLE__)
malloc_size,
#elif defined(_WIN32)
(size_t (*)(const void *))_msize,
#elif defined(EMSCRIPTEN)
NULL,
#elif defined(__linux__)
(size_t (*)(const void *))malloc_usable_size,
#else
/* change this to `NULL,` if compilation fails */
malloc_usable_size,
#endif
js_trace_malloc_usable_size,
};
#define PROG_NAME "qjs"
@ -454,8 +443,10 @@ int main(int argc, char **argv)
}
}
#ifdef CONFIG_BIGNUM
if (load_jscalc)
bignum_ext = 1;
#endif
if (trace_memory) {
js_trace_malloc_init(&trace_data);

View file

@ -76,9 +76,7 @@ static const FeatureEntry feature_list[] = {
{ "promise", "Promise" },
#define FE_MODULE_LOADER 9
{ "module-loader", NULL },
#ifdef CONFIG_BIGNUM
{ "bigint", "BigInt" },
#endif
};
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
@ -332,6 +330,7 @@ static const char main_c_template1[] =
static const char main_c_template2[] =
" js_std_loop(ctx);\n"
" js_std_free_handlers(rt);\n"
" JS_FreeContext(ctx);\n"
" JS_FreeRuntime(rt);\n"
" return 0;\n"
@ -345,8 +344,8 @@ void help(void)
"usage: " PROG_NAME " [options] [files]\n"
"\n"
"options are:\n"
"-c only output bytecode in a C file\n"
"-e output main() and bytecode in a C file (default = executable output)\n"
"-c only output bytecode to a C file\n"
"-e output main() and bytecode to a C file (default = executable output)\n"
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module (default=autodetect)\n"

View file

@ -82,6 +82,7 @@ DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(message, "message")
DEF(cause, "cause")
DEF(errors, "errors")
DEF(stack, "stack")
DEF(name, "name")
@ -166,22 +167,23 @@ DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
DEF(indices, "indices")
DEF(status, "status")
DEF(reason, "reason")
DEF(globalThis, "globalThis")
#ifdef CONFIG_BIGNUM
DEF(bigint, "bigint")
#ifdef CONFIG_BIGNUM
DEF(bigfloat, "bigfloat")
DEF(bigdecimal, "bigdecimal")
DEF(roundingMode, "roundingMode")
DEF(maximumSignificantDigits, "maximumSignificantDigits")
DEF(maximumFractionDigits, "maximumFractionDigits")
#endif
#ifdef CONFIG_ATOMICS
/* the following 3 atoms are only used with CONFIG_ATOMICS */
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
#endif
/* */
DEF(toJSON, "toJSON")
/* class names */
DEF(Object, "Object")
@ -209,15 +211,13 @@ DEF(Int16Array, "Int16Array")
DEF(Uint16Array, "Uint16Array")
DEF(Int32Array, "Int32Array")
DEF(Uint32Array, "Uint32Array")
#ifdef CONFIG_BIGNUM
DEF(BigInt64Array, "BigInt64Array")
DEF(BigUint64Array, "BigUint64Array")
#endif
DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView")
#ifdef CONFIG_BIGNUM
DEF(BigInt, "BigInt")
#ifdef CONFIG_BIGNUM
DEF(BigFloat, "BigFloat")
DEF(BigFloatEnv, "BigFloatEnv")
DEF(BigDecimal, "BigDecimal")

View file

@ -751,6 +751,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
JSValue ret;
JSValueConst options_obj;
BOOL backtrace_barrier = FALSE;
BOOL is_async = FALSE;
int flags;
if (argc >= 2) {
@ -758,6 +759,9 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
if (get_bool_option(ctx, &backtrace_barrier, options_obj,
"backtrace_barrier"))
return JS_EXCEPTION;
if (get_bool_option(ctx, &is_async, options_obj,
"async"))
return JS_EXCEPTION;
}
str = JS_ToCStringLen(ctx, &len, argv[0]);
@ -770,6 +774,8 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
flags = JS_EVAL_TYPE_GLOBAL;
if (backtrace_barrier)
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
if (is_async)
flags |= JS_EVAL_FLAG_ASYNC;
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
JS_FreeCString(ctx, str);
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
@ -1970,6 +1976,13 @@ static int64_t get_time_ms(void)
clock_gettime(CLOCK_MONOTONIC, &ts);
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
/* more portable, but does not work if the date is updated */
static int64_t get_time_ms(void)
@ -1978,8 +1991,21 @@ static int64_t get_time_ms(void)
gettimeofday(&tv, NULL);
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
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)
{
if (th->link.prev) {
@ -2062,6 +2088,38 @@ static JSClassDef js_os_timer_class = {
.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)
{
JSValue ret, func1;
@ -3030,6 +3088,13 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
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] */
static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
@ -3274,6 +3339,7 @@ static void *worker_func(void *opaque)
JSRuntime *rt;
JSThreadState *ts;
JSContext *ctx;
JSValue promise;
rt = JS_NewRuntime();
if (rt == NULL) {
@ -3300,8 +3366,11 @@ static void *worker_func(void *opaque)
js_std_add_helpers(ctx, -1, NULL);
if (!JS_RunModule(ctx, args->basename, args->filename))
promise = JS_LoadModule(ctx, args->basename, args->filename);
if (JS_IsException(promise))
js_std_dump_error(ctx);
/* XXX: check */
JS_FreeValue(ctx, promise);
free(args->filename);
free(args->basename);
free(args);
@ -3621,8 +3690,10 @@ static const JSCFunctionListEntry js_os_funcs[] = {
OS_FLAG(SIGTTIN),
OS_FLAG(SIGTTOU),
#endif
JS_CFUNC_DEF("now", 0, js_os_now ),
JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
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_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
@ -3650,6 +3721,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
JS_CFUNC_DEF("exec", 1, js_os_exec ),
JS_CFUNC_DEF("getpid", 0, js_os_getpid ),
JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),
OS_FLAG(WNOHANG),
JS_CFUNC_DEF("pipe", 0, js_os_pipe ),

View file

@ -172,6 +172,7 @@ DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_loc_checkthis, 3, 0, 1, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
@ -182,6 +183,7 @@ DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
DEF( to_object, 1, 1, 1, none)
//DEF( to_string, 1, 1, 1, none)
@ -208,7 +210,6 @@ DEF( for_of_next, 2, 3, 5, u8)
DEF(iterator_check_object, 1, 1, 1, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF( iterator_close, 1, 3, 0, none)
DEF(iterator_close_return, 1, 4, 4, none)
DEF( iterator_next, 1, 4, 4, none)
DEF( iterator_call, 2, 4, 5, u8)
DEF( initial_yield, 1, 0, 0, none)
@ -256,6 +257,7 @@ DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
DEF( private_in, 1, 2, 1, none)
#ifdef CONFIG_BIGNUM
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
@ -270,6 +272,8 @@ 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 */
/* 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, 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 */
@ -277,10 +281,13 @@ 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_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_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_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 2, 0, 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( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */

File diff suppressed because it is too large Load diff

View file

@ -307,6 +307,9 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
/* don't include the stack frames before this eval in the Error() backtraces */
#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 JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
@ -733,13 +736,13 @@ JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx);
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val,
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
JSAtom prop, JSValue val, JSValueConst this_obj,
int flags);
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val)
{
return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);
return JS_SetPropertyInternal(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW);
}
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx, JSValue val);
@ -831,7 +834,15 @@ typedef struct {
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
const JSSharedArrayBufferFunctions *sf);
typedef enum JSPromiseStateEnum {
JS_PROMISE_PENDING,
JS_PROMISE_FULFILLED,
JS_PROMISE_REJECTED,
} JSPromiseStateEnum;
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 */
typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
@ -902,7 +913,7 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
/* only exported for os.Worker() */
JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
/* only exported for os.Worker() */
JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
JSValue JS_LoadModule(JSContext *ctx, const char *basename,
const char *filename);
/* C function definition */

View file

@ -42,6 +42,7 @@
//#define DUMP_TABLE_SIZE
//#define DUMP_CC_TABLE
//#define DUMP_DECOMP_TABLE
//#define DUMP_CASE_FOLDING_SPECIAL_CASES
/* Ideas:
- Generalize run length encoding + index for all tables
@ -217,15 +218,16 @@ static const char *unicode_prop_short_name[] = {
#undef DEF
};
#undef UNICODE_SPROP_LIST
#undef UNICODE_PROP_LIST
typedef struct {
/* case conv */
uint8_t u_len;
uint8_t l_len;
int u_data[CC_LEN_MAX];
int l_data[CC_LEN_MAX];
int f_code;
uint8_t f_len;
int u_data[CC_LEN_MAX]; /* to upper case */
int l_data[CC_LEN_MAX]; /* to lower case */
int f_data[CC_LEN_MAX]; /* to case folding */
uint8_t combining_class;
uint8_t is_compat:1;
@ -499,7 +501,7 @@ void parse_case_folding(CCInfo *tab, const char *filename)
FILE *f;
char line[1024];
const char *p;
int code;
int code, status;
CCInfo *ci;
f = fopen(filename, "rb");
@ -530,14 +532,28 @@ void parse_case_folding(CCInfo *tab, const char *filename)
/* locale dependent casing */
while (isspace(*p))
p++;
if (*p != 'C' && *p != 'S')
status = *p;
if (status != 'C' && status != 'S' && status != 'F')
continue;
p = get_field(line, 2);
assert(p != 0);
assert(ci->f_code == 0);
ci->f_code = strtoul(p, NULL, 16);
assert(ci->f_code != 0 && ci->f_code != code);
assert(p != NULL);
if (status == 'S') {
/* we always select the simple case folding and assume it
* comes after the full case folding case */
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);
@ -864,19 +880,21 @@ void dump_cc_info(CCInfo *ci, int i)
for(j = 0; j < ci->l_len; j++)
printf(" %05x", ci->l_data[j]);
}
if (ci->f_code != 0) {
printf(" F: %05x", ci->f_code);
if (ci->f_len != 0) {
printf(" F:");
for(j = 0; j < ci->f_len; j++)
printf(" %05x", ci->f_data[j]);
}
printf("\n");
}
void dump_data(CCInfo *tab)
void dump_unicode_data(CCInfo *tab)
{
int i;
CCInfo *ci;
for(i = 0; i <= CHARCODE_MAX; i++) {
ci = &tab[i];
if (ci->u_len != 0 || ci->l_len != 0 || ci->f_code != 0) {
if (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0) {
dump_cc_info(ci, i);
}
}
@ -886,8 +904,8 @@ BOOL is_complicated_case(const CCInfo *ci)
{
return (ci->u_len > 1 || ci->l_len > 1 ||
(ci->u_len > 0 && ci->l_len > 0) ||
(ci->f_code != 0) != ci->l_len ||
(ci->f_code != 0 && ci->l_data[0] != ci->f_code));
(ci->f_len != ci->l_len) ||
(memcmp(ci->f_data, ci->l_data, ci->f_len * sizeof(ci->f_data[0])) != 0));
}
#ifndef USE_TEST
@ -903,9 +921,9 @@ enum {
RUN_TYPE_UF_D1_EXT,
RUN_TYPE_U_EXT,
RUN_TYPE_LF_EXT,
RUN_TYPE_U_EXT2,
RUN_TYPE_L_EXT2,
RUN_TYPE_U_EXT3,
RUN_TYPE_UF_EXT2,
RUN_TYPE_LF_EXT2,
RUN_TYPE_UF_EXT3,
};
#endif
@ -921,9 +939,9 @@ const char *run_type_str[] = {
"UF_D1_EXT",
"U_EXT",
"LF_EXT",
"U_EXT2",
"L_EXT2",
"U_EXT3",
"UF_EXT2",
"LF_EXT2",
"UF_EXT3",
};
typedef struct {
@ -936,6 +954,13 @@ typedef struct {
int data_index; /* 'data' coming from the table */
} 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) */
void find_run_type(TableEntry *te, CCInfo *tab, int code)
@ -949,15 +974,15 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
te->code = code;
if (ci->l_len == 1 && ci->l_data[0] == code + 2 &&
ci->f_code == ci->l_data[0] &&
ci->f_len == 1 && ci->f_data[0] == ci->l_data[0] &&
ci->u_len == 0 &&
ci1->l_len == 1 && ci1->l_data[0] == code + 2 &&
ci1->f_code == ci1->l_data[0] &&
ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0] &&
ci1->u_len == 1 && ci1->u_data[0] == code &&
ci2->l_len == 0 &&
ci2->f_code == 0 &&
ci2->f_len == 0 &&
ci2->u_len == 1 && ci2->u_data[0] == code) {
te->len = 3;
te->data = 0;
@ -972,7 +997,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
if (ci1->u_len != 1 ||
ci1->u_data[0] != ci->u_data[0] + len ||
ci1->l_len != 0 ||
ci1->f_code != ci1->u_data[0])
ci1->f_len != 1 || ci1->f_data[0] != ci1->u_data[0])
break;
len++;
}
@ -983,21 +1008,25 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
return;
}
if (ci->u_len == 2 && ci->u_data[1] == 0x399 &&
ci->f_code == 0 && ci->l_len == 0) {
if (ci->l_len == 0 &&
ci->u_len == 2 && ci->u_data[1] == 0x399 &&
ci->f_len == 2 && ci->f_data[1] == 0x3B9 &&
ci->f_data[0] == simple_to_lower(tab, ci->u_data[0])) {
len = 1;
while (code + len <= CHARCODE_MAX) {
ci1 = &tab[code + len];
if (!(ci1->u_len == 2 &&
ci1->u_data[1] == 0x399 &&
ci1->u_data[1] == ci->u_data[1] &&
ci1->u_data[0] == ci->u_data[0] + len &&
ci1->f_code == 0 &&
ci1->f_len == 2 &&
ci1->f_data[1] == ci->f_data[1] &&
ci1->f_data[0] == ci->f_data[0] + len &&
ci1->l_len == 0))
break;
len++;
}
te->len = len;
te->type = RUN_TYPE_U_EXT2;
te->type = RUN_TYPE_UF_EXT2;
te->ext_data[0] = ci->u_data[0];
te->ext_data[1] = ci->u_data[1];
te->ext_len = 2;
@ -1005,7 +1034,8 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
}
if (ci->u_len == 2 && ci->u_data[1] == 0x399 &&
ci->l_len == 1 && ci->f_code == ci->l_data[0]) {
ci->l_len == 1 &&
ci->f_len == 1 && ci->f_data[0] == ci->l_data[0]) {
len = 1;
while (code + len <= CHARCODE_MAX) {
ci1 = &tab[code + len];
@ -1014,7 +1044,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
ci1->u_data[0] == ci->u_data[0] + len &&
ci1->l_len == 1 &&
ci1->l_data[0] == ci->l_data[0] + len &&
ci1->f_code == ci1->l_data[0]))
ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0]))
break;
len++;
}
@ -1026,13 +1056,13 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
return;
}
if (ci->l_len == 1 && ci->u_len == 0 && ci->f_code == 0) {
if (ci->l_len == 1 && ci->u_len == 0 && ci->f_len == 0) {
len = 1;
while (code + len <= CHARCODE_MAX) {
ci1 = &tab[code + len];
if (!(ci1->l_len == 1 &&
ci1->l_data[0] == ci->l_data[0] + len &&
ci1->u_len == 0 && ci1->f_code == 0))
ci1->u_len == 0 && ci1->f_len == 0))
break;
len++;
}
@ -1045,32 +1075,39 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
if (ci->l_len == 0 &&
ci->u_len == 1 &&
ci->u_data[0] < 0x1000 &&
ci->f_code == ci->u_data[0] + 0x20) {
ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 0x20) {
te->len = 1;
te->type = RUN_TYPE_UF_D20;
te->data = ci->u_data[0];
} else if (ci->l_len == 0 &&
ci->u_len == 1 &&
ci->f_code == ci->u_data[0] + 1) {
ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 1) {
te->len = 1;
te->type = RUN_TYPE_UF_D1_EXT;
te->ext_data[0] = ci->u_data[0];
te->ext_len = 1;
} else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_code == 0) {
} else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_len == 2 &&
ci->l_data[0] == ci->f_data[0] &&
ci->l_data[1] == ci->f_data[1]) {
te->len = 1;
te->type = RUN_TYPE_L_EXT2;
te->type = RUN_TYPE_LF_EXT2;
te->ext_data[0] = ci->l_data[0];
te->ext_data[1] = ci->l_data[1];
te->ext_len = 2;
} else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_code == 0) {
} else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 2 &&
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->type = RUN_TYPE_U_EXT2;
te->type = RUN_TYPE_UF_EXT2;
te->ext_data[0] = ci->u_data[0];
te->ext_data[1] = ci->u_data[1];
te->ext_len = 2;
} else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_code == 0) {
} else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 3 &&
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->type = RUN_TYPE_U_EXT3;
te->type = RUN_TYPE_UF_EXT3;
te->ext_data[0] = ci->u_data[0];
te->ext_data[1] = ci->u_data[1];
te->ext_data[2] = ci->u_data[2];
@ -1188,7 +1225,7 @@ void build_conv_table(CCInfo *tab)
te = conv_table;
for(code = 0; code <= CHARCODE_MAX; code++) {
ci = &tab[code];
if (ci->u_len == 0 && ci->l_len == 0 && ci->f_code == 0)
if (ci->u_len == 0 && ci->l_len == 0 && ci->f_len == 0)
continue;
assert(te - conv_table < countof(conv_table));
find_run_type(te, tab, code);
@ -1244,7 +1281,7 @@ void build_conv_table(CCInfo *tab)
/* find the data index for ext_data */
for(i = 0; i < conv_table_len; i++) {
te = &conv_table[i];
if (te->type == RUN_TYPE_U_EXT3) {
if (te->type == RUN_TYPE_UF_EXT3) {
int p, v;
v = 0;
for(j = 0; j < 3; j++) {
@ -1258,8 +1295,8 @@ void build_conv_table(CCInfo *tab)
for(i = 0; i < conv_table_len; i++) {
te = &conv_table[i];
if (te->type == RUN_TYPE_L_EXT2 ||
te->type == RUN_TYPE_U_EXT2 ||
if (te->type == RUN_TYPE_LF_EXT2 ||
te->type == RUN_TYPE_UF_EXT2 ||
te->type == RUN_TYPE_U2L_399_EXT2) {
int p, v;
v = 0;
@ -1322,6 +1359,54 @@ void dump_case_conv_table(FILE *f)
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 i;
@ -1348,7 +1433,7 @@ void compute_internal_props(void)
for(i = 0; i <= CHARCODE_MAX; i++) {
CCInfo *ci = &unicode_db[i];
has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_code != 0);
has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0);
if (has_ul) {
assert(get_prop(i, PROP_Cased));
} else {
@ -1363,10 +1448,10 @@ void compute_internal_props(void)
set_prop(i, PROP_Changes_When_Titlecased1,
get_prop(i, PROP_Changes_When_Titlecased) ^ (ci->u_len != 0));
set_prop(i, PROP_Changes_When_Casefolded1,
get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_code != 0));
get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_len != 0));
/* XXX: reduce table size (438 bytes) */
set_prop(i, PROP_Changes_When_NFKC_Casefolded1,
get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_code != 0));
get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_len != 0));
#if 0
/* TEST */
#define M(x) (1U << GCAT_ ## x)
@ -1797,8 +1882,10 @@ void check_case_conv(void)
ci->u_len = 1;
ci->u_data[0] = code;
}
if (ci->f_code == 0)
ci->f_code = code;
if (ci->f_len == 0) {
ci->f_len = 1;
ci->f_data[0] = code;
}
error = 0;
l = check_conv(res, code, 0);
@ -1812,7 +1899,7 @@ void check_case_conv(void)
error++;
}
l = check_conv(res, code, 2);
if (l != 1 || res[0] != ci->f_code) {
if (l != ci->f_len || tabcmp((int *)res, ci->f_data, l)) {
printf("ERROR: F\n");
error++;
}
@ -3007,11 +3094,12 @@ int main(int argc, char **argv)
unicode_db_path);
parse_prop_list(filename);
// dump_data(unicode_db);
// dump_unicode_data(unicode_db);
build_conv_table(unicode_db);
// dump_table();
#ifdef DUMP_CASE_FOLDING_SPECIAL_CASES
dump_case_folding_special_cases(unicode_db);
#endif
if (!outfilename) {
#ifdef USE_TEST

View file

@ -72,6 +72,7 @@ DEF(Coptic, "Copt,Qaac")
DEF(Cuneiform, "Xsux")
DEF(Cypriot, "Cprt")
DEF(Cyrillic, "Cyrl")
DEF(Cypro_Minoan, "Cpmn")
DEF(Deseret, "Dsrt")
DEF(Devanagari, "Deva")
DEF(Dives_Akuru, "Diak")
@ -104,6 +105,7 @@ DEF(Javanese, "Java")
DEF(Kaithi, "Kthi")
DEF(Kannada, "Knda")
DEF(Katakana, "Kana")
DEF(Kawi, "Kawi")
DEF(Kayah_Li, "Kali")
DEF(Kharoshthi, "Khar")
DEF(Khmer, "Khmr")
@ -138,6 +140,7 @@ DEF(Mro, "Mroo")
DEF(Multani, "Mult")
DEF(Myanmar, "Mymr")
DEF(Nabataean, "Nbat")
DEF(Nag_Mundari, "Nagm")
DEF(Nandinagari, "Nand")
DEF(New_Tai_Lue, "Talu")
DEF(Newa, "Newa")
@ -154,6 +157,7 @@ DEF(Old_Persian, "Xpeo")
DEF(Old_Sogdian, "Sogo")
DEF(Old_South_Arabian, "Sarb")
DEF(Old_Turkic, "Orkh")
DEF(Old_Uyghur, "Ougr")
DEF(Oriya, "Orya")
DEF(Osage, "Osge")
DEF(Osmanya, "Osma")
@ -192,8 +196,11 @@ DEF(Thai, "Thai")
DEF(Tibetan, "Tibt")
DEF(Tifinagh, "Tfng")
DEF(Tirhuta, "Tirh")
DEF(Tangsa, "Tnsa")
DEF(Toto, "Toto")
DEF(Ugaritic, "Ugar")
DEF(Vai, "Vaii")
DEF(Vithkuqi, "Vith")
DEF(Wancho, "Wcho")
DEF(Warang_Citi, "Wara")
DEF(Yezidi, "Yezi")

View file

@ -19,6 +19,7 @@ use crate::{
use value::{JsFunction, OwnedJsObject};
#[allow(unused_imports)]
pub use value::{JsCompiledFunction, OwnedJsValue};
// JS_TAG_* constants from quickjs.

View file

@ -578,7 +578,7 @@ fn test_bigint_serialize_bigint() {
}
}
#[cfg(feature = "patch_dateparser")]
#[cfg(feature = "patch-dateparser")]
mod dateparser {
use super::*;