You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2026/03/23 21:40:37 UTC
(couchdb) branch upgrade-quickjs-mar-23-2026 created (now f96080bf8)
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a change to branch upgrade-quickjs-mar-23-2026
in repository https://gitbox.apache.org/repos/asf/couchdb.git
at f96080bf8 Update QuickJS: bytecode size checks, memory fixes, test262
This branch includes the following new commits:
new f96080bf8 Update QuickJS: bytecode size checks, memory fixes, test262
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(couchdb) 01/01: Update QuickJS: bytecode size checks, memory fixes, test262
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch upgrade-quickjs-mar-23-2026
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit f96080bf82d98ce04d1aa6e6d0c424a5c88decd7
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Mon Mar 23 17:23:40 2026 -0400
Update QuickJS: bytecode size checks, memory fixes, test262
* Test262 updated: 83341 -> 83394 tests pass
* Added JSON.parse source text access
https://github.com/bellard/quickjs/commit/d7ae12ae71dfd6ab2997527d295014a8996fa0f9
* Added basic protection against too large function in serialized bytecode
https://github.com/bellard/quickjs/commit/a31dcef98c7d1d8927871ac169a1c24ad8875046
Fixes:
* Use-after-free via re-entrant GC in FinalizationRegistry weak ref cleanup
https://github.com/bellard/quickjs/commit/5022f2b76ab0e0c1f535224c796bef7bb1ab8e7d
* Async generator lifecycle bug
https://github.com/bellard/quickjs/commit/e7b9f217b46d567aed203918ba0c36f234d2bd3f
* `RegExp.escape`
https://github.com/bellard/quickjs/commit/4d16546cdf60cdec670ea6f033253140f861ba55
* Buffer overflow in Atomics with resizable typed arrays
https://github.com/bellard/quickjs/commit/46bd985b337b1a66597e30c38dd45c8e1f49feee
* Memory leak in Iterator.prototype.map
https://github.com/bellard/quickjs/commit/f1b63fc1ad0b62262c069b2e1615737b2bb63631
* Stack underflow with generator in iterable
https://github.com/bellard/quickjs/commit/69090b969fa9d01deeb05011a2bf8537ddf61266
* TypedArray constructor semantics which removes a buffer overflow
https://github.com/bellard/quickjs/commit/68caa5f226401a7e148fb8f7358d4688c71372aa
* Buffer overflow when the fast array is resized
https://github.com/bellard/quickjs/commit/0989d4cb4a1e26f12ba1d65e3d5963342211b1dd
* GC traversal of uninitialized fast-array
https://github.com/bellard/quickjs/commit/4c722cea4e709682003eb0da67b393ea38c56e5e
---
.../patches/01-spidermonkey-185-mode.patch | 6 +-
src/couch_quickjs/patches/02-test262-errors.patch | 4 +-
src/couch_quickjs/quickjs/Makefile | 2 +-
src/couch_quickjs/quickjs/quickjs-atom.h | 1 +
src/couch_quickjs/quickjs/quickjs.c | 771 +++++++++++++++------
src/couch_quickjs/quickjs/test262.conf | 6 +-
src/couch_quickjs/quickjs/test262_errors.txt | 6 -
7 files changed, 579 insertions(+), 217 deletions(-)
diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
index db740bf2b..65f473856 100644
--- a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
+++ b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
@@ -1,6 +1,6 @@
---- quickjs-master/quickjs.c 2025-12-22 09:12:46
-+++ quickjs/quickjs.c 2026-01-04 23:55:00
-@@ -31443,10 +31443,24 @@
+--- quickjs-master/quickjs.c 2026-03-23 13:45:52
++++ quickjs/quickjs.c 2026-03-23 17:19:56
+@@ -31462,10 +31462,24 @@
if (s->token.val == TOK_FUNCTION ||
(token_is_pseudo_keyword(s, JS_ATOM_async) &&
peek_token(s, TRUE) == TOK_FUNCTION)) {
diff --git a/src/couch_quickjs/patches/02-test262-errors.patch b/src/couch_quickjs/patches/02-test262-errors.patch
index 81646c900..db454f201 100644
--- a/src/couch_quickjs/patches/02-test262-errors.patch
+++ b/src/couch_quickjs/patches/02-test262-errors.patch
@@ -1,5 +1,5 @@
---- quickjs-master/test262_errors.txt 2025-12-22 09:12:46
-+++ quickjs/test262_errors.txt 2026-01-04 23:55:00
+--- quickjs-master/test262_errors.txt 2026-03-23 13:45:52
++++ quickjs/test262_errors.txt 2026-03-23 17:19:56
@@ -23,6 +23,8 @@
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from-and-import-star-as-and-export.js:74: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-import-star-as-and-export.js' is ambiguous
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js:75: SyntaxError: export 'foo' in module 'test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js' is ambiguous
diff --git a/src/couch_quickjs/quickjs/Makefile b/src/couch_quickjs/quickjs/Makefile
index dcbbf7e5e..218236826 100644
--- a/src/couch_quickjs/quickjs/Makefile
+++ b/src/couch_quickjs/quickjs/Makefile
@@ -55,7 +55,7 @@ PREFIX?=/usr/local
#CONFIG_UBSAN=y
# TEST262 bootstrap config: commit id and shallow "since" parameter
-TEST262_COMMIT?=d0994d64b07cb6c164dd9f345c94ed797a53d69f
+TEST262_COMMIT?=5c8206929d81b2d3d727ca6aac56c18358c8d790
TEST262_SINCE?=2025-09-01
OBJDIR=.obj
diff --git a/src/couch_quickjs/quickjs/quickjs-atom.h b/src/couch_quickjs/quickjs/quickjs-atom.h
index dac2df678..dc64f2fb0 100644
--- a/src/couch_quickjs/quickjs/quickjs-atom.h
+++ b/src/couch_quickjs/quickjs/quickjs-atom.h
@@ -147,6 +147,7 @@ DEF(flags, "flags")
DEF(global, "global")
DEF(unicode, "unicode")
DEF(raw, "raw")
+DEF(rawJSON, "rawJSON")
DEF(new_target, "new.target")
DEF(this_active_func, "this.active_func")
DEF(home_object, "<home_object>")
diff --git a/src/couch_quickjs/quickjs/quickjs.c b/src/couch_quickjs/quickjs/quickjs.c
index 6cad52d76..972f9787b 100644
--- a/src/couch_quickjs/quickjs/quickjs.c
+++ b/src/couch_quickjs/quickjs/quickjs.c
@@ -168,6 +168,7 @@ enum {
JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
JS_CLASS_GENERATOR, /* u.generator_data */
JS_CLASS_GLOBAL_OBJECT, /* u.global_object */
+ JS_CLASS_RAWJSON,
JS_CLASS_PROXY, /* u.proxy_data */
JS_CLASS_PROMISE, /* u.promise_data */
JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
@@ -1597,6 +1598,7 @@ static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
{ JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
{ JS_ATOM_Object, js_global_object_finalizer, js_global_object_mark }, /* JS_CLASS_GLOBAL_OBJECT */
+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_RAWJSON */
};
static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
@@ -1841,15 +1843,17 @@ int JS_GetStripInfo(JSRuntime *rt)
return rt->strip_flags;
}
-/* return 0 if OK, < 0 if exception */
-int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
- int argc, JSValueConst *argv)
+static int JS_EnqueueJob2(JSContext *ctx, JSJobFunc *job_func,
+ int argc, JSValueConst *argv, BOOL no_exception)
{
JSRuntime *rt = ctx->rt;
JSJobEntry *e;
int i;
- e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
+ if (no_exception)
+ e = js_malloc_rt(ctx->rt, sizeof(*e) + argc * sizeof(JSValue));
+ else
+ e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
if (!e)
return -1;
e->realm = JS_DupContext(ctx);
@@ -1862,6 +1866,13 @@ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
return 0;
}
+/* return 0 if OK, < 0 if exception */
+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
+ int argc, JSValueConst *argv)
+{
+ return JS_EnqueueJob2(ctx, job_func, argc, argv, FALSE);
+}
+
BOOL JS_IsJobPending(JSRuntime *rt)
{
return !list_empty(&rt->job_list);
@@ -9153,14 +9164,15 @@ static inline int add_fast_array_element(JSContext *ctx, JSObject *p,
return TRUE;
}
-/* Allocate a new fast array. Its 'length' property is set to zero. It
- maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit
- integer. WARNING: the content of the array is not initialized. */
+/* Allocate a new fast array initialized to JS_UNDEFINED. Its maximum
+ size is 2^31-1 elements. For convenience, 'len' is a 64 bit
+ integer. */
static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
{
JSValue arr;
JSObject *p;
-
+ int i;
+
if (len > INT32_MAX)
return JS_ThrowRangeError(ctx, "invalid array length");
arr = JS_NewArray(ctx);
@@ -9173,6 +9185,10 @@ static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
return JS_EXCEPTION;
}
p->u.array.count = len;
+ for(i = 0; i < len; i++)
+ p->u.array.u.values[i] = JS_UNDEFINED;
+ /* update the 'length' field */
+ set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len));
}
return arr;
}
@@ -21100,9 +21116,8 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx,
} else {
js_async_generator_resolve(ctx, s, arg, TRUE);
}
- } else {
+ } else if (s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING) {
/* restart function execution after await() */
- assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
s->func_state->throw_flag = is_reject;
if (is_reject) {
JS_Throw(ctx, JS_DupValue(ctx, arg));
@@ -21558,7 +21573,7 @@ typedef struct JSParseState {
JSFunctionDef *cur_func;
BOOL is_module; /* parsing a module */
BOOL allow_html_comments;
- BOOL ext_json; /* true if accepting JSON superset */
+ BOOL ext_json; /* JSON parsing: true if accepting JSON superset */
GetLineColCache get_line_col_cache;
} JSParseState;
@@ -28191,8 +28206,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
JS_FreeAtom(ctx, var_name);
if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
- break_entry.has_iterator = is_for_of = TRUE;
- break_entry.drop_count += 2;
+ is_for_of = TRUE;
if (has_initializer)
goto initializer_error;
} else if (s->token.val == TOK_IN) {
@@ -28221,6 +28235,11 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
the TDZ values are in the closures */
close_scopes(s, s->cur_func->scope_level, block_scope_level);
if (is_for_of) {
+ /* set has_iterator after the iterable expression is parsed so
+ that a yield in the expression does not try to close a
+ not-yet-created iterator */
+ break_entry.has_iterator = TRUE;
+ break_entry.drop_count += 2;
if (is_async)
emit_op(s, OP_for_await_of_start);
else
@@ -28774,7 +28793,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
if (js_parse_expect(s, '}'))
goto fail;
if (default_label_pos >= 0) {
- /* Ugly patch for the the `default` label, shameful and risky */
+ /* Ugly patch for the `default` label, shameful and risky */
put_u32(s->cur_func->byte_code.buf + default_label_pos,
label_case);
s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
@@ -38160,9 +38179,10 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
uint16_t v16;
uint8_t v8;
int idx, i, local_count;
- int function_size, cpool_offset, byte_code_offset;
+ int cpool_offset, byte_code_offset;
int closure_var_offset, vardefs_offset;
-
+ uint64_t function_size;
+
memset(&bc, 0, sizeof(bc));
bc.header.ref_count = 1;
//bc.gc_header.mark = 0;
@@ -38212,16 +38232,19 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
function_size = offsetof(JSFunctionBytecode, debug);
}
cpool_offset = function_size;
- function_size += bc.cpool_count * sizeof(*bc.cpool);
+ function_size += (uint64_t)bc.cpool_count * sizeof(*bc.cpool);
vardefs_offset = function_size;
- function_size += local_count * sizeof(*bc.vardefs);
+ function_size += (uint64_t)local_count * sizeof(*bc.vardefs);
closure_var_offset = function_size;
- function_size += bc.closure_var_count * sizeof(*bc.closure_var);
+ function_size += (uint64_t)bc.closure_var_count * sizeof(*bc.closure_var);
byte_code_offset = function_size;
if (!bc.read_only_bytecode) {
function_size += bc.byte_code_len;
}
+ if (function_size > INT32_MAX)
+ return JS_ThrowOutOfMemory(ctx);
+
b = js_mallocz(ctx, function_size);
if (!b)
return JS_EXCEPTION;
@@ -41409,21 +41432,14 @@ static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
} else {
for (; i < idx; i++, pval++)
if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto fill_and_fail;
+ goto exception;
*pval = JS_DupValue(ctx, argv[1]);
for (i++, pval++; i < len; i++, pval++) {
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
- fill_and_fail:
- for (; i < len; i++, pval++)
- *pval = JS_UNDEFINED;
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
goto exception;
- }
}
}
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
- goto exception;
-
ret = arr;
arr = JS_UNDEFINED;
@@ -42307,17 +42323,10 @@ static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
} else {
// Query order is observable; test262 expects descending order.
for (; i >= 0; i--, pval++) {
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
- // Exception; initialize remaining elements.
- for (; i >= 0; i--, pval++)
- *pval = JS_UNDEFINED;
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
goto exception;
- }
}
}
-
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
- goto exception;
}
ret = arr;
@@ -42506,17 +42515,11 @@ static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
assert(pval == last);
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, newlen)) < 0)
- goto exception;
-
done:
ret = arr;
arr = JS_UNDEFINED;
exception:
- while (pval != last)
- *pval++ = JS_UNDEFINED;
-
JS_FreeValue(ctx, arr);
JS_FreeValue(ctx, obj);
return ret;
@@ -42861,16 +42864,10 @@ static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
*pval = JS_DupValue(ctx, arrp[i]);
} else {
for (; i < len; i++, pval++) {
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
- for (; i < len; i++, pval++)
- *pval = JS_UNDEFINED;
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
goto exception;
- }
}
}
-
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
- goto exception;
}
ret = js_array_sort(ctx, arr, argc, argv);
@@ -43991,6 +43988,7 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValueConst this_val,
args[1] = index_val;
ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args);
JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, item);
if (JS_IsException(ret))
goto fail;
goto done;
@@ -47348,9 +47346,10 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val,
JSValue str;
StringBuffer b_s, *b = &b_s;
JSString *p;
- uint32_t c, i;
+ uint32_t c;
char s[16];
-
+ int i, i0;
+
if (!JS_IsString(argv[0]))
return JS_ThrowTypeError(ctx, "not a string");
str = JS_ToString(ctx, argv[0]); /* must call it to linearlize ropes */
@@ -47358,8 +47357,9 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION;
p = JS_VALUE_GET_STRING(str);
string_buffer_init2(ctx, b, 0, p->is_wide_char);
- for (i = 0; i < p->len; i++) {
- c = string_get(p, i);
+ for (i = 0; i < p->len; ) {
+ i0 = i;
+ c = string_getc(p, &i);
if (c < 33) {
if (c >= 9 && c <= 13) {
string_buffer_putc8(b, '\\');
@@ -47371,7 +47371,7 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val,
if ((c >= '0' && c <= '9')
|| (c >= 'A' && c <= 'Z')
|| (c >= 'a' && c <= 'z')) {
- if (i == 0)
+ if (i0 == 0)
goto hex2;
} else if (strchr(",-=<>#&!%:;@~'`\"", c)) {
goto hex2;
@@ -47387,7 +47387,7 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val,
snprintf(s, sizeof(s), "\\u%04x", c);
string_buffer_puts8(b, s);
} else {
- string_buffer_putc16(b, c);
+ string_buffer_putc(b, c);
}
}
JS_FreeValue(ctx, str);
@@ -48645,23 +48645,189 @@ static int json_parse_expect(JSParseState *s, int tok)
return json_next_token(s);
}
-static JSValue json_parse_value(JSParseState *s)
+
+typedef struct {
+ int count;
+ uint32_t hash_size;
+ struct JSONParseRecordEntry *entries;
+ uint32_t *hash_table;
+} JSONParseRecordObject;
+
+typedef struct JSONParseRecord {
+ JSValue value;
+ union {
+ JSONParseRecordObject obj;
+ struct {
+ int count;
+ struct JSONParseRecord *elements;
+ } array;
+ struct {
+ uint32_t source_pos;
+ uint32_t source_len;
+ } primitive;
+ } u;
+} JSONParseRecord;
+
+typedef struct JSONParseRecordEntry {
+ JSAtom atom;
+ uint32_t hash_next;
+ JSONParseRecord parse_record;
+} JSONParseRecordEntry;
+
+static void json_parse_record_init_obj(JSContext *ctx, JSONParseRecord *pr, JSValueConst val)
+{
+ pr->value = JS_DupValue(ctx, val);
+ pr->u.obj.count = 0;
+ pr->u.obj.entries = NULL;
+ pr->u.obj.hash_table = NULL;
+ pr->u.obj.hash_size = 0;
+}
+
+static void json_parse_record_init_array(JSContext *ctx, JSONParseRecord *pr, JSValueConst val)
+{
+ pr->value = JS_DupValue(ctx, val);
+ pr->u.array.count = 0;
+ pr->u.array.elements = NULL;
+}
+
+static void json_parse_record_init_primitive(JSContext *ctx, JSONParseRecord *pr, JSValueConst val,
+ uint32_t source_pos, uint32_t source_len)
+{
+ pr->value = JS_DupValue(ctx, val);
+ pr->u.primitive.source_pos = source_pos;
+ pr->u.primitive.source_len = source_len;
+}
+
+static int json_parse_record_resize_hash(JSContext *ctx, JSONParseRecordObject *po, uint32_t new_hash_size)
+{
+ uint32_t i, h, *new_hash_table;
+ JSONParseRecordEntry *e;
+
+ new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
+ if (!new_hash_table)
+ return -1;
+ js_free(ctx, po->hash_table);
+ po->hash_table = new_hash_table;
+ po->hash_size = new_hash_size;
+
+ for(i = 0; i < po->hash_size; i++) {
+ po->hash_table[i] = -1;
+ }
+ for(i = 0; i < po->count; i++) {
+ e = &po->entries[i];
+ h = e->atom & (po->hash_size - 1);
+ e->hash_next = po->hash_table[h];
+ po->hash_table[h] = i;
+ }
+ return 0;
+}
+
+static JSONParseRecord *json_parse_record_add(JSContext *ctx, JSONParseRecord *pr, JSAtom key, int *psize)
+{
+ JSONParseRecordObject *po = &pr->u.obj;
+ JSONParseRecordEntry *e;
+ JSONParseRecord *pr1;
+ uint32_t h;
+
+ if (js_resize_array(ctx, (void **)&po->entries, sizeof(po->entries[0]),
+ psize, po->count + 1)) {
+ return NULL;
+ }
+ /* don't use a hash table when the number of entries is small */
+ if (po->count >= 8 && (po->count + 1) > po->hash_size) {
+ int hash_bits = 32 - clz32(po->count);
+ if (json_parse_record_resize_hash(ctx, po, 1 << hash_bits))
+ return NULL;
+ }
+
+ e = &po->entries[po->count++];
+ e->atom = JS_DupAtom(ctx, key);
+ pr1 = &e->parse_record;
+ pr1->value = JS_UNDEFINED;
+ if (po->hash_size != 0) {
+ h = key & (po->hash_size - 1);
+ e->hash_next = po->hash_table[h];
+ po->hash_table[h] = po->count - 1;
+ }
+ return pr1;
+}
+
+static JSONParseRecord *json_parse_record_find(JSONParseRecord *pr, JSAtom key)
+{
+ JSONParseRecordObject *po = &pr->u.obj;
+ JSONParseRecordEntry *e;
+ uint32_t h, i;
+
+ if (po->hash_size == 0) {
+ for(i = 0; i < po->count; i++) {
+ if (po->entries[i].atom == key)
+ return &po->entries[i].parse_record;
+ }
+ } else {
+ h = key & (po->hash_size - 1);
+ i = po->hash_table[h];
+ while (i != -1) {
+ e = &po->entries[i];
+ if (e->atom == key)
+ return &e->parse_record;
+ i = e->hash_next;
+ }
+ }
+ return NULL;
+}
+
+static void json_free_parse_record(JSContext *ctx, JSONParseRecord *pr)
+{
+ int i;
+ if (!pr)
+ return;
+ if (JS_IsObject(pr->value)) {
+ if (JS_IsArray(ctx, pr->value)) {
+ for(i = 0; i < pr->u.array.count; i++) {
+ json_free_parse_record(ctx, &pr->u.array.elements[i]);
+ }
+ js_free(ctx, pr->u.array.elements);
+ } else {
+ for(i = 0; i < pr->u.obj.count; i++) {
+ JS_FreeAtom(ctx, pr->u.obj.entries[i].atom);
+ json_free_parse_record(ctx, &pr->u.obj.entries[i].parse_record);
+ }
+ js_free(ctx, pr->u.obj.entries);
+ js_free(ctx, pr->u.obj.hash_table);
+ }
+ }
+ JS_FreeValue(ctx, pr->value);
+ pr->value = JS_UNDEFINED; /* fail safe */
+}
+
+/* 'pr' can be NULL */
+static JSValue json_parse_value(JSParseState *s, JSONParseRecord *pr)
{
JSContext *ctx = s->ctx;
JSValue val = JS_NULL;
int ret;
+ if (pr) {
+ pr->value = JS_UNDEFINED;
+ }
+
switch(s->token.val) {
case '{':
{
JSValue prop_val;
JSAtom prop_name;
-
+ JSONParseRecord *pr1;
+ int pr_size;
+
if (json_next_token(s))
goto fail;
val = JS_NewObject(ctx);
if (JS_IsException(val))
goto fail;
+ if (pr) {
+ json_parse_record_init_obj(ctx, pr, val);
+ pr_size = 0;
+ }
if (s->token.val != '}') {
for(;;) {
if (s->token.val == TOK_STRING) {
@@ -48678,7 +48844,14 @@ static JSValue json_parse_value(JSParseState *s)
goto fail1;
if (json_parse_expect(s, ':'))
goto fail1;
- prop_val = json_parse_value(s);
+ if (pr) {
+ pr1 = json_parse_record_add(ctx, pr, prop_name, &pr_size);
+ if (!pr1)
+ goto fail1;
+ } else {
+ pr1 = NULL;
+ }
+ prop_val = json_parse_value(s, pr1);
if (JS_IsException(prop_val)) {
fail1:
JS_FreeAtom(ctx, prop_name);
@@ -48706,16 +48879,31 @@ static JSValue json_parse_value(JSParseState *s)
{
JSValue el;
uint32_t idx;
-
+ JSONParseRecord *pr1;
+ int pr_size;
+
if (json_next_token(s))
goto fail;
val = JS_NewArray(ctx);
if (JS_IsException(val))
goto fail;
+ if (pr) {
+ json_parse_record_init_array(ctx, pr, val);
+ pr_size = 0;
+ }
if (s->token.val != ']') {
idx = 0;
for(;;) {
- el = json_parse_value(s);
+ if (pr) {
+ if (js_resize_array(ctx, (void **)&pr->u.array.elements, sizeof(pr->u.array.elements[0]),
+ &pr_size, pr->u.array.count + 1))
+ goto fail;
+ pr1 = &pr->u.array.elements[pr->u.array.count++];
+ pr1->value = JS_UNDEFINED;
+ } else {
+ pr1 = NULL;
+ }
+ el = json_parse_value(s, pr1);
if (JS_IsException(el))
goto fail;
ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
@@ -48736,11 +48924,21 @@ static JSValue json_parse_value(JSParseState *s)
break;
case TOK_STRING:
val = JS_DupValue(ctx, s->token.u.str.str);
+ if (pr) {
+ json_parse_record_init_primitive(ctx, pr, val,
+ s->token.ptr - s->buf_start,
+ s->buf_ptr - s->token.ptr);
+ }
if (json_next_token(s))
goto fail;
break;
case TOK_NUMBER:
val = s->token.u.num.val;
+ if (pr) {
+ json_parse_record_init_primitive(ctx, pr, val,
+ s->token.ptr - s->buf_start,
+ s->buf_ptr - s->token.ptr);
+ }
if (json_next_token(s))
goto fail;
break;
@@ -48748,8 +48946,18 @@ static JSValue json_parse_value(JSParseState *s)
if (s->token.u.ident.atom == JS_ATOM_false ||
s->token.u.ident.atom == JS_ATOM_true) {
val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true);
+ if (pr) {
+ json_parse_record_init_primitive(ctx, pr, val,
+ s->token.ptr - s->buf_start,
+ s->buf_ptr - s->token.ptr);
+ }
} else if (s->token.u.ident.atom == JS_ATOM_null) {
val = JS_NULL;
+ if (pr) {
+ json_parse_record_init_primitive(ctx, pr, val,
+ s->token.ptr - s->buf_start,
+ s->buf_ptr - s->token.ptr);
+ }
} else if (s->token.u.ident.atom == JS_ATOM_NaN && s->ext_json) {
/* Note: json5 identifier handling is ambiguous e.g. is
'{ NaN: 1 }' a valid JSON5 production ? */
@@ -48774,12 +48982,13 @@ static JSValue json_parse_value(JSParseState *s)
}
return val;
fail:
+ json_free_parse_record(ctx, pr);
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
-JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
- const char *filename, int flags)
+JSValue JS_ParseJSON3(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags, JSONParseRecord *pr)
{
JSParseState s1, *s = &s1;
JSValue val = JS_UNDEFINED;
@@ -48788,12 +48997,14 @@ JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
if (json_next_token(s))
goto fail;
- val = json_parse_value(s);
+ val = json_parse_value(s, pr);
if (JS_IsException(val))
goto fail;
if (s->token.val != TOK_EOF) {
- if (js_parse_error(s, "unexpected data at the end"))
+ if (js_parse_error(s, "unexpected data at the end")) {
+ json_free_parse_record(ctx, pr);
goto fail;
+ }
}
return val;
fail:
@@ -48802,17 +49013,25 @@ JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
return JS_EXCEPTION;
}
+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags)
+{
+ return JS_ParseJSON3(ctx, buf, buf_len, filename, flags, NULL);
+}
+
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
const char *filename)
{
- return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
+ return JS_ParseJSON3(ctx, buf, buf_len, filename, 0, NULL);
}
+/* if pr != NULL, then pr->value = holder by construction */
static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
- JSAtom name, JSValueConst reviver)
+ JSAtom name, JSValueConst reviver,
+ const char *text_str, JSONParseRecord *pr)
{
- JSValue val, new_el, name_val, res;
- JSValueConst args[2];
+ JSValue val, new_el, name_val, res, context;
+ JSValueConst args[3];
int ret, is_array;
uint32_t i, len = 0;
JSAtom prop;
@@ -48825,6 +49044,29 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
val = JS_GetProperty(ctx, holder, name);
if (JS_IsException(val))
return val;
+
+ if (pr) {
+ if (JS_IsArray(ctx, pr->value)) {
+ if (__JS_AtomIsTaggedInt(name)) {
+ uint32_t idx = __JS_AtomToUInt32(name);
+ if (idx < pr->u.array.count) {
+ pr = &pr->u.array.elements[idx];
+ } else {
+ pr = NULL;
+ }
+ }
+ } else {
+ pr = json_parse_record_find(pr, name);
+ }
+ if (pr && !js_same_value(ctx, pr->value, val)) {
+ pr = NULL;
+ }
+ }
+
+ context = JS_NewObject(ctx);
+ if (JS_IsException(context))
+ goto fail;
+
if (JS_IsObject(val)) {
is_array = JS_IsArray(ctx, val);
if (is_array < 0)
@@ -48845,7 +49087,7 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
} else {
prop = JS_DupAtom(ctx, atoms[i].atom);
}
- new_el = internalize_json_property(ctx, val, prop, reviver);
+ new_el = internalize_json_property(ctx, val, prop, reviver, text_str, pr);
if (JS_IsException(new_el)) {
JS_FreeAtom(ctx, prop);
goto fail;
@@ -48859,6 +49101,15 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
if (ret < 0)
goto fail;
}
+ } else {
+ if (pr) {
+ new_el = JS_NewStringLen(ctx, text_str + pr->u.primitive.source_pos,
+ pr->u.primitive.source_len);
+ if (JS_IsException(new_el))
+ goto fail;
+ if (JS_DefinePropertyValue(ctx, context, JS_ATOM_source, new_el, JS_PROP_C_W_E) < 0)
+ goto fail;
+ }
}
JS_FreePropertyEnum(ctx, atoms, len);
atoms = NULL;
@@ -48867,12 +49118,15 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
goto fail;
args[0] = name_val;
args[1] = val;
- res = JS_Call(ctx, reviver, holder, 2, args);
+ args[2] = context;
+ res = JS_Call(ctx, reviver, holder, 3, args);
JS_FreeValue(ctx, name_val);
JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, context);
return res;
fail:
JS_FreePropertyEnum(ctx, atoms, len);
+ JS_FreeValue(ctx, context);
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
@@ -48880,37 +49134,113 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
- JSValue obj, root;
- JSValueConst reviver;
+ JSValue obj;
const char *str;
size_t len;
-
+
str = JS_ToCStringLen(ctx, &len, argv[0]);
if (!str)
return JS_EXCEPTION;
- obj = JS_ParseJSON(ctx, str, len, "<input>");
- JS_FreeCString(ctx, str);
- if (JS_IsException(obj))
- return obj;
if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
+ JSONParseRecord pr_s, *pr = &pr_s, *pr1;
+ JSValue root;
+ JSValueConst reviver;
+ int size;
+
reviver = argv[1];
root = JS_NewObject(ctx);
- if (JS_IsException(root)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
+ if (JS_IsException(root))
+ goto fail;
+ json_parse_record_init_obj(ctx, pr, root);
+ size = 0;
+ pr1 = json_parse_record_add(ctx, pr, JS_ATOM_empty_string, &size);
+ if (!pr1)
+ goto fail1;
+
+ obj = JS_ParseJSON3(ctx, str, len, "<input>", 0, pr1);
+ if (JS_IsException(obj))
+ goto fail1;
+
if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
JS_PROP_C_W_E) < 0) {
+ JS_FreeValue(ctx, obj);
+ fail1:
+ json_free_parse_record(ctx, pr);
JS_FreeValue(ctx, root);
- return JS_EXCEPTION;
+ goto fail;
}
+
obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
- reviver);
+ reviver, str, pr);
+ json_free_parse_record(ctx, pr);
JS_FreeValue(ctx, root);
+ } else {
+ obj = JS_ParseJSON3(ctx, str, len, "<input>", 0, NULL);
+ }
+ JS_FreeCString(ctx, str);
+ return obj;
+ fail:
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_json_isRawJSON(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ return JS_NewBool(ctx, p->class_id == JS_CLASS_RAWJSON);
+ } else {
+ return JS_FALSE;
+ }
+}
+
+static BOOL is_valid_raw_json_char(int c)
+{
+ return ((c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '"');
+}
+
+static JSValue js_json_rawJSON(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, res, obj;
+ JSString *p;
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ if (p->len == 0 ||
+ !is_valid_raw_json_char(string_get(p, 0)) ||
+ !is_valid_raw_json_char(string_get(p, p->len - 1))) {
+ goto syntax_error;
+ }
+ res = js_json_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&str);
+ if (JS_IsException(res)) {
+ syntax_error:
+ JS_ThrowSyntaxError(ctx, "invalid rawJSON string");
+ goto fail;
+ }
+ JS_FreeValue(ctx, res);
+
+ obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_RAWJSON);
+ if (JS_IsException(obj))
+ goto fail;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_rawJSON, str, JS_PROP_ENUMERABLE) < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
}
+ JS_PreventExtensions(ctx, obj);
return obj;
+ fail:
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
}
+
typedef struct JSONStringifyContext {
JSValueConst replacer_func;
JSValue stack;
@@ -49080,11 +49410,18 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
if (JS_IsException(val))
goto exception;
goto concat_primitive;
- } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT)
- {
+ } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT) {
/* This will thow the same error as for the primitive object */
set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
goto concat_primitive;
+ } else if (cl == JS_CLASS_RAWJSON) {
+ JSValue val1;
+ val1 = JS_GetProperty(ctx, val, JS_ATOM_rawJSON);
+ if (JS_IsException(val1))
+ goto exception;
+ JS_FreeValue(ctx, val);
+ val = val1;
+ goto concat_value;
}
v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
if (JS_IsException(v))
@@ -49373,7 +49710,9 @@ static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
}
static const JSCFunctionListEntry js_json_funcs[] = {
+ JS_CFUNC_DEF("isRawJSON", 1, js_json_isRawJSON ),
JS_CFUNC_DEF("parse", 2, js_json_parse ),
+ JS_CFUNC_DEF("rawJSON", 1, js_json_rawJSON ),
JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
};
@@ -56635,6 +56974,8 @@ static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val,
if (typed_array_is_oob(p) || idx < 0 || idx >= p->u.array.count)
return JS_ThrowRangeError(ctx, "invalid array index");
+ /* warning: 'this_val' may have been resized, so 'len' may be
+ larger than its length */
arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
p->class_id, len);
if (JS_IsException(arr)) {
@@ -57711,7 +58052,7 @@ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
struct TA_sort_context {
JSContext *ctx;
int exception; /* 1 = exception, 2 = detached typed array */
- JSValueConst arr;
+ uint8_t *array;
JSValueConst cmp;
JSValue (*getfun)(JSContext *ctx, const void *a);
int elt_size;
@@ -57724,7 +58065,6 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
JSValueConst argv[2];
JSValue res;
int cmp;
- JSObject *p;
cmp = 0;
if (!psc->exception) {
@@ -57732,15 +58072,9 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
error */
a_idx = *(uint32_t *)a;
b_idx = *(uint32_t *)b;
- p = JS_VALUE_GET_PTR(psc->arr);
- if (a_idx >= p->u.array.count || b_idx >= p->u.array.count) {
- /* OOB case */
- psc->exception = 2;
- return 0;
- }
- argv[0] = psc->getfun(ctx, p->u.array.u.uint8_ptr +
+ argv[0] = psc->getfun(ctx, psc->array +
a_idx * (size_t)psc->elt_size);
- argv[1] = psc->getfun(ctx, p->u.array.u.uint8_ptr +
+ argv[1] = psc->getfun(ctx, psc->array +
b_idx * (size_t)(psc->elt_size));
res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
if (JS_IsException(res)) {
@@ -57781,7 +58115,6 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
tsc.ctx = ctx;
tsc.exception = 0;
- tsc.arr = this_val;
tsc.cmp = argv[0];
if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
@@ -57844,65 +58177,69 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
elt_size = 1 << typed_array_size_log2(p->class_id);
if (!JS_IsUndefined(tsc.cmp)) {
uint32_t *array_idx;
- void *array_tmp;
+ void *array;
size_t i, j;
- /* XXX: a stable sort would use less memory */
+ /* the array must be copied because the comparison
+ function may modify it */
+ array = js_malloc(ctx, len * elt_size);
+ if (!array)
+ return JS_EXCEPTION;
+ memcpy(array, p->u.array.u.ptr, len * elt_size);
+
+ /* array_idx is needed to have a stable sort */
array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
- if (!array_idx)
+ if (!array_idx) {
+ js_free(ctx, array);
return JS_EXCEPTION;
+ }
for(i = 0; i < len; i++)
array_idx[i] = i;
tsc.elt_size = elt_size;
+ tsc.array = array;
rqsort(array_idx, len, sizeof(array_idx[0]),
js_TA_cmp_generic, &tsc);
if (tsc.exception) {
- if (tsc.exception == 1)
- goto fail;
+ if (tsc.exception == 1) {
+ js_free(ctx, array_idx);
+ js_free(ctx, array);
+ return JS_EXCEPTION;
+ }
/* detached typed array during the sort: no error */
} else {
void *array_ptr = p->u.array.u.ptr;
len = min_int(len, p->u.array.count);
- if (len != 0) {
- array_tmp = js_malloc(ctx, len * elt_size);
- if (!array_tmp) {
- fail:
- js_free(ctx, array_idx);
- return JS_EXCEPTION;
+ switch(elt_size) {
+ case 1:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint8_t *)array_ptr)[i] = ((uint8_t *)array)[j];
}
- memcpy(array_tmp, array_ptr, len * elt_size);
- switch(elt_size) {
- case 1:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
- }
- break;
- case 2:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
- }
- break;
- case 4:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
- }
- break;
- case 8:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
- }
- break;
- default:
- abort();
+ break;
+ case 2:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint16_t *)array_ptr)[i] = ((uint16_t *)array)[j];
+ }
+ break;
+ case 4:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint32_t *)array_ptr)[i] = ((uint32_t *)array)[j];
+ }
+ break;
+ case 8:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint64_t *)array_ptr)[i] = ((uint64_t *)array)[j];
}
- js_free(ctx, array_tmp);
+ break;
+ default:
+ abort();
}
}
js_free(ctx, array_idx);
+ js_free(ctx, array);
} else {
rqsort(p->u.array.u.ptr, len, elt_size, cmpfun, &tsc);
if (tsc.exception)
@@ -58135,9 +58472,6 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx,
JS_ThrowTypeErrorArrayBufferOOB(ctx);
goto fail;
}
- ta = p->u.typed_array;
- src_buffer = ta->buffer;
- src_abuf = src_buffer->u.array_buffer;
size_log2 = typed_array_size_log2(classid);
buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
(uint64_t)len << size_log2,
@@ -58153,8 +58487,12 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx,
abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
if (typed_array_init(ctx, obj, buffer, 0, len, /*track_rab*/FALSE))
goto fail;
- if (p->class_id == classid) {
- /* same type: copy the content */
+ ta = p->u.typed_array;
+ src_buffer = ta->buffer;
+ src_abuf = src_buffer->u.array_buffer;
+ if (p->class_id == classid &&
+ (int64_t)ta->offset + (int64_t)abuf->byte_length <= src_abuf->byte_length) {
+ /* same type and no overflow: copy the content */
memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
} else {
for(i = 0; i < len; i++) {
@@ -58187,38 +58525,54 @@ static JSValue js_typed_array_constructor(JSContext *ctx,
if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
if (JS_ToIndex(ctx, &len, argv[0]))
return JS_EXCEPTION;
+ obj = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
len << size_log2,
NULL);
if (JS_IsException(buffer))
- return JS_EXCEPTION;
+ goto fail;
offset = 0;
} else {
JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
- abuf = p->u.array_buffer;
- if (JS_ToIndex(ctx, &offset, argv[1]))
+ obj = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(obj))
return JS_EXCEPTION;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- if ((offset & ((1 << size_log2) - 1)) != 0 ||
- offset > abuf->byte_length)
- return JS_ThrowRangeError(ctx, "invalid offset");
+ if (JS_ToIndex(ctx, &offset, argv[1]))
+ goto fail;
+ if ((offset & ((1 << size_log2) - 1)) != 0)
+ goto invalid_offset;
+ abuf = p->u.array_buffer;
if (JS_IsUndefined(argv[2])) {
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ if (offset > abuf->byte_length) {
+ invalid_offset:
+ JS_ThrowRangeError(ctx, "invalid offset");
+ goto fail;
+ }
track_rab = array_buffer_is_resizable(abuf);
- if (!track_rab)
+ if (!track_rab) {
if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
goto invalid_length;
+ }
len = (abuf->byte_length - offset) >> size_log2;
} else {
if (JS_ToIndex(ctx, &len, argv[2]))
- return JS_EXCEPTION;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
if ((offset + (len << size_log2)) > abuf->byte_length) {
invalid_length:
- return JS_ThrowRangeError(ctx, "invalid length");
+ JS_ThrowRangeError(ctx, "invalid length");
+ goto fail;
}
}
buffer = JS_DupValue(ctx, argv[0]);
@@ -58232,17 +58586,12 @@ static JSValue js_typed_array_constructor(JSContext *ctx,
}
}
}
-
- obj = js_create_from_ctor(ctx, new_target, classid);
- if (JS_IsException(obj)) {
- JS_FreeValue(ctx, buffer);
- return JS_EXCEPTION;
- }
- if (typed_array_init(ctx, obj, buffer, offset, len, track_rab)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
+ if (typed_array_init(ctx, obj, buffer, offset, len, track_rab))
+ goto fail;
return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
}
static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
@@ -58671,9 +59020,8 @@ typedef enum AtomicsOpEnum {
ATOMICS_OP_LOAD,
} AtomicsOpEnum;
-static int js_atomics_get_ptr(JSContext *ctx,
- void **pptr,
- JSArrayBuffer **pabuf,
+static int js_atomics_get_ptr(JSContext *ctx, void **pptr,
+ JSObject **pobj, uint64_t *pidx,
int *psize_log2, JSClassID *pclass_id,
JSValueConst obj, JSValueConst idx_val,
int is_waitable)
@@ -58740,13 +59088,16 @@ static int js_atomics_get_ptr(JSContext *ctx,
size_log2 = typed_array_size_log2(p->class_id);
ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
- if (pabuf)
- *pabuf = abuf;
+
+ *pptr = ptr;
+ if (pobj)
+ *pobj = p;
+ if (pidx)
+ *pidx = idx;
if (psize_log2)
*psize_log2 = size_log2;
if (pclass_id)
*pclass_id = p->class_id;
- *pptr = ptr;
return 0;
}
@@ -58755,13 +59106,13 @@ static JSValue js_atomics_op(JSContext *ctx,
int argc, JSValueConst *argv, int op)
{
int size_log2;
- uint64_t v, a, rep_val;
+ uint64_t v, a, rep_val, idx;
void *ptr;
JSValue ret;
JSClassID class_id;
- JSArrayBuffer *abuf;
-
- if (js_atomics_get_ptr(ctx, &ptr, &abuf, &size_log2, &class_id,
+ JSObject *p;
+
+ if (js_atomics_get_ptr(ctx, &ptr, &p, &idx, &size_log2, &class_id,
argv[0], argv[1], 0))
return JS_EXCEPTION;
rep_val = 0;
@@ -58789,8 +59140,10 @@ static JSValue js_atomics_op(JSContext *ctx,
rep_val = v32;
}
}
- if (abuf->detached)
+ if (typed_array_is_oob(p))
return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if (idx >= p->u.array.count)
+ return JS_ThrowRangeError(ctx, "out-of-bound access");
}
switch(op | (size_log2 << 3)) {
@@ -58901,48 +59254,53 @@ static JSValue js_atomics_store(JSContext *ctx,
int size_log2;
void *ptr;
JSValue ret;
- JSArrayBuffer *abuf;
-
- if (js_atomics_get_ptr(ctx, &ptr, &abuf, &size_log2, NULL,
+ JSObject *p;
+ uint64_t idx;
+ int64_t v;
+
+ if (js_atomics_get_ptr(ctx, &ptr, &p, &idx, &size_log2, NULL,
argv[0], argv[1], 0))
return JS_EXCEPTION;
if (size_log2 == 3) {
- int64_t v64;
ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2]));
if (JS_IsException(ret))
return ret;
- if (JS_ToBigInt64(ctx, &v64, ret)) {
+ if (JS_ToBigInt64(ctx, &v, ret)) {
JS_FreeValue(ctx, ret);
return JS_EXCEPTION;
}
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- atomic_store((_Atomic(uint64_t) *)ptr, v64);
} else {
- uint32_t v;
+ uint32_t v32;
/* XXX: spec, would be simpler to return the written value */
ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
if (JS_IsException(ret))
return ret;
- if (JS_ToUint32(ctx, &v, ret)) {
+ if (JS_ToUint32(ctx, &v32, ret)) {
JS_FreeValue(ctx, ret);
return JS_EXCEPTION;
}
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- switch(size_log2) {
- case 0:
- atomic_store((_Atomic(uint8_t) *)ptr, v);
- break;
- case 1:
- atomic_store((_Atomic(uint16_t) *)ptr, v);
- break;
- case 2:
- atomic_store((_Atomic(uint32_t) *)ptr, v);
- break;
- default:
- abort();
- }
+ v = v32;
+ }
+ if (typed_array_is_oob(p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if (idx >= p->u.array.count)
+ return JS_ThrowRangeError(ctx, "out-of-bound access");
+
+ switch(size_log2) {
+ case 0:
+ atomic_store((_Atomic(uint8_t) *)ptr, v);
+ break;
+ case 1:
+ atomic_store((_Atomic(uint16_t) *)ptr, v);
+ break;
+ case 2:
+ atomic_store((_Atomic(uint32_t) *)ptr, v);
+ break;
+ case 3:
+ atomic_store((_Atomic(uint64_t) *)ptr, v);
+ break;
+ default:
+ abort();
}
return ret;
}
@@ -59025,9 +59383,10 @@ static JSValue js_atomics_wait(JSContext *ctx,
int ret, size_log2, res;
double d;
- if (js_atomics_get_ptr(ctx, &ptr, NULL, &size_log2, NULL,
+ if (js_atomics_get_ptr(ctx, &ptr, NULL, NULL, &size_log2, NULL,
argv[0], argv[1], 2))
return JS_EXCEPTION;
+ /* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */
if (size_log2 == 3) {
if (JS_ToBigInt64(ctx, &v, argv[2]))
return JS_EXCEPTION;
@@ -59103,8 +59462,9 @@ static JSValue js_atomics_notify(JSContext *ctx,
void *ptr;
JSAtomicsWaiter *waiter;
JSArrayBuffer *abuf;
-
- if (js_atomics_get_ptr(ctx, &ptr, &abuf, NULL, NULL, argv[0], argv[1], 1))
+ JSObject *p;
+
+ if (js_atomics_get_ptr(ctx, &ptr, &p, NULL, NULL, NULL, argv[0], argv[1], 1))
return JS_EXCEPTION;
if (JS_IsUndefined(argv[2])) {
@@ -59115,7 +59475,9 @@ static JSValue js_atomics_notify(JSContext *ctx,
}
n = 0;
+ abuf = p->u.typed_array->buffer->u.array_buffer;
if (abuf->shared && count > 0) {
+ /* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */
pthread_mutex_lock(&js_atomics_mutex);
init_list_head(&waiter_list);
list_for_each_safe(el, el1, &js_atomics_waiter_list) {
@@ -59404,7 +59766,8 @@ static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
JSValueConst args[2];
args[0] = frd->cb;
args[1] = fre->held_val;
- JS_EnqueueJob(frd->realm, js_finrec_job, 2, args);
+ /* no exception is raised to avoid recursing into the GC */
+ JS_EnqueueJob2(frd->realm, js_finrec_job, 2, args, TRUE);
js_weakref_free(rt, fre->target);
js_weakref_free(rt, fre->token);
diff --git a/src/couch_quickjs/quickjs/test262.conf b/src/couch_quickjs/quickjs/test262.conf
index aa76e6395..ab8b0a5bd 100644
--- a/src/couch_quickjs/quickjs/test262.conf
+++ b/src/couch_quickjs/quickjs/test262.conf
@@ -71,6 +71,7 @@ async-iteration
Atomics
Atomics.pause
Atomics.waitAsync=skip
+await-dictionary=skip
BigInt
caller
canonical-tz=skip
@@ -119,7 +120,9 @@ hashbang
host-gc-required
immutable-arraybuffer=skip
import-attributes
+import-bytes=skip
import-defer=skip
+import-text=skip
import.meta
joint-iteration=skip
Int16Array
@@ -135,6 +138,7 @@ Intl.DateTimeFormat-fractionalSecondDigits=skip
Intl.DisplayNames-v2=skip
Intl.DisplayNames=skip
Intl.DurationFormat=skip
+Intl.Era-monthcode=skip
Intl.ListFormat=skip
Intl.Locale-info=skip
Intl.Locale=skip
@@ -146,7 +150,7 @@ IsHTMLDDA
iterator-helpers
iterator-sequencing
json-modules
-json-parse-with-source=skip
+json-parse-with-source
json-superset
legacy-regexp=skip
let
diff --git a/src/couch_quickjs/quickjs/test262_errors.txt b/src/couch_quickjs/quickjs/test262_errors.txt
index 796c296e2..4d6d0bd3e 100644
--- a/src/couch_quickjs/quickjs/test262_errors.txt
+++ b/src/couch_quickjs/quickjs/test262_errors.txt
@@ -33,14 +33,8 @@ test262/test/staging/sm/Function/function-name-for.js:13: Test262Error: Expected
test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:12: Test262Error: Expected SameValue(«[object Object]», «undefined») to be true
test262/test/staging/sm/Function/invalid-parameter-list.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/Function/invalid-parameter-list.js:13: strict mode: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
-test262/test/staging/sm/String/string-upper-lower-mapping.js:16: Test262Error: Expected SameValue(«""», «""») to be true
-test262/test/staging/sm/String/string-upper-lower-mapping.js:16: strict mode: Test262Error: Expected SameValue(«""», «""») to be true
-test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:29: Test262Error: Expected a ExpectedError but got a Error
-test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:29: strict mode: Test262Error: Expected a ExpectedError but got a Error
test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: Test262Error: Expected SameValue(«2», «6») to be true
test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: strict mode: Test262Error: Expected SameValue(«2», «6») to be true
-test262/test/staging/sm/TypedArray/sort_modifications.js:9: Test262Error: Int8Array at index 0 for size 4 Expected SameValue(«0», «1») to be true
-test262/test/staging/sm/TypedArray/sort_modifications.js:9: strict mode: Test262Error: Int8Array at index 0 for size 4 Expected SameValue(«0», «1») to be true
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: strict mode: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/async-functions/await-in-arrow-parameters.js:10: Test262Error: AsyncFunction:(a = (b = await/r/g) => {}) => {} Expected a SyntaxError to be thrown but no exception was thrown at all