This is an automated email from the ASF dual-hosted git repository. vatamane pushed a commit to branch merge-3.4.3 in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit e59afbad3c7fb384b166fa7d8cfffc56c7b016aa Author: Nick Vatamaniuc <[email protected]> AuthorDate: Fri Mar 14 13:38:46 2025 -0400 Update QuickJS Specifically, include my date parsing fix and some other updates (stack overflow check in stringify, unicode fixes): https://github.com/bellard/quickjs/compare/6e2e68fd0896957f92eb6c242a2e048c1ef3cae0...master --- .../patches/01-spidermonkey-185-mode.patch | 6 +- src/couch_quickjs/quickjs/libregexp.c | 36 +- src/couch_quickjs/quickjs/libregexp.h | 5 + src/couch_quickjs/quickjs/libunicode.c | 402 ++++++++++----------- src/couch_quickjs/quickjs/quickjs-libc.c | 8 +- src/couch_quickjs/quickjs/quickjs.c | 103 ++++-- 6 files changed, 311 insertions(+), 249 deletions(-) diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch index 6a5f8f2d7..346978fde 100644 --- a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch +++ b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch @@ -1,6 +1,6 @@ ---- quickjs-master/quickjs.c 2024-07-17 07:58:08 -+++ quickjs/quickjs.c 2024-07-18 11:37:31 -@@ -29451,10 +29451,24 @@ +--- quickjs-master/quickjs.c 2025-03-13 13:13:25 ++++ quickjs/quickjs.c 2025-03-14 13:36:01 +@@ -29456,10 +29456,24 @@ if (s->token.val == TOK_FUNCTION || (token_is_pseudo_keyword(s, JS_ATOM_async) && peek_token(s, TRUE) == TOK_FUNCTION)) { diff --git a/src/couch_quickjs/quickjs/libregexp.c b/src/couch_quickjs/quickjs/libregexp.c index a2d56a7d0..9295fe72f 100644 --- a/src/couch_quickjs/quickjs/libregexp.c +++ b/src/couch_quickjs/quickjs/libregexp.c @@ -54,6 +54,9 @@ typedef enum { #define CAPTURE_COUNT_MAX 255 #define STACK_SIZE_MAX 255 +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define INTERRUPT_COUNTER_INIT 10000 /* unicode code points */ #define CP_LS 0x2028 @@ -1931,6 +1934,7 @@ typedef struct { BOOL multi_line; BOOL ignore_case; BOOL is_unicode; + int interrupt_counter; void *opaque; /* used for stack overflow check */ size_t state_size; @@ -1977,7 +1981,17 @@ static int push_state(REExecContext *s, return 0; } -/* return 1 if match, 0 if not match or -1 if error. */ +static int lre_poll_timeout(REExecContext *s) +{ + if (unlikely(--s->interrupt_counter <= 0)) { + s->interrupt_counter = INTERRUPT_COUNTER_INIT; + if (lre_check_timeout(s->opaque)) + return LRE_RET_TIMEOUT; + } + return 0; +} + +/* return 1 if match, 0 if not match or < 0 if error. */ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, StackInt *stack, int stack_len, const uint8_t *pc, const uint8_t *cptr, @@ -2008,6 +2022,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, ret = 0; recurse: for(;;) { + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; if (s->state_stack_len == 0) return ret; rs = (REExecState *)(s->state_stack + @@ -2097,7 +2113,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, ret = push_state(s, capture, stack, stack_len, pc1, cptr, RE_EXEC_STATE_SPLIT, 0); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; break; } case REOP_lookahead: @@ -2109,12 +2125,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, 0); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; break; case REOP_goto: val = get_u32(pc); pc += 4 + (int)val; + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; break; case REOP_line_start: if (cptr == s->cbuf) @@ -2179,6 +2197,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, pc += 4; if (--stack[stack_len - 1] != 0) { pc += (int)val; + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; } break; case REOP_push_char_pos: @@ -2353,9 +2373,12 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, q = 0; for(;;) { + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; res = lre_exec_backtrack(s, capture, stack, stack_len, pc1, cptr, TRUE); - if (res == -1) + if (res == LRE_RET_MEMORY_ERROR || + res == LRE_RET_TIMEOUT) return res; if (!res) break; @@ -2373,7 +2396,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, RE_EXEC_STATE_GREEDY_QUANT, q - quant_min); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; } } break; @@ -2383,7 +2406,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, } } -/* Return 1 if match, 0 if not match or -1 if error. cindex is the +/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the starting position of the match and must be such as 0 <= cindex <= clen. */ int lre_exec(uint8_t **capture, @@ -2405,6 +2428,7 @@ int lre_exec(uint8_t **capture, s->cbuf_type = cbuf_type; if (s->cbuf_type == 1 && s->is_unicode) s->cbuf_type = 2; + s->interrupt_counter = INTERRUPT_COUNTER_INIT; s->opaque = opaque; s->state_size = sizeof(REExecState) + diff --git a/src/couch_quickjs/quickjs/libregexp.h b/src/couch_quickjs/quickjs/libregexp.h index 7af7ece0f..7475bbea9 100644 --- a/src/couch_quickjs/quickjs/libregexp.h +++ b/src/couch_quickjs/quickjs/libregexp.h @@ -36,6 +36,9 @@ #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 */ +#define LRE_RET_MEMORY_ERROR (-1) +#define LRE_RET_TIMEOUT (-2) + uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, const char *buf, size_t buf_len, int re_flags, void *opaque); @@ -50,6 +53,8 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16); /* must be provided by the user, return non zero if overflow */ int lre_check_stack_overflow(void *opaque, size_t alloca_size); +/* must be provided by the user, return non zero if time out */ +int lre_check_timeout(void *opaque); void *lre_realloc(void *opaque, void *ptr, size_t size); #endif /* LIBREGEXP_H */ diff --git a/src/couch_quickjs/quickjs/libunicode.c b/src/couch_quickjs/quickjs/libunicode.c index c80d2f3d1..d1bf1e91c 100644 --- a/src/couch_quickjs/quickjs/libunicode.c +++ b/src/couch_quickjs/quickjs/libunicode.c @@ -537,6 +537,207 @@ int cr_invert(CharRange *cr) return 0; } +#define CASE_U (1 << 0) +#define CASE_L (1 << 1) +#define CASE_F (1 << 2) + +/* use the case conversion table to generate range of characters. + CASE_U: set char if modified by uppercasing, + CASE_L: set char if modified by lowercasing, + CASE_F: set char if modified by case folding, + */ +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(UF_EXT2) | MR(UF_EXT3), + + 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(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; + + if (case_mask == 0) + return 0; + mask = 0; + for(i = 0; i < 3; i++) { + if ((case_mask >> i) & 1) + mask |= tab_run_mask[i]; + } + for(idx = 0; idx < countof(case_conv_table1); idx++) { + v = case_conv_table1[idx]; + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if ((mask >> type) & 1) { + // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); + switch(type) { + case RUN_TYPE_UL: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + code += ((case_mask & CASE_U) != 0); + for(i = 0; i < len; i += 2) { + if (cr_add_interval(cr, code + i, code + i + 1)) + return -1; + } + break; + case RUN_TYPE_LSU: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + if (!(case_mask & CASE_U)) { + if (cr_add_interval(cr, code, code + 1)) + return -1; + } + if (cr_add_interval(cr, code + 1, code + 2)) + return -1; + if (case_mask & CASE_U) { + if (cr_add_interval(cr, code + 2, code + 3)) + return -1; + } + break; + default: + def_case: + if (cr_add_interval(cr, code, code + len)) + return -1; + break; + } + } + } + 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; +} + #ifdef CONFIG_ALL_UNICODE BOOL lre_is_id_start(uint32_t c) @@ -1296,207 +1497,6 @@ static int unicode_prop1(CharRange *cr, int prop_idx) return 0; } -#define CASE_U (1 << 0) -#define CASE_L (1 << 1) -#define CASE_F (1 << 2) - -/* use the case conversion table to generate range of characters. - CASE_U: set char if modified by uppercasing, - CASE_L: set char if modified by lowercasing, - CASE_F: set char if modified by case folding, - */ -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(UF_EXT2) | MR(UF_EXT3), - - 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(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; - - if (case_mask == 0) - return 0; - mask = 0; - for(i = 0; i < 3; i++) { - if ((case_mask >> i) & 1) - mask |= tab_run_mask[i]; - } - for(idx = 0; idx < countof(case_conv_table1); idx++) { - v = case_conv_table1[idx]; - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if ((mask >> type) & 1) { - // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); - switch(type) { - case RUN_TYPE_UL: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - code += ((case_mask & CASE_U) != 0); - for(i = 0; i < len; i += 2) { - if (cr_add_interval(cr, code + i, code + i + 1)) - return -1; - } - break; - case RUN_TYPE_LSU: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - if (!(case_mask & CASE_U)) { - if (cr_add_interval(cr, code, code + 1)) - return -1; - } - if (cr_add_interval(cr, code + 1, code + 2)) - return -1; - if (case_mask & CASE_U) { - if (cr_add_interval(cr, code + 2, code + 3)) - return -1; - } - break; - default: - def_case: - if (cr_add_interval(cr, code, code + len)) - return -1; - break; - } - } - } - 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/src/couch_quickjs/quickjs/quickjs-libc.c b/src/couch_quickjs/quickjs/quickjs-libc.c index 81371507f..141f79f3c 100644 --- a/src/couch_quickjs/quickjs/quickjs-libc.c +++ b/src/couch_quickjs/quickjs/quickjs-libc.c @@ -78,6 +78,10 @@ typedef sig_t sighandler_t; #include "list.h" #include "quickjs-libc.h" +#if !defined(PATH_MAX) +#define PATH_MAX 4096 +#endif + /* TODO: - add socket calls */ @@ -1090,7 +1094,7 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, int64_t pos; if (!f) return JS_EXCEPTION; -#if defined(__linux__) +#if defined(__linux__) || defined(__GLIBC__) pos = ftello(f); #else pos = ftell(f); @@ -1113,7 +1117,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (JS_ToInt32(ctx, &whence, argv[1])) return JS_EXCEPTION; -#if defined(__linux__) +#if defined(__linux__) || defined(__GLIBC__) ret = fseeko(f, pos, whence); #else ret = fseek(f, pos, whence); diff --git a/src/couch_quickjs/quickjs/quickjs.c b/src/couch_quickjs/quickjs/quickjs.c index 54f66ddde..cd35e64de 100644 --- a/src/couch_quickjs/quickjs/quickjs.c +++ b/src/couch_quickjs/quickjs/quickjs.c @@ -34,7 +34,7 @@ #include <math.h> #if defined(__APPLE__) #include <malloc/malloc.h> -#elif defined(__linux__) +#elif defined(__linux__) || defined(__GLIBC__) #include <malloc.h> #elif defined(__FreeBSD__) #include <malloc_np.h> @@ -363,10 +363,7 @@ typedef struct JSVarRef { struct { int __gc_ref_count; /* corresponds to header.ref_count */ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - uint8_t is_detached : 1; - uint8_t is_arg : 1; - uint16_t var_idx; /* index of the corresponding function variable on - the stack */ + uint8_t is_detached; }; }; JSValue *pvalue; /* pointer to the value, either on the stack or @@ -1708,7 +1705,7 @@ static size_t js_def_malloc_usable_size(const void *ptr) return _msize((void *)ptr); #elif defined(EMSCRIPTEN) return 0; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__GLIBC__) return malloc_usable_size((void *)ptr); #else /* change this to `return 0;` if compilation fails */ @@ -6836,15 +6833,19 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); } +static void JS_ThrowInterrupted(JSContext *ctx) +{ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); +} + static no_inline __exception int __js_poll_interrupts(JSContext *ctx) { JSRuntime *rt = ctx->rt; ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; if (rt->interrupt_handler) { if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { - /* XXX: should set a specific flag to avoid catching */ - JS_ThrowInternalError(ctx, "interrupted"); - JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); + JS_ThrowInterrupted(ctx); return -1; } } @@ -8601,6 +8602,8 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { goto read_only_prop; + } else { + break; } } } @@ -14855,16 +14858,16 @@ static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) /* add the length field (cannot fail) */ pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (unlikely(!pr)) + goto fail; pr->u.value = JS_NewInt32(ctx, argc); /* initialize the fast array part */ tab = NULL; if (argc > 0) { tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } + if (!tab) + goto fail; for(i = 0; i < argc; i++) { tab[i] = JS_DupValue(ctx, argv[i]); } @@ -14880,6 +14883,9 @@ static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) ctx->throw_type_error, ctx->throw_type_error, JS_PROP_HAS_GET | JS_PROP_HAS_SET); return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; } #define GLOBAL_VAR_OFFSET 0x40000000 @@ -14904,6 +14910,8 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, /* add the length field (cannot fail) */ pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (unlikely(!pr)) + goto fail; pr->u.value = JS_NewInt32(ctx, argc); for(i = 0; i < arg_count; i++) { @@ -15662,10 +15670,16 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, { JSVarRef *var_ref; struct list_head *el; + JSValue *pvalue; + + if (is_arg) + pvalue = &sf->arg_buf[var_idx]; + else + pvalue = &sf->var_buf[var_idx]; list_for_each(el, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, var_ref_link); - if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + if (var_ref->pvalue == pvalue) { var_ref->header.ref_count++; return var_ref; } @@ -15677,8 +15691,6 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, 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->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 @@ -15694,10 +15706,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, } 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->pvalue = pvalue; return var_ref; } @@ -15925,37 +15934,33 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) { struct list_head *el, *el1; JSVarRef *var_ref; - int var_idx; list_for_each_safe(el, el1, &sf->var_ref_list) { 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]); - else - var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); + var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue); var_ref->pvalue = &var_ref->value; /* the reference is no longer to a local variable */ var_ref->is_detached = TRUE; } } -static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) { + JSValue *pvalue; struct list_head *el, *el1; JSVarRef *var_ref; - int var_idx = idx; + pvalue = &sf->var_buf[var_idx]; list_for_each_safe(el, el1, &sf->var_ref_list) { var_ref = list_entry(el, JSVarRef, var_ref_link); - if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + if (var_ref->pvalue == pvalue) { 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->value = JS_DupValue(ctx, *var_ref->pvalue); var_ref->pvalue = &var_ref->value; /* the reference is no longer to a local variable */ var_ref->is_detached = TRUE; @@ -17188,7 +17193,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, int idx; idx = get_u16(pc); pc += 2; - close_lexical_var(ctx, sf, idx, FALSE); + close_lexical_var(ctx, sf, idx); } BREAK; @@ -43921,12 +43926,20 @@ fail: return JS_EXCEPTION; } -BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +int lre_check_stack_overflow(void *opaque, size_t alloca_size) { JSContext *ctx = opaque; return js_check_stack_overflow(ctx->rt, alloca_size); } +int lre_check_timeout(void *opaque) +{ + JSContext *ctx = opaque; + JSRuntime *rt = ctx->rt; + return (rt->interrupt_handler && + rt->interrupt_handler(rt, rt->interrupt_opaque)); +} + void *lre_realloc(void *opaque, void *ptr, size_t size) { JSContext *ctx = opaque; @@ -43994,7 +44007,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (rc == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } } else { @@ -44190,7 +44207,11 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueCon goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (ret == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } break; @@ -45399,6 +45420,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, tab = JS_UNDEFINED; prop = JS_UNDEFINED; + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + goto exception; + } + if (JS_IsObject(val)) { p = JS_VALUE_GET_OBJ(val); cl = p->class_id; @@ -50020,6 +50046,9 @@ static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval, p_start = p; while ((c = sp[p]) >= '0' && c <= '9') { + /* arbitrary limit to 9 digits */ + if (v >= 100000000) + return FALSE; v = v * 10 + c - '0'; p++; if (p - p_start == max_digits) @@ -50067,7 +50096,7 @@ static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL stric sgn = sp[p++]; if (sgn == '+' || sgn == '-') { int n = p; - if (!string_get_digits(sp, &p, &hh, 1, 9)) + if (!string_get_digits(sp, &p, &hh, 1, 0)) return FALSE; n = p - n; if (strict && n != 2 && n != 4) @@ -50259,7 +50288,7 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp, *is_local = FALSE; } else { p++; - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (c == '-') { if (val == 0) return FALSE; @@ -50270,7 +50299,7 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp, } } } else - if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_get_digits(sp, &p, &val, 1, 0)) { if (string_skip_char(sp, &p, ':')) { /* time part */ fields[3] = val;
