Skip to content

Commit aabba3b

Browse files
committed
QuickJS updates
* Fix exporting destructured variables [1] * Fix class field name get or set [2] * TypeError compat fix [3] * Fixed JS_IsString() with ropes [4] * Fixed the handling of unicode identifiers [1] bellard/quickjs@7adeb5c [2] bellard/quickjs@949c105 [3] bellard/quickjs@d546fbf [4] bellard/quickjs@c505ac0 [5] bellard/quickjs@25ffdb4
1 parent 77294f2 commit aabba3b

File tree

4 files changed

+50
-62
lines changed

4 files changed

+50
-62
lines changed

src/couch_quickjs/patches/01-spidermonkey-185-mode.patch

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
--- quickjs-master/quickjs.c 2025-04-07 13:01:30
2-
+++ quickjs/quickjs.c 2025-04-07 18:37:55
3-
@@ -29256,10 +29256,24 @@
1+
--- quickjs-master/quickjs.c 2025-04-10 10:23:25
2+
+++ quickjs/quickjs.c 2025-04-10 13:50:31
3+
@@ -29270,10 +29270,24 @@
44
if (s->token.val == TOK_FUNCTION ||
55
(token_is_pseudo_keyword(s, JS_ATOM_async) &&
66
peek_token(s, TRUE) == TOK_FUNCTION)) {

src/couch_quickjs/quickjs/quickjs.c

+45-33
Original file line numberDiff line numberDiff line change
@@ -2861,14 +2861,26 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
28612861
return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
28622862
}
28632863

2864+
/* XXX: optimize */
2865+
static size_t count_ascii(const uint8_t *buf, size_t len)
2866+
{
2867+
const uint8_t *p, *p_end;
2868+
p = buf;
2869+
p_end = buf + len;
2870+
while (p < p_end && *p < 128)
2871+
p++;
2872+
return p - buf;
2873+
}
2874+
28642875
/* str is UTF-8 encoded */
28652876
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
28662877
{
28672878
JSValue val;
28682879

2869-
if (len == 0 || !is_digit(*str)) {
2870-
// XXX: this will not work if UTF-8 encoded str contains non ASCII bytes
2871-
JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
2880+
if (len == 0 ||
2881+
(!is_digit(*str) &&
2882+
count_ascii((const uint8_t *)str, len) == len)) {
2883+
JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
28722884
if (atom)
28732885
return atom;
28742886
}
@@ -3810,10 +3822,8 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
38103822

38113823
p_start = (const uint8_t *)buf;
38123824
p_end = p_start + buf_len;
3813-
p = p_start;
3814-
while (p < p_end && *p < 128)
3815-
p++;
3816-
len1 = p - p_start;
3825+
len1 = count_ascii(p_start, buf_len);
3826+
p = p_start + len1;
38173827
if (len1 > JS_STRING_LEN_MAX)
38183828
return JS_ThrowInternalError(ctx, "string too long");
38193829
if (p == p_end) {
@@ -14711,21 +14721,15 @@ static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
1471114721
return 0;
1471214722
}
1471314723

14714-
static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
14715-
int argc, JSValueConst *argv)
14716-
{
14717-
return JS_ThrowTypeError(ctx, "invalid property access");
14718-
}
14719-
1472014724
/* XXX: not 100% compatible, but mozilla seems to use a similar
1472114725
implementation to ensure that caller in non strict mode does not
1472214726
throw (ES5 compatibility) */
14723-
static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,
14724-
int argc, JSValueConst *argv)
14727+
static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
14728+
int argc, JSValueConst *argv)
1472514729
{
1472614730
JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14727-
if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {
14728-
return js_throw_type_error(ctx, this_val, 0, NULL);
14731+
if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype || argc >= 1) {
14732+
return JS_ThrowTypeError(ctx, "invalid property access");
1472914733
}
1473014734
return JS_UNDEFINED;
1473114735
}
@@ -22244,15 +22248,20 @@ static int __exception js_parse_property_name(JSParseState *s,
2224422248

2224522249
prop_type = PROP_TYPE_IDENT;
2224622250
if (allow_method) {
22247-
if (token_is_pseudo_keyword(s, JS_ATOM_get)
22248-
|| token_is_pseudo_keyword(s, JS_ATOM_set)) {
22251+
/* if allow_private is true (for class field parsing) and
22252+
get/set is following by ';' (or LF with ASI), then it
22253+
is a field name */
22254+
if ((token_is_pseudo_keyword(s, JS_ATOM_get) ||
22255+
token_is_pseudo_keyword(s, JS_ATOM_set)) &&
22256+
(!allow_private || peek_token(s, TRUE) != '\n')) {
2224922257
/* get x(), set x() */
2225022258
name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
2225122259
if (next_token(s))
2225222260
goto fail1;
2225322261
if (s->token.val == ':' || s->token.val == ',' ||
2225422262
s->token.val == '}' || s->token.val == '(' ||
22255-
s->token.val == '=') {
22263+
s->token.val == '=' ||
22264+
(s->token.val == ';' && allow_private)) {
2225622265
is_non_reserved_ident = TRUE;
2225722266
goto ident_found;
2225822267
}
@@ -23865,7 +23874,7 @@ static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
2386523874
present at the top level. */
2386623875
static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
2386723876
int hasval, int has_ellipsis,
23868-
BOOL allow_initializer)
23877+
BOOL allow_initializer, BOOL export_flag)
2386923878
{
2387023879
int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
2387123880
int start_addr, assign_addr;
@@ -23981,7 +23990,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
2398123990
emit_op(s, OP_get_field2);
2398223991
emit_u32(s, prop_name);
2398323992
}
23984-
if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)
23993+
if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE, export_flag) < 0)
2398523994
return -1;
2398623995
if (s->token.val == '}')
2398723996
break;
@@ -24105,6 +24114,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
2410524114
if (tok) {
2410624115
if (js_define_var(s, var_name, tok))
2410724116
goto var_error;
24117+
if (export_flag) {
24118+
if (!add_export_entry(s, s->cur_func->module, var_name, var_name,
24119+
JS_EXPORT_TYPE_LOCAL))
24120+
goto var_error;
24121+
}
2410824122
scope = s->cur_func->scope_level;
2410924123
}
2411024124
if (s->token.val == '=') { /* handle optional default value */
@@ -24180,7 +24194,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
2418024194
emit_u8(s, 0);
2418124195
emit_op(s, OP_drop);
2418224196
}
24183-
if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
24197+
if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0)
2418424198
return -1;
2418524199
} else {
2418624200
var_name = JS_ATOM_NULL;
@@ -24470,7 +24484,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
2447024484
{
2447124485
int skip_bits;
2447224486
if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
24473-
if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
24487+
if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
2447424488
return -1;
2447524489
} else {
2447624490
if (s->token.val == '{') {
@@ -26022,7 +26036,7 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
2602226036
if ((s->token.val == '[' || s->token.val == '{')
2602326037
&& js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
2602426038
emit_op(s, OP_undefined);
26025-
if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
26039+
if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0)
2602626040
return -1;
2602726041
} else {
2602826042
return js_parse_error(s, "variable name expected");
@@ -26144,7 +26158,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
2614426158

2614526159
if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
2614626160
if (s->token.val == '[' || s->token.val == '{') {
26147-
if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)
26161+
if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE, FALSE) < 0)
2614826162
return -1;
2614926163
has_destructuring = TRUE;
2615026164
} else {
@@ -26173,7 +26187,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
2617326187
int skip_bits;
2617426188
if ((s->token.val == '[' || s->token.val == '{')
2617526189
&& ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
26176-
if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
26190+
if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
2617726191
return -1;
2617826192
} else {
2617926193
int lvalue_label;
@@ -26848,7 +26862,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2684826862
if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
2684926863
if (s->token.val == '[' || s->token.val == '{') {
2685026864
/* XXX: TOK_LET is not completely correct */
26851-
if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)
26865+
if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE, FALSE) < 0)
2685226866
goto fail;
2685326867
} else {
2685426868
js_parse_error(s, "identifier expected");
@@ -33807,7 +33821,7 @@ static __exception int js_parse_function_decl2(JSParseState *s,
3380733821
emit_op(s, OP_get_arg);
3380833822
emit_u16(s, idx);
3380933823
}
33810-
has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);
33824+
has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE, FALSE);
3381133825
if (has_initializer < 0)
3381233826
goto fail;
3381333827
if (has_initializer)
@@ -50723,16 +50737,14 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
5072350737
ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
5072450738

5072550739
/* add caller and arguments properties to throw a TypeError */
50726-
obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);
5072750740
JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
50728-
obj1, ctx->throw_type_error,
50741+
ctx->throw_type_error, ctx->throw_type_error,
5072950742
JS_PROP_HAS_GET | JS_PROP_HAS_SET |
5073050743
JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
5073150744
JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
50732-
obj1, ctx->throw_type_error,
50745+
ctx->throw_type_error, ctx->throw_type_error,
5073350746
JS_PROP_HAS_GET | JS_PROP_HAS_SET |
5073450747
JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
50735-
JS_FreeValue(ctx, obj1);
5073650748
JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
5073750749

5073850750
ctx->global_obj = JS_NewObject(ctx);

src/couch_quickjs/quickjs/quickjs.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,8 @@ static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
634634

635635
static inline JS_BOOL JS_IsString(JSValueConst v)
636636
{
637-
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING;
637+
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING ||
638+
JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE;
638639
}
639640

640641
static inline JS_BOOL JS_IsSymbol(JSValueConst v)

src/couch_quickjs/quickjs/test262_errors.txt

-25
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,6 @@
11
test262/test/annexB/language/comments/single-line-html-close-first-line-1.js:1: unexpected error type: SyntaxError: unexpected token in expression: '>'
22
test262/test/annexB/language/comments/single-line-html-close-first-line-2.js:1: unexpected error type: SyntaxError: unexpected token in expression: '>'
33
test262/test/annexB/language/comments/single-line-html-close-first-line-3.js:1: unexpected error type: SyntaxError: unexpected token in expression: '>'
4-
test262/test/built-ins/Function/prototype/arguments/prop-desc.js:31: Test262Error: Function.prototype.arguments property getter/setter are the same function Expected SameValue(«function () {
5-
[native code]
6-
}», «function () {
7-
[native code]
8-
}») to be true
9-
test262/test/built-ins/Function/prototype/arguments/prop-desc.js:31: strict mode: Test262Error: Function.prototype.arguments property getter/setter are the same function Expected SameValue(«function () {
10-
[native code]
11-
}», «function () {
12-
[native code]
13-
}») to be true
14-
test262/test/built-ins/Function/prototype/caller/prop-desc.js:29: Test262Error: Caller property getter/setter are the same function Expected SameValue(«function () {
15-
[native code]
16-
}», «function () {
17-
[native code]
18-
}») to be true
19-
test262/test/built-ins/Function/prototype/caller/prop-desc.js:29: strict mode: Test262Error: Caller property getter/setter are the same function Expected SameValue(«function () {
20-
[native code]
21-
}», «function () {
22-
[native code]
23-
}») to be true
244
test262/test/language/destructuring/binding/keyed-destructuring-property-reference-target-evaluation-order-with-bindings.js:73: Test262Error: Actual [binding::source, binding::sourceKey, sourceKey, get source, binding::defaultValue, binding::varTarget] and expected [binding::source, binding::sourceKey, sourceKey, binding::varTarget, get source, binding::defaultValue] should have the same contents.
255
test262/test/language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js:42: Test262Error: Actual [source, iterator, target, target-key, target-key-tostring, iterator-step, iterator-done, set] and expected [source, iterator, target, target-key, iterator-step, iterator-done, target-key-tostring, set] should have the same contents.
266
test262/test/language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js:42: strict mode: Test262Error: Actual [source, iterator, target, target-key, target-key-tostring, iterator-step, iterator-done, set] and expected [source, iterator, target, target-key, iterator-step, iterator-done, target-key-tostring, set] should have the same contents.
@@ -41,12 +21,7 @@ test262/test/language/expressions/member-expression/computed-reference-null-or-u
4121
test262/test/language/expressions/member-expression/computed-reference-null-or-undefined.js:28: strict mode: Test262Error: Expected a TypeError but got a Test262Error
4222
test262/test/language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js:31: Test262Error: Expected SameValue(«"bad"», «"ok"») to be true
4323
test262/test/language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js:31: strict mode: Test262Error: Expected SameValue(«"bad"», «"ok"») to be true
44-
test262/test/language/module-code/top-level-await/async-module-does-not-block-sibling-modules.js:13: SyntaxError: Could not find export 'check' in module 'test262/test/language/module-code/top-level-await/async-module-sync_FIXTURE.js'
4524
test262/test/language/module-code/top-level-await/module-graphs-does-not-hang.js:10: TypeError: $DONE() not called
46-
test262/test/language/statements/class/elements/syntax/valid/grammar-field-named-get-followed-by-generator-asi.js:40: SyntaxError: invalid property name
47-
test262/test/language/statements/class/elements/syntax/valid/grammar-field-named-get-followed-by-generator-asi.js:40: strict mode: SyntaxError: invalid property name
48-
test262/test/language/statements/class/elements/syntax/valid/grammar-field-named-set-followed-by-generator-asi.js:40: SyntaxError: invalid property name
49-
test262/test/language/statements/class/elements/syntax/valid/grammar-field-named-set-followed-by-generator-asi.js:40: strict mode: SyntaxError: invalid property name
5025
test262/test/language/statements/with/get-binding-value-call-with-proxy-env.js:39: Test262Error: Actual [has:Object, get:Symbol(Symbol.unscopables), get:Object] and expected [has:Object, get:Symbol(Symbol.unscopables), has:Object, get:Object] should have the same contents.
5126
test262/test/language/statements/with/get-binding-value-idref-with-proxy-env.js:39: Test262Error: Actual [has:Object, get:Symbol(Symbol.unscopables), get:Object] and expected [has:Object, get:Symbol(Symbol.unscopables), has:Object, get:Object] should have the same contents.
5227
test262/test/language/statements/with/get-mutable-binding-binding-deleted-in-get-unscopables-strict-mode.js:21: Test262Error: Expected a ReferenceError to be thrown but no exception was thrown at all

0 commit comments

Comments
 (0)