Note: On some OSes atomic operations are not available or need a
+specific library. If you get related errors, you should either add
+-latomics
in the Makefile LIBS
variable or disable
+CONFIG_ATOMICS
in quickjs.c.
+
The ES2023 specification is almost fully supported including the Annex
B (legacy web compatibility) and the Unicode related features.
The following features are not supported yet:
@@ -411,6 +416,10 @@ B (legacy web compatibility) and the Unicode related features.
Call the function func
after delay
ms. Return a handle
to the timer.
@@ -1310,7 +1340,7 @@ stack holds the Javascript parameters and local variables.
4.4 RegExp
A specific regular expression engine was developed. It is both small
-and efficient and supports all the ES2020 features including the
+and efficient and supports all the ES2023 features including the
Unicode properties. As the Javascript compiler, it directly generates
bytecode without a parse tree.
@@ -1318,9 +1348,6 @@ bytecode without a parse tree.
recursion on the system stack. Simple quantifiers are specifically
optimized to avoid recursions.
-Infinite recursions coming from quantifiers with empty terms are
-avoided.
-
The full regexp library weights about 15 KiB (x86 code), excluding the
Unicode library.
@@ -1359,11 +1386,11 @@ Bellard and Charlie Gordon.
-https://tc39.es/ecma262/
+https://tc39.es/ecma262/2023
https://github.com/tc39/test262
-https://tc39.github.io/ecma262/
+https://tc39.es/ecma262/
The old
ES5.1 tests can be extracted with git clone --single-branch
diff --git a/deps/quickjs/doc/quickjs.pdf b/deps/quickjs/doc/quickjs.pdf
index 30ba6ad4..2338d7a7 100644
Binary files a/deps/quickjs/doc/quickjs.pdf and b/deps/quickjs/doc/quickjs.pdf differ
diff --git a/deps/quickjs/doc/quickjs.texi b/deps/quickjs/doc/quickjs.texi
index 9eb6354b..898d46c8 100644
--- a/deps/quickjs/doc/quickjs.texi
+++ b/deps/quickjs/doc/quickjs.texi
@@ -19,9 +19,9 @@
@chapter Introduction
-QuickJS is a small and embeddable Javascript engine. It supports the
-ES2020 specification
-@footnote{@url{https://tc39.es/ecma262/}}
+QuickJS is a small and embeddable Javascript engine. It supports most of the
+ES2023 specification
+@footnote{@url{https://tc39.es/ecma262/2023 }}
including modules, asynchronous generators, proxies and BigInt.
It supports mathematical extensions such as big decimal float float
@@ -34,14 +34,14 @@ and operator overloading.
@item Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple ``hello world'' program.
-@item Fast interpreter with very low startup time: runs the 69000 tests of the ECMAScript Test Suite@footnote{@url{https://github.com/tc39/test262}} in about 95 seconds on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds.
+@item Fast interpreter with very low startup time: runs the 77000 tests of the ECMAScript Test Suite@footnote{@url{https://github.com/tc39/test262}} in less than 2 minutes on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds.
-@item Almost complete ES2020 support including modules, asynchronous
-generators and full Annex B support (legacy web compatibility). Many
-features from the upcoming ES2021 specification
-@footnote{@url{https://tc39.github.io/ecma262/}} are also supported.
+@item Almost complete ES2023 support including modules, asynchronous
+generators and full Annex B support (legacy web compatibility). Some
+features from the upcoming ES2024 specification
+@footnote{@url{https://tc39.es/ecma262/}} are also supported.
-@item Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2020 features.
+@item Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2023 features.
@item Compile Javascript sources to executables with no external dependency.
@@ -69,6 +69,11 @@ options then run @code{make}.
You can type @code{make install} as root if you wish to install the binaries and support files to
@code{/usr/local} (this is not necessary to use QuickJS).
+Note: On some OSes atomic operations are not available or need a
+specific library. If you get related errors, you should either add
+@code{-latomics} in the Makefile @code{LIBS} variable or disable
+@code{CONFIG_ATOMICS} in @file{quickjs.c}.
+
@section Quick start
@code{qjs} is the command line interpreter (Read-Eval-Print Loop). You can pass
@@ -265,9 +270,9 @@ about 100 seconds).
@section Language support
-@subsection ES2020 support
+@subsection ES2023 support
-The ES2020 specification is almost fully supported including the Annex
+The ES2023 specification is almost fully supported including the Annex
B (legacy web compatibility) and the Unicode related features.
The following features are not supported yet:
@@ -276,6 +281,10 @@ The following features are not supported yet:
@item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.}
+@item WeakRef and FinalizationRegistry objects
+
+@item Symbols as WeakMap keys
+
@end itemize
@subsection ECMA402
@@ -368,6 +377,9 @@ optional properties:
@item backtrace_barrier
Boolean (default = false). If true, error backtraces do not list the
stack frames below the evalScript.
+ @item async
+ Boolean (default = false). If true, @code{await} is accepted in the
+ script and a promise is returned.
@end table
@item loadScript(filename)
@@ -749,6 +761,9 @@ object containing optional parameters:
@end table
+@item getpid()
+Return the current process ID.
+
@item waitpid(pid, options)
@code{waitpid} Unix system call. Return the array @code{[ret,
status]}. @code{ret} contains @code{-errno} in case of error.
@@ -769,6 +784,17 @@ write_fd]} or null in case of error.
@item sleep(delay_ms)
Sleep during @code{delay_ms} milliseconds.
+@item sleepAsync(delay_ms)
+Asynchronouse sleep during @code{delay_ms} milliseconds. Returns a promise. Example:
+@example
+await os.sleepAsync(500);
+@end example
+
+@item now()
+Return a timestamp in milliseconds with more precision than
+@code{Date.now()}. The time origin is unspecified and is normally not
+impacted by system clock adjustments.
+
@item setTimeout(func, delay)
Call the function @code{func} after @code{delay} ms. Return a handle
to the timer.
@@ -1053,7 +1079,7 @@ stack holds the Javascript parameters and local variables.
@section RegExp
A specific regular expression engine was developed. It is both small
-and efficient and supports all the ES2020 features including the
+and efficient and supports all the ES2023 features including the
Unicode properties. As the Javascript compiler, it directly generates
bytecode without a parse tree.
@@ -1061,9 +1087,6 @@ Backtracking with an explicit stack is used so that there is no
recursion on the system stack. Simple quantifiers are specifically
optimized to avoid recursions.
-Infinite recursions coming from quantifiers with empty terms are
-avoided.
-
The full regexp library weights about 15 KiB (x86 code), excluding the
Unicode library.
diff --git a/deps/quickjs/libbf.h b/deps/quickjs/libbf.h
index 48e9d956..0457c180 100644
--- a/deps/quickjs/libbf.h
+++ b/deps/quickjs/libbf.h
@@ -27,7 +27,7 @@
#include
#include
-#if INTPTR_MAX >= INT64_MAX
+#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5
diff --git a/deps/quickjs/libregexp-opcode.h b/deps/quickjs/libregexp-opcode.h
index f90c23b3..189d1215 100644
--- a/deps/quickjs/libregexp-opcode.h
+++ b/deps/quickjs/libregexp-opcode.h
@@ -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)
diff --git a/deps/quickjs/libregexp.c b/deps/quickjs/libregexp.c
index dab8fa1e..b6af4548 100644
--- a/deps/quickjs/libregexp.c
+++ b/deps/quickjs/libregexp.c
@@ -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;
}
@@ -1596,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;
}
@@ -1617,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);
}
@@ -1672,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_goto(s, REOP_goto, pos);
+ 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);
}
@@ -1801,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;
@@ -2297,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:
diff --git a/deps/quickjs/libregexp.h b/deps/quickjs/libregexp.h
index 9aedb7e9..c0bc58d0 100644
--- a/deps/quickjs/libregexp.h
+++ b/deps/quickjs/libregexp.h
@@ -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 */
diff --git a/deps/quickjs/libunicode-table.h b/deps/quickjs/libunicode-table.h
index b64178b4..513ed94b 100644
--- a/deps/quickjs/libunicode-table.h
+++ b/deps/quickjs/libunicode-table.h
@@ -3779,72 +3779,70 @@ static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = {
0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80,
};
-static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = {
- 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44,
- 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f,
- 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80,
- 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b,
- 0x84,
+static const uint8_t unicode_prop_Changes_When_Casefolded1_table[29] = {
+ 0x41, 0xef, 0x80, 0x41, 0x9e, 0x80, 0x9e, 0x80,
+ 0x5a, 0xe4, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00,
+ 0x80, 0xde, 0x06, 0x06, 0x80, 0x8a, 0x09, 0x81,
+ 0x89, 0x10, 0x81, 0x8d, 0x80,
};
-static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[451] = {
+static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[447] = {
0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12,
- 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b,
- 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80,
- 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08,
- 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08,
- 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80,
- 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87,
- 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11,
- 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe,
- 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88,
- 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00,
- 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03,
- 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81,
- 0x46, 0x52, 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10,
- 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1,
- 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87,
- 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80,
- 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08,
- 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b,
- 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a,
- 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a,
- 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06,
- 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80,
- 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41,
- 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6,
- 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0,
- 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40,
- 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09,
- 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf,
- 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f,
- 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40,
- 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80,
- 0x60, 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81,
- 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9,
- 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00,
- 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91,
- 0xbf, 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, 0x95,
- 0x94, 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00,
- 0x80, 0x40, 0x86, 0x08, 0x80, 0x9f, 0x99, 0x40,
- 0x83, 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, 0x80,
- 0x88, 0x47, 0x87, 0x20, 0xa9, 0x80, 0x88, 0x60,
- 0xb4, 0xe4, 0x83, 0x54, 0xb9, 0x86, 0x8d, 0x87,
- 0xbf, 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01,
- 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80,
- 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04,
- 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23,
- 0x81, 0xb1, 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18,
- 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00,
- 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b,
- 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90,
- 0x22, 0x04, 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84,
- 0x9e, 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee,
- 0x82, 0x8c, 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d,
- 0x89, 0x60, 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05,
- 0xe1, 0x4f, 0xff,
+ 0x10, 0x82, 0xf3, 0x80, 0x8b, 0x80, 0x40, 0x84,
+ 0x01, 0x01, 0x80, 0xa2, 0x01, 0x80, 0x40, 0xbb,
+ 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89,
+ 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, 0x07, 0x80,
+ 0x9e, 0x80, 0xa0, 0x82, 0x9c, 0x80, 0x42, 0x28,
+ 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, 0xfb, 0x08,
+ 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, 0x80, 0x40,
+ 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, 0x80, 0xa7,
+ 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, 0x03, 0x03,
+ 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, 0x26, 0x80,
+ 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, 0x80, 0x8b,
+ 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, 0x46, 0x52,
+ 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, 0x8a, 0x80,
+ 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, 0xa4, 0x40,
+ 0xd5, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, 0x80,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0xb7, 0x05, 0x00, 0x13, 0x05, 0x11, 0x02, 0x0c,
+ 0x11, 0x00, 0x00, 0x0c, 0x15, 0x05, 0x08, 0x8f,
+ 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, 0x00,
+ 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, 0x80,
+ 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, 0x01,
+ 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, 0x05,
+ 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, 0x40,
+ 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, 0x34,
+ 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, 0x82,
+ 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, 0x80,
+ 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, 0xd5,
+ 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, 0x80,
+ 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, 0x9e,
+ 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, 0x60,
+ 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x80,
+ 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, 0x60,
+ 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89,
+ 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xc2,
+ 0x00, 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb,
+ 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7,
+ 0x8c, 0x82, 0x99, 0x95, 0x94, 0x81, 0x8b, 0x80,
+ 0x92, 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, 0x08,
+ 0x80, 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, 0x0d,
+ 0x0a, 0x16, 0x06, 0x80, 0x88, 0x47, 0x87, 0x20,
+ 0xa9, 0x80, 0x88, 0x60, 0xb4, 0xe4, 0x83, 0x54,
+ 0xb9, 0x86, 0x8d, 0x87, 0xbf, 0x85, 0x42, 0x3e,
+ 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80,
+ 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06,
+ 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, 0x41,
+ 0x53, 0x81, 0x41, 0x23, 0x81, 0xb1, 0x48, 0x2f,
+ 0xbd, 0x4d, 0x91, 0x18, 0x9a, 0x01, 0x00, 0x08,
+ 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00,
+ 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00,
+ 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90,
+ 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, 0x9f, 0x99,
+ 0x82, 0xa2, 0x80, 0xee, 0x82, 0x8c, 0xab, 0x83,
+ 0x88, 0x31, 0x49, 0x9d, 0x89, 0x60, 0xfc, 0x05,
+ 0x42, 0x1d, 0x6b, 0x05, 0xe1, 0x4f, 0xff,
};
static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = {
diff --git a/deps/quickjs/libunicode.c b/deps/quickjs/libunicode.c
index 63c12a07..0712a6c0 100644
--- a/deps/quickjs/libunicode.c
+++ b/deps/quickjs/libunicode.c
@@ -43,11 +43,111 @@ 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,
};
+static int lre_case_conv1(uint32_t c, int conv_type)
+{
+ uint32_t res[LRE_CC_RES_LEN_MAX];
+ lre_case_conv(res, c, conv_type);
+ return res[0];
+}
+
+/* case conversion using the table entry 'idx' with value 'v' */
+static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v)
+{
+ uint32_t code, data, type, a, is_lower;
+ is_lower = (conv_type != 0);
+ type = (v >> (32 - 17 - 7 - 4)) & 0xf;
+ data = ((v & 0xf) << 8) | case_conv_table2[idx];
+ code = v >> (32 - 17);
+ switch(type) {
+ case RUN_TYPE_U:
+ case RUN_TYPE_L:
+ case RUN_TYPE_UF:
+ case RUN_TYPE_LF:
+ if (conv_type == (type & 1) ||
+ (type >= RUN_TYPE_UF && conv_type == 2)) {
+ c = c - code + (case_conv_table1[data] >> (32 - 17));
+ }
+ break;
+ case RUN_TYPE_UL:
+ a = c - code;
+ if ((a & 1) != (1 - is_lower))
+ break;
+ c = (a ^ 1) + code;
+ break;
+ case RUN_TYPE_LSU:
+ a = c - code;
+ if (a == 1) {
+ c += 2 * is_lower - 1;
+ } else if (a == (1 - is_lower) * 2) {
+ c += (2 * is_lower - 1) * 2;
+ }
+ break;
+ case RUN_TYPE_U2L_399_EXT2:
+ if (!is_lower) {
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = 0x399;
+ return 2;
+ } else {
+ c = c - code + case_conv_ext[data & 0x3f];
+ }
+ break;
+ case RUN_TYPE_UF_D20:
+ if (conv_type == 1)
+ break;
+ c = data + (conv_type == 2) * 0x20;
+ break;
+ case RUN_TYPE_UF_D1_EXT:
+ if (conv_type == 1)
+ break;
+ c = case_conv_ext[data] + (conv_type == 2);
+ break;
+ case RUN_TYPE_U_EXT:
+ case RUN_TYPE_LF_EXT:
+ if (is_lower != (type - RUN_TYPE_U_EXT))
+ break;
+ c = case_conv_ext[data];
+ break;
+ case RUN_TYPE_LF_EXT2:
+ if (!is_lower)
+ break;
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = case_conv_ext[data & 0x3f];
+ return 2;
+ case RUN_TYPE_UF_EXT2:
+ if (conv_type == 1)
+ break;
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = case_conv_ext[data & 0x3f];
+ if (conv_type == 2) {
+ /* convert to lower */
+ res[0] = lre_case_conv1(res[0], 1);
+ res[1] = lre_case_conv1(res[1], 1);
+ }
+ return 2;
+ default:
+ case RUN_TYPE_UF_EXT3:
+ if (conv_type == 1)
+ break;
+ res[0] = case_conv_ext[data >> 8];
+ res[1] = case_conv_ext[(data >> 4) & 0xf];
+ res[2] = case_conv_ext[data & 0xf];
+ if (conv_type == 2) {
+ /* convert to lower */
+ res[0] = lre_case_conv1(res[0], 1);
+ res[1] = lre_case_conv1(res[1], 1);
+ res[2] = lre_case_conv1(res[2], 1);
+ }
+ return 3;
+ }
+ res[0] = c;
+ return 1;
+}
+
/* conv_type:
0 = to upper
1 = to lower
@@ -66,10 +166,9 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
}
}
} else {
- uint32_t v, code, data, type, len, a, is_lower;
+ uint32_t v, code, len;
int idx, idx_min, idx_max;
- is_lower = (conv_type != 0);
idx_min = 0;
idx_max = countof(case_conv_table1) - 1;
while (idx_min <= idx_max) {
@@ -82,74 +181,7 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
} 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];
- switch(type) {
- case RUN_TYPE_U:
- case RUN_TYPE_L:
- case RUN_TYPE_UF:
- case RUN_TYPE_LF:
- if (conv_type == (type & 1) ||
- (type >= RUN_TYPE_UF && conv_type == 2)) {
- c = c - code + (case_conv_table1[data] >> (32 - 17));
- }
- break;
- case RUN_TYPE_UL:
- a = c - code;
- if ((a & 1) != (1 - is_lower))
- break;
- c = (a ^ 1) + code;
- break;
- case RUN_TYPE_LSU:
- a = c - code;
- if (a == 1) {
- c += 2 * is_lower - 1;
- } else if (a == (1 - is_lower) * 2) {
- c += (2 * is_lower - 1) * 2;
- }
- break;
- case RUN_TYPE_U2L_399_EXT2:
- if (!is_lower) {
- res[0] = c - code + case_conv_ext[data >> 6];
- res[1] = 0x399;
- return 2;
- } else {
- c = c - code + case_conv_ext[data & 0x3f];
- }
- break;
- case RUN_TYPE_UF_D20:
- if (conv_type == 1)
- break;
- c = data + (conv_type == 2) * 0x20;
- break;
- case RUN_TYPE_UF_D1_EXT:
- if (conv_type == 1)
- break;
- c = case_conv_ext[data] + (conv_type == 2);
- break;
- case RUN_TYPE_U_EXT:
- case RUN_TYPE_LF_EXT:
- if (is_lower != (type - RUN_TYPE_U_EXT))
- break;
- c = case_conv_ext[data];
- break;
- case RUN_TYPE_U_EXT2:
- case RUN_TYPE_L_EXT2:
- if (conv_type != (type - RUN_TYPE_U_EXT2))
- break;
- res[0] = c - code + case_conv_ext[data >> 6];
- res[1] = case_conv_ext[data & 0x3f];
- return 2;
- default:
- case RUN_TYPE_U_EXT3:
- if (conv_type != 0)
- break;
- res[0] = case_conv_ext[data >> 8];
- res[1] = case_conv_ext[(data >> 4) & 0xf];
- res[2] = case_conv_ext[data & 0xf];
- return 3;
- }
- break;
+ 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;
@@ -1236,7 +1339,136 @@ 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,
diff --git a/deps/quickjs/libunicode.h b/deps/quickjs/libunicode.h
index cfa600a5..8abacb01 100644
--- a/deps/quickjs/libunicode.h
+++ b/deps/quickjs/libunicode.h
@@ -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);
diff --git a/deps/quickjs/list.h b/deps/quickjs/list.h
index 0a1bc5a4..5c182340 100644
--- a/deps/quickjs/list.h
+++ b/deps/quickjs/list.h
@@ -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)
{
diff --git a/deps/quickjs/qjs.c b/deps/quickjs/qjs.c
index c2d63e9d..77b5cfbc 100644
--- a/deps/quickjs/qjs.c
+++ b/deps/quickjs/qjs.c
@@ -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"
diff --git a/deps/quickjs/qjsc.c b/deps/quickjs/qjsc.c
index d3178268..f8e60b3e 100644
--- a/deps/quickjs/qjsc.c
+++ b/deps/quickjs/qjsc.c
@@ -330,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"
@@ -343,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"
diff --git a/deps/quickjs/quickjs-atom.h b/deps/quickjs/quickjs-atom.h
index 81c65b99..f62a7c40 100644
--- a/deps/quickjs/quickjs-atom.h
+++ b/deps/quickjs/quickjs-atom.h
@@ -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,6 +167,7 @@ DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
+DEF(indices, "indices")
DEF(status, "status")
DEF(reason, "reason")
DEF(globalThis, "globalThis")
@@ -177,11 +179,11 @@ 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")
diff --git a/deps/quickjs/quickjs-libc.c b/deps/quickjs/quickjs-libc.c
index e180dd0c..d4f4d671 100644
--- a/deps/quickjs/quickjs-libc.c
+++ b/deps/quickjs/quickjs-libc.c
@@ -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, "", 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 ),
diff --git a/deps/quickjs/quickjs-opcode.h b/deps/quickjs/quickjs-opcode.h
index 15a9fce6..6d2d6e9f 100644
--- a/deps/quickjs/quickjs-opcode.h
+++ b/deps/quickjs/quickjs-opcode.h
@@ -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, 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 */
diff --git a/deps/quickjs/quickjs.c b/deps/quickjs/quickjs.c
index 7fae7e20..4e58a98f 100644
--- a/deps/quickjs/quickjs.c
+++ b/deps/quickjs/quickjs.c
@@ -87,6 +87,7 @@
8: dump stdlib functions
16: dump bytecode in hex
32: dump line number table
+ 64: dump compute_stack_size
*/
//#define DUMP_BYTECODE (1)
/* dump the occurence of the automatic GC */
@@ -196,7 +197,7 @@ typedef enum JSErrorEnum {
JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
} JSErrorEnum;
-#define JS_MAX_LOCAL_VARS 65536
+#define JS_MAX_LOCAL_VARS 65535
#define JS_STACK_SIZE_MAX 65534
#define JS_STRING_LEN_MAX ((1 << 30) - 1)
@@ -282,7 +283,9 @@ struct JSRuntime {
JSModuleNormalizeFunc *module_normalize_func;
JSModuleLoaderFunc *module_loader_func;
void *module_loader_opaque;
-
+ /* timestamp for internal use in module evaluation */
+ int64_t module_async_evaluation_next_timestamp;
+
BOOL can_block : 8; /* TRUE if Atomics.wait can block */
/* used to allocate, free and clone SharedArrayBuffers */
JSSharedArrayBufferFunctions sab_funcs;
@@ -315,17 +318,18 @@ struct JSClass {
#define JS_MODE_STRICT (1 << 0)
#define JS_MODE_STRIP (1 << 1)
#define JS_MODE_MATH (1 << 2)
+#define JS_MODE_ASYNC (1 << 3) /* async function */
typedef struct JSStackFrame {
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
JSValue *arg_buf; /* arguments */
JSValue *var_buf; /* variables */
- struct list_head var_ref_list; /* list of JSVarRef.link */
+ struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
instruction after the call */
int arg_count;
- int js_mode; /* 0 or JS_MODE_MATH for C functions */
+ int js_mode; /* for C functions, only JS_MODE_MATH may be set */
/* only used in generators. Current stack pointer value. NULL if
the function is running. */
JSValue *cur_sp;
@@ -358,11 +362,6 @@ typedef struct JSVarRef {
struct {
int __gc_ref_count; /* corresponds to header.ref_count */
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
-
- /* 0 : the JSVarRef is on the stack. header.link is an element
- of JSStackFrame.var_ref_list.
- 1 : the JSVarRef is detached. header.link has the normal meanning
- */
uint8_t is_detached : 1;
uint8_t is_arg : 1;
uint16_t var_idx; /* index of the corresponding function variable on
@@ -371,7 +370,13 @@ typedef struct JSVarRef {
};
JSValue *pvalue; /* pointer to the value, either on the stack or
to 'value' */
- JSValue value; /* used when the variable is no longer on the stack */
+ union {
+ JSValue value; /* used when is_detached = TRUE */
+ struct {
+ struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
+ struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
+ }; /* used when is_detached = FALSE */
+ };
} JSVarRef;
/* the same structure is used for big integers and big floats. Big
@@ -439,7 +444,6 @@ struct JSContext {
#endif
/* when the counter reaches zero, JSRutime.interrupt_handler is called */
int interrupt_counter;
- BOOL is_error_property_enabled;
struct list_head loaded_modules; /* list of JSModuleDef.link */
@@ -593,6 +597,7 @@ typedef struct JSFunctionBytecode {
uint8_t has_debug : 1;
uint8_t backtrace_barrier : 1; /* stop backtrace on this function */
uint8_t read_only_bytecode : 1;
+ uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */
/* XXX: 4 bits available */
uint8_t *byte_code_buf; /* (self pointer) */
int byte_code_len;
@@ -633,9 +638,11 @@ typedef enum JSIteratorKindEnum {
typedef struct JSForInIterator {
JSValue obj;
- BOOL is_array;
- uint32_t array_length;
uint32_t idx;
+ uint32_t atom_count;
+ uint8_t in_prototype_chain;
+ uint8_t is_array;
+ JSPropertyEnum *tab_atom; /* is_array = FALSE */
} JSForInIterator;
typedef struct JSRegExp {
@@ -669,21 +676,16 @@ typedef struct JSTypedArray {
} JSTypedArray;
typedef struct JSAsyncFunctionState {
- JSValue this_val; /* 'this' generator argument */
+ JSGCObjectHeader header;
+ JSValue this_val; /* 'this' argument */
int argc; /* number of function arguments */
BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
+ BOOL is_completed; /* TRUE if the function has returned. The stack
+ frame is no longer valid */
+ JSValue resolving_funcs[2]; /* only used in JS async functions */
JSStackFrame frame;
} JSAsyncFunctionState;
-/* XXX: could use an object instead to avoid the
- JS_TAG_ASYNC_FUNCTION tag for the GC */
-typedef struct JSAsyncFunctionData {
- JSGCObjectHeader header; /* must come first */
- JSValue resolving_funcs[2];
- BOOL is_active; /* true if the async function state is valid */
- JSAsyncFunctionState func_state;
-} JSAsyncFunctionData;
-
typedef enum {
/* binary operators */
JS_OVOP_ADD,
@@ -765,6 +767,15 @@ typedef struct JSImportEntry {
int req_module_idx; /* in req_module_entries */
} JSImportEntry;
+typedef enum {
+ JS_MODULE_STATUS_UNLINKED,
+ JS_MODULE_STATUS_LINKING,
+ JS_MODULE_STATUS_LINKED,
+ JS_MODULE_STATUS_EVALUATING,
+ JS_MODULE_STATUS_EVALUATING_ASYNC,
+ JS_MODULE_STATUS_EVALUATED,
+} JSModuleStatus;
+
struct JSModuleDef {
JSRefCountHeader header; /* must come first, 32-bit */
JSAtom module_name;
@@ -789,11 +800,24 @@ struct JSModuleDef {
JSValue module_ns;
JSValue func_obj; /* only used for JS modules */
JSModuleInitFunc *init_func; /* only used for C modules */
+ BOOL has_tla : 8; /* true if func_obj contains await */
BOOL resolved : 8;
BOOL func_created : 8;
- BOOL instantiated : 8;
- BOOL evaluated : 8;
- BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
+ JSModuleStatus status : 8;
+ /* temp use during js_module_link() & js_module_evaluate() */
+ int dfs_index, dfs_ancestor_index;
+ JSModuleDef *stack_prev;
+ /* temp use during js_module_evaluate() */
+ JSModuleDef **async_parent_modules;
+ int async_parent_modules_count;
+ int async_parent_modules_size;
+ int pending_async_dependencies;
+ BOOL async_evaluation;
+ int64_t async_evaluation_timestamp;
+ JSModuleDef *cycle_root;
+ JSValue promise; /* corresponds to spec field: capability */
+ JSValue resolving_funcs[2]; /* corresponds to spec field: capability */
+
/* true if evaluation yielded an exception. It is saved in
eval_exception */
BOOL eval_has_exception : 8;
@@ -901,7 +925,7 @@ struct JSObject {
struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
- struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
+ struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
@@ -1168,11 +1192,17 @@ static JSValue js_typed_array_constructor(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv,
int classid);
+static JSValue js_typed_array_constructor_ta(JSContext *ctx,
+ JSValueConst new_target,
+ JSValueConst src_obj,
+ int classid);
static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
BOOL is_arg);
+static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
+static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_obj,
int argc, JSValueConst *argv,
@@ -1198,6 +1228,8 @@ static __exception int perform_promise_then(JSContext *ctx,
JSValueConst *cap_resolving_funcs);
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 int js_string_compare(JSContext *ctx,
const JSString *p1, const JSString *p2);
static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
@@ -1209,8 +1241,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
JSObject *p, JSAtom prop);
static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
-static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
- JS_MarkFunc *mark_func);
static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
static void js_free_shape(JSRuntime *rt, JSShape *sh);
static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
@@ -1238,13 +1268,14 @@ static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
JSGCObjectTypeEnum type);
static void remove_gc_object(JSGCObjectHeader *h);
-static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
void *opaque);
static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
JSAtom atom, void *opaque);
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
+static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int is_map);
static const JSClassExoticMethods js_arguments_exotic_methods;
static const JSClassExoticMethods js_string_exotic_methods;
@@ -1662,19 +1693,19 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
}
/* default memory allocation functions with memory limitation */
-static inline size_t js_def_malloc_usable_size(void *ptr)
+static size_t js_def_malloc_usable_size(const void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
- return _msize(ptr);
-#elif defined(EMSCRIPTEN) || defined(__HAIKU__) || defined(__OpenBSD__)
+ 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
}
@@ -1738,18 +1769,7 @@ static const JSMallocFunctions def_malloc_funcs = {
js_def_malloc,
js_def_free,
js_def_realloc,
-#if defined(__APPLE__)
- malloc_size,
-#elif defined(_WIN32)
- (size_t (*)(const void *))_msize,
-#elif defined(EMSCRIPTEN) || defined(__HAIKU__) || defined(__OpenBSD__)
- NULL,
-#elif defined(__linux__)
- (size_t (*)(const void *))malloc_usable_size,
-#else
- /* change this to `NULL,` if compilation fails */
- malloc_usable_size,
-#endif
+ js_def_malloc_usable_size,
};
JSRuntime *JS_NewRuntime(void)
@@ -2192,7 +2212,6 @@ JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
typedef enum JSFreeModuleEnum {
JS_FREE_MODULE_ALL,
JS_FREE_MODULE_NOT_RESOLVED,
- JS_FREE_MODULE_NOT_EVALUATED,
} JSFreeModuleEnum;
/* XXX: would be more efficient with separate module lists */
@@ -2202,8 +2221,7 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
list_for_each_safe(el, el1, &ctx->loaded_modules) {
JSModuleDef *m = list_entry(el, JSModuleDef, link);
if (flag == JS_FREE_MODULE_ALL ||
- (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||
- (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {
+ (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) {
js_free_module_def(ctx, m);
}
}
@@ -3358,16 +3376,25 @@ static inline BOOL JS_IsEmptyString(JSValueConst v)
/* JSClass support */
+#ifdef CONFIG_ATOMICS
+static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
/* a new class ID is allocated if *pclass_id != 0 */
JSClassID JS_NewClassID(JSClassID *pclass_id)
{
JSClassID class_id;
- /* XXX: make it thread safe */
+#ifdef CONFIG_ATOMICS
+ pthread_mutex_lock(&js_class_id_mutex);
+#endif
class_id = *pclass_id;
if (class_id == 0) {
class_id = js_class_id_alloc++;
*pclass_id = class_id;
}
+#ifdef CONFIG_ATOMICS
+ pthread_mutex_unlock(&js_class_id_mutex);
+#endif
return class_id;
}
@@ -4063,7 +4090,7 @@ void JS_FreeCString(JSContext *ctx, const char *ptr)
if (!ptr)
return;
/* purposely removing constness */
- p = (JSString *)(void *)(ptr - offsetof(JSString, u));
+ p = container_of(ptr, JSString, u);
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
}
@@ -4442,6 +4469,7 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
JSShapeProperty *pr;
void *sh_alloc;
intptr_t h;
+ JSShape *old_sh;
sh = *psh;
new_size = max_int(count, sh->prop_size * 3 / 2);
@@ -4457,19 +4485,21 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
new_hash_size = sh->prop_hash_mask + 1;
while (new_hash_size < new_size)
new_hash_size = 2 * new_hash_size;
+ /* resize the property shapes. Using js_realloc() is not possible in
+ case the GC runs during the allocation */
+ old_sh = sh;
+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh_alloc)
+ return -1;
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_del(&old_sh->header.link);
+ /* copy all the shape properties */
+ memcpy(sh, old_sh,
+ sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+
if (new_hash_size != (sh->prop_hash_mask + 1)) {
- JSShape *old_sh;
/* resize the hash table and the properties */
- old_sh = sh;
- sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
- if (!sh_alloc)
- return -1;
- sh = get_shape_from_alloc(sh_alloc, new_hash_size);
- list_del(&old_sh->header.link);
- /* copy all the fields and the properties */
- memcpy(sh, old_sh,
- sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
new_hash_mask = new_hash_size - 1;
sh->prop_hash_mask = new_hash_mask;
memset(prop_hash_end(sh) - new_hash_size, 0,
@@ -4481,20 +4511,12 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
prop_hash_end(sh)[-h - 1] = i + 1;
}
}
- js_free(ctx, get_alloc_from_shape(old_sh));
} else {
- /* only resize the properties */
- list_del(&sh->header.link);
- sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),
- get_shape_size(new_hash_size, new_size));
- if (unlikely(!sh_alloc)) {
- /* insert again in the GC list */
- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
- return -1;
- }
- sh = get_shape_from_alloc(sh_alloc, new_hash_size);
- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ /* just copy the previous hash table */
+ memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
}
+ js_free(ctx, get_alloc_from_shape(old_sh));
*psh = sh;
sh->prop_size = new_size;
return 0;
@@ -5224,10 +5246,12 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
if (--var_ref->header.ref_count == 0) {
if (var_ref->is_detached) {
JS_FreeValueRT(rt, var_ref->value);
- remove_gc_object(&var_ref->header);
} else {
- list_del(&var_ref->header.link); /* still on the stack */
+ list_del(&var_ref->var_ref_link); /* still on the stack */
+ if (var_ref->async_func)
+ async_func_free(rt, var_ref->async_func);
}
+ remove_gc_object(&var_ref->header);
js_free_rt(rt, var_ref);
}
}
@@ -5325,7 +5349,7 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
if (var_refs) {
for(i = 0; i < b->closure_var_count; i++) {
JSVarRef *var_ref = var_refs[i];
- if (var_ref && var_ref->is_detached) {
+ if (var_ref) {
mark_func(rt, &var_ref->header);
}
}
@@ -5367,7 +5391,15 @@ static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSForInIterator *it = p->u.for_in_iterator;
+ int i;
+
JS_FreeValueRT(rt, it->obj);
+ if (!it->is_array) {
+ for(i = 0; i < it->atom_count; i++) {
+ JS_FreeAtomRT(rt, it->tab_atom[i].atom);
+ }
+ js_free_rt(rt, it->tab_atom);
+ }
js_free_rt(rt, it);
}
@@ -5435,6 +5467,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
free_function_bytecode(rt, (JSFunctionBytecode *)gp);
break;
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+ __async_func_free(rt, (JSAsyncFunctionState *)gp);
+ break;
default:
abort();
}
@@ -5593,11 +5628,9 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
if (pr->u.getset.setter)
mark_func(rt, &pr->u.getset.setter->header);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- if (pr->u.var_ref->is_detached) {
- /* Note: the tag does not matter
- provided it is a GC object */
- mark_func(rt, &pr->u.var_ref->header);
- }
+ /* Note: the tag does not matter
+ provided it is a GC object */
+ mark_func(rt, &pr->u.var_ref->header);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
js_autoinit_mark(rt, pr, mark_func);
}
@@ -5631,16 +5664,32 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
case JS_GC_OBJ_TYPE_VAR_REF:
{
JSVarRef *var_ref = (JSVarRef *)gp;
- /* only detached variable referenced are taken into account */
- assert(var_ref->is_detached);
- JS_MarkValue(rt, *var_ref->pvalue, mark_func);
+ if (var_ref->is_detached) {
+ JS_MarkValue(rt, *var_ref->pvalue, mark_func);
+ } else if (var_ref->async_func) {
+ mark_func(rt, &var_ref->async_func->header);
+ }
}
break;
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
{
- JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
- if (s->is_active)
- async_func_mark(rt, &s->func_state, mark_func);
+ JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp;
+ JSStackFrame *sf = &s->frame;
+ JSValue *sp;
+
+ if (!s->is_completed) {
+ JS_MarkValue(rt, sf->cur_func, mark_func);
+ JS_MarkValue(rt, s->this_val, mark_func);
+ /* sf->cur_sp = NULL if the function is running */
+ if (sf->cur_sp) {
+ /* if the function is running, cur_sp is not known so we
+ cannot mark the stack. Marking the variables is not needed
+ because a running function cannot be part of a removable
+ cycle */
+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
+ JS_MarkValue(rt, *sp, mark_func);
+ }
+ }
JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
}
@@ -5748,12 +5797,13 @@ static void gc_free_cycles(JSRuntime *rt)
if (el == &rt->tmp_obj_list)
break;
p = list_entry(el, JSGCObjectHeader, link);
- /* Only need to free the GC object associated with JS
- values. The rest will be automatically removed because they
- must be referenced by them. */
+ /* Only need to free the GC object associated with JS values
+ or async functions. The rest will be automatically removed
+ because they must be referenced by them. */
switch(p->gc_obj_type) {
case JS_GC_OBJ_TYPE_JS_OBJECT:
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
#ifdef DUMP_GC_FREE
if (!header_done) {
printf("Freeing cycles:\n");
@@ -5775,7 +5825,8 @@ static void gc_free_cycles(JSRuntime *rt)
list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
p = list_entry(el, JSGCObjectHeader, link);
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
- p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+ p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
+ p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
js_free_rt(rt, p);
}
@@ -6228,10 +6279,10 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
if (obj_classes[0])
fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
- if (obj_classes[class_id]) {
+ if (obj_classes[class_id] && class_id < rt->class_count) {
char buf[ATOM_GET_STR_BUF_SIZE];
fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
- JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));
+ JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name));
}
}
if (obj_classes[JS_CLASS_INIT_COUNT])
@@ -7300,6 +7351,8 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
return 0;
}
+/* add a private brand field to 'home_obj' if not already present and
+ if obj is != null add a private brand to it */
static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
{
JSObject *p, *p1;
@@ -7315,10 +7368,10 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
p = JS_VALUE_GET_OBJ(home_obj);
prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
if (!prs) {
+ /* if the brand is not present, add it */
brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
if (JS_IsException(brand))
return -1;
- /* if the brand is not present, add it */
pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
if (!pr) {
JS_FreeValue(ctx, brand);
@@ -7330,20 +7383,27 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
}
brand_atom = js_symbol_to_atom(ctx, brand);
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
+ if (JS_IsObject(obj)) {
+ p1 = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p1, brand_atom);
+ if (unlikely(prs)) {
+ JS_FreeAtom(ctx, brand_atom);
+ JS_ThrowTypeError(ctx, "private method is already present");
+ return -1;
+ }
+ pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, brand_atom);
+ if (!pr)
+ return -1;
+ pr->u.value = JS_UNDEFINED;
+ } else {
JS_FreeAtom(ctx, brand_atom);
- return -1;
}
- p1 = JS_VALUE_GET_OBJ(obj);
- pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
- JS_FreeAtom(ctx, brand_atom);
- if (!pr)
- return -1;
- pr->u.value = JS_UNDEFINED;
return 0;
}
+/* return a boolean telling if the brand of the home object of 'func'
+ is present on 'obj' or -1 in case of exception */
static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
{
JSObject *p, *p1, *home_obj;
@@ -7352,11 +7412,8 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
JSValueConst brand;
/* get the home object of 'func' */
- if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
- not_obj:
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
+ if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT))
+ goto not_obj;
p1 = JS_VALUE_GET_OBJ(func);
if (!js_class_has_bytecode(p1->class_id))
goto not_obj;
@@ -7374,15 +7431,14 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
goto not_obj;
/* get the brand array of 'obj' */
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
- goto not_obj;
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
- if (!prs) {
- JS_ThrowTypeError(ctx, "invalid brand on object");
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ not_obj:
+ JS_ThrowTypeErrorNotAnObject(ctx);
return -1;
}
- return 0;
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
+ return (prs != NULL);
}
static uint32_t js_string_obj_get_length(JSContext *ctx,
@@ -7839,37 +7895,45 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
JSObject *p;
- uint32_t idx, len;
+ uint32_t idx;
/* fast path for array access */
p = JS_VALUE_GET_OBJ(this_obj);
idx = JS_VALUE_GET_INT(prop);
- len = (uint32_t)p->u.array.count;
- if (unlikely(idx >= len))
- goto slow_path;
switch(p->class_id) {
case JS_CLASS_ARRAY:
case JS_CLASS_ARGUMENTS:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_DupValue(ctx, p->u.array.u.values[idx]);
case JS_CLASS_INT8_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
case JS_CLASS_UINT8C_ARRAY:
case JS_CLASS_UINT8_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
case JS_CLASS_INT16_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
case JS_CLASS_UINT16_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
case JS_CLASS_INT32_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
case JS_CLASS_UINT32_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
case JS_CLASS_BIG_INT64_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
case JS_CLASS_BIG_UINT64_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
case JS_CLASS_FLOAT32_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
case JS_CLASS_FLOAT64_ARRAY:
+ if (unlikely(idx >= p->u.array.count)) goto slow_path;
return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
default:
goto slow_path;
@@ -8287,6 +8351,30 @@ static 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. */
+static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
+{
+ JSValue arr;
+ JSObject *p;
+
+ if (len > INT32_MAX)
+ return JS_ThrowRangeError(ctx, "invalid array length");
+ arr = JS_NewArray(ctx);
+ if (JS_IsException(arr))
+ return arr;
+ if (len > 0) {
+ p = JS_VALUE_GET_OBJ(arr);
+ if (expand_fast_array(ctx, p, len) < 0) {
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+ }
+ p->u.array.count = len;
+ }
+ return arr;
+}
+
static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
{
JS_FreeValue(ctx, desc->getter);
@@ -9074,15 +9162,19 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
spaces. */
if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
goto not_configurable;
+ } else {
+ /* update the reference */
+ set_value(ctx, pr->u.var_ref->pvalue,
+ JS_DupValue(ctx, val));
}
- /* update the reference */
- set_value(ctx, pr->u.var_ref->pvalue,
- JS_DupValue(ctx, val));
}
/* if writable is set to false, no longer a
reference (for mapped arguments) */
if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
JSValue val1;
+ if (p->class_id == JS_CLASS_MODULE_NS) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false");
+ }
if (js_shape_prepare_update(ctx, p, &prs))
return -1;
val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
@@ -9943,12 +10035,13 @@ static inline int to_digit(int c)
}
/* XXX: remove */
-static double js_strtod(const char *p, int radix, BOOL is_float)
+static double js_strtod(const char *str, int radix, BOOL is_float)
{
double d;
int c;
if (!is_float || radix != 10) {
+ const char *p = str;
uint64_t n_max, n;
int int_exp, is_neg;
@@ -9975,6 +10068,8 @@ static double js_strtod(const char *p, int radix, BOOL is_float)
if (n <= n_max) {
n = n * radix + c;
} else {
+ if (radix == 10)
+ goto strtod_case;
int_exp++;
}
p++;
@@ -9986,7 +10081,8 @@ static double js_strtod(const char *p, int radix, BOOL is_float)
if (is_neg)
d = -d;
} else {
- d = strtod(p, NULL);
+ strtod_case:
+ d = strtod(str, NULL);
}
return d;
}
@@ -14409,6 +14505,43 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
return 0;
}
+static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ int ret;
+
+ op1 = sp[-2]; /* object */
+ op2 = sp[-1]; /* field name or method function */
+
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) {
+ JS_ThrowTypeError(ctx, "invalid 'in' operand");
+ return -1;
+ }
+ if (JS_IsObject(op2)) {
+ /* method: use the brand */
+ ret = JS_CheckBrand(ctx, op1, op2);
+ if (ret < 0)
+ return -1;
+ } else {
+ JSAtom atom;
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ /* field */
+ atom = JS_ValueToAtom(ctx, op2);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return -1;
+ p = JS_VALUE_GET_OBJ(op1);
+ prs = find_own_property(&pr, p, atom);
+ JS_FreeAtom(ctx, atom);
+ ret = (prs != NULL);
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
JSAtom atom)
{
@@ -14707,10 +14840,10 @@ static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *
static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
{
- JSObject *p;
+ JSObject *p, *p1;
JSPropertyEnum *tab_atom;
int i;
- JSValue enum_obj, obj1;
+ JSValue enum_obj;
JSForInIterator *it;
uint32_t tag, tab_atom_count;
@@ -14733,14 +14866,65 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
it->is_array = FALSE;
it->obj = obj;
it->idx = 0;
- p = JS_VALUE_GET_OBJ(enum_obj);
- p->u.for_in_iterator = it;
+ it->tab_atom = NULL;
+ it->atom_count = 0;
+ it->in_prototype_chain = FALSE;
+ p1 = JS_VALUE_GET_OBJ(enum_obj);
+ p1->u.for_in_iterator = it;
if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
return enum_obj;
- /* fast path: assume no enumerable properties in the prototype chain */
- obj1 = JS_DupValue(ctx, obj);
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->fast_array) {
+ JSShape *sh;
+ JSShapeProperty *prs;
+ /* check that there are no enumerable normal fields */
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ if (prs->flags & JS_PROP_ENUMERABLE)
+ goto normal_case;
+ }
+ /* for fast arrays, we only store the number of elements */
+ it->is_array = TRUE;
+ it->atom_count = p->u.array.count;
+ } else {
+ normal_case:
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
+ JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
+ JS_FreeValue(ctx, enum_obj);
+ return JS_EXCEPTION;
+ }
+ it->tab_atom = tab_atom;
+ it->atom_count = tab_atom_count;
+ }
+ return enum_obj;
+}
+
+/* obj -> enum_obj */
+static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
+{
+ sp[-1] = build_for_in_iterator(ctx, sp[-1]);
+ if (JS_IsException(sp[-1]))
+ return -1;
+ return 0;
+}
+
+/* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */
+static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx,
+ JSValueConst enum_obj)
+{
+ JSObject *p;
+ JSForInIterator *it;
+ JSPropertyEnum *tab_atom;
+ uint32_t tab_atom_count, i;
+ JSValue obj1;
+
+ p = JS_VALUE_GET_OBJ(enum_obj);
+ it = p->u.for_in_iterator;
+
+ /* check if there are enumerable properties in the prototype chain (fast path) */
+ obj1 = JS_DupValue(ctx, it->obj);
for(;;) {
obj1 = JS_GetPrototypeFree(ctx, obj1);
if (JS_IsNull(obj1))
@@ -14764,75 +14948,29 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
goto fail;
}
}
-
- p = JS_VALUE_GET_OBJ(obj);
-
- if (p->fast_array) {
- JSShape *sh;
- JSShapeProperty *prs;
- /* check that there are no enumerable normal fields */
- sh = p->shape;
- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
- if (prs->flags & JS_PROP_ENUMERABLE)
- goto normal_case;
- }
- /* for fast arrays, we only store the number of elements */
- it->is_array = TRUE;
- it->array_length = p->u.array.count;
- } else {
- normal_case:
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
- JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
- goto fail;
- for(i = 0; i < tab_atom_count; i++) {
- JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, enum_obj, 0);
- }
- js_free_prop_enum(ctx, tab_atom, tab_atom_count);
- }
- return enum_obj;
+ JS_FreeValue(ctx, obj1);
+ return 1;
slow_path:
- /* non enumerable properties hide the enumerables ones in the
- prototype chain */
- obj1 = JS_DupValue(ctx, obj);
- for(;;) {
+ /* add the visited properties, even if they are not enumerable */
+ if (it->is_array) {
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
- JS_VALUE_GET_OBJ(obj1),
+ JS_VALUE_GET_OBJ(it->obj),
JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
- JS_FreeValue(ctx, obj1);
- goto fail;
- }
- for(i = 0; i < tab_atom_count; i++) {
- JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,
- (tab_atom[i].is_enumerable ?
- JS_PROP_ENUMERABLE : 0));
- }
- js_free_prop_enum(ctx, tab_atom, tab_atom_count);
- obj1 = JS_GetPrototypeFree(ctx, obj1);
- if (JS_IsNull(obj1))
- break;
- if (JS_IsException(obj1))
- goto fail;
- /* must check for timeout to avoid infinite loop */
- if (js_poll_interrupts(ctx)) {
- JS_FreeValue(ctx, obj1);
goto fail;
}
+ it->is_array = FALSE;
+ it->tab_atom = tab_atom;
+ it->atom_count = tab_atom_count;
+ }
+
+ for(i = 0; i < it->atom_count; i++) {
+ if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0)
+ goto fail;
}
- return enum_obj;
-
- fail:
- JS_FreeValue(ctx, enum_obj);
- return JS_EXCEPTION;
-}
-
-/* obj -> enum_obj */
-static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
-{
- sp[-1] = build_for_in_iterator(ctx, sp[-1]);
- if (JS_IsException(sp[-1]))
- return -1;
return 0;
+ fail:
+ return -1;
}
/* enum_obj -> enum_obj value done */
@@ -14842,6 +14980,8 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
JSObject *p;
JSAtom prop;
JSForInIterator *it;
+ JSPropertyEnum *tab_atom;
+ uint32_t tab_atom_count;
int ret;
enum_obj = sp[-1];
@@ -14854,28 +14994,68 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
it = p->u.for_in_iterator;
for(;;) {
- if (it->is_array) {
- if (it->idx >= it->array_length)
- goto done;
- prop = __JS_AtomFromUInt32(it->idx);
- it->idx++;
+ if (it->idx >= it->atom_count) {
+ if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj))
+ goto done; /* not an object */
+ /* no more property in the current object: look in the prototype */
+ if (!it->in_prototype_chain) {
+ ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj);
+ if (ret < 0)
+ return -1;
+ if (ret)
+ goto done;
+ it->in_prototype_chain = TRUE;
+ }
+ it->obj = JS_GetPrototypeFree(ctx, it->obj);
+ if (JS_IsException(it->obj))
+ return -1;
+ if (JS_IsNull(it->obj))
+ goto done; /* no more prototype */
+
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx))
+ return -1;
+
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
+ JS_VALUE_GET_OBJ(it->obj),
+ JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
+ return -1;
+ }
+ js_free_prop_enum(ctx, it->tab_atom, it->atom_count);
+ it->tab_atom = tab_atom;
+ it->atom_count = tab_atom_count;
+ it->idx = 0;
} else {
- JSShape *sh = p->shape;
- JSShapeProperty *prs;
- if (it->idx >= sh->prop_count)
- goto done;
- prs = get_shape_prop(sh) + it->idx;
- prop = prs->atom;
- it->idx++;
- if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))
- continue;
+ if (it->is_array) {
+ prop = __JS_AtomFromUInt32(it->idx);
+ it->idx++;
+ } else {
+ BOOL is_enumerable;
+ prop = it->tab_atom[it->idx].atom;
+ is_enumerable = it->tab_atom[it->idx].is_enumerable;
+ it->idx++;
+ if (it->in_prototype_chain) {
+ /* slow case: we are in the prototype chain */
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ continue; /* already visited */
+ /* add to the visited property list */
+ if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL,
+ JS_PROP_ENUMERABLE) < 0)
+ return -1;
+ }
+ if (!is_enumerable)
+ continue;
+ }
+ /* check if the property was deleted */
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ break;
}
- /* check if the property was deleted */
- ret = JS_HasProperty(ctx, it->obj, prop);
- if (ret < 0)
- return ret;
- if (ret)
- break;
}
/* return the property */
sp[0] = JS_AtomToValue(ctx, prop);
@@ -15361,7 +15541,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
struct list_head *el;
list_for_each(el, &sf->var_ref_list) {
- var_ref = list_entry(el, JSVarRef, header.link);
+ var_ref = list_entry(el, JSVarRef, var_ref_link);
if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
var_ref->header.ref_count++;
return var_ref;
@@ -15372,15 +15552,29 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
if (!var_ref)
return NULL;
var_ref->header.ref_count = 1;
+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
var_ref->is_detached = FALSE;
var_ref->is_arg = is_arg;
var_ref->var_idx = var_idx;
- list_add_tail(&var_ref->header.link, &sf->var_ref_list);
+ list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list);
+ if (sf->js_mode & JS_MODE_ASYNC) {
+ /* The stack frame is detached and may be destroyed at any
+ time so its reference count must be increased. Calling
+ close_var_refs() when destroying the stack frame is not
+ possible because it would change the graph between the GC
+ objects. Another solution could be to temporarily detach
+ the JSVarRef of async functions during the GC. It would
+ have the advantage of allowing the release of unused stack
+ frames in a cycle. */
+ var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame);
+ var_ref->async_func->header.ref_count++;
+ } else {
+ var_ref->async_func = NULL;
+ }
if (is_arg)
var_ref->pvalue = &sf->arg_buf[var_idx];
else
var_ref->pvalue = &sf->var_buf[var_idx];
- var_ref->value = JS_UNDEFINED;
return var_ref;
}
@@ -15611,7 +15805,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
int var_idx;
list_for_each_safe(el, el1, &sf->var_ref_list) {
- var_ref = list_entry(el, JSVarRef, header.link);
+ var_ref = list_entry(el, JSVarRef, var_ref_link);
+ /* no need to unlink var_ref->var_ref_link as the list is never used afterwards */
+ if (var_ref->async_func)
+ async_func_free(rt, var_ref->async_func);
var_idx = var_ref->var_idx;
if (var_ref->is_arg)
var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
@@ -15620,7 +15817,6 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
var_ref->pvalue = &var_ref->value;
/* the reference is no longer to a local variable */
var_ref->is_detached = TRUE;
- add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
}
}
@@ -15631,14 +15827,15 @@ static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_
int var_idx = idx;
list_for_each_safe(el, el1, &sf->var_ref_list) {
- var_ref = list_entry(el, JSVarRef, header.link);
+ var_ref = list_entry(el, JSVarRef, var_ref_link);
if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
+ list_del(&var_ref->var_ref_link);
+ if (var_ref->async_func)
+ async_func_free(ctx->rt, var_ref->async_func);
var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
var_ref->pvalue = &var_ref->value;
- list_del(&var_ref->header.link);
/* the reference is no longer to a local variable */
var_ref->is_detached = TRUE;
- add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
}
}
}
@@ -15829,9 +16026,10 @@ typedef enum {
OP_SPECIAL_OBJECT_IMPORT_META,
} OPSpecialObjectEnum;
-#define FUNC_RET_AWAIT 0
-#define FUNC_RET_YIELD 1
-#define FUNC_RET_YIELD_STAR 2
+#define FUNC_RET_AWAIT 0
+#define FUNC_RET_YIELD 1
+#define FUNC_RET_YIELD_STAR 2
+#define FUNC_RET_INITIAL_YIELD 3
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
@@ -16393,8 +16591,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
}
BREAK;
CASE(OP_check_brand):
- if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
- goto exception;
+ {
+ int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]);
+ if (ret < 0)
+ goto exception;
+ if (!ret) {
+ JS_ThrowTypeError(ctx, "invalid brand on object");
+ goto exception;
+ }
+ }
BREAK;
CASE(OP_add_brand):
if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
@@ -16816,6 +17021,19 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
sp++;
}
BREAK;
+ CASE(OP_get_loc_checkthis):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE);
+ goto exception;
+ }
+ sp[0] = JS_DupValue(ctx, var_buf[idx]);
+ sp++;
+ }
+ BREAK;
CASE(OP_put_loc_check):
{
int idx;
@@ -17085,26 +17303,21 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
}
sp--;
BREAK;
- CASE(OP_iterator_close_return):
+ CASE(OP_nip_catch):
{
JSValue ret_val;
- /* iter_obj next catch_offset ... ret_val ->
- ret_eval iter_obj next catch_offset */
+ /* catch_offset ... ret_val -> ret_eval */
ret_val = *--sp;
while (sp > stack_buf &&
JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
JS_FreeValue(ctx, *--sp);
}
- if (unlikely(sp < stack_buf + 3)) {
- JS_ThrowInternalError(ctx, "iterator_close_return");
+ if (unlikely(sp == stack_buf)) {
+ JS_ThrowInternalError(ctx, "nip_catch");
JS_FreeValue(ctx, ret_val);
goto exception;
}
- sp[0] = sp[-1];
- sp[-1] = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = ret_val;
- sp++;
+ sp[-1] = ret_val;
}
BREAK;
@@ -18054,6 +18267,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
goto exception;
sp--;
BREAK;
+ CASE(OP_private_in):
+ if (js_operator_private_in(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
CASE(OP_instanceof):
if (js_operator_instanceof(ctx, sp))
goto exception;
@@ -18240,9 +18458,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
goto done_generator;
CASE(OP_return_async):
- CASE(OP_initial_yield):
ret_val = JS_UNDEFINED;
goto done_generator;
+ CASE(OP_initial_yield):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD);
+ goto done_generator;
CASE(OP_nop):
BREAK;
@@ -18524,26 +18744,35 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
}
/* JSAsyncFunctionState (used by generator and async functions) */
-static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
- JSValueConst func_obj, JSValueConst this_obj,
- int argc, JSValueConst *argv)
+static JSAsyncFunctionState *async_func_init(JSContext *ctx,
+ JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
{
+ JSAsyncFunctionState *s;
JSObject *p;
JSFunctionBytecode *b;
JSStackFrame *sf;
int local_count, i, arg_buf_len, n;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return NULL;
+ s->header.ref_count = 1;
+ add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
+
sf = &s->frame;
init_list_head(&sf->var_ref_list);
p = JS_VALUE_GET_OBJ(func_obj);
b = p->u.func.function_bytecode;
- sf->js_mode = b->js_mode;
+ sf->js_mode = b->js_mode | JS_MODE_ASYNC;
sf->cur_pc = b->byte_code_buf;
arg_buf_len = max_int(b->arg_count, argc);
local_count = arg_buf_len + b->var_count + b->stack_size;
sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
- if (!sf->arg_buf)
- return -1;
+ if (!sf->arg_buf) {
+ js_free(ctx, s);
+ return NULL;
+ }
sf->cur_func = JS_DupValue(ctx, func_obj);
s->this_val = JS_DupValue(ctx, this_obj);
s->argc = argc;
@@ -18555,38 +18784,17 @@ static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
n = arg_buf_len + b->var_count;
for(i = argc; i < n; i++)
sf->arg_buf[i] = JS_UNDEFINED;
- return 0;
+ s->resolving_funcs[0] = JS_UNDEFINED;
+ s->resolving_funcs[1] = JS_UNDEFINED;
+ s->is_completed = FALSE;
+ return s;
}
-static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
- JS_MarkFunc *mark_func)
+static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
{
- JSStackFrame *sf;
+ JSStackFrame *sf = &s->frame;
JSValue *sp;
- sf = &s->frame;
- JS_MarkValue(rt, sf->cur_func, mark_func);
- JS_MarkValue(rt, s->this_val, mark_func);
- if (sf->cur_sp) {
- /* if the function is running, cur_sp is not known so we
- cannot mark the stack. Marking the variables is not needed
- because a running function cannot be part of a removable
- cycle */
- for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
- JS_MarkValue(rt, *sp, mark_func);
- }
-}
-
-static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
-{
- JSStackFrame *sf;
- JSValue *sp;
-
- sf = &s->frame;
-
- /* close the closure variables. */
- close_var_refs(rt, sf);
-
if (sf->arg_buf) {
/* cannot free the function if it is running */
assert(sf->cur_sp != NULL);
@@ -18594,6 +18802,7 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
JS_FreeValueRT(rt, *sp);
}
js_free_rt(rt, sf->arg_buf);
+ sf->arg_buf = NULL;
}
JS_FreeValueRT(rt, sf->cur_func);
JS_FreeValueRT(rt, s->this_val);
@@ -18601,17 +18810,66 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
{
- JSValue func_obj;
+ JSRuntime *rt = ctx->rt;
+ JSStackFrame *sf = &s->frame;
+ JSValue func_obj, ret;
- if (js_check_stack_overflow(ctx->rt, 0))
- return JS_ThrowStackOverflow(ctx);
+ assert(!s->is_completed);
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ ret = JS_ThrowStackOverflow(ctx);
+ } else {
+ /* the tag does not matter provided it is not an object */
+ func_obj = JS_MKPTR(JS_TAG_INT, s);
+ ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
+ s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
+ }
+ if (JS_IsException(ret) || JS_IsUndefined(ret)) {
+ if (JS_IsUndefined(ret)) {
+ ret = sf->cur_sp[-1];
+ sf->cur_sp[-1] = JS_UNDEFINED;
+ }
+ /* end of execution */
+ s->is_completed = TRUE;
- /* the tag does not matter provided it is not an object */
- func_obj = JS_MKPTR(JS_TAG_INT, s);
- return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
- s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
+ /* close the closure variables. */
+ close_var_refs(rt, sf);
+
+ async_func_free_frame(rt, s);
+ }
+ return ret;
}
+static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
+{
+ /* cannot close the closure variables here because it would
+ potentially modify the object graph */
+ if (!s->is_completed) {
+ async_func_free_frame(rt, s);
+ }
+
+ JS_FreeValueRT(rt, s->resolving_funcs[0]);
+ JS_FreeValueRT(rt, s->resolving_funcs[1]);
+
+ remove_gc_object(&s->header);
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) {
+ list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list);
+ } else {
+ js_free_rt(rt, s);
+ }
+}
+
+static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
+{
+ if (--s->header.ref_count == 0) {
+ if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
+ list_del(&s->header.link);
+ list_add(&s->header.link, &rt->gc_zero_ref_count_list);
+ if (rt->gc_phase == JS_GC_PHASE_NONE) {
+ free_zero_refcount(rt);
+ }
+ }
+ }
+}
/* Generators */
@@ -18625,14 +18883,17 @@ typedef enum JSGeneratorStateEnum {
typedef struct JSGeneratorData {
JSGeneratorStateEnum state;
- JSAsyncFunctionState func_state;
+ JSAsyncFunctionState *func_state;
} JSGeneratorData;
static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
{
if (s->state == JS_GENERATOR_STATE_COMPLETED)
return;
- async_func_free(rt, &s->func_state);
+ if (s->func_state) {
+ async_func_free(rt, s->func_state);
+ s->func_state = NULL;
+ }
s->state = JS_GENERATOR_STATE_COMPLETED;
}
@@ -18657,9 +18918,9 @@ static void js_generator_mark(JSRuntime *rt, JSValueConst val,
JSObject *p = JS_VALUE_GET_OBJ(val);
JSGeneratorData *s = p->u.generator_data;
- if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
+ if (!s || !s->func_state)
return;
- async_func_mark(rt, &s->func_state, mark_func);
+ mark_func(rt, &s->func_state->header);
}
/* XXX: use enum */
@@ -18678,7 +18939,7 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
*pdone = TRUE;
if (!s)
return JS_ThrowTypeError(ctx, "not a generator");
- sf = &s->func_state.frame;
+ sf = &s->func_state->frame;
switch(s->state) {
default:
case JS_GENERATOR_STATE_SUSPENDED_START:
@@ -18696,23 +18957,23 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
if (magic == GEN_MAGIC_THROW &&
s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
JS_Throw(ctx, ret);
- s->func_state.throw_flag = TRUE;
+ s->func_state->throw_flag = TRUE;
} else {
sf->cur_sp[-1] = ret;
sf->cur_sp[0] = JS_NewInt32(ctx, magic);
sf->cur_sp++;
exec_no_arg:
- s->func_state.throw_flag = FALSE;
+ s->func_state->throw_flag = FALSE;
}
s->state = JS_GENERATOR_STATE_EXECUTING;
- func_ret = async_func_resume(ctx, &s->func_state);
+ func_ret = async_func_resume(ctx, s->func_state);
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
- if (JS_IsException(func_ret)) {
- /* finalize the execution in case of exception */
+ if (s->func_state->is_completed) {
+ /* finalize the execution in case of exception or normal return */
free_generator_stack(ctx, s);
return func_ret;
- }
- if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
+ } else {
+ assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
/* get the returned yield value at the top of the stack */
ret = sf->cur_sp[-1];
sf->cur_sp[-1] = JS_UNDEFINED;
@@ -18723,12 +18984,6 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
} else {
*pdone = FALSE;
}
- } else {
- /* end of iterator */
- ret = sf->cur_sp[-1];
- sf->cur_sp[-1] = JS_UNDEFINED;
- JS_FreeValue(ctx, func_ret);
- free_generator_stack(ctx, s);
}
break;
case JS_GENERATOR_STATE_COMPLETED:
@@ -18766,13 +19021,14 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
if (!s)
return JS_EXCEPTION;
s->state = JS_GENERATOR_STATE_SUSPENDED_START;
- if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
+ if (!s->func_state) {
s->state = JS_GENERATOR_STATE_COMPLETED;
goto fail;
}
/* execute the function up to 'OP_initial_yield' */
- func_ret = async_func_resume(ctx, &s->func_state);
+ func_ret = async_func_resume(ctx, s->func_state);
if (JS_IsException(func_ret))
goto fail;
JS_FreeValue(ctx, func_ret);
@@ -18790,36 +19046,12 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
/* AsyncFunction */
-static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)
-{
- if (s->is_active) {
- async_func_free(rt, &s->func_state);
- s->is_active = FALSE;
- }
-}
-
-static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)
-{
- js_async_function_terminate(rt, s);
- JS_FreeValueRT(rt, s->resolving_funcs[0]);
- JS_FreeValueRT(rt, s->resolving_funcs[1]);
- remove_gc_object(&s->header);
- js_free_rt(rt, s);
-}
-
-static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)
-{
- if (--s->header.ref_count == 0) {
- js_async_function_free0(rt, s);
- }
-}
-
static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
- JSAsyncFunctionData *s = p->u.async_function_data;
+ JSAsyncFunctionState *s = p->u.async_function_data;
if (s) {
- js_async_function_free(rt, s);
+ async_func_free(rt, s);
}
}
@@ -18827,14 +19059,14 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
- JSAsyncFunctionData *s = p->u.async_function_data;
+ JSAsyncFunctionState *s = p->u.async_function_data;
if (s) {
mark_func(rt, &s->header);
}
}
static int js_async_function_resolve_create(JSContext *ctx,
- JSAsyncFunctionData *s,
+ JSAsyncFunctionState *s,
JSValue *resolving_funcs)
{
int i;
@@ -18856,60 +19088,58 @@ static int js_async_function_resolve_create(JSContext *ctx,
return 0;
}
-static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s)
{
JSValue func_ret, ret2;
- func_ret = async_func_resume(ctx, &s->func_state);
- if (JS_IsException(func_ret)) {
- JSValue error;
- fail:
- error = JS_GetException(ctx);
- ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- js_async_function_terminate(ctx->rt, s);
- JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
- } else {
- JSValue value;
- value = s->func_state.frame.cur_sp[-1];
- s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
- if (JS_IsUndefined(func_ret)) {
- /* function returned */
- ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
- 1, (JSValueConst *)&value);
+ func_ret = async_func_resume(ctx, s);
+ if (s->is_completed) {
+ if (JS_IsException(func_ret)) {
+ JSValue error;
+ fail:
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
+ 1, (JSValueConst *)&error);
+ JS_FreeValue(ctx, error);
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
- JS_FreeValue(ctx, value);
- js_async_function_terminate(ctx->rt, s);
} else {
- JSValue promise, resolving_funcs[2], resolving_funcs1[2];
- int i, res;
-
- /* await */
- JS_FreeValue(ctx, func_ret); /* not used */
- promise = js_promise_resolve(ctx, ctx->promise_ctor,
- 1, (JSValueConst *)&value, 0);
- JS_FreeValue(ctx, value);
- if (JS_IsException(promise))
- goto fail;
- if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
- JS_FreeValue(ctx, promise);
- goto fail;
- }
-
- /* Note: no need to create 'thrownawayCapability' as in
- the spec */
- for(i = 0; i < 2; i++)
- resolving_funcs1[i] = JS_UNDEFINED;
- res = perform_promise_then(ctx, promise,
- (JSValueConst *)resolving_funcs,
- (JSValueConst *)resolving_funcs1);
- JS_FreeValue(ctx, promise);
- for(i = 0; i < 2; i++)
- JS_FreeValue(ctx, resolving_funcs[i]);
- if (res)
- goto fail;
+ /* normal return */
+ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
+ 1, (JSValueConst *)&func_ret);
+ JS_FreeValue(ctx, func_ret);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
}
+ } else {
+ JSValue value, promise, resolving_funcs[2], resolving_funcs1[2];
+ int i, res;
+
+ value = s->frame.cur_sp[-1];
+ s->frame.cur_sp[-1] = JS_UNDEFINED;
+
+ /* await */
+ JS_FreeValue(ctx, func_ret); /* not used */
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, (JSValueConst *)&value, 0);
+ JS_FreeValue(ctx, value);
+ if (JS_IsException(promise))
+ goto fail;
+ if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
+ JS_FreeValue(ctx, promise);
+ goto fail;
+ }
+
+ /* Note: no need to create 'thrownawayCapability' as in
+ the spec */
+ for(i = 0; i < 2; i++)
+ resolving_funcs1[i] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs,
+ (JSValueConst *)resolving_funcs1);
+ JS_FreeValue(ctx, promise);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (res)
+ goto fail;
}
}
@@ -18920,7 +19150,7 @@ static JSValue js_async_function_resolve_call(JSContext *ctx,
int flags)
{
JSObject *p = JS_VALUE_GET_OBJ(func_obj);
- JSAsyncFunctionData *s = p->u.async_function_data;
+ JSAsyncFunctionState *s = p->u.async_function_data;
BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
JSValueConst arg;
@@ -18928,12 +19158,12 @@ static JSValue js_async_function_resolve_call(JSContext *ctx,
arg = argv[0];
else
arg = JS_UNDEFINED;
- s->func_state.throw_flag = is_reject;
+ s->throw_flag = is_reject;
if (is_reject) {
JS_Throw(ctx, JS_DupValue(ctx, arg));
} else {
/* return value of await */
- s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
+ s->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
}
js_async_function_resume(ctx, s);
return JS_UNDEFINED;
@@ -18944,32 +19174,21 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
int argc, JSValueConst *argv, int flags)
{
JSValue promise;
- JSAsyncFunctionData *s;
+ JSAsyncFunctionState *s;
- s = js_mallocz(ctx, sizeof(*s));
+ s = async_func_init(ctx, func_obj, this_obj, argc, argv);
if (!s)
return JS_EXCEPTION;
- s->header.ref_count = 1;
- add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
- s->is_active = FALSE;
- s->resolving_funcs[0] = JS_UNDEFINED;
- s->resolving_funcs[1] = JS_UNDEFINED;
promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
- if (JS_IsException(promise))
- goto fail;
-
- if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
- fail:
- JS_FreeValue(ctx, promise);
- js_async_function_free(ctx->rt, s);
+ if (JS_IsException(promise)) {
+ async_func_free(ctx->rt, s);
return JS_EXCEPTION;
}
- s->is_active = TRUE;
js_async_function_resume(ctx, s);
-
- js_async_function_free(ctx->rt, s);
+
+ async_func_free(ctx->rt, s);
return promise;
}
@@ -18998,7 +19217,8 @@ typedef struct JSAsyncGeneratorRequest {
typedef struct JSAsyncGeneratorData {
JSObject *generator; /* back pointer to the object (const) */
JSAsyncGeneratorStateEnum state;
- JSAsyncFunctionState func_state;
+ /* func_state is NULL is state AWAITING_RETURN and COMPLETED */
+ JSAsyncFunctionState *func_state;
struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
} JSAsyncGeneratorData;
@@ -19016,10 +19236,8 @@ static void js_async_generator_free(JSRuntime *rt,
JS_FreeValueRT(rt, req->resolving_funcs[1]);
js_free_rt(rt, req);
}
- if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
- s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
- async_func_free(rt, &s->func_state);
- }
+ if (s->func_state)
+ async_func_free(rt, s->func_state);
js_free_rt(rt, s);
}
@@ -19046,9 +19264,8 @@ static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
}
- if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
- s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
- async_func_mark(rt, &s->func_state, mark_func);
+ if (s->func_state) {
+ mark_func(rt, &s->func_state->header);
}
}
}
@@ -19158,7 +19375,8 @@ static void js_async_generator_complete(JSContext *ctx,
{
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
- async_func_free(ctx->rt, &s->func_state);
+ async_func_free(ctx->rt, s->func_state);
+ s->func_state = NULL;
}
}
@@ -19169,10 +19387,19 @@ static int js_async_generator_completed_return(JSContext *ctx,
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
int res;
- promise = js_promise_resolve(ctx, ctx->promise_ctor,
- 1, (JSValueConst *)&value, 0);
- if (JS_IsException(promise))
- return -1;
+ // Can fail looking up JS_ATOM_constructor when is_reject==0.
+ promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value,
+ /*is_reject*/0);
+ // A poisoned .constructor property is observable and the resulting
+ // exception should be delivered to the catch handler.
+ if (JS_IsException(promise)) {
+ JSValue err = JS_GetException(ctx);
+ promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err,
+ /*is_reject*/1);
+ JS_FreeValue(ctx, err);
+ if (JS_IsException(promise))
+ return -1;
+ }
if (js_async_generator_resolve_function_create(ctx,
JS_MKPTR(JS_TAG_OBJECT, s->generator),
resolving_funcs1,
@@ -19220,7 +19447,6 @@ static void js_async_generator_resume_next(JSContext *ctx,
} else if (next->completion_type == GEN_MAGIC_RETURN) {
s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
js_async_generator_completed_return(ctx, s, next->result);
- goto done;
} else {
js_async_generator_reject(ctx, s, next->result);
}
@@ -19231,30 +19457,38 @@ static void js_async_generator_resume_next(JSContext *ctx,
if (next->completion_type == GEN_MAGIC_THROW &&
s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
JS_Throw(ctx, value);
- s->func_state.throw_flag = TRUE;
+ s->func_state->throw_flag = TRUE;
} else {
/* 'yield' returns a value. 'yield *' also returns a value
in case the 'throw' method is called */
- s->func_state.frame.cur_sp[-1] = value;
- s->func_state.frame.cur_sp[0] =
+ s->func_state->frame.cur_sp[-1] = value;
+ s->func_state->frame.cur_sp[0] =
JS_NewInt32(ctx, next->completion_type);
- s->func_state.frame.cur_sp++;
+ s->func_state->frame.cur_sp++;
exec_no_arg:
- s->func_state.throw_flag = FALSE;
+ s->func_state->throw_flag = FALSE;
}
s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
resume_exec:
- func_ret = async_func_resume(ctx, &s->func_state);
- if (JS_IsException(func_ret)) {
- value = JS_GetException(ctx);
- js_async_generator_complete(ctx, s);
- js_async_generator_reject(ctx, s, value);
- JS_FreeValue(ctx, value);
- } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
- int func_ret_code;
- value = s->func_state.frame.cur_sp[-1];
- s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ func_ret = async_func_resume(ctx, s->func_state);
+ if (s->func_state->is_completed) {
+ if (JS_IsException(func_ret)) {
+ value = JS_GetException(ctx);
+ js_async_generator_complete(ctx, s);
+ js_async_generator_reject(ctx, s, value);
+ JS_FreeValue(ctx, value);
+ } else {
+ /* end of function */
+ js_async_generator_complete(ctx, s);
+ js_async_generator_resolve(ctx, s, func_ret, TRUE);
+ JS_FreeValue(ctx, func_ret);
+ }
+ } else {
+ int func_ret_code, ret;
+ assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
func_ret_code = JS_VALUE_GET_INT(func_ret);
+ value = s->func_state->frame.cur_sp[-1];
+ s->func_state->frame.cur_sp[-1] = JS_UNDEFINED;
switch(func_ret_code) {
case FUNC_RET_YIELD:
case FUNC_RET_YIELD_STAR:
@@ -19266,20 +19500,17 @@ static void js_async_generator_resume_next(JSContext *ctx,
JS_FreeValue(ctx, value);
break;
case FUNC_RET_AWAIT:
- js_async_generator_await(ctx, s, value);
+ ret = js_async_generator_await(ctx, s, value);
JS_FreeValue(ctx, value);
+ if (ret < 0) {
+ /* exception: throw it */
+ s->func_state->throw_flag = TRUE;
+ goto resume_exec;
+ }
goto done;
default:
abort();
}
- } else {
- assert(JS_IsUndefined(func_ret));
- /* end of function */
- value = s->func_state.frame.cur_sp[-1];
- s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
- js_async_generator_complete(ctx, s);
- js_async_generator_resolve(ctx, s, value, TRUE);
- JS_FreeValue(ctx, value);
}
break;
default:
@@ -19313,12 +19544,12 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx,
} else {
/* restart function execution after await() */
assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
- s->func_state.throw_flag = is_reject;
+ s->func_state->throw_flag = is_reject;
if (is_reject) {
JS_Throw(ctx, JS_DupValue(ctx, arg));
} else {
/* return value of await */
- s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
+ s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
}
js_async_generator_resume_next(ctx, s);
}
@@ -19382,14 +19613,12 @@ static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst fun
return JS_EXCEPTION;
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
init_list_head(&s->queue);
- if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
- s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
+ if (!s->func_state)
goto fail;
- }
-
/* execute the function up to 'OP_initial_yield' (no yield nor
await are possible) */
- func_ret = async_func_resume(ctx, &s->func_state);
+ func_ret = async_func_resume(ctx, s->func_state);
if (JS_IsException(func_ret))
goto fail;
JS_FreeValue(ctx, func_ret);
@@ -19575,6 +19804,7 @@ typedef enum JSParseFunctionEnum {
JS_PARSE_FUNC_GETTER,
JS_PARSE_FUNC_SETTER,
JS_PARSE_FUNC_METHOD,
+ JS_PARSE_FUNC_CLASS_STATIC_INIT,
JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
} JSParseFunctionEnum;
@@ -19694,6 +19924,7 @@ typedef struct JSFunctionDef {
int source_len;
JSModuleDef *module; /* != NULL when parsing a module */
+ BOOL has_await; /* TRUE if await is used (used in module eval) */
} JSFunctionDef;
typedef struct JSToken {
@@ -20244,6 +20475,48 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
return 0;
}
+/* convert a TOK_IDENT to a keyword when needed */
+static void update_token_ident(JSParseState *s)
+{
+ if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
+ (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
+ (s->cur_func->js_mode & JS_MODE_STRICT)) ||
+ (s->token.u.ident.atom == JS_ATOM_yield &&
+ ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
+ !s->cur_func->in_function_body && s->cur_func->parent &&
+ (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
+ (s->token.u.ident.atom == JS_ATOM_await &&
+ (s->is_module ||
+ (s->cur_func->func_kind & JS_FUNC_ASYNC) ||
+ s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT ||
+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
+ !s->cur_func->in_function_body && s->cur_func->parent &&
+ ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) ||
+ s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) {
+ if (s->token.u.ident.has_escape) {
+ s->token.u.ident.is_reserved = TRUE;
+ s->token.val = TOK_IDENT;
+ } else {
+ /* The keywords atoms are pre allocated */
+ s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
+ }
+ }
+}
+
+/* if the current token is an identifier or keyword, reparse it
+ according to the current function type */
+static void reparse_ident_token(JSParseState *s)
+{
+ if (s->token.val == TOK_IDENT ||
+ (s->token.val >= TOK_FIRST_KEYWORD &&
+ s->token.val <= TOK_LAST_KEYWORD)) {
+ s->token.val = TOK_IDENT;
+ s->token.u.ident.is_reserved = FALSE;
+ update_token_ident(s);
+ }
+}
+
/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
BOOL *pident_has_escape, int c, BOOL is_private)
@@ -20450,30 +20723,8 @@ static __exception int next_token(JSParseState *s)
s->token.u.ident.atom = atom;
s->token.u.ident.has_escape = ident_has_escape;
s->token.u.ident.is_reserved = FALSE;
- if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
- (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
- (s->cur_func->js_mode & JS_MODE_STRICT)) ||
- (s->token.u.ident.atom == JS_ATOM_yield &&
- ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
- (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
- !s->cur_func->in_function_body && s->cur_func->parent &&
- (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
- (s->token.u.ident.atom == JS_ATOM_await &&
- (s->is_module ||
- (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
- (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
- !s->cur_func->in_function_body && s->cur_func->parent &&
- (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
- if (ident_has_escape) {
- s->token.u.ident.is_reserved = TRUE;
- s->token.val = TOK_IDENT;
- } else {
- /* The keywords atoms are pre allocated */
- s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
- }
- } else {
- s->token.val = TOK_IDENT;
- }
+ s->token.val = TOK_IDENT;
+ update_token_ident(s);
break;
case '#':
/* private name */
@@ -21120,6 +21371,31 @@ static int peek_token(JSParseState *s, BOOL no_line_terminator)
return simple_next_token(&p, no_line_terminator);
}
+static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end)
+{
+ const uint8_t *p = *pp;
+ int c;
+
+ if (p[0] == '#' && p[1] == '!') {
+ p += 2;
+ while (p < buf_end) {
+ if (*p == '\n' || *p == '\r') {
+ break;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ *pp = p;
+ }
+}
+
/* return true if 'input' contains the source of a module
(heuristic). 'input' must be a zero terminated.
@@ -21130,6 +21406,8 @@ BOOL JS_DetectModule(const char *input, size_t input_len)
{
const uint8_t *p = (const uint8_t *)input;
int tok;
+
+ skip_shebang(&p, p + input_len);
switch(simple_next_token(&p, FALSE)) {
case TOK_IMPORT:
tok = simple_next_token(&p, FALSE);
@@ -21242,6 +21520,14 @@ static int new_label(JSParseState *s)
return new_label_fd(s->cur_func, -1);
}
+/* don't update the last opcode and don't emit line number info */
+static void emit_label_raw(JSParseState *s, int label)
+{
+ emit_u8(s, OP_label);
+ emit_u32(s, label);
+ s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
+}
+
/* return the label ID offset */
static int emit_label(JSParseState *s, int label)
{
@@ -22498,8 +22784,9 @@ static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
typedef struct {
JSFunctionDef *fields_init_fd;
int computed_fields_count;
- BOOL has_brand;
+ BOOL need_brand;
int brand_push_pos;
+ BOOL is_static;
} ClassFieldsDef;
static __exception int emit_class_init_start(JSParseState *s,
@@ -22513,48 +22800,34 @@ static __exception int emit_class_init_start(JSParseState *s,
s->cur_func = cf->fields_init_fd;
- /* XXX: would be better to add the code only if needed, maybe in a
- later pass */
- emit_op(s, OP_push_false); /* will be patched later */
- cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
- label_add_brand = emit_goto(s, OP_if_false, -1);
-
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
-
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_home_object);
- emit_u16(s, 0);
-
- emit_op(s, OP_add_brand);
-
- emit_label(s, label_add_brand);
-
- s->cur_func = s->cur_func->parent;
- return 0;
-}
-
-static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)
-{
- if (!cf->has_brand) {
- /* define the brand field in 'this' of the initializer */
- if (!cf->fields_init_fd) {
- if (emit_class_init_start(s, cf))
- return -1;
- }
- /* patch the start of the function to enable the OP_add_brand code */
- cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
+ if (!cf->is_static) {
+ /* add the brand to the newly created instance */
+ /* XXX: would be better to add the code only if needed, maybe in a
+ later pass */
+ emit_op(s, OP_push_false); /* will be patched later */
+ cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
+ label_add_brand = emit_goto(s, OP_if_false, -1);
- cf->has_brand = TRUE;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_home_object);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_add_brand);
+
+ emit_label(s, label_add_brand);
}
+ s->cur_func = s->cur_func->parent;
return 0;
}
static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
{
int cpool_idx;
-
+
s->cur_func = cf->fields_init_fd;
emit_op(s, OP_return_undef);
s->cur_func = s->cur_func->parent;
@@ -22581,7 +22854,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
const uint8_t *class_start_ptr = s->token.ptr;
const uint8_t *start_ptr;
ClassFieldsDef class_fields[2];
-
+
/* classes are parsed and executed in strict mode */
saved_js_mode = fd->js_mode;
fd->js_mode |= JS_MODE_STRICT;
@@ -22654,7 +22927,8 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
ClassFieldsDef *cf = &class_fields[i];
cf->fields_init_fd = NULL;
cf->computed_fields_count = 0;
- cf->has_brand = FALSE;
+ cf->need_brand = FALSE;
+ cf->is_static = i;
}
ctor_fd = NULL;
@@ -22669,6 +22943,41 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
if (is_static) {
if (next_token(s))
goto fail;
+ if (s->token.val == '{') {
+ ClassFieldsDef *cf = &class_fields[is_static];
+ JSFunctionDef *init;
+ if (!cf->fields_init_fd) {
+ if (emit_class_init_start(s, cf))
+ goto fail;
+ }
+ s->cur_func = cf->fields_init_fd;
+ /* XXX: could try to avoid creating a new function and
+ reuse 'fields_init_fd' with a specific 'var'
+ scope */
+ // stack is now:
+ if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num,
+ JS_PARSE_EXPORT_NONE, &init) < 0) {
+ goto fail;
+ }
+ // stack is now: fclosure
+ push_scope(s);
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+ // stack is now: fclosure this
+ emit_op(s, OP_swap);
+ // stack is now: this fclosure
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ // stack is now: returnvalue
+ emit_op(s, OP_drop);
+ // stack is now:
+ pop_scope(s);
+ s->cur_func = s->cur_func->parent;
+ continue;
+ }
/* allow "static" field name */
if (s->token.val == ';' || s->token.val == '=') {
is_static = FALSE;
@@ -22718,8 +23027,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0)
goto fail;
}
- if (add_brand(s, &class_fields[is_static]) < 0)
- goto fail;
+ class_fields[is_static].need_brand = TRUE;
}
if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
@@ -22866,8 +23174,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
}
if (is_private) {
- if (add_brand(s, &class_fields[is_static]) < 0)
- goto fail;
+ class_fields[is_static].need_brand = TRUE;
}
if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
goto fail;
@@ -22933,12 +23240,29 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
if (next_token(s))
goto fail;
- /* store the function to initialize the fields to that it can be
- referenced by the constructor */
{
ClassFieldsDef *cf = &class_fields[0];
int var_idx;
+
+ if (cf->need_brand) {
+ /* add a private brand to the prototype */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_null);
+ emit_op(s, OP_swap);
+ emit_op(s, OP_add_brand);
+
+ /* define the brand field in 'this' of the initializer */
+ if (!cf->fields_init_fd) {
+ if (emit_class_init_start(s, cf))
+ goto fail;
+ }
+ /* patch the start of the function to enable the
+ OP_add_brand_instance code */
+ cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
+ }
+ /* store the function to initialize the fields to that it can be
+ referenced by the constructor */
var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
JS_VAR_DEF_CONST);
if (var_idx < 0)
@@ -22956,6 +23280,23 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
/* drop the prototype */
emit_op(s, OP_drop);
+ if (class_fields[1].need_brand) {
+ /* add a private brand to the class */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_dup);
+ emit_op(s, OP_add_brand);
+ }
+
+ if (class_name != JS_ATOM_NULL) {
+ /* store the class name in the scoped class name variable (it
+ is independent from the class statement variable
+ definition) */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, class_name);
+ emit_u16(s, fd->scope_level);
+ }
+
/* initialize the static fields */
if (class_fields[1].fields_init_fd != NULL) {
ClassFieldsDef *cf = &class_fields[1];
@@ -22966,15 +23307,6 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
emit_op(s, OP_drop);
}
- if (class_name != JS_ATOM_NULL) {
- /* store the class name in the scoped class name variable (it
- is independent from the class statement variable
- definition) */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, class_name);
- emit_u16(s, fd->scope_level);
- }
pop_scope(s);
pop_scope(s);
@@ -24159,8 +24491,10 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
return -1;
}
name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s)) /* update line number before emitting code */
+ if (next_token(s)) { /* update line number before emitting code */
+ JS_FreeAtom(s->ctx, name);
return -1;
+ }
do_get_var:
emit_op(s, OP_scope_get_var);
emit_u32(s, name);
@@ -24307,6 +24641,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
drop_count = 2;
break;
+ case OP_get_field_opt_chain:
+ {
+ int opt_chain_label, next_label;
+ opt_chain_label = get_u32(fd->byte_code.buf +
+ fd->last_opcode_pos + 1 + 4 + 1);
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
+ fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
+ next_label = emit_goto(s, OP_goto, -1);
+ emit_label(s, opt_chain_label);
+ /* need an additional undefined value for the
+ case where the optional field does not
+ exists */
+ emit_op(s, OP_undefined);
+ emit_label(s, next_label);
+ drop_count = 2;
+ opcode = OP_get_field;
+ }
+ break;
case OP_scope_get_private_field:
/* keep the object on the stack */
fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
@@ -24317,6 +24670,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
drop_count = 2;
break;
+ case OP_get_array_el_opt_chain:
+ {
+ int opt_chain_label, next_label;
+ opt_chain_label = get_u32(fd->byte_code.buf +
+ fd->last_opcode_pos + 1 + 1);
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
+ fd->byte_code.size = fd->last_opcode_pos + 1;
+ next_label = emit_goto(s, OP_goto, -1);
+ emit_label(s, opt_chain_label);
+ /* need an additional undefined value for the
+ case where the optional field does not
+ exists */
+ emit_op(s, OP_undefined);
+ emit_label(s, next_label);
+ drop_count = 2;
+ opcode = OP_get_array_el;
+ }
+ break;
case OP_scope_get_var:
{
JSAtom name;
@@ -24599,8 +24971,23 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
break;
}
}
- if (optional_chaining_label >= 0)
- emit_label(s, optional_chaining_label);
+ if (optional_chaining_label >= 0) {
+ JSFunctionDef *fd = s->cur_func;
+ int opcode;
+ emit_label_raw(s, optional_chaining_label);
+ /* modify the last opcode so that it is an indicator of an
+ optional chain */
+ opcode = get_prev_opcode(fd);
+ if (opcode == OP_get_field || opcode == OP_get_array_el) {
+ if (opcode == OP_get_field)
+ opcode = OP_get_field_opt_chain;
+ else
+ opcode = OP_get_array_el_opt_chain;
+ fd->byte_code.buf[fd->last_opcode_pos] = opcode;
+ } else {
+ fd->last_opcode_pos = -1;
+ }
+ }
return 0;
}
@@ -24616,27 +25003,57 @@ static __exception int js_parse_delete(JSParseState *s)
return -1;
switch(opcode = get_prev_opcode(fd)) {
case OP_get_field:
+ case OP_get_field_opt_chain:
{
JSValue val;
- int ret;
-
+ int ret, opt_chain_label, next_label;
+ if (opcode == OP_get_field_opt_chain) {
+ opt_chain_label = get_u32(fd->byte_code.buf +
+ fd->last_opcode_pos + 1 + 4 + 1);
+ } else {
+ opt_chain_label = -1;
+ }
name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
fd->byte_code.size = fd->last_opcode_pos;
- fd->last_opcode_pos = -1;
val = JS_AtomToValue(s->ctx, name);
ret = emit_push_const(s, val, 1);
JS_FreeValue(s->ctx, val);
JS_FreeAtom(s->ctx, name);
if (ret)
return ret;
+ emit_op(s, OP_delete);
+ if (opt_chain_label >= 0) {
+ next_label = emit_goto(s, OP_goto, -1);
+ emit_label(s, opt_chain_label);
+ /* if the optional chain is not taken, return 'true' */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_push_true);
+ emit_label(s, next_label);
+ }
+ fd->last_opcode_pos = -1;
}
- goto do_delete;
+ break;
case OP_get_array_el:
fd->byte_code.size = fd->last_opcode_pos;
fd->last_opcode_pos = -1;
- do_delete:
emit_op(s, OP_delete);
break;
+ case OP_get_array_el_opt_chain:
+ {
+ int opt_chain_label, next_label;
+ opt_chain_label = get_u32(fd->byte_code.buf +
+ fd->last_opcode_pos + 1 + 1);
+ fd->byte_code.size = fd->last_opcode_pos;
+ emit_op(s, OP_delete);
+ next_label = emit_goto(s, OP_goto, -1);
+ emit_label(s, opt_chain_label);
+ /* if the optional chain is not taken, return 'true' */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_push_true);
+ emit_label(s, next_label);
+ fd->last_opcode_pos = -1;
+ }
+ break;
case OP_scope_get_var:
/* 'delete this': this is not a reference */
name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
@@ -24752,6 +25169,7 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
return -1;
if (js_parse_unary(s, PF_POW_FORBIDDEN))
return -1;
+ s->cur_func->has_await = TRUE;
emit_op(s, OP_await);
parse_flags = 0;
break;
@@ -24823,9 +25241,32 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
if (level == 0) {
return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |
PF_POW_ALLOWED);
+ } else if (s->token.val == TOK_PRIVATE_NAME &&
+ (parse_flags & PF_IN_ACCEPTED) && level == 4 &&
+ peek_token(s, FALSE) == TOK_IN) {
+ JSAtom atom;
+
+ atom = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail_private_in;
+ if (s->token.val != TOK_IN)
+ goto fail_private_in;
+ if (next_token(s))
+ goto fail_private_in;
+ if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) {
+ fail_private_in:
+ JS_FreeAtom(s->ctx, atom);
+ return -1;
+ }
+ emit_op(s, OP_scope_in_private_field);
+ emit_atom(s, atom);
+ emit_u16(s, s->cur_func->scope_level);
+ JS_FreeAtom(s->ctx, atom);
+ return 0;
+ } else {
+ if (js_parse_expr_binary(s, level - 1, parse_flags))
+ return -1;
}
- if (js_parse_expr_binary(s, level - 1, parse_flags))
- return -1;
for(;;) {
op = s->token.val;
switch(level) {
@@ -25125,7 +25566,6 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
/* OP_async_yield_star takes the value as parameter */
emit_op(s, OP_get_field);
emit_atom(s, JS_ATOM_value);
- emit_op(s, OP_await);
emit_op(s, OP_async_yield_star);
} else {
/* OP_yield_star takes (value, done) as parameter */
@@ -25419,61 +25859,61 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
static void emit_return(JSParseState *s, BOOL hasval)
{
BlockEnv *top;
- int drop_count;
- drop_count = 0;
+ if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
+ if (!hasval) {
+ /* no value: direct return in case of async generator */
+ emit_op(s, OP_undefined);
+ hasval = TRUE;
+ } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ /* the await must be done before handling the "finally" in
+ case it raises an exception */
+ emit_op(s, OP_await);
+ }
+ }
+
top = s->cur_func->top_break;
while (top != NULL) {
- /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
- required as all local variables will be closed upon returning
- from JS_CallInternal, but not in the same order. */
- if (top->has_iterator) {
- /* with 'yield', the exact number of OP_drop to emit is
- unknown, so we use a specific operation to look for
- the catch offset */
+ if (top->has_iterator || top->label_finally != -1) {
if (!hasval) {
emit_op(s, OP_undefined);
hasval = TRUE;
}
- emit_op(s, OP_iterator_close_return);
- if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
- int label_next, label_next2;
-
- emit_op(s, OP_drop); /* catch offset */
- emit_op(s, OP_drop); /* next */
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_return);
- /* stack: iter_obj return_func */
- emit_op(s, OP_dup);
- emit_op(s, OP_is_undefined_or_null);
- label_next = emit_goto(s, OP_if_true, -1);
- emit_op(s, OP_call_method);
- emit_u16(s, 0);
- emit_op(s, OP_iterator_check_object);
- emit_op(s, OP_await);
- label_next2 = emit_goto(s, OP_goto, -1);
- emit_label(s, label_next);
- emit_op(s, OP_drop);
- emit_label(s, label_next2);
- emit_op(s, OP_drop);
+ /* Remove the stack elements up to and including the catch
+ offset. When 'yield' is used in an expression we have
+ no easy way to count them, so we use this specific
+ instruction instead. */
+ emit_op(s, OP_nip_catch);
+ /* stack: iter_obj next ret_val */
+ if (top->has_iterator) {
+ if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ int label_next, label_next2;
+ emit_op(s, OP_nip); /* next */
+ emit_op(s, OP_swap);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_return);
+ /* stack: iter_obj return_func */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ label_next = emit_goto(s, OP_if_true, -1);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_await);
+ label_next2 = emit_goto(s, OP_goto, -1);
+ emit_label(s, label_next);
+ emit_op(s, OP_drop);
+ emit_label(s, label_next2);
+ emit_op(s, OP_drop);
+ } else {
+ emit_op(s, OP_rot3r);
+ emit_op(s, OP_undefined); /* dummy catch offset */
+ emit_op(s, OP_iterator_close);
+ }
} else {
- emit_op(s, OP_iterator_close);
+ /* execute the "finally" block */
+ emit_goto(s, OP_gosub, top->label_finally);
}
- drop_count = -3;
- }
- drop_count += top->drop_count;
- if (top->label_finally != -1) {
- while(drop_count) {
- /* must keep the stack top if hasval */
- emit_op(s, hasval ? OP_nip : OP_drop);
- drop_count--;
- }
- if (!hasval) {
- /* must push return value to keep same stack size */
- emit_op(s, OP_undefined);
- hasval = TRUE;
- }
- emit_goto(s, OP_gosub, top->label_finally);
}
top = top->prev;
}
@@ -25490,20 +25930,15 @@ static void emit_return(JSParseState *s, BOOL hasval)
label_return = -1;
}
- /* XXX: if this is not initialized, should throw the
- ReferenceError in the caller realm */
- emit_op(s, OP_scope_get_var);
+ /* The error should be raised in the caller context, so we use
+ a specific opcode */
+ emit_op(s, OP_scope_get_var_checkthis);
emit_atom(s, JS_ATOM_this);
emit_u16(s, 0);
emit_label(s, label_return);
emit_op(s, OP_return);
} else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
- if (!hasval) {
- emit_op(s, OP_undefined);
- } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
- emit_op(s, OP_await);
- }
emit_op(s, OP_return_async);
} else {
emit_op(s, hasval ? OP_return : OP_return_undef);
@@ -25770,6 +26205,9 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
emit_atom(s, var_name);
emit_u16(s, fd->scope_level);
}
+ } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, FALSE) == TOK_OF) {
+ return js_parse_error(s, "'for of' expression cannot start with 'async'");
} else {
int skip_bits;
if ((s->token.val == '[' || s->token.val == '{')
@@ -25986,6 +26424,10 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
js_parse_error(s, "return not in a function");
goto fail;
}
+ if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
+ js_parse_error(s, "return in a static initializer block");
+ goto fail;
+ }
if (next_token(s))
goto fail;
if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
@@ -26154,6 +26596,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
is_async = TRUE;
if (next_token(s))
goto fail;
+ s->cur_func->has_await = TRUE;
}
if (js_parse_expect(s, '('))
goto fail;
@@ -26684,6 +27127,9 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
m->func_obj = JS_UNDEFINED;
m->eval_exception = JS_UNDEFINED;
m->meta_obj = JS_UNDEFINED;
+ m->promise = JS_UNDEFINED;
+ m->resolving_funcs[0] = JS_UNDEFINED;
+ m->resolving_funcs[1] = JS_UNDEFINED;
list_add_tail(&m->link, &ctx->loaded_modules);
return m;
}
@@ -26705,6 +27151,9 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
JS_MarkValue(rt, m->func_obj, mark_func);
JS_MarkValue(rt, m->eval_exception, mark_func);
JS_MarkValue(rt, m->meta_obj, mark_func);
+ JS_MarkValue(rt, m->promise, mark_func);
+ JS_MarkValue(rt, m->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, m->resolving_funcs[1], mark_func);
}
static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
@@ -26735,11 +27184,15 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
JS_FreeAtom(ctx, mi->import_name);
}
js_free(ctx, m->import_entries);
+ js_free(ctx, m->async_parent_modules);
JS_FreeValue(ctx, m->module_ns);
JS_FreeValue(ctx, m->func_obj);
JS_FreeValue(ctx, m->eval_exception);
JS_FreeValue(ctx, m->meta_obj);
+ JS_FreeValue(ctx, m->promise);
+ JS_FreeValue(ctx, m->resolving_funcs[0]);
+ JS_FreeValue(ctx, m->resolving_funcs[1]);
list_del(&m->link);
js_free(ctx, m);
}
@@ -27595,7 +28048,8 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
/* Prepare a module to be executed by resolving all the imported
variables. */
-static int js_link_module(JSContext *ctx, JSModuleDef *m)
+static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m,
+ JSModuleDef **pstack_top, int index)
{
int i;
JSImportEntry *mi;
@@ -27605,21 +28059,47 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m)
BOOL is_c_module;
JSValue ret_val;
- if (m->instantiated)
- return 0;
- m->instantiated = TRUE;
-
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return -1;
+ }
+
#ifdef DUMP_MODULE_RESOLVE
{
char buf1[ATOM_GET_STR_BUF_SIZE];
- printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
}
#endif
+ if (m->status == JS_MODULE_STATUS_LINKING ||
+ m->status == JS_MODULE_STATUS_LINKED ||
+ m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED)
+ return index;
+
+ assert(m->status == JS_MODULE_STATUS_UNLINKED);
+ m->status = JS_MODULE_STATUS_LINKING;
+ m->dfs_index = index;
+ m->dfs_ancestor_index = index;
+ index++;
+ /* push 'm' on stack */
+ m->stack_prev = *pstack_top;
+ *pstack_top = m;
+
for(i = 0; i < m->req_module_entries_count; i++) {
JSReqModuleEntry *rme = &m->req_module_entries[i];
- if (js_link_module(ctx, rme->module) < 0)
+ m1 = rme->module;
+ index = js_inner_module_linking(ctx, m1, pstack_top, index);
+ if (index < 0)
goto fail;
+ assert(m1->status == JS_MODULE_STATUS_LINKING ||
+ m1->status == JS_MODULE_STATUS_LINKED ||
+ m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m1->status == JS_MODULE_STATUS_EVALUATED);
+ if (m1->status == JS_MODULE_STATUS_LINKING) {
+ m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
+ m1->dfs_ancestor_index);
+ }
}
#ifdef DUMP_MODULE_RESOLVE
@@ -27745,14 +28225,59 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m)
JS_FreeValue(ctx, ret_val);
}
+ assert(m->dfs_ancestor_index <= m->dfs_index);
+ if (m->dfs_index == m->dfs_ancestor_index) {
+ for(;;) {
+ /* pop m1 from stack */
+ m1 = *pstack_top;
+ *pstack_top = m1->stack_prev;
+ m1->status = JS_MODULE_STATUS_LINKED;
+ if (m1 == m)
+ break;
+ }
+ }
+
#ifdef DUMP_MODULE_RESOLVE
- printf("done instantiate\n");
+ printf("js_inner_module_linking done\n");
#endif
- return 0;
+ return index;
fail:
return -1;
}
+/* Prepare a module to be executed by resolving all the imported
+ variables. */
+static int js_link_module(JSContext *ctx, JSModuleDef *m)
+{
+ JSModuleDef *stack_top, *m1;
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+ assert(m->status == JS_MODULE_STATUS_UNLINKED ||
+ m->status == JS_MODULE_STATUS_LINKED ||
+ m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED);
+ stack_top = NULL;
+ if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) {
+ while (stack_top != NULL) {
+ m1 = stack_top;
+ assert(m1->status == JS_MODULE_STATUS_LINKING);
+ m1->status = JS_MODULE_STATUS_UNLINKED;
+ stack_top = m1->stack_prev;
+ }
+ return -1;
+ }
+ assert(stack_top == NULL);
+ assert(m->status == JS_MODULE_STATUS_LINKED ||
+ m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED);
+ return 0;
+}
+
/* return JS_ATOM_NULL if the name cannot be found. Only works with
not striped bytecode functions. */
JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
@@ -27761,8 +28286,8 @@ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
JSFunctionBytecode *b;
JSObject *p;
/* XXX: currently we just use the filename of the englobing
- function. It does not work for eval(). Need to add a
- ScriptOrModule info in JSFunctionBytecode */
+ function from the debug info. May need to add a ScriptOrModule
+ info in JSFunctionBytecode. */
sf = ctx->rt->current_stack_frame;
if (!sf)
return JS_ATOM_NULL;
@@ -27771,15 +28296,23 @@ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
if (!sf)
return JS_ATOM_NULL;
}
- if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
- return JS_ATOM_NULL;
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- if (!js_class_has_bytecode(p->class_id))
- return JS_ATOM_NULL;
- b = p->u.func.function_bytecode;
- if (!b->has_debug)
- return JS_ATOM_NULL;
- return JS_DupAtom(ctx, b->debug.filename);
+ for(;;) {
+ if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
+ return JS_ATOM_NULL;
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ if (!js_class_has_bytecode(p->class_id))
+ return JS_ATOM_NULL;
+ b = p->u.func.function_bytecode;
+ if (!b->is_direct_or_indirect_eval) {
+ if (!b->has_debug)
+ return JS_ATOM_NULL;
+ return JS_DupAtom(ctx, b->debug.filename);
+ } else {
+ sf = sf->prev_frame;
+ if (!sf)
+ return JS_ATOM_NULL;
+ }
+ }
}
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
@@ -27822,29 +28355,110 @@ static JSValue js_import_meta(JSContext *ctx)
return JS_GetImportMeta(ctx, m);
}
-/* used by os.Worker() and import() */
-JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
- const char *filename)
+static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m)
{
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+}
+
+static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic, JSValue *func_data)
+{
+ JSValueConst *resolving_funcs = (JSValueConst *)func_data;
+ JSValueConst error;
+ JSValue ret;
+
+ /* XXX: check if the test is necessary */
+ if (argc >= 1)
+ error = argv[0];
+ else
+ error = JS_UNDEFINED;
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+ 1, &error);
+ JS_FreeValue(ctx, ret);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic, JSValue *func_data)
+{
+ JSValueConst *resolving_funcs = (JSValueConst *)func_data;
+ JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]);
+ JSValue ret, ns;
+
+ /* return the module namespace */
+ ns = js_get_module_ns(ctx, m);
+ if (JS_IsException(ns)) {
+ JSValue err = JS_GetException(ctx);
+ js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data);
+ return JS_UNDEFINED;
+ }
+ ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
+ 1, (JSValueConst *)&ns);
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, ns);
+ return JS_UNDEFINED;
+}
+
+static void JS_LoadModuleInternal(JSContext *ctx, const char *basename,
+ const char *filename,
+ JSValueConst *resolving_funcs)
+{
+ JSValue evaluate_promise;
JSModuleDef *m;
- JSValue ret, func_obj;
+ JSValue ret, err, func_obj, evaluate_resolving_funcs[2];
+ JSValueConst func_data[3];
m = js_host_resolve_imported_module(ctx, basename, filename);
if (!m)
- return NULL;
+ goto fail;
if (js_resolve_module(ctx, m) < 0) {
js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
- return NULL;
+ goto fail;
}
/* Evaluate the module code */
- func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- ret = JS_EvalFunction(ctx, func_obj);
- if (JS_IsException(ret))
- return NULL;
+ func_obj = JS_NewModuleValue(ctx, m);
+ evaluate_promise = JS_EvalFunction(ctx, func_obj);
+ if (JS_IsException(evaluate_promise)) {
+ fail:
+ err = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+ 1, (JSValueConst *)&err);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, err);
+ return;
+ }
+
+ func_obj = JS_NewModuleValue(ctx, m);
+ func_data[0] = resolving_funcs[0];
+ func_data[1] = resolving_funcs[1];
+ func_data[2] = func_obj;
+ evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data);
+ evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data);
+ JS_FreeValue(ctx, func_obj);
+ ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs);
JS_FreeValue(ctx, ret);
- return m;
+ JS_FreeValue(ctx, evaluate_resolving_funcs[0]);
+ JS_FreeValue(ctx, evaluate_resolving_funcs[1]);
+ JS_FreeValue(ctx, evaluate_promise);
+}
+
+/* Return a promise or an exception in case of memory error. Used by
+ os.Worker() */
+JSValue JS_LoadModule(JSContext *ctx, const char *basename,
+ const char *filename)
+{
+ JSValue promise, resolving_funcs[2];
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise))
+ return JS_EXCEPTION;
+ JS_LoadModuleInternal(ctx, basename, filename,
+ (JSValueConst *)resolving_funcs);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
}
static JSValue js_dynamic_import_job(JSContext *ctx,
@@ -27853,9 +28467,8 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
JSValueConst *resolving_funcs = argv;
JSValueConst basename_val = argv[2];
JSValueConst specifier = argv[3];
- JSModuleDef *m;
const char *basename = NULL, *filename;
- JSValue ret, err, ns;
+ JSValue ret, err;
if (!JS_IsString(basename_val)) {
JS_ThrowTypeError(ctx, "no function filename for import()");
@@ -27869,24 +28482,12 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
if (!filename)
goto exception;
- m = JS_RunModule(ctx, basename, filename);
+ JS_LoadModuleInternal(ctx, basename, filename,
+ resolving_funcs);
JS_FreeCString(ctx, filename);
- if (!m)
- goto exception;
-
- /* return the module namespace */
- ns = js_get_module_ns(ctx, m);
- if (JS_IsException(ns))
- goto exception;
-
- ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
- 1, (JSValueConst *)&ns);
- JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
- JS_FreeValue(ctx, ns);
JS_FreeCString(ctx, basename);
return JS_UNDEFINED;
exception:
-
err = JS_GetException(ctx);
ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
1, (JSValueConst *)&err);
@@ -27922,6 +28523,8 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
args[2] = basename_val;
args[3] = specifier;
+ /* cannot run JS_LoadModuleInternal synchronously because it would
+ cause an unexpected recursion in js_evaluate_module() */
JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
JS_FreeValue(ctx, basename_val);
@@ -27930,60 +28533,397 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
return promise;
}
-/* Run the function of the module and of all its requested
- modules. */
-static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
+static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m)
+{
+ m->status = JS_MODULE_STATUS_EVALUATED;
+ if (!JS_IsUndefined(m->promise)) {
+ JSValue value, ret_val;
+ assert(m->cycle_root == m);
+ value = JS_UNDEFINED;
+ ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
+ 1, (JSValueConst *)&value);
+ JS_FreeValue(ctx, ret_val);
+ }
+}
+
+typedef struct {
+ JSModuleDef **tab;
+ int count;
+ int size;
+} ExecModuleList;
+
+/* XXX: slow. Could use a linked list instead of ExecModuleList */
+static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m)
+{
+ int i;
+ for(i = 0; i < exec_list->count; i++) {
+ if (exec_list->tab[i] == m)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module,
+ ExecModuleList *exec_list)
+{
+ int i;
+
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return -1;
+ }
+ for(i = 0; i < module->async_parent_modules_count; i++) {
+ JSModuleDef *m = module->async_parent_modules[i];
+ if (!find_in_exec_module_list(exec_list, m) &&
+ !m->cycle_root->eval_has_exception) {
+ assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
+ assert(!m->eval_has_exception);
+ assert(m->async_evaluation);
+ assert(m->pending_async_dependencies > 0);
+ m->pending_async_dependencies--;
+ if (m->pending_async_dependencies == 0) {
+ if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) {
+ return -1;
+ }
+ exec_list->tab[exec_list->count++] = m;
+ if (!m->has_tla) {
+ if (gather_available_ancestors(ctx, m, exec_list))
+ return -1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque)
+{
+ JSModuleDef *m1 = *(JSModuleDef **)p1;
+ JSModuleDef *m2 = *(JSModuleDef **)p2;
+ return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) -
+ (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp);
+}
+
+static int js_execute_async_module(JSContext *ctx, JSModuleDef *m);
+static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
+ JSValue *pvalue);
+
+static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic, JSValue *func_data)
+{
+ JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
+ JSValueConst error = argv[0];
+ int i;
+
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
+
+ if (module->status == JS_MODULE_STATUS_EVALUATED) {
+ assert(module->eval_has_exception);
+ return JS_UNDEFINED;
+ }
+
+ assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
+ assert(!module->eval_has_exception);
+ assert(module->async_evaluation);
+
+ module->eval_has_exception = TRUE;
+ module->eval_exception = JS_DupValue(ctx, error);
+ module->status = JS_MODULE_STATUS_EVALUATED;
+
+ 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);
+ ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED,
+ 1, &error);
+ JS_FreeValue(ctx, ret_val);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic, JSValue *func_data)
+{
+ JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
+ ExecModuleList exec_list_s, *exec_list = &exec_list_s;
+ int i;
+
+ if (module->status == JS_MODULE_STATUS_EVALUATED) {
+ assert(module->eval_has_exception);
+ return JS_UNDEFINED;
+ }
+ assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
+ assert(!module->eval_has_exception);
+ assert(module->async_evaluation);
+ module->async_evaluation = FALSE;
+ js_set_module_evaluated(ctx, module);
+
+ exec_list->tab = NULL;
+ exec_list->count = 0;
+ exec_list->size = 0;
+
+ if (gather_available_ancestors(ctx, module, exec_list) < 0) {
+ js_free(ctx, exec_list->tab);
+ return JS_EXCEPTION;
+ }
+
+ /* sort by increasing async_evaluation timestamp */
+ rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]),
+ exec_module_list_cmp, NULL);
+
+ for(i = 0; i < exec_list->count; i++) {
+ JSModuleDef *m = exec_list->tab[i];
+ if (m->status == JS_MODULE_STATUS_EVALUATED) {
+ assert(m->eval_has_exception);
+ } else if (m->has_tla) {
+ js_execute_async_module(ctx, m);
+ } else {
+ JSValue error;
+ if (js_execute_sync_module(ctx, m, &error) < 0) {
+ JSValue m_obj = JS_NewModuleValue(ctx, m);
+ js_async_module_execution_rejected(ctx, JS_UNDEFINED,
+ 1, (JSValueConst *)&error, 0,
+ &m_obj);
+ JS_FreeValue(ctx, m_obj);
+ JS_FreeValue(ctx, error);
+ } else {
+ js_set_module_evaluated(ctx, m);
+ }
+ }
+ }
+ js_free(ctx, exec_list->tab);
+ return JS_UNDEFINED;
+}
+
+static int js_execute_async_module(JSContext *ctx, JSModuleDef *m)
+{
+ JSValue promise, m_obj;
+ JSValue resolve_funcs[2], ret_val;
+ promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
+ if (JS_IsException(promise))
+ return -1;
+ m_obj = JS_NewModuleValue(ctx, m);
+ resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj);
+ resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj);
+ ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs);
+ JS_FreeValue(ctx, ret_val);
+ JS_FreeValue(ctx, m_obj);
+ JS_FreeValue(ctx, resolve_funcs[0]);
+ JS_FreeValue(ctx, resolve_funcs[1]);
+ JS_FreeValue(ctx, promise);
+ return 0;
+}
+
+/* return < 0 in case of exception. *pvalue contains the exception. */
+static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
+ JSValue *pvalue)
+{
+ if (m->init_func) {
+ /* C module init : no asynchronous execution */
+ if (m->init_func(ctx, m) < 0)
+ goto fail;
+ } else {
+ JSValue promise;
+ JSPromiseStateEnum state;
+
+ promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
+ if (JS_IsException(promise))
+ goto fail;
+ state = JS_PromiseState(ctx, promise);
+ if (state == JS_PROMISE_FULFILLED) {
+ JS_FreeValue(ctx, promise);
+ } else if (state == JS_PROMISE_REJECTED) {
+ *pvalue = JS_PromiseResult(ctx, promise);
+ JS_FreeValue(ctx, promise);
+ return -1;
+ } else {
+ JS_FreeValue(ctx, promise);
+ JS_ThrowTypeError(ctx, "promise is pending");
+ fail:
+ *pvalue = JS_GetException(ctx);
+ return -1;
+ }
+ }
+ *pvalue = JS_UNDEFINED;
+ return 0;
+}
+
+/* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1,
+ exception) */
+static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m,
+ int index, JSModuleDef **pstack_top,
+ JSValue *pvalue)
{
JSModuleDef *m1;
int i;
- JSValue ret_val;
- if (m->eval_mark)
- return JS_UNDEFINED; /* avoid cycles */
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ *pvalue = JS_GetException(ctx);
+ return -1;
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
- if (m->evaluated) {
- /* if the module was already evaluated, rethrow the exception
- it raised */
+ if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED) {
if (m->eval_has_exception) {
- return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
+ *pvalue = JS_DupValue(ctx, m->eval_exception);
+ return -1;
} else {
- return JS_UNDEFINED;
+ *pvalue = JS_UNDEFINED;
+ return index;
}
}
+ if (m->status == JS_MODULE_STATUS_EVALUATING) {
+ *pvalue = JS_UNDEFINED;
+ return index;
+ }
+ assert(m->status == JS_MODULE_STATUS_LINKED);
- m->eval_mark = TRUE;
-
+ m->status = JS_MODULE_STATUS_EVALUATING;
+ m->dfs_index = index;
+ m->dfs_ancestor_index = index;
+ m->pending_async_dependencies = 0;
+ index++;
+ /* push 'm' on stack */
+ m->stack_prev = *pstack_top;
+ *pstack_top = m;
+
for(i = 0; i < m->req_module_entries_count; i++) {
JSReqModuleEntry *rme = &m->req_module_entries[i];
m1 = rme->module;
- if (!m1->eval_mark) {
- ret_val = js_evaluate_module(ctx, m1);
- if (JS_IsException(ret_val)) {
- m->eval_mark = FALSE;
- return ret_val;
+ index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue);
+ if (index < 0)
+ return -1;
+ assert(m1->status == JS_MODULE_STATUS_EVALUATING ||
+ m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m1->status == JS_MODULE_STATUS_EVALUATED);
+ if (m1->status == JS_MODULE_STATUS_EVALUATING) {
+ m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
+ m1->dfs_ancestor_index);
+ } else {
+ m1 = m1->cycle_root;
+ assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m1->status == JS_MODULE_STATUS_EVALUATED);
+ if (m1->eval_has_exception) {
+ *pvalue = JS_DupValue(ctx, m1->eval_exception);
+ return -1;
}
- JS_FreeValue(ctx, ret_val);
+ }
+ if (m1->async_evaluation) {
+ m->pending_async_dependencies++;
+ if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) {
+ *pvalue = JS_GetException(ctx);
+ return -1;
+ }
+ m1->async_parent_modules[m1->async_parent_modules_count++] = m;
}
}
- if (m->init_func) {
- /* C module init */
- if (m->init_func(ctx, m) < 0)
- ret_val = JS_EXCEPTION;
- else
- ret_val = JS_UNDEFINED;
+ if (m->pending_async_dependencies > 0) {
+ assert(!m->async_evaluation);
+ m->async_evaluation = TRUE;
+ m->async_evaluation_timestamp =
+ ctx->rt->module_async_evaluation_next_timestamp++;
+ } else if (m->has_tla) {
+ assert(!m->async_evaluation);
+ m->async_evaluation = TRUE;
+ m->async_evaluation_timestamp =
+ ctx->rt->module_async_evaluation_next_timestamp++;
+ js_execute_async_module(ctx, m);
} else {
- ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);
- m->func_obj = JS_UNDEFINED;
+ if (js_execute_sync_module(ctx, m, pvalue) < 0)
+ return -1;
}
- if (JS_IsException(ret_val)) {
- /* save the thrown exception value */
- m->eval_has_exception = TRUE;
- m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
+
+ assert(m->dfs_ancestor_index <= m->dfs_index);
+ if (m->dfs_index == m->dfs_ancestor_index) {
+ for(;;) {
+ /* pop m1 from stack */
+ m1 = *pstack_top;
+ *pstack_top = m1->stack_prev;
+ if (!m1->async_evaluation) {
+ m1->status = JS_MODULE_STATUS_EVALUATED;
+ } else {
+ m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC;
+ }
+ /* spec bug: cycle_root must be assigned before the test */
+ m1->cycle_root = m;
+ if (m1 == m)
+ break;
+ }
}
- m->eval_mark = FALSE;
- m->evaluated = TRUE;
- return ret_val;
+ *pvalue = JS_UNDEFINED;
+ return index;
+}
+
+/* Run the function of the module and of all its requested
+ modules. Return a promise or an exception. */
+static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
+{
+ JSModuleDef *m1, *stack_top;
+ JSValue ret_val, result;
+
+ assert(m->status == JS_MODULE_STATUS_LINKED ||
+ m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED);
+ if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED) {
+ m = m->cycle_root;
+ }
+ /* a promise may be created only on the cycle_root of a cycle */
+ if (!JS_IsUndefined(m->promise))
+ return JS_DupValue(ctx, m->promise);
+ m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs);
+ if (JS_IsException(m->promise))
+ return JS_EXCEPTION;
+
+ stack_top = NULL;
+ if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) {
+ while (stack_top != NULL) {
+ m1 = stack_top;
+ assert(m1->status == JS_MODULE_STATUS_EVALUATING);
+ m1->status = JS_MODULE_STATUS_EVALUATED;
+ m1->eval_has_exception = TRUE;
+ m1->eval_exception = JS_DupValue(ctx, result);
+ m1->cycle_root = m; /* spec bug: should be present */
+ stack_top = m1->stack_prev;
+ }
+ JS_FreeValue(ctx, result);
+ assert(m->status == JS_MODULE_STATUS_EVALUATED);
+ assert(m->eval_has_exception);
+ ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED,
+ 1, (JSValueConst *)&m->eval_exception);
+ JS_FreeValue(ctx, ret_val);
+ } else {
+ assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
+ m->status == JS_MODULE_STATUS_EVALUATED);
+ assert(!m->eval_has_exception);
+ if (!m->async_evaluation) {
+ JSValue value;
+ assert(m->status == JS_MODULE_STATUS_EVALUATED);
+ value = JS_UNDEFINED;
+ ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
+ 1, (JSValueConst *)&value);
+ JS_FreeValue(ctx, ret_val);
+ }
+ assert(stack_top == NULL);
+ }
+ return JS_DupValue(ctx, m->promise);
}
static __exception JSAtom js_parse_from_clause(JSParseState *s)
@@ -29337,6 +30277,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
case OP_scope_get_ref:
dbuf_putc(bc, OP_undefined);
/* fall thru */
+ case OP_scope_get_var_checkthis:
case OP_scope_get_var_undef:
case OP_scope_get_var:
case OP_scope_put_var:
@@ -29362,7 +30303,12 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
}
} else {
if (s->vars[var_idx].is_lexical) {
- dbuf_putc(bc, OP_get_loc_check);
+ if (op == OP_scope_get_var_checkthis) {
+ /* only used for 'this' return in derived class constructors */
+ dbuf_putc(bc, OP_get_loc_checkthis);
+ } else {
+ dbuf_putc(bc, OP_get_loc_check);
+ }
} else {
dbuf_putc(bc, OP_get_loc);
}
@@ -29826,6 +30772,10 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
abort();
}
break;
+ case OP_scope_in_private_field:
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_private_in);
+ break;
default:
abort();
}
@@ -30503,6 +31453,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
dbuf_putc(&bc_out, op);
dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
break;
+ case OP_scope_get_var_checkthis:
case OP_scope_get_var_undef:
case OP_scope_get_var:
case OP_scope_put_var:
@@ -30532,6 +31483,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
case OP_scope_get_private_field:
case OP_scope_get_private_field2:
case OP_scope_put_private_field:
+ case OP_scope_in_private_field:
{
int ret;
var_name = get_u32(bc_buf + pos + 1);
@@ -30743,6 +31695,17 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
case OP_set_class_name:
/* only used during parsing */
break;
+
+ case OP_get_field_opt_chain: /* equivalent to OP_get_field */
+ {
+ JSAtom name = get_u32(bc_buf + pos + 1);
+ dbuf_putc(&bc_out, OP_get_field);
+ dbuf_put_u32(&bc_out, name);
+ }
+ break;
+ case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */
+ dbuf_putc(&bc_out, OP_get_array_el);
+ break;
default:
no_change:
@@ -31875,6 +32838,7 @@ typedef struct StackSizeState {
int bc_len;
int stack_len_max;
uint16_t *stack_level_tab;
+ int32_t *catch_pos_tab;
int *pc_stack;
int pc_stack_len;
int pc_stack_size;
@@ -31882,7 +32846,7 @@ typedef struct StackSizeState {
/* 'op' is only used for error indication */
static __exception int ss_check(JSContext *ctx, StackSizeState *s,
- int pos, int op, int stack_len)
+ int pos, int op, int stack_len, int catch_pos)
{
if ((unsigned)pos >= s->bc_len) {
JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
@@ -31901,6 +32865,10 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s,
JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)",
s->stack_level_tab[pos], stack_len, pos);
return -1;
+ } else if (s->catch_pos_tab[pos] != catch_pos) {
+ JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)",
+ s->catch_pos_tab[pos], catch_pos, pos);
+ return -1;
} else {
return 0;
}
@@ -31908,7 +32876,8 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s,
/* mark as explored and store the stack size */
s->stack_level_tab[pos] = stack_len;
-
+ s->catch_pos_tab[pos] = catch_pos;
+
/* queue the new PC to explore */
if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
&s->pc_stack_size, s->pc_stack_len + 1))
@@ -31922,7 +32891,7 @@ static __exception int compute_stack_size(JSContext *ctx,
int *pstack_size)
{
StackSizeState s_s, *s = &s_s;
- int i, diff, n_pop, pos_next, stack_len, pos, op;
+ int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level;
const JSOpCode *oi;
const uint8_t *bc_buf;
@@ -31935,24 +32904,33 @@ static __exception int compute_stack_size(JSContext *ctx,
return -1;
for(i = 0; i < s->bc_len; i++)
s->stack_level_tab[i] = 0xffff;
- s->stack_len_max = 0;
s->pc_stack = NULL;
+ s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) *
+ s->bc_len);
+ if (!s->catch_pos_tab)
+ goto fail;
+
+ s->stack_len_max = 0;
s->pc_stack_len = 0;
s->pc_stack_size = 0;
/* breadth-first graph exploration */
- if (ss_check(ctx, s, 0, OP_invalid, 0))
+ if (ss_check(ctx, s, 0, OP_invalid, 0, -1))
goto fail;
while (s->pc_stack_len > 0) {
pos = s->pc_stack[--s->pc_stack_len];
stack_len = s->stack_level_tab[pos];
+ catch_pos = s->catch_pos_tab[pos];
op = bc_buf[pos];
if (op == 0 || op >= OP_COUNT) {
JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
goto fail;
}
oi = &short_opcode_info(op);
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
+ printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
+#endif
pos_next = pos + oi->size;
if (pos_next > s->bc_len) {
JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
@@ -32008,55 +32986,104 @@ static __exception int compute_stack_size(JSContext *ctx,
case OP_if_true8:
case OP_if_false8:
diff = (int8_t)bc_buf[pos + 1];
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
goto fail;
break;
#endif
case OP_if_true:
case OP_if_false:
- case OP_catch:
diff = get_u32(bc_buf + pos + 1);
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
goto fail;
break;
case OP_gosub:
diff = get_u32(bc_buf + pos + 1);
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
goto fail;
break;
case OP_with_get_var:
case OP_with_delete_var:
diff = get_u32(bc_buf + pos + 5);
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos))
goto fail;
break;
case OP_with_make_ref:
case OP_with_get_ref:
case OP_with_get_ref_undef:
diff = get_u32(bc_buf + pos + 5);
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos))
goto fail;
break;
case OP_with_put_var:
diff = get_u32(bc_buf + pos + 5);
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos))
goto fail;
break;
-
+ case OP_catch:
+ diff = get_u32(bc_buf + pos + 1);
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
+ goto fail;
+ catch_pos = pos;
+ break;
+ case OP_for_of_start:
+ case OP_for_await_of_start:
+ catch_pos = pos;
+ break;
+ /* we assume the catch offset entry is only removed with
+ some op codes */
+ case OP_drop:
+ catch_level = stack_len;
+ goto check_catch;
+ case OP_nip:
+ catch_level = stack_len - 1;
+ goto check_catch;
+ case OP_nip1:
+ catch_level = stack_len - 1;
+ goto check_catch;
+ case OP_iterator_close:
+ catch_level = stack_len + 2;
+ check_catch:
+ /* Note: for for_of_start/for_await_of_start we consider
+ the catch offset is on the first stack entry instead of
+ the thirst */
+ if (catch_pos >= 0) {
+ int level;
+ level = s->stack_level_tab[catch_pos];
+ if (bc_buf[catch_pos] != OP_catch)
+ level++; /* for_of_start, for_wait_of_start */
+ /* catch_level = stack_level before op_catch is executed ? */
+ if (catch_level == level) {
+ catch_pos = s->catch_pos_tab[catch_pos];
+ }
+ }
+ break;
+ case OP_nip_catch:
+ if (catch_pos < 0) {
+ JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos);
+ goto fail;
+ }
+ stack_len = s->stack_level_tab[catch_pos];
+ if (bc_buf[catch_pos] != OP_catch)
+ stack_len++; /* for_of_start, for_wait_of_start */
+ stack_len++; /* no stack overflow is possible by construction */
+ catch_pos = s->catch_pos_tab[catch_pos];
+ break;
default:
break;
}
- if (ss_check(ctx, s, pos_next, op, stack_len))
+ if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos))
goto fail;
done_insn: ;
}
- js_free(ctx, s->stack_level_tab);
js_free(ctx, s->pc_stack);
+ js_free(ctx, s->catch_pos_tab);
+ js_free(ctx, s->stack_level_tab);
*pstack_size = s->stack_len_max;
return 0;
fail:
- js_free(ctx, s->stack_level_tab);
js_free(ctx, s->pc_stack);
+ js_free(ctx, s->catch_pos_tab);
+ js_free(ctx, s->stack_level_tab);
*pstack_size = 0;
return -1;
}
@@ -32301,6 +33328,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
b->super_allowed = fd->super_allowed;
b->arguments_allowed = fd->arguments_allowed;
b->backtrace_barrier = fd->backtrace_barrier;
+ b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT ||
+ fd->eval_type == JS_EVAL_TYPE_INDIRECT);
b->realm = JS_DupContext(ctx);
add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
@@ -32583,8 +33612,9 @@ static __exception int js_parse_function_decl2(JSParseState *s,
func_type == JS_PARSE_FUNC_EXPR &&
(func_kind & JS_FUNC_GENERATOR)) ||
(s->token.u.ident.atom == JS_ATOM_await &&
- func_type == JS_PARSE_FUNC_EXPR &&
- (func_kind & JS_FUNC_ASYNC))) {
+ ((func_type == JS_PARSE_FUNC_EXPR &&
+ (func_kind & JS_FUNC_ASYNC)) ||
+ func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) {
return js_parse_error_reserved_identifier(s);
}
}
@@ -32678,7 +33708,8 @@ static __exception int js_parse_function_decl2(JSParseState *s,
func_type == JS_PARSE_FUNC_SETTER ||
func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
- fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
+ fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW &&
+ func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT);
fd->has_this_binding = fd->has_arguments_binding;
fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
if (func_type == JS_PARSE_FUNC_ARROW) {
@@ -32686,6 +33717,11 @@ static __exception int js_parse_function_decl2(JSParseState *s,
fd->super_call_allowed = fd->parent->super_call_allowed;
fd->super_allowed = fd->parent->super_allowed;
fd->arguments_allowed = fd->parent->arguments_allowed;
+ } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
+ fd->new_target_allowed = TRUE; // although new.target === undefined
+ fd->super_call_allowed = FALSE;
+ fd->super_allowed = TRUE;
+ fd->arguments_allowed = FALSE;
} else {
fd->new_target_allowed = TRUE;
fd->super_call_allowed = fd->is_derived_class_constructor;
@@ -32723,7 +33759,7 @@ static __exception int js_parse_function_decl2(JSParseState *s,
if (add_arg(ctx, fd, name) < 0)
goto fail;
fd->defined_arg_count = 1;
- } else {
+ } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
if (s->token.val == '(') {
int skip_bits;
/* if there is an '=' inside the parameter list, we
@@ -32944,8 +33980,10 @@ static __exception int js_parse_function_decl2(JSParseState *s,
}
}
- if (js_parse_expect(s, '{'))
- goto fail;
+ if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
+ if (js_parse_expect(s, '{'))
+ goto fail;
+ }
if (js_parse_directives(s))
goto fail;
@@ -32975,9 +34013,15 @@ static __exception int js_parse_function_decl2(JSParseState *s,
if (js_is_live_code(s)) {
emit_return(s, FALSE);
}
-done:
+ done:
s->cur_func = fd->parent;
+ /* Reparse identifiers after the function is terminated so that
+ the token is parsed in the englobing function. It could be done
+ by just using next_token() here for normal functions, but it is
+ necessary for arrow functions with an expression body. */
+ reparse_ident_token(s);
+
/* create the function object */
{
int idx;
@@ -33132,9 +34176,9 @@ static __exception int js_parse_program(JSParseState *s)
emit_op(s, OP_get_loc);
emit_u16(s, fd->eval_ret_idx);
- emit_op(s, OP_return);
+ emit_return(s, TRUE);
} else {
- emit_op(s, OP_return_undef);
+ emit_return(s, FALSE);
}
return 0;
@@ -33177,7 +34221,6 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
ret_val = js_evaluate_module(ctx, m);
if (JS_IsException(ret_val)) {
fail:
- js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);
return JS_EXCEPTION;
}
} else {
@@ -33192,31 +34235,6 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
}
-static void skip_shebang(JSParseState *s)
-{
- const uint8_t *p = s->buf_ptr;
- int c;
-
- if (p[0] == '#' && p[1] == '!') {
- p += 2;
- while (p < s->buf_end) {
- if (*p == '\n' || *p == '\r') {
- break;
- } else if (*p >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- if (c == CP_LS || c == CP_PS) {
- break;
- } else if (c == -1) {
- p++; /* skip invalid UTF-8 */
- }
- } else {
- p++;
- }
- }
- s->buf_ptr = p;
- }
-}
-
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
const char *input, size_t input_len,
@@ -33232,7 +34250,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
JSModuleDef *m;
js_parse_init(ctx, s, input, input_len, filename);
- skip_shebang(s);
+ skip_shebang(&s->buf_ptr, s->buf_end);
eval_type = flags & JS_EVAL_TYPE_MASK;
m = NULL;
@@ -33290,6 +34308,10 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
goto fail;
}
fd->module = m;
+ if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) {
+ fd->in_function_body = TRUE;
+ fd->func_kind = JS_FUNC_ASYNC;
+ }
s->is_module = (m != NULL);
s->allow_html_comments = !s->is_module;
@@ -33304,6 +34326,9 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
goto fail1;
}
+ if (m != NULL)
+ m->has_tla = fd->has_await;
+
/* create the function object and all the enclosed functions */
fun_obj = js_create_function(ctx, fd);
if (JS_IsException(fun_obj))
@@ -33313,7 +34338,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
m->func_obj = fun_obj;
if (js_resolve_module(ctx, m) < 0)
goto fail1;
- fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ fun_obj = JS_NewModuleValue(ctx, m);
}
if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
ret_val = fun_obj;
@@ -33954,6 +34979,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
bc_set_flags(&flags, &idx, b->has_debug, 1);
bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
+ bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1);
assert(idx <= 16);
bc_put_u16(s, flags);
bc_put_u8(s, b->js_mode);
@@ -34059,6 +35085,8 @@ static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
bc_put_atom(s, mi->import_name);
bc_put_leb128(s, mi->req_module_idx);
}
+
+ bc_put_u8(s, m->has_tla);
if (JS_WriteObjectRec(s, m->func_obj))
goto fail;
@@ -34892,6 +35920,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
bc.has_debug = bc_get_flags(v16, &idx, 1);
bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
+ bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1);
bc.read_only_bytecode = s->is_rom_data;
if (bc_get_u8(s, &v8))
goto fail;
@@ -35070,7 +36099,7 @@ static JSValue JS_ReadModule(BCReaderState *s)
m = js_new_module_def(ctx, module_name);
if (!m)
goto fail;
- obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ obj = JS_NewModuleValue(ctx, m);
if (bc_get_leb128_int(s, &m->req_module_entries_count))
goto fail;
if (m->req_module_entries_count != 0) {
@@ -35143,6 +36172,10 @@ static JSValue JS_ReadModule(BCReaderState *s)
}
}
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ m->has_tla = (v8 != 0);
+
m->func_obj = JS_ReadObjectRec(s);
if (JS_IsException(m->func_obj))
goto fail;
@@ -37100,6 +38133,7 @@ static const JSCFunctionListEntry js_object_funcs[] = {
JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
+ JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ),
JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
@@ -37530,7 +38564,8 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv, int magic)
{
JSValue obj, msg, proto;
- JSValueConst message;
+ JSValueConst message, options;
+ int arg_index;
if (JS_IsUndefined(new_target))
new_target = JS_GetActiveFunction(ctx);
@@ -37556,12 +38591,9 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
JS_FreeValue(ctx, proto);
if (JS_IsException(obj))
return obj;
- if (magic == JS_AGGREGATE_ERROR) {
- message = argv[1];
- } else {
- message = argv[0];
- }
+ arg_index = (magic == JS_AGGREGATE_ERROR);
+ message = argv[arg_index++];
if (!JS_IsUndefined(message)) {
msg = JS_ToString(ctx, message);
if (unlikely(JS_IsException(msg)))
@@ -37570,6 +38602,22 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
+ if (arg_index < argc) {
+ options = argv[arg_index];
+ if (JS_IsObject(options)) {
+ int present = JS_HasProperty(ctx, options, JS_ATOM_cause);
+ if (present < 0)
+ goto exception;
+ if (present) {
+ JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause);
+ if (JS_IsException(cause))
+ goto exception;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+ }
+ }
+
if (magic == JS_AGGREGATE_ERROR) {
JSValue error_list = iterator_to_array(ctx, argv[0]);
if (JS_IsException(error_list))
@@ -37998,6 +39046,71 @@ static JSValue js_array_at(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION;
}
+static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, obj, ret, *arrp, *pval;
+ JSObject *p;
+ int64_t i, len, idx;
+ uint32_t count32;
+
+ ret = JS_EXCEPTION;
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ if (JS_ToInt64Sat(ctx, &idx, argv[0]))
+ goto exception;
+
+ if (idx < 0)
+ idx = len + idx;
+
+ if (idx < 0 || idx >= len) {
+ JS_ThrowRangeError(ctx, "out of bound");
+ goto exception;
+ }
+
+ arr = js_allocate_fast_array(ctx, len);
+ if (JS_IsException(arr))
+ goto exception;
+
+ p = JS_VALUE_GET_OBJ(arr);
+ i = 0;
+ pval = p->u.array.u.values;
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ for (; i < idx; i++, pval++)
+ *pval = JS_DupValue(ctx, arrp[i]);
+ *pval = JS_DupValue(ctx, argv[1]);
+ for (i++, pval++; i < len; i++, pval++)
+ *pval = JS_DupValue(ctx, arrp[i]);
+ } else {
+ for (; i < idx; i++, pval++)
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
+ goto fill_and_fail;
+ *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;
+ goto exception;
+ }
+ }
+ }
+
+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
+ goto exception;
+
+ ret = arr;
+ arr = JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, obj);
+ return ret;
+}
+
static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -38816,6 +39929,61 @@ static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION;
}
+// Note: a.toReversed() is a.slice().reverse() with the twist that a.slice()
+// leaves holes in sparse arrays intact whereas a.toReversed() replaces them
+// with undefined, thus in effect creating a dense array.
+// Does not use Array[@@species], always returns a base Array.
+static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, obj, ret, *arrp, *pval;
+ JSObject *p;
+ int64_t i, len;
+ uint32_t count32;
+
+ ret = JS_EXCEPTION;
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ arr = js_allocate_fast_array(ctx, len);
+ if (JS_IsException(arr))
+ goto exception;
+
+ if (len > 0) {
+ p = JS_VALUE_GET_OBJ(arr);
+
+ i = len - 1;
+ pval = p->u.array.u.values;
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ for (; i >= 0; i--, pval++)
+ *pval = JS_DupValue(ctx, arrp[i]);
+ } 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;
+ goto exception;
+ }
+ }
+ }
+
+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
+ goto exception;
+ }
+
+ ret = arr;
+ arr = JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, obj);
+ return ret;
+}
+
static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int splice)
{
@@ -38923,6 +40091,92 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION;
}
+static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, obj, ret, *arrp, *pval, *last;
+ JSObject *p;
+ int64_t i, j, len, newlen, start, add, del;
+ uint32_t count32;
+
+ pval = NULL;
+ last = NULL;
+ ret = JS_EXCEPTION;
+ arr = JS_UNDEFINED;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ start = 0;
+ if (argc > 0)
+ if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+
+ del = 0;
+ if (argc > 0)
+ del = len - start;
+ if (argc > 1)
+ if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0))
+ goto exception;
+
+ add = 0;
+ if (argc > 2)
+ add = argc - 2;
+
+ newlen = len + add - del;
+ if (newlen > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "invalid array length");
+ goto exception;
+ }
+
+ arr = js_allocate_fast_array(ctx, newlen);
+ if (JS_IsException(arr))
+ goto exception;
+
+ if (newlen <= 0)
+ goto done;
+
+ p = JS_VALUE_GET_OBJ(arr);
+ pval = &p->u.array.u.values[0];
+ last = &p->u.array.u.values[newlen];
+
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ for (i = 0; i < start; i++, pval++)
+ *pval = JS_DupValue(ctx, arrp[i]);
+ for (j = 0; j < add; j++, pval++)
+ *pval = JS_DupValue(ctx, argv[2 + j]);
+ for (i += del; i < len; i++, pval++)
+ *pval = JS_DupValue(ctx, arrp[i]);
+ } else {
+ for (i = 0; i < start; i++, pval++)
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
+ goto exception;
+ for (j = 0; j < add; j++, pval++)
+ *pval = JS_DupValue(ctx, argv[2 + j]);
+ for (i += del; i < len; i++, pval++)
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
+ goto exception;
+ }
+
+ 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;
+}
+
static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -39226,6 +40480,68 @@ fail:
return JS_EXCEPTION;
}
+// Note: a.toSorted() is a.slice().sort() with the twist that a.slice()
+// leaves holes in sparse arrays intact whereas a.toSorted() replaces them
+// with undefined, thus in effect creating a dense array.
+// Does not use Array[@@species], always returns a base Array.
+static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, obj, ret, *arrp, *pval;
+ JSObject *p;
+ int64_t i, len;
+ uint32_t count32;
+ int ok;
+
+ ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]);
+ if (!ok)
+ return JS_ThrowTypeError(ctx, "not a function");
+
+ ret = JS_EXCEPTION;
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ arr = js_allocate_fast_array(ctx, len);
+ if (JS_IsException(arr))
+ goto exception;
+
+ if (len > 0) {
+ p = JS_VALUE_GET_OBJ(arr);
+ i = 0;
+ pval = p->u.array.u.values;
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ for (; i < len; i++, pval++)
+ *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;
+ 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);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, ret);
+
+ ret = arr;
+ arr = JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, obj);
+ return ret;
+}
+
typedef struct JSArrayIteratorData {
JSValue obj;
JSIteratorKindEnum kind;
@@ -39379,6 +40695,7 @@ static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
static const JSCFunctionListEntry js_array_proto_funcs[] = {
JS_CFUNC_DEF("at", 1, js_array_at ),
+ JS_CFUNC_DEF("with", 2, js_array_with ),
JS_CFUNC_DEF("concat", 1, js_array_concat ),
JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
@@ -39403,9 +40720,12 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
+ JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ),
JS_CFUNC_DEF("sort", 1, js_array_sort ),
+ JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ),
JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
+ JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ),
JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
@@ -40213,6 +41533,84 @@ static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
return index;
}
+/* return the position of the first invalid character in the string or
+ -1 if none */
+static int js_string_find_invalid_codepoint(JSString *p)
+{
+ int i, c;
+ if (!p->is_wide_char)
+ return -1;
+ for(i = 0; i < p->len; i++) {
+ c = p->u.str16[i];
+ if (c >= 0xD800 && c <= 0xDFFF) {
+ if (c >= 0xDC00 || (i + 1) >= p->len)
+ return i;
+ c = p->u.str16[i + 1];
+ if (c < 0xDC00 || c > 0xDFFF)
+ return i;
+ i++;
+ }
+ }
+ return -1;
+}
+
+static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ JSString *p;
+ BOOL ret;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_STRING(str);
+ ret = (js_string_find_invalid_codepoint(p) < 0);
+ JS_FreeValue(ctx, str);
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ JSString *p;
+ int c, i;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+
+ p = JS_VALUE_GET_STRING(str);
+ /* avoid reallocating the string if it is well-formed */
+ i = js_string_find_invalid_codepoint(p);
+ if (i < 0)
+ return str;
+
+ ret = js_new_string16(ctx, p->u.str16, p->len);
+ JS_FreeValue(ctx, str);
+ if (JS_IsException(ret))
+ return JS_EXCEPTION;
+
+ p = JS_VALUE_GET_STRING(ret);
+ for (; i < p->len; i++) {
+ c = p->u.str16[i];
+ if (c >= 0xD800 && c <= 0xDFFF) {
+ if (c >= 0xDC00 || (i + 1) >= p->len) {
+ p->u.str16[i] = 0xFFFD;
+ } else {
+ c = p->u.str16[i + 1];
+ if (c < 0xDC00 || c > 0xDFFF) {
+ p->u.str16[i] = 0xFFFD;
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int lastIndexOf)
{
@@ -41025,26 +42423,6 @@ static BOOL test_final_sigma(JSString *p, int sigma_pos)
return !lre_is_cased(c1);
}
-static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue a, b;
- int cmp;
-
- a = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(a))
- return JS_EXCEPTION;
- b = JS_ToString(ctx, argv[0]);
- if (JS_IsException(b)) {
- JS_FreeValue(ctx, a);
- return JS_EXCEPTION;
- }
- cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
- JS_FreeValue(ctx, a);
- JS_FreeValue(ctx, b);
- return JS_NewInt32(ctx, cmp);
-}
-
static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int to_lower)
{
@@ -41130,23 +42508,38 @@ static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
return JS_EXCEPTION;
}
+static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf,
+ JSValueConst val,
+ UnicodeNormalizationEnum n_type)
+{
+ int buf_len, out_len;
+ uint32_t *buf, *out_buf;
+
+ buf_len = JS_ToUTF32String(ctx, &buf, val);
+ if (buf_len < 0)
+ return -1;
+ out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
+ ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
+ js_free(ctx, buf);
+ if (out_len < 0)
+ return -1;
+ *pout_buf = out_buf;
+ return out_len;
+}
+
static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
const char *form, *p;
size_t form_len;
- int is_compat, buf_len, out_len;
+ int is_compat, out_len;
UnicodeNormalizationEnum n_type;
JSValue val;
- uint32_t *buf, *out_buf;
+ uint32_t *out_buf;
val = JS_ToStringCheckObject(ctx, this_val);
if (JS_IsException(val))
return val;
- buf_len = JS_ToUTF32String(ctx, &buf, val);
- JS_FreeValue(ctx, val);
- if (buf_len < 0)
- return JS_EXCEPTION;
if (argc == 0 || JS_IsUndefined(argv[0])) {
n_type = UNICODE_NFC;
@@ -41172,22 +42565,96 @@ static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
JS_FreeCString(ctx, form);
JS_ThrowRangeError(ctx, "bad normalization form");
fail1:
- js_free(ctx, buf);
+ JS_FreeValue(ctx, val);
return JS_EXCEPTION;
}
JS_FreeCString(ctx, form);
}
- out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
- ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
- js_free(ctx, buf);
+ out_len = js_string_normalize1(ctx, &out_buf, val, n_type);
+ JS_FreeValue(ctx, val);
if (out_len < 0)
return JS_EXCEPTION;
val = JS_NewUTF32String(ctx, out_buf, out_len);
js_free(ctx, out_buf);
return val;
}
-#endif /* CONFIG_ALL_UNICODE */
+
+/* return < 0, 0 or > 0 */
+static int js_UTF32_compare(const uint32_t *buf1, int buf1_len,
+ const uint32_t *buf2, int buf2_len)
+{
+ int i, len, c, res;
+ len = min_int(buf1_len, buf2_len);
+ for(i = 0; i < len; i++) {
+ /* Note: range is limited so a subtraction is valid */
+ c = buf1[i] - buf2[i];
+ if (c != 0)
+ return c;
+ }
+ if (buf1_len == buf2_len)
+ res = 0;
+ else if (buf1_len < buf2_len)
+ res = -1;
+ else
+ res = 1;
+ return res;
+}
+
+static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue a, b;
+ int cmp, a_len, b_len;
+ uint32_t *a_buf, *b_buf;
+
+ a = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(a))
+ return JS_EXCEPTION;
+ b = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(b)) {
+ JS_FreeValue(ctx, a);
+ return JS_EXCEPTION;
+ }
+ a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC);
+ JS_FreeValue(ctx, a);
+ if (a_len < 0) {
+ JS_FreeValue(ctx, b);
+ return JS_EXCEPTION;
+ }
+
+ b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC);
+ JS_FreeValue(ctx, b);
+ if (b_len < 0) {
+ js_free(ctx, a_buf);
+ return JS_EXCEPTION;
+ }
+ cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len);
+ js_free(ctx, a_buf);
+ js_free(ctx, b_buf);
+ return JS_NewInt32(ctx, cmp);
+}
+#else /* CONFIG_ALL_UNICODE */
+static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue a, b;
+ int cmp;
+
+ a = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(a))
+ return JS_EXCEPTION;
+ b = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(b)) {
+ JS_FreeValue(ctx, a);
+ return JS_EXCEPTION;
+ }
+ cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
+ JS_FreeValue(ctx, a);
+ JS_FreeValue(ctx, b);
+ return JS_NewInt32(ctx, cmp);
+}
+#endif /* !CONFIG_ALL_UNICODE */
/* also used for String.prototype.valueOf */
static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
@@ -41364,6 +42831,8 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ),
JS_CFUNC_DEF("concat", 1, js_string_concat ),
JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
+ JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ),
+ JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ),
JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
@@ -41696,40 +43165,13 @@ static const JSCFunctionListEntry js_math_obj[] = {
/* Date */
-#if 0
-/* OS dependent: return the UTC time in ms since 1970. */
-static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- int64_t d;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
- return JS_NewInt64(ctx, d);
-}
-#endif
-
-/* OS dependent: return the UTC time in microseconds since 1970. */
-static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- int64_t d;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
- return JS_NewInt64(ctx, d);
-}
-
/* OS dependent. d = argv[0] is in ms from 1970. Return the difference
between UTC time and local time 'd' in minutes */
-static int getTimezoneOffset(int64_t time) {
-#if defined(_WIN32)
- /* XXX: TODO */
- return 0;
-#else
+static int getTimezoneOffset(int64_t time)
+{
time_t ti;
- struct tm tm;
-
+ int res;
+
time /= 1000; /* convert to seconds */
if (sizeof(time_t) == 4) {
/* on 32-bit systems, we need to clamp the time value to the
@@ -41752,9 +43194,27 @@ static int getTimezoneOffset(int64_t time) {
}
}
ti = time;
- localtime_r(&ti, &tm);
- return -tm.tm_gmtoff / 60;
+#if defined(_WIN32)
+ {
+ struct tm *tm;
+ time_t gm_ti, loc_ti;
+
+ tm = gmtime(&ti);
+ gm_ti = mktime(tm);
+
+ tm = localtime(&ti);
+ loc_ti = mktime(tm);
+
+ res = (gm_ti - loc_ti) / 60;
+ }
+#else
+ {
+ struct tm tm;
+ localtime_r(&ti, &tm);
+ res = -tm.tm_gmtoff / 60;
+ }
#endif
+ return res;
}
#if 0
@@ -41831,6 +43291,9 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
/* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
for (i = 0; i < len; i++) {
switch(str[i]) {
+ case 'd':
+ mask = LRE_FLAG_INDICES;
+ break;
case 'g':
mask = LRE_FLAG_GLOBAL;
break;
@@ -42174,6 +43637,11 @@ static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'd';
res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
if (res < 0)
goto exception;
@@ -42253,25 +43721,32 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
{
JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
JSString *str;
- JSValue str_val, obj, val, groups = JS_UNDEFINED;
+ JSValue t, ret, str_val, obj, val, groups;
+ JSValue indices, indices_groups;
uint8_t *re_bytecode;
- int ret;
uint8_t **capture, *str_buf;
- int capture_count, shift, i, re_flags;
+ int rc, capture_count, shift, i, re_flags;
int64_t last_index;
const char *group_name_ptr;
if (!re)
return JS_EXCEPTION;
+
str_val = JS_ToString(ctx, argv[0]);
if (JS_IsException(str_val))
- return str_val;
- val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
- if (JS_IsException(val) ||
- JS_ToLengthFree(ctx, &last_index, val)) {
- JS_FreeValue(ctx, str_val);
return JS_EXCEPTION;
- }
+
+ ret = JS_EXCEPTION;
+ obj = JS_NULL;
+ groups = JS_UNDEFINED;
+ indices = JS_UNDEFINED;
+ indices_groups = JS_UNDEFINED;
+ capture = NULL;
+
+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
+ if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
+ goto fail;
+
re_bytecode = re->bytecode->u.str8;
re_flags = lre_get_flags(re_bytecode);
if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
@@ -42279,27 +43754,23 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
}
str = JS_VALUE_GET_STRING(str_val);
capture_count = lre_get_capture_count(re_bytecode);
- capture = NULL;
if (capture_count > 0) {
capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
- if (!capture) {
- JS_FreeValue(ctx, str_val);
- return JS_EXCEPTION;
- }
+ if (!capture)
+ goto fail;
}
shift = str->is_wide_char;
str_buf = str->u.str8;
if (last_index > str->len) {
- ret = 2;
+ rc = 2;
} else {
- ret = lre_exec(capture, re_bytecode,
- str_buf, last_index, str->len,
- shift, ctx);
+ rc = lre_exec(capture, re_bytecode,
+ str_buf, last_index, str->len,
+ shift, ctx);
}
- obj = JS_NULL;
- if (ret != 1) {
- if (ret >= 0) {
- if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
+ if (rc != 1) {
+ if (rc >= 0) {
+ if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
JS_NewInt32(ctx, 0)) < 0)
goto fail;
@@ -42308,7 +43779,6 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
JS_ThrowInternalError(ctx, "out of memory in regexp execution");
goto fail;
}
- JS_FreeValue(ctx, str_val);
} else {
int prop_flags;
if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
@@ -42326,52 +43796,124 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
if (JS_IsException(groups))
goto fail;
}
-
- for(i = 0; i < capture_count; i++) {
- int start, end;
- JSValue val;
- if (capture[2 * i] == NULL ||
- capture[2 * i + 1] == NULL) {
- val = JS_UNDEFINED;
- } else {
- start = (capture[2 * i] - str_buf) >> shift;
- end = (capture[2 * i + 1] - str_buf) >> shift;
- val = js_sub_string(ctx, str, start, end);
- if (JS_IsException(val))
+ if (re_flags & LRE_FLAG_INDICES) {
+ indices = JS_NewArray(ctx);
+ if (JS_IsException(indices))
+ goto fail;
+ if (group_name_ptr) {
+ indices_groups = JS_NewObjectProto(ctx, JS_NULL);
+ if (JS_IsException(indices_groups))
goto fail;
}
+ }
+
+ for(i = 0; i < capture_count; i++) {
+ const char *name = NULL;
+ uint8_t **match = &capture[2 * i];
+ int start = -1;
+ int end = -1;
+ JSValue val;
+
if (group_name_ptr && i > 0) {
- if (*group_name_ptr) {
- if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,
- JS_DupValue(ctx, val),
- prop_flags) < 0) {
+ if (*group_name_ptr) name = group_name_ptr;
+ group_name_ptr += strlen(group_name_ptr) + 1;
+ }
+
+ if (match[0] && match[1]) {
+ start = (match[0] - str_buf) >> shift;
+ end = (match[1] - str_buf) >> shift;
+ }
+
+ if (!JS_IsUndefined(indices)) {
+ val = JS_UNDEFINED;
+ if (start != -1) {
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_DefinePropertyValueUint32(ctx, val, 0,
+ JS_NewInt32(ctx, start),
+ prop_flags) < 0) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, val, 1,
+ JS_NewInt32(ctx, end),
+ prop_flags) < 0) {
JS_FreeValue(ctx, val);
goto fail;
}
}
- group_name_ptr += strlen(group_name_ptr) + 1;
+ if (name && !JS_IsUndefined(indices_groups)) {
+ val = JS_DupValue(ctx, val);
+ if (JS_DefinePropertyValueStr(ctx, indices_groups,
+ name, val, prop_flags) < 0) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ }
+ if (JS_DefinePropertyValueUint32(ctx, indices, i, val,
+ prop_flags) < 0) {
+ goto fail;
+ }
}
+
+ val = JS_UNDEFINED;
+ if (start != -1) {
+ val = js_sub_string(ctx, str, start, end);
+ if (JS_IsException(val))
+ goto fail;
+ }
+
+ if (name) {
+ if (JS_DefinePropertyValueStr(ctx, groups, name,
+ JS_DupValue(ctx, val),
+ prop_flags) < 0) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ }
+
if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
goto fail;
}
+
+ t = groups, groups = JS_UNDEFINED;
if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
- groups, prop_flags) < 0)
+ t, prop_flags) < 0) {
goto fail;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,
- JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)
+ }
+
+ t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift);
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0)
goto fail;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)
- goto fail1;
+
+ t = str_val, str_val = JS_UNDEFINED;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0)
+ goto fail;
+
+ if (!JS_IsUndefined(indices)) {
+ t = indices_groups, indices_groups = JS_UNDEFINED;
+ if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups,
+ t, prop_flags) < 0) {
+ goto fail;
+ }
+ t = indices, indices = JS_UNDEFINED;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices,
+ t, prop_flags) < 0) {
+ goto fail;
+ }
+ }
}
- js_free(ctx, capture);
- return obj;
+ ret = obj;
+ obj = JS_UNDEFINED;
fail:
- JS_FreeValue(ctx, groups);
+ JS_FreeValue(ctx, indices_groups);
+ JS_FreeValue(ctx, indices);
JS_FreeValue(ctx, str_val);
-fail1:
+ JS_FreeValue(ctx, groups);
JS_FreeValue(ctx, obj);
js_free(ctx, capture);
- return JS_EXCEPTION;
+ return ret;
}
/* delete portions of a string that match a given regex */
@@ -43221,12 +44763,13 @@ static const JSCFunctionListEntry js_regexp_funcs[] = {
static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
- JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),
- JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),
- JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),
- JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),
- JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),
- JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),
+ JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ),
+ JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ),
+ JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ),
+ JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ),
+ JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UTF16 ),
+ JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ),
+ JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ),
JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
JS_CFUNC_DEF("test", 1, js_regexp_test ),
@@ -45696,6 +47239,123 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED;
}
+static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int is_map)
+{
+ JSValueConst cb, args[2];
+ JSValue res, iter, next, groups, key, v, prop;
+ JSAtom key_atom = JS_ATOM_NULL;
+ int64_t idx;
+ BOOL done;
+
+ // "is function?" check must be observed before argv[0] is accessed
+ cb = argv[1];
+ if (check_function(ctx, cb))
+ return JS_EXCEPTION;
+
+ iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE);
+ if (JS_IsException(iter))
+ return JS_EXCEPTION;
+
+ key = JS_UNDEFINED;
+ key_atom = JS_ATOM_NULL;
+ v = JS_UNDEFINED;
+ prop = JS_UNDEFINED;
+ groups = JS_UNDEFINED;
+
+ next = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next))
+ goto exception;
+
+ if (is_map) {
+ groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0);
+ } else {
+ groups = JS_NewObjectProto(ctx, JS_NULL);
+ }
+ if (JS_IsException(groups))
+ goto exception;
+
+ for (idx = 0; ; idx++) {
+ if (idx >= MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "too many elements");
+ goto iterator_close_exception;
+ }
+ v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception;
+ if (done)
+ break; // v is JS_UNDEFINED
+
+ args[0] = v;
+ args[1] = JS_NewInt64(ctx, idx);
+ key = JS_Call(ctx, cb, ctx->global_obj, 2, args);
+ if (JS_IsException(key))
+ goto iterator_close_exception;
+
+ if (is_map) {
+ prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0);
+ } else {
+ key_atom = JS_ValueToAtom(ctx, key);
+ JS_FreeValue(ctx, key);
+ key = JS_UNDEFINED;
+ if (key_atom == JS_ATOM_NULL)
+ goto iterator_close_exception;
+ prop = JS_GetProperty(ctx, groups, key_atom);
+ }
+ if (JS_IsException(prop))
+ goto exception;
+
+ if (JS_IsUndefined(prop)) {
+ prop = JS_NewArray(ctx);
+ if (JS_IsException(prop))
+ goto exception;
+ if (is_map) {
+ args[0] = key;
+ args[1] = prop;
+ res = js_map_set(ctx, groups, 2, args, 0);
+ if (JS_IsException(res))
+ goto exception;
+ JS_FreeValue(ctx, res);
+ } else {
+ prop = JS_DupValue(ctx, prop);
+ if (JS_DefinePropertyValue(ctx, groups, key_atom, prop,
+ JS_PROP_C_W_E) < 0) {
+ goto exception;
+ }
+ }
+ }
+ res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0);
+ if (JS_IsException(res))
+ goto exception;
+ // res is an int64
+
+ JS_FreeValue(ctx, prop);
+ JS_FreeValue(ctx, key);
+ JS_FreeAtom(ctx, key_atom);
+ JS_FreeValue(ctx, v);
+ prop = JS_UNDEFINED;
+ key = JS_UNDEFINED;
+ key_atom = JS_ATOM_NULL;
+ v = JS_UNDEFINED;
+ }
+
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, next);
+ return groups;
+
+ iterator_close_exception:
+ JS_IteratorClose(ctx, iter, TRUE);
+ exception:
+ JS_FreeAtom(ctx, key_atom);
+ JS_FreeValue(ctx, prop);
+ JS_FreeValue(ctx, key);
+ JS_FreeValue(ctx, v);
+ JS_FreeValue(ctx, groups);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, next);
+ return JS_EXCEPTION;
+}
+
static void js_map_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p;
@@ -45876,6 +47536,7 @@ static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
}
static const JSCFunctionListEntry js_map_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ),
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
};
@@ -45996,12 +47657,6 @@ static const JSCFunctionListEntry js_generator_proto_funcs[] = {
/* Promise */
-typedef enum JSPromiseStateEnum {
- JS_PROMISE_PENDING,
- JS_PROMISE_FULFILLED,
- JS_PROMISE_REJECTED,
-} JSPromiseStateEnum;
-
typedef struct JSPromiseData {
JSPromiseStateEnum promise_state;
/* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
@@ -46026,6 +47681,22 @@ typedef struct JSPromiseReactionData {
JSValue handler;
} JSPromiseReactionData;
+JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise)
+{
+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
+ if (!s)
+ return -1;
+ return s->promise_state;
+}
+
+JSValue JS_PromiseResult(JSContext *ctx, JSValue promise)
+{
+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
+ if (!s)
+ return JS_UNDEFINED;
+ return JS_DupValue(ctx, s->promise_result);
+}
+
static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
JSValueConst promise);
@@ -46471,17 +48142,14 @@ static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
return result_promise;
}
-#if 0
-static JSValue js_promise___newPromiseCapability(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_promise_withResolvers(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
JSValue result_promise, resolving_funcs[2], obj;
- JSValueConst ctor;
- ctor = argv[0];
- if (!JS_IsObject(ctor))
+ if (!JS_IsObject(this_val))
return JS_ThrowTypeErrorNotAnObject(ctx);
- result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
if (JS_IsException(result_promise))
return result_promise;
obj = JS_NewObject(ctx);
@@ -46496,7 +48164,6 @@ static JSValue js_promise___newPromiseCapability(JSContext *ctx,
JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
return obj;
}
-#endif
static __exception int remainingElementsCount_add(JSContext *ctx,
JSValueConst resolve_element_env,
@@ -46986,7 +48653,7 @@ static const JSCFunctionListEntry js_promise_funcs[] = {
JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
JS_CFUNC_DEF("race", 1, js_promise_race ),
- //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),
+ JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ),
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
};
@@ -47597,12 +49264,6 @@ static const JSCFunctionListEntry js_global_funcs[] = {
JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
JS_PROP_UNDEFINED_DEF("undefined", 0 ),
-
- /* for the 'Date' implementation */
- JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),
- //JS_CFUNC_DEF("__date_now", 0, js___date_now ),
- //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),
- //JS_CFUNC_DEF("__date_create", 3, js___date_create ),
};
/* Date */
@@ -47930,7 +49591,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
break;
case 3:
pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,
+ "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
(h < 12) ? 'A' : 'P');
break;
}
@@ -50758,6 +52419,9 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
"flatMap" "\0"
"includes" "\0"
"keys" "\0"
+ "toReversed" "\0"
+ "toSorted" "\0"
+ "toSpliced" "\0"
"values" "\0";
const char *p = unscopables;
obj1 = JS_NewObjectProto(ctx, JS_NULL);
@@ -51012,11 +52676,26 @@ static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSArrayBuffer *abuf = p->u.array_buffer;
+ struct list_head *el, *el1;
+
if (abuf) {
/* The ArrayBuffer finalizer may be called before the typed
array finalizers using it, so abuf->array_list is not
necessarily empty. */
- // assert(list_empty(&abuf->array_list));
+ list_for_each_safe(el, el1, &abuf->array_list) {
+ JSTypedArray *ta;
+ JSObject *p1;
+
+ ta = list_entry(el, JSTypedArray, link);
+ ta->link.prev = NULL;
+ ta->link.next = NULL;
+ p1 = ta->obj;
+ /* Note: the typed array length and offset fields are not modified */
+ if (p1->class_id != JS_CLASS_DATAVIEW) {
+ p1->u.array.count = 0;
+ p1->u.array.u.ptr = NULL;
+ }
+ }
if (abuf->shared && rt->sab_funcs.sab_free) {
rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
} else {
@@ -51469,6 +53148,43 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val,
return JS_GetPropertyInt64(ctx, this_val, idx);
}
+static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, val;
+ JSObject *p;
+ int64_t idx, len;
+
+ p = get_typed_array(ctx, this_val, /*is_dataview*/0);
+ if (!p)
+ return JS_EXCEPTION;
+
+ if (JS_ToInt64Sat(ctx, &idx, argv[0]))
+ return JS_EXCEPTION;
+
+ len = p->u.array.count;
+ if (idx < 0)
+ idx = len + idx;
+ if (idx < 0 || idx >= len)
+ return JS_ThrowRangeError(ctx, "out of bound");
+
+ val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+
+ arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
+ p->class_id);
+ if (JS_IsException(arr)) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) {
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+ }
+ return arr;
+}
+
static JSValue js_typed_array_set(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
@@ -52247,6 +53963,24 @@ static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
return JS_DupValue(ctx, this_val);
}
+static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, ret;
+ JSObject *p;
+
+ p = get_typed_array(ctx, this_val, /*is_dataview*/0);
+ if (!p)
+ return JS_EXCEPTION;
+ arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
+ p->class_id);
+ if (JS_IsException(arr))
+ return JS_EXCEPTION;
+ ret = js_typed_array_reverse(ctx, arr, argc, argv);
+ JS_FreeValue(ctx, arr);
+ return ret;
+}
+
static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -52455,7 +54189,7 @@ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
struct TA_sort_context {
JSContext *ctx;
- int exception;
+ int exception; /* 1 = exception, 2 = detached typed array */
JSValueConst arr;
JSValueConst cmp;
JSValue (*getfun)(JSContext *ctx, const void *a);
@@ -52473,6 +54207,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
cmp = 0;
if (!psc->exception) {
+ /* Note: the typed array can be detached without causing an
+ error */
a_idx = *(uint32_t *)a;
b_idx = *(uint32_t *)b;
argv[0] = psc->getfun(ctx, psc->array_ptr +
@@ -52500,8 +54236,9 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
/* make sort stable: compare array offsets */
cmp = (a_idx > b_idx) - (a_idx < b_idx);
}
- if (validate_typed_array(ctx, psc->arr) < 0) {
- psc->exception = 1;
+ if (unlikely(typed_array_is_detached(ctx,
+ JS_VALUE_GET_PTR(psc->arr)))) {
+ psc->exception = 2;
}
done:
JS_FreeValue(ctx, (JSValue)argv[0]);
@@ -52525,11 +54262,11 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
tsc.arr = this_val;
tsc.cmp = argv[0];
+ if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
+ return JS_EXCEPTION;
len = js_typed_array_get_length_internal(ctx, this_val);
if (len < 0)
return JS_EXCEPTION;
- if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
- return JS_EXCEPTION;
if (len > 1) {
p = JS_VALUE_GET_OBJ(this_val);
@@ -52595,44 +54332,48 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
tsc.elt_size = elt_size;
rqsort(array_idx, len, sizeof(array_idx[0]),
js_TA_cmp_generic, &tsc);
- if (tsc.exception)
- goto fail;
- array_tmp = js_malloc(ctx, len * elt_size);
- if (!array_tmp) {
- fail:
- js_free(ctx, array_idx);
- return JS_EXCEPTION;
+ if (tsc.exception) {
+ if (tsc.exception == 1)
+ goto fail;
+ /* detached typed array during the sort: no error */
+ } else {
+ array_tmp = js_malloc(ctx, len * elt_size);
+ if (!array_tmp) {
+ fail:
+ js_free(ctx, array_idx);
+ return JS_EXCEPTION;
+ }
+ 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();
+ }
+ js_free(ctx, array_tmp);
}
- 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();
- }
- js_free(ctx, array_tmp);
js_free(ctx, array_idx);
} else {
rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
@@ -52643,6 +54384,24 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
return JS_DupValue(ctx, this_val);
}
+static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue arr, ret;
+ JSObject *p;
+
+ p = get_typed_array(ctx, this_val, /*is_dataview*/0);
+ if (!p)
+ return JS_EXCEPTION;
+ arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
+ p->class_id);
+ if (JS_IsException(arr))
+ return JS_EXCEPTION;
+ ret = js_typed_array_sort(ctx, arr, argc, argv);
+ JS_FreeValue(ctx, arr);
+ return ret;
+}
+
static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
JS_CFUNC_DEF("from", 1, js_typed_array_from ),
JS_CFUNC_DEF("of", 0, js_typed_array_of ),
@@ -52655,6 +54414,7 @@ static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
JS_CFUNC_DEF("at", 1, js_typed_array_at ),
+ JS_CFUNC_DEF("with", 2, js_typed_array_with ),
JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
@@ -52678,9 +54438,11 @@ static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, special_findLast ),
JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, special_findLastIndex ),
JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
+ JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ),
JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
+ JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ),
JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
@@ -52952,7 +54714,7 @@ static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
if (ta) {
/* during the GC the finalizers are called in an arbitrary
order so the ArrayBuffer finalizer may have been called */
- if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) {
+ if (ta->link.next) {
list_del(&ta->link);
}
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
diff --git a/deps/quickjs/quickjs.h b/deps/quickjs/quickjs.h
index ce3dc908..700ee61d 100644
--- a/deps/quickjs/quickjs.h
+++ b/deps/quickjs/quickjs.h
@@ -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);
@@ -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,8 +913,8 @@ 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,
- const char *filename);
+JSValue JS_LoadModule(JSContext *ctx, const char *basename,
+ const char *filename);
/* C function definition */
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
diff --git a/deps/quickjs/release.sh b/deps/quickjs/release.sh
index 26fba1b0..6b1b496c 100755
--- a/deps/quickjs/release.sh
+++ b/deps/quickjs/release.sh
@@ -8,12 +8,12 @@ version=`cat VERSION`
if [ "$1" = "-h" ] ; then
echo "release.sh [release_list]"
echo ""
- echo "release_list: extras binary win_binary quickjs"
+ echo "release_list: extras binary win_binary cosmo_binary quickjs"
exit 1
fi
-release_list="extras binary win_binary quickjs"
+release_list="extras binary win_binary cosmo_binary quickjs"
if [ "$1" != "" ] ; then
release_list="$1"
@@ -84,6 +84,28 @@ cp $dlldir/libwinpthread-1.dll $outdir
fi
+#################################################"
+# Cosmopolitan binary release
+
+if echo $release_list | grep -w -q cosmo_binary ; then
+
+export PATH=$PATH:$HOME/cosmocc/bin
+
+d="quickjs-cosmo-${version}"
+outdir="/tmp/${d}"
+
+rm -rf $outdir
+mkdir -p $outdir
+
+make clean
+make CONFIG_COSMO=y -j4 qjs run-test262
+cp qjs run-test262 $outdir
+cp readme-cosmo.txt $outdir/readme.txt
+
+( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . )
+
+fi
+
#################################################"
# Linux binary release
diff --git a/deps/quickjs/repl.js b/deps/quickjs/repl.js
index 484269e1..62714a99 100644
--- a/deps/quickjs/repl.js
+++ b/deps/quickjs/repl.js
@@ -118,6 +118,7 @@ import * as os from "os";
var utf8 = true;
var show_time = false;
var show_colors = true;
+ var eval_start_time;
var eval_time = 0;
var mexpr = "";
@@ -814,10 +815,8 @@ import * as os from "os";
prompt += ps2;
} else {
if (show_time) {
- var t = Math.round(eval_time) + " ";
- eval_time = 0;
- t = dupstr("0", 5 - t.length) + t;
- prompt += t.substring(0, t.length - 4) + "." + t.substring(t.length - 4);
+ var t = eval_time / 1000;
+ prompt += t.toFixed(6) + " ";
}
plen = prompt.length;
prompt += ps1;
@@ -1224,37 +1223,6 @@ import * as os from "os";
}
}
- function eval_and_print(expr) {
- var result;
-
- try {
- if (eval_mode === "math")
- expr = '"use math"; void 0;' + expr;
- var now = (new Date).getTime();
- /* eval as a script */
- result = std.evalScript(expr, { backtrace_barrier: true });
- eval_time = (new Date).getTime() - now;
- std.puts(colors[styles.result]);
- print(result);
- std.puts("\n");
- std.puts(colors.none);
- /* set the last result */
- g._ = result;
- } catch (error) {
- std.puts(colors[styles.error_msg]);
- if (error instanceof Error) {
- console.log(error);
- if (error.stack) {
- std.puts(error.stack);
- }
- } else {
- std.puts("Throw: ");
- console.log(error);
- }
- std.puts(colors.none);
- }
- }
-
function cmd_start() {
if (!config_numcalc) {
if (has_jscalc)
@@ -1281,29 +1249,32 @@ import * as os from "os";
}
function readline_handle_cmd(expr) {
- handle_cmd(expr);
- cmd_readline_start();
+ if (!handle_cmd(expr)) {
+ cmd_readline_start();
+ }
}
+ /* return true if async termination */
function handle_cmd(expr) {
var colorstate, cmd;
if (expr === null) {
expr = "";
- return;
+ return false;
}
if (expr === "?") {
help();
- return;
+ return false;
}
cmd = extract_directive(expr);
if (cmd.length > 0) {
- if (!handle_directive(cmd, expr))
- return;
+ if (!handle_directive(cmd, expr)) {
+ return false;
+ }
expr = expr.substring(cmd.length + 1);
}
if (expr === "")
- return;
+ return false;
if (mexpr)
expr = mexpr + '\n' + expr;
@@ -1312,20 +1283,73 @@ import * as os from "os";
level = colorstate[1];
if (pstate) {
mexpr = expr;
- return;
+ return false;
}
mexpr = "";
if (has_bignum) {
- BigFloatEnv.setPrec(eval_and_print.bind(null, expr),
+ /* XXX: async is not supported in this case */
+ BigFloatEnv.setPrec(eval_and_print_start.bind(null, expr, false),
prec, expBits);
} else {
- eval_and_print(expr);
+ eval_and_print_start(expr, true);
}
- level = 0;
+ return true;
+ }
+
+ function eval_and_print_start(expr, is_async) {
+ var result;
+ try {
+ if (eval_mode === "math")
+ expr = '"use math"; void 0;' + expr;
+ eval_start_time = os.now();
+ /* eval as a script */
+ result = std.evalScript(expr, { backtrace_barrier: true, async: is_async });
+ if (is_async) {
+ /* result is a promise */
+ result.then(print_eval_result, print_eval_error);
+ } else {
+ print_eval_result(result);
+ }
+ } catch (error) {
+ print_eval_error(error);
+ }
+ }
+
+ function print_eval_result(result) {
+ eval_time = os.now() - eval_start_time;
+ std.puts(colors[styles.result]);
+ print(result);
+ std.puts("\n");
+ std.puts(colors.none);
+ /* set the last result */
+ g._ = result;
+
+ handle_cmd_end();
+ }
+
+ function print_eval_error(error) {
+ std.puts(colors[styles.error_msg]);
+ if (error instanceof Error) {
+ console.log(error);
+ if (error.stack) {
+ std.puts(error.stack);
+ }
+ } else {
+ std.puts("Throw: ");
+ console.log(error);
+ }
+ std.puts(colors.none);
+
+ handle_cmd_end();
+ }
+
+ function handle_cmd_end() {
+ level = 0;
/* run the garbage collector after each command */
std.gc();
+ cmd_readline_start();
}
function colorize_js(str) {
diff --git a/deps/quickjs/run-test262.c b/deps/quickjs/run-test262.c
index 2092caca..bf905bda 100644
--- a/deps/quickjs/run-test262.c
+++ b/deps/quickjs/run-test262.c
@@ -1174,7 +1174,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
{
JSValue res_val, exception_val;
int ret, error_line, pos, pos_line;
- BOOL is_error, has_error_line;
+ BOOL is_error, has_error_line, ret_promise;
const char *error_name;
pos = skip_comments(buf, 1, &pos_line);
@@ -1183,12 +1183,19 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
exception_val = JS_UNDEFINED;
error_name = NULL;
+ /* a module evaluation returns a promise */
+ ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0);
async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */
-
+
res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
- if (is_async && !JS_IsException(res_val)) {
- JS_FreeValue(ctx, res_val);
+ if ((is_async || ret_promise) && !JS_IsException(res_val)) {
+ JSValue promise = JS_UNDEFINED;
+ if (ret_promise) {
+ promise = res_val;
+ } else {
+ JS_FreeValue(ctx, res_val);
+ }
for(;;) {
JSContext *ctx1;
ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
@@ -1196,15 +1203,27 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
res_val = JS_EXCEPTION;
break;
} else if (ret == 0) {
- /* test if the test called $DONE() once */
- if (async_done != 1) {
- res_val = JS_ThrowTypeError(ctx, "$DONE() not called");
+ if (is_async) {
+ /* test if the test called $DONE() once */
+ if (async_done != 1) {
+ res_val = JS_ThrowTypeError(ctx, "$DONE() not called");
+ } else {
+ res_val = JS_UNDEFINED;
+ }
} else {
- res_val = JS_UNDEFINED;
+ /* check that the returned promise is fulfilled */
+ JSPromiseStateEnum state = JS_PromiseState(ctx, promise);
+ if (state == JS_PROMISE_FULFILLED)
+ res_val = JS_UNDEFINED;
+ else if (state == JS_PROMISE_REJECTED)
+ res_val = JS_Throw(ctx, JS_PromiseResult(ctx, promise));
+ else
+ res_val = JS_ThrowTypeError(ctx, "promise is pending");
}
break;
}
}
+ JS_FreeValue(ctx, promise);
}
if (JS_IsException(res_val)) {
@@ -1498,7 +1517,7 @@ void update_stats(JSRuntime *rt, const char *filename) {
#undef update
}
-int run_test_buf(const char *filename, char *harness, namelist_t *ip,
+int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
char *buf, size_t buf_len, const char* error_type,
int eval_flags, BOOL is_negative, BOOL is_async,
BOOL can_block)
@@ -1582,6 +1601,8 @@ int run_test(const char *filename, int index)
if (p) {
snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s",
(int)(p - filename), filename, "harness");
+ } else {
+ pstrcpy(harnessbuf, sizeof(harnessbuf), "");
}
harness = harnessbuf;
}
@@ -1669,6 +1690,8 @@ int run_test(const char *filename, int index)
if (p) {
snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s",
(int)(p - filename), filename, "test/harness");
+ } else {
+ pstrcpy(harnessbuf, sizeof(harnessbuf), "");
}
harness = harnessbuf;
}
@@ -1835,17 +1858,32 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
js_std_dump_error(ctx);
ret_code = 1;
} else {
- JS_FreeValue(ctx, res_val);
+ JSValue promise = JS_UNDEFINED;
+ if (is_module) {
+ promise = res_val;
+ } else {
+ JS_FreeValue(ctx, res_val);
+ }
for(;;) {
JSContext *ctx1;
ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if (ret < 0) {
- js_std_dump_error(ctx1);
- ret_code = 1;
+ js_std_dump_error(ctx1);
+ ret_code = 1;
} else if (ret == 0) {
- break;
+ break;
}
}
+ /* dump the error if the module returned an error. */
+ if (is_module) {
+ JSPromiseStateEnum state = JS_PromiseState(ctx, promise);
+ if (state == JS_PROMISE_REJECTED) {
+ JS_Throw(ctx, JS_PromiseResult(ctx, promise));
+ js_std_dump_error(ctx);
+ ret_code = 1;
+ }
+ }
+ JS_FreeValue(ctx, promise);
}
free(buf);
#ifdef CONFIG_AGENT
diff --git a/deps/quickjs/test262.conf b/deps/quickjs/test262.conf
index c4349f12..fd9862e8 100644
--- a/deps/quickjs/test262.conf
+++ b/deps/quickjs/test262.conf
@@ -56,7 +56,7 @@ AggregateError
align-detached-buffer-semantics-with-web-reality
arbitrary-module-namespace-names=skip
array-find-from-last
-array-grouping=skip
+array-grouping
Array.fromAsync=skip
Array.prototype.at
Array.prototype.flat
@@ -73,13 +73,13 @@ Atomics
Atomics.waitAsync=skip
BigInt
caller
-change-array-by-copy=skip
+change-array-by-copy
class
class-fields-private
-class-fields-private-in=skip
+class-fields-private-in
class-fields-public
class-methods-private
-class-static-block=skip
+class-static-block
class-static-fields-private
class-static-fields-public
class-static-methods-private
@@ -102,7 +102,7 @@ default-parameters
destructuring-assignment
destructuring-binding
dynamic-import
-error-cause=skip
+error-cause
exponentiation
export-star-as-namespace-from-module
FinalizationGroup=skip
@@ -141,7 +141,7 @@ Object.is
optional-catch-binding
optional-chaining
Promise
-promise-with-resolvers=skip
+promise-with-resolvers
Promise.allSettled
Promise.any
Promise.prototype.finally
@@ -154,7 +154,7 @@ Reflect.setPrototypeOf
regexp-dotall
regexp-duplicate-named-groups=skip
regexp-lookbehind
-regexp-match-indices=skip
+regexp-match-indices
regexp-named-groups
regexp-unicode-property-escapes
regexp-v-flag=skip
@@ -169,10 +169,10 @@ String.fromCodePoint
String.prototype.at
String.prototype.endsWith
String.prototype.includes
-String.prototype.isWellFormed=skip
+String.prototype.isWellFormed
String.prototype.matchAll
String.prototype.replaceAll
-String.prototype.toWellFormed=skip
+String.prototype.toWellFormed
String.prototype.trimEnd
String.prototype.trimStart
super
@@ -195,7 +195,7 @@ symbols-as-weakmap-keys=skip
tail-call-optimization=skip
template
Temporal=skip
-top-level-await=skip
+top-level-await
TypedArray
TypedArray.prototype.at
u180e
diff --git a/deps/quickjs/test262_errors.txt b/deps/quickjs/test262_errors.txt
index e9462dd4..edf5372a 100644
--- a/deps/quickjs/test262_errors.txt
+++ b/deps/quickjs/test262_errors.txt
@@ -1,41 +1,8 @@
test262/test/annexB/language/eval-code/direct/script-decl-lex-collision-in-sloppy-mode.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
-test262/test/built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js:53: TypeError: $DONE() not called
-test262/test/built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js:53: strict mode: TypeError: $DONE() not called
-test262/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js:34: TypeError: $DONE() not called
-test262/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js:34: strict mode: TypeError: $DONE() not called
-test262/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js:39: TypeError: $DONE() not called
-test262/test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js:39: strict mode: TypeError: $DONE() not called
-test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: Test262Error: Expected a ReferenceError but got a different error constructor with the same name
-test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: strict mode: Test262Error: Expected a ReferenceError but got a different error constructor with the same name
-test262/test/built-ins/RegExp/lookahead-quantifier-match-groups.js:27: Test262Error: Expected [a, abc] and [a, undefined] to have the same contents. ? quantifier
-test262/test/built-ins/RegExp/lookahead-quantifier-match-groups.js:27: strict mode: Test262Error: Expected [a, abc] and [a, undefined] to have the same contents. ? quantifier
-test262/test/built-ins/RegExp/unicode_full_case_folding.js:20: Test262Error: \u0390 does not match \u1fd3
-test262/test/built-ins/RegExp/unicode_full_case_folding.js:20: strict mode: Test262Error: \u0390 does not match \u1fd3
-test262/test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js:62: Test262Error: String.prototype.localeCompare considers ö (\u006f\u0308) ≠ ö (\u00f6).
-test262/test/built-ins/String/prototype/localeCompare/15.5.4.9_CE.js:62: strict mode: Test262Error: String.prototype.localeCompare considers ö (\u006f\u0308) ≠ ö (\u00f6).
-test262/test/built-ins/TypedArray/prototype/sort/sort-tonumber.js:30: TypeError: ArrayBuffer is detached (Testing with Float64Array.)
-test262/test/built-ins/TypedArray/prototype/sort/sort-tonumber.js:30: strict mode: TypeError: ArrayBuffer is detached (Testing with Float64Array.)
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: Test262Error: Expected a DummyError but got a TypeError
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError
test262/test/language/expressions/assignment/target-member-computed-reference-undefined.js:32: Test262Error: Expected a DummyError but got a TypeError
test262/test/language/expressions/assignment/target-member-computed-reference-undefined.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError
-test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called
-test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
-test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: cannot read property 'c' of undefined
-test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: cannot read property '_b' of undefined
+test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated.
+test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated.
test262/test/language/global-code/script-decl-lex-var-declared-via-eval-sloppy.js:13: Test262Error: variable Expected a SyntaxError to be thrown but no exception was thrown at all
-test262/test/language/module-code/namespace/internals/define-own-property.js:30: Test262Error: Object.freeze: 1 Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/async-generator/yield-star-promise-not-unwrapped.js:25: TypeError: $DONE() not called
-test262/test/language/statements/async-generator/yield-star-promise-not-unwrapped.js:25: strict mode: TypeError: $DONE() not called
-test262/test/language/statements/async-generator/yield-star-return-then-getter-ticks.js:131: TypeError: $DONE() not called
-test262/test/language/statements/async-generator/yield-star-return-then-getter-ticks.js:131: strict mode: TypeError: $DONE() not called
-test262/test/language/statements/class/elements/private-method-double-initialisation-get-and-set.js:33: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation-get-and-set.js:33: strict mode: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation-get.js:32: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation-get.js:32: strict mode: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation-set.js:32: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation-set.js:32: strict mode: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation.js:32: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/class/elements/private-method-double-initialisation.js:32: strict mode: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
-test262/test/language/statements/for-of/head-lhs-async-invalid.js:14: unexpected error type: Test262: This statement should not be evaluated.
-test262/test/language/statements/for-of/head-lhs-async-invalid.js:14: strict mode: unexpected error type: Test262: This statement should not be evaluated.
diff --git a/deps/quickjs/tests/microbench.js b/deps/quickjs/tests/microbench.js
index 1c5f52d2..c1b57bb2 100644
--- a/deps/quickjs/tests/microbench.js
+++ b/deps/quickjs/tests/microbench.js
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
import * as std from "std";
+import * as os from "os";
function pad(str, n) {
str += "";
@@ -93,21 +94,12 @@ function log_line() {
console.log(s);
}
-var clocks_per_sec = 1000000;
-var max_iterations = 100;
-var clock_threshold = 2000; /* favoring short measuring spans */
+var clocks_per_sec = 1000;
+var max_iterations = 10;
+var clock_threshold = 100; /* favoring short measuring spans */
var min_n_argument = 1;
-var get_clock;
-
-if (typeof globalThis.__date_clock != "function") {
- console.log("using fallback millisecond clock");
- clocks_per_sec = 1000;
- max_iterations = 10;
- clock_threshold = 100;
- get_clock = Date.now;
-} else {
- get_clock = globalThis.__date_clock;
-}
+//var get_clock = Date.now;
+var get_clock = os.now;
function log_one(text, n, ti) {
var ref;
diff --git a/deps/quickjs/tests/test_builtin.js b/deps/quickjs/tests/test_builtin.js
index c5c0f833..e2560664 100644
--- a/deps/quickjs/tests/test_builtin.js
+++ b/deps/quickjs/tests/test_builtin.js
@@ -538,7 +538,17 @@ function test_regexp()
assert(/{1a}/.toString(), "/{1a}/");
a = /a{1+/.exec("a{11");
- assert(a, ["a{11"] );
+ assert(a, ["a{11"]);
+
+ /* test zero length matches */
+ a = /(?:(?=(abc)))a/.exec("abc");
+ assert(a, ["a", "abc"]);
+ a = /(?:(?=(abc)))?a/.exec("abc");
+ assert(a, ["a", undefined]);
+ a = /(?:(?=(abc))){0,2}a/.exec("abc");
+ assert(a, ["a", undefined]);
+ a = /(?:|[\w])+([0-9])/.exec("123a23");
+ assert(a, ["123a23", "3"]);
}
function test_symbol()
@@ -645,6 +655,18 @@ function test_generator()
assert(ret, "ret_val");
return 3;
}
+ function *f3() {
+ var ret;
+ /* test stack consistency with nip_n to handle yield return +
+ * finally clause */
+ try {
+ ret = 2 + (yield 1);
+ } catch(e) {
+ } finally {
+ ret++;
+ }
+ return ret;
+ }
var g, v;
g = f();
v = g.next();
@@ -665,6 +687,12 @@ function test_generator()
assert(v.value === 3 && v.done === true);
v = g.next();
assert(v.value === undefined && v.done === true);
+
+ g = f3();
+ v = g.next();
+ assert(v.value === 1 && v.done === false);
+ v = g.next(3);
+ assert(v.value === 6 && v.done === true);
}
test();
diff --git a/deps/quickjs/tests/test_language.js b/deps/quickjs/tests/test_language.js
index 8d13deb9..65dab5f8 100644
--- a/deps/quickjs/tests/test_language.js
+++ b/deps/quickjs/tests/test_language.js
@@ -120,6 +120,7 @@ function test_cvt()
assert((Infinity >>> 0) === 0);
assert(((-Infinity) >>> 0) === 0);
assert(((4294967296 * 3 - 4) >>> 0) === (4294967296 - 4));
+ assert((19686109595169230000).toString() === "19686109595169230000");
}
function test_eq()
@@ -325,6 +326,15 @@ function test_class()
/* test class name scope */
var E1 = class E { static F() { return E; } };
assert(E1 === E1.F());
+
+ class S {
+ static x = 42;
+ static y = S.x;
+ static z = this.x;
+ }
+ assert(S.x === 42);
+ assert(S.y === 42);
+ assert(S.z === 42);
};
function test_template()
@@ -526,6 +536,53 @@ function test_function_expr_name()
assert_throws(TypeError, f);
}
+function test_parse_semicolon()
+{
+ /* 'yield' or 'await' may not be considered as a token if the
+ previous ';' is missing */
+ function *f()
+ {
+ function func() {
+ }
+ yield 1;
+ var h = x => x + 1
+ yield 2;
+ }
+ async function g()
+ {
+ function func() {
+ }
+ await 1;
+ var h = x => x + 1
+ await 2;
+ }
+}
+
+/* optional chaining tests not present in test262 */
+function test_optional_chaining()
+{
+ var a, z;
+ z = null;
+ a = { b: { c: 2 } };
+ assert(delete z?.b.c, true);
+ assert(delete a?.b.c, true);
+ assert(JSON.stringify(a), '{"b":{}}', "optional chaining delete");
+
+ a = { b: { c: 2 } };
+ assert(delete z?.b["c"], true);
+ assert(delete a?.b["c"], true);
+ assert(JSON.stringify(a), '{"b":{}}');
+
+ a = {
+ b() { return this._b; },
+ _b: { c: 42 }
+ };
+
+ assert((a?.b)().c, 42);
+
+ assert((a?.["b"])().c, 42);
+}
+
test_op1();
test_cvt();
test_eq();
@@ -545,3 +602,5 @@ test_spread();
test_function_length();
test_argument_scope();
test_function_expr_name();
+test_parse_semicolon();
+test_optional_chaining();
diff --git a/deps/quickjs/tests/test_loop.js b/deps/quickjs/tests/test_loop.js
index 5fda9d8e..084d6589 100644
--- a/deps/quickjs/tests/test_loop.js
+++ b/deps/quickjs/tests/test_loop.js
@@ -167,6 +167,29 @@ function test_for_in2()
assert(tab.toString() == "x,y");
}
+function test_for_in_proxy() {
+ let removed_key = "";
+ let target = {}
+ let proxy = new Proxy(target, {
+ ownKeys: function() {
+ return ["a", "b", "c"];
+ },
+ getOwnPropertyDescriptor: function(target, key) {
+ if (removed_key != "" && key == removed_key)
+ return undefined;
+ else
+ return { enumerable: true, configurable: true, value: this[key] };
+ }
+ });
+ let str = "";
+ for(let o in proxy) {
+ str += " " + o;
+ if (o == "a")
+ removed_key = "b";
+ }
+ assert(str == " a c");
+}
+
function test_for_break()
{
var i, c;
@@ -357,6 +380,7 @@ test_switch1();
test_switch2();
test_for_in();
test_for_in2();
+test_for_in_proxy();
test_try_catch1();
test_try_catch2();
diff --git a/deps/quickjs/tests/test_std.js b/deps/quickjs/tests/test_std.js
index 3ea6e345..6fb94c26 100644
--- a/deps/quickjs/tests/test_std.js
+++ b/deps/quickjs/tests/test_std.js
@@ -1,3 +1,4 @@
+#! (shebang test)
import * as std from "std";
import * as os from "os";
@@ -270,6 +271,26 @@ function test_timer()
os.clearTimeout(th[i]);
}
+/* test closure variable handling when freeing asynchronous
+ function */
+function test_async_gc()
+{
+ (async function run () {
+ let obj = {}
+
+ let done = () => {
+ obj
+ std.gc();
+ }
+
+ Promise.resolve().then(done)
+
+ const p = new Promise(() => {})
+
+ await p
+ })();
+}
+
test_printf();
test_file1();
test_file2();
@@ -279,3 +300,5 @@ test_os();
test_os_exec();
test_timer();
test_ext_json();
+test_async_gc();
+
diff --git a/deps/quickjs/tests/test_worker_module.js b/deps/quickjs/tests/test_worker_module.js
index c783e1fe..f19600ac 100644
--- a/deps/quickjs/tests/test_worker_module.js
+++ b/deps/quickjs/tests/test_worker_module.js
@@ -10,6 +10,7 @@ function handle_msg(e) {
switch(ev.type) {
case "abort":
parent.postMessage({ type: "done" });
+ parent.onMessage = null; /* terminate the worker */
break;
case "sab":
/* modify the SharedArrayBuffer */
diff --git a/deps/quickjs/unicode_gen.c b/deps/quickjs/unicode_gen.c
index f18aaa0a..54da2c15 100644
--- a/deps/quickjs/unicode_gen.c
+++ b/deps/quickjs/unicode_gen.c
@@ -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->u_len == 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