This is an automated email from the ASF dual-hosted git repository. vatamane pushed a commit to branch update-quickjs-10 in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit c1d9843deed6c0b8b8dec523db6f8c72018c49d2 Author: Nick Vatamaniuc <[email protected]> AuthorDate: Mon Oct 20 10:51:43 2025 -0400 QuickJS Update. Optimized string operations. And more spec compliance fixes. Rename `update_and_apply_patches.sh` -> `update.sh` since we always apply patches. Upstream changes (Thanks, Fabrice!): * Add `js_string_eq()` https://github.com/bellard/quickjs/commit/c720e35d26773235aee1ceddff016f3f5b79fcc9 * Inline `get_length()` https://github.com/bellard/quickjs/commit/3e5f2bbe693d20ae5402f3fba6611e6e28716b37 * Optimized Array.prototype.push https://github.com/bellard/quickjs/commit/9a421b3338b950f1b1a414b260a654aaba687d09 * Changed module rejection order according to spec change https://github.com/bellard/quickjs/commit/af16a979211abb17836e3115579120ccb7eb1c48 * Fixed operation order in Regexp constructor https://github.com/bellard/quickjs/commit/c31809e84d6ec81ffe8988951731cd2598fca9f3 * Remove un-initialized variable https://github.com/bellard/quickjs/commit/eb2c89087def1829ed99630cb14b549d7a98408c --- .../patches/01-spidermonkey-185-mode.patch | 6 +- src/couch_quickjs/patches/02-test262-errors.patch | 8 +- src/couch_quickjs/quickjs/quickjs.c | 155 ++++++++++++++------- src/couch_quickjs/quickjs/test262.conf | 2 + src/couch_quickjs/quickjs/test262_errors.txt | 5 - .../{update_and_apply_patches.sh => update.sh} | 0 6 files changed, 111 insertions(+), 65 deletions(-) diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch index 1af4c4f50..05b8020c7 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-10-13 08:51:37 -+++ quickjs/quickjs.c 2025-10-15 21:57:44 -@@ -30997,10 +30997,24 @@ +--- quickjs-master/quickjs.c 2025-10-18 06:04:12 ++++ quickjs/quickjs.c 2025-10-20 10:47:26 +@@ -31017,10 +31017,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 c73d088cf..a75d8b978 100644 --- a/src/couch_quickjs/patches/02-test262-errors.patch +++ b/src/couch_quickjs/patches/02-test262-errors.patch @@ -1,9 +1,9 @@ ---- quickjs-master/test262_errors.txt 2025-10-13 08:51:37 -+++ quickjs/test262_errors.txt 2025-10-15 22:00:27 -@@ -7,6 +7,8 @@ +--- quickjs-master/test262_errors.txt 2025-10-18 06:04:12 ++++ quickjs/test262_errors.txt 2025-10-20 10:49:07 +@@ -6,6 +6,8 @@ + test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33: SyntaxError: invalid assignment left-hand side test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20: SyntaxError: invalid assignment left-hand side test262/test/language/identifier-resolution/assign-to-global-undefined.js:20: strict mode: expected error - test262/test/language/module-code/top-level-await/rejection-order.js:20: TypeError: $DONE() not called +test262/test/language/statements/expression/S12.4_A1.js:15: unexpected error type: Test262: This statement should not be evaluated. +test262/test/language/statements/expression/S12.4_A1.js:15: strict mode: unexpected error type: Test262: This statement should not be evaluated. test262/test/staging/sm/Function/arguments-parameter-shadowing.js:14: Test262Error: Expected SameValue(«true», «false») to be true diff --git a/src/couch_quickjs/quickjs/quickjs.c b/src/couch_quickjs/quickjs/quickjs.c index 22a3ecedf..73429a705 100644 --- a/src/couch_quickjs/quickjs/quickjs.c +++ b/src/couch_quickjs/quickjs/quickjs.c @@ -1176,8 +1176,8 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, JSValueConst flags); -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc); +static JSValue js_regexp_set_internal(JSContext *ctx, JSValue obj, + JSValue pattern, JSValue bc); static void gc_decref(JSRuntime *rt); static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def, JSAtom name); @@ -1261,6 +1261,8 @@ static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +static BOOL js_string_eq(JSContext *ctx, + const JSString *p1, const JSString *p2); static int js_string_compare(JSContext *ctx, const JSString *p1, const JSString *p2); static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); @@ -3226,9 +3228,9 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) JS_FreeValue(ctx, num); return str; } - ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); + ret = js_string_eq(ctx, p, JS_VALUE_GET_STRING(str)); JS_FreeValue(ctx, str); - if (ret == 0) { + if (ret) { return num; } else { JS_FreeValue(ctx, num); @@ -4130,6 +4132,16 @@ static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2, return res; } +static BOOL js_string_eq(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + if (p1->len != p2->len) + return FALSE; + if (p1 == p2) + return TRUE; + return js_string_memcmp(p1, 0, p2, 0, p1->len) == 0; +} + /* return < 0, 0 or > 0 */ static int js_string_compare(JSContext *ctx, const JSString *p1, const JSString *p2) @@ -5226,7 +5238,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas case JS_CLASS_REGEXP: p->u.regexp.pattern = NULL; p->u.regexp.bytecode = NULL; - goto set_exotic; + break; default: set_exotic: if (ctx->rt->class_array[class_id].exotic) { @@ -13414,6 +13426,11 @@ static void js_print_regexp(JSPrintValueState *s, JSObject *p1) int i, n, c, c2, bra, flags; static const char regexp_flags[] = { 'g', 'i', 'm', 's', 'u', 'y', 'd', 'v' }; + if (!re->pattern || !re->bytecode) { + /* the regexp fields are zeroed at init */ + js_puts(s, "[uninitialized_regexp]"); + return; + } p = re->pattern; js_putc(s, '/'); if (p->len == 0) { @@ -15265,8 +15282,8 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, if (!tag_is_string(tag2)) { res = FALSE; } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { - res = (js_string_compare(ctx, JS_VALUE_GET_STRING(op1), - JS_VALUE_GET_STRING(op2)) == 0); + res = js_string_eq(ctx, JS_VALUE_GET_STRING(op1), + JS_VALUE_GET_STRING(op2)); } else { res = (js_string_rope_compare(ctx, op1, op2, TRUE) == 0); } @@ -17120,18 +17137,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_push_empty_string): *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); BREAK; - CASE(OP_get_length): - { - JSValue val; - - sf->cur_pc = pc; - val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; #endif CASE(OP_push_atom_value): *sp++ = JS_AtomToValue(ctx, get_u32(pc)); @@ -17638,8 +17643,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_regexp): { - sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, - sp[-2], sp[-1]); + JSValue obj; + obj = JS_NewObjectClass(ctx, JS_CLASS_REGEXP); + if (JS_IsException(obj)) + goto exception; + sp[-2] = js_regexp_set_internal(ctx, obj, sp[-2], sp[-1]); + if (JS_IsException(sp[-2])) + goto exception; sp--; } BREAK; @@ -18305,7 +18315,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; -#define GET_FIELD_INLINE(name, keep) \ +#define GET_FIELD_INLINE(name, keep, is_length) \ { \ JSValue val, obj; \ JSAtom atom; \ @@ -18313,8 +18323,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSProperty *pr; \ JSShapeProperty *prs; \ \ - atom = get_u32(pc); \ - pc += 4; \ + if (is_length) { \ + atom = JS_ATOM_length; \ + } else { \ + atom = get_u32(pc); \ + pc += 4; \ + } \ \ obj = sp[-1]; \ if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { \ @@ -18358,13 +18372,19 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_get_field): - GET_FIELD_INLINE(get_field, 0); + GET_FIELD_INLINE(get_field, 0, 0); BREAK; CASE(OP_get_field2): - GET_FIELD_INLINE(get_field2, 1); + GET_FIELD_INLINE(get_field2, 1, 0); BREAK; +#if SHORT_OPCODES + CASE(OP_get_length): + GET_FIELD_INLINE(get_length, 0, 1); + BREAK; +#endif + CASE(OP_put_field): { int ret; @@ -30239,14 +30259,6 @@ static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst t module->status = JS_MODULE_STATUS_EVALUATED; module->async_evaluation = FALSE; - for(i = 0; i < module->async_parent_modules_count; i++) { - JSModuleDef *m = module->async_parent_modules[i]; - JSValue m_obj = JS_NewModuleValue(ctx, m); - js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, - &m_obj); - JS_FreeValue(ctx, m_obj); - } - if (!JS_IsUndefined(module->promise)) { JSValue ret_val; assert(module->cycle_root == module); @@ -30254,6 +30266,14 @@ static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst t 1, &error); JS_FreeValue(ctx, ret_val); } + + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + } return JS_UNDEFINED; } @@ -41537,6 +41557,31 @@ static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, int i; int64_t len, from, newLen; + if (likely(JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT && !unshift)) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (likely(p->class_id == JS_CLASS_ARRAY && p->fast_array && + p->extensible && + p->shape->proto == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) && + ctx->std_array_prototype && + JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT && + JS_VALUE_GET_INT(p->prop[0].u.value) == p->u.array.count && + (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE) != 0)) { + /* fast case */ + uint32_t new_len; + new_len = p->u.array.count + argc; + if (likely(new_len <= INT32_MAX)) { + if (unlikely(new_len > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_len)) + return JS_EXCEPTION; + } + for(i = 0; i < argc; i++) + p->u.array.u.values[p->u.array.count + i] = JS_DupValue(ctx, argv[i]); + p->prop[0].u.value = JS_NewInt32(ctx, new_len); + p->u.array.count = new_len; + return JS_NewInt32(ctx, new_len); + } + } + } obj = JS_ToObject(ctx, this_val); if (js_get_length64(ctx, &len, obj)) goto exception; @@ -46084,8 +46129,10 @@ static void js_regexp_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSRegExp *re = &p->u.regexp; - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); + if (re->bytecode != NULL) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + if (re->pattern != NULL) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); } /* create a string containing the RegExp bytecode */ @@ -46167,32 +46214,29 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, return ret; } -/* create a RegExp object from a string containing the RegExp bytecode - and the source pattern */ -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc) +/* set the RegExp fields */ +static JSValue js_regexp_set_internal(JSContext *ctx, + JSValue obj, + JSValue pattern, JSValue bc) { - JSValue obj; JSObject *p; JSRegExp *re; /* sanity check */ - if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || - JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { + if (unlikely(JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || + JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING)) { JS_ThrowTypeError(ctx, "string expected"); - fail: + JS_FreeValue(ctx, obj); JS_FreeValue(ctx, bc); JS_FreeValue(ctx, pattern); return JS_EXCEPTION; } - obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); - if (JS_IsException(obj)) - goto fail; p = JS_VALUE_GET_OBJ(obj); re = &p->u.regexp; re->pattern = JS_VALUE_GET_STRING(pattern); re->bytecode = JS_VALUE_GET_STRING(bc); + /* Note: cannot fail because the field is preallocated */ JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), JS_PROP_WRITABLE); return obj; @@ -46229,7 +46273,7 @@ static int js_is_regexp(JSContext *ctx, JSValueConst obj) static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) { - JSValue pattern, flags, bc, val; + JSValue pattern, flags, bc, val, obj = JS_UNDEFINED; JSValueConst pat, flags1; JSRegExp *re; int pat_is_regexp; @@ -46255,18 +46299,19 @@ static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, } } re = js_get_regexp(ctx, pat, FALSE); + flags = JS_UNDEFINED; if (re) { pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); if (JS_IsUndefined(flags1)) { bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_REGEXP); + if (JS_IsException(obj)) + goto fail; goto no_compilation; } else { - flags = JS_ToString(ctx, flags1); - if (JS_IsException(flags)) - goto fail; + flags = JS_DupValue(ctx, flags1); } } else { - flags = JS_UNDEFINED; if (pat_is_regexp) { pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); if (JS_IsException(pattern)) @@ -46292,15 +46337,19 @@ static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, goto fail; } } + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_REGEXP); + if (JS_IsException(obj)) + goto fail; bc = js_compile_regexp(ctx, pattern, flags); if (JS_IsException(bc)) goto fail; JS_FreeValue(ctx, flags); no_compilation: - return js_regexp_constructor_internal(ctx, new_target, pattern, bc); + return js_regexp_set_internal(ctx, obj, pattern, bc); fail: JS_FreeValue(ctx, pattern); JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, obj); return JS_EXCEPTION; } diff --git a/src/couch_quickjs/quickjs/test262.conf b/src/couch_quickjs/quickjs/test262.conf index 430843a25..cf8b91d53 100644 --- a/src/couch_quickjs/quickjs/test262.conf +++ b/src/couch_quickjs/quickjs/test262.conf @@ -336,6 +336,8 @@ test262/test/staging/sm/regress/regress-699682.js test262/test/staging/sm/RegExp/toString.js test262/test/staging/sm/RegExp/source.js test262/test/staging/sm/RegExp/escape.js +# RegExp.lastMatch not supported +test262/test/staging/sm/statements/regress-642975.js # source directives are not standard yet test262/test/staging/sm/syntax/syntax-parsed-arrow-then-directive.js # returning "bound fn" as initialName for a function is permitted by the spec diff --git a/src/couch_quickjs/quickjs/test262_errors.txt b/src/couch_quickjs/quickjs/test262_errors.txt index 31bb92940..247ae4470 100644 --- a/src/couch_quickjs/quickjs/test262_errors.txt +++ b/src/couch_quickjs/quickjs/test262_errors.txt @@ -6,7 +6,6 @@ test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in- test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33: SyntaxError: invalid assignment left-hand side test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20: SyntaxError: invalid assignment left-hand side test262/test/language/identifier-resolution/assign-to-global-undefined.js:20: strict mode: expected error -test262/test/language/module-code/top-level-await/rejection-order.js:20: TypeError: $DONE() not called test262/test/language/statements/expression/S12.4_A1.js:15: unexpected error type: Test262: This statement should not be evaluated. test262/test/language/statements/expression/S12.4_A1.js:15: strict mode: unexpected error type: Test262: This statement should not be evaluated. test262/test/staging/sm/Function/arguments-parameter-shadowing.js:14: Test262Error: Expected SameValue(«true», «false») to be true @@ -17,8 +16,6 @@ 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/RegExp/constructor-ordering-2.js:12: Test262Error: Expected SameValue(«false», «true») to be true -test262/test/staging/sm/RegExp/constructor-ordering-2.js:12: strict mode: Test262Error: Expected SameValue(«false», «true») to be true test262/test/staging/sm/RegExp/regress-613820-1.js:12: Test262Error: Actual [aaa, aa, a] and expected [aa, a, a] should have the same contents. test262/test/staging/sm/RegExp/regress-613820-1.js:12: strict mode: Test262Error: Actual [aaa, aa, a] and expected [aa, a, a] should have the same contents. test262/test/staging/sm/RegExp/regress-613820-2.js:12: Test262Error: Actual [foobar, f, o, o, b, a, r] and expected [foobar, undefined, undefined, undefined, b, a, r] should have the same contents. @@ -57,7 +54,5 @@ test262/test/staging/sm/regress/regress-1383630.js:28: Test262Error: proxy must test262/test/staging/sm/regress/regress-1383630.js:28: strict mode: Test262Error: proxy must report the same value for the non-writable, non-configurable property Expected a TypeError to be thrown but no exception was thrown at all test262/test/staging/sm/statements/arrow-function-in-for-statement-head.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/staging/sm/statements/arrow-function-in-for-statement-head.js:13: strict mode: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/staging/sm/statements/regress-642975.js:11: Test262Error: Expected SameValue(«undefined», «"y"») to be true -test262/test/staging/sm/statements/regress-642975.js:11: strict mode: Test262Error: Expected SameValue(«undefined», «"y"») to be true test262/test/staging/sm/statements/try-completion.js:11: Test262Error: Expected SameValue(«"try"», «undefined») to be true test262/test/staging/sm/statements/try-completion.js:11: strict mode: Test262Error: Expected SameValue(«"try"», «undefined») to be true diff --git a/src/couch_quickjs/update_and_apply_patches.sh b/src/couch_quickjs/update.sh similarity index 100% rename from src/couch_quickjs/update_and_apply_patches.sh rename to src/couch_quickjs/update.sh
