This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a commit to branch update-quickjs
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 6a854ff3868726bc090a4644f362f761f2f0c408
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Sun Jan 4 23:56:15 2026 -0500

    Update QuickJS: regexp updates + memory leak fixes
    
     * Add regexp duplicate named groups
     
https://github.com/bellard/quickjs/commit/24379bf53c30b35151f345318d850bac49219288
    
     * Fix fast array extension optimization when there are multiple realms
     
https://github.com/bellard/quickjs/commit/e5fd3918c1c4a2ee39016e71b66a9eeda85ce716
    
     * Remove memory leak in case of error in `cpool_add()`
     
https://github.com/bellard/quickjs/commit/fcd33c1afa7b3028531f53cd1190a3877454f6b3
    
     * Remove use after free in `js_create_module_bytecode_function()`
     
https://github.com/bellard/quickjs/commit/1dbba8a88eaa40d15a8a9b70bb1a0b8fb5b552e6
    
     * Don't call well-known Symbol methods for RegExp on primitive values
     
https://github.com/bellard/quickjs/commit/c73a435f365e5250dd529cde00675528d7609edf
    
     * Slightly faster lexical variable assignment
     
https://github.com/bellard/quickjs/commit/31ef02b90785fbc6effb82c300bf05a2a0903088
    
     * `\x{N}` is a syntax error
     
https://github.com/bellard/quickjs/commit/7bd1ae2c76f9053e00e405998a3ea66a995403c4
    
     * Removed `alloca()` is `lre_exec()`
     
https://github.com/bellard/quickjs/commit/f1139494d18a2053630c5ed3384a42bb70db3c53
---
 .../patches/01-spidermonkey-185-mode.patch         |   6 +-
 src/couch_quickjs/patches/02-test262-errors.patch  |   4 +-
 src/couch_quickjs/quickjs/Changelog                |   1 +
 src/couch_quickjs/quickjs/libregexp-opcode.h       |   4 +-
 src/couch_quickjs/quickjs/libregexp.c              | 437 +++++++++++++--------
 src/couch_quickjs/quickjs/libregexp.h              |   4 +
 src/couch_quickjs/quickjs/libunicode.h             |   5 +
 src/couch_quickjs/quickjs/quickjs-opcode.h         |   1 +
 src/couch_quickjs/quickjs/quickjs.c                | 184 +++++----
 src/couch_quickjs/quickjs/test262.conf             |  50 +--
 src/couch_quickjs/quickjs/test262_errors.txt       |   6 -
 11 files changed, 418 insertions(+), 284 deletions(-)

diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch 
b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
index 2f642114e..db740bf2b 100644
--- a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
+++ b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
@@ -1,6 +1,6 @@
---- quickjs-master/quickjs.c   2025-11-29 09:14:41.000000000 -0500
-+++ quickjs/quickjs.c  2025-11-29 20:38:38.829402534 -0500
-@@ -31420,10 +31420,24 @@
+--- quickjs-master/quickjs.c   2025-12-22 09:12:46
++++ quickjs/quickjs.c  2026-01-04 23:55:00
+@@ -31443,10 +31443,24 @@
      if (s->token.val == TOK_FUNCTION ||
          (token_is_pseudo_keyword(s, JS_ATOM_async) &&
           peek_token(s, TRUE) == TOK_FUNCTION)) {
diff --git a/src/couch_quickjs/patches/02-test262-errors.patch 
b/src/couch_quickjs/patches/02-test262-errors.patch
index 692e080ac..81646c900 100644
--- a/src/couch_quickjs/patches/02-test262-errors.patch
+++ b/src/couch_quickjs/patches/02-test262-errors.patch
@@ -1,5 +1,5 @@
---- quickjs-master/test262_errors.txt  2025-11-29 09:14:41.000000000 -0500
-+++ quickjs/test262_errors.txt 2025-11-29 20:38:38.835402578 -0500
+--- quickjs-master/test262_errors.txt  2025-12-22 09:12:46
++++ quickjs/test262_errors.txt 2026-01-04 23:55:00
 @@ -23,6 +23,8 @@
  
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from-and-import-star-as-and-export.js:74:
 SyntaxError: export 'foo' in module 
'test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-import-star-as-and-export.js'
 is ambiguous
  
test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js:75:
 SyntaxError: export 'foo' in module 
'test262/test/language/module-code/ambiguous-export-bindings/namespace-unambiguous-if-export-star-as-from.js'
 is ambiguous
diff --git a/src/couch_quickjs/quickjs/Changelog 
b/src/couch_quickjs/quickjs/Changelog
index 070b0a77a..3c08f0c58 100644
--- a/src/couch_quickjs/quickjs/Changelog
+++ b/src/couch_quickjs/quickjs/Changelog
@@ -6,6 +6,7 @@
 - added Atomics.pause
 - added added Map and WeakMap upsert methods
 - added Math.sumPrecise()
+- added regexp duplicate named groups
 - misc bug fixes
 
 2025-09-13:
diff --git a/src/couch_quickjs/quickjs/libregexp-opcode.h 
b/src/couch_quickjs/quickjs/libregexp-opcode.h
index 6b97b1273..b3d7b6fdf 100644
--- a/src/couch_quickjs/quickjs/libregexp-opcode.h
+++ b/src/couch_quickjs/quickjs/libregexp-opcode.h
@@ -31,6 +31,8 @@ DEF(char32, 5)
 DEF(char32_i, 5)
 DEF(dot, 1)
 DEF(any, 1) /* same as dot but match any character including line terminator */
+DEF(space, 1)
+DEF(not_space, 1) /* must come after */
 DEF(line_start, 1)
 DEF(line_start_m, 1)
 DEF(line_end, 1)
@@ -54,7 +56,7 @@ DEF(word_boundary, 1)
 DEF(word_boundary_i, 1)
 DEF(not_word_boundary, 1)
 DEF(not_word_boundary_i, 1)
-DEF(back_reference, 2)
+DEF(back_reference, 2) /* variable length */
 DEF(back_reference_i, 2) /* must come after */
 DEF(backward_back_reference, 2) /* must come after */
 DEF(backward_back_reference_i, 2) /* must come after */
diff --git a/src/couch_quickjs/quickjs/libregexp.c 
b/src/couch_quickjs/quickjs/libregexp.c
index 0c989b969..c387f0043 100644
--- a/src/couch_quickjs/quickjs/libregexp.c
+++ b/src/couch_quickjs/quickjs/libregexp.c
@@ -34,7 +34,9 @@
 
 /*
   TODO:
-
+  - remove REOP_char_i and REOP_range_i by precomputing the case folding.
+  - add specific opcodes for simple unicode property tests so that the
+    generated bytecode is smaller.
   - 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
@@ -77,6 +79,7 @@ typedef struct {
     BOOL ignore_case;
     BOOL multi_line;
     BOOL dotall;
+    uint8_t group_name_scope;
     int capture_count;
     int total_capture_count; /* -1 = not computed yet */
     int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */
@@ -478,7 +481,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t 
*buf,
             if (i != 1)
                 printf(",");
             printf("<%s>", p);
-            p += strlen(p) + 1;
+            p += strlen(p) + LRE_GROUP_NAME_TRAILER_LEN;
         }
         printf("\n");
         assert(p == (char *)(buf + buf_len));
@@ -547,11 +550,22 @@ static __maybe_unused void lre_dump_bytecode(const 
uint8_t *buf,
             break;
         case REOP_save_start:
         case REOP_save_end:
+            printf(" %u", buf[pos + 1]);
+            break;
         case REOP_back_reference:
         case REOP_back_reference_i:
         case REOP_backward_back_reference:
         case REOP_backward_back_reference_i:
-            printf(" %u", buf[pos + 1]);
+            {
+                int n, i;
+                n = buf[pos + 1];
+                len += n;
+                for(i = 0; i < n; i++) {
+                    if (i != 0)
+                        printf(",");
+                    printf(" %u", buf[pos + 2 + i]);
+                }
+            }
             break;
         case REOP_save_reset:
             printf(" %u %u", buf[pos + 1], buf[pos + 2]);
@@ -745,9 +759,21 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
         c = '\v';
         break;
     case 'x':
+        {
+            int h0, h1;
+
+            h0 = from_hex(*p++);
+            if (h0 < 0)
+                return -1;
+            h1 = from_hex(*p++);
+            if (h1 < 0)
+                return -1;
+            c = (h0 << 4) | h1;
+        }
+        break;
     case 'u':
         {
-            int h, n, i;
+            int h, i;
             uint32_t c1;
 
             if (*p == '{' && allow_utf16) {
@@ -765,14 +791,8 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
                 }
                 p++;
             } else {
-                if (c == 'x') {
-                    n = 2;
-                } else {
-                    n = 4;
-                }
-
                 c = 0;
-                for(i = 0; i < n; i++) {
+                for(i = 0; i < 4; i++) {
                     h = from_hex(*p++);
                     if (h < 0) {
                         return -1;
@@ -1060,7 +1080,7 @@ static int get_class_atom(REParseState *s, REStringList 
*cr,
                 goto default_escape;
             if (cr_init_char_range(s, cr, c))
                 return -1;
-            c = CLASS_RANGE_BASE;
+            c += CLASS_RANGE_BASE;
             break;
         case 'c':
             c = *p;
@@ -1531,17 +1551,18 @@ static int re_parse_char_class(REParseState *s, const 
uint8_t **pp)
     return -1;
 }
 
-/* Return:
-   - true if the opcodes may not advance the char pointer
-   - false if the opcodes always advance the char pointer
+/* need_check_adv: false if the opcodes always advance the char pointer
+   need_capture_init: true if all the captures in the atom are not set
 */
-static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
+static BOOL re_need_check_adv_and_capture_init(BOOL *pneed_capture_init,
+                                               const uint8_t *bc_buf, int 
bc_buf_len)
 {
     int pos, opcode, len;
     uint32_t val;
-    BOOL ret;
+    BOOL need_check_adv, need_capture_init;
 
-    ret = TRUE;
+    need_check_adv = TRUE;
+    need_capture_init = FALSE;
     pos = 0;
     while (pos < bc_buf_len) {
         opcode = bc_buf[pos];
@@ -1551,20 +1572,23 @@ static BOOL re_need_check_advance(const uint8_t 
*bc_buf, int bc_buf_len)
         case REOP_range_i:
             val = get_u16(bc_buf + pos + 1);
             len += val * 4;
-            goto simple_char;
+            need_check_adv = FALSE;
+            break;
         case REOP_range32:
         case REOP_range32_i:
             val = get_u16(bc_buf + pos + 1);
             len += val * 8;
-            goto simple_char;
+            need_check_adv = FALSE;
+            break;
         case REOP_char:
         case REOP_char_i:
         case REOP_char32:
         case REOP_char32_i:
         case REOP_dot:
         case REOP_any:
-        simple_char:
-            ret = FALSE;
+        case REOP_space:
+        case REOP_not_space:
+            need_check_adv = FALSE;
             break;
         case REOP_line_start:
         case REOP_line_start_m:
@@ -1582,18 +1606,25 @@ static BOOL re_need_check_advance(const uint8_t 
*bc_buf, int bc_buf_len)
         case REOP_save_start:
         case REOP_save_end:
         case REOP_save_reset:
+            break;
         case REOP_back_reference:
         case REOP_back_reference_i:
         case REOP_backward_back_reference:
         case REOP_backward_back_reference_i:
+            val = bc_buf[pos + 1];
+            len += val;
+            need_capture_init = TRUE;
             break;
         default:
             /* safe behavior: we cannot predict the outcome */
-            return TRUE;
+            need_capture_init = TRUE;
+            goto done;
         }
         pos += len;
     }
-    return ret;
+ done:
+    *pneed_capture_init = need_capture_init;
+    return need_check_adv;
 }
 
 /* '*pp' is the first char after '<' */
@@ -1652,16 +1683,16 @@ static int re_parse_group_name(char *buf, int buf_size, 
const uint8_t **pp)
 }
 
 /* if capture_name = NULL: return the number of captures + 1.
-   Otherwise, return the capture index corresponding to capture_name
-   or -1 if none */
+   Otherwise, return the number of matching capture groups  */
 static int re_parse_captures(REParseState *s, int *phas_named_captures,
-                             const char *capture_name)
+                             const char *capture_name, BOOL emit_group_index)
 {
     const uint8_t *p;
-    int capture_index;
+    int capture_index, n;
     char name[TMP_BUF_SIZE];
 
     capture_index = 1;
+    n = 0;
     *phas_named_captures = 0;
     for (p = s->buf_start; p < s->buf_end; p++) {
         switch (*p) {
@@ -1673,8 +1704,11 @@ static int re_parse_captures(REParseState *s, int 
*phas_named_captures,
                     if (capture_name) {
                         p += 3;
                         if (re_parse_group_name(name, sizeof(name), &p) == 0) {
-                            if (!strcmp(name, capture_name))
-                                return capture_index;
+                            if (!strcmp(name, capture_name)) {
+                                if (emit_group_index)
+                                    dbuf_putc(&s->byte_code, capture_index);
+                                n++;
+                            }
                         }
                     }
                     capture_index++;
@@ -1699,17 +1733,18 @@ static int re_parse_captures(REParseState *s, int 
*phas_named_captures,
         }
     }
  done:
-    if (capture_name)
-        return -1;
-    else
+    if (capture_name) {
+        return n;
+    } else {
         return capture_index;
+    }
 }
 
 static int re_count_captures(REParseState *s)
 {
     if (s->total_capture_count < 0) {
         s->total_capture_count = re_parse_captures(s, &s->has_named_captures,
-                                                   NULL);
+                                                   NULL, FALSE);
     }
     return s->total_capture_count;
 }
@@ -1721,25 +1756,53 @@ static BOOL re_has_named_captures(REParseState *s)
     return s->has_named_captures;
 }
 
-static int find_group_name(REParseState *s, const char *name)
+static int find_group_name(REParseState *s, const char *name, BOOL 
emit_group_index)
 {
     const char *p, *buf_end;
     size_t len, name_len;
-    int capture_index;
+    int capture_index, n;
 
     p = (char *)s->group_names.buf;
-    if (!p) return -1;
+    if (!p)
+        return 0;
     buf_end = (char *)s->group_names.buf + s->group_names.size;
     name_len = strlen(name);
     capture_index = 1;
+    n = 0;
     while (p < buf_end) {
         len = strlen(p);
-        if (len == name_len && memcmp(name, p, name_len) == 0)
-            return capture_index;
-        p += len + 1;
+        if (len == name_len && memcmp(name, p, name_len) == 0) {
+            if (emit_group_index)
+                dbuf_putc(&s->byte_code, capture_index);
+            n++;
+        }
+        p += len + LRE_GROUP_NAME_TRAILER_LEN;
         capture_index++;
     }
-    return -1;
+    return n;
+}
+
+static BOOL is_duplicate_group_name(REParseState *s, const char *name, int 
scope)
+{
+    const char *p, *buf_end;
+    size_t len, name_len;
+    int scope1;
+    
+    p = (char *)s->group_names.buf;
+    if (!p)
+        return 0;
+    buf_end = (char *)s->group_names.buf + s->group_names.size;
+    name_len = strlen(name);
+    while (p < buf_end) {
+        len = strlen(p);
+        if (len == name_len && memcmp(name, p, name_len) == 0) {
+            scope1 = (uint8_t)p[len + 1];
+            if (scope == scope1)
+                return TRUE;
+        }
+        p += len + LRE_GROUP_NAME_TRAILER_LEN;
+    }
+    return FALSE;
 }
 
 static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir);
@@ -1783,7 +1846,7 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
 {
     const uint8_t *p;
     int c, last_atom_start, quant_min, quant_max, last_capture_count;
-    BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead;
+    BOOL greedy, is_neg, is_backward_lookahead;
     REStringList cr_s, *cr = &cr_s;
 
     last_atom_start = -1;
@@ -1922,12 +1985,16 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
                                         &p)) {
                     return re_parse_error(s, "invalid group name");
                 }
-                if (find_group_name(s, s->u.tmp_buf) > 0) {
+                /* poor's man method to test duplicate group
+                   names. */
+                /* XXX: this method does not catch all the errors*/
+                if (is_duplicate_group_name(s, s->u.tmp_buf, 
s->group_name_scope)) {
                     return re_parse_error(s, "duplicate group name");
                 }
                 /* group name with a trailing zero */
                 dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf,
                          strlen(s->u.tmp_buf) + 1);
+                dbuf_putc(&s->group_names, s->group_name_scope);
                 s->has_named_captures = 1;
                 goto parse_capture;
             } else {
@@ -1938,6 +2005,7 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
             p++;
             /* capture without group name */
             dbuf_putc(&s->group_names, 0);
+            dbuf_putc(&s->group_names, 0);
         parse_capture:
             if (s->capture_count >= CAPTURE_COUNT_MAX)
                 return re_parse_error(s, "too many captures");
@@ -1964,17 +2032,18 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
         case 'b':
         case 'B':
             if (p[1] != 'b') {
-                re_emit_op(s, s->ignore_case ? REOP_not_word_boundary_i : 
REOP_not_word_boundary);
+                re_emit_op(s, s->ignore_case && s->is_unicode ? 
REOP_not_word_boundary_i : REOP_not_word_boundary);
             } else {
-                re_emit_op(s, s->ignore_case ? REOP_word_boundary_i : 
REOP_word_boundary);
+                re_emit_op(s, s->ignore_case && s->is_unicode ? 
REOP_word_boundary_i : REOP_word_boundary);
             }
             p += 2;
             break;
         case 'k':
             {
                 const uint8_t *p1;
-                int dummy_res;
-
+                int dummy_res, n;
+                BOOL is_forward;
+                
                 p1 = p;
                 if (p1[2] != '<') {
                     /* annex B: we tolerate invalid group names in non
@@ -1993,21 +2062,33 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
                     else
                         goto parse_class_atom;
                 }
-                c = find_group_name(s, s->u.tmp_buf);
-                if (c < 0) {
+                is_forward = FALSE;
+                n = find_group_name(s, s->u.tmp_buf, FALSE);
+                if (n == 0) {
                     /* no capture name parsed before, try to look
                        after (inefficient, but hopefully not common */
-                    c = re_parse_captures(s, &dummy_res, s->u.tmp_buf);
-                    if (c < 0) {
+                    n = re_parse_captures(s, &dummy_res, s->u.tmp_buf, FALSE);
+                    if (n == 0) {
                         if (s->is_unicode || re_has_named_captures(s))
                             return re_parse_error(s, "group name not defined");
                         else
                             goto parse_class_atom;
                     }
+                    is_forward = TRUE;
+                }
+                last_atom_start = s->byte_code.size;
+                last_capture_count = s->capture_count;
+                
+                /* emit back references to all the captures indexes matching 
the group name */
+                re_emit_op_u8(s, REOP_back_reference + 2 * is_backward_dir + 
s->ignore_case, n);
+                if (is_forward) {
+                    re_parse_captures(s, &dummy_res, s->u.tmp_buf, TRUE);
+                } else {
+                    find_group_name(s, s->u.tmp_buf, TRUE);
                 }
                 p = p1;
             }
-            goto emit_back_reference;
+            break;
         case '0':
             p += 2;
             c = 0;
@@ -2053,11 +2134,11 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
                     }
                     return re_parse_error(s, "back reference out of range in 
regular expression");
                 }
-            emit_back_reference:
                 last_atom_start = s->byte_code.size;
                 last_capture_count = s->capture_count;
                 
-                re_emit_op_u8(s, REOP_back_reference + 2 * is_backward_dir + 
s->ignore_case, c);
+                re_emit_op_u8(s, REOP_back_reference + 2 * is_backward_dir + 
s->ignore_case, 1);
+                dbuf_putc(&s->byte_code, c);
             }
             break;
         default:
@@ -2090,8 +2171,15 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
         if (is_backward_dir)
             re_emit_op(s, REOP_prev);
         if (c >= CLASS_RANGE_BASE) {
-            int ret;
-            ret = re_emit_string_list(s, cr);
+            int ret = 0;
+            /* optimize the common 'space' tests */
+            if (c == (CLASS_RANGE_BASE + CHAR_RANGE_s)) {
+                re_emit_op(s, REOP_space);
+            } else if (c == (CLASS_RANGE_BASE + CHAR_RANGE_S)) {
+                re_emit_op(s, REOP_not_space);
+            } else {
+                ret = re_emit_string_list(s, cr);
+            }
             re_string_list_free(cr);
             if (ret)
                 return -1;
@@ -2166,20 +2254,39 @@ static int re_parse_term(REParseState *s, BOOL 
is_backward_dir)
             if (last_atom_start < 0) {
                 return re_parse_error(s, "nothing to repeat");
             }
-            /* 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);
-
             {
+                BOOL need_capture_init, add_zero_advance_check;
                 int len, pos;
+                
+                /* 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_adv_and_capture_init(&need_capture_init,
+                                                       s->byte_code.buf + 
last_atom_start,
+                                                       s->byte_code.size - 
last_atom_start);
+            
+                /* general case: need to reset the capture at each
+                   iteration. We don't do it if there are no captures
+                   in the atom or if we are sure all captures are
+                   initialized in the atom. If quant_min = 0, we still
+                   need to reset once the captures in case the atom
+                   does not match. */
+                if (need_capture_init && last_capture_count != 
s->capture_count) {
+                    if (dbuf_insert(&s->byte_code, last_atom_start, 3))
+                        goto out_of_memory;
+                    int pos = last_atom_start;
+                    s->byte_code.buf[pos++] = REOP_save_reset;
+                    s->byte_code.buf[pos++] = last_capture_count;
+                    s->byte_code.buf[pos++] = s->capture_count - 1;
+                }
+
                 len = s->byte_code.size - last_atom_start;
                 if (quant_min == 0) {
                     /* need to reset the capture in case the atom is
                        not executed */
-                    if (last_capture_count != s->capture_count) {
+                    if (!need_capture_init && last_capture_count != 
s->capture_count) {
                         if (dbuf_insert(&s->byte_code, last_atom_start, 3))
                             goto out_of_memory;
                         s->byte_code.buf[last_atom_start++] = REOP_save_reset;
@@ -2320,6 +2427,8 @@ static int re_parse_disjunction(REParseState *s, BOOL 
is_backward_dir)
 
         pos = re_emit_op_u32(s, REOP_goto, 0);
 
+        s->group_name_scope++;
+        
         if (re_parse_alternative(s, is_backward_dir))
             return -1;
 
@@ -2382,6 +2491,13 @@ static int compute_register_count(uint8_t *bc_buf, int 
bc_buf_len)
             val = get_u16(bc_buf + pos + 1);
             len += val * 8;
             break;
+        case REOP_back_reference:
+        case REOP_back_reference_i:
+        case REOP_backward_back_reference:
+        case REOP_backward_back_reference_i:
+            val = bc_buf[pos + 1];
+            len += val;
+            break;
         }
         pos += len;
     }
@@ -2481,7 +2597,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int 
error_msg_size,
             s->byte_code.size - RE_HEADER_LEN);
 
     /* add the named groups if needed */
-    if (s->group_names.size > (s->capture_count - 1)) {
+    if (s->group_names.size > (s->capture_count - 1) * 
LRE_GROUP_NAME_TRAILER_LEN) {
         dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size);
         put_u16(s->byte_code.buf + RE_HEADER_FLAGS,
                 lre_get_flags(s->byte_code.buf) | LRE_FLAG_NAMED_GROUPS);
@@ -2502,14 +2618,6 @@ static BOOL is_line_terminator(uint32_t c)
     return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS);
 }
 
-static BOOL is_word_char(uint32_t c)
-{
-    return ((c >= '0' && c <= '9') ||
-            (c >= 'a' && c <= 'z') ||
-            (c >= 'A' && c <= 'Z') ||
-            (c == '_'));
-}
-
 #define GET_CHAR(c, cptr, cbuf_end, cbuf_type)                          \
     do {                                                                \
         if (cbuf_type == 0) {                                           \
@@ -2664,7 +2772,7 @@ static no_inline int stack_realloc(REExecContext *s, 
size_t n)
 
 /* return 1 if match, 0 if not match or < 0 if error. */
 static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
-                                   uint8_t **regs, const uint8_t *pc, const 
uint8_t *cptr)
+                                   const uint8_t *pc, const uint8_t *cptr)
 {
     int opcode;
     int cbuf_type;
@@ -2704,24 +2812,24 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
     }
 
     /* avoid saving the previous value if already saved */
-#define SAVE_REG(idx, value)                    \
+#define SAVE_CAPTURE_CHECK(idx, value)          \
     {                                           \
         StackElem *sp1;                         \
         sp1 = sp;                               \
         for(;;) {                               \
             if (sp1 > bp) {                             \
-                if (sp1[-2].val == -(int)(idx + 1))     \
+                if (sp1[-2].val == idx)                 \
                     break;                              \
                 sp1 -= 2;                               \
             } else {                                    \
                 CHECK_STACK_SPACE(2);                   \
-                sp[0].val = -(int)(idx + 1);            \
-                sp[1].ptr = regs[idx];                  \
+                sp[0].val = idx;                        \
+                sp[1].ptr = capture[idx];               \
                 sp += 2;                                \
                 break;                                  \
             }                                           \
         }                                               \
-        regs[idx] = (value);                            \
+        capture[idx] = (value);                         \
     }
 
 
@@ -2746,13 +2854,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
                 REExecStateEnum type;
                 if (bp == s->stack_buf)
                     return 0;
-                /* undo the modifications to capture[] and regs[] */
+                /* undo the modifications to capture[] */
                 while (sp > bp) {
-                    intptr_t idx2 = sp[-2].val;
-                    if (idx2 >= 0)
-                        capture[idx2] = sp[-1].ptr;
-                    else
-                        regs[-idx2 - 1] = sp[-1].ptr;
+                    capture[sp[-2].val] = sp[-1].ptr;
                     sp -= 2;
                 }
                 
@@ -2805,13 +2909,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
             for(;;) {
                 REExecStateEnum type;
                 type = bp[-1].bp.type;
-                /* undo the modifications to capture[] and regs[] */
+                /* undo the modifications to capture[] */
                 while (sp > bp) {
-                    intptr_t idx2 = sp[-2].val;
-                    if (idx2 >= 0)
-                        capture[idx2] = sp[-1].ptr;
-                    else
-                        regs[-idx2 - 1] = sp[-1].ptr;
+                    capture[sp[-2].val] = sp[-1].ptr;
                     sp -= 2;
                 }
                 pc = sp[-3].ptr;
@@ -2914,6 +3014,20 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
                 goto no_match;
             GET_CHAR(c, cptr, cbuf_end, cbuf_type);
             break;
+        case REOP_space:
+            if (cptr == cbuf_end)
+                goto no_match;
+            GET_CHAR(c, cptr, cbuf_end, cbuf_type);
+            if (!lre_is_space(c))
+                goto no_match;
+            break;
+        case REOP_not_space:
+            if (cptr == cbuf_end)
+                goto no_match;
+            GET_CHAR(c, cptr, cbuf_end, cbuf_type);
+            if (lre_is_space(c))
+                goto no_match;
+            break;
         case REOP_save_start:
         case REOP_save_end:
             val = *pc++;
@@ -2939,20 +3053,20 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
             }
             break;
         case REOP_set_i32:
-            idx = pc[0];
+            idx = 2 * s->capture_count + pc[0];
             val = get_u32(pc + 1);
             pc += 5;
-            SAVE_REG(idx, (void *)(uintptr_t)val);
+            SAVE_CAPTURE_CHECK(idx, (void *)(uintptr_t)val);
             break;
         case REOP_loop:
             {
                 uint32_t val2;
-                idx = pc[0];
+                idx = 2 * s->capture_count + pc[0];
                 val = get_u32(pc + 1);
                 pc += 5;
 
-                val2 = (uintptr_t)regs[idx] - 1;
-                SAVE_REG(idx, (void *)(uintptr_t)val2);
+                val2 = (uintptr_t)capture[idx] - 1;
+                SAVE_CAPTURE_CHECK(idx, (void *)(uintptr_t)val2);
                 if (val2 != 0) {
                     pc += (int)val;
                     if (lre_poll_timeout(s))
@@ -2967,14 +3081,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
             {
                 const uint8_t *pc1;
                 uint32_t val2, limit;
-                idx = pc[0];
+                idx = 2 * s->capture_count + pc[0];
                 limit = get_u32(pc + 1);
                 val = get_u32(pc + 5);
                 pc += 9;
 
                 /* decrement the counter */
-                val2 = (uintptr_t)regs[idx] - 1;
-                SAVE_REG(idx, (void *)(uintptr_t)val2);
+                val2 = (uintptr_t)capture[idx] - 1;
+                SAVE_CAPTURE_CHECK(idx, (void *)(uintptr_t)val2);
 
                 if (val2 > limit) {
                     /* normal loop if counter > limit */
@@ -2985,7 +3099,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
                     /* check advance */
                     if ((opcode == REOP_loop_check_adv_split_goto_first ||
                          opcode == REOP_loop_check_adv_split_next_first) &&
-                        regs[idx + 1] == cptr &&
+                        capture[idx + 1] == cptr &&
                         val2 != limit) {
                         goto no_match;
                     }
@@ -3011,14 +3125,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
             }
             break;
         case REOP_set_char_pos:
-            idx = pc[0];
+            idx = 2 * s->capture_count + pc[0];
             pc++;
-            SAVE_REG(idx, (uint8_t *)cptr);
+            SAVE_CAPTURE_CHECK(idx, (uint8_t *)cptr);
             break;
         case REOP_check_advance:
-            idx = pc[0];
+            idx = 2 * s->capture_count + pc[0];
             pc++;
-            if (regs[idx] == cptr)
+            if (capture[idx] == cptr)
                 goto no_match;
             break;
         case REOP_word_boundary:
@@ -3034,18 +3148,22 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
                     v1 = FALSE;
                 } else {
                     PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
-                    if (ignore_case)
-                        c = lre_canonicalize(c, s->is_unicode);
-                    v1 = is_word_char(c);
+                    if (c < 256) {
+                        v1 = (lre_is_word_byte(c) != 0);
+                    } else {
+                        v1 = ignore_case && (c == 0x017f || c == 0x212a);
+                    }
                 }
                 /* current char */
                 if (cptr >= cbuf_end) {
                     v2 = FALSE;
                 } else {
                     PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
-                    if (ignore_case)
-                        c = lre_canonicalize(c, s->is_unicode);
-                    v2 = is_word_char(c);
+                    if (c < 256) {
+                        v2 = (lre_is_word_byte(c) != 0);
+                    } else {
+                        v2 = ignore_case && (c == 0x017f || c == 0x212a);
+                    }
                 }
                 if (v1 ^ v2 ^ is_boundary)
                     goto no_match;
@@ -3057,43 +3175,53 @@ static intptr_t lre_exec_backtrack(REExecContext *s, 
uint8_t **capture,
         case REOP_backward_back_reference_i:
             {
                 const uint8_t *cptr1, *cptr1_end, *cptr1_start;
+                const uint8_t *pc1;
                 uint32_t c1, c2;
+                int i, n;
 
-                val = *pc++;
-                if (val >= s->capture_count)
-                    goto no_match;
-                cptr1_start = capture[2 * val];
-                cptr1_end = capture[2 * val + 1];
-                if (!cptr1_start || !cptr1_end)
-                    break;
-                if (opcode == REOP_back_reference ||
-                    opcode == REOP_back_reference_i) {
-                    cptr1 = cptr1_start;
-                    while (cptr1 < cptr1_end) {
-                        if (cptr >= cbuf_end)
-                            goto no_match;
-                        GET_CHAR(c1, cptr1, cptr1_end, cbuf_type);
-                        GET_CHAR(c2, cptr, cbuf_end, cbuf_type);
-                        if (opcode == REOP_back_reference_i) {
-                            c1 = lre_canonicalize(c1, s->is_unicode);
-                            c2 = lre_canonicalize(c2, s->is_unicode);
-                        }
-                        if (c1 != c2)
-                            goto no_match;
-                    }
-                } else {
-                    cptr1 = cptr1_end;
-                    while (cptr1 > cptr1_start) {
-                        if (cptr == s->cbuf)
-                            goto no_match;
-                        GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type);
-                        GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type);
-                        if (opcode == REOP_backward_back_reference_i) {
-                            c1 = lre_canonicalize(c1, s->is_unicode);
-                            c2 = lre_canonicalize(c2, s->is_unicode);
+                n = *pc++;
+                pc1 = pc;
+                pc += n;
+
+                for(i = 0; i < n; i++) {
+                    val = pc1[i];
+                    if (val >= s->capture_count)
+                        goto no_match;
+                    cptr1_start = capture[2 * val];
+                    cptr1_end = capture[2 * val + 1];
+                    /* test the first not empty capture */
+                    if (cptr1_start && cptr1_end) {
+                        if (opcode == REOP_back_reference ||
+                            opcode == REOP_back_reference_i) {
+                            cptr1 = cptr1_start;
+                            while (cptr1 < cptr1_end) {
+                                if (cptr >= cbuf_end)
+                                    goto no_match;
+                                GET_CHAR(c1, cptr1, cptr1_end, cbuf_type);
+                                GET_CHAR(c2, cptr, cbuf_end, cbuf_type);
+                                if (opcode == REOP_back_reference_i) {
+                                    c1 = lre_canonicalize(c1, s->is_unicode);
+                                    c2 = lre_canonicalize(c2, s->is_unicode);
+                                }
+                                if (c1 != c2)
+                                    goto no_match;
+                            }
+                        } else {
+                            cptr1 = cptr1_end;
+                            while (cptr1 > cptr1_start) {
+                                if (cptr == s->cbuf)
+                                    goto no_match;
+                                GET_PREV_CHAR(c1, cptr1, cptr1_start, 
cbuf_type);
+                                GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type);
+                                if (opcode == REOP_backward_back_reference_i) {
+                                    c1 = lre_canonicalize(c1, s->is_unicode);
+                                    c2 = lre_canonicalize(c2, s->is_unicode);
+                                }
+                                if (c1 != c2)
+                                    goto no_match;
+                            }
                         }
-                        if (c1 != c2)
-                            goto no_match;
+                        break;
                     }
                 }
             }
@@ -3200,8 +3328,7 @@ int lre_exec(uint8_t **capture,
              int cbuf_type, void *opaque)
 {
     REExecContext s_s, *s = &s_s;
-    int re_flags, i, ret, register_count;
-    uint8_t **regs;
+    int re_flags, i, ret;
     const uint8_t *cptr;
 
     re_flags = lre_get_flags(bc_buf);
@@ -3220,10 +3347,6 @@ int lre_exec(uint8_t **capture,
 
     for(i = 0; i < s->capture_count * 2; i++)
         capture[i] = NULL;
-    /* XXX: modify the API so that the registers are allocated after
-       the captures to suppress some tests */
-    register_count = bc_buf[RE_HEADER_REGISTER_COUNT];
-    regs = alloca(register_count * sizeof(regs[0]));
 
     cptr = cbuf + (cindex << cbuf_type);
     if (0 < cindex && cindex < clen && s->cbuf_type == 2) {
@@ -3233,13 +3356,19 @@ int lre_exec(uint8_t **capture,
         }
     }
 
-    ret = lre_exec_backtrack(s, capture, regs, bc_buf + RE_HEADER_LEN,
-                             cptr);
+    ret = lre_exec_backtrack(s, capture, bc_buf + RE_HEADER_LEN, cptr);
+
     if (s->stack_buf != s->static_stack_buf)
         lre_realloc(s->opaque, s->stack_buf, 0);
     return ret;
 }
 
+int lre_get_alloc_count(const uint8_t *bc_buf)
+{
+    return bc_buf[RE_HEADER_CAPTURE_COUNT] * 2 +
+        bc_buf[RE_HEADER_REGISTER_COUNT];
+}
+
 int lre_get_capture_count(const uint8_t *bc_buf)
 {
     return bc_buf[RE_HEADER_CAPTURE_COUNT];
@@ -3278,7 +3407,7 @@ int main(int argc, char **argv)
     int len, flags, ret, i;
     uint8_t *bc;
     char error_msg[64];
-    uint8_t *capture[CAPTURE_COUNT_MAX * 2];
+    uint8_t *capture;
     const char *input;
     int input_len, capture_count;
 
@@ -3297,6 +3426,7 @@ int main(int argc, char **argv)
     input = argv[3];
     input_len = strlen(input);
 
+    capture = malloc(sizeof(capture[0]) * lre_get_alloc_count(bc));
     ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);
     printf("ret=%d\n", ret);
     if (ret == 1) {
@@ -3312,6 +3442,7 @@ int main(int argc, char **argv)
             printf("\n");
         }
     }
+    free(capture);
     return 0;
 }
 #endif
diff --git a/src/couch_quickjs/quickjs/libregexp.h 
b/src/couch_quickjs/quickjs/libregexp.h
index da76e4cef..0905bcb79 100644
--- a/src/couch_quickjs/quickjs/libregexp.h
+++ b/src/couch_quickjs/quickjs/libregexp.h
@@ -40,9 +40,13 @@
 #define LRE_RET_MEMORY_ERROR (-1)
 #define LRE_RET_TIMEOUT      (-2)
 
+/* trailer length after the group name including the trailing '\0' */
+#define LRE_GROUP_NAME_TRAILER_LEN 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);
+int lre_get_alloc_count(const uint8_t *bc_buf);
 int lre_get_capture_count(const uint8_t *bc_buf);
 int lre_get_flags(const uint8_t *bc_buf);
 const char *lre_get_groupnames(const uint8_t *bc_buf);
diff --git a/src/couch_quickjs/quickjs/libunicode.h 
b/src/couch_quickjs/quickjs/libunicode.h
index 5d964e40f..5b02c82b4 100644
--- a/src/couch_quickjs/quickjs/libunicode.h
+++ b/src/couch_quickjs/quickjs/libunicode.h
@@ -147,6 +147,11 @@ static inline int lre_is_id_continue_byte(uint8_t c) {
                                 UNICODE_C_DIGIT);
 }
 
+static inline int lre_is_word_byte(uint8_t c) {
+    return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
+                                UNICODE_C_UNDER | UNICODE_C_DIGIT);
+}
+
 int lre_is_space_non_ascii(uint32_t c);
 
 static inline int lre_is_space(uint32_t c) {
diff --git a/src/couch_quickjs/quickjs/quickjs-opcode.h 
b/src/couch_quickjs/quickjs/quickjs-opcode.h
index d93852133..7b98ddf05 100644
--- a/src/couch_quickjs/quickjs/quickjs-opcode.h
+++ b/src/couch_quickjs/quickjs/quickjs-opcode.h
@@ -168,6 +168,7 @@ DEF(    set_var_ref, 3, 1, 1, var_ref) /* must come after 
put_var_ref */
 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(  set_loc_check, 3, 1, 1, loc) /* must come after put_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)
diff --git a/src/couch_quickjs/quickjs/quickjs.c 
b/src/couch_quickjs/quickjs/quickjs.c
index b84af4a27..6cad52d76 100644
--- a/src/couch_quickjs/quickjs/quickjs.c
+++ b/src/couch_quickjs/quickjs/quickjs.c
@@ -451,13 +451,6 @@ struct JSContext {
 
     uint16_t binary_object_count;
     int binary_object_size;
-    /* TRUE if the array prototype is "normal":
-      - no small index properties which are get/set or non writable
-      - its prototype is Object.prototype
-      - Object.prototype has no small index properties which are get/set or 
non writable
-      - the prototype of Object.prototype is null (always true as it is 
immutable)
-    */
-    uint8_t std_array_prototype;
     
     JSShape *array_shape;   /* initial shape for Array objects */
     JSShape *arguments_shape;  /* shape for arguments objects */
@@ -936,7 +929,13 @@ struct JSObject {
         struct {
             int __gc_ref_count; /* corresponds to header.ref_count */
             uint8_t __gc_mark : 7; /* corresponds to header.mark/gc_obj_type */
-            uint8_t is_prototype : 1; /* object may be used as prototype */
+            /* TRUE if the array prototype is "normal":
+               - no small index properties which are get/set or non writable
+               - its prototype is Object.prototype
+               - Object.prototype has no small index properties which are 
get/set or non writable
+               - the prototype of Object.prototype is null (always true as it 
is immutable)
+            */
+            uint8_t is_std_array_prototype : 1;
 
             uint8_t extensible : 1;
             uint8_t free_mark : 1; /* only used when freeing objects with 
cycles */
@@ -5206,7 +5205,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, 
JSShape *sh, JSClassID clas
     if (unlikely(!p))
         goto fail;
     p->class_id = class_id;
-    p->is_prototype = 0;
+    p->is_std_array_prototype = 0;
     p->extensible = TRUE;
     p->free_mark = 0;
     p->is_exotic = 0;
@@ -7566,14 +7565,7 @@ static int JS_SetPrototypeInternal(JSContext *ctx, 
JSValueConst obj,
     if (sh->proto)
         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
     sh->proto = proto;
-    if (proto)
-        proto->is_prototype = TRUE;
-    if (p->is_prototype) {
-        /* track modification of Array.prototype */
-        if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) 
{
-            ctx->std_array_prototype = FALSE;
-        }
-    }
+    p->is_std_array_prototype = FALSE; 
     return TRUE;
 }
 
@@ -8773,12 +8765,25 @@ static JSProperty *add_property(JSContext *ctx,
 {
     JSShape *sh, *new_sh;
 
-    if (unlikely(p->is_prototype)) {
-        /* track addition of small integer properties to Array.prototype and 
Object.prototype */
-        if (unlikely((p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) 
||
-                      p == 
JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) &&
-                     __JS_AtomIsTaggedInt(prop))) {
-            ctx->std_array_prototype = FALSE;
+    if (unlikely(__JS_AtomIsTaggedInt(prop))) {
+        /* update is_std_array_prototype */
+        if (unlikely(p->is_std_array_prototype)) {
+            p->is_std_array_prototype = FALSE;
+        } else if (unlikely(p->has_immutable_prototype)) {
+            struct list_head *el;
+            
+            /* modifying Object.prototype : reset the corresponding 
is_std_array_prototype */
+            list_for_each(el, &ctx->rt->context_list) {
+                JSContext *ctx1 = list_entry(el, JSContext, link);
+                if (JS_IsObject(ctx1->class_proto[JS_CLASS_OBJECT]) && 
+                    JS_VALUE_GET_OBJ(ctx1->class_proto[JS_CLASS_OBJECT]) == p) 
{
+                    if (JS_IsObject(ctx1->class_proto[JS_CLASS_ARRAY])) {
+                        JSObject *p1 = 
JS_VALUE_GET_OBJ(ctx1->class_proto[JS_CLASS_ARRAY]);
+                        p1->is_std_array_prototype = FALSE;
+                    }
+                    break;
+                }
+            }
         }
     }
     sh = p->shape;
@@ -8860,11 +8865,7 @@ static no_inline __exception int 
convert_fast_array_to_array(JSContext *ctx,
     p->u.array.u.values = NULL; /* fail safe */
     p->u.array.u1.size = 0;
     p->fast_array = 0;
-
-    /* track modification of Array.prototype */
-    if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) {
-        ctx->std_array_prototype = FALSE;
-    }
+    p->is_std_array_prototype = FALSE;
     return 0;
 }
 
@@ -9509,6 +9510,18 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst 
obj,
     }
 }
 
+/* return true if an element can be added to a fast array without further 
tests */
+static force_inline BOOL can_extend_fast_array(JSObject *p)
+{
+    JSObject *proto;
+    if (!p->extensible)
+        return FALSE;
+    proto = p->shape->proto;
+    if (!proto)
+        return TRUE;
+    return proto->is_std_array_prototype;
+}
+
 /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
                                JSValue prop, JSValue val, int flags)
@@ -9529,9 +9542,7 @@ static int JS_SetPropertyValue(JSContext *ctx, 
JSValueConst this_obj,
                 /* fast path to add an element to the array */
                 if (unlikely(idx != (uint32_t)p->u.array.count ||
                              !p->fast_array ||
-                             !p->extensible ||
-                             p->shape->proto != 
JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) ||
-                             !ctx->std_array_prototype)) {
+                             !can_extend_fast_array(p))) {
                     goto slow_path;
                 }
                 /* add element */
@@ -18335,6 +18346,18 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, 
JSValueConst func_obj,
                 sp--;
             }
             BREAK;
+        CASE(OP_set_loc_check):
+            {
+                int idx;
+                idx = get_u16(pc);
+                pc += 2;
+                if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
+                    JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
+                    goto exception;
+                }
+                set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
+            }
+            BREAK;
         CASE(OP_put_loc_check_init):
             {
                 int idx;
@@ -19142,9 +19165,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, 
JSValueConst func_obj,
                         uint32_t new_len, array_len;
                         if (unlikely(idx != (uint32_t)p->u.array.count ||
                                      !p->fast_array ||
-                                     !p->extensible ||
-                                     p->shape->proto != 
JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) ||
-                                     !ctx->std_array_prototype)) {
+                                     !can_extend_fast_array(p))) {
                             goto put_array_el_slow_path;
                         }
                         if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) != 
JS_TAG_INT))
@@ -23355,8 +23376,10 @@ static int cpool_add(JSParseState *s, JSValue val)
     JSFunctionDef *fd = s->cur_func;
 
     if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
-                        &fd->cpool_size, fd->cpool_count + 1))
+                        &fd->cpool_size, fd->cpool_count + 1)) {
+        JS_FreeValue(s->ctx, val);
         return -1;
+    }
     fd->cpool[fd->cpool_count++] = val;
     return fd->cpool_count - 1;
 }
@@ -29946,6 +29969,7 @@ static int js_create_module_bytecode_function(JSContext 
*ctx, JSModuleDef *m)
 
     if (JS_IsException(func_obj))
         return -1;
+    m->func_obj = func_obj;
     b = JS_VALUE_GET_PTR(bfunc);
     func_obj = js_closure2(ctx, func_obj, b, NULL, NULL, TRUE, m);
     if (JS_IsException(func_obj)) {
@@ -29953,7 +29977,6 @@ static int js_create_module_bytecode_function(JSContext 
*ctx, JSModuleDef *m)
         JS_FreeValue(ctx, func_obj);
         return -1;
     }
-    m->func_obj = func_obj;
     return 0;
 }
 
@@ -34760,7 +34783,7 @@ static __exception int resolve_labels(JSContext *ctx, 
JSFunctionDef *s)
                 /* Transformation: dup put_x(n) drop -> put_x(n) */
                 int op1, line2 = -1;
                 /* Transformation: dup put_x(n) -> set_x(n) */
-                if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, 
OP_put_var_ref), -1, -1)) {
+                if (code_match(&cc, pos_next, M4(OP_put_loc, OP_put_loc_check, 
OP_put_arg, OP_put_var_ref), -1, -1)) {
                     if (cc.line_num >= 0) line_num = cc.line_num;
                     op1 = cc.op + 1;  /* put_x -> set_x */
                     pos_next = cc.pos;
@@ -34868,6 +34891,7 @@ static __exception int resolve_labels(JSContext *ctx, 
JSFunctionDef *s)
             goto no_change;
 #endif
         case OP_put_loc:
+        case OP_put_loc_check:
         case OP_put_arg:
         case OP_put_var_ref:
             if (OPTIMIZE) {
@@ -42131,9 +42155,7 @@ static JSValue js_array_push(JSContext *ctx, 
JSValueConst this_val,
     if (likely(JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT && !unshift)) {
         JSObject *p = JS_VALUE_GET_OBJ(this_val);
         if (likely(p->class_id == JS_CLASS_ARRAY && p->fast_array &&
-                   p->extensible &&
-                   p->shape->proto == 
JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) &&
-                   ctx->std_array_prototype &&
+                   can_extend_fast_array(p) &&
                    JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT &&
                    JS_VALUE_GET_INT(p->prop[0].u.value) == p->u.array.count &&
                    (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE) != 0)) 
{
@@ -45147,7 +45169,7 @@ static JSValue js_string_match(JSContext *ctx, 
JSValueConst this_val,
     if (JS_IsUndefined(O) || JS_IsNull(O))
         return JS_ThrowTypeError(ctx, "cannot convert to object");
 
-    if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
+    if (JS_IsObject(regexp)) {
         matcher = JS_GetProperty(ctx, regexp, atom);
         if (JS_IsException(matcher))
             return JS_EXCEPTION;
@@ -45327,7 +45349,7 @@ static JSValue js_string_replace(JSContext *ctx, 
JSValueConst this_val,
     replaceValue_str = JS_UNDEFINED;
     repl_str = JS_UNDEFINED;
 
-    if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
+    if (JS_IsObject(searchValue)) {
         JSValue replacer;
         if (is_replaceAll) {
             if (check_regexp_g_flag(ctx, searchValue) < 0)
@@ -45438,7 +45460,7 @@ static JSValue js_string_split(JSContext *ctx, 
JSValueConst this_val,
     A = JS_UNDEFINED;
     R = JS_UNDEFINED;
 
-    if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
+    if (JS_IsObject(separator)) {
         JSValue splitter;
         splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
         if (JS_IsException(splitter))
@@ -45479,7 +45501,6 @@ static JSValue js_string_split(JSContext *ctx, 
JSValueConst this_val,
             goto add_tail;
         goto done;
     }
-    q = p;
     for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
         e = string_indexof(sp, rp, q);
         if (e < 0)
@@ -47415,11 +47436,12 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_val,
     JSValue indices, indices_groups;
     uint8_t *re_bytecode;
     uint8_t **capture, *str_buf;
-    int rc, capture_count, shift, i, re_flags;
+    int rc, capture_count, shift, i, re_flags, alloc_count;
     int64_t last_index;
     const char *group_name_ptr;
     JSObject *p_obj;
-
+    JSAtom group_name;
+    
     if (!re)
         return JS_EXCEPTION;
 
@@ -47433,7 +47455,8 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_val,
     indices = JS_UNDEFINED;
     indices_groups = JS_UNDEFINED;
     capture = NULL;
-
+    group_name = JS_ATOM_NULL;
+    
     if (js_regexp_get_lastIndex(ctx, &last_index, this_val))
         goto fail;
 
@@ -47443,12 +47466,13 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_val,
         last_index = 0;
     }
     str = JS_VALUE_GET_STRING(str_val);
-    capture_count = lre_get_capture_count(re_bytecode);
-    if (capture_count > 0) {
-        capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
+    alloc_count = lre_get_alloc_count(re_bytecode);
+    if (alloc_count > 0) {
+        capture = js_malloc(ctx, sizeof(capture[0]) * alloc_count);
         if (!capture)
             goto fail;
     }
+    capture_count = lre_get_capture_count(re_bytecode);
     shift = str->is_wide_char;
     str_buf = str->u.str8;
     if (last_index > str->len) {
@@ -47515,15 +47539,20 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_val,
             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) name = group_name_ptr;
-                group_name_ptr += strlen(group_name_ptr) + 1;
+                if (*group_name_ptr) {
+                    /* XXX: slow, should create a shape when the regexp is
+                       compiled */
+                    group_name = JS_NewAtom(ctx, group_name_ptr);
+                    if (group_name == JS_ATOM_NULL)
+                        goto fail;
+                }
+                group_name_ptr += strlen(group_name_ptr) + 
LRE_GROUP_NAME_TRAILER_LEN;
             }
 
             if (match[0] && match[1]) {
@@ -47550,12 +47579,15 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_val,
                         goto fail;
                     }
                 }
-                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 (group_name != JS_ATOM_NULL) {
+                    /* JS_HasProperty() cannot fail here */
+                    if (!JS_IsUndefined(val) ||
+                        !JS_HasProperty(ctx, indices_groups, group_name)) {
+                        if (JS_DefinePropertyValue(ctx, indices_groups,
+                                                   group_name, 
JS_DupValue(ctx, val), prop_flags) < 0) {
+                            JS_FreeValue(ctx, val);
+                            goto fail;
+                        }
                     }
                 }
                 if (JS_DefinePropertyValueUint32(ctx, indices, i, val,
@@ -47571,13 +47603,19 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_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 (group_name != JS_ATOM_NULL) {
+                /* JS_HasProperty() cannot fail here */
+                if (!JS_IsUndefined(val) ||
+                    !JS_HasProperty(ctx, groups, group_name)) {
+                    if (JS_DefinePropertyValue(ctx, groups, group_name,
+                                               JS_DupValue(ctx, val),
+                                               prop_flags) < 0) {
+                        JS_FreeValue(ctx, val);
+                        goto fail;
+                    }
                 }
+                JS_FreeAtom(ctx, group_name);
+                group_name = JS_ATOM_NULL;
             }
             p_obj->u.array.u.values[p_obj->u.array.count++] = val;
         }
@@ -47598,6 +47636,7 @@ static JSValue js_regexp_exec(JSContext *ctx, 
JSValueConst this_val,
     ret = obj;
     obj = JS_UNDEFINED;
 fail:
+    JS_FreeAtom(ctx, group_name);
     JS_FreeValue(ctx, indices_groups);
     JS_FreeValue(ctx, indices);
     JS_FreeValue(ctx, str_val);
@@ -47617,7 +47656,7 @@ static JSValue js_regexp_replace(JSContext *ctx, 
JSValueConst this_val, JSValueC
     uint8_t *re_bytecode;
     int ret;
     uint8_t **capture, *str_buf;
-    int capture_count, shift, re_flags;
+    int capture_count, alloc_count, shift, re_flags;
     int next_src_pos, start, end;
     int64_t last_index;
     StringBuffer b_s, *b = &b_s;
@@ -47651,12 +47690,13 @@ static JSValue js_regexp_replace(JSContext *ctx, 
JSValueConst this_val, JSValueC
         if (js_regexp_get_lastIndex(ctx, &last_index, this_val))
             goto fail;
     }
-    capture_count = lre_get_capture_count(re_bytecode);
-    if (capture_count > 0) {
-        capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
+    alloc_count = lre_get_alloc_count(re_bytecode);
+    if (alloc_count > 0) {
+        capture = js_malloc(ctx, sizeof(capture[0]) * alloc_count);
         if (!capture)
             goto fail;
     }
+    capture_count = lre_get_capture_count(re_bytecode);
     fullUnicode = ((re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 
0);
     shift = str->is_wide_char;
     str_buf = str->u.str8;
@@ -55405,6 +55445,11 @@ static int JS_AddIntrinsicBasicObjects(JSContext *ctx)
         return -1;
     ctx->array_ctor = obj;
 
+    {
+        JSObject *p = JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]);
+        p->is_std_array_prototype = TRUE;
+    }
+    
     ctx->array_shape = js_new_shape2(ctx, 
get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
                                      JS_PROP_INITIAL_HASH_SIZE, 1);
     if (!ctx->array_shape)
@@ -55412,7 +55457,6 @@ static int JS_AddIntrinsicBasicObjects(JSContext *ctx)
     if (add_shape_property(ctx, &ctx->array_shape, NULL,
                            JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH))
         return -1;
-    ctx->std_array_prototype = TRUE;
 
     ctx->arguments_shape = js_new_shape2(ctx, 
get_proto_obj(ctx->class_proto[JS_CLASS_OBJECT]),
                                          JS_PROP_INITIAL_HASH_SIZE, 3);
diff --git a/src/couch_quickjs/quickjs/test262.conf 
b/src/couch_quickjs/quickjs/test262.conf
index fe52a0b08..aa76e6395 100644
--- a/src/couch_quickjs/quickjs/test262.conf
+++ b/src/couch_quickjs/quickjs/test262.conf
@@ -176,7 +176,7 @@ Reflect.construct
 Reflect.set
 Reflect.setPrototypeOf
 regexp-dotall
-regexp-duplicate-named-groups=skip
+regexp-duplicate-named-groups
 regexp-lookbehind
 regexp-match-indices
 regexp-modifiers
@@ -255,54 +255,6 @@ 
test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js
 #test262/test/built-ins/RegExp/CharacterClassEscapes/
 #test262/test/built-ins/RegExp/property-escapes/
 
-# not yet in official specification
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-number-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-number-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-string-primitive.js
-test262/test/built-ins/String/prototype/match/cstm-matcher-on-string-primitive.js
-test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-number-primitive.js
-test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-number-primitive.js
-test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-string-primitive.js
-test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-string-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-number-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-number-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-string-primitive.js
-test262/test/built-ins/String/prototype/replace/cstm-replace-on-string-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-number-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-number-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-string-primitive.js
-test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-string-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-number-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-number-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-string-primitive.js
-test262/test/built-ins/String/prototype/search/cstm-search-on-string-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-bigint-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-boolean-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-number-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-number-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-string-primitive.js
-test262/test/built-ins/String/prototype/split/cstm-split-on-string-primitive.js
-
 ####################################
 # staging tests
 
diff --git a/src/couch_quickjs/quickjs/test262_errors.txt 
b/src/couch_quickjs/quickjs/test262_errors.txt
index d60814ac4..796c296e2 100644
--- a/src/couch_quickjs/quickjs/test262_errors.txt
+++ b/src/couch_quickjs/quickjs/test262_errors.txt
@@ -33,12 +33,6 @@ test262/test/staging/sm/Function/function-name-for.js:13: 
Test262Error: Expected
 test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:12: 
Test262Error: Expected SameValue(«[object Object]», «undefined») to be true
 test262/test/staging/sm/Function/invalid-parameter-list.js:13: Test262Error: 
Expected a SyntaxError to be thrown but no exception was thrown at all
 test262/test/staging/sm/Function/invalid-parameter-list.js:13: strict mode: 
Test262Error: Expected a SyntaxError to be thrown but no exception was thrown 
at all
-test262/test/staging/sm/RegExp/regress-613820-1.js:12: Test262Error: Actual 
[aaa, aa, a] and expected [aa, a, a] should have the same contents. 
-test262/test/staging/sm/RegExp/regress-613820-1.js:12: strict mode: 
Test262Error: Actual [aaa, aa, a] and expected [aa, a, a] should have the same 
contents. 
-test262/test/staging/sm/RegExp/regress-613820-2.js:12: Test262Error: Actual 
[foobar, f, o, o, b, a, r] and expected [foobar, undefined, undefined, 
undefined, b, a, r] should have the same contents. 
-test262/test/staging/sm/RegExp/regress-613820-2.js:12: strict mode: 
Test262Error: Actual [foobar, f, o, o, b, a, r] and expected [foobar, 
undefined, undefined, undefined, b, a, r] should have the same contents. 
-test262/test/staging/sm/RegExp/regress-613820-3.js:12: Test262Error: Actual 
[aab, a, undefined, ab] and expected [aa, undefined, a, undefined] should have 
the same contents. 
-test262/test/staging/sm/RegExp/regress-613820-3.js:12: strict mode: 
Test262Error: Actual [aab, a, undefined, ab] and expected [aa, undefined, a, 
undefined] should have the same contents. 
 test262/test/staging/sm/String/string-upper-lower-mapping.js:16: Test262Error: 
Expected SameValue(«"꟏"», «"꟎"») to be true
 test262/test/staging/sm/String/string-upper-lower-mapping.js:16: strict mode: 
Test262Error: Expected SameValue(«"꟏"», «"꟎"») to be true
 test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:29: 
Test262Error: Expected a ExpectedError but got a Error

Reply via email to