packages feed

quickjs-hs (empty) → 0.1

raw patch · 17 files changed

+72528/−0 lines, 17 filesdep +HUnitdep +QuickCheckdep +aesonsetup-changed

Dependencies added: HUnit, QuickCheck, aeson, base, bytestring, containers, exceptions, inline-c, mtl, quickjs-hs, scientific, string-conv, tasty, tasty-hunit, tasty-quickcheck, template-haskell, text, time, transformers, unliftio-core, unordered-containers, vector

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Changelog for quickjs-hs++### 0.1++* initial release
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2020 Samuel Balco++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,47 @@+# quickjs-hs++![Tests CI](https://github.com/goodlyrottenapple/quickjs-hs/workflows/Tests%20CI/badge.svg)+++This package provides a Haskell wrapper for the [QuickJS](https://bellard.org/quickjs/) Javascript Engine.++## Features++The functionality is quite basic and is currently limited to:+- evaluating JS code+- calling a JS function in the global scope+- marshalling [Aeson Values](https://hackage.haskell.org/package/aeson-1.5.3.0/docs/Data-Aeson.html#t:Value) to and from JSValues.+++## Examples+Evaluate an expression:++```haskell+import Quickjs++one_plus_two = quickjs $ do+  res <- eval "1+2"+  liftIO $ print res+```++Declare a function and call it on an argument:++```haskell+call_f = quickjs $ do+  _ <- eval_ "f = (x) => x+1"+  res <- eval "f(2)"+  liftIO $ print res+```++Pass a Haskell value (which has a [ToJSON](https://hackage.haskell.org/package/aeson-1.5.3.0/docs/Data-Aeson.html#t:ToJSON) instance) to the JS runtime:++```haskell+aeson_marshall = quickjs $ do+  _ <- eval_ "f = (x) => x+1"+  res <- withJSValue (3::Int) $ \x -> call "f" [x]+  liftIO $ print res+```++## Contributing++Please feel free to report bugs/submit feature requests via the [github issue tracker](https://github.com/goodlyrottenapple/quickjs-hs/issues) and submit any pull requests to the [git repository](https://github.com/goodlyrottenapple/quickjs-hs/)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ quickjs-hs.cabal view
@@ -0,0 +1,90 @@+cabal-version:  1.12+name:           quickjs-hs+version:        0.1+homepage:       https://github.com/goodlyrottenapple/quickjs-hs#readme+bug-reports:    https://github.com/goodlyrottenapple/quickjs-hs/issues+author:         Sam Balco+maintainer:     goodlyrottenapple@gmail.com+copyright:      2020 Sam Balco+license:        MIT+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md+category:       Javascript+synopsis:       Wrapper for the QuickJS Javascript Engine+description:+    This library is a wrapper for the [QuickJS](https://bellard.org/quickjs/) Javascript Engine.+    .+    To get started, see the ReadMe below.++source-repository head+  type: git+  location: https://github.com/goodlyrottenapple/quickjs-hs++library+  exposed-modules:+      Quickjs, Quickjs.Types, Quickjs.Error+  other-modules:+      Paths_quickjs_hs+  hs-source-dirs:+      src+  build-depends:+      base                 >=4.7       && <5+    , aeson                >=1.4.7.1   && <1.5+    , bytestring           >=0.10.10.0 && <0.11+    , containers           >=0.6.2.1   && <0.7+    , exceptions           >=0.10.4    && <0.11+    , inline-c             >=0.9.1.0   && <0.10+    , mtl                  >=2.2.2     && <2.3+    , scientific           >=0.3.6.2   && <0.4+    , string-conv          >=0.1.2     && <0.2+    , template-haskell     >=2.15.0.0  && <2.16+    , text                 >=1.2.4.0   && <1.3+    , time                 >=1.9.3     && <1.10+    , transformers         >=0.5.6.2   && <0.6+    , unliftio-core        >=0.1.2.0   && <0.2+    , unordered-containers >=0.2.10.0  && <0.3+    , vector               >=0.12.1.2  && <0.13+  default-language: Haskell2010+  include-dirs: quickjs+  -- Order matters for dynamic linking, see GHC#12152.+  -- To make both `cabal repl` and `stack ghci` work, we have to+  -- make "a.cpp" come alphabetically before "main.cpp".+  c-sources: +      quickjs/cutils.c+    , quickjs/libbf.c+    , quickjs/libregexp.c+    , quickjs/libunicode.c+    , quickjs/quickjs.h+    , quickjs/quickjs.c+    , quickjs/quickjs-libc.h+    , quickjs/quickjs-libc.c+  cc-options: +    -static -D_GNU_SOURCE +    -DCONFIG_VERSION="2020-07-05"+    -DCONFIG_BIGNUM+++test-suite quickjs-hs-test+    type:             exitcode-stdio-1.0+    main-is:          Spec.hs+    hs-source-dirs:   test+    other-modules:    Paths_quickjs_hs+    default-language: Haskell2010+    ghc-options:      -threaded -rtsopts -with-rtsopts=-N+    build-depends:+        base+      , quickjs-hs           -any+      , aeson+      , exceptions+      , HUnit                >=1.6.0.0  && <1.7+      , QuickCheck           >=2.13.2   && <2.14+      , tasty                >=1.2.3    && <1.3+      , tasty-hunit          >=0.10.0.2 && <0.11+      , tasty-quickcheck     >=0.10.1.1 && <0.11+      , text+      , unordered-containers+      , vector+
+ quickjs/cutils.c view
@@ -0,0 +1,620 @@+/*+ * C utilities+ * + * Copyright (c) 2017 Fabrice Bellard+ * Copyright (c) 2018 Charlie Gordon+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#include <stdlib.h>+#include <stdio.h>+#include <stdarg.h>+#include <string.h>++#include "cutils.h"++void pstrcpy(char *buf, int buf_size, const char *str)+{+    int c;+    char *q = buf;++    if (buf_size <= 0)+        return;++    for(;;) {+        c = *str++;+        if (c == 0 || q >= buf + buf_size - 1)+            break;+        *q++ = c;+    }+    *q = '\0';+}++/* strcat and truncate. */+char *pstrcat(char *buf, int buf_size, const char *s)+{+    int len;+    len = strlen(buf);+    if (len < buf_size)+        pstrcpy(buf + len, buf_size - len, s);+    return buf;+}++int strstart(const char *str, const char *val, const char **ptr)+{+    const char *p, *q;+    p = str;+    q = val;+    while (*q != '\0') {+        if (*p != *q)+            return 0;+        p++;+        q++;+    }+    if (ptr)+        *ptr = p;+    return 1;+}++int has_suffix(const char *str, const char *suffix)+{+    size_t len = strlen(str);+    size_t slen = strlen(suffix);+    return (len >= slen && !memcmp(str + len - slen, suffix, slen));+}++/* Dynamic buffer package */++static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)+{+    return realloc(ptr, size);+}++void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)+{+    memset(s, 0, sizeof(*s));+    if (!realloc_func)+        realloc_func = dbuf_default_realloc;+    s->opaque = opaque;+    s->realloc_func = realloc_func;+}++void dbuf_init(DynBuf *s)+{+    dbuf_init2(s, NULL, NULL);+}++/* return < 0 if error */+int dbuf_realloc(DynBuf *s, size_t new_size)+{+    size_t size;+    uint8_t *new_buf;+    if (new_size > s->allocated_size) {+        if (s->error)+            return -1;+        size = s->allocated_size * 3 / 2;+        if (size > new_size)+            new_size = size;+        new_buf = s->realloc_func(s->opaque, s->buf, new_size);+        if (!new_buf) {+            s->error = TRUE;+            return -1;+        }+        s->buf = new_buf;+        s->allocated_size = new_size;+    }+    return 0;+}++int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)+{+    size_t end;+    end = offset + len;+    if (dbuf_realloc(s, end))+        return -1;+    memcpy(s->buf + offset, data, len);+    if (end > s->size)+        s->size = end;+    return 0;+}++int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)+{+    if (unlikely((s->size + len) > s->allocated_size)) {+        if (dbuf_realloc(s, s->size + len))+            return -1;+    }+    memcpy(s->buf + s->size, data, len);+    s->size += len;+    return 0;+}++int dbuf_put_self(DynBuf *s, size_t offset, size_t len)+{+    if (unlikely((s->size + len) > s->allocated_size)) {+        if (dbuf_realloc(s, s->size + len))+            return -1;+    }+    memcpy(s->buf + s->size, s->buf + offset, len);+    s->size += len;+    return 0;+}++int dbuf_putc(DynBuf *s, uint8_t c)+{+    return dbuf_put(s, &c, 1);+}++int dbuf_putstr(DynBuf *s, const char *str)+{+    return dbuf_put(s, (const uint8_t *)str, strlen(str));+}++int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,+                                                      const char *fmt, ...)+{+    va_list ap;+    char buf[128];+    int len;+    +    va_start(ap, fmt);+    len = vsnprintf(buf, sizeof(buf), fmt, ap);+    va_end(ap);+    if (len < sizeof(buf)) {+        /* fast case */+        return dbuf_put(s, (uint8_t *)buf, len);+    } else {+        if (dbuf_realloc(s, s->size + len + 1))+            return -1;+        va_start(ap, fmt);+        vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,+                  fmt, ap);+        va_end(ap);+        s->size += len;+    }+    return 0;+}++void dbuf_free(DynBuf *s)+{+    /* we test s->buf as a fail safe to avoid crashing if dbuf_free()+       is called twice */+    if (s->buf) {+        s->realloc_func(s->opaque, s->buf, 0);+    }+    memset(s, 0, sizeof(*s));+}++/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes+   are output. */+int unicode_to_utf8(uint8_t *buf, unsigned int c)+{+    uint8_t *q = buf;++    if (c < 0x80) {+        *q++ = c;+    } else {+        if (c < 0x800) {+            *q++ = (c >> 6) | 0xc0;+        } else {+            if (c < 0x10000) {+                *q++ = (c >> 12) | 0xe0;+            } else {+                if (c < 0x00200000) {+                    *q++ = (c >> 18) | 0xf0;+                } else {+                    if (c < 0x04000000) {+                        *q++ = (c >> 24) | 0xf8;+                    } else if (c < 0x80000000) {+                        *q++ = (c >> 30) | 0xfc;+                        *q++ = ((c >> 24) & 0x3f) | 0x80;+                    } else {+                        return 0;+                    }+                    *q++ = ((c >> 18) & 0x3f) | 0x80;+                }+                *q++ = ((c >> 12) & 0x3f) | 0x80;+            }+            *q++ = ((c >> 6) & 0x3f) | 0x80;+        }+        *q++ = (c & 0x3f) | 0x80;+    }+    return q - buf;+}++static const unsigned int utf8_min_code[5] = {+    0x80, 0x800, 0x10000, 0x00200000, 0x04000000,+};++static const unsigned char utf8_first_code_mask[5] = {+    0x1f, 0xf, 0x7, 0x3, 0x1,+};++/* return -1 if error. *pp is not updated in this case. max_len must+   be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */+int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)+{+    int l, c, b, i;++    c = *p++;+    if (c < 0x80) {+        *pp = p;+        return c;+    }+    switch(c) {+    case 0xc0 ... 0xdf:+        l = 1;+        break;+    case 0xe0 ... 0xef:+        l = 2;+        break;+    case 0xf0 ... 0xf7:+        l = 3;+        break;+    case 0xf8 ... 0xfb:+        l = 4;+        break;+    case 0xfc ... 0xfd:+        l = 5;+        break;+    default:+        return -1;+    }+    /* check that we have enough characters */+    if (l > (max_len - 1))+        return -1;+    c &= utf8_first_code_mask[l - 1];+    for(i = 0; i < l; i++) {+        b = *p++;+        if (b < 0x80 || b >= 0xc0)+            return -1;+        c = (c << 6) | (b & 0x3f);+    }+    if (c < utf8_min_code[l - 1])+        return -1;+    *pp = p;+    return c;+}++#if 0++#if defined(EMSCRIPTEN) || defined(__ANDROID__)++static void *rqsort_arg;+static int (*rqsort_cmp)(const void *, const void *, void *);++static int rqsort_cmp2(const void *p1, const void *p2)+{+    return rqsort_cmp(p1, p2, rqsort_arg);+}++/* not reentrant, but not needed with emscripten */+void rqsort(void *base, size_t nmemb, size_t size,+            int (*cmp)(const void *, const void *, void *),+            void *arg)+{+    rqsort_arg = arg;+    rqsort_cmp = cmp;+    qsort(base, nmemb, size, rqsort_cmp2);+}++#endif++#else++typedef void (*exchange_f)(void *a, void *b, size_t size);+typedef int (*cmp_f)(const void *, const void *, void *opaque);++static void exchange_bytes(void *a, void *b, size_t size) {+    uint8_t *ap = (uint8_t *)a;+    uint8_t *bp = (uint8_t *)b;++    while (size-- != 0) {+        uint8_t t = *ap;+        *ap++ = *bp;+        *bp++ = t;+    }+}++static void exchange_one_byte(void *a, void *b, size_t size) {+    uint8_t *ap = (uint8_t *)a;+    uint8_t *bp = (uint8_t *)b;+    uint8_t t = *ap;+    *ap = *bp;+    *bp = t;+}++static void exchange_int16s(void *a, void *b, size_t size) {+    uint16_t *ap = (uint16_t *)a;+    uint16_t *bp = (uint16_t *)b;++    for (size /= sizeof(uint16_t); size-- != 0;) {+        uint16_t t = *ap;+        *ap++ = *bp;+        *bp++ = t;+    }+}++static void exchange_one_int16(void *a, void *b, size_t size) {+    uint16_t *ap = (uint16_t *)a;+    uint16_t *bp = (uint16_t *)b;+    uint16_t t = *ap;+    *ap = *bp;+    *bp = t;+}++static void exchange_int32s(void *a, void *b, size_t size) {+    uint32_t *ap = (uint32_t *)a;+    uint32_t *bp = (uint32_t *)b;++    for (size /= sizeof(uint32_t); size-- != 0;) {+        uint32_t t = *ap;+        *ap++ = *bp;+        *bp++ = t;+    }+}++static void exchange_one_int32(void *a, void *b, size_t size) {+    uint32_t *ap = (uint32_t *)a;+    uint32_t *bp = (uint32_t *)b;+    uint32_t t = *ap;+    *ap = *bp;+    *bp = t;+}++static void exchange_int64s(void *a, void *b, size_t size) {+    uint64_t *ap = (uint64_t *)a;+    uint64_t *bp = (uint64_t *)b;++    for (size /= sizeof(uint64_t); size-- != 0;) {+        uint64_t t = *ap;+        *ap++ = *bp;+        *bp++ = t;+    }+}++static void exchange_one_int64(void *a, void *b, size_t size) {+    uint64_t *ap = (uint64_t *)a;+    uint64_t *bp = (uint64_t *)b;+    uint64_t t = *ap;+    *ap = *bp;+    *bp = t;+}++static void exchange_int128s(void *a, void *b, size_t size) {+    uint64_t *ap = (uint64_t *)a;+    uint64_t *bp = (uint64_t *)b;++    for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {+        uint64_t t = ap[0];+        uint64_t u = ap[1];+        ap[0] = bp[0];+        ap[1] = bp[1];+        bp[0] = t;+        bp[1] = u;+    }+}++static void exchange_one_int128(void *a, void *b, size_t size) {+    uint64_t *ap = (uint64_t *)a;+    uint64_t *bp = (uint64_t *)b;+    uint64_t t = ap[0];+    uint64_t u = ap[1];+    ap[0] = bp[0];+    ap[1] = bp[1];+    bp[0] = t;+    bp[1] = u;+}++static inline exchange_f exchange_func(const void *base, size_t size) {+    switch (((uintptr_t)base | (uintptr_t)size) & 15) {+    case 0:+        if (size == sizeof(uint64_t) * 2)+            return exchange_one_int128;+        else+            return exchange_int128s;+    case 8:+        if (size == sizeof(uint64_t))+            return exchange_one_int64;+        else+            return exchange_int64s;+    case 4:+    case 12:+        if (size == sizeof(uint32_t))+            return exchange_one_int32;+        else+            return exchange_int32s;+    case 2:+    case 6:+    case 10:+    case 14:+        if (size == sizeof(uint16_t))+            return exchange_one_int16;+        else+            return exchange_int16s;+    default:+        if (size == 1)+            return exchange_one_byte;+        else+            return exchange_bytes;+    }+}++static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)+{+    uint8_t *basep = (uint8_t *)base;+    size_t i, n, c, r;+    exchange_f swap = exchange_func(base, size);++    if (nmemb > 1) {+        i = (nmemb / 2) * size;+        n = nmemb * size;++        while (i > 0) {+            i -= size;+            for (r = i; (c = r * 2 + size) < n; r = c) {+                if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)+                    c += size;+                if (cmp(basep + r, basep + c, opaque) > 0)+                    break;+                swap(basep + r, basep + c, size);+            }+        }+        for (i = n - size; i > 0; i -= size) {+            swap(basep, basep + i, size);++            for (r = 0; (c = r * 2 + size) < i; r = c) {+                if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)+                    c += size;+                if (cmp(basep + r, basep + c, opaque) > 0)+                    break;+                swap(basep + r, basep + c, size);+            }+        }+    }+}++static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)+{+    return cmp(a, b, opaque) < 0 ?+        (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :+        (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));+}++/* pointer based version with local stack and insertion sort threshhold */+void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)+{+    struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;+    uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;+    size_t m4, i, lt, gt, span, span2;+    int c, depth;+    exchange_f swap = exchange_func(base, size);+    exchange_f swap_block = exchange_func(base, size | 128);++    if (nmemb < 2 || size <= 0)+        return;++    sp->base = (uint8_t *)base;+    sp->count = nmemb;+    sp->depth = 0;+    sp++;++    while (sp > stack) {+        sp--;+        ptr = sp->base;+        nmemb = sp->count;+        depth = sp->depth;++        while (nmemb > 6) {+            if (++depth > 50) {+                /* depth check to ensure worst case logarithmic time */+                heapsortx(ptr, nmemb, size, cmp, opaque);+                nmemb = 0;+                break;+            }+            /* select median of 3 from 1/4, 1/2, 3/4 positions */+            /* should use median of 5 or 9? */+            m4 = (nmemb >> 2) * size;+            m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);+            swap(ptr, m, size);  /* move the pivot to the start or the array */+            i = lt = 1;+            pi = plt = ptr + size;+            gt = nmemb;+            pj = pgt = top = ptr + nmemb * size;+            for (;;) {+                while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {+                    if (c == 0) {+                        swap(plt, pi, size);+                        lt++;+                        plt += size;+                    }+                    i++;+                    pi += size;+                }+                while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {+                    if (c == 0) {+                        gt--;+                        pgt -= size;+                        swap(pgt, pj, size);+                    }+                }+                if (pi >= pj)+                    break;+                swap(pi, pj, size);+                i++;+                pi += size;+            }+            /* array has 4 parts:+             * from 0 to lt excluded: elements identical to pivot+             * from lt to pi excluded: elements smaller than pivot+             * from pi to gt excluded: elements greater than pivot+             * from gt to n excluded: elements identical to pivot+             */+            /* move elements identical to pivot in the middle of the array: */+            /* swap values in ranges [0..lt[ and [i-lt..i[+               swapping the smallest span between lt and i-lt is sufficient+             */+            span = plt - ptr;+            span2 = pi - plt;+            lt = i - lt;+            if (span > span2)+                span = span2;+            swap_block(ptr, pi - span, span);+            /* swap values in ranges [gt..top[ and [i..top-(top-gt)[+               swapping the smallest span between top-gt and gt-i is sufficient+             */+            span = top - pgt;+            span2 = pgt - pi;+            pgt = top - span2;+            gt = nmemb - (gt - i);+            if (span > span2)+                span = span2;+            swap_block(pi, top - span, span);++            /* now array has 3 parts:+             * from 0 to lt excluded: elements smaller than pivot+             * from lt to gt excluded: elements identical to pivot+             * from gt to n excluded: elements greater than pivot+             */+            /* stack the larger segment and keep processing the smaller one+               to minimize stack use for pathological distributions */+            if (lt > nmemb - gt) {+                sp->base = ptr;+                sp->count = lt;+                sp->depth = depth;+                sp++;+                ptr = pgt;+                nmemb -= gt;+            } else {+                sp->base = pgt;+                sp->count = nmemb - gt;+                sp->depth = depth;+                sp++;+                nmemb = lt;+            }+        }+        /* Use insertion sort for small fragments */+        for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {+            for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)+                swap(pj, pj - size, size);+        }+    }+}++#endif
+ quickjs/libbf.c view
@@ -0,0 +1,8466 @@+/*+ * Tiny arbitrary precision floating point library+ * + * Copyright (c) 2017-2020 Fabrice Bellard+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#include <stdlib.h>+#include <stdio.h>+#include <inttypes.h>+#include <math.h>+#include <string.h>+#include <assert.h>++#ifdef __AVX2__+#include <immintrin.h>+#endif++#include "cutils.h"+#include "libbf.h"++/* enable it to check the multiplication result */+//#define USE_MUL_CHECK+/* enable it to use FFT/NTT multiplication */+#define USE_FFT_MUL+/* enable decimal floating point support */+#define USE_BF_DEC++//#define inline __attribute__((always_inline))++#ifdef __AVX2__+#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */+#else+#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */+#endif++/* XXX: adjust */+#define DIVNORM_LARGE_THRESHOLD 50+#define UDIV1NORM_THRESHOLD 3++#if LIMB_BITS == 64+#define FMT_LIMB1 "%" PRIx64 +#define FMT_LIMB "%016" PRIx64 +#define PRId_LIMB PRId64+#define PRIu_LIMB PRIu64++#else++#define FMT_LIMB1 "%x"+#define FMT_LIMB "%08x"+#define PRId_LIMB "d"+#define PRIu_LIMB "u"++#endif++typedef intptr_t mp_size_t;++typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+                          bf_flags_t flags);++#ifdef USE_FFT_MUL++#define FFT_MUL_R_OVERLAP_A (1 << 0)+#define FFT_MUL_R_OVERLAP_B (1 << 1)+#define FFT_MUL_R_NORESIZE  (1 << 2)++static no_inline int fft_mul(bf_context_t *s,+                             bf_t *res, limb_t *a_tab, limb_t a_len,+                             limb_t *b_tab, limb_t b_len, int mul_flags);+static void fft_clear_cache(bf_context_t *s);+#endif+#ifdef USE_BF_DEC+static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos);+#endif+++/* could leading zeros */+static inline int clz(limb_t a)+{+    if (a == 0) {+        return LIMB_BITS;+    } else {+#if LIMB_BITS == 64+        return clz64(a);+#else+        return clz32(a);+#endif+    }+}++static inline int ctz(limb_t a)+{+    if (a == 0) {+        return LIMB_BITS;+    } else {+#if LIMB_BITS == 64+        return ctz64(a);+#else+        return ctz32(a);+#endif+    }+}++static inline int ceil_log2(limb_t a)+{+    if (a <= 1)+        return 0;+    else+        return LIMB_BITS - clz(a - 1);+}++/* b must be >= 1 */+static inline slimb_t ceil_div(slimb_t a, slimb_t b)+{+    if (a >= 0)+        return (a + b - 1) / b;+    else+        return a / b;+}++/* b must be >= 1 */+static inline slimb_t floor_div(slimb_t a, slimb_t b)+{+    if (a >= 0) {+        return a / b;+    } else {+        return (a - b + 1) / b;+    }+}++/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */+static inline limb_t smod(slimb_t a, slimb_t b)+{+    a = a % (slimb_t)b;+    if (a < 0)+        a += b;+    return a;+}++/* signed addition with saturation */+static inline slimb_t sat_add(slimb_t a, slimb_t b)+{+    slimb_t r;+    r = a + b;+    /* overflow ? */+    if (((a ^ r) & (b ^ r)) < 0)+        r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1);+    return r;+}++#define malloc(s) malloc_is_forbidden(s)+#define free(p) free_is_forbidden(p)+#define realloc(p, s) realloc_is_forbidden(p, s)++void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,+                     void *realloc_opaque)+{+    memset(s, 0, sizeof(*s));+    s->realloc_func = realloc_func;+    s->realloc_opaque = realloc_opaque;+}++void bf_context_end(bf_context_t *s)+{+    bf_clear_cache(s);+}++void bf_init(bf_context_t *s, bf_t *r)+{+    r->ctx = s;+    r->sign = 0;+    r->expn = BF_EXP_ZERO;+    r->len = 0;+    r->tab = NULL;+}++/* return 0 if OK, -1 if alloc error */+int bf_resize(bf_t *r, limb_t len)+{+    limb_t *tab;+    +    if (len != r->len) {+        tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t));+        if (!tab && len != 0)+            return -1;+        r->tab = tab;+        r->len = len;+    }+    return 0;+}++/* return 0 or BF_ST_MEM_ERROR */+int bf_set_ui(bf_t *r, uint64_t a)+{+    r->sign = 0;+    if (a == 0) {+        r->expn = BF_EXP_ZERO;+        bf_resize(r, 0); /* cannot fail */+    } +#if LIMB_BITS == 32+    else if (a <= 0xffffffff)+#else+    else+#endif+    {+        int shift;+        if (bf_resize(r, 1))+            goto fail;+        shift = clz(a);+        r->tab[0] = a << shift;+        r->expn = LIMB_BITS - shift;+    }+#if LIMB_BITS == 32+    else {+        uint32_t a1, a0;+        int shift;+        if (bf_resize(r, 2))+            goto fail;+        a0 = a;+        a1 = a >> 32;+        shift = clz(a1);+        r->tab[0] = a0 << shift;+        r->tab[1] = (a1 << shift) | (a0 >> (LIMB_BITS - shift));+        r->expn = 2 * LIMB_BITS - shift;+    }+#endif+    return 0;+ fail:+    bf_set_nan(r);+    return BF_ST_MEM_ERROR;+}++/* return 0 or BF_ST_MEM_ERROR */+int bf_set_si(bf_t *r, int64_t a)+{+    int ret;++    if (a < 0) {+        ret = bf_set_ui(r, -a);+        r->sign = 1;+    } else {+        ret = bf_set_ui(r, a);+    }+    return ret;+}++void bf_set_nan(bf_t *r)+{+    bf_resize(r, 0); /* cannot fail */+    r->expn = BF_EXP_NAN;+    r->sign = 0;+}++void bf_set_zero(bf_t *r, int is_neg)+{+    bf_resize(r, 0); /* cannot fail */+    r->expn = BF_EXP_ZERO;+    r->sign = is_neg;+}++void bf_set_inf(bf_t *r, int is_neg)+{+    bf_resize(r, 0); /* cannot fail */+    r->expn = BF_EXP_INF;+    r->sign = is_neg;+}++/* return 0 or BF_ST_MEM_ERROR */+int bf_set(bf_t *r, const bf_t *a)+{+    if (r == a)+        return 0;+    if (bf_resize(r, a->len)) {+        bf_set_nan(r);+        return BF_ST_MEM_ERROR;+    }+    r->sign = a->sign;+    r->expn = a->expn;+    memcpy(r->tab, a->tab, a->len * sizeof(limb_t));+    return 0;+}++/* equivalent to bf_set(r, a); bf_delete(a) */+void bf_move(bf_t *r, bf_t *a)+{+    bf_context_t *s = r->ctx;+    if (r == a)+        return;+    bf_free(s, r->tab);+    *r = *a;+}++static limb_t get_limbz(const bf_t *a, limb_t idx)+{+    if (idx >= a->len)+        return 0;+    else+        return a->tab[idx];+}++/* get LIMB_BITS at bit position 'pos' in tab */+static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos)+{+    limb_t i, a0, a1;+    int p;++    i = pos >> LIMB_LOG2_BITS;+    p = pos & (LIMB_BITS - 1);+    if (i < len)+        a0 = tab[i];+    else+        a0 = 0;+    if (p == 0) {+        return a0;+    } else {+        i++;+        if (i < len)+            a1 = tab[i];+        else+            a1 = 0;+        return (a0 >> p) | (a1 << (LIMB_BITS - p));+    }+}++static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos)+{+    slimb_t i;+    i = pos >> LIMB_LOG2_BITS;+    if (i < 0 || i >= len)+        return 0;+    return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1;+}++static inline limb_t limb_mask(int start, int last)+{+    limb_t v;+    int n;+    n = last - start + 1;+    if (n == LIMB_BITS)+        v = -1;+    else+        v = (((limb_t)1 << n) - 1) << start;+    return v;+}++static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n)+{+    mp_size_t i;+    for(i = 0; i < n; i++) {+        if (tab[i] != 0)+            return 1;+    }+    return 0;+}++/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */+static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos)+{+    slimb_t pos;+    limb_t v;+    +    pos = bit_pos >> LIMB_LOG2_BITS;+    if (pos < 0)+        return 0;+    v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1));+    if (v != 0)+        return 1;+    pos--;+    while (pos >= 0) {+        if (r->tab[pos] != 0)+            return 1;+        pos--;+    }+    return 0;+}++/* return the addend for rounding. Note that prec can be <= 0 (for+   BF_FLAG_RADPNT_PREC) */+static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l,+                          slimb_t prec, int rnd_mode)+{+    int add_one, inexact;+    limb_t bit1, bit0;+    +    if (rnd_mode == BF_RNDF) {+        bit0 = 1; /* faithful rounding does not honor the INEXACT flag */+    } else {+        /* starting limb for bit 'prec + 1' */+        bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1));+    }++    /* get the bit at 'prec' */+    bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec);+    inexact = (bit1 | bit0) != 0;+    +    add_one = 0;+    switch(rnd_mode) {+    case BF_RNDZ:+        break;+    case BF_RNDN:+        if (bit1) {+            if (bit0) {+                add_one = 1;+            } else {+                /* round to even */+                add_one =+                    get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1));+            }+        }+        break;+    case BF_RNDD:+    case BF_RNDU:+        if (r->sign == (rnd_mode == BF_RNDD))+            add_one = inexact;+        break;+    case BF_RNDA:+        add_one = inexact;+        break;+    case BF_RNDNA:+    case BF_RNDF:+        add_one = bit1;+        break;+    default:+        abort();+    }+    +    if (inexact)+        *pret |= BF_ST_INEXACT;+    return add_one;+}++static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags)+{+    slimb_t i, l, e_max;+    int rnd_mode;+    +    rnd_mode = flags & BF_RND_MASK;+    if (prec == BF_PREC_INF ||+        rnd_mode == BF_RNDN ||+        rnd_mode == BF_RNDNA ||+        rnd_mode == BF_RNDA ||+        (rnd_mode == BF_RNDD && sign == 1) ||+        (rnd_mode == BF_RNDU && sign == 0)) {+        bf_set_inf(r, sign);+    } else {+        /* set to maximum finite number */+        l = (prec + LIMB_BITS - 1) / LIMB_BITS;+        if (bf_resize(r, l)) {+            bf_set_nan(r);+            return BF_ST_MEM_ERROR;+        }+        r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1),+                              LIMB_BITS - 1);+        for(i = 1; i < l; i++)+            r->tab[i] = (limb_t)-1;+        e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1);+        r->expn = e_max;+        r->sign = sign;+    }+    return BF_ST_OVERFLOW | BF_ST_INEXACT;+}++/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is+   assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be+   infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result+   is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of+   overflow not returning infinity. */+static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l,+                      int ret)+{+    limb_t v, a;+    int shift, add_one, rnd_mode;+    slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec;++    /* e_min and e_max are computed to match the IEEE 754 conventions */+    e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1);+    e_min = -e_range + 3;+    e_max = e_range;+    +    if (flags & BF_FLAG_RADPNT_PREC) {+        /* 'prec' is the precision after the radix point */+        if (prec1 != BF_PREC_INF)+            prec = r->expn + prec1;+        else+            prec = prec1;+    } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {+        /* restrict the precision in case of potentially subnormal+           result */+        assert(prec1 != BF_PREC_INF);+        prec = prec1 - (e_min - r->expn);+    } else {+        prec = prec1;+    }++    /* round to prec bits */+    rnd_mode = flags & BF_RND_MASK;+    add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode);+    +    if (prec <= 0) {+        if (add_one) {+            bf_resize(r, 1); /* cannot fail */+            r->tab[0] = (limb_t)1 << (LIMB_BITS - 1);+            r->expn += 1 - prec;+            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;+            return ret;+        } else {+            goto underflow;+        }+    } else if (add_one) {+        limb_t carry;+        +        /* add one starting at digit 'prec - 1' */+        bit_pos = l * LIMB_BITS - 1 - (prec - 1);+        pos = bit_pos >> LIMB_LOG2_BITS;+        carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1));+        +        for(i = pos; i < l; i++) {+            v = r->tab[i] + carry;+            carry = (v < carry);+            r->tab[i] = v;+            if (carry == 0)+                break;+        }+        if (carry) {+            /* shift right by one digit */+            v = 1;+            for(i = l - 1; i >= pos; i--) {+                a = r->tab[i];+                r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1));+                v = a;+            }+            r->expn++;+        }+    }+    +    /* check underflow */+    if (unlikely(r->expn < e_min)) {+        if (flags & BF_FLAG_SUBNORMAL) {+            /* if inexact, also set the underflow flag */+            if (ret & BF_ST_INEXACT)+                ret |= BF_ST_UNDERFLOW;+        } else {+        underflow:+            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;+            bf_set_zero(r, r->sign);+            return ret;+        }+    }+    +    /* check overflow */+    if (unlikely(r->expn > e_max))+        return bf_set_overflow(r, r->sign, prec1, flags);+    +    /* keep the bits starting at 'prec - 1' */+    bit_pos = l * LIMB_BITS - 1 - (prec - 1);+    i = bit_pos >> LIMB_LOG2_BITS;+    if (i >= 0) {+        shift = bit_pos & (LIMB_BITS - 1);+        if (shift != 0)+            r->tab[i] &= limb_mask(shift, LIMB_BITS - 1);+    } else {+        i = 0;+    }+    /* remove trailing zeros */+    while (r->tab[i] == 0)+        i++;+    if (i > 0) {+        l -= i;+        memmove(r->tab, r->tab + i, l * sizeof(limb_t));+    }+    bf_resize(r, l); /* cannot fail */+    return ret;+}++/* 'r' must be a finite number. */+int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags)+{+    limb_t l, v, a;+    int shift, ret;+    slimb_t i;+    +    //    bf_print_str("bf_renorm", r);+    l = r->len;+    while (l > 0 && r->tab[l - 1] == 0)+        l--;+    if (l == 0) {+        /* zero */+        r->expn = BF_EXP_ZERO;+        bf_resize(r, 0); /* cannot fail */+        ret = 0;+    } else {+        r->expn -= (r->len - l) * LIMB_BITS;+        /* shift to have the MSB set to '1' */+        v = r->tab[l - 1];+        shift = clz(v);+        if (shift != 0) {+            v = 0;+            for(i = 0; i < l; i++) {+                a = r->tab[i];+                r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift));+                v = a;+            }+            r->expn -= shift;+        }+        ret = __bf_round(r, prec1, flags, l, 0);+    }+    //    bf_print_str("r_final", r);+    return ret;+}++/* return true if rounding can be done at precision 'prec' assuming+   the exact result r is such that |r-a| <= 2^(EXP(a)-k). */+/* XXX: check the case where the exponent would be incremented by the+   rounding */+int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k)+{+    BOOL is_rndn;+    slimb_t bit_pos, n;+    limb_t bit;+    +    if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN)+        return FALSE;+    if (rnd_mode == BF_RNDF) {+        return (k >= (prec + 1));+    }+    if (a->expn == BF_EXP_ZERO)+        return FALSE;+    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);+    if (k < (prec + 2))+        return FALSE;+    bit_pos = a->len * LIMB_BITS - 1 - prec;+    n = k - prec;+    /* bit pattern for RNDN or RNDNA: 0111.. or 1000...+       for other rounding modes: 000... or 111... +    */+    bit = get_bit(a->tab, a->len, bit_pos);+    bit_pos--;+    n--;+    bit ^= is_rndn;+    /* XXX: slow, but a few iterations on average */+    while (n != 0) {+        if (get_bit(a->tab, a->len, bit_pos) != bit)+            return TRUE;+        bit_pos--;+        n--;+    }+    return FALSE;+}++/* Cannot fail with BF_ST_MEM_ERROR. */+int bf_round(bf_t *r, limb_t prec, bf_flags_t flags)+{+    if (r->len == 0)+        return 0;+    return __bf_round(r, prec, flags, r->len, 0);+}++/* for debugging */+static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n)+{+    limb_t i;+    printf("%s: len=%" PRId_LIMB "\n", str, n);+    for(i = 0; i < n; i++) {+        printf("%" PRId_LIMB ": " FMT_LIMB "\n",+               i, tab[i]);+    }+}++void mp_print_str(const char *str, const limb_t *tab, limb_t n)+{+    slimb_t i;+    printf("%s= 0x", str);+    for(i = n - 1; i >= 0; i--) {+        if (i != (n - 1))+            printf("_");+        printf(FMT_LIMB, tab[i]);+    }+    printf("\n");+}++static __maybe_unused void mp_print_str_h(const char *str,+                                          const limb_t *tab, limb_t n,+                                          limb_t high)+{+    slimb_t i;+    printf("%s= 0x", str);+    printf(FMT_LIMB, high);+    for(i = n - 1; i >= 0; i--) {+        printf("_");+        printf(FMT_LIMB, tab[i]);+    }+    printf("\n");+}++/* for debugging */+void bf_print_str(const char *str, const bf_t *a)+{+    slimb_t i;+    printf("%s=", str);++    if (a->expn == BF_EXP_NAN) {+        printf("NaN");+    } else {+        if (a->sign)+            putchar('-');+        if (a->expn == BF_EXP_ZERO) {+            putchar('0');+        } else if (a->expn == BF_EXP_INF) {+            printf("Inf");+        } else {+            printf("0x0.");+            for(i = a->len - 1; i >= 0; i--)+                printf(FMT_LIMB, a->tab[i]);+            printf("p%" PRId_LIMB, a->expn);+        }+    }+    printf("\n");+}++/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0+   if a = b and > 0 otherwise. */+int bf_cmpu(const bf_t *a, const bf_t *b)+{+    slimb_t i;+    limb_t len, v1, v2;+    +    if (a->expn != b->expn) {+        if (a->expn < b->expn)+            return -1;+        else+            return 1;+    }+    len = bf_max(a->len, b->len);+    for(i = len - 1; i >= 0; i--) {+        v1 = get_limbz(a, a->len - len + i);+        v2 = get_limbz(b, b->len - len + i);+        if (v1 != v2) {+            if (v1 < v2)+                return -1;+            else+                return 1;+        }+    }+    return 0;+}++/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */+int bf_cmp_full(const bf_t *a, const bf_t *b)+{+    int res;+    +    if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+        if (a->expn == b->expn)+            res = 0;+        else if (a->expn == BF_EXP_NAN)+            res = 1;+        else+            res = -1;+    } else if (a->sign != b->sign) {+        res = 1 - 2 * a->sign;+    } else {+        res = bf_cmpu(a, b);+        if (a->sign)+            res = -res;+    }+    return res;+}++/* Standard floating point comparison: return 2 if one of the operands+   is NaN (unordered) or -1, 0, 1 depending on the ordering assuming+   -0 == +0 */+int bf_cmp(const bf_t *a, const bf_t *b)+{+    int res;+    +    if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+        res = 2;+    } else if (a->sign != b->sign) {+        if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO)+            res = 0;+        else+            res = 1 - 2 * a->sign;+    } else {+        res = bf_cmpu(a, b);+        if (a->sign)+            res = -res;+    }+    return res;+}++/* Compute the number of bits 'n' matching the pattern:+   a= X1000..0+   b= X0111..1+              +   When computing a-b, the result will have at least n leading zero+   bits.++   Precondition: a > b and a.expn - b.expn = 0 or 1+*/+static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b)+{+    slimb_t bit_offset, b_offset, n;+    int p, p1;+    limb_t v1, v2, mask;++    bit_offset = a->len * LIMB_BITS - 1;+    b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) ++        a->expn - b->expn;+    n = 0;++    /* first search the equals bits */+    for(;;) {+        v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS);+        v2 = get_bits(b->tab, b->len, bit_offset + b_offset);+        //        printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2);+        if (v1 != v2)+            break;+        n += LIMB_BITS;+        bit_offset -= LIMB_BITS;+    }+    /* find the position of the first different bit */+    p = clz(v1 ^ v2) + 1;+    n += p;+    /* then search for '0' in a and '1' in b */+    p = LIMB_BITS - p;+    if (p > 0) {+        /* search in the trailing p bits of v1 and v2 */+        mask = limb_mask(0, p - 1);+        p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p);+        n += p1;+        if (p1 != p)+            goto done;+    }+    bit_offset -= LIMB_BITS;+    for(;;) {+        v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS);+        v2 = get_bits(b->tab, b->len, bit_offset + b_offset);+        //        printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2);+        if (v1 != 0 || v2 != -1) {+            /* different: count the matching bits */+            p1 = bf_min(clz(v1), clz(~v2));+            n += p1;+            break;+        }+        n += LIMB_BITS;+        bit_offset -= LIMB_BITS;+    }+ done:+    return n;+}++static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+                           bf_flags_t flags, int b_neg)+{+    const bf_t *tmp;+    int is_sub, ret, cmp_res, a_sign, b_sign;++    a_sign = a->sign;+    b_sign = b->sign ^ b_neg;+    is_sub = a_sign ^ b_sign;+    cmp_res = bf_cmpu(a, b);+    if (cmp_res < 0) {+        tmp = a;+        a = b;+        b = tmp;+        a_sign = b_sign; /* b_sign is never used later */+    }+    /* abs(a) >= abs(b) */+    if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) {+        /* zero result */+        bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD);+        ret = 0;+    } else if (a->len == 0 || b->len == 0) {+        ret = 0;+        if (a->expn >= BF_EXP_INF) {+            if (a->expn == BF_EXP_NAN) {+                /* at least one operand is NaN */+                bf_set_nan(r);+            } else if (b->expn == BF_EXP_INF && is_sub) {+                /* infinities with different signs */+                bf_set_nan(r);+                ret = BF_ST_INVALID_OP;+            } else {+                bf_set_inf(r, a_sign);+            }+        } else {+            /* at least one zero and not subtract */+            bf_set(r, a);+            r->sign = a_sign;+            goto renorm;+        }+    } else {+        slimb_t d, a_offset, b_bit_offset, i, cancelled_bits;+        limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask;++        r->sign = a_sign;+        r->expn = a->expn;+        d = a->expn - b->expn;+        /* must add more precision for the leading cancelled bits in+           subtraction */+        if (is_sub) {+            if (d <= 1)+                cancelled_bits = count_cancelled_bits(a, b);+            else+                cancelled_bits = 1;+        } else {+            cancelled_bits = 0;+        }+        +        /* add two extra bits for rounding */+        precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS;+        tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS);+        r_len = bf_min(precl, tot_len);+        if (bf_resize(r, r_len))+            goto fail;+        a_offset = a->len - r_len;+        b_bit_offset = (b->len - r_len) * LIMB_BITS + d;++        /* compute the bits before for the rounding */+        carry = is_sub;+        z = 0;+        sub_mask = -is_sub;+        i = r_len - tot_len;+        while (i < 0) {+            slimb_t ap, bp;+            BOOL inflag;+            +            ap = a_offset + i;+            bp = b_bit_offset + i * LIMB_BITS;+            inflag = FALSE;+            if (ap >= 0 && ap < a->len) {+                v1 = a->tab[ap];+                inflag = TRUE;+            } else {+                v1 = 0;+            }+            if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) {+                v2 = get_bits(b->tab, b->len, bp);+                inflag = TRUE;+            } else {+                v2 = 0;+            }+            if (!inflag) {+                /* outside 'a' and 'b': go directly to the next value+                   inside a or b so that the running time does not+                   depend on the exponent difference */+                i = 0;+                if (ap < 0)+                    i = bf_min(i, -a_offset);+                /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1+                   equivalent to +                   i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS)+                */+                if (bp + LIMB_BITS <= 0)+                    i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS);+            } else {+                i++;+            }+            v2 ^= sub_mask;+            u = v1 + v2;+            carry1 = u < v1;+            u += carry;+            carry = (u < carry) | carry1;+            z |= u;+        }+        /* and the result */+        for(i = 0; i < r_len; i++) {+            v1 = get_limbz(a, a_offset + i);+            v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS);+            v2 ^= sub_mask;+            u = v1 + v2;+            carry1 = u < v1;+            u += carry;+            carry = (u < carry) | carry1;+            r->tab[i] = u;+        }+        /* set the extra bits for the rounding */+        r->tab[0] |= (z != 0);++        /* carry is only possible in add case */+        if (!is_sub && carry) {+            if (bf_resize(r, r_len + 1))+                goto fail;+            r->tab[r_len] = 1;+            r->expn += LIMB_BITS;+        }+    renorm:+        ret = bf_normalize_and_round(r, prec, flags);+    }+    return ret;+ fail:+    bf_set_nan(r);+    return BF_ST_MEM_ERROR;+}++static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+                     bf_flags_t flags)+{+    return bf_add_internal(r, a, b, prec, flags, 0);+}++static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+                     bf_flags_t flags)+{+    return bf_add_internal(r, a, b, prec, flags, 1);+}++limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, +              limb_t n, limb_t carry)+{+    slimb_t i;+    limb_t k, a, v, k1;+    +    k = carry;+    for(i=0;i<n;i++) {+        v = op1[i];+        a = v + op2[i];+        k1 = a < v;+        a = a + k;+        k = (a < k) | k1;+        res[i] = a;+    }+    return k;+}++limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n)+{+    size_t i;+    limb_t k, a;++    k=b;+    for(i=0;i<n;i++) {+        if (k == 0)+            break;+        a = tab[i] + k;+        k = (a < k);+        tab[i] = a;+    }+    return k;+}++limb_t mp_sub(limb_t *res, const limb_t *op1, const limb_t *op2, +              mp_size_t n, limb_t carry)+{+    int i;+    limb_t k, a, v, k1;+    +    k = carry;+    for(i=0;i<n;i++) {+        v = op1[i];+        a = v - op2[i];+        k1 = a > v;+        v = a - k;+        k = (v > a) | k1;+        res[i] = v;+    }+    return k;+}++/* compute 0 - op2 */+static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry)+{+    int i;+    limb_t k, a, v, k1;+    +    k = carry;+    for(i=0;i<n;i++) {+        v = 0;+        a = v - op2[i];+        k1 = a > v;+        v = a - k;+        k = (v > a) | k1;+        res[i] = v;+    }+    return k;+}++limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n)+{+    mp_size_t i;+    limb_t k, a, v;+    +    k=b;+    for(i=0;i<n;i++) {+        v = tab[i];+        a = v - k;+        k = a > v;+        tab[i] = a;+        if (k == 0)+            break;+    }+    return k;+}++/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). +   1 <= shift <= LIMB_BITS - 1 */+static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, +                     int shift, limb_t high)+{+    mp_size_t i;+    limb_t l, a;++    assert(shift >= 1 && shift < LIMB_BITS);+    l = high;+    for(i = n - 1; i >= 0; i--) {+        a = tab[i];+        tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift));+        l = a;+    }+    return l & (((limb_t)1 << shift) - 1);+}++/* tabr[] = taba[] * b + l. Return the high carry */+static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, +                      limb_t b, limb_t l)+{+    limb_t i;+    dlimb_t t;++    for(i = 0; i < n; i++) {+        t = (dlimb_t)taba[i] * (dlimb_t)b + l;+        tabr[i] = t;+        l = t >> LIMB_BITS;+    }+    return l;+}++/* tabr[] += taba[] * b, return the high word. */+static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n,+                          limb_t b)+{+    limb_t i, l;+    dlimb_t t;+    +    l = 0;+    for(i = 0; i < n; i++) {+        t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i];+        tabr[i] = t;+        l = t >> LIMB_BITS;+    }+    return l;+}++/* size of the result : op1_size + op2_size. */+static void mp_mul_basecase(limb_t *result, +                            const limb_t *op1, limb_t op1_size, +                            const limb_t *op2, limb_t op2_size) +{+    limb_t i, r;+    +    result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);+    for(i=1;i<op2_size;i++) {+        r = mp_add_mul1(result + i, op1, op1_size, op2[i]);+        result[i + op1_size] = r;+    }+}++/* return 0 if OK, -1 if memory error */+/* XXX: change API so that result can be allocated */+int mp_mul(bf_context_t *s, limb_t *result, +           const limb_t *op1, limb_t op1_size, +           const limb_t *op2, limb_t op2_size) +{+#ifdef USE_FFT_MUL+    if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) {+        bf_t r_s, *r = &r_s;+        r->tab = result;+        /* XXX: optimize memory usage in API */+        if (fft_mul(s, r, (limb_t *)op1, op1_size,+                    (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE))+            return -1;+    } else+#endif+    {+        mp_mul_basecase(result, op1, op1_size, op2, op2_size);+    }+    return 0;+}++/* tabr[] -= taba[] * b. Return the value to substract to the high+   word. */+static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n,+                          limb_t b)+{+    limb_t i, l;+    dlimb_t t;+    +    l = 0;+    for(i = 0; i < n; i++) {+        t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l;+        tabr[i] = t;+        l = -(t >> LIMB_BITS);+    }+    return l;+}++/* WARNING: d must be >= 2^(LIMB_BITS-1) */+static inline limb_t udiv1norm_init(limb_t d)+{+    limb_t a0, a1;+    a1 = -d - 1;+    a0 = -1;+    return (((dlimb_t)a1 << LIMB_BITS) | a0) / d;+}++/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0+   / d' with 0 <= a1 < d. */+static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0,+                                limb_t d, limb_t d_inv)+{+    limb_t n1m, n_adj, q, r, ah;+    dlimb_t a;+    n1m = ((slimb_t)a0 >> (LIMB_BITS - 1));+    n_adj = a0 + (n1m & d);+    a = (dlimb_t)d_inv * (a1 - n1m) + n_adj;+    q = (a >> LIMB_BITS) + a1;+    /* compute a - q * r and update q so that the remainder is\+       between 0 and d - 1 */+    a = ((dlimb_t)a1 << LIMB_BITS) | a0;+    a = a - (dlimb_t)q * d - d;+    ah = a >> LIMB_BITS;+    q += 1 + ah;+    r = (limb_t)a + (ah & d);+    *pr = r;+    return q;+}++/* b must be >= 1 << (LIMB_BITS - 1) */+static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n,+                          limb_t b, limb_t r)+{+    slimb_t i;++    if (n >= UDIV1NORM_THRESHOLD) {+        limb_t b_inv;+        b_inv = udiv1norm_init(b);+        for(i = n - 1; i >= 0; i--) {+            tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);+        }+    } else {+        dlimb_t a1;+        for(i = n - 1; i >= 0; i--) {+            a1 = ((dlimb_t)r << LIMB_BITS) | taba[i];+            tabr[i] = a1 / b;+            r = a1 % b;+        }+    }+    return r;+}++static int mp_divnorm_large(bf_context_t *s, +                            limb_t *tabq, limb_t *taba, limb_t na, +                            const limb_t *tabb, limb_t nb);++/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb+   - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba'+   is modified and contains the remainder (nb limbs). tabq[0..na-nb]+   contains the quotient with tabq[na - nb] <= 1. */+static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, +                      const limb_t *tabb, limb_t nb)+{+    limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;+    slimb_t i, j;++    b1 = tabb[nb - 1];+    if (nb == 1) {+        taba[0] = mp_div1norm(tabq, taba, na, b1, 0);+        return 0;+    }+    n = na - nb;+    if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) {+        return mp_divnorm_large(s, tabq, taba, na, tabb, nb);+    }+    +    if (n >= UDIV1NORM_THRESHOLD)+        b1_inv = udiv1norm_init(b1);+    else+        b1_inv = 0;++    /* first iteration: the quotient is only 0 or 1 */+    q = 1;+    for(j = nb - 1; j >= 0; j--) {+        if (taba[n + j] != tabb[j]) {+            if (taba[n + j] < tabb[j])+                q = 0;+            break;+        }+    }+    tabq[n] = q;+    if (q) {+        mp_sub(taba + n, taba + n, tabb, nb, 0);+    }+    +    for(i = n - 1; i >= 0; i--) {+        if (unlikely(taba[i + nb] >= b1)) {+            q = -1;+        } else if (b1_inv) {+            q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);+        } else {+            dlimb_t al;+            al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1];+            q = al / b1;+            r = al % b1;+        }+        r = mp_sub_mul1(taba + i, tabb, nb, q);++        v = taba[i + nb];+        a = v - r;+        c = (a > v);+        taba[i + nb] = a;++        if (c != 0) {+            /* negative result */+            for(;;) {+                q--;+                c = mp_add(taba + i, taba + i, tabb, nb, 0);+                /* propagate carry and test if positive result */+                if (c != 0) {+                    if (++taba[i + nb] == 0) {+                        break;+                    }+                }+            }+        }+        tabq[i] = q;+    }+    return 0;+}++/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a'+   has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1.+   +   See Modern Computer Arithmetic by Richard P. Brent and Paul+   Zimmermann, algorithm 3.5 */+int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n)+{+    mp_size_t l, h, k, i;+    limb_t *tabxh, *tabt, c, *tabu;+    +    if (n <= 2) {+        /* return ceil(B^(2*n)/a) - 1 */+        /* XXX: could avoid allocation */+        tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1));+        tabt = bf_malloc(s, sizeof(limb_t) * (n + 2));+        if (!tabt || !tabu)+            goto fail;+        for(i = 0; i < 2 * n; i++)+            tabu[i] = 0;+        tabu[2 * n] = 1;+        if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n))+            goto fail;+        for(i = 0; i < n + 1; i++)+            tabr[i] = tabt[i];+        if (mp_scan_nz(tabu, n) == 0) {+            /* only happens for a=B^n/2 */+            mp_sub_ui(tabr, 1, n + 1);+        }+    } else {+        l = (n - 1) / 2;+        h = n - l;+        /* n=2p  -> l=p-1, h = p + 1, k = p + 3+           n=2p+1-> l=p,  h = p + 1; k = p + 2+        */+        tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1));+        tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2));+        if (!tabt || !tabu)+            goto fail;+        tabxh = tabr + l;+        if (mp_recip(s, tabxh, taba + l, h))+            goto fail;+        if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */+            goto fail;+        while (tabt[n + h] != 0) {+            mp_sub_ui(tabxh, 1, h + 1);+            c = mp_sub(tabt, tabt, taba, n, 0);+            mp_sub_ui(tabt + n, c, h + 1);+        }+        /* T = B^(n+h) - T */+        mp_neg(tabt, tabt, n + h + 1, 0);+        tabt[n + h]++;+        if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1))+            goto fail;+        /* n + 2*h - l + 2 limbs */+        k = 2 * h - l;+        for(i = 0; i < l; i++)+            tabr[i] = tabu[i + k];+        mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0);+    }+    bf_free(s, tabt);+    bf_free(s, tabu);+    return 0;+ fail:+    bf_free(s, tabt);+    bf_free(s, tabu);+    return -1;+}++/* return -1, 0 or 1 */+static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n)+{+    mp_size_t i;+    for(i = n - 1; i >= 0; i--) {+        if (taba[i] != tabb[i]) {+            if (taba[i] < tabb[i])+                return -1;+            else+                return 1;+        }+    }+    return 0;+}++//#define DEBUG_DIVNORM_LARGE+//#define DEBUG_DIVNORM_LARGE2++/* subquadratic divnorm */+static int mp_divnorm_large(bf_context_t *s, +                            limb_t *tabq, limb_t *taba, limb_t na, +                            const limb_t *tabb, limb_t nb)+{+    limb_t *tabb_inv, nq, *tabt, i, n;+    nq = na - nb;+#ifdef DEBUG_DIVNORM_LARGE+    printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq);+    mp_print_str("a", taba, na);+    mp_print_str("b", tabb, nb);+#endif+    assert(nq >= 1);+    n = nq;+    if (nq < nb)+        n++; +    tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1));+    tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1));+    if (!tabb_inv || !tabt)+        goto fail;++    if (n >= nb) {+        for(i = 0; i < n - nb; i++)+            tabt[i] = 0;+        for(i = 0; i < nb; i++)+            tabt[i + n - nb] = tabb[i];+    } else {+        /* truncate B: need to increment it so that the approximate+           inverse is smaller that the exact inverse */+        for(i = 0; i < n; i++)+            tabt[i] = tabb[i + nb - n];+        if (mp_add_ui(tabt, 1, n)) {+            /* tabt = B^n : tabb_inv = B^n */+            memset(tabb_inv, 0, n * sizeof(limb_t));+            tabb_inv[n] = 1;+            goto recip_done;+        }+    }+    if (mp_recip(s, tabb_inv, tabt, n))+        goto fail;+ recip_done:+    /* Q=A*B^-1 */+    if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1))+        goto fail;+    +    for(i = 0; i < nq + 1; i++)+        tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)];+#ifdef DEBUG_DIVNORM_LARGE+    mp_print_str("q", tabq, nq + 1);+#endif++    bf_free(s, tabt);+    bf_free(s, tabb_inv);+    tabb_inv = NULL;+    +    /* R=A-B*Q */+    tabt = bf_malloc(s, sizeof(limb_t) * (na + 1));+    if (!tabt)+        goto fail;+    if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb))+        goto fail;+    /* we add one more limb for the result */+    mp_sub(taba, taba, tabt, nb + 1, 0);+    bf_free(s, tabt);+    /* the approximated quotient is smaller than than the exact one,+       hence we may have to increment it */+#ifdef DEBUG_DIVNORM_LARGE2+    int cnt = 0;+    static int cnt_max;+#endif+    for(;;) {+        if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0)+            break;+        taba[nb] -= mp_sub(taba, taba, tabb, nb, 0);+        mp_add_ui(tabq, 1, nq + 1);+#ifdef DEBUG_DIVNORM_LARGE2+        cnt++;+#endif+    }+#ifdef DEBUG_DIVNORM_LARGE2+    if (cnt > cnt_max) {+        cnt_max = cnt;+        printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb);+    }+#endif+    return 0;+ fail:+    bf_free(s, tabb_inv);+    bf_free(s, tabt);+    return -1;+}++int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+           bf_flags_t flags)+{+    int ret, r_sign;++    if (a->len < b->len) {+        const bf_t *tmp = a;+        a = b;+        b = tmp;+    }+    r_sign = a->sign ^ b->sign;+    /* here b->len <= a->len */+    if (b->len == 0) {+        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            ret = 0;+        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) {+            if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) ||+                (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) {+                bf_set_nan(r);+                ret = BF_ST_INVALID_OP;+            } else {+                bf_set_inf(r, r_sign);+                ret = 0;+            }+        } else {+            bf_set_zero(r, r_sign);+            ret = 0;+        }+    } else {+        bf_t tmp, *r1 = NULL;+        limb_t a_len, b_len, precl;+        limb_t *a_tab, *b_tab;+            +        a_len = a->len;+        b_len = b->len;+        +        if ((flags & BF_RND_MASK) == BF_RNDF) {+            /* faithful rounding does not require using the full inputs */+            precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS;+            a_len = bf_min(a_len, precl);+            b_len = bf_min(b_len, precl);+        }+        a_tab = a->tab + a->len - a_len;+        b_tab = b->tab + b->len - b_len;+        +#ifdef USE_FFT_MUL+        if (b_len >= FFT_MUL_THRESHOLD) {+            int mul_flags = 0;+            if (r == a)+                mul_flags |= FFT_MUL_R_OVERLAP_A;+            if (r == b)+                mul_flags |= FFT_MUL_R_OVERLAP_B;+            if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags))+                goto fail;+        } else+#endif+        {+            if (r == a || r == b) {+                bf_init(r->ctx, &tmp);+                r1 = r;+                r = &tmp;+            }+            if (bf_resize(r, a_len + b_len)) {+            fail:+                bf_set_nan(r);+                ret = BF_ST_MEM_ERROR;+                goto done;+            }+            mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len);+        }+        r->sign = r_sign;+        r->expn = a->expn + b->expn;+        ret = bf_normalize_and_round(r, prec, flags);+    done:+        if (r == &tmp)+            bf_move(r1, &tmp);+    }+    return ret;+}++/* multiply 'r' by 2^e */+int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags)+{+    slimb_t e_max;+    if (r->len == 0)+        return 0;+    e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1;+    e = bf_max(e, -e_max);+    e = bf_min(e, e_max);+    r->expn += e;+    return __bf_round(r, prec, flags, r->len, 0);+}++/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero,+   Infinite or Nan. */+slimb_t bf_get_exp_min(const bf_t *a)+{+    slimb_t i;+    limb_t v;+    int k;+    +    for(i = 0; i < a->len; i++) {+        v = a->tab[i];+        if (v != 0) {+            k = ctz(v);+            return a->expn - (a->len - i) * LIMB_BITS + k;+        }+    }+    return 0;+}++/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the+   integer defined as floor(a/b) and r = a - q * b. */+static void bf_tdivremu(bf_t *q, bf_t *r,+                        const bf_t *a, const bf_t *b)+{+    if (bf_cmpu(a, b) < 0) {+        bf_set_ui(q, 0);+        bf_set(r, a);+    } else {+        bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ);+        bf_rint(q, BF_RNDZ);+        bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ);+        bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ);+    }+}++static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+                    bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    int ret, r_sign;+    limb_t n, nb, precl;+    +    r_sign = a->sign ^ b->sign;+    if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) {+        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else if (a->expn == BF_EXP_INF) {+            bf_set_inf(r, r_sign);+            return 0;+        } else {+            bf_set_zero(r, r_sign);+            return 0;+        }+    } else if (a->expn == BF_EXP_ZERO) {+        if (b->expn == BF_EXP_ZERO) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_set_zero(r, r_sign);+            return 0;+        }+    } else if (b->expn == BF_EXP_ZERO) {+        bf_set_inf(r, r_sign);+        return BF_ST_DIVIDE_ZERO;+    }++    /* number of limbs of the quotient (2 extra bits for rounding) */+    precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS;+    nb = b->len;+    n = bf_max(a->len, precl);+    +    {+        limb_t *taba, na;+        slimb_t d;+        +        na = n + nb;+        taba = bf_malloc(s, (na + 1) * sizeof(limb_t));+        if (!taba)+            goto fail;+        d = na - a->len;+        memset(taba, 0, d * sizeof(limb_t));+        memcpy(taba + d, a->tab, a->len * sizeof(limb_t));+        if (bf_resize(r, n + 1))+            goto fail1;+        if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) {+        fail1:+            bf_free(s, taba);+            goto fail;+        }+        /* see if non zero remainder */+        if (mp_scan_nz(taba, nb))+            r->tab[0] |= 1;+        bf_free(r->ctx, taba);+        r->expn = a->expn - b->expn + LIMB_BITS;+        r->sign = r_sign;+        ret = bf_normalize_and_round(r, prec, flags);+    }+    return ret;+ fail:+    bf_set_nan(r);+    return BF_ST_MEM_ERROR;+}++/* division and remainder. +   +   rnd_mode is the rounding mode for the quotient. The additional+   rounding mode BF_RND_EUCLIDIAN is supported.++   'q' is an integer. 'r' is rounded with prec and flags (prec can be+   BF_PREC_INF).+*/+int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,+              limb_t prec, bf_flags_t flags, int rnd_mode)+{+    bf_t a1_s, *a1 = &a1_s;+    bf_t b1_s, *b1 = &b1_s;+    int q_sign, ret;+    BOOL is_ceil, is_rndn;+    +    assert(q != a && q != b);+    assert(r != a && r != b);+    assert(q != r);+    +    if (a->len == 0 || b->len == 0) {+        bf_set_zero(q, 0);+        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_set(r, a);+            return bf_round(r, prec, flags);+        }+    }++    q_sign = a->sign ^ b->sign;+    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);+    switch(rnd_mode) {+    default:+    case BF_RNDZ:+    case BF_RNDN:+    case BF_RNDNA:+        is_ceil = FALSE;+        break;+    case BF_RNDD:+        is_ceil = q_sign;+        break;+    case BF_RNDU:+        is_ceil = q_sign ^ 1;+        break;+    case BF_RNDA:+        is_ceil = TRUE;+        break;+    case BF_DIVREM_EUCLIDIAN:+        is_ceil = a->sign;+        break;+    }++    a1->expn = a->expn;+    a1->tab = a->tab;+    a1->len = a->len;+    a1->sign = 0;+    +    b1->expn = b->expn;+    b1->tab = b->tab;+    b1->len = b->len;+    b1->sign = 0;++    /* XXX: could improve to avoid having a large 'q' */+    bf_tdivremu(q, r, a1, b1);+    if (bf_is_nan(q) || bf_is_nan(r))+        goto fail;++    if (r->len != 0) {+        if (is_rndn) {+            int res;+            b1->expn--;+            res = bf_cmpu(r, b1);+            b1->expn++;+            if (res > 0 ||+                (res == 0 &&+                 (rnd_mode == BF_RNDNA ||+                  get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) {+                goto do_sub_r;+            }+        } else if (is_ceil) {+        do_sub_r:+            ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);+            ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);+            if (ret & BF_ST_MEM_ERROR)+                goto fail;+        }+    }++    r->sign ^= a->sign;+    q->sign = q_sign;+    return bf_round(r, prec, flags);+ fail:+    bf_set_nan(q);+    bf_set_nan(r);+    return BF_ST_MEM_ERROR;+}++int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+           bf_flags_t flags, int rnd_mode)+{+    bf_t q_s, *q = &q_s;+    int ret;+    +    bf_init(r->ctx, q);+    ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);+    bf_delete(q);+    return ret;+}++static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags)+{+#if LIMB_BITS == 32+    return bf_get_int32(pres, a, flags);+#else+    return bf_get_int64(pres, a, flags);+#endif+}++int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+              bf_flags_t flags, int rnd_mode)+{+    bf_t q_s, *q = &q_s;+    int ret;+    +    bf_init(r->ctx, q);+    ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);+    bf_get_limb(pq, q, BF_GET_INT_MOD);+    bf_delete(q);+    return ret;+}++static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m)+{+    dlimb_t t;+    t = (dlimb_t)a * (dlimb_t)b;+    return t % m;+}++#if defined(USE_MUL_CHECK)+static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r)+{+    slimb_t i;+    dlimb_t t;++    for(i = n - 1; i >= 0; i--) {+        t = ((dlimb_t)r << LIMB_BITS) | tab[i];+        r = t % m;+    }+    return r;+}+#endif++static const uint16_t sqrt_table[192] = {+128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255,+};++/* a >= 2^(LIMB_BITS - 2).  Return (s, r) with s=floor(sqrt(a)) and+   r=a-s^2. 0 <= r <= 2 * s */+static limb_t mp_sqrtrem1(limb_t *pr, limb_t a)+{+    limb_t s1, r1, s, r, q, u, num;+    +    /* use a table for the 16 -> 8 bit sqrt */+    s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64];+    r1 = (a >> (LIMB_BITS - 16)) - s1 * s1;+    if (r1 > 2 * s1) {+        r1 -= 2 * s1 + 1;+        s1++;+    }+    +    /* one iteration to get a 32 -> 16 bit sqrt */+    num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff);+    q = num / (2 * s1); /* q <= 2^8 */+    u = num % (2 * s1);+    s = (s1 << 8) + q;+    r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff);+    r -= q * q;+    if ((slimb_t)r < 0) {+        s--;+        r += 2 * s + 1;+    }++#if LIMB_BITS == 64+    s1 = s;+    r1 = r;+    /* one more iteration for 64 -> 32 bit sqrt */+    num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff);+    q = num / (2 * s1); /* q <= 2^16 */+    u = num % (2 * s1);+    s = (s1 << 16) + q;+    r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff);+    r -= q * q;+    if ((slimb_t)r < 0) {+        s--;+        r += 2 * s + 1;+    }+#endif+    *pr = r;+    return s;+}++/* return floor(sqrt(a)) */+limb_t bf_isqrt(limb_t a)+{+    limb_t s, r;+    int k;++    if (a == 0)+        return 0;+    k = clz(a) & ~1;+    s = mp_sqrtrem1(&r, a << k);+    s >>= (k >> 1);+    return s;+}++static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba)+{+    limb_t s1, r1, s, q, u, a0, a1;+    dlimb_t r, num;+    int l;++    a0 = taba[0];+    a1 = taba[1];+    s1 = mp_sqrtrem1(&r1, a1);+    l = LIMB_BITS / 2;+    num = ((dlimb_t)r1 << l) | (a0 >> l);+    q = num / (2 * s1);+    u = num % (2 * s1);+    s = (s1 << l) + q;+    r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1));+    if (unlikely((q >> l) != 0))+        r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */+    else+        r -= q * q;+    if ((slimb_t)(r >> LIMB_BITS) < 0) {+        s--;+        r += 2 * (dlimb_t)s + 1;+    }+    tabs[0] = s;+    taba[0] = r;+    return r >> LIMB_BITS;+}++//#define DEBUG_SQRTREM++/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest+   limb of the remainder. */+static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n,+                          limb_t *tmp_buf, limb_t *prh)+{+    limb_t l, h, rh, ql, qh, c, i;+    +    if (n == 1) {+        *prh = mp_sqrtrem2(tabs, taba);+        return 0;+    }+#ifdef DEBUG_SQRTREM+    mp_print_str("a", taba, 2 * n);+#endif+    l = n / 2;+    h = n - l;+    if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh))+        return -1;+#ifdef DEBUG_SQRTREM+    mp_print_str("s1", tabs + l, h);+    mp_print_str_h("r1", taba + 2 * l, h, qh);+    mp_print_str_h("r2", taba + l, n, qh);+#endif+    +    /* the remainder is in taba + 2 * l. Its high bit is in qh */+    if (qh) {+        mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0);+    }+    /* instead of dividing by 2*s, divide by s (which is normalized)+       and update q and r */+    if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h))+        return -1;+    qh += tmp_buf[l];+    for(i = 0; i < l; i++)+        tabs[i] = tmp_buf[i];+    ql = mp_shr(tabs, tabs, l, 1, qh & 1);+    qh = qh >> 1; /* 0 or 1 */+    if (ql)+        rh = mp_add(taba + l, taba + l, tabs + l, h, 0);+    else+        rh = 0;+#ifdef DEBUG_SQRTREM+    mp_print_str_h("q", tabs, l, qh);+    mp_print_str_h("u", taba + l, h, rh);+#endif+    +    mp_add_ui(tabs + l, qh, h);+#ifdef DEBUG_SQRTREM+    mp_print_str_h("s2", tabs, n, sh);+#endif+    +    /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */+    /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */+    if (qh) {+        c = qh;+    } else {+        if (mp_mul(s, taba + n, tabs, l, tabs, l))+            return -1;+        c = mp_sub(taba, taba, taba + n, 2 * l, 0);+    }+    rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l);+    if ((slimb_t)rh < 0) {+        mp_sub_ui(tabs, 1, n);+        rh += mp_add_mul1(taba, tabs, n, 2);+        rh += mp_add_ui(taba, 1, n);+    }+    *prh = rh;+    return 0;+}++/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS+   - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2+   * s. tabs has n limbs. r is returned in the lower n limbs of+   taba. Its r[n] is the returned value of the function. */+/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and+   inspirated from its GMP implementation */+int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n)+{+    limb_t tmp_buf1[8];+    limb_t *tmp_buf;+    mp_size_t n2;+    int ret;+    n2 = n / 2 + 1;+    if (n2 <= countof(tmp_buf1)) {+        tmp_buf = tmp_buf1;+    } else {+        tmp_buf = bf_malloc(s, sizeof(limb_t) * n2);+        if (!tmp_buf)+            return -1;+    }+    ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n);+    if (tmp_buf != tmp_buf1)+        bf_free(s, tmp_buf);+    return ret;+}++/* Integer square root with remainder. 'a' must be an integer. r =+   floor(sqrt(a)) and rem = a - r^2.  BF_ST_INEXACT is set if the result+   is inexact. 'rem' can be NULL if the remainder is not needed. */+int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a)+{+    int ret;+    +    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+        } else if (a->expn == BF_EXP_INF && a->sign) {+            goto invalid_op;+        } else {+            bf_set(r, a);+        }+        if (rem1)+            bf_set_ui(rem1, 0);+        ret = 0;+    } else if (a->sign) {+ invalid_op:+        bf_set_nan(r);+        if (rem1)+            bf_set_ui(rem1, 0);+        ret = BF_ST_INVALID_OP;+    } else {+        bf_t rem_s, *rem;+        +        bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ);+        bf_rint(r, BF_RNDZ);+        /* see if the result is exact by computing the remainder */+        if (rem1) {+            rem = rem1;+        } else {+            rem = &rem_s;+            bf_init(r->ctx, rem);+        }+        /* XXX: could avoid recomputing the remainder */+        bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ);+        bf_neg(rem);+        bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ);+        if (bf_is_nan(rem)) {+            ret = BF_ST_MEM_ERROR;+            goto done;+        }+        if (rem->len != 0) {+            ret = BF_ST_INEXACT;+        } else {+            ret = 0;+        }+    done:+        if (!rem1)+            bf_delete(rem);+    }+    return ret;+}++int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = a->ctx;+    int ret;++    assert(r != a);++    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+        } else if (a->expn == BF_EXP_INF && a->sign) {+            goto invalid_op;+        } else {+            bf_set(r, a);+        }+        ret = 0;+    } else if (a->sign) {+ invalid_op:+        bf_set_nan(r);+        ret = BF_ST_INVALID_OP;+    } else {+        limb_t *a1;+        slimb_t n, n1;+        limb_t res;+        +        /* convert the mantissa to an integer with at least 2 *+           prec + 4 bits */+        n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS);+        if (bf_resize(r, n))+            goto fail;+        a1 = bf_malloc(s, sizeof(limb_t) * 2 * n);+        if (!a1)+            goto fail;+        n1 = bf_min(2 * n, a->len);+        memset(a1, 0, (2 * n - n1) * sizeof(limb_t));+        memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t));+        if (a->expn & 1) {+            res = mp_shr(a1, a1, 2 * n, 1, 0);+        } else {+            res = 0;+        }+        if (mp_sqrtrem(s, r->tab, a1, n)) {+            bf_free(s, a1);+            goto fail;+        }+        if (!res) {+            res = mp_scan_nz(a1, n + 1);+        }+        bf_free(s, a1);+        if (!res) {+            res = mp_scan_nz(a->tab, a->len - n1);+        }+        if (res != 0)+            r->tab[0] |= 1;+        r->sign = 0;+        r->expn = (a->expn + 1) >> 1;+        ret = bf_round(r, prec, flags);+    }+    return ret;+ fail:+    bf_set_nan(r);+    return BF_ST_MEM_ERROR;+}++static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+                            bf_flags_t flags, bf_op2_func_t *func)+{+    bf_t tmp;+    int ret;+    +    if (r == a || r == b) {+        bf_init(r->ctx, &tmp);+        ret = func(&tmp, a, b, prec, flags);+        bf_move(r, &tmp);+    } else {+        ret = func(r, a, b, prec, flags);+    }+    return ret;+}++int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+            bf_flags_t flags)+{+    return bf_op2(r, a, b, prec, flags, __bf_add);+}++int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+            bf_flags_t flags)+{+    return bf_op2(r, a, b, prec, flags, __bf_sub);+}++int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,+           bf_flags_t flags)+{+    return bf_op2(r, a, b, prec, flags, __bf_div);+}++int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec,+               bf_flags_t flags)+{+    bf_t b;+    int ret;+    bf_init(r->ctx, &b);+    ret = bf_set_ui(&b, b1);+    ret |= bf_mul(r, a, &b, prec, flags);+    bf_delete(&b);+    return ret;+}++int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,+               bf_flags_t flags)+{+    bf_t b;+    int ret;+    bf_init(r->ctx, &b);+    ret = bf_set_si(&b, b1);+    ret |= bf_mul(r, a, &b, prec, flags);+    bf_delete(&b);+    return ret;+}++int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,+              bf_flags_t flags)+{+    bf_t b;+    int ret;+    +    bf_init(r->ctx, &b);+    ret = bf_set_si(&b, b1);+    ret |= bf_add(r, a, &b, prec, flags);+    bf_delete(&b);+    return ret;+}++static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec,+                     bf_flags_t flags)+{+    int ret, n_bits, i;+    +    assert(r != a);+    if (b == 0)+        return bf_set_ui(r, 1);+    ret = bf_set(r, a);+    n_bits = LIMB_BITS - clz(b);+    for(i = n_bits - 2; i >= 0; i--) {+        ret |= bf_mul(r, r, r, prec, flags);+        if ((b >> i) & 1)+            ret |= bf_mul(r, r, a, prec, flags);+    }+    return ret;+}++static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b,+                        limb_t prec, bf_flags_t flags)+{+    bf_t a;+    int ret;+    +    if (a1 == 10 && b <= LIMB_DIGITS) {+        /* use precomputed powers. We do not round at this point+           because we expect the caller to do it */+        ret = bf_set_ui(r, mp_pow_dec[b]);+    } else {+        bf_init(r->ctx, &a);+        ret = bf_set_ui(&a, a1);+        ret |= bf_pow_ui(r, &a, b, prec, flags);+        bf_delete(&a);+    }+    return ret;+}++/* convert to integer (infinite precision) */+int bf_rint(bf_t *r, int rnd_mode)+{+    return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC);+}++/* logical operations */+#define BF_LOGIC_OR  0+#define BF_LOGIC_XOR 1+#define BF_LOGIC_AND 2++static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op)+{+    switch(op) {+    case BF_LOGIC_OR:+        return a | b;+    case BF_LOGIC_XOR:+        return a ^ b;+    default:+    case BF_LOGIC_AND:+        return a & b;+    }+}++static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op)+{+    bf_t b1_s, a1_s, *a, *b;+    limb_t a_sign, b_sign, r_sign;+    slimb_t l, i, a_bit_offset, b_bit_offset;+    limb_t v1, v2, v1_mask, v2_mask, r_mask;+    int ret;+    +    assert(r != a1 && r != b1);++    if (a1->expn <= 0)+        a_sign = 0; /* minus zero is considered as positive */+    else+        a_sign = a1->sign;++    if (b1->expn <= 0)+        b_sign = 0; /* minus zero is considered as positive */+    else+        b_sign = b1->sign;+    +    if (a_sign) {+        a = &a1_s;+        bf_init(r->ctx, a);+        if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) {+            b = NULL;+            goto fail;+        }+    } else {+        a = (bf_t *)a1;+    }++    if (b_sign) {+        b = &b1_s;+        bf_init(r->ctx, b);+        if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ))+            goto fail;+    } else {+        b = (bf_t *)b1;+    }+    +    r_sign = bf_logic_op1(a_sign, b_sign, op);+    if (op == BF_LOGIC_AND && r_sign == 0) {+        /* no need to compute extra zeros for and */+        if (a_sign == 0 && b_sign == 0)+            l = bf_min(a->expn, b->expn);+        else if (a_sign == 0)+            l = a->expn;+        else+            l = b->expn;+    } else {+        l = bf_max(a->expn, b->expn);+    }+    /* Note: a or b can be zero */+    l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS;+    if (bf_resize(r, l))+        goto fail;+    a_bit_offset = a->len * LIMB_BITS - a->expn;+    b_bit_offset = b->len * LIMB_BITS - b->expn;+    v1_mask = -a_sign;+    v2_mask = -b_sign;+    r_mask = -r_sign;+    for(i = 0; i < l; i++) {+        v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask;+        v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask;+        r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask;+    }+    r->expn = l * LIMB_BITS;+    r->sign = r_sign;+    bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */+    if (r_sign) {+        if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ))+            goto fail;+    }+    ret = 0;+ done:+    if (a == &a1_s)+        bf_delete(a);+    if (b == &b1_s)+        bf_delete(b);+    return ret;+ fail:+    bf_set_nan(r);+    ret = BF_ST_MEM_ERROR;+    goto done;+}++/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */+int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b)+{+    return bf_logic_op(r, a, b, BF_LOGIC_OR);+}++/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */+int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b)+{+    return bf_logic_op(r, a, b, BF_LOGIC_XOR);+}++/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */+int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b)+{+    return bf_logic_op(r, a, b, BF_LOGIC_AND);+}++/* conversion between fixed size types */++typedef union {+    double d;+    uint64_t u;+} Float64Union;++int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode)+{+    Float64Union u;+    int e, ret;+    uint64_t m;+    +    ret = 0;+    if (a->expn == BF_EXP_NAN) {+        u.u = 0x7ff8000000000000; /* quiet nan */+    } else {+        bf_t b_s, *b = &b_s;+        +        bf_init(a->ctx, b);+        bf_set(b, a);+        if (bf_is_finite(b)) {+            ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11));+        }+        if (b->expn == BF_EXP_INF) {+            e = (1 << 11) - 1;+            m = 0;+        } else if (b->expn == BF_EXP_ZERO) {+            e = 0;+            m = 0;+        } else {+            e = b->expn + 1023 - 1;+#if LIMB_BITS == 32+            if (b->len == 2) {+                m = ((uint64_t)b->tab[1] << 32) | b->tab[0];+            } else {+                m = ((uint64_t)b->tab[0] << 32);+            }+#else+            m = b->tab[0];+#endif+            if (e <= 0) {+                /* subnormal */+                m = m >> (12 - e);+                e = 0;+            } else {+                m = (m << 1) >> 12;+            }+        }+        u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63);+        bf_delete(b);+    }+    *pres = u.d;+    return ret;+}++int bf_set_float64(bf_t *a, double d)+{+    Float64Union u;+    uint64_t m;+    int shift, e, sgn;+    +    u.d = d;+    sgn = u.u >> 63;+    e = (u.u >> 52) & ((1 << 11) - 1);+    m = u.u & (((uint64_t)1 << 52) - 1);+    if (e == ((1 << 11) - 1)) {+        if (m != 0) {+            bf_set_nan(a);+        } else {+            bf_set_inf(a, sgn);+        }+    } else if (e == 0) {+        if (m == 0) {+            bf_set_zero(a, sgn);+        } else {+            /* subnormal number */+            m <<= 12;+            shift = clz64(m);+            m <<= shift;+            e = -shift;+            goto norm;+        }+    } else {+        m = (m << 11) | ((uint64_t)1 << 63);+    norm:+        a->expn = e - 1023 + 1;+#if LIMB_BITS == 32+        if (bf_resize(a, 2))+            goto fail;+        a->tab[0] = m;+        a->tab[1] = m >> 32;+#else+        if (bf_resize(a, 1))+            goto fail;+        a->tab[0] = m;+#endif+        a->sign = sgn;+    }+    return 0;+fail:+    bf_set_nan(a);+    return BF_ST_MEM_ERROR;+}++/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there+   is an overflow and 0 otherwise. */+int bf_get_int32(int *pres, const bf_t *a, int flags)+{+    uint32_t v;+    int ret;+    if (a->expn >= BF_EXP_INF) {+        ret = BF_ST_INVALID_OP;+        if (flags & BF_GET_INT_MOD) {+            v = 0;+        } else if (a->expn == BF_EXP_INF) {+            v = (uint32_t)INT32_MAX + a->sign;+        } else {+            v = INT32_MAX;+        }+    } else if (a->expn <= 0) {+        v = 0;+        ret = 0;+    } else if (a->expn <= 31) {+        v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);+        if (a->sign)+            v = -v;+        ret = 0;+    } else if (!(flags & BF_GET_INT_MOD)) {+        ret = BF_ST_INVALID_OP;+        if (a->sign) {+            v = (uint32_t)INT32_MAX + 1;+            if (a->expn == 32 && +                (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) {+                ret = 0;+            }+        } else {+            v = INT32_MAX;+        }+    } else {+        v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); +        if (a->sign)+            v = -v;+        ret = 0;+    }+    *pres = v;+    return ret;+}++/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there+   is an overflow and 0 otherwise. */+int bf_get_int64(int64_t *pres, const bf_t *a, int flags)+{+    uint64_t v;+    int ret;+    if (a->expn >= BF_EXP_INF) {+        ret = BF_ST_INVALID_OP;+        if (flags & BF_GET_INT_MOD) {+            v = 0;+        } else if (a->expn == BF_EXP_INF) {+            v = (uint64_t)INT64_MAX + a->sign;+        } else {+            v = INT64_MAX;+        }+    } else if (a->expn <= 0) {+        v = 0;+        ret = 0;+    } else if (a->expn <= 63) {+#if LIMB_BITS == 32+        if (a->expn <= 32)+            v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);+        else+            v = (((uint64_t)a->tab[a->len - 1] << 32) |+                 get_limbz(a, a->len - 2)) >> (64 - a->expn);+#else+        v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);+#endif+        if (a->sign)+            v = -v;+        ret = 0;+    } else if (!(flags & BF_GET_INT_MOD)) {+        ret = BF_ST_INVALID_OP;+        if (a->sign) {+            uint64_t v1;+            v = (uint64_t)INT64_MAX + 1;+            if (a->expn == 64) {+                v1 = a->tab[a->len - 1];+#if LIMB_BITS == 32+                v1 = (v1 << 32) | get_limbz(a, a->len - 2);+#endif+                if (v1 == v)+                    ret = 0;+            }+        } else {+            v = INT64_MAX;+        }+    } else {+        slimb_t bit_pos = a->len * LIMB_BITS - a->expn;+        v = get_bits(a->tab, a->len, bit_pos); +#if LIMB_BITS == 32+        v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32;+#endif+        if (a->sign)+            v = -v;+        ret = 0;+    }+    *pres = v;+    return ret;+}++/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there+   is an overflow and 0 otherwise. */+int bf_get_uint64(uint64_t *pres, const bf_t *a)+{+    uint64_t v;+    int ret;+    if (a->expn == BF_EXP_NAN) {+        goto overflow;+    } else if (a->expn <= 0) {+        v = 0;+        ret = 0;+    } else if (a->sign) {+        v = 0;+        ret = BF_ST_INVALID_OP;+    } else if (a->expn <= 64) {+#if LIMB_BITS == 32+        if (a->expn <= 32)+            v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);+        else+            v = (((uint64_t)a->tab[a->len - 1] << 32) |+                 get_limbz(a, a->len - 2)) >> (64 - a->expn);+#else+        v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);+#endif+        ret = 0;+    } else {+    overflow:+        v = UINT64_MAX;+        ret = BF_ST_INVALID_OP;+    }+    *pres = v;+    return ret;+}++/* base conversion from radix */++static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = {+#if LIMB_BITS == 32+32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,+#else+64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,+#endif+};++static limb_t get_limb_radix(int radix)+{+    int i, k;+    limb_t radixl;+    +    k = digits_per_limb_table[radix - 2];+    radixl = radix;+    for(i = 1; i < k; i++)+        radixl *= radix;+    return radixl;+}++/* return != 0 if error */+static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab,+                                     limb_t n, int level, limb_t n0,+                                     limb_t radix, bf_t *pow_tab)+{+    int ret;+    if (n == 1) {+        ret = bf_set_ui(r, tab[0]);+    } else {+        bf_t T_s, *T = &T_s, *B;+        limb_t n1, n2;+        +        n2 = (((n0 * 2) >> (level + 1)) + 1) / 2;+        n1 = n - n2;+        //        printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2);+        B = &pow_tab[level];+        if (B->len == 0) {+            ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ);+            if (ret)+                return ret;+        }+        ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0,+                                        radix, pow_tab);+        if (ret)+            return ret;+        ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ);+        if (ret)+            return ret;+        bf_init(r->ctx, T);+        ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0,+                                        radix, pow_tab);+        if (!ret)+            ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ);+        bf_delete(T);+    }+    return ret;+    //    bf_print_str("  r=", r);+}++/* return 0 if OK != 0 if memory error */+static int bf_integer_from_radix(bf_t *r, const limb_t *tab,+                                 limb_t n, limb_t radix)+{+    bf_context_t *s = r->ctx;+    int pow_tab_len, i, ret;+    limb_t radixl;+    bf_t *pow_tab;+    +    radixl = get_limb_radix(radix);+    pow_tab_len = ceil_log2(n) + 2; /* XXX: check */+    pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);+    if (!pow_tab)+        return -1;+    for(i = 0; i < pow_tab_len; i++)+        bf_init(r->ctx, &pow_tab[i]);+    ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab);+    for(i = 0; i < pow_tab_len; i++) {+        bf_delete(&pow_tab[i]);+    }+    bf_free(s, pow_tab);+    return ret;+}++/* compute and round T * radix^expn. */+int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,+                     slimb_t expn, limb_t prec, bf_flags_t flags)+{+    int ret, expn_sign, overflow;+    slimb_t e, extra_bits, prec1, ziv_extra_bits;+    bf_t B_s, *B = &B_s;++    if (T->len == 0) {+        return bf_set(r, T);+    } else if (expn == 0) {+        ret = bf_set(r, T);+        ret |= bf_round(r, prec, flags);+        return ret;+    }++    e = expn;+    expn_sign = 0;+    if (e < 0) {+        e = -e;+        expn_sign = 1;+    }+    bf_init(r->ctx, B);+    if (prec == BF_PREC_INF) {+        /* infinite precision: only used if the result is known to be exact */+        ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN);+        if (expn_sign) {+            ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN);+        } else {+            ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN);+        }+    } else {+        ziv_extra_bits = 16;+        for(;;) {+            prec1 = prec + ziv_extra_bits;+            /* XXX: correct overflow/underflow handling */+            /* XXX: rigorous error analysis needed */+            extra_bits = ceil_log2(e) * 2 + 1;+            ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);+            overflow = !bf_is_finite(B);+            /* XXX: if bf_pow_ui_ui returns an exact result, can stop+               after the next operation */+            if (expn_sign)+                ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);+            else+                ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);+            if (ret & BF_ST_MEM_ERROR)+                break;+            if ((ret & BF_ST_INEXACT) &&+                !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) &&+                !overflow) {+                /* and more precision and retry */+                ziv_extra_bits = ziv_extra_bits  + (ziv_extra_bits / 2);+            } else {+                /* XXX: need to use __bf_round() to pass the inexact+                   flag for the subnormal case */+                ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT);+                break;+            }+        }+    }+    bf_delete(B);+    return ret;+}++static inline int to_digit(int c)+{+    if (c >= '0' && c <= '9')+        return c - '0';+    else if (c >= 'A' && c <= 'Z')+        return c - 'A' + 10;+    else if (c >= 'a' && c <= 'z')+        return c - 'a' + 10;+    else+        return 36;+}++/* add a limb at 'pos' and decrement pos. new space is created if+   needed. Return 0 if OK, -1 if memory error */+static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v)+{+    slimb_t pos;+    pos = *ppos;+    if (unlikely(pos < 0)) {+        limb_t new_size, d, *new_tab;+        new_size = bf_max(a->len + 1, a->len * 3 / 2);+        new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size);+        if (!new_tab)+            return -1;+        a->tab = new_tab;+        d = new_size - a->len;+        memmove(a->tab + d, a->tab, a->len * sizeof(limb_t));+        a->len = new_size;+        pos += d;+    }+    a->tab[pos--] = v;+    *ppos = pos;+    return 0;+}++static int bf_tolower(int c)+{+    if (c >= 'A' && c <= 'Z')+        c = c - 'A' + 'a';+    return c;+}++static int strcasestart(const char *str, const char *val, const char **ptr)+{+    const char *p, *q;+    p = str;+    q = val;+    while (*q != '\0') {+        if (bf_tolower(*p) != *q)+            return 0;+        p++;+        q++;+    }+    if (ptr)+        *ptr = p;+    return 1;+}++static int bf_atof_internal(bf_t *r, slimb_t *pexponent,+                            const char *str, const char **pnext, int radix,+                            limb_t prec, bf_flags_t flags, BOOL is_dec)+{+    const char *p, *p_start;+    int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift;+    limb_t cur_limb;+    slimb_t pos, expn, int_len, digit_count;+    BOOL has_decpt, is_bin_exp;+    bf_t a_s, *a;+    +    *pexponent = 0;+    p = str;+    if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&+        strcasestart(p, "nan", &p)) {+        bf_set_nan(r);+        ret = 0;+        goto done;+    }+    is_neg = 0;+    +    if (p[0] == '+') {+        p++;+        p_start = p;+    } else if (p[0] == '-') {+        is_neg = 1;+        p++;+        p_start = p;+    } else {+        p_start = p;+    }+    if (p[0] == '0') {+        if ((p[1] == 'x' || p[1] == 'X') &&+            (radix == 0 || radix == 16) &&+            !(flags & BF_ATOF_NO_HEX)) {+            radix = 16;+            p += 2;+        } else if ((p[1] == 'o' || p[1] == 'O') &&+                   radix == 0 && (flags & BF_ATOF_BIN_OCT)) {+            p += 2;+            radix = 8;+        } else if ((p[1] == 'b' || p[1] == 'B') &&+                   radix == 0 && (flags & BF_ATOF_BIN_OCT)) {+            p += 2;+            radix = 2;+        } else {+            goto no_prefix;+        }+        /* there must be a digit after the prefix */+        if (to_digit((uint8_t)*p) >= radix) {+            bf_set_nan(r);+            ret = 0;+            goto done;+        }+    no_prefix: ;+    } else {+        if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&+            strcasestart(p, "inf", &p)) {+            bf_set_inf(r, is_neg);+            ret = 0;+            goto done;+        }+    }+    +    if (radix == 0)+        radix = 10;+    if (is_dec) {+        assert(radix == 10);+        radix_bits = 0;+        a = r;+    } else if ((radix & (radix - 1)) != 0) {+        radix_bits = 0; /* base is not a power of two */+        a = &a_s;+        bf_init(r->ctx, a);+    } else {+        radix_bits = ceil_log2(radix);+        a = r;+    }++    /* skip leading zeros */+    /* XXX: could also skip zeros after the decimal point */+    while (*p == '0')+        p++;++    if (radix_bits) {+        shift = digits_per_limb = LIMB_BITS;+    } else {+        radix_bits = 0;+        shift = digits_per_limb = digits_per_limb_table[radix - 2];+    }+    cur_limb = 0;+    bf_resize(a, 1);+    pos = 0;+    has_decpt = FALSE;+    int_len = digit_count = 0;+    for(;;) {+        limb_t c;+        if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) {+            if (has_decpt)+                break;+            has_decpt = TRUE;+            int_len = digit_count;+            p++;+        }+        c = to_digit(*p);+        if (c >= radix)+            break;+        digit_count++;+        p++;+        if (radix_bits) {+            shift -= radix_bits;+            if (shift <= 0) {+                cur_limb |= c >> (-shift);+                if (bf_add_limb(a, &pos, cur_limb))+                    goto mem_error;+                if (shift < 0)+                    cur_limb = c << (LIMB_BITS + shift);+                else+                    cur_limb = 0;+                shift += LIMB_BITS;+            } else {+                cur_limb |= c << shift;+            }+        } else {+            cur_limb = cur_limb * radix + c;+            shift--;+            if (shift == 0) {+                if (bf_add_limb(a, &pos, cur_limb))+                    goto mem_error;+                shift = digits_per_limb;+                cur_limb = 0;+            }+        }+    }+    if (!has_decpt)+        int_len = digit_count;++    /* add the last limb and pad with zeros */+    if (shift != digits_per_limb) {+        if (radix_bits == 0) {+            while (shift != 0) {+                cur_limb *= radix;+                shift--;+            }+        }+        if (bf_add_limb(a, &pos, cur_limb)) {+        mem_error:+            ret = BF_ST_MEM_ERROR;+            if (!radix_bits)+                bf_delete(a);+            bf_set_nan(r);+            goto done;+        }+    }+            +    /* reset the next limbs to zero (we prefer to reallocate in the+       renormalization) */+    memset(a->tab, 0, (pos + 1) * sizeof(limb_t));++    if (p == p_start) {+        ret = 0;+        if (!radix_bits)+            bf_delete(a);+        bf_set_nan(r);+        goto done;+    }++    /* parse the exponent, if any */+    expn = 0;+    is_bin_exp = FALSE;+    if (((radix == 10 && (*p == 'e' || *p == 'E')) ||+         (radix != 10 && (*p == '@' ||+                          (radix_bits && (*p == 'p' || *p == 'P'))))) &&+        p > p_start) {+        is_bin_exp = (*p == 'p' || *p == 'P');+        p++;+        exp_is_neg = 0;+        if (*p == '+') {+            p++;+        } else if (*p == '-') {+            exp_is_neg = 1;+            p++;+        }+        for(;;) {+            int c;+            c = to_digit(*p);+            if (c >= 10)+                break;+            if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {+                /* exponent overflow */+                if (exp_is_neg) {+                    bf_set_zero(r, is_neg);+                    ret = BF_ST_UNDERFLOW | BF_ST_INEXACT;+                } else {+                    bf_set_inf(r, is_neg);+                    ret = BF_ST_OVERFLOW | BF_ST_INEXACT;+                }+                goto done;+            }+            p++;+            expn = expn * 10 + c;+        }+        if (exp_is_neg)+            expn = -expn;+    }+    if (is_dec) {+        a->expn = expn + int_len;+        a->sign = is_neg;+        ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags);+    } else if (radix_bits) {+        /* XXX: may overflow */+        if (!is_bin_exp)+            expn *= radix_bits; +        a->expn = expn + (int_len * radix_bits);+        a->sign = is_neg;+        ret = bf_normalize_and_round(a, prec, flags);+    } else {+        limb_t l;+        pos++;+        l = a->len - pos; /* number of limbs */+        if (l == 0) {+            bf_set_zero(r, is_neg);+            ret = 0;+        } else {+            bf_t T_s, *T = &T_s;++            expn -= l * digits_per_limb - int_len;+            bf_init(r->ctx, T);+            if (bf_integer_from_radix(T, a->tab + pos, l, radix)) {+                bf_set_nan(r);+                ret = BF_ST_MEM_ERROR;+            } else {+                T->sign = is_neg;+                if (flags & BF_ATOF_EXPONENT) {+                    /* return the exponent */+                    *pexponent = expn;+                    ret = bf_set(r, T);+                } else {+                    ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags);+                }+            }+            bf_delete(T);+        }+        bf_delete(a);+    }+ done:+    if (pnext)+        *pnext = p;+    return ret;+}++/* +   Return (status, n, exp). 'status' is the floating point status. 'n'+   is the parsed number. ++   If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of+   two, the parsed number is equal to r *+   (*pexponent)^radix. Otherwise *pexponent = 0.+*/+int bf_atof2(bf_t *r, slimb_t *pexponent,+             const char *str, const char **pnext, int radix,+             limb_t prec, bf_flags_t flags)+{+    return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags,+                            FALSE);+}++int bf_atof(bf_t *r, const char *str, const char **pnext, int radix,+            limb_t prec, bf_flags_t flags)+{+    slimb_t dummy_exp;+    return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE);+}++/* base conversion to radix */++#if LIMB_BITS == 64+#define RADIXL_10 UINT64_C(10000000000000000000)+#else+#define RADIXL_10 UINT64_C(1000000000)+#endif++static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = {+#if LIMB_BITS == 32+{ 0x80000000, 0x00000000,},+{ 0x50c24e60, 0xd4d4f4a7,},+{ 0x40000000, 0x00000000,},+{ 0x372068d2, 0x0a1ee5ca,},+{ 0x3184648d, 0xb8153e7a,},+{ 0x2d983275, 0x9d5369c4,},+{ 0x2aaaaaaa, 0xaaaaaaab,},+{ 0x28612730, 0x6a6a7a54,},+{ 0x268826a1, 0x3ef3fde6,},+{ 0x25001383, 0xbac8a744,},+{ 0x23b46706, 0x82c0c709,},+{ 0x229729f1, 0xb2c83ded,},+{ 0x219e7ffd, 0xa5ad572b,},+{ 0x20c33b88, 0xda7c29ab,},+{ 0x20000000, 0x00000000,},+{ 0x1f50b57e, 0xac5884b3,},+{ 0x1eb22cc6, 0x8aa6e26f,},+{ 0x1e21e118, 0x0c5daab2,},+{ 0x1d9dcd21, 0x439834e4,},+{ 0x1d244c78, 0x367a0d65,},+{ 0x1cb40589, 0xac173e0c,},+{ 0x1c4bd95b, 0xa8d72b0d,},+{ 0x1bead768, 0x98f8ce4c,},+{ 0x1b903469, 0x050f72e5,},+{ 0x1b3b433f, 0x2eb06f15,},+{ 0x1aeb6f75, 0x9c46fc38,},+{ 0x1aa038eb, 0x0e3bfd17,},+{ 0x1a593062, 0xb38d8c56,},+{ 0x1a15f4c3, 0x2b95a2e6,},+{ 0x19d630dc, 0xcc7ddef9,},+{ 0x19999999, 0x9999999a,},+{ 0x195fec80, 0x8a609431,},+{ 0x1928ee7b, 0x0b4f22f9,},+{ 0x18f46acf, 0x8c06e318,},+{ 0x18c23246, 0xdc0a9f3d,},+#else+{ 0x80000000, 0x00000000, 0x00000000,},+{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,},+{ 0x40000000, 0x00000000, 0x00000000,},+{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,},+{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,},+{ 0x2d983275, 0x9d5369c4, 0x4dec1661,},+{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,},+{ 0x28612730, 0x6a6a7a53, 0x810fabde,},+{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,},+{ 0x25001383, 0xbac8a744, 0x385a3349,},+{ 0x23b46706, 0x82c0c709, 0x3f891718,},+{ 0x229729f1, 0xb2c83ded, 0x15fba800,},+{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,},+{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,},+{ 0x20000000, 0x00000000, 0x00000000,},+{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,},+{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,},+{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,},+{ 0x1d9dcd21, 0x439834e3, 0x81667575,},+{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,},+{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,},+{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,},+{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,},+{ 0x1b903469, 0x050f72e5, 0x0cf5488e,},+{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,},+{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,},+{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,},+{ 0x1a593062, 0xb38d8c56, 0x7998ab45,},+{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,},+{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,},+{ 0x19999999, 0x99999999, 0x9999999a,},+{ 0x195fec80, 0x8a609430, 0xe1106014,},+{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,},+{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,},+{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,},+#endif+};++static const limb_t log2_radix[BF_RADIX_MAX - 1] = {+#if LIMB_BITS == 32+0x20000000,+0x32b80347,+0x40000000,+0x4a4d3c26,+0x52b80347,+0x59d5d9fd,+0x60000000,+0x6570068e,+0x6a4d3c26,+0x6eb3a9f0,+0x72b80347,+0x766a008e,+0x79d5d9fd,+0x7d053f6d,+0x80000000,+0x82cc7edf,+0x8570068e,+0x87ef05ae,+0x8a4d3c26,+0x8c8ddd45,+0x8eb3a9f0,+0x90c10501,+0x92b80347,+0x949a784c,+0x966a008e,+0x982809d6,+0x99d5d9fd,+0x9b74948f,+0x9d053f6d,+0x9e88c6b3,+0xa0000000,+0xa16bad37,+0xa2cc7edf,+0xa4231623,+0xa570068e,+#else+0x2000000000000000,+0x32b803473f7ad0f4,+0x4000000000000000,+0x4a4d3c25e68dc57f,+0x52b803473f7ad0f4,+0x59d5d9fd5010b366,+0x6000000000000000,+0x6570068e7ef5a1e8,+0x6a4d3c25e68dc57f,+0x6eb3a9f01975077f,+0x72b803473f7ad0f4,+0x766a008e4788cbcd,+0x79d5d9fd5010b366,+0x7d053f6d26089673,+0x8000000000000000,+0x82cc7edf592262d0,+0x8570068e7ef5a1e8,+0x87ef05ae409a0289,+0x8a4d3c25e68dc57f,+0x8c8ddd448f8b845a,+0x8eb3a9f01975077f,+0x90c10500d63aa659,+0x92b803473f7ad0f4,+0x949a784bcd1b8afe,+0x966a008e4788cbcd,+0x982809d5be7072dc,+0x99d5d9fd5010b366,+0x9b74948f5532da4b,+0x9d053f6d26089673,+0x9e88c6b3626a72aa,+0xa000000000000000,+0xa16bad3758efd873,+0xa2cc7edf592262d0,+0xa4231623369e78e6,+0xa570068e7ef5a1e8,+#endif+};++/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or+   b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed+   when radix is not a power of two. */+slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,+                          int is_ceil1)+{+    int is_neg;+    limb_t a;+    BOOL is_ceil;++    is_ceil = is_ceil1;+    a = a1;+    if (a1 < 0) {+        a = -a;+        is_neg = 1;+    } else {+        is_neg = 0;+    }+    is_ceil ^= is_neg;+    if ((radix & (radix - 1)) == 0) {+        int radix_bits;+        /* radix is a power of two */+        radix_bits = ceil_log2(radix);+        if (is_inv) {+            if (is_ceil)+                a += radix_bits - 1;+            a = a / radix_bits;+        } else {+            a = a * radix_bits;+        }+    } else {+        const uint32_t *tab;+        limb_t b0, b1;+        dlimb_t t;+        +        if (is_inv) {+            tab = inv_log2_radix[radix - 2];+#if LIMB_BITS == 32+            b1 = tab[0];+            b0 = tab[1];+#else+            b1 = ((limb_t)tab[0] << 32) | tab[1];+            b0 = (limb_t)tab[2] << 32;+#endif+            t = (dlimb_t)b0 * (dlimb_t)a;+            t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS);+            a = t >> (LIMB_BITS - 1);+        } else {+            b0 = log2_radix[radix - 2];+            t = (dlimb_t)b0 * (dlimb_t)a;+            a = t >> (LIMB_BITS - 3);+        }+        /* a = floor(result) and 'result' cannot be an integer */+        a += is_ceil;+    }+    if (is_neg)+        a = -a;+    return a;+}++/* 'n' is the number of output limbs */+static int bf_integer_to_radix_rec(bf_t *pow_tab,+                                   limb_t *out, const bf_t *a, limb_t n,+                                   int level, limb_t n0, limb_t radixl,+                                   unsigned int radixl_bits)+{+    limb_t n1, n2, q_prec;+    int ret;+    +    assert(n >= 1);+    if (n == 1) {+        out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn);+    } else if (n == 2) {+        dlimb_t t;+        slimb_t pos;+        pos = a->len * LIMB_BITS - a->expn;+        t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) |+            get_bits(a->tab, a->len, pos);+        if (likely(radixl == RADIXL_10)) {+            /* use division by a constant when possible */+            out[0] = t % RADIXL_10;+            out[1] = t / RADIXL_10;+        } else {+            out[0] = t % radixl;+            out[1] = t / radixl;+        }+    } else {+        bf_t Q, R, *B, *B_inv;+        int q_add;+        bf_init(a->ctx, &Q);+        bf_init(a->ctx, &R);+        n2 = (((n0 * 2) >> (level + 1)) + 1) / 2;+        n1 = n - n2;+        B = &pow_tab[2 * level];+        B_inv = &pow_tab[2 * level + 1];+        ret = 0;+        if (B->len == 0) {+            /* compute BASE^n2 */+            ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ);+            /* we use enough bits for the maximum possible 'n1' value,+               i.e. n2 + 1 */+            ret |= bf_set_ui(&R, 1);+            ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN);+        }+        //        printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2);+        q_prec = n1 * radixl_bits;+        ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN);+        ret |= bf_rint(&Q, BF_RNDZ);+        +        ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ);+        ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ);++        if (ret & BF_ST_MEM_ERROR)+            goto fail;+        /* adjust if necessary */+        q_add = 0;+        while (R.sign && R.len != 0) {+            if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ))+                goto fail;+            q_add--;+        }+        while (bf_cmpu(&R, B) >= 0) {+            if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ))+                goto fail;+            q_add++;+        }+        if (q_add != 0) {+            if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ))+                goto fail;+        }+        if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0,+                                    radixl, radixl_bits))+            goto fail;+        if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0,+                                    radixl, radixl_bits)) {+        fail:+            bf_delete(&Q);+            bf_delete(&R);+            return -1;+        }+        bf_delete(&Q);+        bf_delete(&R);+    }+    return 0;+}++/* return 0 if OK != 0 if memory error */+static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl)+{+    bf_context_t *s = r->ctx;+    limb_t r_len;+    bf_t *pow_tab;+    int i, pow_tab_len, ret;+    +    r_len = r->len;+    pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */+    pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);+    if (!pow_tab)+        return -1;+    for(i = 0; i < pow_tab_len; i++)+        bf_init(r->ctx, &pow_tab[i]);++    ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl,+                                  ceil_log2(radixl));++    for(i = 0; i < pow_tab_len; i++) {+        bf_delete(&pow_tab[i]);+    }+    bf_free(s, pow_tab);+    return ret;+}++/* a must be >= 0. 'P' is the wanted number of digits in radix+   'radix'. 'r' is the mantissa represented as an integer. *pE+   contains the exponent. Return != 0 if memory error. */+static int bf_convert_to_radix(bf_t *r, slimb_t *pE,+                               const bf_t *a, int radix,+                               limb_t P, bf_rnd_t rnd_mode,+                               BOOL is_fixed_exponent)+{+    slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0;+    bf_t B_s, *B = &B_s;+    int e_sign, ret, res;+    +    if (a->len == 0) {+        /* zero case */+        *pE = 0;+        return bf_set(r, a);+    }++    if (is_fixed_exponent) {+        E = *pE;+    } else {+        /* compute the new exponent */+        E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE);+    }+    //    bf_print_str("a", a);+    //    printf("E=%ld P=%ld radix=%d\n", E, P, radix);+    +    for(;;) {+        e = P - E;+        e_sign = 0;+        if (e < 0) {+            e = -e;+            e_sign = 1;+        }+        /* Note: precision for log2(radix) is not critical here */+        prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE);+        ziv_extra_bits = 16;+        for(;;) {+            prec = prec0 + ziv_extra_bits;+            /* XXX: rigorous error analysis needed */+            extra_bits = ceil_log2(e) * 2 + 1;+            ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits,+                               BF_RNDN | BF_FLAG_EXT_EXP);+            if (!e_sign)+                ret |= bf_mul(r, r, a, prec + extra_bits,+                              BF_RNDN | BF_FLAG_EXT_EXP);+            else+                ret |= bf_div(r, a, r, prec + extra_bits,+                              BF_RNDN | BF_FLAG_EXT_EXP);+            if (ret & BF_ST_MEM_ERROR)+                return BF_ST_MEM_ERROR;+            /* if the result is not exact, check that it can be safely+               rounded to an integer */+            if ((ret & BF_ST_INEXACT) &&+                !bf_can_round(r, r->expn, rnd_mode, prec)) {+                /* and more precision and retry */+                ziv_extra_bits = ziv_extra_bits  + (ziv_extra_bits / 2);+                continue;+            } else {+                ret = bf_rint(r, rnd_mode);+                if (ret & BF_ST_MEM_ERROR)+                    return BF_ST_MEM_ERROR;+                break;+            }+        }+        if (is_fixed_exponent)+            break;+        /* check that the result is < B^P */+        /* XXX: do a fast approximate test first ? */+        bf_init(r->ctx, B);+        ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ);+        if (ret) {+            bf_delete(B);+            return ret;+        }+        res = bf_cmpu(r, B);+        bf_delete(B);+        if (res < 0)+            break;+        /* try a larger exponent */+        E++;+    }+    *pE = E;+    return 0;+}++static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len)+{+    int digit, i;++    if (radix == 10) {+        /* specific case with constant divisor */+        for(i = len - 1; i >= 0; i--) {+            digit = (limb_t)n % 10;+            n = (limb_t)n / 10;+            buf[i] = digit + '0';+        }+    } else {+        for(i = len - 1; i >= 0; i--) {+            digit = (limb_t)n % radix;+            n = (limb_t)n / radix;+            if (digit < 10)+                digit += '0';+            else+                digit += 'a' - 10;+            buf[i] = digit;+        }+    }+}++/* for power of 2 radixes */+static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len)+{+    int digit, i;+    unsigned int mask;++    mask = (1 << radix_bits) - 1;+    for(i = len - 1; i >= 0; i--) {+        digit = n & mask;+        n >>= radix_bits;+        if (digit < 10)+            digit += '0';+        else+            digit += 'a' - 10;+        buf[i] = digit;+    }+}++/* 'a' must be an integer if the is_dec = FALSE or if the radix is not+   a power of two. A dot is added before the 'dot_pos' digit. dot_pos+   = n_digits does not display the dot. 0 <= dot_pos <=+   n_digits. n_digits >= 1. */+static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,+                          limb_t dot_pos, BOOL is_dec)+{+    limb_t i, v, l;+    slimb_t pos, pos_incr;+    int digits_per_limb, buf_pos, radix_bits, first_buf_pos;+    char buf[65];+    bf_t a_s, *a;++    if (is_dec) {+        digits_per_limb = LIMB_DIGITS;+        a = (bf_t *)a1;+        radix_bits = 0;+        pos = a->len;+        pos_incr = 1;+        first_buf_pos = 0;+    } else if ((radix & (radix - 1)) == 0) {+        a = (bf_t *)a1;+        radix_bits = ceil_log2(radix);+        digits_per_limb = LIMB_BITS / radix_bits;+        pos_incr = digits_per_limb * radix_bits;+        /* digits are aligned relative to the radix point */+        pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits);+        first_buf_pos = 0;+    } else {+        limb_t n, radixl;++        digits_per_limb = digits_per_limb_table[radix - 2];+        radixl = get_limb_radix(radix);+        a = &a_s;+        bf_init(a1->ctx, a);+        n = (n_digits + digits_per_limb - 1) / digits_per_limb;+        if (bf_resize(a, n)) {+            dbuf_set_error(s);+            goto done;+        }+        if (bf_integer_to_radix(a, a1, radixl)) {+            dbuf_set_error(s);+            goto done;+        }+        radix_bits = 0;+        pos = n;+        pos_incr = 1;+        first_buf_pos = pos * digits_per_limb - n_digits;+    }+    buf_pos = digits_per_limb;+    i = 0;+    while (i < n_digits) {+        if (buf_pos == digits_per_limb) {+            pos -= pos_incr;+            if (radix_bits == 0) {+                v = get_limbz(a, pos);+                limb_to_a(buf, v, radix, digits_per_limb);+            } else {+                v = get_bits(a->tab, a->len, pos);+                limb_to_a2(buf, v, radix_bits, digits_per_limb);+            }+            buf_pos = first_buf_pos;+            first_buf_pos = 0;+        }+        if (i < dot_pos) {+            l = dot_pos;+        } else {+            if (i == dot_pos)+                dbuf_putc(s, '.');+            l = n_digits;+        }+        l = bf_min(digits_per_limb - buf_pos, l - i);+        dbuf_put(s, (uint8_t *)(buf + buf_pos), l);+        buf_pos += l;+        i += l;+    }+ done:+    if (a != a1)+        bf_delete(a);+}++static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size)+{+    bf_context_t *s = opaque;+    return bf_realloc(s, ptr, size);+}++/* return the length in bytes. A trailing '\0' is added */+static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix,+                              limb_t prec, bf_flags_t flags, BOOL is_dec)+{+    bf_context_t *ctx = a2->ctx;+    DynBuf s_s, *s = &s_s;+    int radix_bits;+    +    //    bf_print_str("ftoa", a2);+    //    printf("radix=%d\n", radix);+    dbuf_init2(s, ctx, bf_dbuf_realloc);+    if (a2->expn == BF_EXP_NAN) {+        dbuf_putstr(s, "NaN");+    } else {+        if (a2->sign)+            dbuf_putc(s, '-');+        if (a2->expn == BF_EXP_INF) {+            if (flags & BF_FTOA_JS_QUIRKS)+                dbuf_putstr(s, "Infinity");+            else+                dbuf_putstr(s, "Inf");+        } else {+            int fmt, ret;+            slimb_t n_digits, n, i, n_max, n1;+            bf_t a1_s, *a1 = &a1_s;++            if ((radix & (radix - 1)) != 0)+                radix_bits = 0;+            else+                radix_bits = ceil_log2(radix);++            fmt = flags & BF_FTOA_FORMAT_MASK;+            bf_init(ctx, a1);+            if (fmt == BF_FTOA_FORMAT_FRAC) {+                if (is_dec || radix_bits != 0) {+                    if (bf_set(a1, a2))+                        goto fail1;+#ifdef USE_BF_DEC+                    if (is_dec) {+                        if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR)+                            goto fail1;+                        n = a1->expn;+                    } else+#endif+                    {+                        if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR)+                            goto fail1;+                        n = ceil_div(a1->expn, radix_bits);+                    }+                    if (flags & BF_FTOA_ADD_PREFIX) {+                        if (radix == 16)+                            dbuf_putstr(s, "0x");+                        else if (radix == 8)+                            dbuf_putstr(s, "0o");+                        else if (radix == 2)+                            dbuf_putstr(s, "0b");+                    }+                    if (a1->expn == BF_EXP_ZERO) {+                        dbuf_putstr(s, "0");+                        if (prec > 0) {+                            dbuf_putstr(s, ".");+                            for(i = 0; i < prec; i++) {+                                dbuf_putc(s, '0');+                            }+                        }+                    } else {+                        n_digits = prec + n;+                        if (n <= 0) {+                            /* 0.x */+                            dbuf_putstr(s, "0.");+                            for(i = 0; i < -n; i++) {+                                dbuf_putc(s, '0');+                            }+                            if (n_digits > 0) {+                                output_digits(s, a1, radix, n_digits, n_digits, is_dec);+                            }+                        } else {+                            output_digits(s, a1, radix, n_digits, n, is_dec);+                        }+                    }+                } else {+                    size_t pos, start;+                    bf_t a_s, *a = &a_s;++                    /* make a positive number */+                    a->tab = a2->tab;+                    a->len = a2->len;+                    a->expn = a2->expn;+                    a->sign = 0;+                    +                    /* one more digit for the rounding */+                    n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE);+                    n_digits = n + prec;+                    n1 = n;+                    if (bf_convert_to_radix(a1, &n1, a, radix, n_digits,+                                            flags & BF_RND_MASK, TRUE))+                        goto fail1;+                    start = s->size;+                    output_digits(s, a1, radix, n_digits, n, is_dec);+                    /* remove leading zeros because we allocated one more digit */+                    pos = start;+                    while ((pos + 1) < s->size && s->buf[pos] == '0' &&+                           s->buf[pos + 1] != '.')+                        pos++;+                    if (pos > start) {+                        memmove(s->buf + start, s->buf + pos, s->size - pos);+                        s->size -= (pos - start);+                    }+                }+            } else {+#ifdef USE_BF_DEC+                if (is_dec) {+                    if (bf_set(a1, a2))+                        goto fail1;+                    if (fmt == BF_FTOA_FORMAT_FIXED) {+                        n_digits = prec;+                        n_max = n_digits;+                        if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR)+                            goto fail1;+                    } else {+                        /* prec is ignored */+                        prec = n_digits = a1->len * LIMB_DIGITS;+                        /* remove the trailing zero digits */+                        while (n_digits > 1 &&+                               get_digit(a1->tab, a1->len, prec - n_digits) == 0) {+                            n_digits--;+                        }+                        n_max = n_digits + 4;+                    }+                    n = a1->expn;+                } else+#endif+                if (radix_bits != 0) {+                    if (bf_set(a1, a2))+                        goto fail1;+                    if (fmt == BF_FTOA_FORMAT_FIXED) {+                        slimb_t prec_bits;+                        n_digits = prec;+                        n_max = n_digits;+                        /* align to the radix point */+                        prec_bits = prec * radix_bits -+                            smod(-a1->expn, radix_bits);+                        if (bf_round(a1, prec_bits,+                                     (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR)+                            goto fail1;+                    } else {+                        limb_t digit_mask;+                        slimb_t pos;+                        /* position of the digit before the most+                           significant digit in bits */+                        pos = a1->len * LIMB_BITS ++                            smod(-a1->expn, radix_bits);+                        n_digits = ceil_div(pos, radix_bits);+                        /* remove the trailing zero digits */+                        digit_mask = ((limb_t)1 << radix_bits) - 1;+                        while (n_digits > 1 &&+                               (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) {+                            n_digits--;+                        }+                        n_max = n_digits + 4;+                    }+                    n = ceil_div(a1->expn, radix_bits);+                } else {+                    bf_t a_s, *a = &a_s;+                    +                    /* make a positive number */+                    a->tab = a2->tab;+                    a->len = a2->len;+                    a->expn = a2->expn;+                    a->sign = 0;+                    +                    if (fmt == BF_FTOA_FORMAT_FIXED) {+                        n_digits = prec;+                        n_max = n_digits;+                    } else {+                        slimb_t n_digits_max, n_digits_min;+                        +                        assert(prec != BF_PREC_INF);+                        n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE);+                        /* max number of digits for non exponential+                           notation. The rational is to have the same rule+                           as JS i.e. n_max = 21 for 64 bit float in base 10. */+                        n_max = n_digits + 4;+                        if (fmt == BF_FTOA_FORMAT_FREE_MIN) {+                            bf_t b_s, *b = &b_s;+                            +                            /* find the minimum number of digits by+                               dichotomy. */+                            /* XXX: inefficient */+                            n_digits_max = n_digits;+                            n_digits_min = 1;+                            bf_init(ctx, b);+                            while (n_digits_min < n_digits_max) {+                                n_digits = (n_digits_min + n_digits_max) / 2;+                                if (bf_convert_to_radix(a1, &n, a, radix, n_digits,+                                                        flags & BF_RND_MASK, FALSE)) {+                                    bf_delete(b);+                                    goto fail1;+                                }+                                /* convert back to a number and compare */+                                ret = bf_mul_pow_radix(b, a1, radix, n - n_digits,+                                                       prec,+                                                       (flags & ~BF_RND_MASK) |+                                                       BF_RNDN);+                                if (ret & BF_ST_MEM_ERROR) {+                                    bf_delete(b);+                                    goto fail1;+                                }+                                if (bf_cmpu(b, a) == 0) {+                                    n_digits_max = n_digits;+                                } else {+                                    n_digits_min = n_digits + 1;+                                }+                            }+                            bf_delete(b);+                            n_digits = n_digits_max;+                        }+                    }+                    if (bf_convert_to_radix(a1, &n, a, radix, n_digits,+                                            flags & BF_RND_MASK, FALSE)) {+                    fail1:+                        bf_delete(a1);+                        goto fail;+                    }+                }+                if (a1->expn == BF_EXP_ZERO &&+                    fmt != BF_FTOA_FORMAT_FIXED &&+                    !(flags & BF_FTOA_FORCE_EXP)) {+                    /* just output zero */+                    dbuf_putstr(s, "0");+                } else {+                    if (flags & BF_FTOA_ADD_PREFIX) {+                        if (radix == 16)+                            dbuf_putstr(s, "0x");+                        else if (radix == 8)+                            dbuf_putstr(s, "0o");+                        else if (radix == 2)+                            dbuf_putstr(s, "0b");+                    }+                    if (a1->expn == BF_EXP_ZERO)+                        n = 1;+                    if ((flags & BF_FTOA_FORCE_EXP) ||+                        n <= -6 || n > n_max) {+                        const char *fmt;+                        /* exponential notation */+                        output_digits(s, a1, radix, n_digits, 1, is_dec);+                        if (radix_bits != 0 && radix <= 16) {+                            if (flags & BF_FTOA_JS_QUIRKS)+                                fmt = "p%+" PRId_LIMB;+                            else+                                fmt = "p%" PRId_LIMB;+                            dbuf_printf(s, fmt, (n - 1) * radix_bits);+                        } else {+                            if (flags & BF_FTOA_JS_QUIRKS)+                                fmt = "%c%+" PRId_LIMB;+                            else+                                fmt = "%c%" PRId_LIMB;+                            dbuf_printf(s, fmt,+                                        radix <= 10 ? 'e' : '@', n - 1);+                        }+                    } else if (n <= 0) {+                        /* 0.x */+                        dbuf_putstr(s, "0.");+                        for(i = 0; i < -n; i++) {+                            dbuf_putc(s, '0');+                        }+                        output_digits(s, a1, radix, n_digits, n_digits, is_dec);+                    } else {+                        if (n_digits <= n) {+                            /* no dot */+                            output_digits(s, a1, radix, n_digits, n_digits, is_dec);+                            for(i = 0; i < (n - n_digits); i++)+                                dbuf_putc(s, '0');+                        } else {+                            output_digits(s, a1, radix, n_digits, n, is_dec);+                        }+                    }+                }+            }+            bf_delete(a1);+        }+    }+    dbuf_putc(s, '\0');+    if (dbuf_error(s))+        goto fail;+    if (plen)+        *plen = s->size - 1;+    return (char *)s->buf;+ fail:+    bf_free(ctx, s->buf);+    if (plen)+        *plen = 0;+    return NULL;+}++char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,+              bf_flags_t flags)+{+    return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE);+}++/***************************************************************/+/* transcendental functions */++/* Note: the algorithm is from MPFR */+static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1,+                              limb_t n2, BOOL need_P)+{+    bf_context_t *s = T->ctx;+    if ((n2 - n1) == 1) {+        if (n1 == 0) {+            bf_set_ui(P, 3);+        } else {+            bf_set_ui(P, n1);+            P->sign = 1;+        }+        bf_set_ui(Q, 2 * n1 + 1);+        Q->expn += 2;+        bf_set(T, P);+    } else {+        limb_t m;+        bf_t T1_s, *T1 = &T1_s;+        bf_t P1_s, *P1 = &P1_s;+        bf_t Q1_s, *Q1 = &Q1_s;+        +        m = n1 + ((n2 - n1) >> 1);+        bf_const_log2_rec(T, P, Q, n1, m, TRUE);+        bf_init(s, T1);+        bf_init(s, P1);+        bf_init(s, Q1);+        bf_const_log2_rec(T1, P1, Q1, m, n2, need_P);+        bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ);+        bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ);+        bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ);+        if (need_P)+            bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ);+        bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ);+        bf_delete(T1);+        bf_delete(P1);+        bf_delete(Q1);+    }+}++/* compute log(2) with faithful rounding at precision 'prec' */+static void bf_const_log2_internal(bf_t *T, limb_t prec)+{+    limb_t w, N;+    bf_t P_s, *P = &P_s;+    bf_t Q_s, *Q = &Q_s;++    w = prec + 15;+    N = w / 3 + 1;+    bf_init(T->ctx, P);+    bf_init(T->ctx, Q);+    bf_const_log2_rec(T, P, Q, 0, N, FALSE);+    bf_div(T, T, Q, prec, BF_RNDN);+    bf_delete(P);+    bf_delete(Q);+}++/* PI constant */++#define CHUD_A 13591409+#define CHUD_B 545140134+#define CHUD_C 640320+#define CHUD_BITS_PER_TERM 47++static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g,+                    limb_t prec)+{+    bf_context_t *s = P->ctx;+    int64_t c;++    if (a == (b - 1)) {+        bf_t T0, T1;+        +        bf_init(s, &T0);+        bf_init(s, &T1);+        bf_set_ui(G, 2 * b - 1);+        bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN);+        bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN);+        bf_set_ui(&T0, CHUD_B);+        bf_mul_ui(&T0, &T0, b, prec, BF_RNDN);+        bf_set_ui(&T1, CHUD_A);+        bf_add(&T0, &T0, &T1, prec, BF_RNDN);+        bf_mul(P, G, &T0, prec, BF_RNDN);+        P->sign = b & 1;++        bf_set_ui(Q, b);+        bf_mul_ui(Q, Q, b, prec, BF_RNDN);+        bf_mul_ui(Q, Q, b, prec, BF_RNDN);+        bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN);+        bf_delete(&T0);+        bf_delete(&T1);+    } else {+        bf_t P2, Q2, G2;+        +        bf_init(s, &P2);+        bf_init(s, &Q2);+        bf_init(s, &G2);++        c = (a + b) / 2;+        chud_bs(P, Q, G, a, c, 1, prec);+        chud_bs(&P2, &Q2, &G2, c, b, need_g, prec);+        +        /* Q = Q1 * Q2 */+        /* G = G1 * G2 */+        /* P = P1 * Q2 + P2 * G1 */+        bf_mul(&P2, &P2, G, prec, BF_RNDN);+        if (!need_g)+            bf_set_ui(G, 0);+        bf_mul(P, P, &Q2, prec, BF_RNDN);+        bf_add(P, P, &P2, prec, BF_RNDN);+        bf_delete(&P2);++        bf_mul(Q, Q, &Q2, prec, BF_RNDN);+        bf_delete(&Q2);+        if (need_g)+            bf_mul(G, G, &G2, prec, BF_RNDN);+        bf_delete(&G2);+    }+}++/* compute Pi with faithful rounding at precision 'prec' using the+   Chudnovsky formula */+static void bf_const_pi_internal(bf_t *Q, limb_t prec)+{+    bf_context_t *s = Q->ctx;+    int64_t n, prec1;+    bf_t P, G;++    /* number of serie terms */+    n = prec / CHUD_BITS_PER_TERM + 1;+    /* XXX: precision analysis */+    prec1 = prec + 32;++    bf_init(s, &P);+    bf_init(s, &G);++    chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF);+    +    bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN);+    bf_add(&P, &G, &P, prec1, BF_RNDN);+    bf_div(Q, Q, &P, prec1, BF_RNDF);+ +    bf_set_ui(&P, CHUD_C);+    bf_sqrt(&G, &P, prec1, BF_RNDF);+    bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF);+    bf_mul(Q, Q, &G, prec, BF_RNDN);+    bf_delete(&P);+    bf_delete(&G);+}++static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags,+                        BFConstCache *c,+                        void (*func)(bf_t *res, limb_t prec), int sign)+{+    limb_t ziv_extra_bits, prec1;++    ziv_extra_bits = 32;+    for(;;) {+        prec1 = prec + ziv_extra_bits;+        if (c->prec < prec1) {+            if (c->val.len == 0)+                bf_init(T->ctx, &c->val);+            func(&c->val, prec1);+            c->prec = prec1;+        } else {+            prec1 = c->prec;+        }+        bf_set(T, &c->val);+        T->sign = sign;+        if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) {+            /* and more precision and retry */+            ziv_extra_bits = ziv_extra_bits  + (ziv_extra_bits / 2);+        } else {+            break;+        }+    }+    return bf_round(T, prec, flags);+}++static void bf_const_free(BFConstCache *c)+{+    bf_delete(&c->val);+    memset(c, 0, sizeof(*c));+}++int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = T->ctx;+    return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0);+}++/* return rounded pi * (1 - 2 * sign) */+static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = T->ctx;+    return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal,+                        sign);+}++int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags)+{+    return bf_const_pi_signed(T, 0, prec, flags);+}++void bf_clear_cache(bf_context_t *s)+{+#ifdef USE_FFT_MUL+    fft_clear_cache(s);+#endif+    bf_const_free(&s->log2_cache);+    bf_const_free(&s->pi_cache);+}++/* ZivFunc should compute the result 'r' with faithful rounding at+   precision 'prec'. For efficiency purposes, the final bf_round()+   does not need to be done in the function. */+typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque);++static int bf_ziv_rounding(bf_t *r, const bf_t *a,+                           limb_t prec, bf_flags_t flags,+                           ZivFunc *f, void *opaque)+{+    int rnd_mode, ret;+    slimb_t prec1, ziv_extra_bits;+    +    rnd_mode = flags & BF_RND_MASK;+    if (rnd_mode == BF_RNDF) {+        /* no need to iterate */+        f(r, a, prec, opaque);+        ret = 0;+    } else {+        ziv_extra_bits = 32;+        for(;;) {+            prec1 = prec + ziv_extra_bits;+            ret = f(r, a, prec1, opaque);+            if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) {+                /* overflow or underflow should never happen because+                   it indicates the rounding cannot be done correctly,+                   but we do not catch all the cases */+                return ret;+            }+            /* if the result is exact, we can stop */+            if (!(ret & BF_ST_INEXACT)) {+                ret = 0;+                break;+            }+            if (bf_can_round(r, prec, rnd_mode, prec1)) {+                ret = BF_ST_INEXACT;+                break;+            }+            ziv_extra_bits = ziv_extra_bits * 2;+            //            printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits);+        }+    }+    if (r->len == 0)+        return ret;+    else+        return __bf_round(r, prec, flags, r->len, ret);+}++/* add (1 - 2*e_sign) * 2^e */+static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign,+                          limb_t prec, int flags)+{+    bf_t T_s, *T = &T_s;+    int ret;+    /* small argument case: result = 1 + epsilon * sign(x) */+    bf_init(a->ctx, T);+    bf_set_ui(T, 1);+    T->sign = e_sign;+    T->expn += e;+    ret = bf_add(r, r, T, prec, flags);+    bf_delete(T);+    return ret;+}++/* Compute the exponential using faithful rounding at precision 'prec'.+   Note: the algorithm is from MPFR */+static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    slimb_t n, K, l, i, prec1;+    +    assert(r != a);++    /* argument reduction:+       T = a - n*log(2) with 0 <= T < log(2) and n integer.+    */+    bf_init(s, T);+    if (a->expn <= -1) {+        /* 0 <= abs(a) <= 0.5 */+        if (a->sign)+            n = -1;+        else+            n = 0;+    } else {+        bf_const_log2(T, LIMB_BITS, BF_RNDZ);+        bf_div(T, a, T, LIMB_BITS, BF_RNDD);+        bf_get_limb(&n, T, 0);+    }++    K = bf_isqrt((prec + 1) / 2);+    l = (prec - 1) / K + 1;+    /* XXX: precision analysis ? */+    prec1 = prec + (K + 2 * l + 18) + K + 8;+    if (a->expn > 0)+        prec1 += a->expn;+    //    printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1);++    bf_const_log2(T, prec1, BF_RNDF);+    bf_mul_si(T, T, n, prec1, BF_RNDN);+    bf_sub(T, a, T, prec1, BF_RNDN);++    /* reduce the range of T */+    bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ);+    +    /* Taylor expansion around zero :+     1 + x + x^2/2 + ... + x^n/n! +     = (1 + x * (1 + x/2 * (1 + ... (x/n))))+    */+    {+        bf_t U_s, *U = &U_s;+        +        bf_init(s, U);+        bf_set_ui(r, 1);+        for(i = l ; i >= 1; i--) {+            bf_set_ui(U, i);+            bf_div(U, T, U, prec1, BF_RNDN);+            bf_mul(r, r, U, prec1, BF_RNDN);+            bf_add_si(r, r, 1, prec1, BF_RNDN);+        }+        bf_delete(U);+    }+    bf_delete(T);+    +    /* undo the range reduction */+    for(i = 0; i < K; i++) {+        bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);+    }++    /* undo the argument reduction */+    bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP);++    return BF_ST_INEXACT;+}++/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */+static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r,+                                        const bf_t *a_low, const bf_t *a_high,+                                        limb_t prec, bf_flags_t flags)+{+    bf_t T_s, *T = &T_s;+    bf_t log2_s, *log2 = &log2_s;+    slimb_t e_min, e_max;+    +    if (a_high->expn <= 0)+        return 0;++    e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1);+    e_min = -e_max + 3;+    if (flags & BF_FLAG_SUBNORMAL)+        e_min -= (prec - 1);+    +    bf_init(s, T);+    bf_init(s, log2);+    bf_const_log2(log2, LIMB_BITS, BF_RNDU);+    bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU);+    /* a_low > e_max * log(2) implies exp(a) > e_max */+    if (bf_cmp_lt(T, a_low) > 0) {+        /* overflow */+        bf_delete(T);+        bf_delete(log2);+        return bf_set_overflow(r, 0, prec, flags);+    }+    /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */+    bf_const_log2(log2, LIMB_BITS, BF_RNDD);+    bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD);+    if (bf_cmp_lt(a_high, T)) {+        int rnd_mode = flags & BF_RND_MASK;+        +        /* underflow */+        bf_delete(T);+        bf_delete(log2);+        if (rnd_mode == BF_RNDU) {+            /* set the smallest value */+            bf_set_ui(r, 1);+            r->expn = e_min;+        } else {+            bf_set_zero(r, 0);+        }+        return BF_ST_UNDERFLOW | BF_ST_INEXACT;+    }+    bf_delete(log2);+    bf_delete(T);+    return 0;+}++int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    int ret;+    assert(r != a);+    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+        } else if (a->expn == BF_EXP_INF) {+            if (a->sign)+                bf_set_zero(r, 0);+            else+                bf_set_inf(r, 0);+        } else {+            bf_set_ui(r, 1);+        }+        return 0;+    }++    ret = check_exp_underflow_overflow(s, r, a, a, prec, flags);+    if (ret)+        return ret;+    if (a->expn < 0 && (-a->expn) >= (prec + 2)) { +        /* small argument case: result = 1 + epsilon * sign(x) */+        bf_set_ui(r, 1);+        return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags);+    }+                         +    return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL);+}++static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    bf_t U_s, *U = &U_s;+    bf_t V_s, *V = &V_s;+    slimb_t n, prec1, l, i, K;+    +    assert(r != a);++    bf_init(s, T);+    /* argument reduction 1 */+    /* T=a*2^n with 2/3 <= T <= 4/3 */+    {+        bf_t U_s, *U = &U_s;+        bf_set(T, a);+        n = T->expn;+        T->expn = 0;+        /* U= ~ 2/3 */+        bf_init(s, U);+        bf_set_ui(U, 0xaaaaaaaa); +        U->expn = 0;+        if (bf_cmp_lt(T, U)) {+            T->expn++;+            n--;+        }+        bf_delete(U);+    }+    //    printf("n=%ld\n", n);+    //    bf_print_str("T", T);++    /* XXX: precision analysis */+    /* number of iterations for argument reduction 2 */+    K = bf_isqrt((prec + 1) / 2); +    /* order of Taylor expansion */+    l = prec / (2 * K) + 1; +    /* precision of the intermediate computations */+    prec1 = prec + K + 2 * l + 32;++    bf_init(s, U);+    bf_init(s, V);+    +    /* Note: cancellation occurs here, so we use more precision (XXX:+       reduce the precision by computing the exact cancellation) */+    bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN); ++    /* argument reduction 2 */+    for(i = 0; i < K; i++) {+        /* T = T / (1 + sqrt(1 + T)) */+        bf_add_si(U, T, 1, prec1, BF_RNDN);+        bf_sqrt(V, U, prec1, BF_RNDF);+        bf_add_si(U, V, 1, prec1, BF_RNDN);+        bf_div(T, T, U, prec1, BF_RNDN);+    }++    {+        bf_t Y_s, *Y = &Y_s;+        bf_t Y2_s, *Y2 = &Y2_s;+        bf_init(s, Y);+        bf_init(s, Y2);++        /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x)+           = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1) +           with Y=Y^2+           = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...)))+        */+        bf_add_si(Y, T, 2, prec1, BF_RNDN);+        bf_div(Y, T, Y, prec1, BF_RNDN);++        bf_mul(Y2, Y, Y, prec1, BF_RNDN);+        bf_set_ui(r, 0);+        for(i = l; i >= 1; i--) {+            bf_set_ui(U, 1);+            bf_set_ui(V, 2 * i + 1);+            bf_div(U, U, V, prec1, BF_RNDN);+            bf_add(r, r, U, prec1, BF_RNDN);+            bf_mul(r, r, Y2, prec1, BF_RNDN);+        }+        bf_add_si(r, r, 1, prec1, BF_RNDN);+        bf_mul(r, r, Y, prec1, BF_RNDN);+        bf_delete(Y);+        bf_delete(Y2);+    }+    bf_delete(V);+    bf_delete(U);++    /* multiplication by 2 for the Taylor expansion and undo the+       argument reduction 2*/+    bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ);+    +    /* undo the argument reduction 1 */+    bf_const_log2(T, prec1, BF_RNDF);+    bf_mul_si(T, T, n, prec1, BF_RNDN);+    bf_add(r, r, T, prec1, BF_RNDN);+    +    bf_delete(T);+    return BF_ST_INEXACT;+}++int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    +    assert(r != a);+    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF) {+            if (a->sign) {+                bf_set_nan(r);+                return BF_ST_INVALID_OP;+            } else {+                bf_set_inf(r, 0);+                return 0;+            }+        } else {+            bf_set_inf(r, 1);+            return 0;+        }+    }+    if (a->sign) {+        bf_set_nan(r);+        return BF_ST_INVALID_OP;+    }+    bf_init(s, T);+    bf_set_ui(T, 1);+    if (bf_cmp_eq(a, T)) {+        bf_set_zero(r, 0);+        bf_delete(T);+        return 0;+    }+    bf_delete(T);++    return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL);+}++/* x and y finite and x > 0 */+static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    const bf_t *y = opaque;+    bf_t T_s, *T = &T_s;+    limb_t prec1;++    bf_init(s, T);+    /* XXX: proof for the added precision */+    prec1 = prec + 32;+    bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP);+    bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP);+    if (bf_is_nan(T))+        bf_set_nan(r);+    else+        bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */+    bf_delete(T);+    return BF_ST_INEXACT;+}++/* x and y finite, x > 0, y integer and y fits on one limb */+static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    const bf_t *y = opaque;+    bf_t T_s, *T = &T_s;+    limb_t prec1;+    int ret;+    slimb_t y1;+    +    bf_get_limb(&y1, y, 0);+    if (y1 < 0)+        y1 = -y1;+    /* XXX: proof for the added precision */+    prec1 = prec + ceil_log2(y1) * 2 + 8;+    ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP);+    if (y->sign) {+        bf_init(s, T);+        bf_set_ui(T, 1);+        ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);+        bf_delete(T);+    }+    return ret;+}++/* x must be a finite non zero float. Return TRUE if there is a+   floating point number r such as x=r^(2^n) and return this floating+   point number 'r'. Otherwise return FALSE and r is undefined. */+static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    slimb_t e, i, er;+    limb_t v;+    +    /* x = m*2^e with m odd integer */+    e = bf_get_exp_min(x);+    /* fast check on the exponent */+    if (n > (LIMB_BITS - 1)) {+        if (e != 0)+            return FALSE;+        er = 0;+    } else {+        if ((e & (((limb_t)1 << n) - 1)) != 0)+            return FALSE;+        er = e >> n;+    }+    /* every perfect odd square = 1 modulo 8 */+    v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e);+    if ((v & 7) != 1)+        return FALSE;++    bf_init(s, T);+    bf_set(T, x);+    T->expn -= e;+    for(i = 0; i < n; i++) {+        if (i != 0)+            bf_set(T, r);+        if (bf_sqrtrem(r, NULL, T) != 0)+            return FALSE;+    }+    r->expn += er;+    return TRUE;+}++/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */+int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    bf_t ytmp_s;+    BOOL y_is_int, y_is_odd;+    int r_sign, ret, rnd_mode;+    slimb_t y_emin;+    +    if (x->len == 0 || y->len == 0) {+        if (y->expn == BF_EXP_ZERO) {+            /* pow(x, 0) = 1 */+            bf_set_ui(r, 1);+        } else if (x->expn == BF_EXP_NAN) {+            bf_set_nan(r);+        } else {+            int cmp_x_abs_1;+            bf_set_ui(r, 1);+            cmp_x_abs_1 = bf_cmpu(x, r);+            if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) &&+                (y->expn >= BF_EXP_INF)) {+                bf_set_nan(r);+            } else if (cmp_x_abs_1 == 0 &&+                       (!x->sign || y->expn != BF_EXP_NAN)) {+                /* pow(1, y) = 1 even if y = NaN */+                /* pow(-1, +/-inf) = 1 */+            } else if (y->expn == BF_EXP_NAN) {+                bf_set_nan(r);+            } else if (y->expn == BF_EXP_INF) {+                if (y->sign == (cmp_x_abs_1 > 0)) {+                    bf_set_zero(r, 0);+                } else {+                    bf_set_inf(r, 0);+                }+            } else {+                y_emin = bf_get_exp_min(y);+                y_is_odd = (y_emin == 0);+                if (y->sign == (x->expn == BF_EXP_ZERO)) {+                    bf_set_inf(r, y_is_odd & x->sign);+                    if (y->sign) {+                        /* pow(0, y) with y < 0 */+                        return BF_ST_DIVIDE_ZERO;+                    }+                } else {+                    bf_set_zero(r, y_is_odd & x->sign);+                }+            }+        }+        return 0;+    }+    bf_init(s, T);+    bf_set(T, x);+    y_emin = bf_get_exp_min(y);+    y_is_int = (y_emin >= 0);+    rnd_mode = flags & BF_RND_MASK;+    if (x->sign) {+        if (!y_is_int) {+            bf_set_nan(r);+            bf_delete(T);+            return BF_ST_INVALID_OP;+        }+        y_is_odd = (y_emin == 0);+        r_sign = y_is_odd;+        /* change the directed rounding mode if the sign of the result+           is changed */+        if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU))+            flags ^= 1;+        bf_neg(T);+    } else {+        r_sign = 0;+    }++    bf_set_ui(r, 1);+    if (bf_cmp_eq(T, r)) {+        /* abs(x) = 1: nothing more to do */+        ret = 0;+    } else {+        /* check the overflow/underflow cases */+        {+            bf_t al_s, *al = &al_s;+            bf_t ah_s, *ah = &ah_s;+            limb_t precl = LIMB_BITS;+            +            bf_init(s, al);+            bf_init(s, ah);+            /* compute bounds of log(abs(x)) * y with a low precision */+            /* XXX: compute bf_log() once */+            /* XXX: add a fast test before this slow test */+            bf_log(al, T, precl, BF_RNDD);+            bf_log(ah, T, precl, BF_RNDU);+            bf_mul(al, al, y, precl, BF_RNDD ^ y->sign);+            bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign);+            ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags);+            bf_delete(al);+            bf_delete(ah);+            if (ret)+                goto done;+        }+        +        if (y_is_int) {+            slimb_t T_bits, e;+        int_pow:+            T_bits = T->expn - bf_get_exp_min(T);+            if (T_bits == 1) {+                /* pow(2^b, y) = 2^(b*y) */+                bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ);+                bf_get_limb(&e, T, 0);+                bf_set_ui(r, 1);+                ret = bf_mul_2exp(r, e, prec, flags);+            } else if (prec == BF_PREC_INF) {+                slimb_t y1;+                /* specific case for infinite precision (integer case) */+                bf_get_limb(&y1, y, 0);+                assert(!y->sign);+                /* x must be an integer, so abs(x) >= 2 */+                if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) {+                    bf_delete(T);+                    return bf_set_overflow(r, 0, BF_PREC_INF, flags);+                }+                ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ);+            } else {+                if (y->expn <= 31) {+                    /* small enough power: use exponentiation in all cases */+                } else if (y->sign) {+                    /* cannot be exact */+                    goto general_case;+                } else {+                    if (rnd_mode == BF_RNDF)+                        goto general_case; /* no need to track exact results */+                    /* see if the result has a chance to be exact:+                       if x=a*2^b (a odd), x^y=a^y*2^(b*y)+                       x^y needs a precision of at least floor_log2(a)*y bits+                    */+                    bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ);+                    bf_get_limb(&e, r, 0);+                    if (prec < e)+                        goto general_case;+                }+                ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y);+            }+        } else {+            if (rnd_mode != BF_RNDF) {+                bf_t *y1;+                if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) {+                    /* the problem is reduced to a power to an integer */+#if 0+                    printf("\nn=%" PRId64 "\n", -(int64_t)y_emin);+                    bf_print_str("T", T);+                    bf_print_str("r", r);+#endif+                    bf_set(T, r);+                    y1 = &ytmp_s;+                    y1->tab = y->tab;+                    y1->len = y->len;+                    y1->sign = y->sign;+                    y1->expn = y->expn - y_emin;+                    y = y1;+                    goto int_pow;+                }+            }+        general_case:+            ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y);+        }+    }+ done:+    bf_delete(T);+    r->sign = r_sign;+    return ret;+}++/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */+static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    bf_init(s, T);+    bf_set(T, x);+    bf_mul(r, T, T, prec1, BF_RNDN);+    bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ);+    bf_add(T, T, r, prec1, BF_RNDN);+    bf_neg(T);+    bf_sqrt(r, T, prec1, BF_RNDF);+    bf_delete(T);+}++static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec)+{+    bf_context_t *s1 = a->ctx;+    bf_t T_s, *T = &T_s;+    bf_t U_s, *U = &U_s;+    bf_t r_s, *r = &r_s;+    slimb_t K, prec1, i, l, mod, prec2;+    int is_neg;+    +    assert(c != a && s != a);++    bf_init(s1, T);+    bf_init(s1, U);+    bf_init(s1, r);+    +    /* XXX: precision analysis */+    K = bf_isqrt(prec / 2);+    l = prec / (2 * K) + 1;+    prec1 = prec + 2 * K + l + 8;+    +    /* after the modulo reduction, -pi/4 <= T <= pi/4 */+    if (a->expn <= -1) {+        /* abs(a) <= 0.25: no modulo reduction needed */+        bf_set(T, a);+        mod = 0;+    } else {+        slimb_t cancel;+        cancel = 0;+        for(;;) {+            prec2 = prec1 + a->expn + cancel;+            bf_const_pi(U, prec2, BF_RNDF);+            bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ);+            bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN);+            //            printf("T.expn=%ld prec2=%ld\n", T->expn, prec2);+            if (mod == 0 || (T->expn != BF_EXP_ZERO &&+                             (T->expn + prec2) >= (prec1 - 1)))+                break;+            /* increase the number of bits until the precision is good enough */+            cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2);+        }+        mod &= 3;+    }+    +    is_neg = T->sign;+        +    /* compute cosm1(x) = cos(x) - 1 */+    bf_mul(T, T, T, prec1, BF_RNDN);+    bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ);+    +    /* Taylor expansion:+       -x^2/2 + x^4/4! - x^6/6! + ...+    */+    bf_set_ui(r, 1);+    for(i = l ; i >= 1; i--) {+        bf_set_ui(U, 2 * i - 1);+        bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ);+        bf_div(U, T, U, prec1, BF_RNDN);+        bf_mul(r, r, U, prec1, BF_RNDN);+        bf_neg(r);+        if (i != 1)+            bf_add_si(r, r, 1, prec1, BF_RNDN);+    }+    bf_delete(U);++    /* undo argument reduction:+       cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2)+    */+    for(i = 0; i < K; i++) {+        bf_mul(T, r, r, prec1, BF_RNDN);+        bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ);+        bf_add(r, r, T, prec1, BF_RNDN);+        bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ);+    }+    bf_delete(T);++    if (c) {+        if ((mod & 1) == 0) {+            bf_add_si(c, r, 1, prec1, BF_RNDN);+        } else {+            bf_sqrt_sin(c, r, prec1);+            c->sign = is_neg ^ 1;+        }+        c->sign ^= mod >> 1;+    }+    if (s) {+        if ((mod & 1) == 0) {+            bf_sqrt_sin(s, r, prec1);+            s->sign = is_neg;+        } else {+            bf_add_si(s, r, 1, prec1, BF_RNDN);+        }+        s->sign ^= mod >> 1;+    }+    bf_delete(r);+    return BF_ST_INEXACT;+}++static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)+{+    return bf_sincos(NULL, r, a, prec);+}++int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_set_ui(r, 1);+            return 0;+        }+    }++    /* small argument case: result = 1+r(x) with r(x) = -x^2/2 ++       O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */+    if (a->expn < 0) {+        slimb_t e;+        e = 2 * a->expn - 1;+        if (e < -(prec + 2)) {+            bf_set_ui(r, 1);+            return bf_add_epsilon(r, r, e, 1, prec, flags);+        }+    }+    +    return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL);+}++static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)+{+    return bf_sincos(r, NULL, a, prec);+}++int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_set_zero(r, a->sign);+            return 0;+        }+    }++    /* small argument case: result = x+r(x) with r(x) = -x^3/6 ++       O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */+    if (a->expn < 0) {+        slimb_t e;+        e = sat_add(2 * a->expn, a->expn - 2);+        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {+            bf_set(r, a);+            return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags);+        }+    }++    return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL);+}++static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    limb_t prec1;+    +    /* XXX: precision analysis */+    prec1 = prec + 8;+    bf_init(s, T);+    bf_sincos(r, T, a, prec1);+    bf_div(r, r, T, prec1, BF_RNDF);+    bf_delete(T);+    return BF_ST_INEXACT;+}++int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    assert(r != a);+    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_set_zero(r, a->sign);+            return 0;+        }+    }++    /* small argument case: result = x+r(x) with r(x) = x^3/3 ++       O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */+    if (a->expn < 0) {+        slimb_t e;+        e = sat_add(2 * a->expn, a->expn - 1);+        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {+            bf_set(r, a);+            return bf_add_epsilon(r, r, e, a->sign, prec, flags);+        }+    }+            +    return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL);+}++/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to+   avoid cancellation) */+static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec,+                            void *opaque)+{+    bf_context_t *s = r->ctx;+    BOOL add_pi2 = (BOOL)(intptr_t)opaque;+    bf_t T_s, *T = &T_s;+    bf_t U_s, *U = &U_s;+    bf_t V_s, *V = &V_s;+    bf_t X2_s, *X2 = &X2_s;+    int cmp_1;+    slimb_t prec1, i, K, l;+    +    /* XXX: precision analysis */+    K = bf_isqrt((prec + 1) / 2);+    l = prec / (2 * K) + 1;+    prec1 = prec + K + 2 * l + 32;+    //    printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1);+    +    bf_init(s, T);+    cmp_1 = (a->expn >= 1); /* a >= 1 */+    if (cmp_1) {+        bf_set_ui(T, 1);+        bf_div(T, T, a, prec1, BF_RNDN);+    } else {+        bf_set(T, a);+    }++    /* abs(T) <= 1 */++    /* argument reduction */++    bf_init(s, U);+    bf_init(s, V);+    bf_init(s, X2);+    for(i = 0; i < K; i++) {+        /* T = T / (1 + sqrt(1 + T^2)) */+        bf_mul(U, T, T, prec1, BF_RNDN);+        bf_add_si(U, U, 1, prec1, BF_RNDN);+        bf_sqrt(V, U, prec1, BF_RNDN);+        bf_add_si(V, V, 1, prec1, BF_RNDN);+        bf_div(T, T, V, prec1, BF_RNDN);+    }++    /* Taylor series: +       x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1) +    */+    bf_mul(X2, T, T, prec1, BF_RNDN);+    bf_set_ui(r, 0);+    for(i = l; i >= 1; i--) {+        bf_set_si(U, 1);+        bf_set_ui(V, 2 * i + 1);+        bf_div(U, U, V, prec1, BF_RNDN);+        bf_neg(r);+        bf_add(r, r, U, prec1, BF_RNDN);+        bf_mul(r, r, X2, prec1, BF_RNDN);+    }+    bf_neg(r);+    bf_add_si(r, r, 1, prec1, BF_RNDN);+    bf_mul(r, r, T, prec1, BF_RNDN);++    /* undo the argument reduction */+    bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ);+    +    bf_delete(U);+    bf_delete(V);+    bf_delete(X2);++    i = add_pi2;+    if (cmp_1 > 0) {+        /* undo the inversion : r = sign(a)*PI/2 - r */+        bf_neg(r);+        i += 1 - 2 * a->sign;+    }+    /* add i*(pi/2) with -1 <= i <= 2 */+    if (i != 0) {+        bf_const_pi(T, prec1, BF_RNDF);+        if (i != 2)+            bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ);+        T->sign = (i < 0);+        bf_add(r, T, r, prec1, BF_RNDN);+    }+    +    bf_delete(T);+    return BF_ST_INEXACT;+}++int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    int res;+    +    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF)  {+            /* -PI/2 or PI/2 */+            bf_const_pi_signed(r, a->sign, prec, flags);+            bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);+            return BF_ST_INEXACT;+        } else {+            bf_set_zero(r, a->sign);+            return 0;+        }+    }+    +    bf_init(s, T);+    bf_set_ui(T, 1);+    res = bf_cmpu(a, T);+    bf_delete(T);+    if (res == 0) {+        /* short cut: abs(a) == 1 -> +/-pi/4 */+        bf_const_pi_signed(r, a->sign, prec, flags);+        bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ);+        return BF_ST_INEXACT;+    }++    /* small argument case: result = x+r(x) with r(x) = -x^3/3 ++       O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */+    if (a->expn < 0) {+        slimb_t e;+        e = sat_add(2 * a->expn, a->expn - 1);+        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {+            bf_set(r, a);+            return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags);+        }+    }+    +    return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE);+}++static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    const bf_t *x = opaque;+    bf_t T_s, *T = &T_s;+    limb_t prec1;+    int ret;+    +    if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) {+        bf_set_nan(r);+        return 0;+    }++    /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */+    bf_init(s, T);+    prec1 = prec + 32;+    if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) {+        bf_set_ui(T, 1);+        T->sign = y->sign ^ x->sign;+    } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) {+        bf_set_zero(T, y->sign ^ x->sign);+    } else {+        bf_div(T, y, x, prec1, BF_RNDF);+    }+    ret = bf_atan(r, T, prec1, BF_RNDF);++    if (x->sign) {+        /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */+        bf_const_pi(T, prec1, BF_RNDF);+        T->sign = y->sign;+        bf_add(r, r, T, prec1, BF_RNDN);+        ret |= BF_ST_INEXACT;+    }++    bf_delete(T);+    return ret;+}++int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,+             limb_t prec, bf_flags_t flags)+{+    return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x);+}++static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)+{+    bf_context_t *s = r->ctx;+    BOOL is_acos = (BOOL)(intptr_t)opaque;+    bf_t T_s, *T = &T_s;+    limb_t prec1, prec2;+    +    /* asin(x) = atan(x/sqrt(1-x^2)) +       acos(x) = pi/2 - asin(x) */+    prec1 = prec + 8;+    /* increase the precision in x^2 to compensate the cancellation in+       (1-x^2) if x is close to 1 */+    /* XXX: use less precision when possible */+    if (a->expn >= 0)+        prec2 = BF_PREC_INF;+    else+        prec2 = prec1;+    bf_init(s, T);+    bf_mul(T, a, a, prec2, BF_RNDN);+    bf_neg(T);+    bf_add_si(T, T, 1, prec2, BF_RNDN);++    bf_sqrt(r, T, prec1, BF_RNDN);+    bf_div(T, a, r, prec1, BF_RNDN);+    if (is_acos)+        bf_neg(T);+    bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos);+    bf_delete(T);+    return BF_ST_INEXACT;+}++int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    int res;++    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_set_zero(r, a->sign);+            return 0;+        }+    }+    bf_init(s, T);+    bf_set_ui(T, 1);+    res = bf_cmpu(a, T);+    bf_delete(T);+    if (res > 0) {+        bf_set_nan(r);+        return BF_ST_INVALID_OP;+    }+    +    /* small argument case: result = x+r(x) with r(x) = x^3/6 ++       O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */+    if (a->expn < 0) {+        slimb_t e;+        e = sat_add(2 * a->expn, a->expn - 2);+        if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {+            bf_set(r, a);+            return bf_add_epsilon(r, r, e, a->sign, prec, flags);+        }+    }++    return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE);+}++int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = r->ctx;+    bf_t T_s, *T = &T_s;+    int res;++    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bf_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF) {+            bf_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bf_const_pi(r, prec, flags);+            bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);+            return BF_ST_INEXACT;+        }+    }+    bf_init(s, T);+    bf_set_ui(T, 1);+    res = bf_cmpu(a, T);+    bf_delete(T);+    if (res > 0) {+        bf_set_nan(r);+        return BF_ST_INVALID_OP;+    } else if (res == 0 && a->sign == 0) {+        bf_set_zero(r, 0);+        return 0;+    }+    +    return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE);+}++/***************************************************************/+/* decimal floating point numbers */++#ifdef USE_BF_DEC++#define adddq(r1, r0, a1, a0)                   \+    do {                                        \+        limb_t __t = r0;                        \+        r0 += (a0);                             \+        r1 += (a1) + (r0 < __t);                \+    } while (0)++#define subdq(r1, r0, a1, a0)                   \+    do {                                        \+        limb_t __t = r0;                        \+        r0 -= (a0);                             \+        r1 -= (a1) + (r0 > __t);                \+    } while (0)++#if LIMB_BITS == 64++/* Note: we assume __int128 is available */+#define muldq(r1, r0, a, b)                     \+    do {                                        \+        unsigned __int128 __t;                          \+        __t = (unsigned __int128)(a) * (unsigned __int128)(b);  \+        r0 = __t;                               \+        r1 = __t >> 64;                         \+    } while (0)++#define divdq(q, r, a1, a0, b)                  \+    do {                                        \+        unsigned __int128 __t;                  \+        limb_t __b = (b);                       \+        __t = ((unsigned __int128)(a1) << 64) | (a0);   \+        q = __t / __b;                                  \+        r = __t % __b;                                  \+    } while (0)++#else++#define muldq(r1, r0, a, b)                     \+    do {                                        \+        uint64_t __t;                          \+        __t = (uint64_t)(a) * (uint64_t)(b);  \+        r0 = __t;                               \+        r1 = __t >> 32;                         \+    } while (0)++#define divdq(q, r, a1, a0, b)                  \+    do {                                        \+        uint64_t __t;                  \+        limb_t __b = (b);                       \+        __t = ((uint64_t)(a1) << 32) | (a0);   \+        q = __t / __b;                                  \+        r = __t % __b;                                  \+    } while (0)++#endif /* LIMB_BITS != 64 */++static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift)+{+    if (shift != 0)+        low = (low >> shift) | (high << (LIMB_BITS - shift));+    return low;+}++static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift)+{+    if (shift != 0)+        return (a1 << shift) | (a0 >> (LIMB_BITS - shift));+    else+        return a1;+}++#if LIMB_DIGITS == 19++/* WARNING: hardcoded for b = 1e19. It is assumed that:+   0 <= a1 < 2^63 */+#define divdq_base(q, r, a1, a0)\+do {\+    uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \+    __a0 = a0;\+    __a1 = a1;\+    __t0 = __a1;\+    __t0 = shld(__t0, __a0, 1);\+    muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \+    muldq(__t1, __t0, q, __b);\+    subdq(__a1, __a0, __t1, __t0);\+    subdq(__a1, __a0, 1, __b * 2);    \+    __t0 = (slimb_t)__a1 >> 1; \+    q += 2 + __t0;\+    adddq(__a1, __a0, 0, __b & __t0);\+    q += __a1;                  \+    __a0 += __b & __a1;           \+    r = __a0;\+} while(0)++#elif LIMB_DIGITS == 9++/* WARNING: hardcoded for b = 1e9. It is assumed that:+   0 <= a1 < 2^29 */+#define divdq_base(q, r, a1, a0)\+do {\+    uint32_t __t0, __t1, __b = BF_DEC_BASE; \+    __t0 = a1;\+    __t1 = a0;\+    __t0 = (__t0 << 3) | (__t1 >> (32 - 3));    \+    muldq(q, __t1, __t0, 2305843009U);\+    r = a0 - q * __b;\+    __t1 = (r >= __b);\+    q += __t1;\+    if (__t1)\+        r -= __b;\+} while(0)++#endif++/* fast integer division by a fixed constant */++typedef struct FastDivData {+    limb_t m1; /* multiplier */+    int8_t shift1;+    int8_t shift2;+} FastDivData;++/* From "Division by Invariant Integers using Multiplication" by+   Torborn Granlund and Peter L. Montgomery */+/* d must be != 0 */+static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d)+{+    int l;+    limb_t q, r, m1;+    if (d == 1)+        l = 0;+    else+        l = 64 - clz64(d - 1);+    divdq(q, r, ((limb_t)1 << l) - d, 0, d);+    (void)r;+    m1 = q + 1;+    //    printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1);+    s->m1 = m1;+    s->shift1 = l;+    if (s->shift1 > 1)+        s->shift1 = 1;+    s->shift2 = l - 1;+    if (s->shift2 < 0)+        s->shift2 = 0;+}++static inline limb_t fast_udiv(limb_t a, const FastDivData *s)+{+    limb_t t0, t1;+    muldq(t1, t0, s->m1, a);+    t0 = (a - t1) >> s->shift1;+    return (t1 + t0) >> s->shift2;+}++/* contains 10^i */+const limb_t mp_pow_dec[LIMB_DIGITS + 1] = {+    1U,+    10U,+    100U,+    1000U,+    10000U,+    100000U,+    1000000U,+    10000000U,+    100000000U,+    1000000000U,+#if LIMB_BITS == 64+    10000000000U,+    100000000000U,+    1000000000000U,+    10000000000000U,+    100000000000000U,+    1000000000000000U,+    10000000000000000U,+    100000000000000000U,+    1000000000000000000U,+    10000000000000000000U,+#endif+};++/* precomputed from fast_udiv_init(10^i) */+static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = {+#if LIMB_BITS == 32+    { 0x00000001, 0, 0 },+    { 0x9999999a, 1, 3 },+    { 0x47ae147b, 1, 6 },+    { 0x0624dd30, 1, 9 },+    { 0xa36e2eb2, 1, 13 },+    { 0x4f8b588f, 1, 16 },+    { 0x0c6f7a0c, 1, 19 },+    { 0xad7f29ac, 1, 23 },+    { 0x5798ee24, 1, 26 },+    { 0x12e0be83, 1, 29 },+#else+    { 0x0000000000000001, 0, 0 },+    { 0x999999999999999a, 1, 3 },+    { 0x47ae147ae147ae15, 1, 6 },+    { 0x0624dd2f1a9fbe77, 1, 9 },+    { 0xa36e2eb1c432ca58, 1, 13 },+    { 0x4f8b588e368f0847, 1, 16 },+    { 0x0c6f7a0b5ed8d36c, 1, 19 },+    { 0xad7f29abcaf48579, 1, 23 },+    { 0x5798ee2308c39dfa, 1, 26 },+    { 0x12e0be826d694b2f, 1, 29 },+    { 0xb7cdfd9d7bdbab7e, 1, 33 },+    { 0x5fd7fe17964955fe, 1, 36 },+    { 0x19799812dea11198, 1, 39 },+    { 0xc25c268497681c27, 1, 43 },+    { 0x6849b86a12b9b01f, 1, 46 },+    { 0x203af9ee756159b3, 1, 49 },+    { 0xcd2b297d889bc2b7, 1, 53 },+    { 0x70ef54646d496893, 1, 56 },+    { 0x2725dd1d243aba0f, 1, 59 },+    { 0xd83c94fb6d2ac34d, 1, 63 },+#endif+};++/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */+static inline limb_t fast_shr_dec(limb_t a, int shift)+{+    return fast_udiv(a, &mp_pow_div[shift]);+}++/* division and remainder by 10^shift */+#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift]+    +limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, +                  mp_size_t n, limb_t carry)+{+    limb_t base = BF_DEC_BASE;+    mp_size_t i;+    limb_t k, a, v;++    k=carry;+    for(i=0;i<n;i++) {+        /* XXX: reuse the trick in add_mod */+        v = op1[i];+        a = v + op2[i] + k - base;+        k = a <= v;+        if (!k) +            a += base;+        res[i]=a;+    }+    return k;+}++limb_t mp_add_ui_dec(limb_t *tab, limb_t b, mp_size_t n)+{+    limb_t base = BF_DEC_BASE;+    mp_size_t i;+    limb_t k, a, v;++    k=b;+    for(i=0;i<n;i++) {+        v = tab[i];+        a = v + k - base;+        k = a <= v;+        if (!k) +            a += base;+        tab[i] = a;+        if (k == 0)+            break;+    }+    return k;+}++limb_t mp_sub_dec(limb_t *res, const limb_t *op1, const limb_t *op2, +                  mp_size_t n, limb_t carry)+{+    limb_t base = BF_DEC_BASE;+    mp_size_t i;+    limb_t k, v, a;++    k=carry;+    for(i=0;i<n;i++) {+        v = op1[i];+        a = v - op2[i] - k;+        k = a > v;+        if (k)+            a += base;+        res[i] = a;+    }+    return k;+}++limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n)+{+    limb_t base = BF_DEC_BASE;+    mp_size_t i;+    limb_t k, v, a;+    +    k=b;+    for(i=0;i<n;i++) {+        v = tab[i];+        a = v - k;+        k = a > v;+        if (k)+            a += base;+        tab[i]=a;+        if (k == 0)+            break;+    }+    return k;+}++/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */+limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, +                   limb_t b, limb_t l)+{+    mp_size_t i;+    limb_t t0, t1, r;++    for(i = 0; i < n; i++) {+        muldq(t1, t0, taba[i], b);+        adddq(t1, t0, 0, l);+        divdq_base(l, r, t1, t0);+        tabr[i] = r;+    }+    return l;+}++/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add+   to the high word */+limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,+                       limb_t b)+{+    mp_size_t i;+    limb_t l, t0, t1, r;++    l = 0;+    for(i = 0; i < n; i++) {+        muldq(t1, t0, taba[i], b);+        adddq(t1, t0, 0, l);+        adddq(t1, t0, 0, tabr[i]);+        divdq_base(l, r, t1, t0);+        tabr[i] = r;+    }+    return l;+}++/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to+   substract to the high word. */+limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,+                       limb_t b)+{+    limb_t base = BF_DEC_BASE;+    mp_size_t i;+    limb_t l, t0, t1, r, a, v, c;++    /* XXX: optimize */+    l = 0;+    for(i = 0; i < n; i++) {+        muldq(t1, t0, taba[i], b);+        adddq(t1, t0, 0, l);+        divdq_base(l, r, t1, t0);+        v = tabr[i];+        a = v - r;+        c = a > v;+        if (c)+            a += base;+        /* never bigger than base because r = 0 when l = base - 1 */+        l += c;+        tabr[i] = a;+    }+    return l;+}++/* size of the result : op1_size + op2_size. */+void mp_mul_basecase_dec(limb_t *result, +                         const limb_t *op1, mp_size_t op1_size, +                         const limb_t *op2, mp_size_t op2_size) +{+    mp_size_t i;+    limb_t r;+    +    result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0);++    for(i=1;i<op2_size;i++) {+        r = mp_add_mul1_dec(result + i, op1, op1_size, op2[i]);+        result[i + op1_size] = r;+    }+}++/* taba[] = (taba[] + r*base^na) / b. 0 <= b < base. 0 <= r <+   b. Return the remainder. */+limb_t mp_div1_dec(limb_t *tabr, const limb_t *taba, mp_size_t na, +                   limb_t b, limb_t r)+{+    limb_t base = BF_DEC_BASE;+    mp_size_t i;+    limb_t t0, t1, q;+    int shift;++#if (BF_DEC_BASE % 2) == 0+    if (b == 2) {+        limb_t base_div2;+        /* Note: only works if base is even */+        base_div2 = base >> 1;+        if (r)+            r = base_div2;+        for(i = na - 1; i >= 0; i--) {+            t0 = taba[i];+            tabr[i] = (t0 >> 1) + r;+            r = 0;+            if (t0 & 1)+                r = base_div2;+        }+        if (r)+            r = 1;+    } else +#endif+    if (na >= UDIV1NORM_THRESHOLD) {+        shift = clz(b);+        if (shift == 0) {+            /* normalized case: b >= 2^(LIMB_BITS-1) */+            limb_t b_inv;+            b_inv = udiv1norm_init(b);+            for(i = na - 1; i >= 0; i--) {+                muldq(t1, t0, r, base);+                adddq(t1, t0, 0, taba[i]);+                q = udiv1norm(&r, t1, t0, b, b_inv);+                tabr[i] = q;+            }+        } else {+            limb_t b_inv;+            b <<= shift;+            b_inv = udiv1norm_init(b);+            for(i = na - 1; i >= 0; i--) {+                muldq(t1, t0, r, base);+                adddq(t1, t0, 0, taba[i]);+                t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift));+                t0 <<= shift;+                q = udiv1norm(&r, t1, t0, b, b_inv);+                r >>= shift;+                tabr[i] = q;+            }+        }+    } else {+        for(i = na - 1; i >= 0; i--) {+            muldq(t1, t0, r, base);+            adddq(t1, t0, 0, taba[i]);+            divdq(q, r, t1, t0, b);+            tabr[i] = q;+        }+    }+    return r;+}++static __maybe_unused void mp_print_str_dec(const char *str,+                                       const limb_t *tab, slimb_t n)+{+    slimb_t i;+    printf("%s=", str);+    for(i = n - 1; i >= 0; i--) {+        if (i != n - 1)+            printf("_");+        printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]);+    }+    printf("\n");+}++static __maybe_unused void mp_print_str_h_dec(const char *str,+                                              const limb_t *tab, slimb_t n,+                                              limb_t high)+{+    slimb_t i;+    printf("%s=", str);+    printf("%0*" PRIu_LIMB, LIMB_DIGITS, high);+    for(i = n - 1; i >= 0; i--) {+        printf("_");+        printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]);+    }+    printf("\n");+}++//#define DEBUG_DIV_SLOW++#define DIV_STATIC_ALLOC_LEN 16++/* return q = a / b and r = a % b. ++   taba[na] must be allocated if tabb1[nb - 1] < B / 2.  tabb1[nb - 1]+   must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1]+   >= B / 2.++   The remainder is is returned in taba and contains nb libms. tabq+   contains na - nb + 1 limbs. No overlap is permitted.++   Running time of the standard method: (na - nb + 1) * nb+   Return 0 if OK, -1 if memory alloc error+*/+/* XXX: optimize */+static int mp_div_dec(bf_context_t *s, limb_t *tabq,+                      limb_t *taba, mp_size_t na, +                      const limb_t *tabb1, mp_size_t nb)+{+    limb_t base = BF_DEC_BASE;+    limb_t r, mult, t0, t1, a, c, q, v, *tabb;+    mp_size_t i, j;+    limb_t static_tabb[DIV_STATIC_ALLOC_LEN];+    +#ifdef DEBUG_DIV_SLOW+    mp_print_str_dec("a", taba, na);+    mp_print_str_dec("b", tabb1, nb);+#endif++    /* normalize tabb */+    r = tabb1[nb - 1];+    assert(r != 0);+    i = na - nb;+    if (r >= BF_DEC_BASE / 2) {+        mult = 1;+        tabb = (limb_t *)tabb1;+        q = 1;+        for(j = nb - 1; j >= 0; j--) {+            if (taba[i + j] != tabb[j]) {+                if (taba[i + j] < tabb[j])+                    q = 0;+                break;+            }+        }+        tabq[i] = q;+        if (q) {+            mp_sub_dec(taba + i, taba + i, tabb, nb, 0);+        }+        i--;+    } else {+        mult = base / (r + 1);+        if (likely(nb <= DIV_STATIC_ALLOC_LEN)) {+            tabb = static_tabb;+        } else {+            tabb = bf_malloc(s, sizeof(limb_t) * nb);+            if (!tabb)+                return -1;+        }+        mp_mul1_dec(tabb, tabb1, nb, mult, 0);+        taba[na] = mp_mul1_dec(taba, taba, na, mult, 0);+    }++#ifdef DEBUG_DIV_SLOW+    printf("mult=" FMT_LIMB "\n", mult);+    mp_print_str_dec("a_norm", taba, na + 1);+    mp_print_str_dec("b_norm", tabb, nb);+#endif++    for(; i >= 0; i--) {+        if (unlikely(taba[i + nb] >= tabb[nb - 1])) {+            /* XXX: check if it is really possible */+            q = base - 1;+        } else {+            muldq(t1, t0, taba[i + nb], base);+            adddq(t1, t0, 0, taba[i + nb - 1]);+            divdq(q, r, t1, t0, tabb[nb - 1]);+        }+        //        printf("i=%d q1=%ld\n", i, q);++        r = mp_sub_mul1_dec(taba + i, tabb, nb, q);+        //        mp_dump("r1", taba + i, nb, bd);+        //        printf("r2=%ld\n", r);++        v = taba[i + nb];+        a = v - r;+        c = a > v;+        if (c)+            a += base;+        taba[i + nb] = a;++        if (c != 0) {+            /* negative result */+            for(;;) {+                q--;+                c = mp_add_dec(taba + i, taba + i, tabb, nb, 0);+                /* propagate carry and test if positive result */+                if (c != 0) {+                    if (++taba[i + nb] == base) {+                        break;+                    }+                }+            }+        }+        tabq[i] = q;+    }++#ifdef DEBUG_DIV_SLOW+    mp_print_str_dec("q", tabq, na - nb + 1);+    mp_print_str_dec("r", taba, nb);+#endif++    /* remove the normalization */+    if (mult != 1) {+        mp_div1_dec(taba, taba, nb, mult, 0);+        if (unlikely(tabb != static_tabb))+            bf_free(s, tabb);+    }+    return 0;+}++/* divide by 10^shift */+static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, +                         limb_t shift, limb_t high)+{+    mp_size_t i;+    limb_t l, a, q, r;++    assert(shift >= 1 && shift < LIMB_DIGITS);+    l = high;+    for(i = n - 1; i >= 0; i--) {+        a = tab[i];+        fast_shr_rem_dec(q, r, a, shift);+        tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift];+        l = r;+    }+    return l;+}++/* multiply by 10^shift */+static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, +                         limb_t shift, limb_t low)+{+    mp_size_t i;+    limb_t l, a, q, r;++    assert(shift >= 1 && shift < LIMB_DIGITS);+    l = low;+    for(i = 0; i < n; i++) {+        a = tab[i];+        fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift);+        tab_r[i] = r * mp_pow_dec[shift] + l;+        l = q;+    }+    return l;+}++static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba)+{+    int k;+    dlimb_t a, b, r;+    limb_t taba1[2], s, r0, r1;++    /* convert to binary and normalize */+    a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0];+    k = clz(a >> LIMB_BITS) & ~1;+    b = a << k;+    taba1[0] = b;+    taba1[1] = b >> LIMB_BITS;+    mp_sqrtrem2(&s, taba1);+    s >>= (k >> 1);+    /* convert the remainder back to decimal */+    r = a - (dlimb_t)s * (dlimb_t)s;+    divdq_base(r1, r0, r >> LIMB_BITS, r);+    taba[0] = r0;+    tabs[0] = s;+    return r1;+}++//#define DEBUG_SQRTREM_DEC++/* tmp_buf must contain (n / 2 + 1 limbs) */+static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n,+                                 limb_t *tmp_buf)+{+    limb_t l, h, rh, ql, qh, c, i;+    +    if (n == 1)+        return mp_sqrtrem2_dec(tabs, taba);+#ifdef DEBUG_SQRTREM_DEC+    mp_print_str_dec("a", taba, 2 * n);+#endif+    l = n / 2;+    h = n - l;+    qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf);+#ifdef DEBUG_SQRTREM_DEC+    mp_print_str_dec("s1", tabs + l, h);+    mp_print_str_h_dec("r1", taba + 2 * l, h, qh);+    mp_print_str_h_dec("r2", taba + l, n, qh);+#endif+    +    /* the remainder is in taba + 2 * l. Its high bit is in qh */+    if (qh) {+        mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0);+    }+    /* instead of dividing by 2*s, divide by s (which is normalized)+       and update q and r */+    mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h);+    qh += tmp_buf[l];+    for(i = 0; i < l; i++)+        tabs[i] = tmp_buf[i];+    ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1);+    qh = qh >> 1; /* 0 or 1 */+    if (ql)+        rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0);+    else+        rh = 0;+#ifdef DEBUG_SQRTREM_DEC+    mp_print_str_h_dec("q", tabs, l, qh);+    mp_print_str_h_dec("u", taba + l, h, rh);+#endif+    +    mp_add_ui_dec(tabs + l, qh, h);+#ifdef DEBUG_SQRTREM_DEC+    mp_print_str_dec("s2", tabs, n);+#endif+    +    /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */+    /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */+    if (qh) {+        c = qh;+    } else {+        mp_mul_basecase_dec(taba + n, tabs, l, tabs, l);+        c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0);+    }+    rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l);+    if ((slimb_t)rh < 0) {+        mp_sub_ui_dec(tabs, 1, n);+        rh += mp_add_mul1_dec(taba, tabs, n, 2);+        rh += mp_add_ui_dec(taba, 1, n);+    }+    return rh;+}++/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s,+   r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n+   limbs. r is returned in the lower n limbs of taba. Its r[n] is the+   returned value of the function. */+int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n)+{+    limb_t tmp_buf1[8];+    limb_t *tmp_buf;+    mp_size_t n2;+    n2 = n / 2 + 1;+    if (n2 <= countof(tmp_buf1)) {+        tmp_buf = tmp_buf1;+    } else {+        tmp_buf = bf_malloc(s, sizeof(limb_t) * n2);+        if (!tmp_buf)+            return -1;+    }+    taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf);+    if (tmp_buf != tmp_buf1)+        bf_free(s, tmp_buf);+    return 0;+}++/* return the number of leading zero digits, from 0 to LIMB_DIGITS */+static int clz_dec(limb_t a)+{+    if (a == 0)+        return LIMB_DIGITS;+    switch(LIMB_BITS - 1 - clz(a)) {+    case 0: /* 1-1 */+        return LIMB_DIGITS - 1;+    case 1: /* 2-3 */+        return LIMB_DIGITS - 1;+    case 2: /* 4-7 */+        return LIMB_DIGITS - 1;+    case 3: /* 8-15 */+        if (a < 10)+            return LIMB_DIGITS - 1;+        else+            return LIMB_DIGITS - 2;+    case 4: /* 16-31 */+        return LIMB_DIGITS - 2;+    case 5: /* 32-63 */+        return LIMB_DIGITS - 2;+    case 6: /* 64-127 */+        if (a < 100)+            return LIMB_DIGITS - 2;+        else+            return LIMB_DIGITS - 3;+    case 7: /* 128-255 */+        return LIMB_DIGITS - 3;+    case 8: /* 256-511 */+        return LIMB_DIGITS - 3;+    case 9: /* 512-1023 */+        if (a < 1000)+            return LIMB_DIGITS - 3;+        else+            return LIMB_DIGITS - 4;+    case 10: /* 1024-2047 */+        return LIMB_DIGITS - 4;+    case 11: /* 2048-4095 */+        return LIMB_DIGITS - 4;+    case 12: /* 4096-8191 */+        return LIMB_DIGITS - 4;+    case 13: /* 8192-16383 */+        if (a < 10000)+            return LIMB_DIGITS - 4;+        else+            return LIMB_DIGITS - 5;+    case 14: /* 16384-32767 */+        return LIMB_DIGITS - 5;+    case 15: /* 32768-65535 */+        return LIMB_DIGITS - 5;+    case 16: /* 65536-131071 */+        if (a < 100000)+            return LIMB_DIGITS - 5;+        else+            return LIMB_DIGITS - 6;+    case 17: /* 131072-262143 */+        return LIMB_DIGITS - 6;+    case 18: /* 262144-524287 */+        return LIMB_DIGITS - 6;+    case 19: /* 524288-1048575 */+        if (a < 1000000)+            return LIMB_DIGITS - 6;+        else+            return LIMB_DIGITS - 7;+    case 20: /* 1048576-2097151 */+        return LIMB_DIGITS - 7;+    case 21: /* 2097152-4194303 */+        return LIMB_DIGITS - 7;+    case 22: /* 4194304-8388607 */+        return LIMB_DIGITS - 7;+    case 23: /* 8388608-16777215 */+        if (a < 10000000)+            return LIMB_DIGITS - 7;+        else+            return LIMB_DIGITS - 8;+    case 24: /* 16777216-33554431 */+        return LIMB_DIGITS - 8;+    case 25: /* 33554432-67108863 */+        return LIMB_DIGITS - 8;+    case 26: /* 67108864-134217727 */+        if (a < 100000000)+            return LIMB_DIGITS - 8;+        else+            return LIMB_DIGITS - 9;+#if LIMB_BITS == 64+    case 27: /* 134217728-268435455 */+        return LIMB_DIGITS - 9;+    case 28: /* 268435456-536870911 */+        return LIMB_DIGITS - 9;+    case 29: /* 536870912-1073741823 */+        if (a < 1000000000)+            return LIMB_DIGITS - 9;+        else+            return LIMB_DIGITS - 10;+    case 30: /* 1073741824-2147483647 */+        return LIMB_DIGITS - 10;+    case 31: /* 2147483648-4294967295 */+        return LIMB_DIGITS - 10;+    case 32: /* 4294967296-8589934591 */+        return LIMB_DIGITS - 10;+    case 33: /* 8589934592-17179869183 */+        if (a < 10000000000)+            return LIMB_DIGITS - 10;+        else+            return LIMB_DIGITS - 11;+    case 34: /* 17179869184-34359738367 */+        return LIMB_DIGITS - 11;+    case 35: /* 34359738368-68719476735 */+        return LIMB_DIGITS - 11;+    case 36: /* 68719476736-137438953471 */+        if (a < 100000000000)+            return LIMB_DIGITS - 11;+        else+            return LIMB_DIGITS - 12;+    case 37: /* 137438953472-274877906943 */+        return LIMB_DIGITS - 12;+    case 38: /* 274877906944-549755813887 */+        return LIMB_DIGITS - 12;+    case 39: /* 549755813888-1099511627775 */+        if (a < 1000000000000)+            return LIMB_DIGITS - 12;+        else+            return LIMB_DIGITS - 13;+    case 40: /* 1099511627776-2199023255551 */+        return LIMB_DIGITS - 13;+    case 41: /* 2199023255552-4398046511103 */+        return LIMB_DIGITS - 13;+    case 42: /* 4398046511104-8796093022207 */+        return LIMB_DIGITS - 13;+    case 43: /* 8796093022208-17592186044415 */+        if (a < 10000000000000)+            return LIMB_DIGITS - 13;+        else+            return LIMB_DIGITS - 14;+    case 44: /* 17592186044416-35184372088831 */+        return LIMB_DIGITS - 14;+    case 45: /* 35184372088832-70368744177663 */+        return LIMB_DIGITS - 14;+    case 46: /* 70368744177664-140737488355327 */+        if (a < 100000000000000)+            return LIMB_DIGITS - 14;+        else+            return LIMB_DIGITS - 15;+    case 47: /* 140737488355328-281474976710655 */+        return LIMB_DIGITS - 15;+    case 48: /* 281474976710656-562949953421311 */+        return LIMB_DIGITS - 15;+    case 49: /* 562949953421312-1125899906842623 */+        if (a < 1000000000000000)+            return LIMB_DIGITS - 15;+        else+            return LIMB_DIGITS - 16;+    case 50: /* 1125899906842624-2251799813685247 */+        return LIMB_DIGITS - 16;+    case 51: /* 2251799813685248-4503599627370495 */+        return LIMB_DIGITS - 16;+    case 52: /* 4503599627370496-9007199254740991 */+        return LIMB_DIGITS - 16;+    case 53: /* 9007199254740992-18014398509481983 */+        if (a < 10000000000000000)+            return LIMB_DIGITS - 16;+        else+            return LIMB_DIGITS - 17;+    case 54: /* 18014398509481984-36028797018963967 */+        return LIMB_DIGITS - 17;+    case 55: /* 36028797018963968-72057594037927935 */+        return LIMB_DIGITS - 17;+    case 56: /* 72057594037927936-144115188075855871 */+        if (a < 100000000000000000)+            return LIMB_DIGITS - 17;+        else+            return LIMB_DIGITS - 18;+    case 57: /* 144115188075855872-288230376151711743 */+        return LIMB_DIGITS - 18;+    case 58: /* 288230376151711744-576460752303423487 */+        return LIMB_DIGITS - 18;+    case 59: /* 576460752303423488-1152921504606846975 */+        if (a < 1000000000000000000)+            return LIMB_DIGITS - 18;+        else+            return LIMB_DIGITS - 19;+#endif+    default:+        return 0;+    }+}++/* for debugging */+void bfdec_print_str(const char *str, const bfdec_t *a)+{+    slimb_t i;+    printf("%s=", str);++    if (a->expn == BF_EXP_NAN) {+        printf("NaN");+    } else {+        if (a->sign)+            putchar('-');+        if (a->expn == BF_EXP_ZERO) {+            putchar('0');+        } else if (a->expn == BF_EXP_INF) {+            printf("Inf");+        } else {+            printf("0.");+            for(i = a->len - 1; i >= 0; i--)+                printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]);+            printf("e%" PRId_LIMB, a->expn);+        }+    }+    printf("\n");+}++/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */+static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos)+{+    slimb_t pos;+    limb_t v, q;+    int shift;++    if (bit_pos < 0)+        return 0;+    pos = (limb_t)bit_pos / LIMB_DIGITS;+    shift = (limb_t)bit_pos % LIMB_DIGITS;+    fast_shr_rem_dec(q, v, r->tab[pos], shift + 1);+    (void)q;+    if (v != 0)+        return 1;+    pos--;+    while (pos >= 0) {+        if (r->tab[pos] != 0)+            return 1;+        pos--;+    }+    return 0;+}++static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos)+{+    slimb_t i;+    int shift;+    i = floor_div(pos, LIMB_DIGITS);+    if (i < 0 || i >= len)+        return 0;+    shift = pos - i * LIMB_DIGITS;+    return fast_shr_dec(tab[i], shift) % 10;+}++#if 0+static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos)+{+    limb_t a0, a1;+    int shift;+    slimb_t i;+    +    i = floor_div(pos, LIMB_DIGITS);+    shift = pos - i * LIMB_DIGITS;+    if (i >= 0 && i < len)+        a0 = tab[i];+    else+        a0 = 0;+    if (shift == 0) {+        return a0;+    } else {+        i++;+        if (i >= 0 && i < len)+            a1 = tab[i];+        else+            a1 = 0;+        return fast_shr_dec(a0, shift) ++            fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) *+            mp_pow_dec[shift];+    }+}+#endif++/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */+static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l,+                             slimb_t prec, int rnd_mode)+{+    int add_one, inexact;+    limb_t digit1, digit0;+    +    //    bfdec_print_str("get_rnd_add", r);+    if (rnd_mode == BF_RNDF) {+        digit0 = 1; /* faithful rounding does not honor the INEXACT flag */+    } else {+        /* starting limb for bit 'prec + 1' */+        digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1));+    }++    /* get the digit at 'prec' */+    digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec);+    inexact = (digit1 | digit0) != 0;+    +    add_one = 0;+    switch(rnd_mode) {+    case BF_RNDZ:+        break;+    case BF_RNDN:+        if (digit1 == 5) {+            if (digit0) {+                add_one = 1;+            } else {+                /* round to even */+                add_one =+                    get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1;+            }+        } else if (digit1 > 5) {+            add_one = 1;+        }+        break;+    case BF_RNDD:+    case BF_RNDU:+        if (r->sign == (rnd_mode == BF_RNDD))+            add_one = inexact;+        break;+    case BF_RNDNA:+    case BF_RNDF:+        add_one = (digit1 >= 5);+        break;+    case BF_RNDA:+        add_one = inexact;+        break;+    default:+        abort();+    }+    +    if (inexact)+        *pret |= BF_ST_INEXACT;+    return add_one;+}++/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is+   assumed to have length 'l' (1 <= l <= r->len). prec1 can be+   BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with+   BF_ST_MEM_ERROR.+ */+static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)+{+    int shift, add_one, rnd_mode, ret;+    slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec;++    /* XXX: align to IEEE 754 2008 for decimal numbers ? */+    e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1);+    e_min = -e_range + 3;+    e_max = e_range;+    +    if (flags & BF_FLAG_RADPNT_PREC) {+        /* 'prec' is the precision after the decimal point */+        if (prec1 != BF_PREC_INF)+            prec = r->expn + prec1;+        else+            prec = prec1;+    } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {+        /* restrict the precision in case of potentially subnormal+           result */+        assert(prec1 != BF_PREC_INF);+        prec = prec1 - (e_min - r->expn);+    } else {+        prec = prec1;+    }+    +    /* round to prec bits */+    rnd_mode = flags & BF_RND_MASK;+    ret = 0;+    add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode);+    +    if (prec <= 0) {+        if (add_one) {+            bfdec_resize(r, 1); /* cannot fail because r is non zero */+            r->tab[0] = BF_DEC_BASE / 10;+            r->expn += 1 - prec;+            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;+            return ret;+        } else {+            goto underflow;+        }+    } else if (add_one) {+        limb_t carry;+        +        /* add one starting at digit 'prec - 1' */+        bit_pos = l * LIMB_DIGITS - 1 - (prec - 1);+        pos = bit_pos / LIMB_DIGITS;+        carry = mp_pow_dec[bit_pos % LIMB_DIGITS];+        carry = mp_add_ui_dec(r->tab + pos, carry, l - pos);+        if (carry) {+            /* shift right by one digit */+            mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1);+            r->expn++;+        }+    }+    +    /* check underflow */+    if (unlikely(r->expn < e_min)) {+        if (flags & BF_FLAG_SUBNORMAL) {+            /* if inexact, also set the underflow flag */+            if (ret & BF_ST_INEXACT)+                ret |= BF_ST_UNDERFLOW;+        } else {+        underflow:+            bfdec_set_zero(r, r->sign);+            ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;+            return ret;+        }+    }+    +    /* check overflow */+    if (unlikely(r->expn > e_max)) {+        bfdec_set_inf(r, r->sign);+        ret |= BF_ST_OVERFLOW | BF_ST_INEXACT;+        return ret;+    }+    +    /* keep the bits starting at 'prec - 1' */+    bit_pos = l * LIMB_DIGITS - 1 - (prec - 1);+    i = floor_div(bit_pos, LIMB_DIGITS);+    if (i >= 0) {+        shift = smod(bit_pos, LIMB_DIGITS);+        if (shift != 0) {+            r->tab[i] = fast_shr_dec(r->tab[i], shift) *+                mp_pow_dec[shift];+        }+    } else {+        i = 0;+    }+    /* remove trailing zeros */+    while (r->tab[i] == 0)+        i++;+    if (i > 0) {+        l -= i;+        memmove(r->tab, r->tab + i, l * sizeof(limb_t));+    }+    bfdec_resize(r, l); /* cannot fail */+    return ret;+}++/* Cannot fail with BF_ST_MEM_ERROR. */+int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags)+{+    if (r->len == 0)+        return 0;+    return __bfdec_round(r, prec, flags, r->len);+}++/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR.  */+int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags)+{+    limb_t l, v;+    int shift, ret;+    +    //    bfdec_print_str("bf_renorm", r);+    l = r->len;+    while (l > 0 && r->tab[l - 1] == 0)+        l--;+    if (l == 0) {+        /* zero */+        r->expn = BF_EXP_ZERO;+        bfdec_resize(r, 0); /* cannot fail */+        ret = 0;+    } else {+        r->expn -= (r->len - l) * LIMB_DIGITS;+        /* shift to have the MSB set to '1' */+        v = r->tab[l - 1];+        shift = clz_dec(v);+        if (shift != 0) {+            mp_shl_dec(r->tab, r->tab, l, shift, 0);+            r->expn -= shift;+        }+        ret = __bfdec_round(r, prec1, flags, l);+    }+    //    bf_print_str("r_final", r);+    return ret;+}++int bfdec_set_ui(bfdec_t *r, uint64_t v)+{+#if LIMB_BITS == 32+    if (v >= BF_DEC_BASE * BF_DEC_BASE) {+        if (bfdec_resize(r, 3))+            goto fail;+        r->tab[0] = v % BF_DEC_BASE;+        v /= BF_DEC_BASE;+        r->tab[1] = v % BF_DEC_BASE;+        r->tab[2] = v / BF_DEC_BASE;+        r->expn = 3 * LIMB_DIGITS;+    } else+#endif+    if (v >= BF_DEC_BASE) {+        if (bfdec_resize(r, 2))+            goto fail;+        r->tab[0] = v % BF_DEC_BASE;+        r->tab[1] = v / BF_DEC_BASE;+        r->expn = 2 * LIMB_DIGITS;+    } else {+        if (bfdec_resize(r, 1))+            goto fail;+        r->tab[0] = v;+        r->expn = LIMB_DIGITS;+    }+    r->sign = 0;+    return bfdec_normalize_and_round(r, BF_PREC_INF, 0);+ fail:+    bfdec_set_nan(r);+    return BF_ST_MEM_ERROR;+}++int bfdec_set_si(bfdec_t *r, int64_t v)+{+    int ret;+    if (v < 0) {+        ret = bfdec_set_ui(r, -v);+        r->sign = 1;+    } else {+        ret = bfdec_set_ui(r, v);+    }+    return ret;+}++static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg)+{+    bf_context_t *s = r->ctx;+    int is_sub, cmp_res, a_sign, b_sign, ret;++    a_sign = a->sign;+    b_sign = b->sign ^ b_neg;+    is_sub = a_sign ^ b_sign;+    cmp_res = bfdec_cmpu(a, b);+    if (cmp_res < 0) {+        const bfdec_t *tmp;+        tmp = a;+        a = b;+        b = tmp;+        a_sign = b_sign; /* b_sign is never used later */+    }+    /* abs(a) >= abs(b) */+    if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) {+        /* zero result */+        bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD);+        ret = 0;+    } else if (a->len == 0 || b->len == 0) {+        ret = 0;+        if (a->expn >= BF_EXP_INF) {+            if (a->expn == BF_EXP_NAN) {+                /* at least one operand is NaN */+                bfdec_set_nan(r);+                ret = 0;+            } else if (b->expn == BF_EXP_INF && is_sub) {+                /* infinities with different signs */+                bfdec_set_nan(r);+                ret = BF_ST_INVALID_OP;+            } else {+                bfdec_set_inf(r, a_sign);+            }+        } else {+            /* at least one zero and not subtract */+            if (bfdec_set(r, a))+                return BF_ST_MEM_ERROR;+            r->sign = a_sign;+            goto renorm;+        }+    } else {+        slimb_t d, a_offset, b_offset, i, r_len;+        limb_t carry;+        limb_t *b1_tab;+        int b_shift;+        mp_size_t b1_len;+        +        d = a->expn - b->expn;++        /* XXX: not efficient in time and memory if the precision is+           not infinite */+        r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS);+        if (bfdec_resize(r, r_len))+            goto fail;+        r->sign = a_sign;+        r->expn = a->expn;++        a_offset = r_len - a->len;+        for(i = 0; i < a_offset; i++)+            r->tab[i] = 0;+        for(i = 0; i < a->len; i++)+            r->tab[a_offset + i] = a->tab[i];+        +        b_shift = d % LIMB_DIGITS;+        if (b_shift == 0) {+            b1_len = b->len;+            b1_tab = (limb_t *)b->tab;+        } else {+            b1_len = b->len + 1;+            b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len);+            if (!b1_tab)+                goto fail;+            b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) *+                mp_pow_dec[LIMB_DIGITS - b_shift];+        }+        b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS);+        +        if (is_sub) {+            carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset,+                               b1_tab, b1_len, 0);+            if (carry != 0) {+                carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry,+                                      r_len - (b_offset + b1_len));+                assert(carry == 0);+            }+        } else {+            carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset,+                               b1_tab, b1_len, 0);+            if (carry != 0) {+                carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry,+                                      r_len - (b_offset + b1_len));+            }+            if (carry != 0) {+                if (bfdec_resize(r, r_len + 1)) {+                    if (b_shift != 0)+                        bf_free(s, b1_tab);+                    goto fail;+                }+                r->tab[r_len] = 1;+                r->expn += LIMB_DIGITS;+            }+        }+        if (b_shift != 0)+            bf_free(s, b1_tab);+    renorm:+        ret = bfdec_normalize_and_round(r, prec, flags);+    }+    return ret;+ fail:+    bfdec_set_nan(r);+    return BF_ST_MEM_ERROR;+}++static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+                     bf_flags_t flags)+{+    return bfdec_add_internal(r, a, b, prec, flags, 0);+}++static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+                     bf_flags_t flags)+{+    return bfdec_add_internal(r, a, b, prec, flags, 1);+}++int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+              bf_flags_t flags)+{+    return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,+                  (bf_op2_func_t *)__bfdec_add);+}++int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+              bf_flags_t flags)+{+    return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,+                  (bf_op2_func_t *)__bfdec_sub);+}++int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+              bf_flags_t flags)+{+    int ret, r_sign;++    if (a->len < b->len) {+        const bfdec_t *tmp = a;+        a = b;+        b = tmp;+    }+    r_sign = a->sign ^ b->sign;+    /* here b->len <= a->len */+    if (b->len == 0) {+        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+            bfdec_set_nan(r);+            ret = 0;+        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) {+            if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) ||+                (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) {+                bfdec_set_nan(r);+                ret = BF_ST_INVALID_OP;+            } else {+                bfdec_set_inf(r, r_sign);+                ret = 0;+            }+        } else {+            bfdec_set_zero(r, r_sign);+            ret = 0;+        }+    } else {+        bfdec_t tmp, *r1 = NULL;+        limb_t a_len, b_len;+        limb_t *a_tab, *b_tab;+            +        a_len = a->len;+        b_len = b->len;+        a_tab = a->tab;+        b_tab = b->tab;+        +        if (r == a || r == b) {+            bfdec_init(r->ctx, &tmp);+            r1 = r;+            r = &tmp;+        }+        if (bfdec_resize(r, a_len + b_len)) {+            bfdec_set_nan(r);+            ret = BF_ST_MEM_ERROR;+            goto done;+        }+        mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len);+        r->sign = r_sign;+        r->expn = a->expn + b->expn;+        ret = bfdec_normalize_and_round(r, prec, flags);+    done:+        if (r == &tmp)+            bfdec_move(r1, &tmp);+    }+    return ret;+}++int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,+                 bf_flags_t flags)+{+    bfdec_t b;+    int ret;+    bfdec_init(r->ctx, &b);+    ret = bfdec_set_si(&b, b1);+    ret |= bfdec_mul(r, a, &b, prec, flags);+    bfdec_delete(&b);+    return ret;+}++int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,+                 bf_flags_t flags)+{+    bfdec_t b;+    int ret;+    +    bfdec_init(r->ctx, &b);+    ret = bfdec_set_si(&b, b1);+    ret |= bfdec_add(r, a, &b, prec, flags);+    bfdec_delete(&b);+    return ret;+}++static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b,+                       limb_t prec, bf_flags_t flags)+{+    int ret, r_sign;+    limb_t n, nb, precl;+    +    r_sign = a->sign ^ b->sign;+    if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) {+        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+            bfdec_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) {+            bfdec_set_nan(r);+            return BF_ST_INVALID_OP;+        } else if (a->expn == BF_EXP_INF) {+            bfdec_set_inf(r, r_sign);+            return 0;+        } else {+            bfdec_set_zero(r, r_sign);+            return 0;+        }+    } else if (a->expn == BF_EXP_ZERO) {+        if (b->expn == BF_EXP_ZERO) {+            bfdec_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bfdec_set_zero(r, r_sign);+            return 0;+        }+    } else if (b->expn == BF_EXP_ZERO) {+        bfdec_set_inf(r, r_sign);+        return BF_ST_DIVIDE_ZERO;+    }++    nb = b->len;+    if (prec == BF_PREC_INF) {+        /* infinite precision: return BF_ST_INVALID_OP if not an exact+           result */+        /* XXX: check */+        precl = nb + 1;+    } else if (flags & BF_FLAG_RADPNT_PREC) {+        /* number of digits after the decimal point */+        /* XXX: check (2 extra digits for rounding + 2 digits) */+        precl = (bf_max(a->expn - b->expn, 0) + 2 ++                 prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;+    } else {+        /* number of limbs of the quotient (2 extra digits for rounding) */+        precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;+    }+    n = bf_max(a->len, precl);+    +    {+        limb_t *taba, na, i;+        slimb_t d;+        +        na = n + nb;+        taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t));+        if (!taba)+            goto fail;+        d = na - a->len;+        memset(taba, 0, d * sizeof(limb_t));+        memcpy(taba + d, a->tab, a->len * sizeof(limb_t));+        if (bfdec_resize(r, n + 1))+            goto fail1;+        if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) {+        fail1:+            bf_free(r->ctx, taba);+            goto fail;+        }+        /* see if non zero remainder */+        for(i = 0; i < nb; i++) {+            if (taba[i] != 0)+                break;+        }+        bf_free(r->ctx, taba);+        if (i != nb) {+            if (prec == BF_PREC_INF) {+                bfdec_set_nan(r);+                return BF_ST_INVALID_OP;+            } else {+                r->tab[0] |= 1;+            }+        }+        r->expn = a->expn - b->expn + LIMB_DIGITS;+        r->sign = r_sign;+        ret = bfdec_normalize_and_round(r, prec, flags);+    }+    return ret;+ fail:+    bfdec_set_nan(r);+    return BF_ST_MEM_ERROR;+}++int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+              bf_flags_t flags)+{+    return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,+                  (bf_op2_func_t *)__bfdec_div);+}++/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the+   integer defined as floor(a/b) and r = a - q * b. */+static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r,+                           const bfdec_t *a, const bfdec_t *b)+{+    if (bfdec_cmpu(a, b) < 0) {+        bfdec_set_ui(q, 0);+        bfdec_set(r, a);+    } else {+        bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC);+        bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ);+        bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ);+    }+}++/* division and remainder. +   +   rnd_mode is the rounding mode for the quotient. The additional+   rounding mode BF_RND_EUCLIDIAN is supported.++   'q' is an integer. 'r' is rounded with prec and flags (prec can be+   BF_PREC_INF).+*/+int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,+                 limb_t prec, bf_flags_t flags, int rnd_mode)+{+    bf_context_t *s = q->ctx;+    bfdec_t a1_s, *a1 = &a1_s;+    bfdec_t b1_s, *b1 = &b1_s;+    bfdec_t r1_s, *r1 = &r1_s;+    int q_sign, res;+    BOOL is_ceil, is_rndn;+    +    assert(q != a && q != b);+    assert(r != a && r != b);+    assert(q != r);+    +    if (a->len == 0 || b->len == 0) {+        bfdec_set_zero(q, 0);+        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {+            bfdec_set_nan(r);+            return 0;+        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {+            bfdec_set_nan(r);+            return BF_ST_INVALID_OP;+        } else {+            bfdec_set(r, a);+            return bfdec_round(r, prec, flags);+        }+    }++    q_sign = a->sign ^ b->sign;+    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);+    switch(rnd_mode) {+    default:+    case BF_RNDZ:+    case BF_RNDN:+    case BF_RNDNA:+        is_ceil = FALSE;+        break;+    case BF_RNDD:+        is_ceil = q_sign;+        break;+    case BF_RNDU:+        is_ceil = q_sign ^ 1;+        break;+    case BF_RNDA:+        is_ceil = TRUE;+        break;+    case BF_DIVREM_EUCLIDIAN:+        is_ceil = a->sign;+        break;+    }++    a1->expn = a->expn;+    a1->tab = a->tab;+    a1->len = a->len;+    a1->sign = 0;+    +    b1->expn = b->expn;+    b1->tab = b->tab;+    b1->len = b->len;+    b1->sign = 0;++    //    bfdec_print_str("a1", a1);+    //    bfdec_print_str("b1", b1);+    /* XXX: could improve to avoid having a large 'q' */+    bfdec_tdivremu(s, q, r, a1, b1);+    if (bfdec_is_nan(q) || bfdec_is_nan(r))+        goto fail;+    //    bfdec_print_str("q", q);+    //    bfdec_print_str("r", r);+    +    if (r->len != 0) {+        if (is_rndn) {+            bfdec_init(s, r1);+            if (bfdec_set(r1, r))+                goto fail;+            if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) {+                bfdec_delete(r1);+                goto fail;+            }+            res = bfdec_cmpu(r1, b);+            bfdec_delete(r1);+            if (res > 0 ||+                (res == 0 &&+                 (rnd_mode == BF_RNDNA ||+                  (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) {+                goto do_sub_r;+            }+        } else if (is_ceil) {+        do_sub_r:+            res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);+            res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);+            if (res & BF_ST_MEM_ERROR)+                goto fail;+        }+    }++    r->sign ^= a->sign;+    q->sign = q_sign;+    return bfdec_round(r, prec, flags);+ fail:+    bfdec_set_nan(q);+    bfdec_set_nan(r);+    return BF_ST_MEM_ERROR;+}++int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,+              bf_flags_t flags, int rnd_mode)+{+    bfdec_t q_s, *q = &q_s;+    int ret;+    +    bfdec_init(r->ctx, q);+    ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode);+    bfdec_delete(q);+    return ret;+}++/* convert to integer (infinite precision) */+int bfdec_rint(bfdec_t *r, int rnd_mode)+{+    return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC);+}++int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags)+{+    bf_context_t *s = a->ctx;+    int ret, k;+    limb_t *a1, v;+    slimb_t n, n1, prec1;+    limb_t res;++    assert(r != a);++    if (a->len == 0) {+        if (a->expn == BF_EXP_NAN) {+            bfdec_set_nan(r);+        } else if (a->expn == BF_EXP_INF && a->sign) {+            goto invalid_op;+        } else {+            bfdec_set(r, a);+        }+        ret = 0;+    } else if (a->sign || prec == BF_PREC_INF) {+ invalid_op:+        bfdec_set_nan(r);+        ret = BF_ST_INVALID_OP;+    } else {+        if (flags & BF_FLAG_RADPNT_PREC) {+            prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1);+        } else {+            prec1 = prec;+        }+        /* convert the mantissa to an integer with at least 2 *+           prec + 4 digits */+        n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS);+        if (bfdec_resize(r, n))+            goto fail;+        a1 = bf_malloc(s, sizeof(limb_t) * 2 * n);+        if (!a1)+            goto fail;+        n1 = bf_min(2 * n, a->len);+        memset(a1, 0, (2 * n - n1) * sizeof(limb_t));+        memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t));+        if (a->expn & 1) {+            res = mp_shr_dec(a1, a1, 2 * n, 1, 0);+        } else {+            res = 0;+        }+        /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1+           because mp_sqrtrem2_dec already does it */+        k = 0;+        if (n > 1) {+            v = a1[2 * n - 1];+            while (v < BF_DEC_BASE / 4) {+                k++;+                v *= 4;+            }+            if (k != 0)+                mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0);+        }+        if (mp_sqrtrem_dec(s, r->tab, a1, n)) {+            bf_free(s, a1);+            goto fail;+        }+        if (k != 0)+            mp_div1_dec(r->tab, r->tab, n, 1 << k, 0);+        if (!res) {+            res = mp_scan_nz(a1, n + 1);+        }+        bf_free(s, a1);+        if (!res) {+            res = mp_scan_nz(a->tab, a->len - n1);+        }+        if (res != 0)+            r->tab[0] |= 1;+        r->sign = 0;+        r->expn = (a->expn + 1) >> 1;+        ret = bfdec_round(r, prec, flags);+    }+    return ret;+ fail:+    bfdec_set_nan(r);+    return BF_ST_MEM_ERROR;+}++/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there+   is an overflow and 0 otherwise. No memory error is possible. */+int bfdec_get_int32(int *pres, const bfdec_t *a)+{+    uint32_t v;+    int ret;+    if (a->expn >= BF_EXP_INF) {+        ret = 0;+        if (a->expn == BF_EXP_INF) {+            v = (uint32_t)INT32_MAX + a->sign;+             /* XXX: return overflow ? */+        } else {+            v = INT32_MAX;+        }+    } else if (a->expn <= 0) {+        v = 0;+        ret = 0;+    } else if (a->expn <= 9) {+        v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);+        if (a->sign)+            v = -v;+        ret = 0;+    } else if (a->expn == 10) {+        uint64_t v1;+        uint32_t v_max;+#if LIMB_BITS == 64+        v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);+#else+        v1 = (uint64_t)a->tab[a->len - 1] * 10 ++            get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1);+#endif+        v_max = (uint32_t)INT32_MAX + a->sign;+        if (v1 > v_max) {+            v = v_max;+            ret = BF_ST_OVERFLOW;+        } else {+            v = v1;+            if (a->sign)+                v = -v;+            ret = 0;+        }+    } else {+        v = (uint32_t)INT32_MAX + a->sign;+        ret = BF_ST_OVERFLOW;+    }+    *pres = v;+    return ret;+}++/* power to an integer with infinite precision */+int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b)+{+    int ret, n_bits, i;+    +    assert(r != a);+    if (b == 0)+        return bfdec_set_ui(r, 1);+    ret = bfdec_set(r, a);+    n_bits = LIMB_BITS - clz(b);+    for(i = n_bits - 2; i >= 0; i--) {+        ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ);+        if ((b >> i) & 1)+            ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ);+    }+    return ret;+}++char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags)+{+    return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE);+}++int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,+               limb_t prec, bf_flags_t flags)+{+    slimb_t dummy_exp;+    return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec,+                            flags, TRUE);+}++#endif /* USE_BF_DEC */++#ifdef USE_FFT_MUL+/***************************************************************/+/* Integer multiplication with FFT */++/* or LIMB_BITS at bit position 'pos' in tab */+static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val)+{+    limb_t i;+    int p;++    i = pos >> LIMB_LOG2_BITS;+    p = pos & (LIMB_BITS - 1);+    if (i < len)+        tab[i] |= val << p;+    if (p != 0) {+        i++;+        if (i < len) {+            tab[i] |= val >> (LIMB_BITS - p);+        }+    }+}++#if defined(__AVX2__)++typedef double NTTLimb;++/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */+#define NTT_MOD_LOG2_MIN 50+#define NTT_MOD_LOG2_MAX 51+#define NB_MODS 5+#define NTT_PROOT_2EXP 39+static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, };++static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001,+};++static const limb_t ntt_proot[2][NB_MODS] = {+    { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, },+    { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, },+};++static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {+ 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd,+ 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a,+ 0x00066015555557e3, 0x000725960b60b623,+ 0x0002fc1fa1d6ce12,+};++#else++typedef limb_t NTTLimb;++#if LIMB_BITS == 64++#define NTT_MOD_LOG2_MIN 61+#define NTT_MOD_LOG2_MAX 62+#define NB_MODS 5+#define NTT_PROOT_2EXP 51+static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, };++static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001,+};++static const limb_t ntt_proot[2][NB_MODS] = {+    { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, },+    { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, },+};++static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {+ 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4,+ 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638,+ 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f,+ 0x3a5493e93e93e94a,+};++#elif LIMB_BITS == 32++/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */+#define NTT_MOD_LOG2_MIN 29+#define NTT_MOD_LOG2_MAX 30+#define NB_MODS 5+#define NTT_PROOT_2EXP 20+static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, };++static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001,+};++static const limb_t ntt_proot[2][NB_MODS] = {+    { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, },+    { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, },+};++static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {+ 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b,+ 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938,+ 0x000000000932ab3e, 0x000000002f40eef8,+ 0x000000002e760905,+};++#endif /* LIMB_BITS */++#endif /* !AVX2 */++#if defined(__AVX2__)+#define NTT_TRIG_K_MAX 18+#else+#define NTT_TRIG_K_MAX 19+#endif++typedef struct BFNTTState {+    bf_context_t *ctx;+    +    /* used for mul_mod_fast() */+    limb_t ntt_mods_div[NB_MODS];++    limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1];+    limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1];+    NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1];+    /* 1/2^n mod m */+    limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2];+#if defined(__AVX2__)+    __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2];+    __m256d ntt_mods_vec[NB_MODS];+    __m256d ntt_mods_inv_vec[NB_MODS];+#else+    limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2];+#endif+} BFNTTState;++static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx);++/* add modulo with up to (LIMB_BITS-1) bit modulo */+static inline limb_t add_mod(limb_t a, limb_t b, limb_t m)+{+    limb_t r;+    r = a + b;+    if (r >= m)+        r -= m;+    return r;+}++/* sub modulo with up to LIMB_BITS bit modulo */+static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m)+{+    limb_t r;+    r = a - b;+    if (r > a)+        r += m;+    return r;+}++/* return (r0+r1*B) mod m +   precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) +*/+static inline limb_t mod_fast(dlimb_t r, +                                limb_t m, limb_t m_inv)+{+    limb_t a1, q, t0, r1, r0;+    +    a1 = r >> NTT_MOD_LOG2_MIN;+    +    q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS;+    r = r - (dlimb_t)q * m - m * 2;+    r1 = r >> LIMB_BITS;+    t0 = (slimb_t)r1 >> 1;+    r += m & t0;+    r0 = r;+    r1 = r >> LIMB_BITS;+    r0 += m & r1;+    return r0;+}++/* faster version using precomputed modulo inverse. +   precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */+static inline limb_t mul_mod_fast(limb_t a, limb_t b, +                                    limb_t m, limb_t m_inv)+{+    dlimb_t r;+    r = (dlimb_t)a * (dlimb_t)b;+    return mod_fast(r, m, m_inv);+}++static inline limb_t init_mul_mod_fast(limb_t m)+{+    dlimb_t t;+    assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX);+    assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN);+    t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN);+    return t / m;+}++/* Faster version used when the multiplier is constant. 0 <= a < 2^64,+   0 <= b < m. */+static inline limb_t mul_mod_fast2(limb_t a, limb_t b, +                                     limb_t m, limb_t b_inv)+{+    limb_t r, q;++    q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS;+    r = a * b - q * m;+    if (r >= m)+        r -= m;+    return r;+}++/* Faster version used when the multiplier is constant. 0 <= a < 2^64,+   0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r ++   m'. */+static inline limb_t mul_mod_fast3(limb_t a, limb_t b, +                                     limb_t m, limb_t b_inv)+{+    limb_t r, q;++    q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS;+    r = a * b - q * m;+    return r;+}++static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m)+{+    return ((dlimb_t)b << LIMB_BITS) / m;+}++#ifdef __AVX2__++static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m)+{+    slimb_t v;+    v = a;+    if (v < 0)+        v += m;+    if (v >= m)+        v -= m;+    return v;+}++static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m)+{+    return (slimb_t)a;+}++static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m)+{+    if (a >= (m / 2))+        a -= m;+    return (slimb_t)a;+}++/* return r + m if r < 0 otherwise r. */+static inline __m256d ntt_mod1(__m256d r, __m256d m)+{+    return _mm256_blendv_pd(r, r + m, r);+}++/* input: abs(r) < 2 * m. Output: abs(r) < m */+static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f)+{+    return _mm256_blendv_pd(r, r + m2f, r) - mf;+}++/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */+static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf,+                                  __m256d m_inv)+{+    __m256d r, q, ab1, ab0, qm0, qm1;+    ab1 = a * b;+    q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */+    qm1 = q * mf;+    qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */+    ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */+    r = (ab1 - qm1) + (ab0 - qm0);+    return r;+}++static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align)+{+    void *ptr;+    void **ptr1;+    ptr = bf_malloc(s, size + sizeof(void *) + align - 1);+    if (!ptr)+        return NULL;+    ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) &+                     ~(align - 1));+    ptr1[-1] = ptr;+    return ptr1;+}++static void bf_aligned_free(bf_context_t *s, void *ptr)+{+    if (!ptr)+        return;+    bf_free(s, ((void **)ptr)[-1]);+}++static void *ntt_malloc(BFNTTState *s, size_t size)+{+    return bf_aligned_malloc(s->ctx, size, 64);+}++static void ntt_free(BFNTTState *s, void *ptr)+{+    bf_aligned_free(s->ctx, ptr);+}++static no_inline int ntt_fft(BFNTTState *s,+                             NTTLimb *out_buf, NTTLimb *in_buf,+                             NTTLimb *tmp_buf, int fft_len_log2,+                             int inverse, int m_idx)+{+    limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j;+    NTTLimb *tab_in, *tab_out, *tmp, *trig;+    __m256d m_inv, mf, m2f, c, a0, a1, b0, b1;+    limb_t m;+    int l;+    +    m = ntt_mods[m_idx];+    +    m_inv = _mm256_set1_pd(1.0 / (double)m);+    mf = _mm256_set1_pd(m);+    m2f = _mm256_set1_pd(m * 2);++    n = (limb_t)1 << fft_len_log2;+    assert(n >= 8);+    stride_in = n / 2;++    tab_in = in_buf;+    tab_out = tmp_buf;+    trig = get_trig(s, fft_len_log2, inverse, m_idx);+    if (!trig)+        return -1;+    p = 0;+    for(k = 0; k < stride_in; k += 4) {+        a0 = _mm256_load_pd(&tab_in[k]);+        a1 = _mm256_load_pd(&tab_in[k + stride_in]);+        c = _mm256_load_pd(trig);+        trig += 4;+        b0 = ntt_mod(a0 + a1, mf, m2f);+        b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);+        a0 = _mm256_permute2f128_pd(b0, b1, 0x20);+        a1 = _mm256_permute2f128_pd(b0, b1, 0x31);+        a0 = _mm256_permute4x64_pd(a0, 0xd8);+        a1 = _mm256_permute4x64_pd(a1, 0xd8);+        _mm256_store_pd(&tab_out[p], a0);+        _mm256_store_pd(&tab_out[p + 4], a1);+        p += 2 * 4;+    }+    tmp = tab_in;+    tab_in = tab_out;+    tab_out = tmp;++    trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx);+    if (!trig)+        return -1;+    p = 0;+    for(k = 0; k < stride_in; k += 4) {+        a0 = _mm256_load_pd(&tab_in[k]);+        a1 = _mm256_load_pd(&tab_in[k + stride_in]);+        c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]);+        trig += 2;+        b0 = ntt_mod(a0 + a1, mf, m2f);+        b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);+        a0 = _mm256_permute2f128_pd(b0, b1, 0x20);+        a1 = _mm256_permute2f128_pd(b0, b1, 0x31);+        _mm256_store_pd(&tab_out[p], a0);+        _mm256_store_pd(&tab_out[p + 4], a1);+        p += 2 * 4;+    }+    tmp = tab_in;+    tab_in = tab_out;+    tab_out = tmp;+    +    nb_blocks = n / 4;+    fft_per_block = 4;++    l = fft_len_log2 - 2;+    while (nb_blocks != 2) {+        nb_blocks >>= 1;+        p = 0;+        k = 0;+        trig = get_trig(s, l, inverse, m_idx);+        if (!trig)+            return -1;+        for(i = 0; i < nb_blocks; i++) {+            c = _mm256_set1_pd(trig[0]);+            trig++;+            for(j = 0; j < fft_per_block; j += 4) {+                a0 = _mm256_load_pd(&tab_in[k + j]);+                a1 = _mm256_load_pd(&tab_in[k + j + stride_in]);+                b0 = ntt_mod(a0 + a1, mf, m2f);+                b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);+                _mm256_store_pd(&tab_out[p + j], b0);+                _mm256_store_pd(&tab_out[p + j + fft_per_block], b1);+            }+            k += fft_per_block;+            p += 2 * fft_per_block;+        }+        fft_per_block <<= 1;+        l--;+        tmp = tab_in;+        tab_in = tab_out;+        tab_out = tmp;+    }++    tab_out = out_buf;+    for(k = 0; k < stride_in; k += 4) {+        a0 = _mm256_load_pd(&tab_in[k]);+        a1 = _mm256_load_pd(&tab_in[k + stride_in]);+        b0 = ntt_mod(a0 + a1, mf, m2f);+        b1 = ntt_mod(a0 - a1, mf, m2f);+        _mm256_store_pd(&tab_out[k], b0);+        _mm256_store_pd(&tab_out[k + stride_in], b1);+    }+    return 0;+}++static void ntt_vec_mul(BFNTTState *s,+                        NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2,+                        int k_tot, int m_idx)+{+    limb_t i, c_inv, n, m;+    __m256d m_inv, mf, a, b, c;+    +    m = ntt_mods[m_idx];+    c_inv = s->ntt_len_inv[m_idx][k_tot][0];+    m_inv = _mm256_set1_pd(1.0 / (double)m);+    mf = _mm256_set1_pd(m);+    c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m));+    n = (limb_t)1 << fft_len_log2;+    for(i = 0; i < n; i += 4) {+        a = _mm256_load_pd(&tab1[i]);+        b = _mm256_load_pd(&tab2[i]);+        a = ntt_mul_mod(a, b, mf, m_inv);+        a = ntt_mul_mod(a, c, mf, m_inv);+        _mm256_store_pd(&tab1[i], a);+    }+}++static no_inline void mul_trig(NTTLimb *buf,+                               limb_t n, limb_t c1, limb_t m, limb_t m_inv1)+{+    limb_t i, c2, c3, c4;+    __m256d c, c_mul, a0, mf, m_inv;+    assert(n >= 2);+    +    mf = _mm256_set1_pd(m);+    m_inv = _mm256_set1_pd(1.0 / (double)m);++    c2 = mul_mod_fast(c1, c1, m, m_inv1);+    c3 = mul_mod_fast(c2, c1, m, m_inv1);+    c4 = mul_mod_fast(c2, c2, m, m_inv1);+    c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m),+                       int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m));+    c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m));+    for(i = 0; i < n; i += 4) {+        a0 = _mm256_load_pd(&buf[i]);+        a0 = ntt_mul_mod(a0, c, mf, m_inv);+        _mm256_store_pd(&buf[i], a0);+        c = ntt_mul_mod(c, c_mul, mf, m_inv);+    }+}++#else++static void *ntt_malloc(BFNTTState *s, size_t size)+{+    return bf_malloc(s->ctx, size);+}++static void ntt_free(BFNTTState *s, void *ptr)+{+    bf_free(s->ctx, ptr);+}++static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m)+{+    if (a >= m)+        a -= m;+    return a;+}++static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m)+{+    return a;+}++static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf,+                             NTTLimb *tmp_buf, int fft_len_log2,+                             int inverse, int m_idx)+{+    limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2;+    NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv;+    int l;+    +    m = ntt_mods[m_idx];+    m2 = 2 * m;+    n = (limb_t)1 << fft_len_log2;+    nb_blocks = n;+    fft_per_block = 1;+    stride_in = n / 2;+    tab_in = in_buf;+    tab_out = tmp_buf;+    l = fft_len_log2;+    while (nb_blocks != 2) {+        nb_blocks >>= 1;+        p = 0;+        k = 0;+        trig = get_trig(s, l, inverse, m_idx);+        if (!trig)+            return -1;+        for(i = 0; i < nb_blocks; i++) {+            c = trig[0];+            c_inv = trig[1];+            trig += 2;+            for(j = 0; j < fft_per_block; j++) {+                a0 = tab_in[k + j];+                a1 = tab_in[k + j + stride_in];+                b0 = add_mod(a0, a1, m2);+                b1 = a0 - a1 + m2;+                b1 = mul_mod_fast3(b1, c, m, c_inv);+                tab_out[p + j] = b0;+                tab_out[p + j + fft_per_block] = b1;+            }+            k += fft_per_block;+            p += 2 * fft_per_block;+        }+        fft_per_block <<= 1;+        l--;+        tmp = tab_in;+        tab_in = tab_out;+        tab_out = tmp;+    }+    /* no twiddle in last step */+    tab_out = out_buf; +    for(k = 0; k < stride_in; k++) {+        a0 = tab_in[k];+        a1 = tab_in[k + stride_in];+        b0 = add_mod(a0, a1, m2);+        b1 = sub_mod(a0, a1, m2);+        tab_out[k] = b0;+        tab_out[k + stride_in] = b1;+    }+    return 0;+}++static void ntt_vec_mul(BFNTTState *s,+                        NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2,+                        int k_tot, int m_idx)+{+    limb_t i, norm, norm_inv, a, n, m, m_inv;+    +    m = ntt_mods[m_idx];+    m_inv = s->ntt_mods_div[m_idx];+    norm = s->ntt_len_inv[m_idx][k_tot][0];+    norm_inv = s->ntt_len_inv[m_idx][k_tot][1];+    n = (limb_t)1 << fft_len_log2;+    for(i = 0; i < n; i++) {+        a = tab1[i];+        /* need to reduce the range so that the product is <+           2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */+        if (a >= m)+            a -= m;+        a = mul_mod_fast(a, tab2[i], m, m_inv);+        a = mul_mod_fast3(a, norm, m, norm_inv);+        tab1[i] = a;+    }+}++static no_inline void mul_trig(NTTLimb *buf,+                               limb_t n, limb_t c_mul, limb_t m, limb_t m_inv)+{+    limb_t i, c0, c_mul_inv;+    +    c0 = 1;+    c_mul_inv = init_mul_mod_fast2(c_mul, m);+    for(i = 0; i < n; i++) {+        buf[i] = mul_mod_fast(buf[i], c0, m, m_inv);+        c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv);+    }+}++#endif /* !AVX2 */++static no_inline NTTLimb *get_trig(BFNTTState *s,+                                   int k, int inverse, int m_idx)+{+    NTTLimb *tab;+    limb_t i, n2, c, c_mul, m, c_mul_inv;+    +    if (k > NTT_TRIG_K_MAX)+        return NULL;++    tab = s->ntt_trig[m_idx][inverse][k];+    if (tab)+        return tab;+    n2 = (limb_t)1 << (k - 1);+    m = ntt_mods[m_idx];+#ifdef __AVX2__+    tab = ntt_malloc(s, sizeof(NTTLimb) * n2);+#else+    tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2);+#endif+    if (!tab)+        return NULL;+    c = 1;+    c_mul = s->ntt_proot_pow[m_idx][inverse][k];+    c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k];+    for(i = 0; i < n2; i++) {+#ifdef __AVX2__+        tab[i] = int_to_ntt_limb2(c, m);+#else+        tab[2 * i] = int_to_ntt_limb(c, m);+        tab[2 * i + 1] = init_mul_mod_fast2(c, m);+#endif+        c = mul_mod_fast2(c, c_mul, m, c_mul_inv);+    }+    s->ntt_trig[m_idx][inverse][k] = tab;+    return tab;+}++void fft_clear_cache(bf_context_t *s1)+{+    int m_idx, inverse, k;+    BFNTTState *s = s1->ntt_state;+    if (s) {+        for(m_idx = 0; m_idx < NB_MODS; m_idx++) {+            for(inverse = 0; inverse < 2; inverse++) {+                for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) {+                    if (s->ntt_trig[m_idx][inverse][k]) {+                        ntt_free(s, s->ntt_trig[m_idx][inverse][k]);+                        s->ntt_trig[m_idx][inverse][k] = NULL;+                    }+                }+            }+        }+#if defined(__AVX2__)+        bf_aligned_free(s1, s);+#else+        bf_free(s1, s);+#endif+        s1->ntt_state = NULL;+    }+}++#define STRIP_LEN 16++/* dst = buf1, src = buf2 */+static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1,+                           int k1, int k2, limb_t n1, limb_t n2, int inverse,+                           limb_t m_idx)+{+    limb_t i, j, c_mul, c0, m, m_inv, strip_len, l;+    NTTLimb *buf2, *buf3;+    +    buf2 = NULL;+    buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1);+    if (!buf3)+        goto fail;+    if (k2 == 0) {+        if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx))+            goto fail;+    } else {+        strip_len = STRIP_LEN;+        buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len);+        if (!buf2)+            goto fail;+        m = ntt_mods[m_idx];+        m_inv = s->ntt_mods_div[m_idx];+        c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2];+        c_mul = 1;+        assert((n2 % strip_len) == 0);+        for(j = 0; j < n2; j += strip_len) {+            for(i = 0; i < n1; i++) {+                for(l = 0; l < strip_len; l++) {+                    buf2[i + l * n1] = buf1[i * n2 + (j + l)];+                }+            }+            for(l = 0; l < strip_len; l++) {+                if (inverse)+                    mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv);+                if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx))+                    goto fail;+                if (!inverse)+                    mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv);+                c_mul = mul_mod_fast(c_mul, c0, m, m_inv);+            }+            +            for(i = 0; i < n1; i++) {+                for(l = 0; l < strip_len; l++) {+                    buf1[i * n2 + (j + l)] = buf2[i + l *n1];+                }+            }+        }+        ntt_free(s, buf2);+    }+    ntt_free(s, buf3);+    return 0;+ fail:+    ntt_free(s, buf2);+    ntt_free(s, buf3);+    return -1;+}+++/* dst = buf1, src = buf2, tmp = buf3 */+static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2,+                    int k, int k_tot, limb_t m_idx)+{+    limb_t n1, n2, i;+    int k1, k2;+    +    if (k <= NTT_TRIG_K_MAX) {+        k1 = k;+    } else {+        /* recursive split of the FFT */+        k1 = bf_min(k / 2, NTT_TRIG_K_MAX);+    }+    k2 = k - k1;+    n1 = (limb_t)1 << k1;+    n2 = (limb_t)1 << k2;+    +    if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx))+        return -1;+    if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx))+        return -1;+    if (k2 == 0) {+        ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx);+    } else {+        for(i = 0; i < n1; i++) {+            ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx);+        }+    }+    if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx))+        return -1;+    return 0;+}+++static no_inline void limb_to_ntt(BFNTTState *s,+                                  NTTLimb *tabr, limb_t fft_len,+                                  const limb_t *taba, limb_t a_len, int dpl,+                                  int first_m_idx, int nb_mods)+{+    slimb_t i, n;+    dlimb_t a, b;+    int j, shift;+    limb_t base_mask1, a0, a1, a2, r, m, m_inv;+    +#if 0+    for(i = 0; i < a_len; i++) {+        printf("%" PRId64 ": " FMT_LIMB "\n",+               (int64_t)i, taba[i]);+    }+#endif   +    memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods);+    shift = dpl & (LIMB_BITS - 1);+    if (shift == 0)+        base_mask1 = -1;+    else+        base_mask1 = ((limb_t)1 << shift) - 1;+    n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl);+    for(i = 0; i < n; i++) {+        a0 = get_bits(taba, a_len, i * dpl);+        if (dpl <= LIMB_BITS) {+            a0 &= base_mask1;+            a = a0;+        } else {+            a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS);+            if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) {+                a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS);+            } else {+                if (dpl > 2 * LIMB_BITS) {+                    a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) &+                        base_mask1;+                } else {+                    a1 &= base_mask1;+                    a2 = 0;+                }+                //            printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0);+                a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) |+                    ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) |+                    ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN));+                a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1;+            }+        }+        for(j = 0; j < nb_mods; j++) {+            m = ntt_mods[first_m_idx + j];+            m_inv = s->ntt_mods_div[first_m_idx + j];+            r = mod_fast(a, m, m_inv);+            if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) {+                b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0;+                r = mod_fast(b, m, m_inv);+            }+            tabr[i + j * fft_len] = int_to_ntt_limb(r, m);+        }+    }+}++#if defined(__AVX2__)++#define VEC_LEN 4++typedef union {+    __m256d v;+    double d[4];+} VecUnion;++static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,+                                  const NTTLimb *buf, int fft_len_log2, int dpl,+                                  int nb_mods)+{+    const limb_t *mods = ntt_mods + NB_MODS - nb_mods;+    const __m256d *mods_cr_vec, *mf, *m_inv;+    VecUnion y[NB_MODS];+    limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r;+    slimb_t i, len, pos;+    int j, k, l, shift, n_limb1, p;+    dlimb_t t;+        +    j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2;+    mods_cr_vec = s->ntt_mods_cr_vec + j;+    mf = s->ntt_mods_vec + NB_MODS - nb_mods;+    m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods;+        +    shift = dpl & (LIMB_BITS - 1);+    if (shift == 0)+        base_mask1 = -1;+    else+        base_mask1 = ((limb_t)1 << shift) - 1;+    n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS;+    for(j = 0; j < NB_MODS; j++) +        carry[j] = 0;+    for(j = 0; j < NB_MODS; j++) +        u[j] = 0; /* avoid warnings */+    memset(tabr, 0, sizeof(limb_t) * r_len);+    fft_len = (limb_t)1 << fft_len_log2;+    len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl);+    len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1);+    i = 0;+    while (i < len) {+        for(j = 0; j < nb_mods; j++)+            y[j].v = *(__m256d *)&buf[i + fft_len * j];++        /* Chinese remainder to get mixed radix representation */+        l = 0;+        for(j = 0; j < nb_mods - 1; j++) {+            y[j].v = ntt_mod1(y[j].v, mf[j]);+            for(k = j + 1; k < nb_mods; k++) {+                y[k].v = ntt_mul_mod(y[k].v - y[j].v,+                                     mods_cr_vec[l], mf[k], m_inv[k]);+                l++;+            }+        }+        y[j].v = ntt_mod1(y[j].v, mf[j]);+        +        for(p = 0; p < VEC_LEN; p++) {+            /* back to normal representation */+            u[0] = (int64_t)y[nb_mods - 1].d[p];+            l = 1;+            for(j = nb_mods - 2; j >= 1; j--) {+                r = (int64_t)y[j].d[p];+                for(k = 0; k < l; k++) {+                    t = (dlimb_t)u[k] * mods[j] + r;+                    r = t >> LIMB_BITS;+                    u[k] = t;+                }+                u[l] = r;+                l++;+            }+            /* XXX: for nb_mods = 5, l should be 4 */+            +            /* last step adds the carry */+            r = (int64_t)y[0].d[p];+            for(k = 0; k < l; k++) {+                t = (dlimb_t)u[k] * mods[j] + r + carry[k];+                r = t >> LIMB_BITS;+                u[k] = t;+            }+            u[l] = r + carry[l];++#if 0+            printf("%" PRId64 ": ", i);+            for(j = nb_mods - 1; j >= 0; j--) {+                printf(" %019" PRIu64, u[j]);+            }+            printf("\n");+#endif+            +            /* write the digits */+            pos = i * dpl;+            for(j = 0; j < n_limb1; j++) {+                put_bits(tabr, r_len, pos, u[j]);+                pos += LIMB_BITS;+            }+            put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1);+            /* shift by dpl digits and set the carry */+            if (shift == 0) {+                for(j = n_limb1 + 1; j < nb_mods; j++)+                    carry[j - (n_limb1 + 1)] = u[j];+            } else {+                for(j = n_limb1; j < nb_mods - 1; j++) {+                    carry[j - n_limb1] = (u[j] >> shift) |+                        (u[j + 1] << (LIMB_BITS - shift));+                }+                carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift;+            }+            i++;+        }+    }+}+#else+static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,+                                  const NTTLimb *buf, int fft_len_log2, int dpl,+                                  int nb_mods)+{+    const limb_t *mods = ntt_mods + NB_MODS - nb_mods;+    const limb_t *mods_cr, *mods_cr_inv;+    limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r;+    slimb_t i, len, pos;+    int j, k, l, shift, n_limb1;+    dlimb_t t;+        +    j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2;+    mods_cr = ntt_mods_cr + j;+    mods_cr_inv = s->ntt_mods_cr_inv + j;++    shift = dpl & (LIMB_BITS - 1);+    if (shift == 0)+        base_mask1 = -1;+    else+        base_mask1 = ((limb_t)1 << shift) - 1;+    n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS;+    for(j = 0; j < NB_MODS; j++) +        carry[j] = 0;+    for(j = 0; j < NB_MODS; j++) +        u[j] = 0; /* avoid warnings */+    memset(tabr, 0, sizeof(limb_t) * r_len);+    fft_len = (limb_t)1 << fft_len_log2;+    len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl);+    for(i = 0; i < len; i++) {+        for(j = 0; j < nb_mods; j++)  {+            y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]);+        }++        /* Chinese remainder to get mixed radix representation */+        l = 0;+        for(j = 0; j < nb_mods - 1; j++) {+            for(k = j + 1; k < nb_mods; k++) {+                limb_t m;+                m = mods[k];+                /* Note: there is no overflow in the sub_mod() because+                   the modulos are sorted by increasing order */+                y[k] = mul_mod_fast2(y[k] - y[j] + m, +                                     mods_cr[l], m, mods_cr_inv[l]);+                l++;+            }+        }+        +        /* back to normal representation */+        u[0] = y[nb_mods - 1];+        l = 1;+        for(j = nb_mods - 2; j >= 1; j--) {+            r = y[j];+            for(k = 0; k < l; k++) {+                t = (dlimb_t)u[k] * mods[j] + r;+                r = t >> LIMB_BITS;+                u[k] = t;+            }+            u[l] = r;+            l++;+        }+        +        /* last step adds the carry */+        r = y[0];+        for(k = 0; k < l; k++) {+            t = (dlimb_t)u[k] * mods[j] + r + carry[k];+            r = t >> LIMB_BITS;+            u[k] = t;+        }+        u[l] = r + carry[l];++#if 0+        printf("%" PRId64 ": ", (int64_t)i);+        for(j = nb_mods - 1; j >= 0; j--) {+            printf(" " FMT_LIMB, u[j]);+        }+        printf("\n");+#endif+        +        /* write the digits */+        pos = i * dpl;+        for(j = 0; j < n_limb1; j++) {+            put_bits(tabr, r_len, pos, u[j]);+            pos += LIMB_BITS;+        }+        put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1);+        /* shift by dpl digits and set the carry */+        if (shift == 0) {+            for(j = n_limb1 + 1; j < nb_mods; j++)+                carry[j - (n_limb1 + 1)] = u[j];+        } else {+            for(j = n_limb1; j < nb_mods - 1; j++) {+                carry[j - n_limb1] = (u[j] >> shift) |+                    (u[j + 1] << (LIMB_BITS - shift));+            }+            carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift;+        }+    }+}+#endif++static int ntt_static_init(bf_context_t *s1)+{+    BFNTTState *s;+    int inverse, i, j, k, l;+    limb_t c, c_inv, c_inv2, m, m_inv;++    if (s1->ntt_state)+        return 0;+#if defined(__AVX2__)+    s = bf_aligned_malloc(s1, sizeof(*s), 64);+#else+    s = bf_malloc(s1, sizeof(*s));+#endif+    if (!s)+        return -1;+    memset(s, 0, sizeof(*s));+    s1->ntt_state = s;+    s->ctx = s1;+    +    for(j = 0; j < NB_MODS; j++) {+        m = ntt_mods[j];+        m_inv = init_mul_mod_fast(m);+        s->ntt_mods_div[j] = m_inv;+#if defined(__AVX2__)+        s->ntt_mods_vec[j] = _mm256_set1_pd(m);+        s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m);+#endif+        c_inv2 = (m + 1) / 2; /* 1/2 */+        c_inv = 1;+        for(i = 0; i <= NTT_PROOT_2EXP; i++) {+            s->ntt_len_inv[j][i][0] = c_inv;+            s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m);+            c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv);+        }++        for(inverse = 0; inverse < 2; inverse++) {+            c = ntt_proot[inverse][j];+            for(i = 0; i < NTT_PROOT_2EXP; i++) {+                s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c;+                s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] =+                    init_mul_mod_fast2(c, m);+                c = mul_mod_fast(c, c, m, m_inv);+            }+        }+    }++    l = 0;+    for(j = 0; j < NB_MODS - 1; j++) {+        for(k = j + 1; k < NB_MODS; k++) {+#if defined(__AVX2__)+            s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l],+                                                                    ntt_mods[k]));+#else+            s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l],+                                                       ntt_mods[k]);+#endif+            l++;+        }+    }+    return 0;+}++int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)+{+    int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found;+    int int_bits, nb_mods_found;+    limb_t cost, min_cost;+    +    min_cost = -1;+    dpl_found = 0;+    nb_mods_found = 4;+    fft_len_log2_found = 0;+    for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) {+        int_bits = ntt_int_bits[NB_MODS - nb_mods];+        dpl = bf_min((int_bits - 4) / 2,+                     2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX);+        for(;;) {+            fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl);+            if (fft_len_log2 > NTT_PROOT_2EXP)+                goto next;+            n_bits = fft_len_log2 + 2 * dpl;+            if (n_bits <= int_bits) {+                cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods;+                //                printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost);+                if (cost < min_cost) {+                    min_cost = cost;+                    dpl_found = dpl;+                    nb_mods_found = nb_mods;+                    fft_len_log2_found = fft_len_log2;+                }+                break;+            }+            dpl--;+            if (dpl == 0)+                break;+        }+    next: ;+    }+    if (!dpl_found)+        abort();+    /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */+    if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) &&+        ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >=+        len * LIMB_BITS) {+        dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN;+    }+    *pnb_mods = nb_mods_found;+    *pdpl = dpl_found;+    return fft_len_log2_found;+}++/* return 0 if OK, -1 if memory error */+static no_inline int fft_mul(bf_context_t *s1,+                             bf_t *res, limb_t *a_tab, limb_t a_len,+                             limb_t *b_tab, limb_t b_len, int mul_flags)+{+    BFNTTState *s;+    int dpl, fft_len_log2, j, nb_mods, reduced_mem;+    slimb_t len, fft_len;+    NTTLimb *buf1, *buf2, *ptr;+#if defined(USE_MUL_CHECK)+    limb_t ha, hb, hr, h_ref;+#endif+    +    if (ntt_static_init(s1))+        return -1;+    s = s1->ntt_state;+    +    /* find the optimal number of digits per limb (dpl) */+    len = a_len + b_len;+    fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len);+    fft_len = (uint64_t)1 << fft_len_log2;+    //    printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl);+#if defined(USE_MUL_CHECK)+    ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0);+    hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0);+#endif+    if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) {+        if (!(mul_flags & FFT_MUL_R_NORESIZE))+            bf_resize(res, 0);+    } else if (mul_flags & FFT_MUL_R_OVERLAP_B) {+        limb_t *tmp_tab, tmp_len;+        /* it is better to free 'b' first */+        tmp_tab = a_tab;+        a_tab = b_tab;+        b_tab = tmp_tab;+        tmp_len = a_len;+        a_len = b_len;+        b_len = tmp_len;+    }+    buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods);+    if (!buf1)+        return -1;+    limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl,+                NB_MODS - nb_mods, nb_mods);+    if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == +        FFT_MUL_R_OVERLAP_A) {+        if (!(mul_flags & FFT_MUL_R_NORESIZE))+            bf_resize(res, 0);+    }+    reduced_mem = (fft_len_log2 >= 14);+    if (!reduced_mem) {+        buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods);+        if (!buf2)+            goto fail;+        limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl,+                    NB_MODS - nb_mods, nb_mods);+        if (!(mul_flags & FFT_MUL_R_NORESIZE))+            bf_resize(res, 0); /* in case res == b */+    } else {+        buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len);+        if (!buf2)+            goto fail;+    }+    for(j = 0; j < nb_mods; j++) {+        if (reduced_mem) {+            limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl,+                        NB_MODS - nb_mods + j, 1);+            ptr = buf2;+        } else {+            ptr = buf2 + fft_len * j;+        }+        if (ntt_conv(s, buf1 + fft_len * j, ptr,+                     fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods))+            goto fail;+    }+    if (!(mul_flags & FFT_MUL_R_NORESIZE))+        bf_resize(res, 0); /* in case res == b and reduced mem */+    ntt_free(s, buf2);+    buf2 = NULL;+    if (!(mul_flags & FFT_MUL_R_NORESIZE)) {+        if (bf_resize(res, len))+            goto fail;+    }+    ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods);+    ntt_free(s, buf1);+#if defined(USE_MUL_CHECK)+    hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0);+    h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD);+    if (hr != h_ref) {+        printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n",+               len, fft_len_log2, dpl, nb_mods);+        //        printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref);+        exit(1);+    }+#endif    +    return 0;+ fail:+    ntt_free(s, buf1);+    ntt_free(s, buf2);+    return -1;+}++#else /* USE_FFT_MUL */++int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)+{+    return 0;+}++#endif /* !USE_FFT_MUL */
+ quickjs/libregexp.c view
@@ -0,0 +1,2588 @@+/*+ * Regular Expression Engine+ * + * Copyright (c) 2017-2018 Fabrice Bellard+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#include <stdlib.h>+#include <stdio.h>+#include <stdarg.h>+#include <inttypes.h>+#include <string.h>+#include <assert.h>++#include "cutils.h"+#include "libregexp.h"++/*+  TODO:++  - Add full unicode canonicalize rules for character ranges (not+    really useful but needed for exact "ignorecase" compatibility).++  - Add a lock step execution mode (=linear time execution guaranteed)+    when the regular expression is "simple" i.e. no backreference nor+    complicated lookahead. The opcodes are designed for this execution+    model.+*/++#if defined(TEST)+#define DUMP_REOP+#endif++typedef enum {+#define DEF(id, size) REOP_ ## id,+#include "libregexp-opcode.h"+#undef DEF+    REOP_COUNT,+} REOPCodeEnum;++#define CAPTURE_COUNT_MAX 255+#define STACK_SIZE_MAX 255++/* unicode code points */+#define CP_LS   0x2028+#define CP_PS   0x2029++#define TMP_BUF_SIZE 128++typedef struct {+    DynBuf byte_code;+    const uint8_t *buf_ptr;+    const uint8_t *buf_end;+    const uint8_t *buf_start;+    int re_flags;+    BOOL is_utf16;+    BOOL ignore_case;+    BOOL dotall;+    int capture_count;+    int total_capture_count; /* -1 = not computed yet */+    int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */+    void *mem_opaque;+    DynBuf group_names;+    union {+        char error_msg[TMP_BUF_SIZE];+        char tmp_buf[TMP_BUF_SIZE];+    } u;+} REParseState;++typedef struct {+#ifdef DUMP_REOP+    const char *name;+#endif+    uint8_t size;+} REOpCode;++static const REOpCode reopcode_info[REOP_COUNT] = {+#ifdef DUMP_REOP+#define DEF(id, size) { #id, size },+#else+#define DEF(id, size) { size },+#endif+#include "libregexp-opcode.h"+#undef DEF+};++#define RE_HEADER_FLAGS         0+#define RE_HEADER_CAPTURE_COUNT 1+#define RE_HEADER_STACK_SIZE    2++#define RE_HEADER_LEN 7++static inline int is_digit(int c) {+    return c >= '0' && c <= '9';+}++/* insert 'len' bytes at position 'pos'. Return < 0 if error. */+static int dbuf_insert(DynBuf *s, int pos, int len)+{+    if (dbuf_realloc(s, s->size + len))+        return -1;+    memmove(s->buf + pos + len, s->buf + pos, s->size - pos);+    s->size += len;+    return 0;+}++/* canonicalize with the specific JS regexp rules */+static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16)+{+    uint32_t res[LRE_CC_RES_LEN_MAX];+    int len;+    if (is_utf16) {+        if (likely(c < 128)) {+            if (c >= 'A' && c <= 'Z')+                c = c - 'A' + 'a';+        } else {+            lre_case_conv(res, c, 2);+            c = res[0];+        }+    } else {+        if (likely(c < 128)) {+            if (c >= 'a' && c <= 'z')+                c = c - 'a' + 'A';+        } else {+            /* legacy regexp: to upper case if single char >= 128 */+            len = lre_case_conv(res, c, FALSE);+            if (len == 1 && res[0] >= 128)+                c = res[0];+        }+    }+    return c;+}++static const uint16_t char_range_d[] = {+    1,+    0x0030, 0x0039 + 1,+};++/* code point ranges for Zs,Zl or Zp property */+static const uint16_t char_range_s[] = {+    10,+    0x0009, 0x000D + 1,+    0x0020, 0x0020 + 1,+    0x00A0, 0x00A0 + 1,+    0x1680, 0x1680 + 1,+    0x2000, 0x200A + 1,+    /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */+    /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */+    0x2028, 0x2029 + 1,+    0x202F, 0x202F + 1,+    0x205F, 0x205F + 1,+    0x3000, 0x3000 + 1,+    /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */+    0xFEFF, 0xFEFF + 1,+};++BOOL lre_is_space(int c)+{+    int i, n, low, high;+    n = (countof(char_range_s) - 1) / 2;+    for(i = 0; i < n; i++) {+        low = char_range_s[2 * i + 1];+        if (c < low)+            return FALSE;+        high = char_range_s[2 * i + 2];+        if (c < high)+            return TRUE;+    }+    return FALSE;+}++uint32_t const lre_id_start_table_ascii[4] = {+    /* $ A-Z _ a-z */+    0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE+};++uint32_t const lre_id_continue_table_ascii[4] = {+    /* $ 0-9 A-Z _ a-z */+    0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE+};+++static const uint16_t char_range_w[] = {+    4,+    0x0030, 0x0039 + 1,+    0x0041, 0x005A + 1,+    0x005F, 0x005F + 1,+    0x0061, 0x007A + 1,+};++#define CLASS_RANGE_BASE 0x40000000++typedef enum {+    CHAR_RANGE_d,+    CHAR_RANGE_D,+    CHAR_RANGE_s,+    CHAR_RANGE_S,+    CHAR_RANGE_w,+    CHAR_RANGE_W,+} CharRangeEnum;++static const uint16_t *char_range_table[] = {+    char_range_d,+    char_range_s,+    char_range_w,+};++static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)+{+    BOOL invert;+    const uint16_t *c_pt;+    int len, i;+    +    invert = c & 1;+    c_pt = char_range_table[c >> 1];+    len = *c_pt++;+    cr_init(cr, s->mem_opaque, lre_realloc);+    for(i = 0; i < len * 2; i++) {+        if (cr_add_point(cr, c_pt[i]))+            goto fail;+    }+    if (invert) {+        if (cr_invert(cr))+            goto fail;+    }+    return 0;+ fail:+    cr_free(cr);+    return -1;+}++static int cr_canonicalize(CharRange *cr)+{+    CharRange a;+    uint32_t pt[2];+    int i, ret;++    cr_init(&a, cr->mem_opaque, lre_realloc);+    pt[0] = 'a';+    pt[1] = 'z' + 1;+    ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER);+    if (ret)+        goto fail;+    /* convert to upper case */+    /* XXX: the generic unicode case would be much more complicated+       and not really useful */+    for(i = 0; i < a.len; i++) {+        a.points[i] += 'A' - 'a';+    }+    /* Note: for simplicity we keep the lower case ranges */+    ret = cr_union1(cr, a.points, a.len);+ fail:+    cr_free(&a);+    return ret;+}++#ifdef DUMP_REOP+static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,+                                                     int buf_len)+{+    int pos, len, opcode, bc_len, re_flags, i;+    uint32_t val;+    +    assert(buf_len >= RE_HEADER_LEN);++    re_flags=  buf[0];+    bc_len = get_u32(buf + 3);+    assert(bc_len + RE_HEADER_LEN <= buf_len);+    printf("flags: 0x%x capture_count=%d stack_size=%d\n",+           re_flags, buf[1], buf[2]);+    if (re_flags & LRE_FLAG_NAMED_GROUPS) {+        const char *p;+        p = (char *)buf + RE_HEADER_LEN + bc_len;+        printf("named groups: ");+        for(i = 1; i < buf[1]; i++) {+            if (i != 1)+                printf(",");+            printf("<%s>", p);+            p += strlen(p) + 1;+        }+        printf("\n");+        assert(p == (char *)(buf + buf_len));+    }+    printf("bytecode_len=%d\n", bc_len);++    buf += RE_HEADER_LEN;+    pos = 0;+    while (pos < bc_len) {+        printf("%5u: ", pos);+        opcode = buf[pos];+        len = reopcode_info[opcode].size;+        if (opcode >= REOP_COUNT) {+            printf(" invalid opcode=0x%02x\n", opcode);+            break;+        }+        if ((pos + len) > bc_len) {+            printf(" buffer overflow (opcode=0x%02x)\n", opcode);+            break;+        }+        printf("%s", reopcode_info[opcode].name);+        switch(opcode) {+        case REOP_char:+            val = get_u16(buf + pos + 1);+            if (val >= ' ' && val <= 126)+                printf(" '%c'", val);+            else+                printf(" 0x%04x", val);+            break;+        case REOP_char32:+            val = get_u32(buf + pos + 1);+            if (val >= ' ' && val <= 126)+                printf(" '%c'", val);+            else+                printf(" 0x%08x", val);+            break;+        case REOP_goto:+        case REOP_split_goto_first:+        case REOP_split_next_first:+        case REOP_loop:+        case REOP_lookahead:+        case REOP_negative_lookahead:+        case REOP_bne_char_pos:+            val = get_u32(buf + pos + 1);+            val += (pos + 5);+            printf(" %u", val);+            break;+        case REOP_simple_greedy_quant:+            printf(" %u %u %u %u",+                   get_u32(buf + pos + 1) + (pos + 17),+                   get_u32(buf + pos + 1 + 4),+                   get_u32(buf + pos + 1 + 8),+                   get_u32(buf + pos + 1 + 12));+            break;+        case REOP_save_start:+        case REOP_save_end:+        case REOP_back_reference:+        case REOP_backward_back_reference:+            printf(" %u", buf[pos + 1]);+            break;+        case REOP_save_reset:+            printf(" %u %u", buf[pos + 1], buf[pos + 2]);+            break;+        case REOP_push_i32:+            val = get_u32(buf + pos + 1);+            printf(" %d", val);+            break;+        case REOP_range:+            {+                int n, i;+                n = get_u16(buf + pos + 1);+                len += n * 4;+                for(i = 0; i < n * 2; i++) {+                    val = get_u16(buf + pos + 3 + i * 2);+                    printf(" 0x%04x", val);+                }+            }+            break;+        case REOP_range32:+            {+                int n, i;+                n = get_u16(buf + pos + 1);+                len += n * 8;+                for(i = 0; i < n * 2; i++) {+                    val = get_u32(buf + pos + 3 + i * 4);+                    printf(" 0x%08x", val);+                }+            }+            break;+        default:+            break;+        }+        printf("\n");+        pos += len;+    }+}+#endif++static void re_emit_op(REParseState *s, int op)+{+    dbuf_putc(&s->byte_code, op);+}++/* return the offset of the u32 value */+static int re_emit_op_u32(REParseState *s, int op, uint32_t val)+{+    int pos;+    dbuf_putc(&s->byte_code, op);+    pos = s->byte_code.size;+    dbuf_put_u32(&s->byte_code, val);+    return pos;+}++static int re_emit_goto(REParseState *s, int op, uint32_t val)+{+    int pos;+    dbuf_putc(&s->byte_code, op);+    pos = s->byte_code.size;+    dbuf_put_u32(&s->byte_code, val - (pos + 4));+    return pos;+}++static void re_emit_op_u8(REParseState *s, int op, uint32_t val)+{+    dbuf_putc(&s->byte_code, op);+    dbuf_putc(&s->byte_code, val);+}++static void re_emit_op_u16(REParseState *s, int op, uint32_t val)+{+    dbuf_putc(&s->byte_code, op);+    dbuf_put_u16(&s->byte_code, val);+}++static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...)+{+    va_list ap;+    va_start(ap, fmt);+    vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap);+    va_end(ap);+    return -1;+}++static int re_parse_out_of_memory(REParseState *s)+{+    return re_parse_error(s, "out of memory");+}++/* If allow_overflow is false, return -1 in case of+   overflow. Otherwise return INT32_MAX. */+static int parse_digits(const uint8_t **pp, BOOL allow_overflow)+{+    const uint8_t *p;+    uint64_t v;+    int c;+    +    p = *pp;+    v = 0;+    for(;;) {+        c = *p;+        if (c < '0' || c > '9')+            break;+        v = v * 10 + c - '0';+        if (v >= INT32_MAX) {+            if (allow_overflow)+                v = INT32_MAX;+            else+                return -1;+        }+        p++;+    }+    *pp = p;+    return v;+}++static int re_parse_expect(REParseState *s, const uint8_t **pp, int c)+{+    const uint8_t *p;+    p = *pp;+    if (*p != c)+        return re_parse_error(s, "expecting '%c'", c);+    p++;+    *pp = p;+    return 0;+}++/* Parse an escape sequence, *pp points after the '\':+   allow_utf16 value:+   0 : no UTF-16 escapes allowed+   1 : UTF-16 escapes allowed+   2 : UTF-16 escapes allowed and escapes of surrogate pairs are+   converted to a unicode character (unicode regexp case).++   Return the unicode char and update *pp if recognized,+   return -1 if malformed escape,+   return -2 otherwise. */+int lre_parse_escape(const uint8_t **pp, int allow_utf16)+{+    const uint8_t *p;+    uint32_t c;++    p = *pp;+    c = *p++;+    switch(c) {+    case 'b':+        c = '\b';+        break;+    case 'f':+        c = '\f';+        break;+    case 'n':+        c = '\n';+        break;+    case 'r':+        c = '\r';+        break;+    case 't':+        c = '\t';+        break;+    case 'v':+        c = '\v';+        break;+    case 'x':+    case 'u':+        {+            int h, n, i;+            uint32_t c1;+            +            if (*p == '{' && allow_utf16) {+                p++;+                c = 0;+                for(;;) {+                    h = from_hex(*p++);+                    if (h < 0)+                        return -1;+                    c = (c << 4) | h;+                    if (c > 0x10FFFF)+                        return -1;+                    if (*p == '}')+                        break;+                }+                p++;+            } else {+                if (c == 'x') {+                    n = 2;+                } else {+                    n = 4;+                }++                c = 0;+                for(i = 0; i < n; i++) {+                    h = from_hex(*p++);+                    if (h < 0) {+                        return -1;+                    }+                    c = (c << 4) | h;+                }+                if (c >= 0xd800 && c < 0xdc00 &&+                    allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') {+                    /* convert an escaped surrogate pair into a+                       unicode char */+                    c1 = 0;+                    for(i = 0; i < 4; i++) {+                        h = from_hex(p[2 + i]);+                        if (h < 0)+                            break;+                        c1 = (c1 << 4) | h;+                    }+                    if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) {+                        p += 6;+                        c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+                    }+                }+            }+        }+        break;+    case '0' ... '7':+        c -= '0';+        if (allow_utf16 == 2) {+            /* only accept \0 not followed by digit */+            if (c != 0 || is_digit(*p))+                return -1;+        } else {+            /* parse a legacy octal sequence */+            uint32_t v;+            v = *p - '0';+            if (v > 7)+                break;+            c = (c << 3) | v;+            p++;+            if (c >= 32)+                break;+            v = *p - '0';+            if (v > 7)+                break;+            c = (c << 3) | v;+            p++;+        }+        break;+    default:+        return -2;+    }+    *pp = p;+    return c;+}++#ifdef CONFIG_ALL_UNICODE+/* XXX: we use the same chars for name and value */+static BOOL is_unicode_char(int c)+{+    return ((c >= '0' && c <= '9') ||+            (c >= 'A' && c <= 'Z') ||+            (c >= 'a' && c <= 'z') ||+            (c == '_'));+}++static int parse_unicode_property(REParseState *s, CharRange *cr,+                                  const uint8_t **pp, BOOL is_inv)+{+    const uint8_t *p;+    char name[64], value[64];+    char *q;+    BOOL script_ext;+    int ret;++    p = *pp;+    if (*p != '{')+        return re_parse_error(s, "expecting '{' after \\p");+    p++;+    q = name;+    while (is_unicode_char(*p)) {+        if ((q - name) > sizeof(name) - 1)+            goto unknown_property_name;+        *q++ = *p++;+    }+    *q = '\0';+    q = value;+    if (*p == '=') {+        p++;+        while (is_unicode_char(*p)) {+            if ((q - value) > sizeof(value) - 1)+                return re_parse_error(s, "unknown unicode property value");+            *q++ = *p++;+        }+    }+    *q = '\0';+    if (*p != '}')+        return re_parse_error(s, "expecting '}'");+    p++;+    //    printf("name=%s value=%s\n", name, value);++    if (!strcmp(name, "Script") || !strcmp(name, "sc")) {+        script_ext = FALSE;+        goto do_script;+    } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) {+        script_ext = TRUE;+    do_script:+        cr_init(cr, s->mem_opaque, lre_realloc);+        ret = unicode_script(cr, value, script_ext);+        if (ret) {+            cr_free(cr);+            if (ret == -2)+                return re_parse_error(s, "unknown unicode script");+            else+                goto out_of_memory;+        }+    } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) {+        cr_init(cr, s->mem_opaque, lre_realloc);+        ret = unicode_general_category(cr, value);+        if (ret) {+            cr_free(cr);+            if (ret == -2)+                return re_parse_error(s, "unknown unicode general category");+            else+                goto out_of_memory;+        }+    } else if (value[0] == '\0') {+        cr_init(cr, s->mem_opaque, lre_realloc);+        ret = unicode_general_category(cr, name);+        if (ret == -1) {+            cr_free(cr);+            goto out_of_memory;+        }+        if (ret < 0) {+            ret = unicode_prop(cr, name);+            if (ret) {+                cr_free(cr);+                if (ret == -2)+                    goto unknown_property_name;+                else+                    goto out_of_memory;+            }+        }+    } else {+    unknown_property_name:+        return re_parse_error(s, "unknown unicode property name");+    }++    if (is_inv) {+        if (cr_invert(cr)) {+            cr_free(cr);+            return -1;+        }+    }+    *pp = p;+    return 0;+ out_of_memory:+    return re_parse_out_of_memory(s);+}+#endif /* CONFIG_ALL_UNICODE */++/* return -1 if error otherwise the character or a class range+   (CLASS_RANGE_BASE). In case of class range, 'cr' is+   initialized. Otherwise, it is ignored. */+static int get_class_atom(REParseState *s, CharRange *cr,+                          const uint8_t **pp, BOOL inclass)+{+    const uint8_t *p;+    uint32_t c;+    int ret;+    +    p = *pp;++    c = *p;+    switch(c) {+    case '\\':+        p++;+        if (p >= s->buf_end)+            goto unexpected_end;+        c = *p++;+        switch(c) {+        case 'd':+            c = CHAR_RANGE_d;+            goto class_range;+        case 'D':+            c = CHAR_RANGE_D;+            goto class_range;+        case 's':+            c = CHAR_RANGE_s;+            goto class_range;+        case 'S':+            c = CHAR_RANGE_S;+            goto class_range;+        case 'w':+            c = CHAR_RANGE_w;+            goto class_range;+        case 'W':+            c = CHAR_RANGE_W;+        class_range:+            if (cr_init_char_range(s, cr, c))+                return -1;+            c = CLASS_RANGE_BASE;+            break;+        case 'c':+            c = *p;+            if ((c >= 'a' && c <= 'z') ||+                (c >= 'A' && c <= 'Z') ||+                (((c >= '0' && c <= '9') || c == '_') &&+                 inclass && !s->is_utf16)) {   /* Annex B.1.4 */+                c &= 0x1f;+                p++;+            } else if (s->is_utf16) {+                goto invalid_escape;+            } else {+                /* otherwise return '\' and 'c' */+                p--;+                c = '\\';+            }+            break;+#ifdef CONFIG_ALL_UNICODE+        case 'p':+        case 'P':+            if (s->is_utf16) {+                if (parse_unicode_property(s, cr, &p, (c == 'P')))+                    return -1;+                c = CLASS_RANGE_BASE;+                break;+            }+            /* fall thru */+#endif+        default:+            p--;+            ret = lre_parse_escape(&p, s->is_utf16 * 2);+            if (ret >= 0) {+                c = ret;+            } else {+                if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) {+                    /* always valid to escape these characters */+                    goto normal_char;+                } else if (s->is_utf16) {+                invalid_escape:+                    return re_parse_error(s, "invalid escape sequence in regular expression");+                } else {+                    /* just ignore the '\' */+                    goto normal_char;+                }+            }+            break;+        }+        break;+    case '\0':+        if (p >= s->buf_end) {+        unexpected_end:+            return re_parse_error(s, "unexpected end");+        }+        /* fall thru */+    default:+    normal_char:+        /* normal char */+        if (c >= 128) {+            c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+            if ((unsigned)c > 0xffff && !s->is_utf16) {+                /* XXX: should handle non BMP-1 code points */+                return re_parse_error(s, "malformed unicode char");+            }+        } else {+            p++;+        }+        break;+    }+    *pp = p;+    return c;+}++static int re_emit_range(REParseState *s, const CharRange *cr)+{+    int len, i;+    uint32_t high;+    +    len = (unsigned)cr->len / 2;+    if (len >= 65535)+        return re_parse_error(s, "too many ranges");+    if (len == 0) {+        /* not sure it can really happen. Emit a match that is always+           false */+        re_emit_op_u32(s, REOP_char32, -1);+    } else {+        high = cr->points[cr->len - 1];+        if (high == UINT32_MAX)+            high = cr->points[cr->len - 2];+        if (high <= 0xffff) {+            /* can use 16 bit ranges with the conversion that 0xffff =+               infinity */+            re_emit_op_u16(s, REOP_range, len);+            for(i = 0; i < cr->len; i += 2) {+                dbuf_put_u16(&s->byte_code, cr->points[i]);+                high = cr->points[i + 1] - 1;+                if (high == UINT32_MAX - 1)+                    high = 0xffff;+                dbuf_put_u16(&s->byte_code, high);+            }+        } else {+            re_emit_op_u16(s, REOP_range32, len);+            for(i = 0; i < cr->len; i += 2) {+                dbuf_put_u32(&s->byte_code, cr->points[i]);+                dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1);+            }+        }+    }+    return 0;+}++static int re_parse_char_class(REParseState *s, const uint8_t **pp)+{+    const uint8_t *p;+    uint32_t c1, c2;+    CharRange cr_s, *cr = &cr_s;+    CharRange cr1_s, *cr1 = &cr1_s;+    BOOL invert;+    +    cr_init(cr, s->mem_opaque, lre_realloc);+    p = *pp;+    p++;    /* skip '[' */+    invert = FALSE;+    if (*p == '^') {+        p++;+        invert = TRUE;+    }+    for(;;) {+        if (*p == ']')+            break;+        c1 = get_class_atom(s, cr1, &p, TRUE);+        if ((int)c1 < 0)+            goto fail;+        if (*p == '-' && p[1] != ']') {+            const uint8_t *p0 = p + 1;+            if (c1 >= CLASS_RANGE_BASE) {+                if (s->is_utf16) {+                    cr_free(cr1);+                    goto invalid_class_range;+                }+                /* Annex B: match '-' character */+                goto class_atom;+            }+            c2 = get_class_atom(s, cr1, &p0, TRUE);+            if ((int)c2 < 0)+                goto fail;+            if (c2 >= CLASS_RANGE_BASE) {+                cr_free(cr1);+                if (s->is_utf16) {+                    goto invalid_class_range;+                }+                /* Annex B: match '-' character */+                goto class_atom;+            }+            p = p0;+            if (c2 < c1) {+            invalid_class_range:+                re_parse_error(s, "invalid class range");+                goto fail;+            }+            if (cr_union_interval(cr, c1, c2))+                goto memory_error;+        } else {+        class_atom:+            if (c1 >= CLASS_RANGE_BASE) {+                int ret;+                ret = cr_union1(cr, cr1->points, cr1->len);+                cr_free(cr1);+                if (ret)+                    goto memory_error;+            } else {+                if (cr_union_interval(cr, c1, c1))+                    goto memory_error;+            }+        }+    }+    if (s->ignore_case) {+        if (cr_canonicalize(cr))+            goto memory_error;+    }+    if (invert) {+        if (cr_invert(cr))+            goto memory_error;+    }+    if (re_emit_range(s, cr))+        goto fail;+    cr_free(cr);+    p++;    /* skip ']' */+    *pp = p;+    return 0;+ memory_error:+    re_parse_out_of_memory(s);+ fail:+    cr_free(cr);+    return -1;+}++/* Return:+   1 if the opcodes in bc_buf[] always advance the character pointer.+   0 if the character pointer may not be advanced.+   -1 if the code may depend on side effects of its previous execution (backreference)+*/+static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)+{+    int pos, opcode, ret, len, i;+    uint32_t val, last;+    BOOL has_back_reference;+    uint8_t capture_bitmap[CAPTURE_COUNT_MAX];+    +    ret = -2; /* not known yet */+    pos = 0;+    has_back_reference = FALSE;+    memset(capture_bitmap, 0, sizeof(capture_bitmap));+    +    while (pos < bc_buf_len) {+        opcode = bc_buf[pos];+        len = reopcode_info[opcode].size;+        switch(opcode) {+        case REOP_range:+            val = get_u16(bc_buf + pos + 1);+            len += val * 4;+            goto simple_char;+        case REOP_range32:+            val = get_u16(bc_buf + pos + 1);+            len += val * 8;+            goto simple_char;+        case REOP_char:+        case REOP_char32:+        case REOP_dot:+        case REOP_any:+        simple_char:+            if (ret == -2)+                ret = 1;+            break;+        case REOP_line_start:+        case REOP_line_end:+        case REOP_push_i32:+        case REOP_push_char_pos:+        case REOP_drop:+        case REOP_word_boundary:+        case REOP_not_word_boundary:+        case REOP_prev:+            /* no effect */+            break;+        case REOP_save_start:+        case REOP_save_end:+            val = bc_buf[pos + 1];+            capture_bitmap[val] |= 1;+            break;+        case REOP_save_reset:+            {+                val = bc_buf[pos + 1];+                last = bc_buf[pos + 2];+                while (val < last)+                    capture_bitmap[val++] |= 1;+            }+            break;+        case REOP_back_reference:+        case REOP_backward_back_reference:+            val = bc_buf[pos + 1];+            capture_bitmap[val] |= 2;+            has_back_reference = TRUE;+            break;+        default:+            /* safe behvior: we cannot predict the outcome */+            if (ret == -2)+                ret = 0;+            break;+        }+        pos += len;+    }+    if (has_back_reference) {+        /* check if there is back reference which references a capture+           made in the some code */+        for(i = 0; i < CAPTURE_COUNT_MAX; i++) {+            if (capture_bitmap[i] == 3)+                return -1;+        }+    }+    if (ret == -2)+        ret = 0;+    return ret;+}++/* return -1 if a simple quantifier cannot be used. Otherwise return+   the number of characters in the atom. */+static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)+{+    int pos, opcode, len, count;+    uint32_t val;+    +    count = 0;+    pos = 0;+    while (pos < bc_buf_len) {+        opcode = bc_buf[pos];+        len = reopcode_info[opcode].size;+        switch(opcode) {+        case REOP_range:+            val = get_u16(bc_buf + pos + 1);+            len += val * 4;+            goto simple_char;+        case REOP_range32:+            val = get_u16(bc_buf + pos + 1);+            len += val * 8;+            goto simple_char;+        case REOP_char:+        case REOP_char32:+        case REOP_dot:+        case REOP_any:+        simple_char:+            count++;+            break;+        case REOP_line_start:+        case REOP_line_end:+        case REOP_word_boundary:+        case REOP_not_word_boundary:+            break;+        default:+            return -1;+        }+        pos += len;+    }+    return count;+}++/* '*pp' is the first char after '<' */+static int re_parse_group_name(char *buf, int buf_size,+                               const uint8_t **pp, BOOL is_utf16)+{+    const uint8_t *p;+    uint32_t c;+    char *q;++    p = *pp;+    q = buf;+    for(;;) {+        c = *p;+        if (c == '\\') {+            p++;+            if (*p != 'u')+                return -1;+            c = lre_parse_escape(&p, is_utf16 * 2);+        } else if (c == '>') {+            break;+        } else if (c >= 128) {+            c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+        } else {+            p++;+        }+        if (c > 0x10FFFF)+            return -1;+        if (q == buf) {+            if (!lre_js_is_ident_first(c))+                return -1;+        } else {+            if (!lre_js_is_ident_next(c))+                return -1;+        }+        if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size)+            return -1;+        if (c < 128) {+            *q++ = c;+        } else {+            q += unicode_to_utf8((uint8_t*)q, c);+        }+    }+    if (q == buf)+        return -1;+    *q = '\0';+    p++;+    *pp = p;+    return 0;+}++/* if capture_name = NULL: return the number of captures + 1.+   Otherwise, return the capture index corresponding to capture_name+   or -1 if none */+static int re_parse_captures(REParseState *s, int *phas_named_captures,+                             const char *capture_name)+{+    const uint8_t *p;+    int capture_index;+    char name[TMP_BUF_SIZE];++    capture_index = 1;+    *phas_named_captures = 0;+    for (p = s->buf_start; p < s->buf_end; p++) {+        switch (*p) {+        case '(':+            if (p[1] == '?') {+                if (p[2] == '<' && p[3] != '=' && p[3] != '!') {+                    *phas_named_captures = 1;+                    /* potential named capture */+                    if (capture_name) {+                        p += 3;+                        if (re_parse_group_name(name, sizeof(name), &p,+                                                s->is_utf16) == 0) {+                            if (!strcmp(name, capture_name))+                                return capture_index;+                        }+                    }+                    capture_index++;+                }+            } else {+                capture_index++;+            }+            break;+        case '\\':+            p++;+            break;+        case '[':+            for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) {+                if (*p == '\\')+                    p++;+            }+            break;+        }+    }+    if (capture_name)+        return -1;+    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);+    }+    return s->total_capture_count;+}++static BOOL re_has_named_captures(REParseState *s)+{+    if (s->has_named_captures < 0)+        re_count_captures(s);+    return s->has_named_captures;+}++static int find_group_name(REParseState *s, const char *name)+{+    const char *p, *buf_end;+    size_t len, name_len;+    int capture_index;+    +    name_len = strlen(name);+    p = (char *)s->group_names.buf;+    buf_end = (char *)s->group_names.buf + s->group_names.size;+    capture_index = 1;+    while (p < buf_end) {+        len = strlen(p);+        if (len == name_len && memcmp(name, p, name_len) == 0)+            return capture_index;+        p += len + 1;+        capture_index++;+    }+    return -1;+}++static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir);++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;+    CharRange cr_s, *cr = &cr_s;+    +    last_atom_start = -1;+    last_capture_count = 0;+    p = s->buf_ptr;+    c = *p;+    switch(c) {+    case '^':+        p++;+        re_emit_op(s, REOP_line_start);+        break;+    case '$':+        p++;+        re_emit_op(s, REOP_line_end);+        break;+    case '.':+        p++;+        last_atom_start = s->byte_code.size;+        last_capture_count = s->capture_count;+        if (is_backward_dir)+            re_emit_op(s, REOP_prev);+        re_emit_op(s, s->dotall ? REOP_any : REOP_dot);+        if (is_backward_dir)+            re_emit_op(s, REOP_prev);+        break;+    case '{':+        if (s->is_utf16) {+            return re_parse_error(s, "syntax error");+        } else if (!is_digit(p[1])) {+            /* Annex B: we accept '{' not followed by digits as a+               normal atom */+            goto parse_class_atom;+        } else {+            const uint8_t *p1 = p + 1;+            /* Annex B: error if it is like a repetition count */+            parse_digits(&p1, TRUE);+            if (*p1 == ',') {+                p1++;+                if (is_digit(*p1)) {+                    parse_digits(&p1, TRUE);+                }+            }+            if (*p1 != '}') {+                goto parse_class_atom;+            }+        }+        /* fall thru */+    case '*':+    case '+':+    case '?':+        return re_parse_error(s, "nothing to repeat");+    case '(':+        if (p[1] == '?') {+            if (p[2] == ':') {+                p += 3;+                last_atom_start = s->byte_code.size;+                last_capture_count = s->capture_count;+                s->buf_ptr = p;+                if (re_parse_disjunction(s, is_backward_dir))+                    return -1;+                p = s->buf_ptr;+                if (re_parse_expect(s, &p, ')'))+                    return -1;+            } else if ((p[2] == '=' || p[2] == '!')) {+                is_neg = (p[2] == '!');+                is_backward_lookahead = FALSE;+                p += 3;+                goto lookahead;+            } else if (p[2] == '<' &&+                       (p[3] == '=' || p[3] == '!')) {+                int pos;+                is_neg = (p[3] == '!');+                is_backward_lookahead = TRUE;+                p += 4;+                /* lookahead */+            lookahead:+                /* Annex B allows lookahead to be used as an atom for+                   the quantifiers */+                if (!s->is_utf16 && !is_backward_lookahead)  {+                    last_atom_start = s->byte_code.size;+                    last_capture_count = s->capture_count;+                }+                pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0);+                s->buf_ptr = p;+                if (re_parse_disjunction(s, is_backward_lookahead))+                    return -1;+                p = s->buf_ptr;+                if (re_parse_expect(s, &p, ')'))+                    return -1;+                re_emit_op(s, REOP_match);+                /* jump after the 'match' after the lookahead is successful */+                if (dbuf_error(&s->byte_code))+                    return -1;+                put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4));+            } else if (p[2] == '<') {+                p += 3;+                if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),+                                        &p, s->is_utf16)) {+                    return re_parse_error(s, "invalid group name");+                }+                if (find_group_name(s, s->u.tmp_buf) > 0) {+                    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);+                s->has_named_captures = 1;+                goto parse_capture;+            } else {+                return re_parse_error(s, "invalid group");+            }+        } else {+            int capture_index;+            p++;+            /* capture without group name */+            dbuf_putc(&s->group_names, 0);+        parse_capture:+            if (s->capture_count >= CAPTURE_COUNT_MAX)+                return re_parse_error(s, "too many captures");+            last_atom_start = s->byte_code.size;+            last_capture_count = s->capture_count;+            capture_index = s->capture_count++;+            re_emit_op_u8(s, REOP_save_start + is_backward_dir,+                          capture_index);+            +            s->buf_ptr = p;+            if (re_parse_disjunction(s, is_backward_dir))+                return -1;+            p = s->buf_ptr;+            +            re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir,+                          capture_index);+            +            if (re_parse_expect(s, &p, ')'))+                return -1;+        }+        break;+    case '\\':+        switch(p[1]) {+        case 'b':+        case 'B':+            re_emit_op(s, REOP_word_boundary + (p[1] != 'b'));+            p += 2;+            break;+        case 'k':+            {+                const uint8_t *p1;+                int dummy_res;+                +                p1 = p;+                if (p1[2] != '<') {+                    /* annex B: we tolerate invalid group names in non+                       unicode mode if there is no named capture+                       definition */+                    if (s->is_utf16 || re_has_named_captures(s))+                        return re_parse_error(s, "expecting group name");+                    else+                        goto parse_class_atom;+                }+                p1 += 3;+                if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),+                                        &p1, s->is_utf16)) {+                    if (s->is_utf16 || re_has_named_captures(s))+                        return re_parse_error(s, "invalid group name");+                    else+                        goto parse_class_atom;+                }+                c = find_group_name(s, s->u.tmp_buf);+                if (c < 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) {+                        if (s->is_utf16 || re_has_named_captures(s))+                            return re_parse_error(s, "group name not defined");+                        else+                            goto parse_class_atom;+                    }+                }+                p = p1;+            }+            goto emit_back_reference;+        case '0':+            p += 2;+            c = 0;+            if (s->is_utf16) {+                if (is_digit(*p)) {+                    return re_parse_error(s, "invalid decimal escape in regular expression");+                }+            } else {+                /* Annex B.1.4: accept legacy octal */+                if (*p >= '0' && *p <= '7') {+                    c = *p++ - '0';+                    if (*p >= '0' && *p <= '7') {+                        c = (c << 3) + *p++ - '0';+                    }+                }+            }+            goto normal_char;+        case '1' ... '9':+            {+                const uint8_t *q = ++p;+                +                c = parse_digits(&p, FALSE);+                if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {+                    if (!s->is_utf16) {+                        /* Annex B.1.4: accept legacy octal */+                        p = q;+                        if (*p <= '7') {+                            c = 0;+                            if (*p <= '3')+                                c = *p++ - '0';+                            if (*p >= '0' && *p <= '7') {+                                c = (c << 3) + *p++ - '0';+                                if (*p >= '0' && *p <= '7') {+                                    c = (c << 3) + *p++ - '0';+                                }+                            }+                        } else {+                            c = *p++;+                        }+                        goto normal_char;+                    }+                    return re_parse_error(s, "back reference out of range in reguar 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 + is_backward_dir, c);+            }+            break;+        default:+            goto parse_class_atom;+        }+        break;+    case '[':+        last_atom_start = s->byte_code.size;+        last_capture_count = s->capture_count;+        if (is_backward_dir)+            re_emit_op(s, REOP_prev);+        if (re_parse_char_class(s, &p))+            return -1;+        if (is_backward_dir)+            re_emit_op(s, REOP_prev);+        break;+    case ']':+    case '}':+        if (s->is_utf16)+            return re_parse_error(s, "syntax error");+        goto parse_class_atom;+    default:+    parse_class_atom:+        c = get_class_atom(s, cr, &p, FALSE);+        if ((int)c < 0)+            return -1;+    normal_char:+        last_atom_start = s->byte_code.size;+        last_capture_count = s->capture_count;+        if (is_backward_dir)+            re_emit_op(s, REOP_prev);+        if (c >= CLASS_RANGE_BASE) {+            int ret;+            /* Note: canonicalization is not needed */+            ret = re_emit_range(s, cr);+            cr_free(cr);+            if (ret)+                return -1;+        } else {+            if (s->ignore_case)+                c = lre_canonicalize(c, s->is_utf16);+            if (c <= 0xffff)+                re_emit_op_u16(s, REOP_char, c);+            else+                re_emit_op_u32(s, REOP_char32, c);+        }+        if (is_backward_dir)+            re_emit_op(s, REOP_prev);+        break;+    }++    /* quantifier */+    if (last_atom_start >= 0) {+        c = *p;+        switch(c) {+        case '*':+            p++;+            quant_min = 0;+            quant_max = INT32_MAX;+            goto quantifier;+        case '+':+            p++;+            quant_min = 1;+            quant_max = INT32_MAX;+            goto quantifier;+        case '?':+            p++;+            quant_min = 0;+            quant_max = 1;+            goto quantifier;+        case '{':+            {+                const uint8_t *p1 = p;+                /* As an extension (see ES6 annex B), we accept '{' not+                   followed by digits as a normal atom */+                if (!is_digit(p[1])) {+                    if (s->is_utf16)+                        goto invalid_quant_count;+                    break;+                }+                p++;+                quant_min = parse_digits(&p, TRUE);+                quant_max = quant_min;+                if (*p == ',') {+                    p++;+                    if (is_digit(*p)) {+                        quant_max = parse_digits(&p, TRUE);+                        if (quant_max < quant_min) {+                        invalid_quant_count:+                            return re_parse_error(s, "invalid repetition count");+                        }+                    } else {+                        quant_max = INT32_MAX; /* infinity */+                    }+                }+                if (*p != '}' && !s->is_utf16) {+                    /* Annex B: normal atom if invalid '{' syntax */+                    p = p1;+                    break;+                }+                if (re_parse_expect(s, &p, '}'))+                    return -1;+            }+        quantifier:+            greedy = TRUE;+            if (*p == '?') {+                p++;+                greedy = FALSE;+            }+            if (last_atom_start < 0) {+                return re_parse_error(s, "nothing to repeat");+            }+            if (greedy) {+                int len, pos;+                +                if (quant_max > 0) {+                    /* specific optimization for simple quantifiers */+                    if (dbuf_error(&s->byte_code))+                        goto out_of_memory;+                    len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start,+                                                 s->byte_code.size - last_atom_start);+                    if (len > 0) {+                        re_emit_op(s, REOP_match);+                        +                        if (dbuf_insert(&s->byte_code, last_atom_start, 17))+                            goto out_of_memory;+                        pos = last_atom_start;+                        s->byte_code.buf[pos++] = REOP_simple_greedy_quant;+                        put_u32(&s->byte_code.buf[pos],+                                s->byte_code.size - last_atom_start - 17);+                        pos += 4;+                        put_u32(&s->byte_code.buf[pos], quant_min);+                        pos += 4;+                        put_u32(&s->byte_code.buf[pos], quant_max);+                        pos += 4;+                        put_u32(&s->byte_code.buf[pos], len);+                        pos += 4;+                        goto done;+                    }+                }+                +                if (dbuf_error(&s->byte_code))+                    goto out_of_memory;+                add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,+                                                           s->byte_code.size - last_atom_start) == 0);+            } else {+                add_zero_advance_check = FALSE;+            }+            +            {+                int len, pos;+                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 (dbuf_insert(&s->byte_code, last_atom_start, 3))+                            goto out_of_memory;+                        s->byte_code.buf[last_atom_start++] = REOP_save_reset;+                        s->byte_code.buf[last_atom_start++] = last_capture_count;+                        s->byte_code.buf[last_atom_start++] = s->capture_count - 1;+                    }+                    if (quant_max == 0) {+                        s->byte_code.size = last_atom_start;+                    } else if (quant_max == 1) {+                        if (dbuf_insert(&s->byte_code, last_atom_start, 5))+                            goto out_of_memory;+                        s->byte_code.buf[last_atom_start] = REOP_split_goto_first ++                            greedy;+                        put_u32(s->byte_code.buf + last_atom_start + 1, len);+                    } else if (quant_max == INT32_MAX) {+                        if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))+                            goto out_of_memory;+                        s->byte_code.buf[last_atom_start] = REOP_split_goto_first ++                            greedy;+                        put_u32(s->byte_code.buf + last_atom_start + 1,+                                len + 5 + add_zero_advance_check);+                        if (add_zero_advance_check) {+                            /* avoid infinite loop by stoping the+                               recursion if no advance was made in the+                               atom (only works if the atom has no+                               side effect) */+                            s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;+                            re_emit_goto(s, REOP_bne_char_pos, last_atom_start); +                        } else {+                            re_emit_goto(s, REOP_goto, last_atom_start);+                        }+                    } else {+                        if (dbuf_insert(&s->byte_code, last_atom_start, 10))+                            goto out_of_memory;+                        pos = last_atom_start;+                        s->byte_code.buf[pos++] = REOP_push_i32;+                        put_u32(s->byte_code.buf + pos, quant_max);+                        pos += 4;+                        s->byte_code.buf[pos++] = REOP_split_goto_first + greedy;+                        put_u32(s->byte_code.buf + pos, len + 5);+                        re_emit_goto(s, REOP_loop, last_atom_start + 5);+                        re_emit_op(s, REOP_drop);+                    }+                } else if (quant_min == 1 && quant_max == INT32_MAX &&+                           !add_zero_advance_check) {+                    re_emit_goto(s, REOP_split_next_first - greedy,+                                 last_atom_start);+                } else {+                    if (quant_min == 1) {+                        /* nothing to add */+                    } else {+                        if (dbuf_insert(&s->byte_code, last_atom_start, 5))+                            goto out_of_memory;+                        s->byte_code.buf[last_atom_start] = REOP_push_i32;+                        put_u32(s->byte_code.buf + last_atom_start + 1,+                                quant_min);+                        last_atom_start += 5;+                        re_emit_goto(s, REOP_loop, last_atom_start);+                        re_emit_op(s, REOP_drop);+                    }+                    if (quant_max == INT32_MAX) {+                        pos = s->byte_code.size;+                        re_emit_op_u32(s, REOP_split_goto_first + greedy,+                                       len + 5 + add_zero_advance_check);+                        if (add_zero_advance_check)+                            re_emit_op(s, REOP_push_char_pos);+                        /* copy the atom */+                        dbuf_put_self(&s->byte_code, last_atom_start, len);+                        if (add_zero_advance_check)+                            re_emit_goto(s, REOP_bne_char_pos, pos);+                        else+                            re_emit_goto(s, REOP_goto, pos);+                    } else if (quant_max > quant_min) {+                        re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min);+                        pos = s->byte_code.size;+                        re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5);+                        /* copy the atom */+                        dbuf_put_self(&s->byte_code, last_atom_start, len);+                        +                        re_emit_goto(s, REOP_loop, pos);+                        re_emit_op(s, REOP_drop);+                    }+                }+                last_atom_start = -1;+            }+            break;+        default:+            break;+        }+    }+ done:+    s->buf_ptr = p;+    return 0;+ out_of_memory:+    return re_parse_out_of_memory(s);+}++static int re_parse_alternative(REParseState *s, BOOL is_backward_dir)+{+    const uint8_t *p;+    int ret;+    size_t start, term_start, end, term_size;++    start = s->byte_code.size;+    for(;;) {+        p = s->buf_ptr;+        if (p >= s->buf_end)+            break;+        if (*p == '|' || *p == ')')+            break;+        term_start = s->byte_code.size;+        ret = re_parse_term(s, is_backward_dir);+        if (ret)+            return ret;+        if (is_backward_dir) {+            /* reverse the order of the terms (XXX: inefficient, but+               speed is not really critical here) */+            end = s->byte_code.size;+            term_size = end - term_start;+            if (dbuf_realloc(&s->byte_code, end + term_size))+                return -1;+            memmove(s->byte_code.buf + start + term_size,+                    s->byte_code.buf + start,+                    end - start);+            memcpy(s->byte_code.buf + start, s->byte_code.buf + end,+                   term_size);+        }+    }+    return 0;+}+    +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)+{+    int start, len, pos;++    start = s->byte_code.size;+    if (re_parse_alternative(s, is_backward_dir))+        return -1;+    while (*s->buf_ptr == '|') {+        s->buf_ptr++;++        len = s->byte_code.size - start;++        /* insert a split before the first alternative */+        if (dbuf_insert(&s->byte_code, start, 5)) {+            return re_parse_out_of_memory(s);+        }+        s->byte_code.buf[start] = REOP_split_next_first;+        put_u32(s->byte_code.buf + start + 1, len + 5);++        pos = re_emit_op_u32(s, REOP_goto, 0);++        if (re_parse_alternative(s, is_backward_dir))+            return -1;+        +        /* patch the goto */+        len = s->byte_code.size - (pos + 4);+        put_u32(s->byte_code.buf + pos, len);+    }+    return 0;+}++/* the control flow is recursive so the analysis can be linear */+static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)+{+    int stack_size, stack_size_max, pos, opcode, len;+    uint32_t val;+    +    stack_size = 0;+    stack_size_max = 0;+    bc_buf += RE_HEADER_LEN;+    bc_buf_len -= RE_HEADER_LEN;+    pos = 0;+    while (pos < bc_buf_len) {+        opcode = bc_buf[pos];+        len = reopcode_info[opcode].size;+        assert(opcode < REOP_COUNT);+        assert((pos + len) <= bc_buf_len);+        switch(opcode) {+        case REOP_push_i32:+        case REOP_push_char_pos:+            stack_size++;+            if (stack_size > stack_size_max) {+                if (stack_size > STACK_SIZE_MAX)+                    return -1;+                stack_size_max = stack_size;+            }+            break;+        case REOP_drop:+        case REOP_bne_char_pos:+            assert(stack_size > 0);+            stack_size--;+            break;+        case REOP_range:+            val = get_u16(bc_buf + pos + 1);+            len += val * 4;+            break;+        case REOP_range32:+            val = get_u16(bc_buf + pos + 1);+            len += val * 8;+            break;+        }+        pos += len;+    }+    return stack_size_max;+}++/* 'buf' must be a zero terminated UTF-8 string of length buf_len.+   Return NULL if error and allocate an error message in *perror_msg,+   otherwise the compiled bytecode and its length in plen.+*/+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)+{+    REParseState s_s, *s = &s_s;+    int stack_size;+    BOOL is_sticky;+    +    memset(s, 0, sizeof(*s));+    s->mem_opaque = opaque;+    s->buf_ptr = (const uint8_t *)buf;+    s->buf_end = s->buf_ptr + buf_len;+    s->buf_start = s->buf_ptr;+    s->re_flags = re_flags;+    s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0);+    is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0);+    s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0);+    s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0);+    s->capture_count = 1;+    s->total_capture_count = -1;+    s->has_named_captures = -1;+    +    dbuf_init2(&s->byte_code, opaque, lre_realloc);+    dbuf_init2(&s->group_names, opaque, lre_realloc);++    dbuf_putc(&s->byte_code, re_flags); /* first element is the flags */+    dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */+    dbuf_putc(&s->byte_code, 0); /* stack size */+    dbuf_put_u32(&s->byte_code, 0); /* bytecode length */+    +    if (!is_sticky) {+        /* iterate thru all positions (about the same as .*?( ... ) )+           .  We do it without an explicit loop so that lock step+           thread execution will be possible in an optimized+           implementation */+        re_emit_op_u32(s, REOP_split_goto_first, 1 + 5);+        re_emit_op(s, REOP_any);+        re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5));+    }+    re_emit_op_u8(s, REOP_save_start, 0);++    if (re_parse_disjunction(s, FALSE)) {+    error:+        dbuf_free(&s->byte_code);+        dbuf_free(&s->group_names);+        pstrcpy(error_msg, error_msg_size, s->u.error_msg);+        *plen = 0;+        return NULL;+    }++    re_emit_op_u8(s, REOP_save_end, 0);+    +    re_emit_op(s, REOP_match);++    if (*s->buf_ptr != '\0') {+        re_parse_error(s, "extraneous characters at the end");+        goto error;+    }++    if (dbuf_error(&s->byte_code)) {+        re_parse_out_of_memory(s);+        goto error;+    }+    +    stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size);+    if (stack_size < 0) {+        re_parse_error(s, "too many imbricated quantifiers");+        goto error;+    }+    +    s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;+    s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;+    put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN);++    /* add the named groups if needed */+    if (s->group_names.size > (s->capture_count - 1)) {+        dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size);+        s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS;+    }+    dbuf_free(&s->group_names);+    +#ifdef DUMP_REOP+    lre_dump_bytecode(s->byte_code.buf, s->byte_code.size);+#endif+    +    error_msg[0] = '\0';+    *plen = s->byte_code.size;+    return s->byte_code.buf;+}++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)                                     \+    do {                                                                \+        if (cbuf_type == 0) {                                           \+            c = *cptr++;                                                \+        } else {                                                        \+            uint32_t __c1;                                              \+            c = *(uint16_t *)cptr;                                      \+            cptr += 2;                                                  \+            if (c >= 0xd800 && c < 0xdc00 &&                            \+                cbuf_type == 2 && cptr < cbuf_end) {                    \+                __c1 = *(uint16_t *)cptr;                               \+                if (__c1 >= 0xdc00 && __c1 < 0xe000) {                  \+                    c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \+                    cptr += 2;                                          \+                }                                                       \+            }                                                           \+        }                                                               \+    } while (0)++#define PEEK_CHAR(c, cptr, cbuf_end)             \+    do {                                         \+        if (cbuf_type == 0) {                    \+            c = cptr[0];                         \+        } else {                                 \+            uint32_t __c1;                                              \+            c = ((uint16_t *)cptr)[0];                                  \+            if (c >= 0xd800 && c < 0xdc00 &&                            \+                cbuf_type == 2 && (cptr + 2) < cbuf_end) {              \+                __c1 = ((uint16_t *)cptr)[1];                           \+                if (__c1 >= 0xdc00 && __c1 < 0xe000) {                  \+                    c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \+                }                                                       \+            }                                                           \+        }                                        \+    } while (0)++#define PEEK_PREV_CHAR(c, cptr, cbuf_start)                 \+    do {                                         \+        if (cbuf_type == 0) {                    \+            c = cptr[-1];                        \+        } else {                                 \+            uint32_t __c1;                                              \+            c = ((uint16_t *)cptr)[-1];                                 \+            if (c >= 0xdc00 && c < 0xe000 &&                            \+                cbuf_type == 2 && (cptr - 4) >= cbuf_start) {              \+                __c1 = ((uint16_t *)cptr)[-2];                          \+                if (__c1 >= 0xd800 && __c1 < 0xdc00 ) {                 \+                    c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \+                }                                                       \+            }                                                           \+        }                                                               \+    } while (0)++#define GET_PREV_CHAR(c, cptr, cbuf_start)       \+    do {                                         \+        if (cbuf_type == 0) {                    \+            cptr--;                              \+            c = cptr[0];                         \+        } else {                                 \+            uint32_t __c1;                                              \+            cptr -= 2;                                                  \+            c = ((uint16_t *)cptr)[0];                                 \+            if (c >= 0xdc00 && c < 0xe000 &&                            \+                cbuf_type == 2 && cptr > cbuf_start) {                  \+                __c1 = ((uint16_t *)cptr)[-1];                          \+                if (__c1 >= 0xd800 && __c1 < 0xdc00 ) {                 \+                    cptr -= 2;                                          \+                    c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \+                }                                                       \+            }                                                           \+        }                                                               \+    } while (0)++#define PREV_CHAR(cptr, cbuf_start)       \+    do {                                  \+        if (cbuf_type == 0) {             \+            cptr--;                       \+        } else {                          \+            cptr -= 2;                          \+            if (cbuf_type == 2) {                                       \+                c = ((uint16_t *)cptr)[0];                              \+                if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) {   \+                    c = ((uint16_t *)cptr)[-1];                         \+                    if (c >= 0xd800 && c < 0xdc00)                      \+                        cptr -= 2;                                      \+                }                                                       \+            }                                                           \+        }                                                               \+    } while (0)++typedef uintptr_t StackInt;++typedef enum {+    RE_EXEC_STATE_SPLIT,+    RE_EXEC_STATE_LOOKAHEAD,+    RE_EXEC_STATE_NEGATIVE_LOOKAHEAD,+    RE_EXEC_STATE_GREEDY_QUANT,+} REExecStateEnum;++typedef struct REExecState {+    REExecStateEnum type : 8;+    uint8_t stack_len;+    size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */+    const uint8_t *cptr;+    const uint8_t *pc;+    void *buf[0];+} REExecState;++typedef struct {+    const uint8_t *cbuf;+    const uint8_t *cbuf_end;+    /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */+    int cbuf_type; +    int capture_count;+    int stack_size_max;+    BOOL multi_line;+    BOOL ignore_case;+    BOOL is_utf16;+    void *opaque; /* used for stack overflow check */++    size_t state_size;+    uint8_t *state_stack;+    size_t state_stack_size;+    size_t state_stack_len;+} REExecContext;++static int push_state(REExecContext *s,+                      uint8_t **capture,+                      StackInt *stack, size_t stack_len,+                      const uint8_t *pc, const uint8_t *cptr,+                      REExecStateEnum type, size_t count)+{+    REExecState *rs;+    uint8_t *new_stack;+    size_t new_size, i, n;+    StackInt *stack_buf;++    if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) {+        /* reallocate the stack */+        new_size = s->state_stack_size * 3 / 2;+        if (new_size < 8)+            new_size = 8;+        new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size);+        if (!new_stack)+            return -1;+        s->state_stack_size = new_size;+        s->state_stack = new_stack;+    }+    rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size);+    s->state_stack_len++;+    rs->type = type;+    rs->count = count;+    rs->stack_len = stack_len;+    rs->cptr = cptr;+    rs->pc = pc;+    n = 2 * s->capture_count;+    for(i = 0; i < n; i++)+        rs->buf[i] = capture[i];+    stack_buf = (StackInt *)(rs->buf + n);+    for(i = 0; i < stack_len; i++)+        stack_buf[i] = stack[i];+    return 0;+}++/* return 1 if match, 0 if not match or -1 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,+                                   BOOL no_recurse)+{+    int opcode, ret;+    int cbuf_type;+    uint32_t val, c;+    const uint8_t *cbuf_end;+    +    cbuf_type = s->cbuf_type;+    cbuf_end = s->cbuf_end;++    for(;;) {+        //        printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN)));+        opcode = *pc++;+        switch(opcode) {+        case REOP_match:+            {+                REExecState *rs;+                if (no_recurse)+                    return (intptr_t)cptr;+                ret = 1;+                goto recurse;+            no_match:+                if (no_recurse)+                    return 0;+                ret = 0;+            recurse:+                for(;;) {+                    if (s->state_stack_len == 0)+                        return ret;+                    rs = (REExecState *)(s->state_stack ++                                         (s->state_stack_len - 1) * s->state_size);+                    if (rs->type == RE_EXEC_STATE_SPLIT) {+                        if (!ret) {+                        pop_state:+                            memcpy(capture, rs->buf,+                                   sizeof(capture[0]) * 2 * s->capture_count);+                        pop_state1:+                            pc = rs->pc;+                            cptr = rs->cptr;+                            stack_len = rs->stack_len;+                            memcpy(stack, rs->buf + 2 * s->capture_count,+                                   stack_len * sizeof(stack[0]));+                            s->state_stack_len--;+                            break;+                        }+                    } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) {+                        if (!ret) {+                            uint32_t char_count, i;+                            memcpy(capture, rs->buf,+                                   sizeof(capture[0]) * 2 * s->capture_count);+                            stack_len = rs->stack_len;+                            memcpy(stack, rs->buf + 2 * s->capture_count,+                                   stack_len * sizeof(stack[0]));+                            pc = rs->pc;+                            cptr = rs->cptr;+                            /* go backward */+                            char_count = get_u32(pc + 12);+                            for(i = 0; i < char_count; i++) {+                                PREV_CHAR(cptr, s->cbuf);+                            }+                            pc = (pc + 16) + (int)get_u32(pc);+                            rs->cptr = cptr;+                            rs->count--;+                            if (rs->count == 0) {+                                s->state_stack_len--;+                            }+                            break;+                        }+                    } else {+                        ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) ||+                               (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret));+                        if (ret) {+                            /* keep the capture in case of positive lookahead */+                            if (rs->type == RE_EXEC_STATE_LOOKAHEAD)+                                goto pop_state1;+                            else+                                goto pop_state;+                        }+                    }+                    s->state_stack_len--;+                }+            }+            break;+        case REOP_char32:+            val = get_u32(pc);+            pc += 4;+            goto test_char;+        case REOP_char:+            val = get_u16(pc);+            pc += 2;+        test_char:+            if (cptr >= cbuf_end)+                goto no_match;+            GET_CHAR(c, cptr, cbuf_end);+            if (s->ignore_case) {+                c = lre_canonicalize(c, s->is_utf16);+            }+            if (val != c)+                goto no_match;+            break;+        case REOP_split_goto_first:+        case REOP_split_next_first:+            {+                const uint8_t *pc1;+                +                val = get_u32(pc);+                pc += 4;+                if (opcode == REOP_split_next_first) {+                    pc1 = pc + (int)val;+                } else {+                    pc1 = pc;+                    pc = pc + (int)val;+                }+                ret = push_state(s, capture, stack, stack_len,+                                 pc1, cptr, RE_EXEC_STATE_SPLIT, 0);+                if (ret < 0)+                    return -1;+                break;+            }+        case REOP_lookahead:+        case REOP_negative_lookahead:+            val = get_u32(pc);+            pc += 4;+            ret = push_state(s, capture, stack, stack_len,+                             pc + (int)val, cptr,+                             RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead,+                             0);+            if (ret < 0)+                return -1;+            break;+            +        case REOP_goto:+            val = get_u32(pc);+            pc += 4 + (int)val;+            break;+        case REOP_line_start:+            if (cptr == s->cbuf)+                break;+            if (!s->multi_line)+                goto no_match;+            PEEK_PREV_CHAR(c, cptr, s->cbuf);+            if (!is_line_terminator(c))+                goto no_match;+            break;+        case REOP_line_end:+            if (cptr == cbuf_end)+                break;+            if (!s->multi_line)+                goto no_match;+            PEEK_CHAR(c, cptr, cbuf_end);+            if (!is_line_terminator(c))+                goto no_match;+            break;+        case REOP_dot:+            if (cptr == cbuf_end)+                goto no_match;+            GET_CHAR(c, cptr, cbuf_end);+            if (is_line_terminator(c))+                goto no_match;+            break;+        case REOP_any:+            if (cptr == cbuf_end)+                goto no_match;+            GET_CHAR(c, cptr, cbuf_end);+            break;+        case REOP_save_start:+        case REOP_save_end:+            val = *pc++;+            assert(val < s->capture_count);+            capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr;+            break;+        case REOP_save_reset:+            {+                uint32_t val2;+                val = pc[0];+                val2 = pc[1];+                pc += 2;+                assert(val2 < s->capture_count);+                while (val <= val2) {+                    capture[2 * val] = NULL;+                    capture[2 * val + 1] = NULL;+                    val++;+                }+            }+            break;+        case REOP_push_i32:+            val = get_u32(pc);+            pc += 4;+            stack[stack_len++] = val;+            break;+        case REOP_drop:+            stack_len--;+            break;+        case REOP_loop:+            val = get_u32(pc);+            pc += 4;+            if (--stack[stack_len - 1] != 0) {+                pc += (int)val;+            }+            break;+        case REOP_push_char_pos:+            stack[stack_len++] = (uintptr_t)cptr;+            break;+        case REOP_bne_char_pos:+            val = get_u32(pc);+            pc += 4;+            if (stack[--stack_len] != (uintptr_t)cptr)+                pc += (int)val;+            break;+        case REOP_word_boundary:+        case REOP_not_word_boundary:+            {+                BOOL v1, v2;+                /* char before */+                if (cptr == s->cbuf) {+                    v1 = FALSE;+                } else {+                    PEEK_PREV_CHAR(c, cptr, s->cbuf);+                    v1 = is_word_char(c);+                }+                /* current char */+                if (cptr >= cbuf_end) {+                    v2 = FALSE;+                } else {+                    PEEK_CHAR(c, cptr, cbuf_end);+                    v2 = is_word_char(c);+                }+                if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode))+                    goto no_match;+            }+            break;+        case REOP_back_reference:+        case REOP_backward_back_reference:+            {+                const uint8_t *cptr1, *cptr1_end, *cptr1_start;+                uint32_t c1, c2;+                +                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) {+                    cptr1 = cptr1_start;+                    while (cptr1 < cptr1_end) {+                        if (cptr >= cbuf_end)+                            goto no_match;+                        GET_CHAR(c1, cptr1, cptr1_end);+                        GET_CHAR(c2, cptr, cbuf_end);+                        if (s->ignore_case) {+                            c1 = lre_canonicalize(c1, s->is_utf16);+                            c2 = lre_canonicalize(c2, s->is_utf16);+                        }+                        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);+                        GET_PREV_CHAR(c2, cptr, s->cbuf);+                        if (s->ignore_case) {+                            c1 = lre_canonicalize(c1, s->is_utf16);+                            c2 = lre_canonicalize(c2, s->is_utf16);+                        }+                        if (c1 != c2)+                            goto no_match;+                    }+                }+            }+            break;+        case REOP_range:+            {+                int n;+                uint32_t low, high, idx_min, idx_max, idx;+                +                n = get_u16(pc); /* n must be >= 1 */+                pc += 2;+                if (cptr >= cbuf_end)+                    goto no_match;+                GET_CHAR(c, cptr, cbuf_end);+                if (s->ignore_case) {+                    c = lre_canonicalize(c, s->is_utf16);+                }+                idx_min = 0;+                low = get_u16(pc + 0 * 4);+                if (c < low)+                    goto no_match;+                idx_max = n - 1;+                high = get_u16(pc + idx_max * 4 + 2);+                /* 0xffff in for last value means +infinity */+                if (unlikely(c >= 0xffff) && high == 0xffff)+                    goto range_match;+                if (c > high)+                    goto no_match;+                while (idx_min <= idx_max) {+                    idx = (idx_min + idx_max) / 2;+                    low = get_u16(pc + idx * 4);+                    high = get_u16(pc + idx * 4 + 2);+                    if (c < low)+                        idx_max = idx - 1;+                    else if (c > high)+                        idx_min = idx + 1;+                    else+                        goto range_match;+                }+                goto no_match;+            range_match:+                pc += 4 * n;+            }+            break;+        case REOP_range32:+            {+                int n;+                uint32_t low, high, idx_min, idx_max, idx;+                +                n = get_u16(pc); /* n must be >= 1 */+                pc += 2;+                if (cptr >= cbuf_end)+                    goto no_match;+                GET_CHAR(c, cptr, cbuf_end);+                if (s->ignore_case) {+                    c = lre_canonicalize(c, s->is_utf16);+                }+                idx_min = 0;+                low = get_u32(pc + 0 * 8);+                if (c < low)+                    goto no_match;+                idx_max = n - 1;+                high = get_u32(pc + idx_max * 8 + 4);+                if (c > high)+                    goto no_match;+                while (idx_min <= idx_max) {+                    idx = (idx_min + idx_max) / 2;+                    low = get_u32(pc + idx * 8);+                    high = get_u32(pc + idx * 8 + 4);+                    if (c < low)+                        idx_max = idx - 1;+                    else if (c > high)+                        idx_min = idx + 1;+                    else+                        goto range32_match;+                }+                goto no_match;+            range32_match:+                pc += 8 * n;+            }+            break;+        case REOP_prev:+            /* go to the previous char */+            if (cptr == s->cbuf)+                goto no_match;+            PREV_CHAR(cptr, s->cbuf);+            break;+        case REOP_simple_greedy_quant:+            {+                uint32_t next_pos, quant_min, quant_max;+                size_t q;+                intptr_t res;+                const uint8_t *pc1;+                +                next_pos = get_u32(pc);+                quant_min = get_u32(pc + 4);+                quant_max = get_u32(pc + 8);+                pc += 16;+                pc1 = pc;+                pc += (int)next_pos;+                +                q = 0;+                for(;;) {+                    res = lre_exec_backtrack(s, capture, stack, stack_len,+                                             pc1, cptr, TRUE);+                    if (res == -1)+                        return res;+                    if (!res)+                        break;+                    cptr = (uint8_t *)res;+                    q++;+                    if (q >= quant_max && quant_max != INT32_MAX)+                        break;+                }+                if (q < quant_min)+                    goto no_match;+                if (q > quant_min) {+                    /* will examine all matches down to quant_min */+                    ret = push_state(s, capture, stack, stack_len,+                                     pc1 - 16, cptr,+                                     RE_EXEC_STATE_GREEDY_QUANT,+                                     q - quant_min);+                    if (ret < 0)+                        return -1;+                }+            }+            break;+        default:+            abort();+        }+    }+}++/* Return 1 if match, 0 if not match or -1 if error. cindex is the+   starting position of the match and must be such as 0 <= cindex <=+   clen. */+int lre_exec(uint8_t **capture,+             const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,+             int cbuf_type, void *opaque)+{+    REExecContext s_s, *s = &s_s;+    int re_flags, i, alloca_size, ret;+    StackInt *stack_buf;+    +    re_flags = bc_buf[RE_HEADER_FLAGS];+    s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0;+    s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;+    s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0;+    s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT];+    s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE];+    s->cbuf = cbuf;+    s->cbuf_end = cbuf + (clen << cbuf_type);+    s->cbuf_type = cbuf_type;+    if (s->cbuf_type == 1 && s->is_utf16)+        s->cbuf_type = 2;+    s->opaque = opaque;++    s->state_size = sizeof(REExecState) ++        s->capture_count * sizeof(capture[0]) * 2 ++        s->stack_size_max * sizeof(stack_buf[0]);+    s->state_stack = NULL;+    s->state_stack_len = 0;+    s->state_stack_size = 0;+    +    for(i = 0; i < s->capture_count * 2; i++)+        capture[i] = NULL;+    alloca_size = s->stack_size_max * sizeof(stack_buf[0]);+    stack_buf = alloca(alloca_size);+    ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN,+                             cbuf + (cindex << cbuf_type), FALSE);+    lre_realloc(s->opaque, s->state_stack, 0);+    return ret;+}++int lre_get_capture_count(const uint8_t *bc_buf)+{+    return bc_buf[RE_HEADER_CAPTURE_COUNT];+}++int lre_get_flags(const uint8_t *bc_buf)+{+    return bc_buf[RE_HEADER_FLAGS];+}++#ifdef TEST++BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)+{+    return FALSE;+}++void *lre_realloc(void *opaque, void *ptr, size_t size)+{+    return realloc(ptr, size);+}++int main(int argc, char **argv)+{+    int len, ret, i;+    uint8_t *bc;+    char error_msg[64];+    uint8_t *capture[CAPTURE_COUNT_MAX * 2];+    const char *input;+    int input_len, capture_count;+    +    if (argc < 3) {+        printf("usage: %s regexp input\n", argv[0]);+        exit(1);+    }+    bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1],+                     strlen(argv[1]), 0, NULL);+    if (!bc) {+        fprintf(stderr, "error: %s\n", error_msg);+        exit(1);+    }++    input = argv[2];+    input_len = strlen(input);+    +    ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);+    printf("ret=%d\n", ret);+    if (ret == 1) {+        capture_count = lre_get_capture_count(bc);+        for(i = 0; i < 2 * capture_count; i++) {+            uint8_t *ptr;+            ptr = capture[i];+            printf("%d: ", i);+            if (!ptr)+                printf("<nil>");+            else+                printf("%u", (int)(ptr - (uint8_t *)input));+            printf("\n");+        }+    }+    return 0;+}+#endif
+ quickjs/libunicode.c view
@@ -0,0 +1,1538 @@+/*+ * Unicode utilities+ * + * Copyright (c) 2017-2018 Fabrice Bellard+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#include <stdlib.h>+#include <stdio.h>+#include <stdarg.h>+#include <string.h>+#include <assert.h>++#include "cutils.h"+#include "libunicode.h"+#include "libunicode-table.h"++enum {+    RUN_TYPE_U,+    RUN_TYPE_L,+    RUN_TYPE_UF,+    RUN_TYPE_LF,+    RUN_TYPE_UL,+    RUN_TYPE_LSU,+    RUN_TYPE_U2L_399_EXT2,+    RUN_TYPE_UF_D20,+    RUN_TYPE_UF_D1_EXT,+    RUN_TYPE_U_EXT,+    RUN_TYPE_LF_EXT,+    RUN_TYPE_U_EXT2,+    RUN_TYPE_L_EXT2,+    RUN_TYPE_U_EXT3,+};++/* conv_type:+   0 = to upper +   1 = to lower+   2 = case folding (= to lower with modifications) +*/+int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)+{+    if (c < 128) {+        if (conv_type) {+            if (c >= 'A' && c <= 'Z') {+                c = c - 'A' + 'a';+            }+        } else {+            if (c >= 'a' && c <= 'z') {+                c = c - 'a' + 'A';+            }+        }+    } else {+        uint32_t v, code, data, type, len, a, is_lower;+        int idx, idx_min, idx_max;+        +        is_lower = (conv_type != 0);+        idx_min = 0;+        idx_max = countof(case_conv_table1) - 1;+        while (idx_min <= idx_max) {+            idx = (unsigned)(idx_max + idx_min) / 2;+            v = case_conv_table1[idx];+            code = v >> (32 - 17);+            len = (v >> (32 - 17 - 7)) & 0x7f;+            if (c < code) {+                idx_max = idx - 1;+            } else if (c >= code + len) {+                idx_min = idx + 1;+            } else {+                type = (v >> (32 - 17 - 7 - 4)) & 0xf;+                data = ((v & 0xf) << 8) | case_conv_table2[idx];+                switch(type) {+                case RUN_TYPE_U:+                case RUN_TYPE_L:+                case RUN_TYPE_UF:+                case RUN_TYPE_LF:+                    if (conv_type == (type & 1) ||+                        (type >= RUN_TYPE_UF && conv_type == 2)) {+                        c = c - code + (case_conv_table1[data] >> (32 - 17));+                    }+                    break;+                case RUN_TYPE_UL:+                    a = c - code;+                    if ((a & 1) != (1 - is_lower))+                        break;+                    c = (a ^ 1) + code;+                    break;+                case RUN_TYPE_LSU:+                    a = c - code;+                    if (a == 1) {+                        c += 2 * is_lower - 1;+                    } else if (a == (1 - is_lower) * 2) {+                        c += (2 * is_lower - 1) * 2;+                    }+                    break;+                case RUN_TYPE_U2L_399_EXT2:+                    if (!is_lower) {+                        res[0] = c - code + case_conv_ext[data >> 6];+                        res[1] = 0x399;+                        return 2;+                    } else {+                        c = c - code + case_conv_ext[data & 0x3f];+                    }+                    break;+                case RUN_TYPE_UF_D20:+                    if (conv_type == 1)+                        break;+                    c = data + (conv_type == 2) * 0x20;+                    break;+                case RUN_TYPE_UF_D1_EXT:+                    if (conv_type == 1)+                        break;+                    c = case_conv_ext[data] + (conv_type == 2);+                    break;+                case RUN_TYPE_U_EXT:+                case RUN_TYPE_LF_EXT:+                    if (is_lower != (type - RUN_TYPE_U_EXT))+                        break;+                    c = case_conv_ext[data];+                    break;+                case RUN_TYPE_U_EXT2:+                case RUN_TYPE_L_EXT2:+                    if (conv_type != (type - RUN_TYPE_U_EXT2))+                        break;+                    res[0] = c - code + case_conv_ext[data >> 6];+                    res[1] = case_conv_ext[data & 0x3f];+                    return 2;+                default:+                case RUN_TYPE_U_EXT3:+                    if (conv_type != 0)+                        break;+                    res[0] = case_conv_ext[data >> 8];+                    res[1] = case_conv_ext[(data >> 4) & 0xf];+                    res[2] = case_conv_ext[data & 0xf];+                    return 3;+                }+                break;+            }+        }+    }+    res[0] = c;+    return 1;+}++static uint32_t get_le24(const uint8_t *ptr)+{+#if defined(__x86__) || defined(__x86_64__)+    return *(uint16_t *)ptr | (ptr[2] << 16);+#else+    return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);+#endif+}++#define UNICODE_INDEX_BLOCK_LEN 32++/* return -1 if not in table, otherwise the offset in the block */+static int get_index_pos(uint32_t *pcode, uint32_t c,+                         const uint8_t *index_table, int index_table_len)+{+    uint32_t code, v;+    int idx_min, idx_max, idx;++    idx_min = 0;+    v = get_le24(index_table);+    code = v & ((1 << 21) - 1);+    if (c < code) {+        *pcode = 0;+        return 0;+    }+    idx_max = index_table_len - 1;+    code = get_le24(index_table + idx_max * 3);+    if (c >= code)+        return -1;+    /* invariant: tab[idx_min] <= c < tab2[idx_max] */+    while ((idx_max - idx_min) > 1) {+        idx = (idx_max + idx_min) / 2;+        v = get_le24(index_table + idx * 3);+        code = v & ((1 << 21) - 1);+        if (c < code) {+            idx_max = idx;+        } else {+            idx_min = idx;+        }+    }+    v = get_le24(index_table + idx_min * 3);+    *pcode = v & ((1 << 21) - 1);+    return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21);+}++static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,+                            const uint8_t *index_table, int index_table_len)+{+    uint32_t code, b, bit;+    int pos;+    const uint8_t *p;+    +    pos = get_index_pos(&code, c, index_table, index_table_len);+    if (pos < 0)+        return FALSE; /* outside the table */+    p = table + pos;+    bit = 0;+    for(;;) {+        b = *p++;+        if (b < 64) {+            code += (b >> 3) + 1;+            if (c < code)+                return bit;+            bit ^= 1;+            code += (b & 7) + 1;+        } else if (b >= 0x80) {+            code += b - 0x80 + 1;+        } else if (b < 0x60) {+            code += (((b - 0x40) << 8) | p[0]) + 1;+            p++;+        } else {+            code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1;+            p += 2;+        }+        if (c < code)+            return bit;+        bit ^= 1;+    }+}++BOOL lre_is_cased(uint32_t c)+{+    uint32_t v, code, len;+    int idx, idx_min, idx_max;+        +    idx_min = 0;+    idx_max = countof(case_conv_table1) - 1;+    while (idx_min <= idx_max) {+        idx = (unsigned)(idx_max + idx_min) / 2;+        v = case_conv_table1[idx];+        code = v >> (32 - 17);+        len = (v >> (32 - 17 - 7)) & 0x7f;+        if (c < code) {+            idx_max = idx - 1;+        } else if (c >= code + len) {+            idx_min = idx + 1;+        } else {+            return TRUE;+        }+    }+    return lre_is_in_table(c, unicode_prop_Cased1_table,+                           unicode_prop_Cased1_index,+                           sizeof(unicode_prop_Cased1_index) / 3);+}++BOOL lre_is_case_ignorable(uint32_t c)+{+    return lre_is_in_table(c, unicode_prop_Case_Ignorable_table,+                           unicode_prop_Case_Ignorable_index,+                           sizeof(unicode_prop_Case_Ignorable_index) / 3);+}++/* character range */++static __maybe_unused void cr_dump(CharRange *cr)+{+    int i;+    for(i = 0; i < cr->len; i++)+        printf("%d: 0x%04x\n", i, cr->points[i]);+}++static void *cr_default_realloc(void *opaque, void *ptr, size_t size)+{+    return realloc(ptr, size);+}++void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func)+{+    cr->len = cr->size = 0;+    cr->points = NULL;+    cr->mem_opaque = mem_opaque;+    cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc;+}++void cr_free(CharRange *cr)+{+    cr->realloc_func(cr->mem_opaque, cr->points, 0);+}++int cr_realloc(CharRange *cr, int size)+{+    int new_size;+    uint32_t *new_buf;+    +    if (size > cr->size) {+        new_size = max_int(size, cr->size * 3 / 2);+        new_buf = cr->realloc_func(cr->mem_opaque, cr->points,+                                   new_size * sizeof(cr->points[0]));+        if (!new_buf)+            return -1;+        cr->points = new_buf;+        cr->size = new_size;+    }+    return 0;+}++int cr_copy(CharRange *cr, const CharRange *cr1)+{+    if (cr_realloc(cr, cr1->len))+        return -1;+    memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len);+    cr->len = cr1->len;+    return 0;+}++/* merge consecutive intervals and remove empty intervals */+static void cr_compress(CharRange *cr)+{+    int i, j, k, len;+    uint32_t *pt;+    +    pt = cr->points;+    len = cr->len;+    i = 0;+    j = 0;+    k = 0;+    while ((i + 1) < len) {+        if (pt[i] == pt[i + 1]) {+            /* empty interval */+            i += 2;+        } else {+            j = i;+            while ((j + 3) < len && pt[j + 1] == pt[j + 2])+                j += 2;+            /* just copy */+            pt[k] = pt[i];+            pt[k + 1] = pt[j + 1];+            k += 2;+            i = j + 2;+        }+    }+    cr->len = k;+}++/* union or intersection */+int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,+          const uint32_t *b_pt, int b_len, int op)+{+    int a_idx, b_idx, is_in;+    uint32_t v;+    +    a_idx = 0;+    b_idx = 0;+    for(;;) {+        /* get one more point from a or b in increasing order */+        if (a_idx < a_len && b_idx < b_len) {+            if (a_pt[a_idx] < b_pt[b_idx]) {+                goto a_add;+            } else if (a_pt[a_idx] == b_pt[b_idx]) {+                v = a_pt[a_idx];+                a_idx++;+                b_idx++;+            } else {+                goto b_add;+            }+        } else if (a_idx < a_len) {+        a_add:+            v = a_pt[a_idx++];+        } else if (b_idx < b_len) {+        b_add:+            v = b_pt[b_idx++];+        } else {+            break;+        }+        /* add the point if the in/out status changes */+        switch(op) {+        case CR_OP_UNION:+            is_in = (a_idx & 1) | (b_idx & 1);+            break;+        case CR_OP_INTER:+            is_in = (a_idx & 1) & (b_idx & 1);+            break;+        case CR_OP_XOR:+            is_in = (a_idx & 1) ^ (b_idx & 1);+            break;+        default:+            abort();+        }+        if (is_in != (cr->len & 1)) {+            if (cr_add_point(cr, v))+                return -1;+        }+    }+    cr_compress(cr);+    return 0;+}++int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len)+{+    CharRange a = *cr;+    int ret;+    cr->len = 0;+    cr->size = 0;+    cr->points = NULL;+    ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION);+    cr_free(&a);+    return ret;+}++int cr_invert(CharRange *cr)+{+    int len;+    len = cr->len;+    if (cr_realloc(cr, len + 2))+        return -1;+    memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0]));+    cr->points[0] = 0;+    cr->points[len + 1] = UINT32_MAX;+    cr->len = len + 2;+    cr_compress(cr);+    return 0;+}++#ifdef CONFIG_ALL_UNICODE++BOOL lre_is_id_start(uint32_t c)+{+    return lre_is_in_table(c, unicode_prop_ID_Start_table,+                           unicode_prop_ID_Start_index,+                           sizeof(unicode_prop_ID_Start_index) / 3);+}++BOOL lre_is_id_continue(uint32_t c)+{+    return lre_is_id_start(c) ||+        lre_is_in_table(c, unicode_prop_ID_Continue1_table,+                        unicode_prop_ID_Continue1_index,+                        sizeof(unicode_prop_ID_Continue1_index) / 3);+}++#define UNICODE_DECOMP_LEN_MAX 18++typedef enum {+    DECOMP_TYPE_C1, /* 16 bit char */+    DECOMP_TYPE_L1, /* 16 bit char table */+    DECOMP_TYPE_L2,+    DECOMP_TYPE_L3,+    DECOMP_TYPE_L4,+    DECOMP_TYPE_L5, /* XXX: not used */+    DECOMP_TYPE_L6, /* XXX: could remove */+    DECOMP_TYPE_L7, /* XXX: could remove */+    DECOMP_TYPE_LL1, /* 18 bit char table */+    DECOMP_TYPE_LL2,+    DECOMP_TYPE_S1, /* 8 bit char table */+    DECOMP_TYPE_S2,+    DECOMP_TYPE_S3,+    DECOMP_TYPE_S4,+    DECOMP_TYPE_S5,+    DECOMP_TYPE_I1, /* increment 16 bit char value */+    DECOMP_TYPE_I2_0,+    DECOMP_TYPE_I2_1,+    DECOMP_TYPE_I3_1,+    DECOMP_TYPE_I3_2,+    DECOMP_TYPE_I4_1,+    DECOMP_TYPE_I4_2,+    DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */+    DECOMP_TYPE_B2,+    DECOMP_TYPE_B3,+    DECOMP_TYPE_B4,+    DECOMP_TYPE_B5,+    DECOMP_TYPE_B6,+    DECOMP_TYPE_B7,+    DECOMP_TYPE_B8,+    DECOMP_TYPE_B18,+    DECOMP_TYPE_LS2,+    DECOMP_TYPE_PAT3,+    DECOMP_TYPE_S2_UL,+    DECOMP_TYPE_LS2_UL,+} DecompTypeEnum;++static uint32_t unicode_get_short_code(uint32_t c)+{+    static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 };++    if (c < 0x80)+        return c;+    else if (c < 0x80 + 0x50)+        return c - 0x80 + 0x300;+    else+        return unicode_short_table[c - 0x80 - 0x50];+}++static uint32_t unicode_get_lower_simple(uint32_t c)+{+    if (c < 0x100 || (c >= 0x410 && c <= 0x42f))+        c += 0x20;+    else+        c++;+    return c;+}++static uint16_t unicode_get16(const uint8_t *p)+{+    return p[0] | (p[1] << 8);+}++static int unicode_decomp_entry(uint32_t *res, uint32_t c,+                                int idx, uint32_t code, uint32_t len,+                                uint32_t type)+{+    uint32_t c1;+    int l, i, p;+    const uint8_t *d;++    if (type == DECOMP_TYPE_C1) {+        res[0] = unicode_decomp_table2[idx];+        return 1;+    } else {+        d = unicode_decomp_data + unicode_decomp_table2[idx];+        switch(type) {+        case DECOMP_TYPE_L1 ... DECOMP_TYPE_L7:+            l = type - DECOMP_TYPE_L1 + 1;+            d += (c - code) * l * 2;+            for(i = 0; i < l; i++) {+                if ((res[i] = unicode_get16(d + 2 * i)) == 0)+                    return 0;+            }+            return l;+        case DECOMP_TYPE_LL1 ... DECOMP_TYPE_LL2:+            {+                uint32_t k, p;+                l = type - DECOMP_TYPE_LL1 + 1;+                k = (c - code) * l;+                p = len * l * 2;+                for(i = 0; i < l; i++) {+                    c1 = unicode_get16(d + 2 * k) |+                        (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16);+                    if (!c1)+                        return 0;+                    res[i] = c1;+                    k++;+                }+            }+            return l;+        case DECOMP_TYPE_S1 ... DECOMP_TYPE_S5:+            l = type - DECOMP_TYPE_S1 + 1;+            d += (c - code) * l;+            for(i = 0; i < l; i++) {+                if ((res[i] = unicode_get_short_code(d[i])) == 0)+                    return 0;+            }+            return l;+        case DECOMP_TYPE_I1:+            l = 1;+            p = 0;+            goto decomp_type_i;+        case DECOMP_TYPE_I2_0:+        case DECOMP_TYPE_I2_1:+        case DECOMP_TYPE_I3_1:+        case DECOMP_TYPE_I3_2:+        case DECOMP_TYPE_I4_1:+        case DECOMP_TYPE_I4_2:+            l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1);+            p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2);+        decomp_type_i:+            for(i = 0; i < l; i++) {+                c1 = unicode_get16(d + 2 * i);+                if (i == p)+                    c1 += c - code;+                res[i] = c1;+            }+            return l;+        case DECOMP_TYPE_B18:+            l = 18;+            goto decomp_type_b;+        case DECOMP_TYPE_B1 ... DECOMP_TYPE_B8:+            l = type - DECOMP_TYPE_B1 + 1;+        decomp_type_b:+            {+                uint32_t c_min;+                c_min = unicode_get16(d);+                d += 2 + (c - code) * l;+                for(i = 0; i < l; i++) {+                    c1 = d[i];+                    if (c1 == 0xff)+                        c1 = 0x20;+                    else+                        c1 += c_min;+                    res[i] = c1;+                }+            }+            return l;+        case DECOMP_TYPE_LS2:+            d += (c - code) * 3;+            if (!(res[0] = unicode_get16(d)))+                return 0;+            res[1] = unicode_get_short_code(d[2]);+            return 2;+        case DECOMP_TYPE_PAT3:+            res[0] = unicode_get16(d);+            res[2] = unicode_get16(d + 2);+            d += 4 + (c - code) * 2;+            res[1] = unicode_get16(d);+            return 3;+        case DECOMP_TYPE_S2_UL:+        case DECOMP_TYPE_LS2_UL:+            c1 = c - code;+            if (type == DECOMP_TYPE_S2_UL) {+                d += c1 & ~1;+                c = unicode_get_short_code(*d);+                d++;+            } else {+                d += (c1 >> 1) * 3;+                c = unicode_get16(d);+                d += 2;+            }+            if (c1 & 1)+                c = unicode_get_lower_simple(c);+            res[0] = c;+            res[1] = unicode_get_short_code(*d);+            return 2;+        }+    }+    return 0;+}+++/* return the length of the decomposition (length <=+   UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */+static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1)+{+    uint32_t v, type, is_compat, code, len;+    int idx_min, idx_max, idx;+    +    idx_min = 0;+    idx_max = countof(unicode_decomp_table1) - 1;+    while (idx_min <= idx_max) {+        idx = (idx_max + idx_min) / 2;+        v = unicode_decomp_table1[idx];+        code = v >> (32 - 18);+        len = (v >> (32 - 18 - 7)) & 0x7f;+        //        printf("idx=%d code=%05x len=%d\n", idx, code, len);+        if (c < code) {+            idx_max = idx - 1;+        } else if (c >= code + len) {+            idx_min = idx + 1;+        } else {+            is_compat = v & 1;+            if (is_compat1 < is_compat)+                break;+            type = (v >> (32 - 18 - 7 - 6)) & 0x3f;+            return unicode_decomp_entry(res, c, idx, code, len, type);+        }+    }+    return 0;+}++/* return 0 if no pair found */+static int unicode_compose_pair(uint32_t c0, uint32_t c1)+{+    uint32_t code, len, type, v, idx1, d_idx, d_offset, ch;+    int idx_min, idx_max, idx, d;+    uint32_t pair[2];+    +    idx_min = 0;+    idx_max = countof(unicode_comp_table) - 1;+    while (idx_min <= idx_max) {+        idx = (idx_max + idx_min) / 2;+        idx1 = unicode_comp_table[idx];++        /* idx1 represent an entry of the decomposition table */+        d_idx = idx1 >> 6;+        d_offset = idx1 & 0x3f;+        v = unicode_decomp_table1[d_idx];+        code = v >> (32 - 18);+        len = (v >> (32 - 18 - 7)) & 0x7f;+        type = (v >> (32 - 18 - 7 - 6)) & 0x3f;+        ch = code + d_offset;+        unicode_decomp_entry(pair, ch, d_idx, code, len, type);+        d = c0 - pair[0];+        if (d == 0)+            d = c1 - pair[1];+        if (d < 0) {+            idx_max = idx - 1;+        } else if (d > 0) {+            idx_min = idx + 1;+        } else {+            return ch;+        }+    }+    return 0;+}++/* return the combining class of character c (between 0 and 255) */+static int unicode_get_cc(uint32_t c)+{+    uint32_t code, n, type, cc, c1, b;+    int pos;+    const uint8_t *p;+    +    pos = get_index_pos(&code, c,+                        unicode_cc_index, sizeof(unicode_cc_index) / 3);+    if (pos < 0)+        return 0;+    p = unicode_cc_table + pos;+    for(;;) {+        b = *p++;+        type = b >> 6;+        n = b & 0x3f;+        if (n < 48) {+        } else if (n < 56) {+            n = (n - 48) << 8;+            n |= *p++;+            n += 48;+        } else {+            n = (n - 56) << 8;+            n |= *p++ << 8;+            n |= *p++;+            n += 48 + (1 << 11);+        }+        if (type <= 1)+            p++;+        c1 = code + n + 1;+        if (c < c1) {+            switch(type) {+            case 0:+                cc = p[-1];+                break;+            case 1:+                cc = p[-1] + c - code;+                break;+            case 2:+                cc = 0;+                break;+            default:+            case 3:+                cc = 230;+                break;+            }+            return cc;+        }+        code = c1;+    }+}++static void sort_cc(int *buf, int len)+{+    int i, j, k, cc, cc1, start, ch1;+    +    for(i = 0; i < len; i++) {+        cc = unicode_get_cc(buf[i]);+        if (cc != 0) {+            start = i;+            j = i + 1;+            while (j < len) {+                ch1 = buf[j];+                cc1 = unicode_get_cc(ch1);+                if (cc1 == 0)+                    break;+                k = j - 1;+                while (k >= start) {+                    if (unicode_get_cc(buf[k]) <= cc1)+                        break;+                    buf[k + 1] = buf[k];+                    k--;+                }+                buf[k + 1] = ch1;+                j++;+            }+#if 0+            printf("cc:");+            for(k = start; k < j; k++) {+                printf(" %3d", unicode_get_cc(buf[k]));+            }+            printf("\n");+#endif+            i = j;+        }+    }+}++static void to_nfd_rec(DynBuf *dbuf,+                       const int *src, int src_len, int is_compat)+{+    uint32_t c, v;+    int i, l;+    uint32_t res[UNICODE_DECOMP_LEN_MAX];+    +    for(i = 0; i < src_len; i++) {+        c = src[i];+        if (c >= 0xac00 && c < 0xd7a4) {+            /* Hangul decomposition */+            c -= 0xac00;+            dbuf_put_u32(dbuf, 0x1100 + c / 588);+            dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28);+            v = c % 28;+            if (v != 0)+                dbuf_put_u32(dbuf, 0x11a7 + v);+        } else {+            l = unicode_decomp_char(res, c, is_compat);+            if (l) {+                to_nfd_rec(dbuf, (int *)res, l, is_compat);+            } else {+                dbuf_put_u32(dbuf, c);+            }+        }+    }+}++/* return 0 if not found */+static int compose_pair(uint32_t c0, uint32_t c1)+{+    /* Hangul composition */+    if (c0 >= 0x1100 && c0 < 0x1100 + 19 &&+        c1 >= 0x1161 && c1 < 0x1161 + 21) {+        return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28;+    } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 &&+               (c0 - 0xac00) % 28 == 0 &&+               c1 >= 0x11a7 && c1 < 0x11a7 + 28) {+        return c0 + c1 - 0x11a7;+    } else {+        return unicode_compose_pair(c0, c1);+    }+}++int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,+                      UnicodeNormalizationEnum n_type,+                      void *opaque, DynBufReallocFunc *realloc_func)+{+    int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len;+    BOOL is_compat;+    DynBuf dbuf_s, *dbuf = &dbuf_s;+    +    is_compat = n_type >> 1;++    dbuf_init2(dbuf, opaque, realloc_func);+    if (dbuf_realloc(dbuf, sizeof(int) * src_len))+        goto fail;++    /* common case: latin1 is unaffected by NFC */+    if (n_type == UNICODE_NFC) {+        for(i = 0; i < src_len; i++) {+            if (src[i] >= 0x100)+                goto not_latin1;+        }+        buf = (int *)dbuf->buf;+        memcpy(buf, src, src_len * sizeof(int));+        *pdst = (uint32_t *)buf;+        return src_len;+    not_latin1: ;+    }++    to_nfd_rec(dbuf, (const int *)src, src_len, is_compat);+    if (dbuf_error(dbuf)) {+    fail:+        *pdst = NULL;+        return -1;+    }+    buf = (int *)dbuf->buf;+    buf_len = dbuf->size / sizeof(int);+        +    sort_cc(buf, buf_len);+    +    if (buf_len <= 1 || (n_type & 1) != 0) {+        /* NFD / NFKD */+        *pdst = (uint32_t *)buf;+        return buf_len;+    }+    +    i = 1;+    out_len = 1;+    while (i < buf_len) {+        /* find the starter character and test if it is blocked from+           the character at 'i' */+        last_cc = unicode_get_cc(buf[i]);+        starter_pos = out_len - 1;+        while (starter_pos >= 0) {+            cc = unicode_get_cc(buf[starter_pos]);+            if (cc == 0)+                break;+            if (cc >= last_cc)+                goto next;+            last_cc = 256;+            starter_pos--;+        }+        if (starter_pos >= 0 &&+            (p = compose_pair(buf[starter_pos], buf[i])) != 0) {+            buf[starter_pos] = p;+            i++;+        } else {+        next:+            buf[out_len++] = buf[i++];+        }+    }+    *pdst = (uint32_t *)buf;+    return out_len;+}++/* char ranges for various unicode properties */++static int unicode_find_name(const char *name_table, const char *name)+{+    const char *p, *r;+    int pos;+    size_t name_len, len;+    +    p = name_table;+    pos = 0;+    name_len = strlen(name);+    while (*p) {+        for(;;) {+            r = strchr(p, ',');+            if (!r)+                len = strlen(p);+            else+                len = r - p;+            if (len == name_len && !memcmp(p, name, name_len))+                return pos;+            p += len + 1;+            if (!r)+                break;+        }+        pos++;+    }+    return -1;+}++/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2+   if not found */+int unicode_script(CharRange *cr,+                   const char *script_name, BOOL is_ext)+{+    int script_idx;+    const uint8_t *p, *p_end;+    uint32_t c, c1, b, n, v, v_len, i, type;+    CharRange cr1_s, *cr1;+    CharRange cr2_s, *cr2 = &cr2_s;+    BOOL is_common;+    +    script_idx = unicode_find_name(unicode_script_name_table, script_name);+    if (script_idx < 0)+        return -2;+    /* Note: we remove the "Unknown" Script */+    script_idx += UNICODE_SCRIPT_Unknown + 1;+        +    is_common = (script_idx == UNICODE_SCRIPT_Common ||+                 script_idx == UNICODE_SCRIPT_Inherited);+    if (is_ext) {+        cr1 = &cr1_s;+        cr_init(cr1, cr->mem_opaque, cr->realloc_func);+        cr_init(cr2, cr->mem_opaque, cr->realloc_func);+    } else {+        cr1 = cr;+    }++    p = unicode_script_table;+    p_end = unicode_script_table + countof(unicode_script_table);+    c = 0;+    while (p < p_end) {+        b = *p++;+        type = b >> 7;+        n = b & 0x7f;+        if (n < 96) {+        } else if (n < 112) {+            n = (n - 96) << 8;+            n |= *p++;+            n += 96;+        } else {+            n = (n - 112) << 16;+            n |= *p++ << 8;+            n |= *p++;+            n += 96 + (1 << 12);+        }+        if (type == 0)+            v = 0;+        else+            v = *p++;+        c1 = c + n + 1;+        if (v == script_idx) {+            if (cr_add_interval(cr1, c, c1))+                goto fail;+        }+        c = c1;+    }++    if (is_ext) {+        /* add the script extensions */+        p = unicode_script_ext_table;+        p_end = unicode_script_ext_table + countof(unicode_script_ext_table);+        c = 0;+        while (p < p_end) {+            b = *p++;+            if (b < 128) {+                n = b;+            } else if (b < 128 + 64) {+                n = (b - 128) << 8;+                n |= *p++;+                n += 128;+            } else {+                n = (b - 128 - 64) << 16;+                n |= *p++ << 8;+                n |= *p++;+                n += 128 + (1 << 14);+            }+            c1 = c + n + 1;+            v_len = *p++;+            if (is_common) {+                if (v_len != 0) {+                    if (cr_add_interval(cr2, c, c1))+                        goto fail;+                }+            } else {+                for(i = 0; i < v_len; i++) {+                    if (p[i] == script_idx) {+                        if (cr_add_interval(cr2, c, c1))+                            goto fail;+                        break;+                    }+                }+            }+            p += v_len;+            c = c1;+        }+        if (is_common) {+            /* remove all the characters with script extensions */+            if (cr_invert(cr2))+                goto fail;+            if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len,+                      CR_OP_INTER))+                goto fail;+        } else {+            if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len,+                      CR_OP_UNION))+                goto fail;+        }+        cr_free(cr1);+        cr_free(cr2);+    }+    return 0;+ fail:+    if (is_ext) {+        cr_free(cr1);+        cr_free(cr2);+    }+    goto fail;+}++#define M(id) (1U << UNICODE_GC_ ## id)++static int unicode_general_category1(CharRange *cr, uint32_t gc_mask)+{+    const uint8_t *p, *p_end;+    uint32_t c, c0, b, n, v;++    p = unicode_gc_table;+    p_end = unicode_gc_table + countof(unicode_gc_table);+    c = 0;+    while (p < p_end) {+        b = *p++;+        n = b >> 5;+        v = b & 0x1f;+        if (n == 7) {+            n = *p++;+            if (n < 128) {+                n += 7;+            } else if (n < 128 + 64) {+                n = (n - 128) << 8;+                n |= *p++;+                n += 7 + 128;+            } else {+                n = (n - 128 - 64) << 16;+                n |= *p++ << 8;+                n |= *p++;+                n += 7 + 128 + (1 << 14);+            }+        }+        c0 = c;+        c += n + 1;+        if (v == 31) {+            /* run of Lu / Ll */+            b = gc_mask & (M(Lu) | M(Ll));+            if (b != 0) {+                if (b == (M(Lu) | M(Ll))) {+                    goto add_range;+                } else {+                    c0 += ((gc_mask & M(Ll)) != 0);+                    for(; c0 < c; c0 += 2) {+                        if (cr_add_interval(cr, c0, c0 + 1))+                            return -1;+                    }+                }+            }+        } else if ((gc_mask >> v) & 1) {+        add_range:+            if (cr_add_interval(cr, c0, c))+                return -1;+        }+    }+    return 0;+}++static int unicode_prop1(CharRange *cr, int prop_idx)+{+    const uint8_t *p, *p_end;+    uint32_t c, c0, b, bit;++    p = unicode_prop_table[prop_idx];+    p_end = p + unicode_prop_len_table[prop_idx];+    c = 0;+    bit = 0;+    while (p < p_end) {+        c0 = c;+        b = *p++;+        if (b < 64) {+            c += (b >> 3) + 1;+            if (bit)  {+                if (cr_add_interval(cr, c0, c))+                    return -1;+            }+            bit ^= 1;+            c0 = c;+            c += (b & 7) + 1;+        } else if (b >= 0x80) {+            c += b - 0x80 + 1;+        } else if (b < 0x60) {+            c += (((b - 0x40) << 8) | p[0]) + 1;+            p++;+        } else {+            c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1;+            p += 2;+        }+        if (bit)  {+            if (cr_add_interval(cr, c0, c))+                return -1;+        }+        bit ^= 1;+    }+    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(U_EXT2) | MR(U_EXT3),++        MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2),++        MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT),+    };+#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;+}+        +typedef enum {+    POP_GC,+    POP_PROP,+    POP_CASE,+    POP_UNION,+    POP_INTER,+    POP_XOR,+    POP_INVERT,+    POP_END,+} PropOPEnum;++#define POP_STACK_LEN_MAX 4++static int unicode_prop_ops(CharRange *cr, ...)+{+    va_list ap;+    CharRange stack[POP_STACK_LEN_MAX];+    int stack_len, op, ret, i;+    uint32_t a;+    +    va_start(ap, cr);+    stack_len = 0;+    for(;;) {+        op = va_arg(ap, int);+        switch(op) {+        case POP_GC:+            assert(stack_len < POP_STACK_LEN_MAX);+            a = va_arg(ap, int);+            cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);+            if (unicode_general_category1(&stack[stack_len - 1], a))+                goto fail;+            break;+        case POP_PROP:+            assert(stack_len < POP_STACK_LEN_MAX);+            a = va_arg(ap, int);+            cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);+            if (unicode_prop1(&stack[stack_len - 1], a))+                goto fail;+            break;+        case POP_CASE:+            assert(stack_len < POP_STACK_LEN_MAX);+            a = va_arg(ap, int);+            cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);+            if (unicode_case1(&stack[stack_len - 1], a))+                goto fail;+            break;+        case POP_UNION:+        case POP_INTER:+        case POP_XOR:+            {+                CharRange *cr1, *cr2, *cr3;+                assert(stack_len >= 2);+                assert(stack_len < POP_STACK_LEN_MAX);+                cr1 = &stack[stack_len - 2];+                cr2 = &stack[stack_len - 1];+                cr3 = &stack[stack_len++];+                cr_init(cr3, cr->mem_opaque, cr->realloc_func);+                if (cr_op(cr3, cr1->points, cr1->len,+                          cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION))+                    goto fail;+                cr_free(cr1);+                cr_free(cr2);+                *cr1 = *cr3;+                stack_len -= 2;+            }+            break;+        case POP_INVERT:+            assert(stack_len >= 1);+            if (cr_invert(&stack[stack_len - 1]))+                goto fail;+            break;+        case POP_END:+            goto done;+        default:+            abort();+        }+    }+ done:+    assert(stack_len == 1);+    ret = cr_copy(cr, &stack[0]);+    cr_free(&stack[0]);+    return ret;+ fail:+    for(i = 0; i < stack_len; i++)+        cr_free(&stack[i]);+    return -1;+}++static const uint32_t unicode_gc_mask_table[] = {+    M(Lu) | M(Ll) | M(Lt), /* LC */+    M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */+    M(Mn) | M(Mc) | M(Me), /* M */+    M(Nd) | M(Nl) | M(No), /* N */+    M(Sm) | M(Sc) | M(Sk) | M(So), /* S */+    M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */+    M(Zs) | M(Zl) | M(Zp), /* Z */+    M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */+};++/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2+   if not found */+int unicode_general_category(CharRange *cr, const char *gc_name)+{+    int gc_idx;+    uint32_t gc_mask;+    +    gc_idx = unicode_find_name(unicode_gc_name_table, gc_name);+    if (gc_idx < 0)+        return -2;+    if (gc_idx <= UNICODE_GC_Co) {+        gc_mask = (uint64_t)1 << gc_idx;+    } else {+        gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC];+    }+    return unicode_general_category1(cr, gc_mask);+}+++/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2+   if not found */+int unicode_prop(CharRange *cr, const char *prop_name)+{+    int prop_idx, ret;+    +    prop_idx = unicode_find_name(unicode_prop_name_table, prop_name);+    if (prop_idx < 0)+        return -2;+    prop_idx += UNICODE_PROP_ASCII_Hex_Digit;++    ret = 0;+    switch(prop_idx) {+    case UNICODE_PROP_ASCII:+        if (cr_add_interval(cr, 0x00, 0x7f + 1))+            return -1;+        break;+    case UNICODE_PROP_Any:+        if (cr_add_interval(cr, 0x00000, 0x10ffff + 1))+            return -1;+        break;+    case UNICODE_PROP_Assigned:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Cn),+                               POP_INVERT,+                               POP_END);+        break;+    case UNICODE_PROP_Math:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Sm),+                               POP_PROP, UNICODE_PROP_Other_Math,+                               POP_UNION,+                               POP_END);+        break;+    case UNICODE_PROP_Lowercase:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Ll),+                               POP_PROP, UNICODE_PROP_Other_Lowercase,+                               POP_UNION,+                               POP_END);+        break;+    case UNICODE_PROP_Uppercase:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu),+                               POP_PROP, UNICODE_PROP_Other_Uppercase,+                               POP_UNION,+                               POP_END);+        break;+    case UNICODE_PROP_Cased:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu) | M(Ll) | M(Lt),+                               POP_PROP, UNICODE_PROP_Other_Uppercase,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Other_Lowercase,+                               POP_UNION,+                               POP_END);+        break;+    case UNICODE_PROP_Alphabetic:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),+                               POP_PROP, UNICODE_PROP_Other_Uppercase,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Other_Lowercase,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Other_Alphabetic,+                               POP_UNION,+                               POP_END);+        break;+    case UNICODE_PROP_Grapheme_Base:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn),+                               POP_PROP, UNICODE_PROP_Other_Grapheme_Extend,+                               POP_UNION,+                               POP_INVERT,+                               POP_END);+        break;+    case UNICODE_PROP_Grapheme_Extend:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Me) | M(Mn),+                               POP_PROP, UNICODE_PROP_Other_Grapheme_Extend,+                               POP_UNION,+                               POP_END);+        break;+    case UNICODE_PROP_XID_Start:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),+                               POP_PROP, UNICODE_PROP_Other_ID_Start,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Pattern_Syntax,+                               POP_PROP, UNICODE_PROP_Pattern_White_Space,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_XID_Start1,+                               POP_UNION,+                               POP_INVERT,+                               POP_INTER,+                               POP_END);+        break;+    case UNICODE_PROP_XID_Continue:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) |+                               M(Mn) | M(Mc) | M(Nd) | M(Pc),+                               POP_PROP, UNICODE_PROP_Other_ID_Start,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Other_ID_Continue,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Pattern_Syntax,+                               POP_PROP, UNICODE_PROP_Pattern_White_Space,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_XID_Continue1,+                               POP_UNION,+                               POP_INVERT,+                               POP_INTER,+                               POP_END);+        break;+    case UNICODE_PROP_Changes_When_Uppercased:+        ret = unicode_case1(cr, CASE_U);+        break;+    case UNICODE_PROP_Changes_When_Lowercased:+        ret = unicode_case1(cr, CASE_L);+        break;+    case UNICODE_PROP_Changes_When_Casemapped:+        ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F);+        break;+    case UNICODE_PROP_Changes_When_Titlecased:+        ret = unicode_prop_ops(cr,+                               POP_CASE, CASE_U,+                               POP_PROP, UNICODE_PROP_Changes_When_Titlecased1,+                               POP_XOR,+                               POP_END);+        break;+    case UNICODE_PROP_Changes_When_Casefolded:+        ret = unicode_prop_ops(cr,+                               POP_CASE, CASE_F,+                               POP_PROP, UNICODE_PROP_Changes_When_Casefolded1,+                               POP_XOR,+                               POP_END);+        break;+    case UNICODE_PROP_Changes_When_NFKC_Casefolded:+        ret = unicode_prop_ops(cr,+                               POP_CASE, CASE_F,+                               POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1,+                               POP_XOR,+                               POP_END);+        break;+#if 0+    case UNICODE_PROP_ID_Start:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),+                               POP_PROP, UNICODE_PROP_Other_ID_Start,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Pattern_Syntax,+                               POP_PROP, UNICODE_PROP_Pattern_White_Space,+                               POP_UNION,+                               POP_INVERT,+                               POP_INTER,+                               POP_END);+        break;+    case UNICODE_PROP_ID_Continue:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) |+                               M(Mn) | M(Mc) | M(Nd) | M(Pc),+                               POP_PROP, UNICODE_PROP_Other_ID_Start,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Other_ID_Continue,+                               POP_UNION,+                               POP_PROP, UNICODE_PROP_Pattern_Syntax,+                               POP_PROP, UNICODE_PROP_Pattern_White_Space,+                               POP_UNION,+                               POP_INVERT,+                               POP_INTER,+                               POP_END);+        break;+    case UNICODE_PROP_Case_Ignorable:+        ret = unicode_prop_ops(cr,+                               POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk),+                               POP_PROP, UNICODE_PROP_Case_Ignorable1,+                               POP_XOR,+                               POP_END);+        break;+#else+        /* we use the existing tables */+    case UNICODE_PROP_ID_Continue:+        ret = unicode_prop_ops(cr,+                               POP_PROP, UNICODE_PROP_ID_Start,+                               POP_PROP, UNICODE_PROP_ID_Continue1,+                               POP_XOR,+                               POP_END);+        break;+#endif+    default:+        if (prop_idx >= countof(unicode_prop_table))+            return -2;+        ret = unicode_prop1(cr, prop_idx);+        break;+    }+    return ret;+}++#endif /* CONFIG_ALL_UNICODE */
+ quickjs/quickjs-libc.c view
@@ -0,0 +1,3773 @@+/*+ * QuickJS C library+ * + * Copyright (c) 2017-2020 Fabrice Bellard+ * Copyright (c) 2017-2020 Charlie Gordon+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#include <stdlib.h>+#include <stdio.h>+#include <stdarg.h>+#include <inttypes.h>+#include <string.h>+#include <assert.h>+#include <unistd.h>+#include <errno.h>+#include <fcntl.h>+#include <sys/time.h>+#include <time.h>+#include <signal.h>+#include <limits.h>+#include <sys/stat.h>+#include <dirent.h>+#if defined(_WIN32)+#include <windows.h>+#include <conio.h>+#include <utime.h>+#else+#include <dlfcn.h>+#include <termios.h>+#include <sys/ioctl.h>+#include <sys/wait.h>++#if defined(__APPLE__)+typedef sig_t sighandler_t;+#if !defined(environ)+#include <crt_externs.h>+#define environ (*_NSGetEnviron())+#endif+#endif /* __APPLE__ */++#endif++#if !defined(_WIN32)+/* enable the os.Worker API. IT relies on POSIX threads */+#define USE_WORKER+#endif++#ifdef USE_WORKER+#include <pthread.h>+#include <stdatomic.h>+#endif++#include "cutils.h"+#include "list.h"+#include "quickjs-libc.h"++/* TODO:+   - add socket calls+*/++typedef struct {+    struct list_head link;+    int fd;+    JSValue rw_func[2];+} JSOSRWHandler;++typedef struct {+    struct list_head link;+    int sig_num;+    JSValue func;+} JSOSSignalHandler;++typedef struct {+    struct list_head link;+    BOOL has_object;+    int64_t timeout;+    JSValue func;+} JSOSTimer;++typedef struct {+    struct list_head link;+    uint8_t *data;+    size_t data_len;+    /* list of SharedArrayBuffers, necessary to free the message */+    uint8_t **sab_tab;+    size_t sab_tab_len;+} JSWorkerMessage;++typedef struct {+    int ref_count;+#ifdef USE_WORKER+    pthread_mutex_t mutex;+#endif+    struct list_head msg_queue; /* list of JSWorkerMessage.link */+    int read_fd;+    int write_fd;+} JSWorkerMessagePipe;++typedef struct {+    struct list_head link;+    JSWorkerMessagePipe *recv_pipe;+    JSValue on_message_func;+} JSWorkerMessageHandler;++typedef struct JSThreadState {+    struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */+    struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */+    struct list_head os_timers; /* list of JSOSTimer.link */+    struct list_head port_list; /* list of JSWorkerMessageHandler.link */+    int eval_script_recurse; /* only used in the main thread */+    /* not used in the main thread */+    JSWorkerMessagePipe *recv_pipe, *send_pipe;+} JSThreadState;++static uint64_t os_pending_signals;+static int (*os_poll_func)(JSContext *ctx);++static void js_std_dbuf_init(JSContext *ctx, DynBuf *s)+{+    dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt);+}++static BOOL my_isdigit(int c)+{+    return (c >= '0' && c <= '9');+}++static JSValue js_printf_internal(JSContext *ctx,+                                  int argc, JSValueConst *argv, FILE *fp)+{+    char fmtbuf[32];+    uint8_t cbuf[UTF8_CHAR_LEN_MAX+1];+    JSValue res;+    DynBuf dbuf;+    const char *fmt_str;+    const uint8_t *fmt, *fmt_end;+    const uint8_t *p;+    char *q;+    int i, c, len, mod;+    size_t fmt_len;+    int32_t int32_arg;+    int64_t int64_arg;+    double double_arg;+    const char *string_arg;+    /* Use indirect call to dbuf_printf to prevent gcc warning */+    int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf;++    js_std_dbuf_init(ctx, &dbuf);++    if (argc > 0) {+        fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]);+        if (!fmt_str)+            goto fail;++        i = 1;+        fmt = (const uint8_t *)fmt_str;+        fmt_end = fmt + fmt_len;+        while (fmt < fmt_end) {+            for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++)+                continue;+            dbuf_put(&dbuf, p, fmt - p);+            if (fmt >= fmt_end)+                break;+            q = fmtbuf;+            *q++ = *fmt++;  /* copy '%' */+            +            /* flags */+            for(;;) {+                c = *fmt;+                if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' ||+                    c == '\'') {+                    if (q >= fmtbuf + sizeof(fmtbuf) - 1)+                        goto invalid;+                    *q++ = c;+                    fmt++;+                } else {+                    break;+                }+            }+            /* width */+            if (*fmt == '*') {+                if (i >= argc)+                    goto missing;+                if (JS_ToInt32(ctx, &int32_arg, argv[i++]))+                    goto fail;+                q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);+                fmt++;+            } else {+                while (my_isdigit(*fmt)) {+                    if (q >= fmtbuf + sizeof(fmtbuf) - 1)+                        goto invalid;+                    *q++ = *fmt++;+                }+            }+            if (*fmt == '.') {+                if (q >= fmtbuf + sizeof(fmtbuf) - 1)+                    goto invalid;+                *q++ = *fmt++;+                if (*fmt == '*') {+                    if (i >= argc)+                        goto missing;+                    if (JS_ToInt32(ctx, &int32_arg, argv[i++]))+                        goto fail;+                    q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);+                    fmt++;+                } else {+                    while (my_isdigit(*fmt)) {+                        if (q >= fmtbuf + sizeof(fmtbuf) - 1)+                            goto invalid;+                        *q++ = *fmt++;+                    }+                }+            }++            /* we only support the "l" modifier for 64 bit numbers */+            mod = ' ';+            if (*fmt == 'l') {+                mod = *fmt++;+            }+            +            /* type */+            c = *fmt++;+            if (q >= fmtbuf + sizeof(fmtbuf) - 1)+                goto invalid;+            *q++ = c;+            *q = '\0';+            +            switch (c) {+            case 'c':+                if (i >= argc)+                    goto missing;+                if (JS_IsString(argv[i])) {+                    string_arg = JS_ToCString(ctx, argv[i++]);+                    if (!string_arg)+                        goto fail;+                    int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);+                    JS_FreeCString(ctx, string_arg);+                } else {+                    if (JS_ToInt32(ctx, &int32_arg, argv[i++]))+                        goto fail;+                }+                /* handle utf-8 encoding explicitly */+                if ((unsigned)int32_arg > 0x10FFFF)+                    int32_arg = 0xFFFD;+                /* ignore conversion flags, width and precision */+                len = unicode_to_utf8(cbuf, int32_arg);+                dbuf_put(&dbuf, cbuf, len);+                break;+                +            case 'd':+            case 'i':+            case 'o':+            case 'u':+            case 'x':+            case 'X':+                if (i >= argc)+                    goto missing;+                if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++]))+                    goto fail;+                if (mod == 'l') {+                    /* 64 bit number */+#if defined(_WIN32)+                    if (q >= fmtbuf + sizeof(fmtbuf) - 3)+                        goto invalid;+                    q[2] = q[-1];+                    q[-1] = 'I';+                    q[0] = '6';+                    q[1] = '4';+                    q[3] = '\0';+                    dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg);+#else+                    if (q >= fmtbuf + sizeof(fmtbuf) - 2)+                        goto invalid;+                    q[1] = q[-1];+                    q[-1] = q[0] = 'l';+                    q[2] = '\0';+                    dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg);+#endif+                } else {+                    dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg);+                }+                break;++            case 's':+                if (i >= argc)+                    goto missing;+                /* XXX: handle strings containing null characters */+                string_arg = JS_ToCString(ctx, argv[i++]);+                if (!string_arg)+                    goto fail;+                dbuf_printf_fun(&dbuf, fmtbuf, string_arg);+                JS_FreeCString(ctx, string_arg);+                break;+                +            case 'e':+            case 'f':+            case 'g':+            case 'a':+            case 'E':+            case 'F':+            case 'G':+            case 'A':+                if (i >= argc)+                    goto missing;+                if (JS_ToFloat64(ctx, &double_arg, argv[i++]))+                    goto fail;+                dbuf_printf_fun(&dbuf, fmtbuf, double_arg);+                break;+                +            case '%':+                dbuf_putc(&dbuf, '%');+                break;+                +            default:+                /* XXX: should support an extension mechanism */+            invalid:+                JS_ThrowTypeError(ctx, "invalid conversion specifier in format string");+                goto fail;+            missing:+                JS_ThrowReferenceError(ctx, "missing argument for conversion specifier");+                goto fail;+            }+        }+        JS_FreeCString(ctx, fmt_str);+    }+    if (dbuf.error) {+        res = JS_ThrowOutOfMemory(ctx);+    } else {+        if (fp) {+            len = fwrite(dbuf.buf, 1, dbuf.size, fp);+            res = JS_NewInt32(ctx, len);+        } else {+            res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size);+        }+    }+    dbuf_free(&dbuf);+    return res;++fail:+    dbuf_free(&dbuf);+    return JS_EXCEPTION;+}++uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename)+{+    FILE *f;+    uint8_t *buf;+    size_t buf_len;+    long lret;+    +    f = fopen(filename, "rb");+    if (!f)+        return NULL;+    if (fseek(f, 0, SEEK_END) < 0)+        goto fail;+    lret = ftell(f);+    if (lret < 0)+        goto fail;+    /* XXX: on Linux, ftell() return LONG_MAX for directories */+    if (lret == LONG_MAX) {+        errno = EISDIR;+        goto fail;+    }+    buf_len = lret;+    if (fseek(f, 0, SEEK_SET) < 0)+        goto fail;+    if (ctx)+        buf = js_malloc(ctx, buf_len + 1);+    else+        buf = malloc(buf_len + 1);+    if (!buf)+        goto fail;+    if (fread(buf, 1, buf_len, f) != buf_len) {+        errno = EIO;+        if (ctx)+            js_free(ctx, buf);+        else+            free(buf);+    fail:+        fclose(f);+        return NULL;+    }+    buf[buf_len] = '\0';+    fclose(f);+    *pbuf_len = buf_len;+    return buf;+}++/* load and evaluate a file */+static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    uint8_t *buf;+    const char *filename;+    JSValue ret;+    size_t buf_len;+    +    filename = JS_ToCString(ctx, argv[0]);+    if (!filename)+        return JS_EXCEPTION;+    buf = js_load_file(ctx, &buf_len, filename);+    if (!buf) {+        JS_ThrowReferenceError(ctx, "could not load '%s'", filename);+        JS_FreeCString(ctx, filename);+        return JS_EXCEPTION;+    }+    ret = JS_Eval(ctx, (char *)buf, buf_len, filename,+                  JS_EVAL_TYPE_GLOBAL);+    js_free(ctx, buf);+    JS_FreeCString(ctx, filename);+    return ret;+}++/* load a file as a UTF-8 encoded string */+static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    uint8_t *buf;+    const char *filename;+    JSValue ret;+    size_t buf_len;+    +    filename = JS_ToCString(ctx, argv[0]);+    if (!filename)+        return JS_EXCEPTION;+    buf = js_load_file(ctx, &buf_len, filename);+    JS_FreeCString(ctx, filename);+    if (!buf)+        return JS_NULL;+    ret = JS_NewStringLen(ctx, (char *)buf, buf_len);+    js_free(ctx, buf);+    return ret;+}++typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx,+                                        const char *module_name);+++#if defined(_WIN32)+static JSModuleDef *js_module_loader_so(JSContext *ctx,+                                        const char *module_name)+{+    JS_ThrowReferenceError(ctx, "shared library modules are not supported yet");+    return NULL;+}+#else+static JSModuleDef *js_module_loader_so(JSContext *ctx,+                                        const char *module_name)+{+    JSModuleDef *m;+    void *hd;+    JSInitModuleFunc *init;+    char *filename;+    +    if (!strchr(module_name, '/')) {+        /* must add a '/' so that the DLL is not searched in the+           system library paths */+        filename = js_malloc(ctx, strlen(module_name) + 2 + 1);+        if (!filename)+            return NULL;+        strcpy(filename, "./");+        strcpy(filename + 2, module_name);+    } else {+        filename = (char *)module_name;+    }+    +    /* C module */+    hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL);+    if (filename != module_name)+        js_free(ctx, filename);+    if (!hd) {+        JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library",+                               module_name);+        goto fail;+    }++    init = dlsym(hd, "js_init_module");+    if (!init) {+        JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found",+                               module_name);+        goto fail;+    }++    m = init(ctx, module_name);+    if (!m) {+        JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error",+                               module_name);+    fail:+        if (hd)+            dlclose(hd);+        return NULL;+    }+    return m;+}+#endif /* !_WIN32 */++int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,+                              JS_BOOL use_realpath, JS_BOOL is_main)+{+    JSModuleDef *m;+    char buf[PATH_MAX + 16];+    JSValue meta_obj;+    JSAtom module_name_atom;+    const char *module_name;+    +    assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE);+    m = JS_VALUE_GET_PTR(func_val);++    module_name_atom = JS_GetModuleName(ctx, m);+    module_name = JS_AtomToCString(ctx, module_name_atom);+    JS_FreeAtom(ctx, module_name_atom);+    if (!module_name)+        return -1;+    if (!strchr(module_name, ':')) {+        strcpy(buf, "file://");+#if !defined(_WIN32)+        /* realpath() cannot be used with modules compiled with qjsc+           because the corresponding module source code is not+           necessarily present */+        if (use_realpath) {+            char *res = realpath(module_name, buf + strlen(buf));+            if (!res) {+                JS_ThrowTypeError(ctx, "realpath failure");+                JS_FreeCString(ctx, module_name);+                return -1;+            }+        } else+#endif+        {+            pstrcat(buf, sizeof(buf), module_name);+        }+    } else {+        pstrcpy(buf, sizeof(buf), module_name);+    }+    JS_FreeCString(ctx, module_name);+    +    meta_obj = JS_GetImportMeta(ctx, m);+    if (JS_IsException(meta_obj))+        return -1;+    JS_DefinePropertyValueStr(ctx, meta_obj, "url",+                              JS_NewString(ctx, buf),+                              JS_PROP_C_W_E);+    JS_DefinePropertyValueStr(ctx, meta_obj, "main",+                              JS_NewBool(ctx, is_main),+                              JS_PROP_C_W_E);+    JS_FreeValue(ctx, meta_obj);+    return 0;+}++JSModuleDef *js_module_loader(JSContext *ctx,+                              const char *module_name, void *opaque)+{+    JSModuleDef *m;++    if (has_suffix(module_name, ".so")) {+        m = js_module_loader_so(ctx, module_name);+    } else {+        size_t buf_len;+        uint8_t *buf;+        JSValue func_val;+    +        buf = js_load_file(ctx, &buf_len, module_name);+        if (!buf) {+            JS_ThrowReferenceError(ctx, "could not load module filename '%s'",+                                   module_name);+            return NULL;+        }+        +        /* compile the module */+        func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,+                           JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);+        js_free(ctx, buf);+        if (JS_IsException(func_val))+            return NULL;+        /* XXX: could propagate the exception */+        js_module_set_import_meta(ctx, func_val, TRUE, FALSE);+        /* the module is already referenced, so we must free it */+        m = JS_VALUE_GET_PTR(func_val);+        JS_FreeValue(ctx, func_val);+    }+    return m;+}++static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    int status;+    if (JS_ToInt32(ctx, &status, argv[0]))+        status = -1;+    exit(status);+    return JS_UNDEFINED;+}++static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    const char *name, *str;+    name = JS_ToCString(ctx, argv[0]);+    if (!name)+        return JS_EXCEPTION;+    str = getenv(name);+    JS_FreeCString(ctx, name);+    if (!str)+        return JS_UNDEFINED;+    else+        return JS_NewString(ctx, str);+}++static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val,+                         int argc, JSValueConst *argv)+{+    JS_RunGC(JS_GetRuntime(ctx));+    return JS_UNDEFINED;+}++static int interrupt_handler(JSRuntime *rt, void *opaque)+{+    return (os_pending_signals >> SIGINT) & 1;+}++static int get_bool_option(JSContext *ctx, BOOL *pbool,+                           JSValueConst obj,+                           const char *option)+{+    JSValue val;+    val = JS_GetPropertyStr(ctx, obj, option);+    if (JS_IsException(val))+        return -1;+    if (!JS_IsUndefined(val)) {+        *pbool = JS_ToBool(ctx, val);+    }+    JS_FreeValue(ctx, val);+    return 0;+}++static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    const char *str;+    size_t len;+    JSValue ret;+    JSValueConst options_obj;+    BOOL backtrace_barrier = FALSE;+    int flags;+    +    if (argc >= 2) {+        options_obj = argv[1];+        if (get_bool_option(ctx, &backtrace_barrier, options_obj,+                            "backtrace_barrier"))+            return JS_EXCEPTION;+    }++    str = JS_ToCStringLen(ctx, &len, argv[0]);+    if (!str)+        return JS_EXCEPTION;+    if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) {+        /* install the interrupt handler */+        JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);+    }+    flags = JS_EVAL_TYPE_GLOBAL; +    if (backtrace_barrier)+        flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;+    ret = JS_Eval(ctx, str, len, "<evalScript>", flags);+    JS_FreeCString(ctx, str);+    if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {+        /* remove the interrupt handler */+        JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL);+        os_pending_signals &= ~((uint64_t)1 << SIGINT);+        /* convert the uncatchable "interrupted" error into a normal error+           so that it can be caught by the REPL */+        if (JS_IsException(ret))+            JS_ResetUncatchableError(ctx);+    }+    return ret;+}++static JSClassID js_std_file_class_id;++typedef struct {+    FILE *f;+    BOOL close_in_finalizer;+    BOOL is_popen;+} JSSTDFile;++static void js_std_file_finalizer(JSRuntime *rt, JSValue val)+{+    JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id);+    if (s) {+        if (s->f && s->close_in_finalizer) {+            if (s->is_popen)+                pclose(s->f);+            else+                fclose(s->f);+        }+        js_free_rt(rt, s);+    }+}++static ssize_t js_get_errno(ssize_t ret)+{+    if (ret == -1)+        ret = -errno;+    return ret;+}++static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    int err;+    if (JS_ToInt32(ctx, &err, argv[0]))+        return JS_EXCEPTION;+    return JS_NewString(ctx, strerror(err));+}++static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSValue obj;+    const char *str;+    size_t len;++    str = JS_ToCStringLen(ctx, &len, argv[0]);+    if (!str)+        return JS_EXCEPTION;+    obj = JS_ParseJSON2(ctx, str, len, "<input>", JS_PARSE_JSON_EXT);+    JS_FreeCString(ctx, str);+    return obj;+}++static JSValue js_new_std_file(JSContext *ctx, FILE *f,+                               BOOL close_in_finalizer,+                               BOOL is_popen)+{+    JSSTDFile *s;+    JSValue obj;+    obj = JS_NewObjectClass(ctx, js_std_file_class_id);+    if (JS_IsException(obj))+        return obj;+    s = js_mallocz(ctx, sizeof(*s));+    if (!s) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    s->close_in_finalizer = close_in_finalizer;+    s->is_popen = is_popen;+    s->f = f;+    JS_SetOpaque(obj, s);+    return obj;+}++static void js_set_error_object(JSContext *ctx, JSValue obj, int err)+{+    if (!JS_IsUndefined(obj)) {+        JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err));+    }+}++static JSValue js_std_open(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    const char *filename, *mode = NULL;+    FILE *f;+    int err;+    +    filename = JS_ToCString(ctx, argv[0]);+    if (!filename)+        goto fail;+    mode = JS_ToCString(ctx, argv[1]);+    if (!mode)+        goto fail;+    if (mode[strspn(mode, "rwa+b")] != '\0') {+        JS_ThrowTypeError(ctx, "invalid file mode");+        goto fail;+    }++    f = fopen(filename, mode);+    if (!f)+        err = errno;+    else+        err = 0;+    if (argc >= 3)+        js_set_error_object(ctx, argv[2], err);+    JS_FreeCString(ctx, filename);+    JS_FreeCString(ctx, mode);+    if (!f)+        return JS_NULL;+    return js_new_std_file(ctx, f, TRUE, FALSE);+ fail:+    JS_FreeCString(ctx, filename);+    JS_FreeCString(ctx, mode);+    return JS_EXCEPTION;+}++static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    const char *filename, *mode = NULL;+    FILE *f;+    int err;+    +    filename = JS_ToCString(ctx, argv[0]);+    if (!filename)+        goto fail;+    mode = JS_ToCString(ctx, argv[1]);+    if (!mode)+        goto fail;+    if (mode[strspn(mode, "rw")] != '\0') {+        JS_ThrowTypeError(ctx, "invalid file mode");+        goto fail;+    }++    f = popen(filename, mode);+    if (!f)+        err = errno;+    else+        err = 0;+    if (argc >= 3)+        js_set_error_object(ctx, argv[2], err);+    JS_FreeCString(ctx, filename);+    JS_FreeCString(ctx, mode);+    if (!f)+        return JS_NULL;+    return js_new_std_file(ctx, f, TRUE, TRUE);+ fail:+    JS_FreeCString(ctx, filename);+    JS_FreeCString(ctx, mode);+    return JS_EXCEPTION;+}++static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    const char *mode;+    FILE *f;+    int fd, err;++    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    mode = JS_ToCString(ctx, argv[1]);+    if (!mode)+        goto fail;+    if (mode[strspn(mode, "rwa+")] != '\0') {+        JS_ThrowTypeError(ctx, "invalid file mode");+        goto fail;+    }++    f = fdopen(fd, mode);+    if (!f)+        err = errno;+    else+        err = 0;+    if (argc >= 3)+        js_set_error_object(ctx, argv[2], err);+    JS_FreeCString(ctx, mode);+    if (!f)+        return JS_NULL;+    return js_new_std_file(ctx, f, TRUE, FALSE);+ fail:+    JS_FreeCString(ctx, mode);+    return JS_EXCEPTION;+}++static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    FILE *f;+    f = tmpfile();+    if (argc >= 1)+        js_set_error_object(ctx, argv[0], f ? 0 : errno);+    if (!f)+        return JS_NULL;+    return js_new_std_file(ctx, f, TRUE, FALSE);+}++static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    return js_printf_internal(ctx, argc, argv, NULL);+}++static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    return js_printf_internal(ctx, argc, argv, stdout);+}++static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj)+{+    JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id);+    if (!s)+        return NULL;+    if (!s->f) {+        JS_ThrowTypeError(ctx, "invalid file handle");+        return NULL;+    }+    return s->f;+}++static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv, int magic)+{+    FILE *f;+    int i;+    const char *str;+    size_t len;++    if (magic == 0) {+        f = stdout;+    } else {+        f = js_std_file_get(ctx, this_val);+        if (!f)+            return JS_EXCEPTION;+    }+    +    for(i = 0; i < argc; i++) {+        str = JS_ToCStringLen(ctx, &len, argv[i]);+        if (!str)+            return JS_EXCEPTION;+        fwrite(str, 1, len, f);+        JS_FreeCString(ctx, str);+    }+    return JS_UNDEFINED;+}++static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id);+    int err;+    if (!s)+        return JS_EXCEPTION;+    if (!s->f)+        return JS_ThrowTypeError(ctx, "invalid file handle");+    if (s->is_popen)+        err = js_get_errno(pclose(s->f));+    else+        err = js_get_errno(fclose(s->f));+    s->f = NULL;+    return JS_NewInt32(ctx, err);+}++static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    return js_printf_internal(ctx, argc, argv, f);+}++static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    fflush(f);+    return JS_UNDEFINED;+}++static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv, int is_bigint)+{+    FILE *f = js_std_file_get(ctx, this_val);+    int64_t pos;+    if (!f)+        return JS_EXCEPTION;+#if defined(__linux__)+    pos = ftello(f);+#else+    pos = ftell(f);+#endif+    if (is_bigint)+        return JS_NewBigInt64(ctx, pos);+    else+        return JS_NewInt64(ctx, pos);+}++static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    int64_t pos;+    int whence, ret;+    if (!f)+        return JS_EXCEPTION;+    if (JS_ToInt64Ext(ctx, &pos, argv[0]))+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &whence, argv[1]))+        return JS_EXCEPTION;+#if defined(__linux__)+    ret = fseeko(f, pos, whence);+#else+    ret = fseek(f, pos, whence);+#endif+    if (ret < 0)+        ret = -errno;+    return JS_NewInt32(ctx, ret);+}++static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    return JS_NewBool(ctx, feof(f));+}++static JSValue js_std_file_error(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    return JS_NewBool(ctx, ferror(f));+}++static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    clearerr(f);+    return JS_UNDEFINED;+}++static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    return JS_NewInt32(ctx, fileno(f));+}++static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv, int magic)+{+    FILE *f = js_std_file_get(ctx, this_val);+    uint64_t pos, len;+    size_t size, ret;+    uint8_t *buf;+    +    if (!f)+        return JS_EXCEPTION;+    if (JS_ToIndex(ctx, &pos, argv[1]))+        return JS_EXCEPTION;+    if (JS_ToIndex(ctx, &len, argv[2]))+        return JS_EXCEPTION;+    buf = JS_GetArrayBuffer(ctx, &size, argv[0]);+    if (!buf)+        return JS_EXCEPTION;+    if (pos + len > size)+        return JS_ThrowRangeError(ctx, "read/write array buffer overflow");+    if (magic)+        ret = fwrite(buf + pos, 1, len, f);+    else+        ret = fread(buf + pos, 1, len, f);+    return JS_NewInt64(ctx, ret);+}++/* XXX: could use less memory and go faster */+static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    int c;+    DynBuf dbuf;+    JSValue obj;+    +    if (!f)+        return JS_EXCEPTION;++    js_std_dbuf_init(ctx, &dbuf);+    for(;;) {+        c = fgetc(f);+        if (c == EOF) {+            if (dbuf.size == 0) {+                /* EOF */+                dbuf_free(&dbuf);+                return JS_NULL;+            } else {+                break;+            }+        }+        if (c == '\n')+            break;+        if (dbuf_putc(&dbuf, c)) {+            dbuf_free(&dbuf);+            return JS_ThrowOutOfMemory(ctx);+        }+    }+    obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size);+    dbuf_free(&dbuf);+    return obj;+}++/* XXX: could use less memory and go faster */+static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    int c;+    DynBuf dbuf;+    JSValue obj;+    uint64_t max_size64;+    size_t max_size;+    JSValueConst max_size_val;+    +    if (!f)+        return JS_EXCEPTION;++    if (argc >= 1)+        max_size_val = argv[0];+    else+        max_size_val = JS_UNDEFINED;+    max_size = (size_t)-1;+    if (!JS_IsUndefined(max_size_val)) {+        if (JS_ToIndex(ctx, &max_size64, max_size_val))+            return JS_EXCEPTION;+        if (max_size64 < max_size)+            max_size = max_size64;+    }++    js_std_dbuf_init(ctx, &dbuf);+    while (max_size != 0) {+        c = fgetc(f);+        if (c == EOF)+            break;+        if (dbuf_putc(&dbuf, c)) {+            dbuf_free(&dbuf);+            return JS_EXCEPTION;+        }+        max_size--;+    }+    obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size);+    dbuf_free(&dbuf);+    return obj;+}++static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    if (!f)+        return JS_EXCEPTION;+    return JS_NewInt32(ctx, fgetc(f));+}++static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    FILE *f = js_std_file_get(ctx, this_val);+    int c;+    if (!f)+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &c, argv[0]))+        return JS_EXCEPTION;+    c = fputc(c, f);+    return JS_NewInt32(ctx, c);+}++/* urlGet */++#define URL_GET_PROGRAM "curl -s -i"+#define URL_GET_BUF_SIZE 4096++static int http_get_header_line(FILE *f, char *buf, size_t buf_size,+                                DynBuf *dbuf)+{+    int c;+    char *p;+    +    p = buf;+    for(;;) {+        c = fgetc(f);+        if (c < 0)+            return -1;+        if ((p - buf) < buf_size - 1)+            *p++ = c;+        if (dbuf)+            dbuf_putc(dbuf, c);+        if (c == '\n')+            break;+    }+    *p = '\0';+    return 0;+}++static int http_get_status(const char *buf)+{+    const char *p = buf;+    while (*p != ' ' && *p != '\0')+        p++;+    if (*p != ' ')+        return 0;+    while (*p == ' ')+        p++;+    return atoi(p);+}++static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    const char *url;+    DynBuf cmd_buf;+    DynBuf data_buf_s, *data_buf = &data_buf_s;+    DynBuf header_buf_s, *header_buf = &header_buf_s;+    char *buf; +    size_t i, len;+    int c, status;+    JSValue response = JS_UNDEFINED, ret_obj;+    JSValueConst options_obj;+    FILE *f;+    BOOL binary_flag, full_flag;+    +    url = JS_ToCString(ctx, argv[0]);+    if (!url)+        return JS_EXCEPTION;+    +    binary_flag = FALSE;+    full_flag = FALSE;+    +    if (argc >= 2) {+        options_obj = argv[1];++        if (get_bool_option(ctx, &binary_flag, options_obj, "binary"))+            goto fail_obj;++        if (get_bool_option(ctx, &full_flag, options_obj, "full")) {+        fail_obj:+            JS_FreeCString(ctx, url);+            return JS_EXCEPTION;+        }+    }+    +    js_std_dbuf_init(ctx, &cmd_buf);+    dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM);+    len = strlen(url);+    for(i = 0; i < len; i++) {+        c = url[i];+        if (c == '\'' || c == '\\')+            dbuf_putc(&cmd_buf, '\\');+        dbuf_putc(&cmd_buf, c);+    }+    JS_FreeCString(ctx, url);+    dbuf_putstr(&cmd_buf, "''");+    dbuf_putc(&cmd_buf, '\0');+    if (dbuf_error(&cmd_buf)) {+        dbuf_free(&cmd_buf);+        return JS_EXCEPTION;+    }+    //    printf("%s\n", (char *)cmd_buf.buf);+    f = popen((char *)cmd_buf.buf, "r");+    dbuf_free(&cmd_buf);+    if (!f) {+        return JS_ThrowTypeError(ctx, "could not start curl");+    }++    js_std_dbuf_init(ctx, data_buf);+    js_std_dbuf_init(ctx, header_buf);+    +    buf = js_malloc(ctx, URL_GET_BUF_SIZE);+    if (!buf)+        goto fail;++    /* get the HTTP status */+    if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) {+        status = 0;+        goto bad_header;+    }+    status = http_get_status(buf);+    if (!full_flag && !(status >= 200 && status <= 299)) {+        goto bad_header;+    }+    +    /* wait until there is an empty line */+    for(;;) {+        if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) {+        bad_header:+            response = JS_NULL;+            goto done;+        }+        if (!strcmp(buf, "\r\n"))+            break;+    }+    if (dbuf_error(header_buf))+        goto fail;+    header_buf->size -= 2; /* remove the trailing CRLF */++    /* download the data */+    for(;;) {+        len = fread(buf, 1, URL_GET_BUF_SIZE, f);+        if (len == 0)+            break;+        dbuf_put(data_buf, (uint8_t *)buf, len);+    }+    if (dbuf_error(data_buf))+        goto fail;+    if (binary_flag) {+        response = JS_NewArrayBufferCopy(ctx,+                                         data_buf->buf, data_buf->size);+    } else {+        response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size);+    }+    if (JS_IsException(response))+        goto fail;+ done:+    js_free(ctx, buf);+    buf = NULL;+    pclose(f);+    f = NULL;+    dbuf_free(data_buf);+    data_buf = NULL;++    if (full_flag) {+        ret_obj = JS_NewObject(ctx);+        if (JS_IsException(ret_obj))+            goto fail;+        JS_DefinePropertyValueStr(ctx, ret_obj, "response",+                                  response,+                                  JS_PROP_C_W_E);+        if (!JS_IsNull(response)) {+            JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders",+                                      JS_NewStringLen(ctx, (char *)header_buf->buf,+                                                      header_buf->size),+                                      JS_PROP_C_W_E);+            JS_DefinePropertyValueStr(ctx, ret_obj, "status",+                                      JS_NewInt32(ctx, status),+                                      JS_PROP_C_W_E);+        }+    } else {+        ret_obj = response;+    }+    dbuf_free(header_buf);+    return ret_obj;+ fail:+    if (f)+        pclose(f);+    js_free(ctx, buf);+    if (data_buf)+        dbuf_free(data_buf);+    if (header_buf)+        dbuf_free(header_buf);+    JS_FreeValue(ctx, response);+    return JS_EXCEPTION;+}++static JSClassDef js_std_file_class = {+    "FILE",+    .finalizer = js_std_file_finalizer,+}; ++static const JSCFunctionListEntry js_std_error_props[] = {+    /* various errno values */+#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE )+    DEF(EINVAL),+    DEF(EIO),+    DEF(EACCES),+    DEF(EEXIST),+    DEF(ENOSPC),+    DEF(ENOSYS),+    DEF(EBUSY),+    DEF(ENOENT),+    DEF(EPERM),+    DEF(EPIPE),+    DEF(EBADF),+#undef DEF+};++static const JSCFunctionListEntry js_std_funcs[] = {+    JS_CFUNC_DEF("exit", 1, js_std_exit ),+    JS_CFUNC_DEF("gc", 0, js_std_gc ),+    JS_CFUNC_DEF("evalScript", 1, js_evalScript ),+    JS_CFUNC_DEF("loadScript", 1, js_loadScript ),+    JS_CFUNC_DEF("getenv", 1, js_std_getenv ),+    JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ),+    JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ),+    JS_CFUNC_DEF("strerror", 1, js_std_strerror ),+    JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ),+    +    /* FILE I/O */+    JS_CFUNC_DEF("open", 2, js_std_open ),+    JS_CFUNC_DEF("popen", 2, js_std_popen ),+    JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ),+    JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ),+    JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ),+    JS_CFUNC_DEF("printf", 1, js_std_printf ),+    JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ),+    JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ),+    JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ),+    JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ),+    JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE),+    /* setenv, ... */+};+    +static const JSCFunctionListEntry js_std_file_proto_funcs[] = {+    JS_CFUNC_DEF("close", 0, js_std_file_close ),+    JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ),+    JS_CFUNC_DEF("printf", 1, js_std_file_printf ),+    JS_CFUNC_DEF("flush", 0, js_std_file_flush ),+    JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ),+    JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ),+    JS_CFUNC_DEF("seek", 2, js_std_file_seek ),+    JS_CFUNC_DEF("eof", 0, js_std_file_eof ),+    JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ),+    JS_CFUNC_DEF("error", 0, js_std_file_error ),+    JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ),+    JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ),+    JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ),+    JS_CFUNC_DEF("getline", 0, js_std_file_getline ),+    JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ),+    JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ),+    JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ),+    /* setvbuf, ...  */+};++static int js_std_init(JSContext *ctx, JSModuleDef *m)+{+    JSValue proto;+    +    /* FILE class */+    /* the class ID is created once */+    JS_NewClassID(&js_std_file_class_id);+    /* the class is created once per runtime */+    JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class);+    proto = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs,+                               countof(js_std_file_proto_funcs));+    JS_SetClassProto(ctx, js_std_file_class_id, proto);++    JS_SetModuleExportList(ctx, m, js_std_funcs,+                           countof(js_std_funcs));+    JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE));+    JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE));+    JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE));+    return 0;+}++JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name)+{+    JSModuleDef *m;+    m = JS_NewCModule(ctx, module_name, js_std_init);+    if (!m)+        return NULL;+    JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs));+    JS_AddModuleExport(ctx, m, "in");+    JS_AddModuleExport(ctx, m, "out");+    JS_AddModuleExport(ctx, m, "err");+    return m;+}++/**********************************************************/+/* 'os' object */++static JSValue js_os_open(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    const char *filename;+    int flags, mode, ret;++    filename = JS_ToCString(ctx, argv[0]);+    if (!filename)+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &flags, argv[1]))+        goto fail;+    if (argc >= 3 && !JS_IsUndefined(argv[2])) {+        if (JS_ToInt32(ctx, &mode, argv[2])) {+        fail:+            JS_FreeCString(ctx, filename);+            return JS_EXCEPTION;+        }+    } else {+        mode = 0666;+    }+#if defined(_WIN32)+    /* force binary mode by default */+    if (!(flags & O_TEXT))+        flags |= O_BINARY;+#endif+    ret = js_get_errno(open(filename, flags, mode));+    JS_FreeCString(ctx, filename);+    return JS_NewInt32(ctx, ret);+}++static JSValue js_os_close(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    int fd, ret;+    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    ret = js_get_errno(close(fd));+    return JS_NewInt32(ctx, ret);+}++static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    int fd, whence;+    int64_t pos, ret;+    BOOL is_bigint;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    is_bigint = JS_IsBigInt(ctx, argv[1]);+    if (JS_ToInt64Ext(ctx, &pos, argv[1]))+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &whence, argv[2]))+        return JS_EXCEPTION;+    ret = lseek(fd, pos, whence);+    if (ret == -1)+        ret = -errno;+    if (is_bigint)+        return JS_NewBigInt64(ctx, ret);+    else+        return JS_NewInt64(ctx, ret);+}++static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv, int magic)+{+    int fd;+    uint64_t pos, len;+    size_t size;+    ssize_t ret;+    uint8_t *buf;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    if (JS_ToIndex(ctx, &pos, argv[2]))+        return JS_EXCEPTION;+    if (JS_ToIndex(ctx, &len, argv[3]))+        return JS_EXCEPTION;+    buf = JS_GetArrayBuffer(ctx, &size, argv[1]);+    if (!buf)+        return JS_EXCEPTION;+    if (pos + len > size)+        return JS_ThrowRangeError(ctx, "read/write array buffer overflow");+    if (magic)+        ret = js_get_errno(write(fd, buf + pos, len));+    else+        ret = js_get_errno(read(fd, buf + pos, len));+    return JS_NewInt64(ctx, ret);+}++static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    int fd;+    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    return JS_NewBool(ctx, isatty(fd) == 1);+}++#if defined(_WIN32)+static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    int fd;+    HANDLE handle;+    CONSOLE_SCREEN_BUFFER_INFO info;+    JSValue obj;++    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    handle = (HANDLE)_get_osfhandle(fd);+    +    if (!GetConsoleScreenBufferInfo(handle, &info))+        return JS_NULL;+    obj = JS_NewArray(ctx);+    if (JS_IsException(obj))+        return obj;+    JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E);+    JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E);+    return obj;+}++static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    int fd;+    HANDLE handle;++    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    handle = (HANDLE)_get_osfhandle(fd);+    +    SetConsoleMode(handle, ENABLE_WINDOW_INPUT);+    return JS_UNDEFINED;+}+#else+static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    int fd;+    struct winsize ws;+    JSValue obj;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    if (ioctl(fd, TIOCGWINSZ, &ws) == 0 &&+        ws.ws_col >= 4 && ws.ws_row >= 4) {+        obj = JS_NewArray(ctx);+        if (JS_IsException(obj))+            return obj;+        JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E);+        JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E);+        return obj;+    } else {+        return JS_NULL;+    }+}++static struct termios oldtty;++static void term_exit(void)+{+    tcsetattr(0, TCSANOW, &oldtty);+}++/* XXX: should add a way to go back to normal mode */+static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    struct termios tty;+    int fd;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    +    memset(&tty, 0, sizeof(tty));+    tcgetattr(fd, &tty);+    oldtty = tty;++    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP+                          |INLCR|IGNCR|ICRNL|IXON);+    tty.c_oflag |= OPOST;+    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);+    tty.c_cflag &= ~(CSIZE|PARENB);+    tty.c_cflag |= CS8;+    tty.c_cc[VMIN] = 1;+    tty.c_cc[VTIME] = 0;++    tcsetattr(fd, TCSANOW, &tty);++    atexit(term_exit);+    return JS_UNDEFINED;+}++#endif /* !_WIN32 */++static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    const char *filename;+    int ret;+    +    filename = JS_ToCString(ctx, argv[0]);+    if (!filename)+        return JS_EXCEPTION;+    ret = js_get_errno(remove(filename));+    JS_FreeCString(ctx, filename);+    return JS_NewInt32(ctx, ret);+}++static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    const char *oldpath, *newpath;+    int ret;+    +    oldpath = JS_ToCString(ctx, argv[0]);+    if (!oldpath)+        return JS_EXCEPTION;+    newpath = JS_ToCString(ctx, argv[1]);+    if (!newpath) {+        JS_FreeCString(ctx, oldpath);+        return JS_EXCEPTION;+    }+    ret = js_get_errno(rename(oldpath, newpath));+    JS_FreeCString(ctx, oldpath);+    JS_FreeCString(ctx, newpath);+    return JS_NewInt32(ctx, ret);+}++static BOOL is_main_thread(JSRuntime *rt)+{+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    return !ts->recv_pipe;+}++static JSOSRWHandler *find_rh(JSThreadState *ts, int fd)+{+    JSOSRWHandler *rh;+    struct list_head *el;++    list_for_each(el, &ts->os_rw_handlers) {+        rh = list_entry(el, JSOSRWHandler, link);+        if (rh->fd == fd)+            return rh;+    }+    return NULL;+}++static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh)+{+    int i;+    list_del(&rh->link);+    for(i = 0; i < 2; i++) {+        JS_FreeValueRT(rt, rh->rw_func[i]);+    }+    js_free_rt(rt, rh);+}++static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv, int magic)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    JSOSRWHandler *rh;+    int fd;+    JSValueConst func;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    func = argv[1];+    if (JS_IsNull(func)) {+        rh = find_rh(ts, fd);+        if (rh) {+            JS_FreeValue(ctx, rh->rw_func[magic]);+            rh->rw_func[magic] = JS_NULL;+            if (JS_IsNull(rh->rw_func[0]) &&+                JS_IsNull(rh->rw_func[1])) {+                /* remove the entry */+                free_rw_handler(JS_GetRuntime(ctx), rh);+            }+        }+    } else {+        if (!JS_IsFunction(ctx, func))+            return JS_ThrowTypeError(ctx, "not a function");+        rh = find_rh(ts, fd);+        if (!rh) {+            rh = js_mallocz(ctx, sizeof(*rh));+            if (!rh)+                return JS_EXCEPTION;+            rh->fd = fd;+            rh->rw_func[0] = JS_NULL;+            rh->rw_func[1] = JS_NULL;+            list_add_tail(&rh->link, &ts->os_rw_handlers);+        }+        JS_FreeValue(ctx, rh->rw_func[magic]);+        rh->rw_func[magic] = JS_DupValue(ctx, func);+    }+    return JS_UNDEFINED;+}++static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num)+{+    JSOSSignalHandler *sh;+    struct list_head *el;+    list_for_each(el, &ts->os_signal_handlers) {+        sh = list_entry(el, JSOSSignalHandler, link);+        if (sh->sig_num == sig_num)+            return sh;+    }+    return NULL;+}++static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh)+{+    list_del(&sh->link);+    JS_FreeValueRT(rt, sh->func);+    js_free_rt(rt, sh);+}++static void os_signal_handler(int sig_num)+{+    os_pending_signals |= ((uint64_t)1 << sig_num);+}++#if defined(_WIN32)+typedef void (*sighandler_t)(int sig_num);+#endif++static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    JSOSSignalHandler *sh;+    uint32_t sig_num;+    JSValueConst func;+    sighandler_t handler;++    if (!is_main_thread(rt))+        return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread");+    +    if (JS_ToUint32(ctx, &sig_num, argv[0]))+        return JS_EXCEPTION;+    if (sig_num >= 64)+        return JS_ThrowRangeError(ctx, "invalid signal number");+    func = argv[1];+    /* func = null: SIG_DFL, func = undefined, SIG_IGN */+    if (JS_IsNull(func) || JS_IsUndefined(func)) {+        sh = find_sh(ts, sig_num);+        if (sh) {+            free_sh(JS_GetRuntime(ctx), sh);+        }+        if (JS_IsNull(func))+            handler = SIG_DFL;+        else+            handler = SIG_IGN;+        signal(sig_num, handler);+    } else {+        if (!JS_IsFunction(ctx, func))+            return JS_ThrowTypeError(ctx, "not a function");+        sh = find_sh(ts, sig_num);+        if (!sh) {+            sh = js_mallocz(ctx, sizeof(*sh));+            if (!sh)+                return JS_EXCEPTION;+            sh->sig_num = sig_num;+            list_add_tail(&sh->link, &ts->os_signal_handlers);+        }+        JS_FreeValue(ctx, sh->func);+        sh->func = JS_DupValue(ctx, func);+        signal(sig_num, os_signal_handler);+    }+    return JS_UNDEFINED;+}++#if defined(__linux__) || defined(__APPLE__)+static int64_t get_time_ms(void)+{+    struct timespec ts;+    clock_gettime(CLOCK_MONOTONIC, &ts);+    return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);+}+#else+/* more portable, but does not work if the date is updated */+static int64_t get_time_ms(void)+{+    struct timeval tv;+    gettimeofday(&tv, NULL);+    return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);+}+#endif++static void unlink_timer(JSRuntime *rt, JSOSTimer *th)+{+    if (th->link.prev) {+        list_del(&th->link);+        th->link.prev = th->link.next = NULL;+    }+}++static void free_timer(JSRuntime *rt, JSOSTimer *th)+{+    JS_FreeValueRT(rt, th->func);+    js_free_rt(rt, th);+}++static JSClassID js_os_timer_class_id;++static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)+{+    JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);+    if (th) {+        th->has_object = FALSE;+        if (!th->link.prev)+            free_timer(rt, th);+    }+}++static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,+                             JS_MarkFunc *mark_func)+{+    JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);+    if (th) {+        JS_MarkValue(rt, th->func, mark_func);+    }+}++static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    int64_t delay;+    JSValueConst func;+    JSOSTimer *th;+    JSValue obj;++    func = argv[0];+    if (!JS_IsFunction(ctx, func))+        return JS_ThrowTypeError(ctx, "not a function");+    if (JS_ToInt64(ctx, &delay, argv[1]))+        return JS_EXCEPTION;+    obj = JS_NewObjectClass(ctx, js_os_timer_class_id);+    if (JS_IsException(obj))+        return obj;+    th = js_mallocz(ctx, sizeof(*th));+    if (!th) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    th->has_object = TRUE;+    th->timeout = get_time_ms() + delay;+    th->func = JS_DupValue(ctx, func);+    list_add_tail(&th->link, &ts->os_timers);+    JS_SetOpaque(obj, th);+    return obj;+}++static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id);+    if (!th)+        return JS_EXCEPTION;+    unlink_timer(JS_GetRuntime(ctx), th);+    return JS_UNDEFINED;+}++static JSClassDef js_os_timer_class = {+    "OSTimer",+    .finalizer = js_os_timer_finalizer,+    .gc_mark = js_os_timer_mark,+}; ++static void call_handler(JSContext *ctx, JSValueConst func)+{+    JSValue ret, func1;+    /* 'func' might be destroyed when calling itself (if it frees the+       handler), so must take extra care */+    func1 = JS_DupValue(ctx, func);+    ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL);+    JS_FreeValue(ctx, func1);+    if (JS_IsException(ret))+        js_std_dump_error(ctx);+    JS_FreeValue(ctx, ret);+}++#if defined(_WIN32)++static int js_os_poll(JSContext *ctx)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    int min_delay, console_fd;+    int64_t cur_time, delay;+    JSOSRWHandler *rh;+    struct list_head *el;+    +    /* XXX: handle signals if useful */++    if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers))+        return -1; /* no more events */+    +    /* XXX: only timers and basic console input are supported */+    if (!list_empty(&ts->os_timers)) {+        cur_time = get_time_ms();+        min_delay = 10000;+        list_for_each(el, &ts->os_timers) {+            JSOSTimer *th = list_entry(el, JSOSTimer, link);+            delay = th->timeout - cur_time;+            if (delay <= 0) {+                JSValue func;+                /* the timer expired */+                func = th->func;+                th->func = JS_UNDEFINED;+                unlink_timer(rt, th);+                if (!th->has_object)+                    free_timer(rt, th);+                call_handler(ctx, func);+                JS_FreeValue(ctx, func);+                return 0;+            } else if (delay < min_delay) {+                min_delay = delay;+            }+        }+    } else {+        min_delay = -1;+    }++    console_fd = -1;+    list_for_each(el, &ts->os_rw_handlers) {+        rh = list_entry(el, JSOSRWHandler, link);+        if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {+            console_fd = rh->fd;+            break;+        }+    }++    if (console_fd >= 0) {+        DWORD ti, ret;+        HANDLE handle;+        if (min_delay == -1)+            ti = INFINITE;+        else+            ti = min_delay;+        handle = (HANDLE)_get_osfhandle(console_fd);+        ret = WaitForSingleObject(handle, ti);+        if (ret == WAIT_OBJECT_0) {+            list_for_each(el, &ts->os_rw_handlers) {+                rh = list_entry(el, JSOSRWHandler, link);+                if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {+                    call_handler(ctx, rh->rw_func[0]);+                    /* must stop because the list may have been modified */+                    break;+                }+            }+        }+    } else {+        Sleep(min_delay);+    }+    return 0;+}+#else++#ifdef USE_WORKER++static void js_free_message(JSWorkerMessage *msg);++/* return 1 if a message was handled, 0 if no message */+static int handle_posted_message(JSRuntime *rt, JSContext *ctx,+                                 JSWorkerMessageHandler *port)+{+    JSWorkerMessagePipe *ps = port->recv_pipe;+    int ret;+    struct list_head *el;+    JSWorkerMessage *msg;+    JSValue obj, data_obj, func, retval;+    +    pthread_mutex_lock(&ps->mutex);+    if (!list_empty(&ps->msg_queue)) {+        el = ps->msg_queue.next;+        msg = list_entry(el, JSWorkerMessage, link);++        /* remove the message from the queue */+        list_del(&msg->link);++        if (list_empty(&ps->msg_queue)) {+            uint8_t buf[16];+            int ret;+            for(;;) {+                ret = read(ps->read_fd, buf, sizeof(buf));+                if (ret >= 0)+                    break;+                if (errno != EAGAIN && errno != EINTR)+                    break;+            }+        }++        pthread_mutex_unlock(&ps->mutex);++        data_obj = JS_ReadObject(ctx, msg->data, msg->data_len,+                                 JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE);++        js_free_message(msg);+        +        if (JS_IsException(data_obj))+            goto fail;+        obj = JS_NewObject(ctx);+        if (JS_IsException(obj)) {+            JS_FreeValue(ctx, data_obj);+            goto fail;+        }+        JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E);++        /* 'func' might be destroyed when calling itself (if it frees the+           handler), so must take extra care */+        func = JS_DupValue(ctx, port->on_message_func);+        retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj);+        JS_FreeValue(ctx, obj);+        JS_FreeValue(ctx, func);+        if (JS_IsException(retval)) {+        fail:+            js_std_dump_error(ctx);+        } else {+            JS_FreeValue(ctx, retval);+        }+        ret = 1;+    } else {+        pthread_mutex_unlock(&ps->mutex);+        ret = 0;+    }+    return ret;+}+#else+static int handle_posted_message(JSRuntime *rt, JSContext *ctx,+                                 JSWorkerMessageHandler *port)+{+    return 0;+}+#endif++static int js_os_poll(JSContext *ctx)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    int ret, fd_max, min_delay;+    int64_t cur_time, delay;+    fd_set rfds, wfds;+    JSOSRWHandler *rh;+    struct list_head *el;+    struct timeval tv, *tvp;++    /* only check signals in the main thread */+    if (!ts->recv_pipe &&+        unlikely(os_pending_signals != 0)) {+        JSOSSignalHandler *sh;+        uint64_t mask;+        +        list_for_each(el, &ts->os_signal_handlers) {+            sh = list_entry(el, JSOSSignalHandler, link);+            mask = (uint64_t)1 << sh->sig_num;+            if (os_pending_signals & mask) {+                os_pending_signals &= ~mask;+                call_handler(ctx, sh->func);+                return 0;+            }+        }+    }++    if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) &&+        list_empty(&ts->port_list))+        return -1; /* no more events */+    +    if (!list_empty(&ts->os_timers)) {+        cur_time = get_time_ms();+        min_delay = 10000;+        list_for_each(el, &ts->os_timers) {+            JSOSTimer *th = list_entry(el, JSOSTimer, link);+            delay = th->timeout - cur_time;+            if (delay <= 0) {+                JSValue func;+                /* the timer expired */+                func = th->func;+                th->func = JS_UNDEFINED;+                unlink_timer(rt, th);+                if (!th->has_object)+                    free_timer(rt, th);+                call_handler(ctx, func);+                JS_FreeValue(ctx, func);+                return 0;+            } else if (delay < min_delay) {+                min_delay = delay;+            }+        }+        tv.tv_sec = min_delay / 1000;+        tv.tv_usec = (min_delay % 1000) * 1000;+        tvp = &tv;+    } else {+        tvp = NULL;+    }+    +    FD_ZERO(&rfds);+    FD_ZERO(&wfds);+    fd_max = -1;+    list_for_each(el, &ts->os_rw_handlers) {+        rh = list_entry(el, JSOSRWHandler, link);+        fd_max = max_int(fd_max, rh->fd);+        if (!JS_IsNull(rh->rw_func[0]))+            FD_SET(rh->fd, &rfds);+        if (!JS_IsNull(rh->rw_func[1]))+            FD_SET(rh->fd, &wfds);+    }++    list_for_each(el, &ts->port_list) {+        JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);+        if (!JS_IsNull(port->on_message_func)) {+            JSWorkerMessagePipe *ps = port->recv_pipe;+            fd_max = max_int(fd_max, ps->read_fd);+            FD_SET(ps->read_fd, &rfds);+        }+    }++    ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp);+    if (ret > 0) {+        list_for_each(el, &ts->os_rw_handlers) {+            rh = list_entry(el, JSOSRWHandler, link);+            if (!JS_IsNull(rh->rw_func[0]) &&+                FD_ISSET(rh->fd, &rfds)) {+                call_handler(ctx, rh->rw_func[0]);+                /* must stop because the list may have been modified */+                goto done;+            }+            if (!JS_IsNull(rh->rw_func[1]) &&+                FD_ISSET(rh->fd, &wfds)) {+                call_handler(ctx, rh->rw_func[1]);+                /* must stop because the list may have been modified */+                goto done;+            }+        }++        list_for_each(el, &ts->port_list) {+            JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);+            if (!JS_IsNull(port->on_message_func)) {+                JSWorkerMessagePipe *ps = port->recv_pipe;+                if (FD_ISSET(ps->read_fd, &rfds)) {+                    if (handle_posted_message(rt, ctx, port))+                        goto done;+                }+            }+        }+    }+    done:+    return 0;+}+#endif /* !_WIN32 */++static JSValue make_obj_error(JSContext *ctx,+                              JSValue obj,+                              int err)+{+    JSValue arr;+    if (JS_IsException(obj))+        return obj;+    arr = JS_NewArray(ctx);+    if (JS_IsException(arr))+        return JS_EXCEPTION;+    JS_DefinePropertyValueUint32(ctx, arr, 0, obj,+                                 JS_PROP_C_W_E);+    JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err),+                                 JS_PROP_C_W_E);+    return arr;+}++static JSValue make_string_error(JSContext *ctx,+                                 const char *buf,+                                 int err)+{+    return make_obj_error(ctx, JS_NewString(ctx, buf), err);+}++/* return [cwd, errorcode] */+static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    char buf[PATH_MAX];+    int err;+    +    if (!getcwd(buf, sizeof(buf))) {+        buf[0] = '\0';+        err = errno;+    } else {+        err = 0;+    }+    return make_string_error(ctx, buf, err);+}++static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    const char *target;+    int err;++    target = JS_ToCString(ctx, argv[0]);+    if (!target)+        return JS_EXCEPTION;+    err = js_get_errno(chdir(target));+    JS_FreeCString(ctx, target);+    return JS_NewInt32(ctx, err);+}++static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    int mode, ret;+    const char *path;+    +    if (argc >= 2) {+        if (JS_ToInt32(ctx, &mode, argv[1]))+            return JS_EXCEPTION;+    } else {+        mode = 0777;+    }+    path = JS_ToCString(ctx, argv[0]);+    if (!path)+        return JS_EXCEPTION;+#if defined(_WIN32)+    (void)mode;+    ret = js_get_errno(mkdir(path));+#else+    ret = js_get_errno(mkdir(path, mode));+#endif+    JS_FreeCString(ctx, path);+    return JS_NewInt32(ctx, ret);+}++/* return [array, errorcode] */+static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    const char *path;+    DIR *f;+    struct dirent *d;+    JSValue obj;+    int err;+    uint32_t len;+    +    path = JS_ToCString(ctx, argv[0]);+    if (!path)+        return JS_EXCEPTION;+    obj = JS_NewArray(ctx);+    if (JS_IsException(obj)) {+        JS_FreeCString(ctx, path);+        return JS_EXCEPTION;+    }+    f = opendir(path);+    if (!f)+        err = errno;+    else+        err = 0;+    JS_FreeCString(ctx, path);+    if (!f)+        goto done;+    len = 0;+    for(;;) {+        errno = 0;+        d = readdir(f);+        if (!d) {+            err = errno;+            break;+        }+        JS_DefinePropertyValueUint32(ctx, obj, len++,+                                     JS_NewString(ctx, d->d_name),+                                     JS_PROP_C_W_E);+    }+    closedir(f);+ done:+    return make_obj_error(ctx, obj, err);+}++#if !defined(_WIN32)+static int64_t timespec_to_ms(const struct timespec *tv)+{+    return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000);+}+#endif++/* return [obj, errcode] */+static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv, int is_lstat)+{+    const char *path;+    int err, res;+    struct stat st;+    JSValue obj;++    path = JS_ToCString(ctx, argv[0]);+    if (!path)+        return JS_EXCEPTION;+#if defined(_WIN32)+    res = stat(path, &st);+#else+    if (is_lstat)+        res = lstat(path, &st);+    else+        res = stat(path, &st);+#endif+    JS_FreeCString(ctx, path);+    if (res < 0) {+        err = errno;+        obj = JS_NULL;+    } else {+        err = 0;+        obj = JS_NewObject(ctx);+        if (JS_IsException(obj))+            return JS_EXCEPTION;+        JS_DefinePropertyValueStr(ctx, obj, "dev",+                                  JS_NewInt64(ctx, st.st_dev),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "ino",+                                  JS_NewInt64(ctx, st.st_ino),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "mode",+                                  JS_NewInt32(ctx, st.st_mode),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "nlink",+                                  JS_NewInt64(ctx, st.st_nlink),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "uid",+                                  JS_NewInt64(ctx, st.st_uid),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "gid",+                                  JS_NewInt64(ctx, st.st_gid),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "rdev",+                                  JS_NewInt64(ctx, st.st_rdev),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "size",+                                  JS_NewInt64(ctx, st.st_size),+                                  JS_PROP_C_W_E);+#if !defined(_WIN32)+        JS_DefinePropertyValueStr(ctx, obj, "blocks",+                                  JS_NewInt64(ctx, st.st_blocks),+                                  JS_PROP_C_W_E);+#endif+#if defined(_WIN32)+        JS_DefinePropertyValueStr(ctx, obj, "atime",+                                  JS_NewInt64(ctx, (int64_t)st.st_atime * 1000),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "mtime",+                                  JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "ctime",+                                  JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000),+                                  JS_PROP_C_W_E);+#elif defined(__APPLE__)+        JS_DefinePropertyValueStr(ctx, obj, "atime",+                                  JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "mtime",+                                  JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "ctime",+                                  JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)),+                                  JS_PROP_C_W_E);+#else+        JS_DefinePropertyValueStr(ctx, obj, "atime",+                                  JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "mtime",+                                  JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)),+                                  JS_PROP_C_W_E);+        JS_DefinePropertyValueStr(ctx, obj, "ctime",+                                  JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)),+                                  JS_PROP_C_W_E);+#endif+    }+    return make_obj_error(ctx, obj, err);+}++#if !defined(_WIN32)+static void ms_to_timeval(struct timeval *tv, uint64_t v)+{+    tv->tv_sec = v / 1000;+    tv->tv_usec = (v % 1000) * 1000;+}+#endif++static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    const char *path;+    int64_t atime, mtime;+    int ret;+    +    if (JS_ToInt64(ctx, &atime, argv[1]))+        return JS_EXCEPTION;+    if (JS_ToInt64(ctx, &mtime, argv[2]))+        return JS_EXCEPTION;+    path = JS_ToCString(ctx, argv[0]);+    if (!path)+        return JS_EXCEPTION;+#if defined(_WIN32)+    {+        struct _utimbuf times;+        times.actime = atime / 1000;+        times.modtime = mtime / 1000;+        ret = js_get_errno(_utime(path, &times));+    }+#else+    {+        struct timeval times[2];+        ms_to_timeval(&times[0], atime);+        ms_to_timeval(&times[1], mtime);+        ret = js_get_errno(utimes(path, times));+    }+#endif+    JS_FreeCString(ctx, path);+    return JS_NewInt32(ctx, ret);+}++#if !defined(_WIN32)++/* return [path, errorcode] */+static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    const char *path;+    char buf[PATH_MAX], *res;+    int err;++    path = JS_ToCString(ctx, argv[0]);+    if (!path)+        return JS_EXCEPTION;+    res = realpath(path, buf);+    JS_FreeCString(ctx, path);+    if (!res) {+        buf[0] = '\0';+        err = errno;+    } else {+        err = 0;+    }+    return make_string_error(ctx, buf, err);+}++static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    const char *target, *linkpath;+    int err;+    +    target = JS_ToCString(ctx, argv[0]);+    if (!target)+        return JS_EXCEPTION;+    linkpath = JS_ToCString(ctx, argv[1]);+    if (!linkpath) {+        JS_FreeCString(ctx, target);+        return JS_EXCEPTION;+    }+    err = js_get_errno(symlink(target, linkpath));+    JS_FreeCString(ctx, target);+    JS_FreeCString(ctx, linkpath);+    return JS_NewInt32(ctx, err);+}++/* return [path, errorcode] */+static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    const char *path;+    char buf[PATH_MAX];+    int err;+    ssize_t res;+    +    path = JS_ToCString(ctx, argv[0]);+    if (!path)+        return JS_EXCEPTION;+    res = readlink(path, buf, sizeof(buf) - 1);+    if (res < 0) {+        buf[0] = '\0';+        err = errno;+    } else {+        buf[res] = '\0';+        err = 0;+    }+    JS_FreeCString(ctx, path);+    return make_string_error(ctx, buf, err);+}++static char **build_envp(JSContext *ctx, JSValueConst obj)+{+    uint32_t len, i;+    JSPropertyEnum *tab;+    char **envp, *pair;+    const char *key, *str;+    JSValue val;+    size_t key_len, str_len;+    +    if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj,+                               JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0)+        return NULL;+    envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1));+    if (!envp)+        goto fail;+    for(i = 0; i < len; i++) {+        val = JS_GetProperty(ctx, obj, tab[i].atom);+        if (JS_IsException(val))+            goto fail;+        str = JS_ToCString(ctx, val);+        JS_FreeValue(ctx, val);+        if (!str)+            goto fail;+        key = JS_AtomToCString(ctx, tab[i].atom);+        if (!key) {+            JS_FreeCString(ctx, str);+            goto fail;+        }+        key_len = strlen(key);+        str_len = strlen(str);+        pair = js_malloc(ctx, key_len + str_len + 2);+        if (!pair) {+            JS_FreeCString(ctx, key);+            JS_FreeCString(ctx, str);+            goto fail;+        }+        memcpy(pair, key, key_len);+        pair[key_len] = '=';+        memcpy(pair + key_len + 1, str, str_len);+        pair[key_len + 1 + str_len] = '\0';+        envp[i] = pair;+        JS_FreeCString(ctx, key);+        JS_FreeCString(ctx, str);+    }+ done:+    for(i = 0; i < len; i++)+        JS_FreeAtom(ctx, tab[i].atom);+    js_free(ctx, tab);+    return envp;+ fail:+    if (envp) {+        for(i = 0; i < len; i++)+            js_free(ctx, envp[i]);+        js_free(ctx, envp);+        envp = NULL;+    }+    goto done;+}++/* execvpe is not available on non GNU systems */+static int my_execvpe(const char *filename, char **argv, char **envp)+{+    char *path, *p, *p_next, *p1;+    char buf[PATH_MAX];+    size_t filename_len, path_len;+    BOOL eacces_error;+    +    filename_len = strlen(filename);+    if (filename_len == 0) {+        errno = ENOENT;+        return -1;+    }+    if (strchr(filename, '/'))+        return execve(filename, argv, envp);+    +    path = getenv("PATH");+    if (!path)+        path = (char *)"/bin:/usr/bin";+    eacces_error = FALSE;+    p = path;+    for(p = path; p != NULL; p = p_next) {+        p1 = strchr(p, ':');+        if (!p1) {+            p_next = NULL;+            path_len = strlen(p);+        } else {+            p_next = p1 + 1;+            path_len = p1 - p;+        }+        /* path too long */+        if ((path_len + 1 + filename_len + 1) > PATH_MAX)+            continue;+        memcpy(buf, p, path_len);+        buf[path_len] = '/';+        memcpy(buf + path_len + 1, filename, filename_len);+        buf[path_len + 1 + filename_len] = '\0';+        +        execve(buf, argv, envp);++        switch(errno) {+        case EACCES:+            eacces_error = TRUE;+            break;+        case ENOENT:+        case ENOTDIR:+            break;+        default:+            return -1;+        }+    }+    if (eacces_error)+        errno = EACCES;+    return -1;+}++/* exec(args[, options]) -> exitcode */+static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    JSValueConst options, args = argv[0];+    JSValue val, ret_val;+    const char **exec_argv, *file = NULL, *str, *cwd = NULL;+    char **envp = environ;+    uint32_t exec_argc, i;+    int ret, pid, status;+    BOOL block_flag = TRUE, use_path = TRUE;+    static const char *std_name[3] = { "stdin", "stdout", "stderr" };+    int std_fds[3];+    uint32_t uid = -1, gid = -1;+    +    val = JS_GetPropertyStr(ctx, args, "length");+    if (JS_IsException(val))+        return JS_EXCEPTION;+    ret = JS_ToUint32(ctx, &exec_argc, val);+    JS_FreeValue(ctx, val);+    if (ret)+        return JS_EXCEPTION;+    /* arbitrary limit to avoid overflow */+    if (exec_argc < 1 || exec_argc > 65535) {+        return JS_ThrowTypeError(ctx, "invalid number of arguments");+    }+    exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1));+    if (!exec_argv)+        return JS_EXCEPTION;+    for(i = 0; i < exec_argc; i++) {+        val = JS_GetPropertyUint32(ctx, args, i);+        if (JS_IsException(val))+            goto exception;+        str = JS_ToCString(ctx, val);+        JS_FreeValue(ctx, val);+        if (!str)+            goto exception;+        exec_argv[i] = str;+    }+    exec_argv[exec_argc] = NULL;++    for(i = 0; i < 3; i++)+        std_fds[i] = i;+    +    /* get the options, if any */+    if (argc >= 2) {+        options = argv[1];++        if (get_bool_option(ctx, &block_flag, options, "block"))+            goto exception;+        if (get_bool_option(ctx, &use_path, options, "usePath"))+            goto exception;+        +        val = JS_GetPropertyStr(ctx, options, "file");+        if (JS_IsException(val))+            goto exception;+        if (!JS_IsUndefined(val)) {+            file = JS_ToCString(ctx, val);+            JS_FreeValue(ctx, val);+            if (!file)+                goto exception;+        }++        val = JS_GetPropertyStr(ctx, options, "cwd");+        if (JS_IsException(val))+            goto exception;+        if (!JS_IsUndefined(val)) {+            cwd = JS_ToCString(ctx, val);+            JS_FreeValue(ctx, val);+            if (!cwd)+                goto exception;+        }++        /* stdin/stdout/stderr handles */+        for(i = 0; i < 3; i++) {+            val = JS_GetPropertyStr(ctx, options, std_name[i]);+            if (JS_IsException(val))+                goto exception;+            if (!JS_IsUndefined(val)) {+                int fd;+                ret = JS_ToInt32(ctx, &fd, val);+                JS_FreeValue(ctx, val);+                if (ret)+                    goto exception;+                std_fds[i] = fd;+            }+        }++        val = JS_GetPropertyStr(ctx, options, "env");+        if (JS_IsException(val))+            goto exception;+        if (!JS_IsUndefined(val)) {+            envp = build_envp(ctx, val);+            JS_FreeValue(ctx, val);+            if (!envp)+                goto exception;+        }+        +        val = JS_GetPropertyStr(ctx, options, "uid");+        if (JS_IsException(val))+            goto exception;+        if (!JS_IsUndefined(val)) {+            ret = JS_ToUint32(ctx, &uid, val);+            JS_FreeValue(ctx, val);+            if (ret)+                goto exception;+        }++        val = JS_GetPropertyStr(ctx, options, "gid");+        if (JS_IsException(val))+            goto exception;+        if (!JS_IsUndefined(val)) {+            ret = JS_ToUint32(ctx, &gid, val);+            JS_FreeValue(ctx, val);+            if (ret)+                goto exception;+        }+    }++    pid = fork();+    if (pid < 0) {+        JS_ThrowTypeError(ctx, "fork error");+        goto exception;+    }+    if (pid == 0) {+        /* child */+        int fd_max = sysconf(_SC_OPEN_MAX);++        /* remap the stdin/stdout/stderr handles if necessary */+        for(i = 0; i < 3; i++) {+            if (std_fds[i] != i) {+                if (dup2(std_fds[i], i) < 0)+                    _exit(127);+            }+        }++        for(i = 3; i < fd_max; i++)+            close(i);+        if (cwd) {+            if (chdir(cwd) < 0)+                _exit(127);+        }+        if (uid != -1) {+            if (setuid(uid) < 0)+                _exit(127);+        }+        if (gid != -1) {+            if (setgid(gid) < 0)+                _exit(127);+        }++        if (!file)+            file = exec_argv[0];+        if (use_path)+            ret = my_execvpe(file, (char **)exec_argv, envp);+        else+            ret = execve(file, (char **)exec_argv, envp);+        _exit(127);+    }+    /* parent */+    if (block_flag) {+        for(;;) {+            ret = waitpid(pid, &status, 0);+            if (ret == pid) {+                if (WIFEXITED(status)) {+                    ret = WEXITSTATUS(status);+                    break;+                } else if (WIFSIGNALED(status)) {+                    ret = -WTERMSIG(status);+                    break;+                }+            }+        }+    } else {+        ret = pid;+    }+    ret_val = JS_NewInt32(ctx, ret);+ done:+    JS_FreeCString(ctx, file);+    JS_FreeCString(ctx, cwd);+    for(i = 0; i < exec_argc; i++)+        JS_FreeCString(ctx, exec_argv[i]);+    js_free(ctx, exec_argv);+    if (envp != environ) {+        char **p;+        p = envp;+        while (*p != NULL) {+            js_free(ctx, *p);+            p++;+        }+        js_free(ctx, envp);+    }+    return ret_val;+ exception:+    ret_val = JS_EXCEPTION;+    goto done;+}++/* waitpid(pid, block) -> [pid, status] */+static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    int pid, status, options, ret;+    JSValue obj;+    +    if (JS_ToInt32(ctx, &pid, argv[0]))+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &options, argv[1]))+        return JS_EXCEPTION;++    ret = waitpid(pid, &status, options);+    if (ret < 0) {+        ret = -errno;+        status = 0;+    }++    obj = JS_NewArray(ctx);+    if (JS_IsException(obj))+        return obj;+    JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret),+                                 JS_PROP_C_W_E);+    JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status),+                                 JS_PROP_C_W_E);+    return obj;+}    ++/* pipe() -> [read_fd, write_fd] or null if error */+static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    int pipe_fds[2], ret;+    JSValue obj;+    +    ret = pipe(pipe_fds);+    if (ret < 0)+        return JS_NULL;+    obj = JS_NewArray(ctx);+    if (JS_IsException(obj))+        return obj;+    JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]),+                                 JS_PROP_C_W_E);+    JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]),+                                 JS_PROP_C_W_E);+    return obj;+}++/* kill(pid, sig) */+static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    int pid, sig, ret;+    +    if (JS_ToInt32(ctx, &pid, argv[0]))+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &sig, argv[1]))+        return JS_EXCEPTION;+    ret = js_get_errno(kill(pid, sig));+    return JS_NewInt32(ctx, ret);+}++/* sleep(delay_ms) */+static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv)+{+    int64_t delay;+    struct timespec ts;+    int ret;+    +    if (JS_ToInt64(ctx, &delay, argv[0]))+        return JS_EXCEPTION;+    ts.tv_sec = delay / 1000;+    ts.tv_nsec = (delay % 1000) * 1000000;+    ret = js_get_errno(nanosleep(&ts, NULL));+    return JS_NewInt32(ctx, ret);+}++/* dup(fd) */+static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val,+                         int argc, JSValueConst *argv)+{+    int fd, ret;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    ret = js_get_errno(dup(fd));+    return JS_NewInt32(ctx, ret);+}++/* dup2(fd) */+static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,+                         int argc, JSValueConst *argv)+{+    int fd, fd2, ret;+    +    if (JS_ToInt32(ctx, &fd, argv[0]))+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &fd2, argv[1]))+        return JS_EXCEPTION;+    ret = js_get_errno(dup2(fd, fd2));+    return JS_NewInt32(ctx, ret);+}++#endif /* !_WIN32 */++#ifdef USE_WORKER++/* Worker */++typedef struct {+    JSWorkerMessagePipe *recv_pipe;+    JSWorkerMessagePipe *send_pipe;+    JSWorkerMessageHandler *msg_handler;+} JSWorkerData;++typedef struct {+    /* source code of the worker */+    char *eval_buf;+    size_t eval_buf_len;+    JSWorkerMessagePipe *recv_pipe, *send_pipe;+} WorkerFuncArgs;++typedef struct {+    int ref_count;+    uint64_t buf[0];+} JSSABHeader;++static JSClassID js_worker_class_id;++static int atomic_add_int(int *ptr, int v)+{+    return atomic_fetch_add((_Atomic(uint32_t) *)ptr, v) + v;+}++/* shared array buffer allocator */+static void *js_sab_alloc(void *opaque, size_t size)+{+    JSSABHeader *sab;+    sab = malloc(sizeof(JSSABHeader) + size);+    if (!sab)+        return NULL;+    sab->ref_count = 1;+    return sab->buf;+}++static void js_sab_free(void *opaque, void *ptr)+{+    JSSABHeader *sab;+    int ref_count;+    sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader));+    ref_count = atomic_add_int(&sab->ref_count, -1);+    assert(ref_count >= 0);+    if (ref_count == 0) {+        free(sab);+    }+}++static void js_sab_dup(void *opaque, void *ptr)+{+    JSSABHeader *sab;+    sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader));+    atomic_add_int(&sab->ref_count, 1);+}++static JSWorkerMessagePipe *js_new_message_pipe(void)+{+    JSWorkerMessagePipe *ps;+    int pipe_fds[2];+    +    if (pipe(pipe_fds) < 0)+        return NULL;++    ps = malloc(sizeof(*ps));+    if (!ps) {+        close(pipe_fds[0]);+        close(pipe_fds[1]);+        return NULL;+    }+    ps->ref_count = 1;+    init_list_head(&ps->msg_queue);+    pthread_mutex_init(&ps->mutex, NULL);+    ps->read_fd = pipe_fds[0];+    ps->write_fd = pipe_fds[1];+    return ps;+}++static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps)+{+    atomic_add_int(&ps->ref_count, 1);+    return ps;+}++static void js_free_message(JSWorkerMessage *msg)+{+    size_t i;+    /* free the SAB */+    for(i = 0; i < msg->sab_tab_len; i++) {+        js_sab_free(NULL, msg->sab_tab[i]);+    }+    free(msg->sab_tab);+    free(msg->data);+    free(msg);+}++static void js_free_message_pipe(JSWorkerMessagePipe *ps)+{+    struct list_head *el, *el1;+    JSWorkerMessage *msg;+    int ref_count;+    +    if (!ps)+        return;+    +    ref_count = atomic_add_int(&ps->ref_count, -1);+    assert(ref_count >= 0);+    if (ref_count == 0) {+        list_for_each_safe(el, el1, &ps->msg_queue) {+            msg = list_entry(el, JSWorkerMessage, link);+            js_free_message(msg);+        }+        pthread_mutex_destroy(&ps->mutex);+        close(ps->read_fd);+        close(ps->write_fd);+        free(ps);+    }+}++static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port)+{+    if (port) {+        js_free_message_pipe(port->recv_pipe);+        JS_FreeValueRT(rt, port->on_message_func);+        list_del(&port->link);+        js_free_rt(rt, port);+    }+}++static void js_worker_finalizer(JSRuntime *rt, JSValue val)+{+    JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id);+    if (worker) {+        js_free_message_pipe(worker->recv_pipe);+        js_free_message_pipe(worker->send_pipe);+        js_free_port(rt, worker->msg_handler);+        js_free_rt(rt, worker);+    }+}++static JSClassDef js_worker_class = {+    "Worker",+    .finalizer = js_worker_finalizer,+}; ++static void *worker_func(void *opaque)+{+    WorkerFuncArgs *args = opaque;+    JSRuntime *rt;+    JSThreadState *ts;+    JSContext *ctx;+    JSValue retval;+    +    rt = JS_NewRuntime();+    if (rt == NULL) {+        fprintf(stderr, "JS_NewRuntime failure");+        exit(1);+    }        +    js_std_init_handlers(rt);++    /* set the pipe to communicate with the parent */+    ts = JS_GetRuntimeOpaque(rt);+    ts->recv_pipe = args->recv_pipe;+    ts->send_pipe = args->send_pipe;+    +    ctx = JS_NewContext(rt);+    if (ctx == NULL) {+        fprintf(stderr, "JS_NewContext failure");+    }++    JS_SetCanBlock(rt, TRUE);++    js_std_add_helpers(ctx, -1, NULL);++    /* system modules */+    js_init_module_std(ctx, "std");+    js_init_module_os(ctx, "os");++    retval = JS_Eval(ctx, args->eval_buf, args->eval_buf_len,+                      "<worker>", JS_EVAL_TYPE_MODULE);+    free(args->eval_buf);+    free(args);++    if (JS_IsException(retval))+        js_std_dump_error(ctx);+    JS_FreeValue(ctx, retval);++    js_std_loop(ctx);++    JS_FreeContext(ctx);+    js_std_free_handlers(rt);+    JS_FreeRuntime(rt);+    return NULL;+}++static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target,+                                       JSWorkerMessagePipe *recv_pipe,+                                       JSWorkerMessagePipe *send_pipe)+{+    JSValue obj = JS_UNDEFINED, proto;+    JSWorkerData *s;+    +    /* create the object */+    if (JS_IsUndefined(new_target)) {+        proto = JS_GetClassProto(ctx, js_worker_class_id);+    } else {+        proto = JS_GetPropertyStr(ctx, new_target, "prototype");+        if (JS_IsException(proto))+            goto fail;+    }+    obj = JS_NewObjectProtoClass(ctx, proto, js_worker_class_id);+    JS_FreeValue(ctx, proto);+    if (JS_IsException(obj))+        goto fail;+    s = js_mallocz(ctx, sizeof(*s));+    if (!s)+        goto fail;+    s->recv_pipe = js_dup_message_pipe(recv_pipe);+    s->send_pipe = js_dup_message_pipe(send_pipe);++    JS_SetOpaque(obj, s);+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,+                              int argc, JSValueConst *argv)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    WorkerFuncArgs *args;+    const char *str;+    size_t str_len;+    pthread_t tid;+    pthread_attr_t attr;+    JSValue obj = JS_UNDEFINED;+    int ret;+    +    /* XXX: in order to avoid problems with resource liberation, we+       don't support creating workers inside workers */+    if (!is_main_thread(rt))+        return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker");+    +    /* script source */+    +    str = JS_ToCStringLen(ctx, &str_len, argv[0]);+    if (!str)+        return JS_EXCEPTION;++    args = malloc(sizeof(*args));+    if (!args) {+        JS_ThrowOutOfMemory(ctx);+        goto fail;+    }+    memset(args, 0, sizeof(*args));+    args->eval_buf = malloc(str_len + 1);+    if (!args->eval_buf) {+        JS_ThrowOutOfMemory(ctx);+        goto fail;+    }+    memcpy(args->eval_buf, str, str_len + 1);+    args->eval_buf_len = str_len;+    JS_FreeCString(ctx, str);+    str = NULL;++    /* ports */+    args->recv_pipe = js_new_message_pipe();+    if (!args->recv_pipe)+        goto fail;+    args->send_pipe = js_new_message_pipe();+    if (!args->send_pipe)+        goto fail;++    obj = js_worker_ctor_internal(ctx, new_target,+                                  args->send_pipe, args->recv_pipe);+    if (JS_IsUndefined(obj))+        goto fail;+    +    pthread_attr_init(&attr);+    /* no join at the end */+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);+    ret = pthread_create(&tid, &attr, worker_func, args);+    pthread_attr_destroy(&attr);+    if (ret != 0) {+        JS_ThrowTypeError(ctx, "could not create worker");+        goto fail;+    }+    return obj;+ fail:+    JS_FreeCString(ctx, str);+    if (args) {+        free(args->eval_buf);+        js_free_message_pipe(args->recv_pipe);+        js_free_message_pipe(args->send_pipe);+        free(args);+    }+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);+    JSWorkerMessagePipe *ps;+    size_t data_len, sab_tab_len, i;+    uint8_t *data;+    JSWorkerMessage *msg;+    uint8_t **sab_tab;+    +    if (!worker)+        return JS_EXCEPTION;+    +    data = JS_WriteObject2(ctx, &data_len, argv[0],+                           JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE,+                           &sab_tab, &sab_tab_len);+    if (!data)+        return JS_EXCEPTION;++    msg = malloc(sizeof(*msg));+    if (!msg)+        goto fail;+    msg->data = NULL;+    msg->sab_tab = NULL;++    /* must reallocate because the allocator may be different */+    msg->data = malloc(data_len);+    if (!msg->data)+        goto fail;+    memcpy(msg->data, data, data_len);+    msg->data_len = data_len;++    msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);+    if (!msg->sab_tab)+        goto fail;+    memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);+    msg->sab_tab_len = sab_tab_len;++    js_free(ctx, data);+    js_free(ctx, sab_tab);+    +    /* increment the SAB reference counts */+    for(i = 0; i < msg->sab_tab_len; i++) {+        js_sab_dup(NULL, msg->sab_tab[i]);+    }++    ps = worker->send_pipe;+    pthread_mutex_lock(&ps->mutex);+    /* indicate that data is present */+    if (list_empty(&ps->msg_queue)) {+        uint8_t ch = '\0';+        int ret;+        for(;;) {+            ret = write(ps->write_fd, &ch, 1);+            if (ret == 1)+                break;+            if (ret < 0 && (errno != EAGAIN || errno != EINTR))+                break;+        }+    }+    list_add_tail(&msg->link, &ps->msg_queue);+    pthread_mutex_unlock(&ps->mutex);+    return JS_UNDEFINED;+ fail:+    if (msg) {+        free(msg->data);+        free(msg->sab_tab);+        free(msg);+    }+    js_free(ctx, data);+    js_free(ctx, sab_tab);+    return JS_EXCEPTION;+    +}++static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val,+                                   JSValueConst func)+{+    JSRuntime *rt = JS_GetRuntime(ctx);+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);+    JSWorkerMessageHandler *port;+    +    if (!worker)+        return JS_EXCEPTION;++    port = worker->msg_handler;+    if (JS_IsNull(func)) {+        if (port) {+            js_free_port(rt, port);+            worker->msg_handler = NULL;+        }+    } else {+        if (!JS_IsFunction(ctx, func))+            return JS_ThrowTypeError(ctx, "not a function");+        if (!port) {+            port = js_mallocz(ctx, sizeof(*port));+            if (!port)+                return JS_EXCEPTION;+            port->recv_pipe = js_dup_message_pipe(worker->recv_pipe);+            port->on_message_func = JS_NULL;+            list_add_tail(&port->link, &ts->port_list);+            worker->msg_handler = port;+        }+        JS_FreeValue(ctx, port->on_message_func);+        port->on_message_func = JS_DupValue(ctx, func);+    }+    return JS_UNDEFINED;+}++static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val)+{+    JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);+    JSWorkerMessageHandler *port;+    if (!worker)+        return JS_EXCEPTION;+    port = worker->msg_handler;+    if (port) {+        return JS_DupValue(ctx, port->on_message_func);+    } else {+        return JS_NULL;+    }+}++static const JSCFunctionListEntry js_worker_proto_funcs[] = {+    JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ),+    JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ),+};++#endif /* USE_WORKER */++#if defined(_WIN32)+#define OS_PLATFORM "win32"+#elif defined(__APPLE__)+#define OS_PLATFORM "darwin"+#elif defined(EMSCRIPTEN)+#define OS_PLATFORM "js"+#else+#define OS_PLATFORM "linux"+#endif++#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE )++static const JSCFunctionListEntry js_os_funcs[] = {+    JS_CFUNC_DEF("open", 2, js_os_open ),+    OS_FLAG(O_RDONLY),+    OS_FLAG(O_WRONLY),+    OS_FLAG(O_RDWR),+    OS_FLAG(O_APPEND),+    OS_FLAG(O_CREAT),+    OS_FLAG(O_EXCL),+    OS_FLAG(O_TRUNC),+#if defined(_WIN32)+    OS_FLAG(O_BINARY),+    OS_FLAG(O_TEXT),+#endif+    JS_CFUNC_DEF("close", 1, js_os_close ),+    JS_CFUNC_DEF("seek", 3, js_os_seek ),+    JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ),+    JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ),+    JS_CFUNC_DEF("isatty", 1, js_os_isatty ),+    JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ),+    JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ),+    JS_CFUNC_DEF("remove", 1, js_os_remove ),+    JS_CFUNC_DEF("rename", 2, js_os_rename ),+    JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ),+    JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ),+    JS_CFUNC_DEF("signal", 2, js_os_signal ),+    OS_FLAG(SIGINT),+    OS_FLAG(SIGABRT),+    OS_FLAG(SIGFPE),+    OS_FLAG(SIGILL),+    OS_FLAG(SIGSEGV),+    OS_FLAG(SIGTERM),+#if !defined(_WIN32)+    OS_FLAG(SIGQUIT),+    OS_FLAG(SIGPIPE),+    OS_FLAG(SIGALRM),+    OS_FLAG(SIGUSR1),+    OS_FLAG(SIGUSR2),+    OS_FLAG(SIGCHLD),+    OS_FLAG(SIGCONT),+    OS_FLAG(SIGSTOP),+    OS_FLAG(SIGTSTP),+    OS_FLAG(SIGTTIN),+    OS_FLAG(SIGTTOU),+#endif+    JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),+    JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),+    JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),+    JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),+    JS_CFUNC_DEF("chdir", 0, js_os_chdir ),+    JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ),+    JS_CFUNC_DEF("readdir", 1, js_os_readdir ),+    /* st_mode constants */+    OS_FLAG(S_IFMT),+    OS_FLAG(S_IFIFO),+    OS_FLAG(S_IFCHR),+    OS_FLAG(S_IFDIR),+    OS_FLAG(S_IFBLK),+    OS_FLAG(S_IFREG),+#if !defined(_WIN32)+    OS_FLAG(S_IFSOCK),+    OS_FLAG(S_IFLNK),+    OS_FLAG(S_ISGID),+    OS_FLAG(S_ISUID),+#endif+    JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),+    JS_CFUNC_DEF("utimes", 3, js_os_utimes ),+#if !defined(_WIN32)+    JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ),+    JS_CFUNC_DEF("realpath", 1, js_os_realpath ),+    JS_CFUNC_DEF("symlink", 2, js_os_symlink ),+    JS_CFUNC_DEF("readlink", 1, js_os_readlink ),+    JS_CFUNC_DEF("exec", 1, js_os_exec ),+    JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),+    OS_FLAG(WNOHANG),+    JS_CFUNC_DEF("pipe", 0, js_os_pipe ),+    JS_CFUNC_DEF("kill", 2, js_os_kill ),+    JS_CFUNC_DEF("sleep", 1, js_os_sleep ),+    JS_CFUNC_DEF("dup", 1, js_os_dup ),+    JS_CFUNC_DEF("dup2", 2, js_os_dup2 ),+#endif+};++static int js_os_init(JSContext *ctx, JSModuleDef *m)+{+    os_poll_func = js_os_poll;+    +    /* OSTimer class */+    JS_NewClassID(&js_os_timer_class_id);+    JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);++#ifdef USE_WORKER+    {+        JSRuntime *rt = JS_GetRuntime(ctx);+        JSThreadState *ts = JS_GetRuntimeOpaque(rt);+        JSValue proto, obj;+        /* Worker class */+        JS_NewClassID(&js_worker_class_id);+        JS_NewClass(JS_GetRuntime(ctx), js_worker_class_id, &js_worker_class);+        proto = JS_NewObject(ctx);+        JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs));+        +        obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1,+                               JS_CFUNC_constructor, 0);+        JS_SetConstructor(ctx, obj, proto);+        +        JS_SetClassProto(ctx, js_worker_class_id, proto);+        +        /* set 'Worker.parent' if necessary */+        if (ts->recv_pipe && ts->send_pipe) {+            JS_DefinePropertyValueStr(ctx, obj, "parent",+                                      js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe),+                                      JS_PROP_C_W_E);+        }+        +        JS_SetModuleExport(ctx, m, "Worker", obj);+    }+#endif /* USE_WORKER */++    return JS_SetModuleExportList(ctx, m, js_os_funcs,+                                  countof(js_os_funcs));+}++JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name)+{+    JSModuleDef *m;+    m = JS_NewCModule(ctx, module_name, js_os_init);+    if (!m)+        return NULL;+    JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs));+#ifdef USE_WORKER+    JS_AddModuleExport(ctx, m, "Worker");+#endif+    return m;+}++/**********************************************************/++static JSValue js_print(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    int i;+    const char *str;+    size_t len;++    for(i = 0; i < argc; i++) {+        if (i != 0)+            putchar(' ');+        str = JS_ToCStringLen(ctx, &len, argv[i]);+        if (!str)+            return JS_EXCEPTION;+        fwrite(str, 1, len, stdout);+        JS_FreeCString(ctx, str);+    }+    putchar('\n');+    return JS_UNDEFINED;+}++void js_std_add_helpers(JSContext *ctx, int argc, char **argv)+{+    JSValue global_obj, console, args;+    int i;++    /* XXX: should these global definitions be enumerable? */+    global_obj = JS_GetGlobalObject(ctx);++    console = JS_NewObject(ctx);+    JS_SetPropertyStr(ctx, console, "log",+                      JS_NewCFunction(ctx, js_print, "log", 1));+    JS_SetPropertyStr(ctx, global_obj, "console", console);++    /* same methods as the mozilla JS shell */+    if (argc >= 0) {+        args = JS_NewArray(ctx);+        for(i = 0; i < argc; i++) {+            JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i]));+        }+        JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args);+    }+    +    JS_SetPropertyStr(ctx, global_obj, "print",+                      JS_NewCFunction(ctx, js_print, "print", 1));+    JS_SetPropertyStr(ctx, global_obj, "__loadScript",+                      JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1));+    +    JS_FreeValue(ctx, global_obj);+}++void js_std_init_handlers(JSRuntime *rt)+{+    JSThreadState *ts;++    ts = malloc(sizeof(*ts));+    if (!ts) {+        fprintf(stderr, "Could not allocate memory for the worker");+        exit(1);+    }+    memset(ts, 0, sizeof(*ts));+    init_list_head(&ts->os_rw_handlers);+    init_list_head(&ts->os_signal_handlers);+    init_list_head(&ts->os_timers);+    init_list_head(&ts->port_list);++    JS_SetRuntimeOpaque(rt, ts);++#ifdef USE_WORKER+    /* set the SharedArrayBuffer memory handlers */+    {+        JSSharedArrayBufferFunctions sf;+        memset(&sf, 0, sizeof(sf));+        sf.sab_alloc = js_sab_alloc;+        sf.sab_free = js_sab_free;+        sf.sab_dup = js_sab_dup;+        JS_SetSharedArrayBufferFunctions(rt, &sf);+    }+#endif+}++void js_std_free_handlers(JSRuntime *rt)+{+    JSThreadState *ts = JS_GetRuntimeOpaque(rt);+    struct list_head *el, *el1;++    list_for_each_safe(el, el1, &ts->os_rw_handlers) {+        JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link);+        free_rw_handler(rt, rh);+    }++    list_for_each_safe(el, el1, &ts->os_signal_handlers) {+        JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link);+        free_sh(rt, sh);+    }+    +    list_for_each_safe(el, el1, &ts->os_timers) {+        JSOSTimer *th = list_entry(el, JSOSTimer, link);+        unlink_timer(rt, th);+        if (!th->has_object)+            free_timer(rt, th);+    }++    free(ts);+    JS_SetRuntimeOpaque(rt, NULL); /* fail safe */+}++static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)+{+    const char *str;+    +    str = JS_ToCString(ctx, val);+    if (str) {+        fprintf(f, "%s\n", str);+        JS_FreeCString(ctx, str);+    } else {+        fprintf(f, "[exception]\n");+    }+}++static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val)+{+    JSValue val;+    BOOL is_error;+    +    is_error = JS_IsError(ctx, exception_val);+    js_dump_obj(ctx, stderr, exception_val);+    if (is_error) {+        val = JS_GetPropertyStr(ctx, exception_val, "stack");+        if (!JS_IsUndefined(val)) {+            js_dump_obj(ctx, stderr, val);+        }+        JS_FreeValue(ctx, val);+    }+}++void js_std_dump_error(JSContext *ctx)+{+    JSValue exception_val;+    +    exception_val = JS_GetException(ctx);+    js_std_dump_error1(ctx, exception_val);+    JS_FreeValue(ctx, exception_val);+}++void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,+                                      JSValueConst reason,+                                      BOOL is_handled, void *opaque)+{+    if (!is_handled) {+        fprintf(stderr, "Possibly unhandled promise rejection: ");+        js_std_dump_error1(ctx, reason);+    }+}++/* main loop which calls the user JS callbacks */+void js_std_loop(JSContext *ctx)+{+    JSContext *ctx1;+    int err;++    for(;;) {+        /* execute the pending jobs */+        for(;;) {+            err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);+            if (err <= 0) {+                if (err < 0) {+                    js_std_dump_error(ctx1);+                }+                break;+            }+        }++        if (!os_poll_func || os_poll_func(ctx))+            break;+    }+}++void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,+                        int load_only)+{+    JSValue obj, val;+    obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE);+    if (JS_IsException(obj))+        goto exception;+    if (load_only) {+        if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {+            js_module_set_import_meta(ctx, obj, FALSE, FALSE);+        }+    } else {+        if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {+            if (JS_ResolveModule(ctx, obj) < 0) {+                JS_FreeValue(ctx, obj);+                goto exception;+            }+            js_module_set_import_meta(ctx, obj, FALSE, TRUE);+        }+        val = JS_EvalFunction(ctx, obj);+        if (JS_IsException(val)) {+        exception:+            js_std_dump_error(ctx);+            exit(1);+        }+        JS_FreeValue(ctx, val);+    }+}
+ quickjs/quickjs-libc.h view
@@ -0,0 +1,58 @@+/*+ * QuickJS C library+ * + * Copyright (c) 2017-2018 Fabrice Bellard+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#ifndef QUICKJS_LIBC_H+#define QUICKJS_LIBC_H++#include <stdio.h>+#include <stdlib.h>++#include "quickjs.h"++#ifdef __cplusplus+extern "C" {+#endif++JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);+JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);+void js_std_add_helpers(JSContext *ctx, int argc, char **argv);+void js_std_loop(JSContext *ctx);+void js_std_init_handlers(JSRuntime *rt);+void js_std_free_handlers(JSRuntime *rt);+void js_std_dump_error(JSContext *ctx);+uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);+int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,+                              JS_BOOL use_realpath, JS_BOOL is_main);+JSModuleDef *js_module_loader(JSContext *ctx,+                              const char *module_name, void *opaque);+void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,+                        int flags);+void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,+                                      JSValueConst reason,+                                      JS_BOOL is_handled, void *opaque);++#ifdef __cplusplus+} /* extern "C" { */+#endif++#endif /* QUICKJS_LIBC_H */
+ quickjs/quickjs.c view
@@ -0,0 +1,53313 @@+/*+ * QuickJS Javascript Engine+ * + * Copyright (c) 2017-2020 Fabrice Bellard+ * Copyright (c) 2017-2020 Charlie Gordon+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#include <stdlib.h>+#include <stdio.h>+#include <stdarg.h>+#include <inttypes.h>+#include <string.h>+#include <assert.h>+#include <sys/time.h>+#include <time.h>+#include <fenv.h>+#include <math.h>+#if defined(__APPLE__)+#include <malloc/malloc.h>+#elif defined(__linux__)+#include <malloc.h>+#endif++#include "cutils.h"+#include "list.h"+#include "quickjs.h"+#include "libregexp.h"+#ifdef CONFIG_BIGNUM+#include "libbf.h"+#endif++#define OPTIMIZE         1+#define SHORT_OPCODES    1+#if defined(EMSCRIPTEN)+#define DIRECT_DISPATCH  0+#else+#define DIRECT_DISPATCH  1+#endif++#if defined(__APPLE__)+#define MALLOC_OVERHEAD  0+#else+#define MALLOC_OVERHEAD  8+#endif++#if !defined(_WIN32)+/* define it if printf uses the RNDN rounding mode instead of RNDNA */+#define CONFIG_PRINTF_RNDN+#endif++/* define to include Atomics.* operations which depend on the OS+   threads */+#if !defined(EMSCRIPTEN)+#define CONFIG_ATOMICS+#endif++#if !defined(EMSCRIPTEN)+/* enable stack limitation */+#define CONFIG_STACK_CHECK+#endif+++/* dump object free */+//#define DUMP_FREE+//#define DUMP_CLOSURE+/* dump the bytecode of the compiled functions: combination of bits+   1: dump pass 3 final byte code+   2: dump pass 2 code+   4: dump pass 1 code+   8: dump stdlib functions+  16: dump bytecode in hex+  32: dump line number table+ */+//#define DUMP_BYTECODE  (1)+/* dump the occurence of the automatic GC */+//#define DUMP_GC+/* dump objects freed by the garbage collector */+//#define DUMP_GC_FREE+/* dump objects leaking when freeing the runtime */+//#define DUMP_LEAKS  1+/* dump memory usage before running the garbage collector */+//#define DUMP_MEM+//#define DUMP_OBJECTS    /* dump objects in JS_FreeContext */+//#define DUMP_ATOMS      /* dump atoms in JS_FreeContext */+//#define DUMP_SHAPES     /* dump shapes in JS_FreeContext */+//#define DUMP_MODULE_RESOLVE+//#define DUMP_PROMISE+//#define DUMP_READ_OBJECT++/* test the GC by forcing it before each object allocation */+//#define FORCE_GC_AT_MALLOC++#ifdef CONFIG_ATOMICS+#include <pthread.h>+#include <stdatomic.h>+#include <errno.h>+#endif++enum {+    /* classid tag        */    /* union usage   | properties */+    JS_CLASS_OBJECT = 1,        /* must be first */+    JS_CLASS_ARRAY,             /* u.array       | length */+    JS_CLASS_ERROR,+    JS_CLASS_NUMBER,            /* u.object_data */+    JS_CLASS_STRING,            /* u.object_data */+    JS_CLASS_BOOLEAN,           /* u.object_data */+    JS_CLASS_SYMBOL,            /* u.object_data */+    JS_CLASS_ARGUMENTS,         /* u.array       | length */+    JS_CLASS_MAPPED_ARGUMENTS,  /*               | length */+    JS_CLASS_DATE,              /* u.object_data */+    JS_CLASS_MODULE_NS,+    JS_CLASS_C_FUNCTION,        /* u.cfunc */+    JS_CLASS_BYTECODE_FUNCTION, /* u.func */+    JS_CLASS_BOUND_FUNCTION,    /* u.bound_function */+    JS_CLASS_C_FUNCTION_DATA,   /* u.c_function_data_record */+    JS_CLASS_GENERATOR_FUNCTION, /* u.func */+    JS_CLASS_FOR_IN_ITERATOR,   /* u.for_in_iterator */+    JS_CLASS_REGEXP,            /* u.regexp */+    JS_CLASS_ARRAY_BUFFER,      /* u.array_buffer */+    JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */+    JS_CLASS_UINT8C_ARRAY,      /* u.array (typed_array) */+    JS_CLASS_INT8_ARRAY,        /* u.array (typed_array) */+    JS_CLASS_UINT8_ARRAY,       /* u.array (typed_array) */+    JS_CLASS_INT16_ARRAY,       /* u.array (typed_array) */+    JS_CLASS_UINT16_ARRAY,      /* u.array (typed_array) */+    JS_CLASS_INT32_ARRAY,       /* u.array (typed_array) */+    JS_CLASS_UINT32_ARRAY,      /* u.array (typed_array) */+#ifdef CONFIG_BIGNUM+    JS_CLASS_BIG_INT64_ARRAY,   /* u.array (typed_array) */+    JS_CLASS_BIG_UINT64_ARRAY,  /* u.array (typed_array) */+#endif+    JS_CLASS_FLOAT32_ARRAY,     /* u.array (typed_array) */+    JS_CLASS_FLOAT64_ARRAY,     /* u.array (typed_array) */+    JS_CLASS_DATAVIEW,          /* u.typed_array */+#ifdef CONFIG_BIGNUM+    JS_CLASS_BIG_INT,           /* u.object_data */+    JS_CLASS_BIG_FLOAT,         /* u.object_data */+    JS_CLASS_FLOAT_ENV,         /* u.float_env */+    JS_CLASS_BIG_DECIMAL,       /* u.object_data */+    JS_CLASS_OPERATOR_SET,      /* u.operator_set */+#endif+    JS_CLASS_MAP,               /* u.map_state */+    JS_CLASS_SET,               /* u.map_state */+    JS_CLASS_WEAKMAP,           /* u.map_state */+    JS_CLASS_WEAKSET,           /* u.map_state */+    JS_CLASS_MAP_ITERATOR,      /* u.map_iterator_data */+    JS_CLASS_SET_ITERATOR,      /* u.map_iterator_data */+    JS_CLASS_ARRAY_ITERATOR,    /* u.array_iterator_data */+    JS_CLASS_STRING_ITERATOR,   /* u.array_iterator_data */+    JS_CLASS_REGEXP_STRING_ITERATOR,   /* u.regexp_string_iterator_data */+    JS_CLASS_GENERATOR,         /* u.generator_data */+    JS_CLASS_PROXY,             /* u.proxy_data */+    JS_CLASS_PROMISE,           /* u.promise_data */+    JS_CLASS_PROMISE_RESOLVE_FUNCTION,  /* u.promise_function_data */+    JS_CLASS_PROMISE_REJECT_FUNCTION,   /* u.promise_function_data */+    JS_CLASS_ASYNC_FUNCTION,            /* u.func */+    JS_CLASS_ASYNC_FUNCTION_RESOLVE,    /* u.async_function_data */+    JS_CLASS_ASYNC_FUNCTION_REJECT,     /* u.async_function_data */+    JS_CLASS_ASYNC_FROM_SYNC_ITERATOR,  /* u.async_from_sync_iterator_data */+    JS_CLASS_ASYNC_GENERATOR_FUNCTION,  /* u.func */+    JS_CLASS_ASYNC_GENERATOR,   /* u.async_generator_data */++    JS_CLASS_INIT_COUNT, /* last entry for predefined classes */+};++/* number of typed array types */+#define JS_TYPED_ARRAY_COUNT  (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)+static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];+#define typed_array_size_log2(classid)  (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])++typedef enum JSErrorEnum {+    JS_EVAL_ERROR,+    JS_RANGE_ERROR,+    JS_REFERENCE_ERROR,+    JS_SYNTAX_ERROR,+    JS_TYPE_ERROR,+    JS_URI_ERROR,+    JS_INTERNAL_ERROR,+    JS_AGGREGATE_ERROR,+    +    JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */+} JSErrorEnum;++#define JS_MAX_LOCAL_VARS 65536+#define JS_STACK_SIZE_MAX 65536+#define JS_STRING_LEN_MAX ((1 << 30) - 1)++#define __exception __attribute__((warn_unused_result))++typedef struct JSShape JSShape;+typedef struct JSString JSString;+typedef struct JSString JSAtomStruct;++typedef enum {+    JS_GC_PHASE_NONE,+    JS_GC_PHASE_DECREF,+    JS_GC_PHASE_REMOVE_CYCLES,+} JSGCPhaseEnum;++typedef enum OPCodeEnum OPCodeEnum;++#ifdef CONFIG_BIGNUM+/* function pointers are used for numeric operations so that it is+   possible to remove some numeric types */+typedef struct {+    JSValue (*to_string)(JSContext *ctx, JSValueConst val);+    JSValue (*from_string)(JSContext *ctx, const char *buf,+                           int radix, int flags, slimb_t *pexponent);+    int (*unary_arith)(JSContext *ctx,+                       JSValue *pres, OPCodeEnum op, JSValue op1);+    int (*binary_arith)(JSContext *ctx, OPCodeEnum op,+                        JSValue *pres, JSValue op1, JSValue op2);+    int (*compare)(JSContext *ctx, OPCodeEnum op,+                   JSValue op1, JSValue op2);+    /* only for bigfloat: */+    JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,+                                    int64_t exponent);+    int (*mul_pow10)(JSContext *ctx, JSValue *sp);+} JSNumericOperations;+#endif++struct JSRuntime {+    JSMallocFunctions mf;+    JSMallocState malloc_state;+    const char *rt_info;++    int atom_hash_size; /* power of two */+    int atom_count;+    int atom_size;+    int atom_count_resize; /* resize hash table at this count */+    uint32_t *atom_hash;+    JSAtomStruct **atom_array;+    int atom_free_index; /* 0 = none */++    int class_count;    /* size of class_array */+    JSClass *class_array;++    struct list_head context_list; /* list of JSContext.link */+    /* list of JSGCObjectHeader.link. List of allocated GC objects (used+       by the garbage collector) */+    struct list_head gc_obj_list;+    /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */+    struct list_head gc_zero_ref_count_list; +    struct list_head tmp_obj_list; /* used during GC */+    JSGCPhaseEnum gc_phase : 8;+    size_t malloc_gc_threshold;+#ifdef DUMP_LEAKS+    struct list_head string_list; /* list of JSString.link */+#endif+    /* stack limitation */+    const uint8_t *stack_top;+    size_t stack_size; /* in bytes */++    JSValue current_exception;+    /* true if inside an out of memory error, to avoid recursing */+    BOOL in_out_of_memory : 8;++    struct JSStackFrame *current_stack_frame;++    JSInterruptHandler *interrupt_handler;+    void *interrupt_opaque;++    JSHostPromiseRejectionTracker *host_promise_rejection_tracker;+    void *host_promise_rejection_tracker_opaque;+    +    struct list_head job_list; /* list of JSJobEntry.link */++    JSModuleNormalizeFunc *module_normalize_func;+    JSModuleLoaderFunc *module_loader_func;+    void *module_loader_opaque;++    BOOL can_block : 8; /* TRUE if Atomics.wait can block */+    /* used to allocate, free and clone SharedArrayBuffers */+    JSSharedArrayBufferFunctions sab_funcs;+    +    /* Shape hash table */+    int shape_hash_bits;+    int shape_hash_size;+    int shape_hash_count; /* number of hashed shapes */+    JSShape **shape_hash;+#ifdef CONFIG_BIGNUM+    bf_context_t bf_ctx;+    JSNumericOperations bigint_ops;+    JSNumericOperations bigfloat_ops;+    JSNumericOperations bigdecimal_ops;+    uint32_t operator_count;+#endif+    void *user_opaque;+};++struct JSClass {+    uint32_t class_id; /* 0 means free entry */+    JSAtom class_name;+    JSClassFinalizer *finalizer;+    JSClassGCMark *gc_mark;+    JSClassCall *call;+    /* pointers for exotic behavior, can be NULL if none are present */+    const JSClassExoticMethods *exotic;+};++#define JS_MODE_STRICT (1 << 0)+#define JS_MODE_STRIP  (1 << 1)+#define JS_MODE_MATH   (1 << 2)++typedef struct JSStackFrame {+    struct JSStackFrame *prev_frame; /* NULL if first stack frame */+    JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */+    JSValue *arg_buf; /* arguments */+    JSValue *var_buf; /* variables */+    struct list_head var_ref_list; /* list of JSVarRef.link */+    const uint8_t *cur_pc; /* only used in bytecode functions : PC of the+                        instruction after the call */+    int arg_count;+    int js_mode; /* 0 or JS_MODE_MATH for C functions */+    /* only used in generators. Current stack pointer value. NULL if+       the function is running. */ +    JSValue *cur_sp;+} JSStackFrame;++typedef enum {+    JS_GC_OBJ_TYPE_JS_OBJECT,+    JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,+    JS_GC_OBJ_TYPE_SHAPE,+    JS_GC_OBJ_TYPE_VAR_REF,+    JS_GC_OBJ_TYPE_ASYNC_FUNCTION,+    JS_GC_OBJ_TYPE_JS_CONTEXT,+} JSGCObjectTypeEnum;++/* header for GC objects. GC objects are C data structures with a+   reference count that can reference other GC objects. JS Objects are+   a particular type of GC object. */+struct JSGCObjectHeader {+    int ref_count; /* must come first, 32-bit */+    JSGCObjectTypeEnum gc_obj_type : 4;+    uint8_t mark : 4; /* used by the GC */+    uint8_t dummy1; /* not used by the GC */+    uint16_t dummy2; /* not used by the GC */+    struct list_head link;+};++typedef struct JSVarRef {+    union {+        JSGCObjectHeader header; /* must come first */+        struct {+            int __gc_ref_count; /* corresponds to header.ref_count */+            uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */++            /* 0 : the JSVarRef is on the stack. header.link is an element+               of JSStackFrame.var_ref_list.+               1 : the JSVarRef is detached. header.link has the normal meanning +            */+            uint8_t is_detached : 1; +            uint8_t is_arg : 1;+            uint16_t var_idx; /* index of the corresponding function variable on+                                 the stack */+        };+    };+    JSValue *pvalue; /* pointer to the value, either on the stack or+                        to 'value' */+    JSValue value; /* used when the variable is no longer on the stack */+} JSVarRef;++#ifdef CONFIG_BIGNUM+typedef struct JSFloatEnv {+    limb_t prec;+    bf_flags_t flags;+    unsigned int status;+} JSFloatEnv;++/* the same structure is used for big integers and big floats. Big+   integers are never infinite or NaNs */+typedef struct JSBigFloat {+    JSRefCountHeader header; /* must come first, 32-bit */+    bf_t num;+} JSBigFloat;++typedef struct JSBigDecimal {+    JSRefCountHeader header; /* must come first, 32-bit */+    bfdec_t num;+} JSBigDecimal;+#endif++typedef enum {+    JS_AUTOINIT_ID_PROTOTYPE,+    JS_AUTOINIT_ID_MODULE_NS,+    JS_AUTOINIT_ID_PROP,+} JSAutoInitIDEnum;++/* must be large enough to have a negligible runtime cost and small+   enough to call the interrupt callback often. */+#define JS_INTERRUPT_COUNTER_INIT 10000++struct JSContext {+    JSGCObjectHeader header; /* must come first */+    JSRuntime *rt;+    struct list_head link;++    uint16_t binary_object_count;+    int binary_object_size;++    JSShape *array_shape;   /* initial shape for Array objects */++    JSValue *class_proto;+    JSValue function_proto;+    JSValue function_ctor;+    JSValue array_ctor;+    JSValue regexp_ctor;+    JSValue promise_ctor;+    JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];+    JSValue iterator_proto;+    JSValue async_iterator_proto;+    JSValue array_proto_values;+    JSValue throw_type_error;+    JSValue eval_obj;++    JSValue global_obj; /* global object */+    JSValue global_var_obj; /* contains the global let/const definitions */++    uint64_t random_state;+#ifdef CONFIG_BIGNUM+    bf_context_t *bf_ctx;   /* points to rt->bf_ctx, shared by all contexts */+    JSFloatEnv fp_env; /* global FP environment */+    BOOL bignum_ext : 8; /* enable math mode */+    BOOL allow_operator_overloading : 8;+#endif+    /* when the counter reaches zero, JSRutime.interrupt_handler is called */+    int interrupt_counter;+    BOOL is_error_property_enabled;++    struct list_head loaded_modules; /* list of JSModuleDef.link */++    /* if NULL, RegExp compilation is not supported */+    JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,+                              JSValueConst flags);+    /* if NULL, eval is not supported */+    JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,+                             const char *input, size_t input_len,+                             const char *filename, int flags, int scope_idx);+    void *user_opaque;+};++typedef union JSFloat64Union {+    double d;+    uint64_t u64;+    uint32_t u32[2];+} JSFloat64Union;++enum {+    JS_ATOM_TYPE_STRING = 1,+    JS_ATOM_TYPE_GLOBAL_SYMBOL,+    JS_ATOM_TYPE_SYMBOL,+    JS_ATOM_TYPE_PRIVATE,+};++enum {+    JS_ATOM_HASH_SYMBOL,+    JS_ATOM_HASH_PRIVATE,+};++typedef enum {+    JS_ATOM_KIND_STRING,+    JS_ATOM_KIND_SYMBOL,+    JS_ATOM_KIND_PRIVATE,+} JSAtomKindEnum;++#define JS_ATOM_HASH_MASK  ((1 << 30) - 1)++struct JSString {+    JSRefCountHeader header; /* must come first, 32-bit */+    uint32_t len : 31;+    uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */+    /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,+       for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3+       XXX: could change encoding to have one more bit in hash */+    uint32_t hash : 30;+    uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */+    uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */+#ifdef DUMP_LEAKS+    struct list_head link; /* string list */+#endif+    union {+        uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */+        uint16_t str16[0];+    } u;+};++typedef struct JSClosureVar {+    uint8_t is_local : 1;+    uint8_t is_arg : 1;+    uint8_t is_const : 1;+    uint8_t is_lexical : 1;+    uint8_t var_kind : 3; /* see JSVarKindEnum */+    /* 9 bits available */+    uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the+                    parent function. otherwise: index to a closure+                    variable of the parent function */+    JSAtom var_name;+} JSClosureVar;++typedef struct JSVarScope {+    int parent;  /* index into fd->scopes of the enclosing scope */+    int first;   /* index into fd->vars of the last variable in this scope */+} JSVarScope;++typedef enum {+    /* XXX: add more variable kinds here instead of using bit fields */+    JS_VAR_NORMAL,+    JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */+    JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator+                                 function declaration */+    JS_VAR_CATCH,+    JS_VAR_PRIVATE_FIELD,+    JS_VAR_PRIVATE_METHOD,+    JS_VAR_PRIVATE_GETTER,+    JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */+    JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */+} JSVarKindEnum;++typedef struct JSVarDef {+    JSAtom var_name;+    int scope_level;   /* index into fd->scopes of this variable lexical scope */+    int scope_next;    /* index into fd->vars of the next variable in the+                        * same or enclosing lexical scope */+    uint8_t is_func_var : 1; /* used for the function self reference */+    uint8_t is_const : 1;+    uint8_t is_lexical : 1;+    uint8_t is_captured : 1;+    uint8_t var_kind : 4; /* see JSVarKindEnum */+    /* only used during compilation: function pool index for lexical+       variables with var_kind =+       JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of+       the definition of the 'var' variables (they have scope_level =+       0) */+    int func_pool_or_scope_idx : 24; /* only used during compilation */+} JSVarDef;++/* for the encoding of the pc2line table */+#define PC2LINE_BASE     (-1)+#define PC2LINE_RANGE    5+#define PC2LINE_OP_FIRST 1+#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)++typedef enum JSFunctionKindEnum {+    JS_FUNC_NORMAL = 0,+    JS_FUNC_GENERATOR = (1 << 0),+    JS_FUNC_ASYNC = (1 << 1),+    JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),+} JSFunctionKindEnum;++typedef struct JSFunctionBytecode {+    JSGCObjectHeader header; /* must come first */+    uint8_t js_mode;+    uint8_t has_prototype : 1; /* true if a prototype field is necessary */+    uint8_t has_simple_parameter_list : 1;+    uint8_t is_derived_class_constructor : 1;+    /* true if home_object needs to be initialized */+    uint8_t need_home_object : 1;+    uint8_t func_kind : 2;+    uint8_t new_target_allowed : 1;+    uint8_t super_call_allowed : 1;+    uint8_t super_allowed : 1;+    uint8_t arguments_allowed : 1;+    uint8_t has_debug : 1;+    uint8_t backtrace_barrier : 1; /* stop backtrace on this function */+    uint8_t read_only_bytecode : 1;+    /* XXX: 4 bits available */+    uint8_t *byte_code_buf; /* (self pointer) */+    int byte_code_len;+    JSAtom func_name;+    JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */+    JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */+    uint16_t arg_count;+    uint16_t var_count;+    uint16_t defined_arg_count; /* for length function property */+    uint16_t stack_size; /* maximum stack size */+    JSContext *realm; /* function realm */+    JSValue *cpool; /* constant pool (self pointer) */+    int cpool_count;+    int closure_var_count;+    struct {+        /* debug info, move to separate structure to save memory? */+        JSAtom filename;+        int line_num;+        int source_len;+        int pc2line_len;+        uint8_t *pc2line_buf;+        char *source;+    } debug;+} JSFunctionBytecode;++typedef struct JSBoundFunction {+    JSValue func_obj;+    JSValue this_val;+    int argc;+    JSValue argv[0];+} JSBoundFunction;++typedef enum JSIteratorKindEnum {+    JS_ITERATOR_KIND_KEY,+    JS_ITERATOR_KIND_VALUE,+    JS_ITERATOR_KIND_KEY_AND_VALUE,+} JSIteratorKindEnum;++typedef struct JSForInIterator {+    JSValue obj;+    BOOL is_array;+    uint32_t array_length;+    uint32_t idx;+} JSForInIterator;++typedef struct JSRegExp {+    JSString *pattern;+    JSString *bytecode; /* also contains the flags */+} JSRegExp;++typedef struct JSProxyData {+    JSValue target;+    JSValue handler;+    uint8_t is_func;+    uint8_t is_revoked;+} JSProxyData;++typedef struct JSArrayBuffer {+    int byte_length; /* 0 if detached */+    uint8_t detached;+    uint8_t shared; /* if shared, the array buffer cannot be detached */+    uint8_t *data; /* NULL if detached */+    struct list_head array_list;+    void *opaque;+    JSFreeArrayBufferDataFunc *free_func;+} JSArrayBuffer;++typedef struct JSTypedArray {+    struct list_head link; /* link to arraybuffer */+    JSObject *obj; /* back pointer to the TypedArray/DataView object */+    JSObject *buffer; /* based array buffer */+    uint32_t offset; /* offset in the array buffer */+    uint32_t length; /* length in the array buffer */+} JSTypedArray;++typedef struct JSAsyncFunctionState {+    JSValue this_val; /* 'this' generator argument */+    int argc; /* number of function arguments */+    BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */+    JSStackFrame frame;+} JSAsyncFunctionState;++/* XXX: could use an object instead to avoid the+   JS_TAG_ASYNC_FUNCTION tag for the GC */+typedef struct JSAsyncFunctionData {+    JSGCObjectHeader header; /* must come first */+    JSValue resolving_funcs[2];+    BOOL is_active; /* true if the async function state is valid */+    JSAsyncFunctionState func_state;+} JSAsyncFunctionData;++typedef enum {+   /* binary operators */+   JS_OVOP_ADD,+   JS_OVOP_SUB,+   JS_OVOP_MUL,+   JS_OVOP_DIV,+   JS_OVOP_MOD,+   JS_OVOP_POW,+   JS_OVOP_OR,+   JS_OVOP_AND,+   JS_OVOP_XOR,+   JS_OVOP_SHL,+   JS_OVOP_SAR,+   JS_OVOP_SHR,+   JS_OVOP_EQ,+   JS_OVOP_LESS,++   JS_OVOP_BINARY_COUNT,+   /* unary operators */+   JS_OVOP_POS = JS_OVOP_BINARY_COUNT,+   JS_OVOP_NEG,+   JS_OVOP_INC,+   JS_OVOP_DEC,+   JS_OVOP_NOT,++   JS_OVOP_COUNT,+} JSOverloadableOperatorEnum;++typedef struct {+    uint32_t operator_index;+    JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */+} JSBinaryOperatorDefEntry;++typedef struct {+    int count;+    JSBinaryOperatorDefEntry *tab;+} JSBinaryOperatorDef;++typedef struct {+    uint32_t operator_counter;+    BOOL is_primitive; /* OperatorSet for a primitive type */+    /* NULL if no operator is defined */+    JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */+    JSBinaryOperatorDef left;+    JSBinaryOperatorDef right;+} JSOperatorSetData;++typedef struct JSReqModuleEntry {+    JSAtom module_name;+    JSModuleDef *module; /* used using resolution */+} JSReqModuleEntry;++typedef enum JSExportTypeEnum {+    JS_EXPORT_TYPE_LOCAL,+    JS_EXPORT_TYPE_INDIRECT,+} JSExportTypeEnum;++typedef struct JSExportEntry {+    union {+        struct {+            int var_idx; /* closure variable index */+            JSVarRef *var_ref; /* if != NULL, reference to the variable */+        } local; /* for local export */+        int req_module_idx; /* module for indirect export */+    } u;+    JSExportTypeEnum export_type;+    JSAtom local_name; /* '*' if export ns from. not used for local+                          export after compilation */+    JSAtom export_name; /* exported variable name */+} JSExportEntry;++typedef struct JSStarExportEntry {+    int req_module_idx; /* in req_module_entries */+} JSStarExportEntry;++typedef struct JSImportEntry {+    int var_idx; /* closure variable index */+    JSAtom import_name;+    int req_module_idx; /* in req_module_entries */+} JSImportEntry;++struct JSModuleDef {+    JSRefCountHeader header; /* must come first, 32-bit */+    JSAtom module_name;+    struct list_head link;++    JSReqModuleEntry *req_module_entries;+    int req_module_entries_count;+    int req_module_entries_size;++    JSExportEntry *export_entries;+    int export_entries_count;+    int export_entries_size;++    JSStarExportEntry *star_export_entries;+    int star_export_entries_count;+    int star_export_entries_size;++    JSImportEntry *import_entries;+    int import_entries_count;+    int import_entries_size;++    JSValue module_ns;+    JSValue func_obj; /* only used for JS modules */+    JSModuleInitFunc *init_func; /* only used for C modules */+    BOOL resolved : 8;+    BOOL func_created : 8;+    BOOL instantiated : 8;+    BOOL evaluated : 8;+    BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */+    /* true if evaluation yielded an exception. It is saved in+       eval_exception */+    BOOL eval_has_exception : 8; +    JSValue eval_exception;+    JSValue meta_obj; /* for import.meta */+};++typedef struct JSJobEntry {+    struct list_head link;+    JSContext *ctx;+    JSJobFunc *job_func;+    int argc;+    JSValue argv[0];+} JSJobEntry;++typedef struct JSProperty {+    union {+        JSValue value;      /* JS_PROP_NORMAL */+        struct {            /* JS_PROP_GETSET */+            JSObject *getter; /* NULL if undefined */+            JSObject *setter; /* NULL if undefined */+        } getset;+        JSVarRef *var_ref;  /* JS_PROP_VARREF */+        struct {            /* JS_PROP_AUTOINIT */+            /* in order to use only 2 pointers, we compress the realm+               and the init function pointer */+            uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)+                                       in the 2 low bits */+            void *opaque;+        } init;+    } u;+} JSProperty;++#define JS_PROP_INITIAL_SIZE 2+#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */+#define JS_ARRAY_INITIAL_SIZE 2++typedef struct JSShapeProperty {+    uint32_t hash_next : 26; /* 0 if last in list */+    uint32_t flags : 6;   /* JS_PROP_XXX */+    JSAtom atom; /* JS_ATOM_NULL = free property entry */+} JSShapeProperty;++struct JSShape {+    uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1+                                  before the start of the structure. */+    JSGCObjectHeader header;+    /* true if the shape is inserted in the shape hash table. If not,+       JSShape.hash is not valid */+    uint8_t is_hashed;+    /* If true, the shape may have small array index properties 'n' with 0+       <= n <= 2^31-1. If false, the shape is guaranteed not to have+       small array index properties */+    uint8_t has_small_array_index;+    uint32_t hash; /* current hash value */+    uint32_t prop_hash_mask;+    int prop_size; /* allocated properties */+    int prop_count; /* include deleted properties */+    int deleted_prop_count;+    JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */+    JSObject *proto;+    JSShapeProperty prop[0]; /* prop_size elements */+};++struct JSObject {+    union {+        JSGCObjectHeader header;+        struct {+            int __gc_ref_count; /* corresponds to header.ref_count */+            uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */+            +            uint8_t extensible : 1;+            uint8_t free_mark : 1; /* only used when freeing objects with cycles */+            uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */+            uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */+            uint8_t is_constructor : 1; /* TRUE if object is a constructor function */+            uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */+            uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */+            uint16_t class_id; /* see JS_CLASS_x */+        };+    };+    /* byte offsets: 16/24 */+    JSShape *shape; /* prototype and property names + flag */+    JSProperty *prop; /* array of properties */+    /* byte offsets: 24/40 */+    struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */+    /* byte offsets: 28/48 */+    union {+        void *opaque;+        struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */+        struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */+        struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */+        struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */+        struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */+#ifdef CONFIG_BIGNUM+        struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */+        struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */+#endif+        struct JSMapState *map_state;   /* JS_CLASS_MAP..JS_CLASS_WEAKSET */+        struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */+        struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */+        struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */+        struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */+        struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */+        struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */+        struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */+        struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */+        struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */+        struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */+        struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */+            /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */+            struct JSFunctionBytecode *function_bytecode;+            JSVarRef **var_refs;+            JSObject *home_object; /* for 'super' access */+        } func;+        struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */+            JSContext *realm;+            JSCFunctionType c_function;+            uint8_t length;+            uint8_t cproto;+            int16_t magic;+        } cfunc;+        /* array part for fast arrays and typed arrays */+        struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */+            union {+                uint32_t size;          /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */+                struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */+            } u1;+            union {+                JSValue *values;        /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ +                void *ptr;              /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */+                int8_t *int8_ptr;       /* JS_CLASS_INT8_ARRAY */+                uint8_t *uint8_ptr;     /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */+                int16_t *int16_ptr;     /* JS_CLASS_INT16_ARRAY */+                uint16_t *uint16_ptr;   /* JS_CLASS_UINT16_ARRAY */+                int32_t *int32_ptr;     /* JS_CLASS_INT32_ARRAY */+                uint32_t *uint32_ptr;   /* JS_CLASS_UINT32_ARRAY */+                int64_t *int64_ptr;     /* JS_CLASS_INT64_ARRAY */+                uint64_t *uint64_ptr;   /* JS_CLASS_UINT64_ARRAY */+                float *float_ptr;       /* JS_CLASS_FLOAT32_ARRAY */+                double *double_ptr;     /* JS_CLASS_FLOAT64_ARRAY */+            } u;+            uint32_t count; /* <= 2^31-1. 0 for a detached typed array */+        } array;    /* 12/20 bytes */+        JSRegExp regexp;    /* JS_CLASS_REGEXP: 8/16 bytes */+        JSValue object_data;    /* for JS_SetObjectData(): 8/16/16 bytes */+    } u;+    /* byte sizes: 40/48/72 */+};+enum {+    JS_ATOM_NULL,+#define DEF(name, str) JS_ATOM_ ## name,+#include "quickjs-atom.h"+#undef DEF+    JS_ATOM_END,+};+#define JS_ATOM_LAST_KEYWORD JS_ATOM_super+#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield++static const char js_atom_init[] =+#define DEF(name, str) str "\0"+#include "quickjs-atom.h"+#undef DEF+;++typedef enum OPCodeFormat {+#define FMT(f) OP_FMT_ ## f,+#define DEF(id, size, n_pop, n_push, f)+#include "quickjs-opcode.h"+#undef DEF+#undef FMT+} OPCodeFormat;++enum OPCodeEnum {+#define FMT(f)+#define DEF(id, size, n_pop, n_push, f) OP_ ## id,+#define def(id, size, n_pop, n_push, f)+#include "quickjs-opcode.h"+#undef def+#undef DEF+#undef FMT+    OP_COUNT, /* excluding temporary opcodes */+    /* temporary opcodes : overlap with the short opcodes */+    OP_TEMP_START = OP_nop + 1,+    OP___dummy = OP_TEMP_START - 1,+#define FMT(f)+#define DEF(id, size, n_pop, n_push, f)+#define def(id, size, n_pop, n_push, f) OP_ ## id,+#include "quickjs-opcode.h"+#undef def+#undef DEF+#undef FMT+    OP_TEMP_END,+};++static int JS_InitAtoms(JSRuntime *rt);+static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,+                               int atom_type);+static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);+static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);+static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,+                                  JSValueConst this_obj,+                                  int argc, JSValueConst *argv, int flags);+static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,+                                      JSValueConst this_obj,+                                      int argc, JSValueConst *argv, int flags);+static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,+                               JSValueConst this_obj, JSValueConst new_target,+                               int argc, JSValue *argv, int flags);+static JSValue JS_CallConstructorInternal(JSContext *ctx,+                                          JSValueConst func_obj,+                                          JSValueConst new_target,+                                          int argc, JSValue *argv, int flags);+static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,+                           int argc, JSValueConst *argv);+static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,+                             int argc, JSValueConst *argv);+static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,+                                            JSValue val);+static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,+                             JSValueConst val, int flags, int scope_idx);+JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);+static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);+static __maybe_unused void JS_DumpString(JSRuntime *rt,+                                                  const JSString *p);+static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);+static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);+static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,+                                                      JSValueConst val);+static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);+static __maybe_unused void JS_PrintValue(JSContext *ctx,+                                                  const char *str,+                                                  JSValueConst val);+static __maybe_unused void JS_DumpShapes(JSRuntime *rt);+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv, int magic);+static void js_array_finalizer(JSRuntime *rt, JSValue val);+static void js_array_mark(JSRuntime *rt, JSValueConst val,+                          JS_MarkFunc *mark_func);+static void js_object_data_finalizer(JSRuntime *rt, JSValue val);+static void js_object_data_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_c_function_finalizer(JSRuntime *rt, JSValue val);+static void js_c_function_mark(JSRuntime *rt, JSValueConst val,+                               JS_MarkFunc *mark_func);+static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);+static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);+static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_regexp_finalizer(JSRuntime *rt, JSValue val);+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);+static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);+static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_proxy_finalizer(JSRuntime *rt, JSValue val);+static void js_proxy_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_map_finalizer(JSRuntime *rt, JSValue val);+static void js_map_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_generator_finalizer(JSRuntime *rt, JSValue obj);+static void js_generator_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_promise_finalizer(JSRuntime *rt, JSValue val);+static void js_promise_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);+static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func);+#ifdef CONFIG_BIGNUM+static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);+static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,+                                 JS_MarkFunc *mark_func);+#endif+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);+static int JS_ToBoolFree(JSContext *ctx, JSValue val);+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);+static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,+                                 JSValueConst flags);+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,+                                              JSValue pattern, JSValue bc);+static void gc_decref(JSRuntime *rt);+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,+                        const JSClassDef *class_def, JSAtom name);++typedef enum JSStrictEqModeEnum {+    JS_EQ_STRICT,+    JS_EQ_SAME_VALUE,+    JS_EQ_SAME_VALUE_ZERO,+} JSStrictEqModeEnum;++static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,+                          JSStrictEqModeEnum eq_mode);+static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);+static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);+static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);+static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);+static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);+static JSProperty *add_property(JSContext *ctx,+                                JSObject *p, JSAtom prop, int prop_flags);+#ifdef CONFIG_BIGNUM+static void js_float_env_finalizer(JSRuntime *rt, JSValue val);+static JSValue JS_NewBigFloat(JSContext *ctx);+static inline bf_t *JS_GetBigFloat(JSValueConst val)+{+    JSBigFloat *p = JS_VALUE_GET_PTR(val);+    return &p->num;+}+static JSValue JS_NewBigDecimal(JSContext *ctx);+static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)+{+    JSBigDecimal *p = JS_VALUE_GET_PTR(val);+    return &p->num;+}+static JSValue JS_NewBigInt(JSContext *ctx);+static inline bf_t *JS_GetBigInt(JSValueConst val)+{+    JSBigFloat *p = JS_VALUE_GET_PTR(val);+    return &p->num;+}+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,+                                 BOOL convert_to_safe_integer);+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);+static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);+static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,+                                   BOOL allow_null_or_undefined);+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);+#endif+JSValue JS_ThrowOutOfMemory(JSContext *ctx);+static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);+static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,+                                   JSValueConst proto_val, BOOL throw_flag);+static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);+static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);+static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);+static int JS_CreateProperty(JSContext *ctx, JSObject *p,+                             JSAtom prop, JSValueConst val,+                             JSValueConst getter, JSValueConst setter,+                             int flags);+static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);+static void reset_weak_ref(JSRuntime *rt, JSObject *p);+static JSValue js_array_buffer_constructor3(JSContext *ctx,+                                            JSValueConst new_target,+                                            uint64_t len, JSClassID class_id,+                                            uint8_t *buf,+                                            JSFreeArrayBufferDataFunc *free_func,+                                            void *opaque, BOOL alloc_flag);+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);+static JSValue js_typed_array_constructor(JSContext *ctx,+                                          JSValueConst this_val,+                                          int argc, JSValueConst *argv,+                                          int classid);+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);+static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,+                             BOOL is_arg);+static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,+                                          JSValueConst this_obj,+                                          int argc, JSValueConst *argv,+                                          int flags);+static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);+static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,+                                           JS_MarkFunc *mark_func);+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,+                               const char *input, size_t input_len,+                               const char *filename, int flags, int scope_idx);+static void js_free_module_def(JSContext *ctx, JSModuleDef *m);+static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,+                               JS_MarkFunc *mark_func);+static JSValue js_import_meta(JSContext *ctx);+static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);+static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);+static JSValue js_new_promise_capability(JSContext *ctx,+                                         JSValue *resolving_funcs,+                                         JSValueConst ctor);+static __exception int perform_promise_then(JSContext *ctx,+                                            JSValueConst promise,+                                            JSValueConst *resolve_reject,+                                            JSValueConst *cap_resolving_funcs);+static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv, int magic);+static int js_string_compare(JSContext *ctx,+                             const JSString *p1, const JSString *p2);+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,+                               JSValue prop, JSValue val, int flags);+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,+                                     JSObject *p, JSAtom prop);+static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);+static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,+                            JS_MarkFunc *mark_func);+static void JS_AddIntrinsicBasicObjects(JSContext *ctx);+static void js_free_shape(JSRuntime *rt, JSShape *sh);+static void js_free_shape_null(JSRuntime *rt, JSShape *sh);+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,+                                   JSShapeProperty **pprs);+static int init_shape_hash(JSRuntime *rt);+static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,+                                       JSValueConst obj);+static __exception int js_get_length64(JSContext *ctx, int64_t *pres,+                                       JSValueConst obj);+static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,+                               JSValueConst array_arg);+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,+                              JSValue **arrpp, uint32_t *countp);+static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,+                                              JSValueConst sync_iter);+static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);+static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,+                                    JS_MarkFunc *mark_func);+static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,+                                       JSValueConst this_val,+                                       int argc, JSValueConst *argv, int flags);+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);+static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,+                          JSGCObjectTypeEnum type);+static void remove_gc_object(JSGCObjectHeader *h);+static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);+static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);+static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,+                                 void *opaque);+static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p,+                                          JSAtom atom, void *opaque);+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);++static const JSClassExoticMethods js_arguments_exotic_methods;+static const JSClassExoticMethods js_string_exotic_methods;+static const JSClassExoticMethods js_proxy_exotic_methods;+static const JSClassExoticMethods js_module_ns_exotic_methods;+static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;++static void js_trigger_gc(JSRuntime *rt, size_t size)+{+    BOOL force_gc;+#ifdef FORCE_GC_AT_MALLOC+    force_gc = TRUE;+#else+    force_gc = ((rt->malloc_state.malloc_size + size) >+                rt->malloc_gc_threshold);+#endif+    if (force_gc) {+#ifdef DUMP_GC+        printf("GC: size=%" PRIu64 "\n",+               (uint64_t)rt->malloc_state.malloc_size);+#endif+        JS_RunGC(rt);+        rt->malloc_gc_threshold = rt->malloc_state.malloc_size ++            (rt->malloc_state.malloc_size >> 1);+    }+}++static size_t js_malloc_usable_size_unknown(const void *ptr)+{+    return 0;+}++void *js_malloc_rt(JSRuntime *rt, size_t size)+{+    return rt->mf.js_malloc(&rt->malloc_state, size);+}++void js_free_rt(JSRuntime *rt, void *ptr)+{+    rt->mf.js_free(&rt->malloc_state, ptr);+}++void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)+{+    return rt->mf.js_realloc(&rt->malloc_state, ptr, size);+}++size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)+{+    return rt->mf.js_malloc_usable_size(ptr);+}++void *js_mallocz_rt(JSRuntime *rt, size_t size)+{+    void *ptr;+    ptr = js_malloc_rt(rt, size);+    if (!ptr)+        return NULL;+    return memset(ptr, 0, size);+}++#ifdef CONFIG_BIGNUM+/* called by libbf */+static void *js_bf_realloc(void *opaque, void *ptr, size_t size)+{+    JSRuntime *rt = opaque;+    return js_realloc_rt(rt, ptr, size);+}+#endif /* CONFIG_BIGNUM */++/* Throw out of memory in case of error */+void *js_malloc(JSContext *ctx, size_t size)+{+    void *ptr;+    ptr = js_malloc_rt(ctx->rt, size);+    if (unlikely(!ptr)) {+        JS_ThrowOutOfMemory(ctx);+        return NULL;+    }+    return ptr;+}++/* Throw out of memory in case of error */+void *js_mallocz(JSContext *ctx, size_t size)+{+    void *ptr;+    ptr = js_mallocz_rt(ctx->rt, size);+    if (unlikely(!ptr)) {+        JS_ThrowOutOfMemory(ctx);+        return NULL;+    }+    return ptr;+}++void js_free(JSContext *ctx, void *ptr)+{+    js_free_rt(ctx->rt, ptr);+}++/* Throw out of memory in case of error */+void *js_realloc(JSContext *ctx, void *ptr, size_t size)+{+    void *ret;+    ret = js_realloc_rt(ctx->rt, ptr, size);+    if (unlikely(!ret && size != 0)) {+        JS_ThrowOutOfMemory(ctx);+        return NULL;+    }+    return ret;+}++/* store extra allocated size in *pslack if successful */+void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)+{+    void *ret;+    ret = js_realloc_rt(ctx->rt, ptr, size);+    if (unlikely(!ret && size != 0)) {+        JS_ThrowOutOfMemory(ctx);+        return NULL;+    }+    if (pslack) {+        size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);+        *pslack = (new_size > size) ? new_size - size : 0;+    }+    return ret;+}++size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)+{+    return js_malloc_usable_size_rt(ctx->rt, ptr);+}++/* Throw out of memory exception in case of error */+char *js_strndup(JSContext *ctx, const char *s, size_t n)+{+    char *ptr;+    ptr = js_malloc(ctx, n + 1);+    if (ptr) {+        memcpy(ptr, s, n);+        ptr[n] = '\0';+    }+    return ptr;+}++char *js_strdup(JSContext *ctx, const char *str)+{+    return js_strndup(ctx, str, strlen(str));+}++static no_inline int js_realloc_array(JSContext *ctx, void **parray,+                                      int elem_size, int *psize, int req_size)+{+    int new_size;+    size_t slack;+    void *new_array;+    /* XXX: potential arithmetic overflow */+    new_size = max_int(req_size, *psize * 3 / 2);+    new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);+    if (!new_array)+        return -1;+    new_size += slack / elem_size;+    *psize = new_size;+    *parray = new_array;+    return 0;+}++/* resize the array and update its size if req_size > *psize */+static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,+                                  int *psize, int req_size)+{+    if (unlikely(req_size > *psize))+        return js_realloc_array(ctx, parray, elem_size, psize, req_size);+    else+        return 0;+}++static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)+{+    dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);+}++static inline int is_digit(int c) {+    return c >= '0' && c <= '9';+}++typedef struct JSClassShortDef {+    JSAtom class_name;+    JSClassFinalizer *finalizer;+    JSClassGCMark *gc_mark;+} JSClassShortDef;++static JSClassShortDef const js_std_class_def[] = {+    { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_OBJECT */+    { JS_ATOM_Array, js_array_finalizer, js_array_mark },       /* JS_CLASS_ARRAY */+    { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */+    { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */+    { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */+    { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */+    { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */+    { JS_ATOM_Arguments, js_array_finalizer, js_array_mark },   /* JS_CLASS_ARGUMENTS */+    { JS_ATOM_Arguments, NULL, NULL },                          /* JS_CLASS_MAPPED_ARGUMENTS */+    { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */+    { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_MODULE_NS */+    { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */+    { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */+    { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */+    { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */+    { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_GENERATOR_FUNCTION */+    { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark },      /* JS_CLASS_FOR_IN_ITERATOR */+    { JS_ATOM_RegExp, js_regexp_finalizer, NULL },                              /* JS_CLASS_REGEXP */+    { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL },                   /* JS_CLASS_ARRAY_BUFFER */+    { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL },             /* JS_CLASS_SHARED_ARRAY_BUFFER */+    { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */+    { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark },       /* JS_CLASS_INT8_ARRAY */+    { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_UINT8_ARRAY */+    { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT16_ARRAY */+    { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT16_ARRAY */+    { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT32_ARRAY */+    { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT32_ARRAY */+#ifdef CONFIG_BIGNUM+    { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark },   /* JS_CLASS_BIG_INT64_ARRAY */+    { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark },  /* JS_CLASS_BIG_UINT64_ARRAY */+#endif+    { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT32_ARRAY */+    { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT64_ARRAY */+    { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark },        /* JS_CLASS_DATAVIEW */+#ifdef CONFIG_BIGNUM+    { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark },      /* JS_CLASS_BIG_INT */+    { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_FLOAT */+    { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL },      /* JS_CLASS_FLOAT_ENV */+    { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_DECIMAL */+    { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark },    /* JS_CLASS_OPERATOR_SET */+#endif+    { JS_ATOM_Map, js_map_finalizer, js_map_mark },             /* JS_CLASS_MAP */+    { JS_ATOM_Set, js_map_finalizer, js_map_mark },             /* JS_CLASS_SET */+    { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKMAP */+    { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKSET */+    { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */+    { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */+    { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */+    { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */+    { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */+    { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */+};++static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,+                            int start, int count)+{+    JSClassDef cm_s, *cm = &cm_s;+    int i, class_id;++    for(i = 0; i < count; i++) {+        class_id = i + start;+        memset(cm, 0, sizeof(*cm));+        cm->finalizer = tab[i].finalizer;+        cm->gc_mark = tab[i].gc_mark;+        if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)+            return -1;+    }+    return 0;+}++#ifdef CONFIG_BIGNUM+static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)+{+    return JS_ThrowTypeError(ctx, "unsupported operation");+}++static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)+{+    return JS_ThrowUnsupportedOperation(ctx);+}++static JSValue invalid_from_string(JSContext *ctx, const char *buf,+                                   int radix, int flags, slimb_t *pexponent)+{+    return JS_NAN;+}++static int invalid_unary_arith(JSContext *ctx,+                               JSValue *pres, OPCodeEnum op, JSValue op1)+{+    JS_FreeValue(ctx, op1);+    JS_ThrowUnsupportedOperation(ctx);+    return -1;+}++static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,+                                JSValue *pres, JSValue op1, JSValue op2)+{+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    JS_ThrowUnsupportedOperation(ctx);+    return -1;+}++static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,+                                            int64_t exponent)+{+    return JS_ThrowUnsupportedOperation(ctx);+}++static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)+{+    JS_ThrowUnsupportedOperation(ctx);+    return -1;+}++static void set_dummy_numeric_ops(JSNumericOperations *ops)+{+    ops->to_string = invalid_to_string;+    ops->from_string = invalid_from_string;+    ops->unary_arith = invalid_unary_arith;+    ops->binary_arith = invalid_binary_arith;+    ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;+    ops->mul_pow10 = invalid_mul_pow10;+}++#endif /* CONFIG_BIGNUM */++#if !defined(CONFIG_STACK_CHECK)+/* no stack limitation */+static inline uint8_t *js_get_stack_pointer(void)+{+    return NULL;+}++static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)+{+    return FALSE;+}+#else+/* Note: OS and CPU dependent */+static inline uint8_t *js_get_stack_pointer(void)+{+    return __builtin_frame_address(0);+}++static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)+{+    size_t size;+    size = rt->stack_top - js_get_stack_pointer();+    return unlikely((size + alloca_size) > rt->stack_size);+}+#endif++JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)+{+    JSRuntime *rt;+    JSMallocState ms;++    memset(&ms, 0, sizeof(ms));+    ms.opaque = opaque;+    ms.malloc_limit = -1;++    rt = mf->js_malloc(&ms, sizeof(JSRuntime));+    if (!rt)+        return NULL;+    memset(rt, 0, sizeof(*rt));+    rt->mf = *mf;+    if (!rt->mf.js_malloc_usable_size) {+        /* use dummy function if none provided */+        rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;+    }+    rt->malloc_state = ms;+    rt->malloc_gc_threshold = 256 * 1024;++#ifdef CONFIG_BIGNUM+    bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);+    set_dummy_numeric_ops(&rt->bigint_ops);+    set_dummy_numeric_ops(&rt->bigfloat_ops);+    set_dummy_numeric_ops(&rt->bigdecimal_ops);+#endif++    init_list_head(&rt->context_list);+    init_list_head(&rt->gc_obj_list);+    init_list_head(&rt->gc_zero_ref_count_list);+    rt->gc_phase = JS_GC_PHASE_NONE;+    +#ifdef DUMP_LEAKS+    init_list_head(&rt->string_list);+#endif+    init_list_head(&rt->job_list);++    if (JS_InitAtoms(rt))+        goto fail;++    /* create the object, array and function classes */+    if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,+                         countof(js_std_class_def)) < 0)+        goto fail;+    rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;+    rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;+    rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;++    rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;+    rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;+    rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;+    rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;+    if (init_shape_hash(rt))+        goto fail;++    rt->stack_top = js_get_stack_pointer();+    rt->stack_size = JS_DEFAULT_STACK_SIZE;+    rt->current_exception = JS_NULL;++    return rt;+ fail:+    JS_FreeRuntime(rt);+    return NULL;+}++void *JS_GetRuntimeOpaque(JSRuntime *rt)+{+    return rt->user_opaque;+}++void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)+{+    rt->user_opaque = opaque;+}++/* default memory allocation functions with memory limitation */+static inline size_t js_def_malloc_usable_size(void *ptr)+{+#if defined(__APPLE__)+    return malloc_size(ptr);+#elif defined(_WIN32)+    return _msize(ptr);+#elif defined(EMSCRIPTEN)+    return 0;+#elif defined(__linux__)+    return malloc_usable_size(ptr);+#else+    /* change this to `return 0;` if compilation fails */+    return malloc_usable_size(ptr);+#endif+}++static void *js_def_malloc(JSMallocState *s, size_t size)+{+    void *ptr;++    /* Do not allocate zero bytes: behavior is platform dependent */+    assert(size != 0);++    if (unlikely(s->malloc_size + size > s->malloc_limit))+        return NULL;++    ptr = malloc(size);+    if (!ptr)+        return NULL;++    s->malloc_count++;+    s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;+    return ptr;+}++static void js_def_free(JSMallocState *s, void *ptr)+{+    if (!ptr)+        return;++    s->malloc_count--;+    s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;+    free(ptr);+}++static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)+{+    size_t old_size;++    if (!ptr) {+        if (size == 0)+            return NULL;+        return js_def_malloc(s, size);+    }+    old_size = js_def_malloc_usable_size(ptr);+    if (size == 0) {+        s->malloc_count--;+        s->malloc_size -= old_size + MALLOC_OVERHEAD;+        free(ptr);+        return NULL;+    }+    if (s->malloc_size + size - old_size > s->malloc_limit)+        return NULL;++    ptr = realloc(ptr, size);+    if (!ptr)+        return NULL;++    s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;+    return ptr;+}++static const JSMallocFunctions def_malloc_funcs = {+    js_def_malloc,+    js_def_free,+    js_def_realloc,+#if defined(__APPLE__)+    malloc_size,+#elif defined(_WIN32)+    (size_t (*)(const void *))_msize,+#elif defined(EMSCRIPTEN)+    NULL,+#elif defined(__linux__)+    (size_t (*)(const void *))malloc_usable_size,+#else+    /* change this to `NULL,` if compilation fails */+    malloc_usable_size,+#endif+};++JSRuntime *JS_NewRuntime(void)+{+    return JS_NewRuntime2(&def_malloc_funcs, NULL);+}++void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)+{+    rt->malloc_state.malloc_limit = limit;+}++/* use -1 to disable automatic GC */+void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)+{+    rt->malloc_gc_threshold = gc_threshold;+}++#define malloc(s) malloc_is_forbidden(s)+#define free(p) free_is_forbidden(p)+#define realloc(p,s) realloc_is_forbidden(p,s)++void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)+{+    rt->interrupt_handler = cb;+    rt->interrupt_opaque = opaque;+}++void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)+{+    rt->can_block = can_block;+}++void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,+                                      const JSSharedArrayBufferFunctions *sf)+{+    rt->sab_funcs = *sf;+}++/* return 0 if OK, < 0 if exception */+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,+                  int argc, JSValueConst *argv)+{+    JSRuntime *rt = ctx->rt;+    JSJobEntry *e;+    int i;++    e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));+    if (!e)+        return -1;+    e->ctx = ctx;+    e->job_func = job_func;+    e->argc = argc;+    for(i = 0; i < argc; i++) {+        e->argv[i] = JS_DupValue(ctx, argv[i]);+    }+    list_add_tail(&e->link, &rt->job_list);+    return 0;+}++BOOL JS_IsJobPending(JSRuntime *rt)+{+    return !list_empty(&rt->job_list);+}++/* return < 0 if exception, 0 if no job pending, 1 if a job was+   executed successfully. the context of the job is stored in '*pctx' */+int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)+{+    JSContext *ctx;+    JSJobEntry *e;+    JSValue res;+    int i, ret;++    if (list_empty(&rt->job_list)) {+        *pctx = NULL;+        return 0;+    }++    /* get the first pending job and execute it */+    e = list_entry(rt->job_list.next, JSJobEntry, link);+    list_del(&e->link);+    ctx = e->ctx;+    res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);+    for(i = 0; i < e->argc; i++)+        JS_FreeValue(ctx, e->argv[i]);+    if (JS_IsException(res))+        ret = -1;+    else+        ret = 1;+    JS_FreeValue(ctx, res);+    js_free(ctx, e);+    *pctx = ctx;+    return ret;+}++static inline uint32_t atom_get_free(const JSAtomStruct *p)+{+    return (uintptr_t)p >> 1;+}++static inline BOOL atom_is_free(const JSAtomStruct *p)+{+    return (uintptr_t)p & 1;+}++static inline JSAtomStruct *atom_set_free(uint32_t v)+{+    return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);+}++/* Note: the string contents are uninitialized */+static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)+{+    JSString *str;+    str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);+    if (unlikely(!str))+        return NULL;+    str->header.ref_count = 1;+    str->is_wide_char = is_wide_char;+    str->len = max_len;+    str->atom_type = 0;+    str->hash = 0;          /* optional but costless */+    str->hash_next = 0;     /* optional */+#ifdef DUMP_LEAKS+    list_add_tail(&str->link, &rt->string_list);+#endif+    return str;+}++static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)+{+    JSString *p;+    p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);+    if (unlikely(!p)) {+        JS_ThrowOutOfMemory(ctx);+        return NULL;+    }+    return p;+}++/* same as JS_FreeValueRT() but faster */+static inline void js_free_string(JSRuntime *rt, JSString *str)+{+    if (--str->header.ref_count <= 0) {+        if (str->atom_type) {+            JS_FreeAtomStruct(rt, str);+        } else {+#ifdef DUMP_LEAKS+            list_del(&str->link);+#endif+            js_free_rt(rt, str);+        }+    }+}++void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)+{+    if (rt)+        rt->rt_info = s;+}++void JS_FreeRuntime(JSRuntime *rt)+{+    struct list_head *el, *el1;+    int i;++    JS_FreeValueRT(rt, rt->current_exception);++    list_for_each_safe(el, el1, &rt->job_list) {+        JSJobEntry *e = list_entry(el, JSJobEntry, link);+        for(i = 0; i < e->argc; i++)+            JS_FreeValueRT(rt, e->argv[i]);+        js_free_rt(rt, e);+    }+    init_list_head(&rt->job_list);++    JS_RunGC(rt);++#ifdef DUMP_LEAKS+    /* leaking objects */+    {+        BOOL header_done;+        JSGCObjectHeader *p;+        int count;++        /* remove the internal refcounts to display only the object+           referenced externally */+        list_for_each(el, &rt->gc_obj_list) {+            p = list_entry(el, JSGCObjectHeader, link);+            p->mark = 0;+        }+        gc_decref(rt);++        header_done = FALSE;+        list_for_each(el, &rt->gc_obj_list) {+            p = list_entry(el, JSGCObjectHeader, link);+            if (p->ref_count != 0) {+                if (!header_done) {+                    printf("Object leaks:\n");+                    JS_DumpObjectHeader(rt);+                    header_done = TRUE;+                }+                JS_DumpGCObject(rt, p);+            }+        }++        count = 0;+        list_for_each(el, &rt->gc_obj_list) {+            p = list_entry(el, JSGCObjectHeader, link);+            if (p->ref_count == 0) {+                count++;+            }+        }+        if (count != 0)+            printf("Secondary object leaks: %d\n", count);+    }+#endif+    assert(list_empty(&rt->gc_obj_list));++    /* free the classes */+    for(i = 0; i < rt->class_count; i++) {+        JSClass *cl = &rt->class_array[i];+        if (cl->class_id != 0) {+            JS_FreeAtomRT(rt, cl->class_name);+        }+    }+    js_free_rt(rt, rt->class_array);++#ifdef CONFIG_BIGNUM+    bf_context_end(&rt->bf_ctx);+#endif++#ifdef DUMP_LEAKS+    /* only the atoms defined in JS_InitAtoms() should be left */+    {+        BOOL header_done = FALSE;++        for(i = 0; i < rt->atom_size; i++) {+            JSAtomStruct *p = rt->atom_array[i];+            if (!atom_is_free(p) /* && p->str*/) {+                if (i >= JS_ATOM_END || p->header.ref_count != 1) {+                    if (!header_done) {+                        header_done = TRUE;+                        if (rt->rt_info) {+                            printf("%s:1: atom leakage:", rt->rt_info);+                        } else {+                            printf("Atom leaks:\n"+                                   "    %6s %6s %s\n",+                                   "ID", "REFCNT", "NAME");+                        }+                    }+                    if (rt->rt_info) {+                        printf(" ");+                    } else {+                        printf("    %6u %6u ", i, p->header.ref_count);+                    }+                    switch (p->atom_type) {+                    case JS_ATOM_TYPE_STRING:+                        JS_DumpString(rt, p);+                        break;+                    case JS_ATOM_TYPE_GLOBAL_SYMBOL:+                        printf("Symbol.for(");+                        JS_DumpString(rt, p);+                        printf(")");+                        break;+                    case JS_ATOM_TYPE_SYMBOL:+                        if (p->hash == JS_ATOM_HASH_SYMBOL) {+                            printf("Symbol(");+                            JS_DumpString(rt, p);+                            printf(")");+                        } else {+                            printf("Private(");+                            JS_DumpString(rt, p);+                            printf(")");+                        }+                        break;+                    }+                    if (rt->rt_info) {+                        printf(":%u", p->header.ref_count);+                    } else {+                        printf("\n");+                    }+                }+            }+        }+        if (rt->rt_info && header_done)+            printf("\n");+    }+#endif++    /* free the atoms */+    for(i = 0; i < rt->atom_size; i++) {+        JSAtomStruct *p = rt->atom_array[i];+        if (!atom_is_free(p)) {+#ifdef DUMP_LEAKS+            list_del(&p->link);+#endif+            js_free_rt(rt, p);+        }+    }+    js_free_rt(rt, rt->atom_array);+    js_free_rt(rt, rt->atom_hash);+    js_free_rt(rt, rt->shape_hash);+#ifdef DUMP_LEAKS+    if (!list_empty(&rt->string_list)) {+        if (rt->rt_info) {+            printf("%s:1: string leakage:", rt->rt_info);+        } else {+            printf("String leaks:\n"+                   "    %6s %s\n",+                   "REFCNT", "VALUE");+        }+        list_for_each_safe(el, el1, &rt->string_list) {+            JSString *str = list_entry(el, JSString, link);+            if (rt->rt_info) {+                printf(" ");+            } else {+                printf("    %6u ", str->header.ref_count);+            }+            JS_DumpString(rt, str);+            if (rt->rt_info) {+                printf(":%u", str->header.ref_count);+            } else {+                printf("\n");+            }+            list_del(&str->link);+            js_free_rt(rt, str);+        }+        if (rt->rt_info)+            printf("\n");+    }+    {+        JSMallocState *s = &rt->malloc_state;+        if (s->malloc_count > 1) {+            if (rt->rt_info)+                printf("%s:1: ", rt->rt_info);+            printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",+                   (uint64_t)(s->malloc_size - sizeof(JSRuntime)),+                   (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);+        }+    }+#endif++    {+        JSMallocState ms = rt->malloc_state;+        rt->mf.js_free(&ms, rt);+    }+}++JSContext *JS_NewContextRaw(JSRuntime *rt)+{+    JSContext *ctx;+    int i;++    ctx = js_mallocz_rt(rt, sizeof(JSContext));+    if (!ctx)+        return NULL;+    ctx->header.ref_count = 1;+    add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);++    ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *+                                    rt->class_count);+    if (!ctx->class_proto) {+        js_free_rt(rt, ctx);+        return NULL;+    }+    ctx->rt = rt;+    list_add_tail(&ctx->link, &rt->context_list);+#ifdef CONFIG_BIGNUM+    ctx->bf_ctx = &rt->bf_ctx;+    ctx->fp_env.prec = 113;+    ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;+#endif+    for(i = 0; i < rt->class_count; i++)+        ctx->class_proto[i] = JS_NULL;+    ctx->array_ctor = JS_NULL;+    ctx->regexp_ctor = JS_NULL;+    ctx->promise_ctor = JS_NULL;+    init_list_head(&ctx->loaded_modules);++    JS_AddIntrinsicBasicObjects(ctx);+    return ctx;+}++JSContext *JS_NewContext(JSRuntime *rt)+{+    JSContext *ctx;++    ctx = JS_NewContextRaw(rt);+    if (!ctx)+        return NULL;++    JS_AddIntrinsicBaseObjects(ctx);+    JS_AddIntrinsicDate(ctx);+    JS_AddIntrinsicEval(ctx);+    JS_AddIntrinsicStringNormalize(ctx);+    JS_AddIntrinsicRegExp(ctx);+    JS_AddIntrinsicJSON(ctx);+    JS_AddIntrinsicProxy(ctx);+    JS_AddIntrinsicMapSet(ctx);+    JS_AddIntrinsicTypedArrays(ctx);+    JS_AddIntrinsicPromise(ctx);+#ifdef CONFIG_BIGNUM+    JS_AddIntrinsicBigInt(ctx);+#endif+    return ctx;+}++void *JS_GetContextOpaque(JSContext *ctx)+{+    return ctx->user_opaque;+}++void JS_SetContextOpaque(JSContext *ctx, void *opaque)+{+    ctx->user_opaque = opaque;+}++/* set the new value and free the old value after (freeing the value+   can reallocate the object data) */+static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)+{+    JSValue old_val;+    old_val = *pval;+    *pval = new_val;+    JS_FreeValue(ctx, old_val);+}++void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)+{+    JSRuntime *rt = ctx->rt;+    assert(class_id < rt->class_count);+    set_value(ctx, &ctx->class_proto[class_id], obj);+}++JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)+{+    JSRuntime *rt = ctx->rt;+    assert(class_id < rt->class_count);+    return JS_DupValue(ctx, ctx->class_proto[class_id]);+}++typedef enum JSFreeModuleEnum {+    JS_FREE_MODULE_ALL,+    JS_FREE_MODULE_NOT_RESOLVED,+    JS_FREE_MODULE_NOT_EVALUATED,+} JSFreeModuleEnum;++/* XXX: would be more efficient with separate module lists */+static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)+{+    struct list_head *el, *el1;+    list_for_each_safe(el, el1, &ctx->loaded_modules) {+        JSModuleDef *m = list_entry(el, JSModuleDef, link);+        if (flag == JS_FREE_MODULE_ALL ||+            (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||+            (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {+            js_free_module_def(ctx, m);+        }+    }+}++JSContext *JS_DupContext(JSContext *ctx)+{+    ctx->header.ref_count++;+    return ctx;+}++/* used by the GC */+static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,+                           JS_MarkFunc *mark_func)+{+    int i;+    struct list_head *el;++    /* modules are not seen by the GC, so we directly mark the objects+       referenced by each module */+    list_for_each(el, &ctx->loaded_modules) {+        JSModuleDef *m = list_entry(el, JSModuleDef, link);+        js_mark_module_def(rt, m, mark_func);+    }++    JS_MarkValue(rt, ctx->global_obj, mark_func);+    JS_MarkValue(rt, ctx->global_var_obj, mark_func);++    JS_MarkValue(rt, ctx->throw_type_error, mark_func);+    JS_MarkValue(rt, ctx->eval_obj, mark_func);++    JS_MarkValue(rt, ctx->array_proto_values, mark_func);+    for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+        JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);+    }+    for(i = 0; i < rt->class_count; i++) {+        JS_MarkValue(rt, ctx->class_proto[i], mark_func);+    }+    JS_MarkValue(rt, ctx->iterator_proto, mark_func);+    JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);+    JS_MarkValue(rt, ctx->promise_ctor, mark_func);+    JS_MarkValue(rt, ctx->array_ctor, mark_func);+    JS_MarkValue(rt, ctx->regexp_ctor, mark_func);+    JS_MarkValue(rt, ctx->function_ctor, mark_func);+    JS_MarkValue(rt, ctx->function_proto, mark_func);++    if (ctx->array_shape)+        mark_func(rt, &ctx->array_shape->header);+}++void JS_FreeContext(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    int i;++    if (--ctx->header.ref_count > 0)+        return;+    assert(ctx->header.ref_count == 0);+    +#ifdef DUMP_ATOMS+    JS_DumpAtoms(ctx->rt);+#endif+#ifdef DUMP_SHAPES+    JS_DumpShapes(ctx->rt);+#endif+#ifdef DUMP_OBJECTS+    {+        struct list_head *el;+        JSGCObjectHeader *p;+        printf("JSObjects: {\n");+        JS_DumpObjectHeader(ctx->rt);+        list_for_each(el, &rt->gc_obj_list) {+            p = list_entry(el, JSGCObjectHeader, link);+            JS_DumpGCObject(rt, p);+        }+        printf("}\n");+    }+#endif+#ifdef DUMP_MEM+    {+        JSMemoryUsage stats;+        JS_ComputeMemoryUsage(rt, &stats);+        JS_DumpMemoryUsage(stdout, &stats, rt);+    }+#endif++    js_free_modules(ctx, JS_FREE_MODULE_ALL);++    JS_FreeValue(ctx, ctx->global_obj);+    JS_FreeValue(ctx, ctx->global_var_obj);++    JS_FreeValue(ctx, ctx->throw_type_error);+    JS_FreeValue(ctx, ctx->eval_obj);++    JS_FreeValue(ctx, ctx->array_proto_values);+    for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+        JS_FreeValue(ctx, ctx->native_error_proto[i]);+    }+    for(i = 0; i < rt->class_count; i++) {+        JS_FreeValue(ctx, ctx->class_proto[i]);+    }+    js_free_rt(rt, ctx->class_proto);+    JS_FreeValue(ctx, ctx->iterator_proto);+    JS_FreeValue(ctx, ctx->async_iterator_proto);+    JS_FreeValue(ctx, ctx->promise_ctor);+    JS_FreeValue(ctx, ctx->array_ctor);+    JS_FreeValue(ctx, ctx->regexp_ctor);+    JS_FreeValue(ctx, ctx->function_ctor);+    JS_FreeValue(ctx, ctx->function_proto);++    js_free_shape_null(ctx->rt, ctx->array_shape);++    list_del(&ctx->link);+    remove_gc_object(&ctx->header);+    js_free_rt(ctx->rt, ctx);+}++JSRuntime *JS_GetRuntime(JSContext *ctx)+{+    return ctx->rt;+}++void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)+{+    rt->stack_size = stack_size;+}++static inline BOOL is_strict_mode(JSContext *ctx)+{+    JSStackFrame *sf = ctx->rt->current_stack_frame;+    return (sf && (sf->js_mode & JS_MODE_STRICT));+}++#ifdef CONFIG_BIGNUM+static inline BOOL is_math_mode(JSContext *ctx)+{+    JSStackFrame *sf = ctx->rt->current_stack_frame;+    return (sf && (sf->js_mode & JS_MODE_MATH));+}+#endif++/* JSAtom support */++#define JS_ATOM_TAG_INT (1U << 31)+#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)+#define JS_ATOM_MAX     ((1U << 30) - 1)++/* return the max count from the hash size */+#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)++static inline BOOL __JS_AtomIsConst(JSAtom v)+{+#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1+        return (int32_t)v <= 0;+#else+        return (int32_t)v < JS_ATOM_END;+#endif+}++static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)+{+    return (v & JS_ATOM_TAG_INT) != 0;+}++static inline JSAtom __JS_AtomFromUInt32(uint32_t v)+{+    return v | JS_ATOM_TAG_INT;+}++static inline uint32_t __JS_AtomToUInt32(JSAtom atom)+{+    return atom & ~JS_ATOM_TAG_INT;+}++static inline int is_num(int c)+{+    return c >= '0' && c <= '9';+}++/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */+static inline BOOL is_num_string(uint32_t *pval, const JSString *p)+{+    uint32_t n;+    uint64_t n64;+    int c, i, len;++    len = p->len;+    if (len == 0 || len > 10)+        return FALSE;+    if (p->is_wide_char)+        c = p->u.str16[0];+    else+        c = p->u.str8[0];+    if (is_num(c)) {+        if (c == '0') {+            if (len != 1)+                return FALSE;+            n = 0;+        } else {+            n = c - '0';+            for(i = 1; i < len; i++) {+                if (p->is_wide_char)+                    c = p->u.str16[i];+                else+                    c = p->u.str8[i];+                if (!is_num(c))+                    return FALSE;+                n64 = (uint64_t)n * 10 + (c - '0');+                if ((n64 >> 32) != 0)+                    return FALSE;+                n = n64;+            }+        }+        *pval = n;+        return TRUE;+    } else {+        return FALSE;+    }+}++/* XXX: could use faster version ? */+static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)+{+    size_t i;++    for(i = 0; i < len; i++)+        h = h * 263 + str[i];+    return h;+}++static inline uint32_t hash_string16(const uint16_t *str,+                                     size_t len, uint32_t h)+{+    size_t i;++    for(i = 0; i < len; i++)+        h = h * 263 + str[i];+    return h;+}++static uint32_t hash_string(const JSString *str, uint32_t h)+{+    if (str->is_wide_char)+        h = hash_string16(str->u.str16, str->len, h);+    else+        h = hash_string8(str->u.str8, str->len, h);+    return h;+}++static __maybe_unused void JS_DumpString(JSRuntime *rt,+                                                  const JSString *p)+{+    int i, c, sep;++    if (p == NULL) {+        printf("<null>");+        return;+    }+    printf("%d", p->header.ref_count);+    sep = (p->header.ref_count == 1) ? '\"' : '\'';+    putchar(sep);+    for(i = 0; i < p->len; i++) {+        if (p->is_wide_char)+            c = p->u.str16[i];+        else+            c = p->u.str8[i];+        if (c == sep || c == '\\') {+            putchar('\\');+            putchar(c);+        } else if (c >= ' ' && c <= 126) {+            putchar(c);+        } else if (c == '\n') {+            putchar('\\');+            putchar('n');+        } else {+            printf("\\u%04x", c);+        }+    }+    putchar(sep);+}++static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)+{+    JSAtomStruct *p;+    int h, i;+    /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */+    printf("JSAtom count=%d size=%d hash_size=%d:\n",+           rt->atom_count, rt->atom_size, rt->atom_hash_size);+    printf("JSAtom hash table: {\n");+    for(i = 0; i < rt->atom_hash_size; i++) {+        h = rt->atom_hash[i];+        if (h) {+            printf("  %d:", i);+            while (h) {+                p = rt->atom_array[h];+                printf(" ");+                JS_DumpString(rt, p);+                h = p->hash_next;+            }+            printf("\n");+        }+    }+    printf("}\n");+    printf("JSAtom table: {\n");+    for(i = 0; i < rt->atom_size; i++) {+        p = rt->atom_array[i];+        if (!atom_is_free(p)) {+            printf("  %d: { %d %08x ", i, p->atom_type, p->hash);+            if (!(p->len == 0 && p->is_wide_char != 0))+                JS_DumpString(rt, p);+            printf(" %d }\n", p->hash_next);+        }+    }+    printf("}\n");+}++static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)+{+    JSAtomStruct *p;+    uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;++    assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */+    new_hash_mask = new_hash_size - 1;+    new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);+    if (!new_hash)+        return -1;+    for(i = 0; i < rt->atom_hash_size; i++) {+        h = rt->atom_hash[i];+        while (h != 0) {+            p = rt->atom_array[h];+            hash_next1 = p->hash_next;+            /* add in new hash table */+            j = p->hash & new_hash_mask;+            p->hash_next = new_hash[j];+            new_hash[j] = h;+            h = hash_next1;+        }+    }+    js_free_rt(rt, rt->atom_hash);+    rt->atom_hash = new_hash;+    rt->atom_hash_size = new_hash_size;+    rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);+    //    JS_DumpAtoms(rt);+    return 0;+}++static int JS_InitAtoms(JSRuntime *rt)+{+    int i, len, atom_type;+    const char *p;++    rt->atom_hash_size = 0;+    rt->atom_hash = NULL;+    rt->atom_count = 0;+    rt->atom_size = 0;+    rt->atom_free_index = 0;+    if (JS_ResizeAtomHash(rt, 256))     /* there are at least 195 predefined atoms */+        return -1;++    p = js_atom_init;+    for(i = 1; i < JS_ATOM_END; i++) {+        if (i == JS_ATOM_Private_brand)+            atom_type = JS_ATOM_TYPE_PRIVATE;+        else if (i >= JS_ATOM_Symbol_toPrimitive)+            atom_type = JS_ATOM_TYPE_SYMBOL;+        else+            atom_type = JS_ATOM_TYPE_STRING;+        len = strlen(p);+        if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)+            return -1;+        p = p + len + 1;+    }+    return 0;+}++static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)+{+    JSAtomStruct *p;++    if (!__JS_AtomIsConst(v)) {+        p = rt->atom_array[v];+        p->header.ref_count++;+    }+    return v;+}++JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)+{+    JSRuntime *rt;+    JSAtomStruct *p;++    if (!__JS_AtomIsConst(v)) {+        rt = ctx->rt;+        p = rt->atom_array[v];+        p->header.ref_count++;+    }+    return v;+}++static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)+{+    JSRuntime *rt;+    JSAtomStruct *p;++    rt = ctx->rt;+    if (__JS_AtomIsTaggedInt(v))+        return JS_ATOM_KIND_STRING;+    p = rt->atom_array[v];+    switch(p->atom_type) {+    case JS_ATOM_TYPE_STRING:+        return JS_ATOM_KIND_STRING;+    case JS_ATOM_TYPE_GLOBAL_SYMBOL:+        return JS_ATOM_KIND_SYMBOL;+    case JS_ATOM_TYPE_SYMBOL:+        switch(p->hash) {+        case JS_ATOM_HASH_SYMBOL:+            return JS_ATOM_KIND_SYMBOL;+        case JS_ATOM_HASH_PRIVATE:+            return JS_ATOM_KIND_PRIVATE;+        default:+            abort();+        }+    default:+        abort();+    }+}++static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)+{+    return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;+}++static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)+{+    uint32_t i = p->hash_next;  /* atom_index */+    if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {+        JSAtomStruct *p1;++        i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];+        p1 = rt->atom_array[i];+        while (p1 != p) {+            assert(i != 0);+            i = p1->hash_next;+            p1 = rt->atom_array[i];+        }+    }+    return i;+}++/* string case (internal). Return JS_ATOM_NULL if error. 'str' is+   freed. */+static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)+{+    uint32_t h, h1, i;+    JSAtomStruct *p;+    int len;++#if 0+    printf("__JS_NewAtom: ");  JS_DumpString(rt, str); printf("\n");+#endif+    if (atom_type < JS_ATOM_TYPE_SYMBOL) {+        /* str is not NULL */+        if (str->atom_type == atom_type) {+            /* str is the atom, return its index */+            i = js_get_atom_index(rt, str);+            /* reduce string refcount and increase atom's unless constant */+            if (__JS_AtomIsConst(i))+                str->header.ref_count--;+            return i;+        }+        /* try and locate an already registered atom */+        len = str->len;+        h = hash_string(str, atom_type);+        h &= JS_ATOM_HASH_MASK;+        h1 = h & (rt->atom_hash_size - 1);+        i = rt->atom_hash[h1];+        while (i != 0) {+            p = rt->atom_array[i];+            if (p->hash == h &&+                p->atom_type == atom_type &&+                p->len == len &&+                js_string_memcmp(p, str, len) == 0) {+                if (!__JS_AtomIsConst(i))+                    p->header.ref_count++;+                goto done;+            }+            i = p->hash_next;+        }+    } else {+        h1 = 0; /* avoid warning */+        if (atom_type == JS_ATOM_TYPE_SYMBOL) {+            h = JS_ATOM_HASH_SYMBOL;+        } else {+            h = JS_ATOM_HASH_PRIVATE;+            atom_type = JS_ATOM_TYPE_SYMBOL;+        }+    }++    if (rt->atom_free_index == 0) {+        /* allow new atom entries */+        uint32_t new_size, start;+        JSAtomStruct **new_array;++        /* alloc new with size progression 3/2:+           4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092+           preallocating space for predefined atoms (at least 195).+         */+        new_size = max_int(211, rt->atom_size * 3 / 2);+        if (new_size > JS_ATOM_MAX)+            goto fail;+        /* XXX: should use realloc2 to use slack space */+        new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);+        if (!new_array)+            goto fail;+        /* Note: the atom 0 is not used */+        start = rt->atom_size;+        if (start == 0) {+            /* JS_ATOM_NULL entry */+            p = js_mallocz_rt(rt, sizeof(JSAtomStruct));+            if (!p) {+                js_free_rt(rt, new_array);+                goto fail;+            }+            p->header.ref_count = 1;  /* not refcounted */+            p->atom_type = JS_ATOM_TYPE_SYMBOL;+#ifdef DUMP_LEAKS+            list_add_tail(&p->link, &rt->string_list);+#endif+            new_array[0] = p;+            rt->atom_count++;+            start = 1;+        }+        rt->atom_size = new_size;+        rt->atom_array = new_array;+        rt->atom_free_index = start;+        for(i = start; i < new_size; i++) {+            uint32_t next;+            if (i == (new_size - 1))+                next = 0;+            else+                next = i + 1;+            rt->atom_array[i] = atom_set_free(next);+        }+    }++    if (str) {+        if (str->atom_type == 0) {+            p = str;+            p->atom_type = atom_type;+        } else {+            p = js_malloc_rt(rt, sizeof(JSString) ++                             (str->len << str->is_wide_char) ++                             1 - str->is_wide_char);+            if (unlikely(!p))+                goto fail;+            p->header.ref_count = 1;+            p->is_wide_char = str->is_wide_char;+            p->len = str->len;+#ifdef DUMP_LEAKS+            list_add_tail(&p->link, &rt->string_list);+#endif+            memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) ++                   1 - str->is_wide_char);+            js_free_string(rt, str);+        }+    } else {+        p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */+        if (!p)+            return JS_ATOM_NULL;+        p->header.ref_count = 1;+        p->is_wide_char = 1;    /* Hack to represent NULL as a JSString */+        p->len = 0;+#ifdef DUMP_LEAKS+        list_add_tail(&p->link, &rt->string_list);+#endif+    }++    /* use an already free entry */+    i = rt->atom_free_index;+    rt->atom_free_index = atom_get_free(rt->atom_array[i]);+    rt->atom_array[i] = p;++    p->hash = h;+    p->hash_next = i;   /* atom_index */+    p->atom_type = atom_type;++    rt->atom_count++;++    if (atom_type != JS_ATOM_TYPE_SYMBOL) {+        p->hash_next = rt->atom_hash[h1];+        rt->atom_hash[h1] = i;+        if (unlikely(rt->atom_count >= rt->atom_count_resize))+            JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);+    }++    //    JS_DumpAtoms(rt);+    return i;++ fail:+    i = JS_ATOM_NULL;+ done:+    if (str)+        js_free_string(rt, str);+    return i;+}++/* only works with zero terminated 8 bit strings */+static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,+                               int atom_type)+{+    JSString *p;+    p = js_alloc_string_rt(rt, len, 0);+    if (!p)+        return JS_ATOM_NULL;+    memcpy(p->u.str8, str, len);+    p->u.str8[len] = '\0';+    return __JS_NewAtom(rt, p, atom_type);+}++static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,+                            int atom_type)+{+    uint32_t h, h1, i;+    JSAtomStruct *p;++    h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);+    h &= JS_ATOM_HASH_MASK;+    h1 = h & (rt->atom_hash_size - 1);+    i = rt->atom_hash[h1];+    while (i != 0) {+        p = rt->atom_array[i];+        if (p->hash == h &&+            p->atom_type == JS_ATOM_TYPE_STRING &&+            p->len == len &&+            p->is_wide_char == 0 &&+            memcmp(p->u.str8, str, len) == 0) {+            if (!__JS_AtomIsConst(i))+                p->header.ref_count++;+            return i;+        }+        i = p->hash_next;+    }+    return JS_ATOM_NULL;+}++static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)+{+#if 0   /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */+    if (unlikely(i == JS_ATOM_NULL)) {+        p->header.ref_count = INT32_MAX / 2;+        return;+    }+#endif+    uint32_t i = p->hash_next;  /* atom_index */+    if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {+        JSAtomStruct *p0, *p1;+        uint32_t h0;++        h0 = p->hash & (rt->atom_hash_size - 1);+        i = rt->atom_hash[h0];+        p1 = rt->atom_array[i];+        if (p1 == p) {+            rt->atom_hash[h0] = p1->hash_next;+        } else {+            for(;;) {+                assert(i != 0);+                p0 = p1;+                i = p1->hash_next;+                p1 = rt->atom_array[i];+                if (p1 == p) {+                    p0->hash_next = p1->hash_next;+                    break;+                }+            }+        }+    }+    /* insert in free atom list */+    rt->atom_array[i] = atom_set_free(rt->atom_free_index);+    rt->atom_free_index = i;+    /* free the string structure */+#ifdef DUMP_LEAKS+    list_del(&p->link);+#endif+    js_free_rt(rt, p);+    rt->atom_count--;+    assert(rt->atom_count >= 0);+}++static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)+{+    JSAtomStruct *p;++    p = rt->atom_array[i];+    if (--p->header.ref_count > 0)+        return;+    JS_FreeAtomStruct(rt, p);+}++/* Warning: 'p' is freed */+static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)+{+    JSRuntime *rt = ctx->rt;+    uint32_t n;+    if (is_num_string(&n, p)) {+        if (n <= JS_ATOM_MAX_INT) {+            js_free_string(rt, p);+            return __JS_AtomFromUInt32(n);+        }+    }+    /* XXX: should generate an exception */+    return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);+}++JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)+{+    JSValue val;++    if (len == 0 || !is_digit(*str)) {+        JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);+        if (atom)+            return atom;+    }+    val = JS_NewStringLen(ctx, str, len);+    if (JS_IsException(val))+        return JS_ATOM_NULL;+    return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));+}++JSAtom JS_NewAtom(JSContext *ctx, const char *str)+{+    return JS_NewAtomLen(ctx, str, strlen(str));+}++JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)+{+    if (n <= JS_ATOM_MAX_INT) {+        return __JS_AtomFromUInt32(n);+    } else {+        char buf[11];+        JSValue val;+        snprintf(buf, sizeof(buf), "%u", n);+        val = JS_NewString(ctx, buf);+        if (JS_IsException(val))+            return JS_ATOM_NULL;+        return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),+                            JS_ATOM_TYPE_STRING);+    }+}++static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)+{+    if ((uint64_t)n <= JS_ATOM_MAX_INT) {+        return __JS_AtomFromUInt32((uint32_t)n);+    } else {+        char buf[24];+        JSValue val;+        snprintf(buf, sizeof(buf), "%" PRId64 , n);+        val = JS_NewString(ctx, buf);+        if (JS_IsException(val))+            return JS_ATOM_NULL;+        return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),+                            JS_ATOM_TYPE_STRING);+    }+}++/* 'p' is freed */+static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)+{+    JSRuntime *rt = ctx->rt;+    JSAtom atom;+    atom = __JS_NewAtom(rt, p, atom_type);+    if (atom == JS_ATOM_NULL)+        return JS_ThrowOutOfMemory(ctx);+    return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);+}++/* descr must be a non-numeric string atom */+static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,+                                    int atom_type)+{+    JSRuntime *rt = ctx->rt;+    JSString *p;++    assert(!__JS_AtomIsTaggedInt(descr));+    assert(descr < rt->atom_size);+    p = rt->atom_array[descr];+    JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+    return JS_NewSymbol(ctx, p, atom_type);+}++#define ATOM_GET_STR_BUF_SIZE 64++/* Should only be used for debug. */+static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,+                                   JSAtom atom)+{+    if (__JS_AtomIsTaggedInt(atom)) {+        snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));+    } else {+        JSAtomStruct *p;+        assert(atom < rt->atom_size);+        if (atom == JS_ATOM_NULL) {+            snprintf(buf, buf_size, "<null>");+        } else {+            int i, c;+            char *q;+            JSString *str;++            q = buf;+            p = rt->atom_array[atom];+            assert(!atom_is_free(p));+            str = p;+            if (str) {+                if (!str->is_wide_char) {+                    /* special case ASCII strings */+                    c = 0;+                    for(i = 0; i < str->len; i++) {+                        c |= str->u.str8[i];+                    }+                    if (c < 0x80)+                        return (const char *)str->u.str8;+                }+                for(i = 0; i < str->len; i++) {+                    if (str->is_wide_char)+                        c = str->u.str16[i];+                    else+                        c = str->u.str8[i];+                    if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)+                        break;+                    if (c < 128) {+                        *q++ = c;+                    } else {+                        q += unicode_to_utf8((uint8_t *)q, c);+                    }+                }+            }+            *q = '\0';+        }+    }+    return buf;+}++static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)+{+    return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);+}++static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)+{+    char buf[ATOM_GET_STR_BUF_SIZE];++    if (__JS_AtomIsTaggedInt(atom)) {+        snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));+        return JS_NewString(ctx, buf);+    } else {+        JSRuntime *rt = ctx->rt;+        JSAtomStruct *p;+        assert(atom < rt->atom_size);+        p = rt->atom_array[atom];+        if (p->atom_type == JS_ATOM_TYPE_STRING) {+            goto ret_string;+        } else if (force_string) {+            if (p->len == 0 && p->is_wide_char != 0) {+                /* no description string */+                p = rt->atom_array[JS_ATOM_empty_string];+            }+        ret_string:+            return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+        } else {+            return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));+        }+    }+}++JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)+{+    return __JS_AtomToValue(ctx, atom, FALSE);+}++JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)+{+    return __JS_AtomToValue(ctx, atom, TRUE);+}++/* return TRUE if the atom is an array index (i.e. 0 <= index <=+   2^32-2 and return its value */+static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)+{+    if (__JS_AtomIsTaggedInt(atom)) {+        *pval = __JS_AtomToUInt32(atom);+        return TRUE;+    } else {+        JSRuntime *rt = ctx->rt;+        JSAtomStruct *p;+        uint32_t val;++        assert(atom < rt->atom_size);+        p = rt->atom_array[atom];+        if (p->atom_type == JS_ATOM_TYPE_STRING &&+            is_num_string(&val, p) && val != -1) {+            *pval = val;+            return TRUE;+        } else {+            *pval = 0;+            return FALSE;+        }+    }+}++/* This test must be fast if atom is not a numeric index (e.g. a+   method name). Return JS_UNDEFINED if not a numeric+   index. JS_EXCEPTION can also be returned. */+static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)+{+    JSRuntime *rt = ctx->rt;+    JSAtomStruct *p1;+    JSString *p;+    int c, len, ret;+    JSValue num, str;++    if (__JS_AtomIsTaggedInt(atom))+        return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));+    assert(atom < rt->atom_size);+    p1 = rt->atom_array[atom];+    if (p1->atom_type != JS_ATOM_TYPE_STRING)+        return JS_UNDEFINED;+    p = p1;+    len = p->len;+    if (p->is_wide_char) {+        const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;+        if (r >= r_end)+            return JS_UNDEFINED;+        c = *r;+        if (c == '-') {+            if (r >= r_end)+                return JS_UNDEFINED;+            r++;+            c = *r;+            /* -0 case is specific */+            if (c == '0' && len == 2)+                goto minus_zero;+        }+        /* XXX: should test NaN, but the tests do not check it */+        if (!is_num(c)) {+            /* XXX: String should be normalized, therefore 8-bit only */+            const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };+            if (!(c =='I' && (r_end - r) == 8 &&+                  !memcmp(r + 1, nfinity16, sizeof(nfinity16))))+                return JS_UNDEFINED;+        }+    } else {+        const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;+        if (r >= r_end)+            return JS_UNDEFINED;+        c = *r;+        if (c == '-') {+            if (r >= r_end)+                return JS_UNDEFINED;+            r++;+            c = *r;+            /* -0 case is specific */+            if (c == '0' && len == 2) {+            minus_zero:+                return __JS_NewFloat64(ctx, -0.0);+            }+        }+        if (!is_num(c)) {+            if (!(c =='I' && (r_end - r) == 8 &&+                  !memcmp(r + 1, "nfinity", 7)))+                return JS_UNDEFINED;+        }+    }+    /* XXX: bignum: would be better to only accept integer to avoid+       relying on current floating point precision */+    /* this is ECMA CanonicalNumericIndexString primitive */+    num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));+    if (JS_IsException(num))+        return num;+    str = JS_ToString(ctx, num);+    if (JS_IsException(str)) {+        JS_FreeValue(ctx, num);+        return str;+    }+    ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));+    JS_FreeValue(ctx, str);+    if (ret == 0) {+        return num;+    } else {+        JS_FreeValue(ctx, num);+        return JS_UNDEFINED;+    }+}++/* return -1 if exception or TRUE/FALSE */+static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)+{+    JSValue num;+    num = JS_AtomIsNumericIndex1(ctx, atom);+    if (likely(JS_IsUndefined(num)))+        return FALSE;+    if (JS_IsException(num))+        return -1;+    JS_FreeValue(ctx, num);+    return TRUE;+}++void JS_FreeAtom(JSContext *ctx, JSAtom v)+{+    if (!__JS_AtomIsConst(v))+        __JS_FreeAtom(ctx->rt, v);+}++void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)+{+    if (!__JS_AtomIsConst(v))+        __JS_FreeAtom(rt, v);+}++/* return TRUE if 'v' is a symbol with a string description */+static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)+{+    JSRuntime *rt;+    JSAtomStruct *p;++    rt = ctx->rt;+    if (__JS_AtomIsTaggedInt(v))+        return FALSE;+    p = rt->atom_array[v];+    return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&+              p->hash == JS_ATOM_HASH_SYMBOL) ||+             p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&+            !(p->len == 0 && p->is_wide_char != 0));+}++static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)+{+    char buf[ATOM_GET_STR_BUF_SIZE];+    const char *p;+    int i;++    /* XXX: should handle embedded null characters */+    /* XXX: should move encoding code to JS_AtomGetStr */+    p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);+    for (i = 0; p[i]; i++) {+        int c = (unsigned char)p[i];+        if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||+              (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))+            break;+    }+    if (i > 0 && p[i] == '\0') {+        printf("%s", p);+    } else {+        putchar('"');+        printf("%.*s", i, p);+        for (; p[i]; i++) {+            int c = (unsigned char)p[i];+            if (c == '\"' || c == '\\') {+                putchar('\\');+                putchar(c);+            } else if (c >= ' ' && c <= 126) {+                putchar(c);+            } else if (c == '\n') {+                putchar('\\');+                putchar('n');+            } else {+                printf("\\u%04x", c);+            }+        }+        putchar('\"');+    }+}++/* free with JS_FreeCString() */+const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)+{+    JSValue str;+    const char *cstr;++    str = JS_AtomToString(ctx, atom);+    if (JS_IsException(str))+        return NULL;+    cstr = JS_ToCString(ctx, str);+    JS_FreeValue(ctx, str);+    return cstr;+}++/* return a string atom containing name concatenated with str1 */+static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)+{+    JSValue str;+    JSAtom atom;+    const char *cstr;+    char *cstr2;+    size_t len, len1;+    +    str = JS_AtomToString(ctx, name);+    if (JS_IsException(str))+        return JS_ATOM_NULL;+    cstr = JS_ToCStringLen(ctx, &len, str);+    if (!cstr)+        goto fail;+    len1 = strlen(str1);+    cstr2 = js_malloc(ctx, len + len1 + 1);+    if (!cstr2)+        goto fail;+    memcpy(cstr2, cstr, len);+    memcpy(cstr2 + len, str1, len1);+    cstr2[len + len1] = '\0';+    atom = JS_NewAtomLen(ctx, cstr2, len + len1);+    js_free(ctx, cstr2);+    JS_FreeCString(ctx, cstr);+    JS_FreeValue(ctx, str);+    return atom;+ fail:+    JS_FreeCString(ctx, cstr);+    JS_FreeValue(ctx, str);+    return JS_ATOM_NULL;+}++static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)+{+    char buf[16];+    snprintf(buf, sizeof(buf), "%u", n);+    return js_atom_concat_str(ctx, name, buf);+}++static inline BOOL JS_IsEmptyString(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;+}++/* JSClass support */++/* a new class ID is allocated if *pclass_id != 0 */+JSClassID JS_NewClassID(JSClassID *pclass_id)+{+    JSClassID class_id;+    /* XXX: make it thread safe */+    class_id = *pclass_id;+    if (class_id == 0) {+        class_id = js_class_id_alloc++;+        *pclass_id = class_id;+    }+    return class_id;+}++BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)+{+    return (class_id < rt->class_count &&+            rt->class_array[class_id].class_id != 0);+}++/* create a new object internal class. Return -1 if error, 0 if+   OK. The finalizer can be NULL if none is needed. */+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,+                        const JSClassDef *class_def, JSAtom name)+{+    int new_size, i;+    JSClass *cl, *new_class_array;+    struct list_head *el;++    if (class_id < rt->class_count &&+        rt->class_array[class_id].class_id != 0)+        return -1;++    if (class_id >= rt->class_count) {+        new_size = max_int(JS_CLASS_INIT_COUNT,+                           max_int(class_id + 1, rt->class_count * 3 / 2));++        /* reallocate the context class prototype array, if any */+        list_for_each(el, &rt->context_list) {+            JSContext *ctx = list_entry(el, JSContext, link);+            JSValue *new_tab;+            new_tab = js_realloc_rt(rt, ctx->class_proto,+                                    sizeof(ctx->class_proto[0]) * new_size);+            if (!new_tab)+                return -1;+            for(i = rt->class_count; i < new_size; i++)+                new_tab[i] = JS_NULL;+            ctx->class_proto = new_tab;+        }+        /* reallocate the class array */+        new_class_array = js_realloc_rt(rt, rt->class_array,+                                        sizeof(JSClass) * new_size);+        if (!new_class_array)+            return -1;+        memset(new_class_array + rt->class_count, 0,+               (new_size - rt->class_count) * sizeof(JSClass));+        rt->class_array = new_class_array;+        rt->class_count = new_size;+    }+    cl = &rt->class_array[class_id];+    cl->class_id = class_id;+    cl->class_name = JS_DupAtomRT(rt, name);+    cl->finalizer = class_def->finalizer;+    cl->gc_mark = class_def->gc_mark;+    cl->call = class_def->call;+    cl->exotic = class_def->exotic;+    return 0;+}++int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)+{+    int ret, len;+    JSAtom name;++    len = strlen(class_def->class_name);+    name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);+    if (name == JS_ATOM_NULL) {+        name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);+        if (name == JS_ATOM_NULL)+            return -1;+    }+    ret = JS_NewClass1(rt, class_id, class_def, name);+    JS_FreeAtomRT(rt, name);+    return ret;+}++static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)+{+    JSString *str;++    if (len <= 0) {+        return JS_AtomToString(ctx, JS_ATOM_empty_string);+    }+    str = js_alloc_string(ctx, len, 0);+    if (!str)+        return JS_EXCEPTION;+    memcpy(str->u.str8, buf, len);+    str->u.str8[len] = '\0';+    return JS_MKPTR(JS_TAG_STRING, str);+}++static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)+{+    JSString *str;+    str = js_alloc_string(ctx, len, 1);+    if (!str)+        return JS_EXCEPTION;+    memcpy(str->u.str16, buf, len * 2);+    return JS_MKPTR(JS_TAG_STRING, str);+}++static JSValue js_new_string_char(JSContext *ctx, uint16_t c)+{+    if (c < 0x100) {+        uint8_t ch8 = c;+        return js_new_string8(ctx, &ch8, 1);+    } else {+        uint16_t ch16 = c;+        return js_new_string16(ctx, &ch16, 1);+    }+}++static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)+{+    int len = end - start;+    if (start == 0 && end == p->len) {+        return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+    }+    if (p->is_wide_char && len > 0) {+        JSString *str;+        int i;+        uint16_t c = 0;+        for (i = start; i < end; i++) {+            c |= p->u.str16[i];+        }+        if (c > 0xFF)+            return js_new_string16(ctx, p->u.str16 + start, len);++        str = js_alloc_string(ctx, len, 0);+        if (!str)+            return JS_EXCEPTION;+        for (i = 0; i < len; i++) {+            str->u.str8[i] = p->u.str16[start + i];+        }+        str->u.str8[len] = '\0';+        return JS_MKPTR(JS_TAG_STRING, str);+    } else {+        return js_new_string8(ctx, p->u.str8 + start, len);+    }+}++typedef struct StringBuffer {+    JSContext *ctx;+    JSString *str;+    int len;+    int size;+    int is_wide_char;+    int error_status;+} StringBuffer;++/* It is valid to call string_buffer_end() and all string_buffer functions even+   if string_buffer_init() or another string_buffer function returns an error.+   If the error_status is set, string_buffer_end() returns JS_EXCEPTION.+ */+static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,+                               int is_wide)+{+    s->ctx = ctx;+    s->size = size;+    s->len = 0;+    s->is_wide_char = is_wide;+    s->error_status = 0;+    s->str = js_alloc_string(ctx, size, is_wide);+    if (unlikely(!s->str)) {+        s->size = 0;+        return s->error_status = -1;+    }+#ifdef DUMP_LEAKS+    /* the StringBuffer may reallocate the JSString, only link it at the end */+    list_del(&s->str->link);+#endif+    return 0;+}++static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)+{+    return string_buffer_init2(ctx, s, size, 0);+}++static void string_buffer_free(StringBuffer *s)+{+    js_free(s->ctx, s->str);+    s->str = NULL;+}++static int string_buffer_set_error(StringBuffer *s)+{+    js_free(s->ctx, s->str);+    s->str = NULL;+    s->size = 0;+    s->len = 0;+    return s->error_status = -1;+}++static no_inline int string_buffer_widen(StringBuffer *s, int size)+{+    JSString *str;+    size_t slack;+    int i;++    if (s->error_status)+        return -1;++    str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);+    if (!str)+        return string_buffer_set_error(s);+    size += slack >> 1;+    for(i = s->len; i-- > 0;) {+        str->u.str16[i] = str->u.str8[i];+    }+    s->is_wide_char = 1;+    s->size = size;+    s->str = str;+    return 0;+}++static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)+{+    JSString *new_str;+    int new_size;+    size_t new_size_bytes, slack;++    if (s->error_status)+        return -1;++    if (new_len > JS_STRING_LEN_MAX) {+        JS_ThrowInternalError(s->ctx, "string too long");+        return string_buffer_set_error(s);+    }+    new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);+    if (!s->is_wide_char && c >= 0x100) {+        return string_buffer_widen(s, new_size);+    }+    new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;+    new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);+    if (!new_str)+        return string_buffer_set_error(s);+    new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);+    s->size = new_size;+    s->str = new_str;+    return 0;+}++static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)+{+    if (unlikely(s->len >= s->size)) {+        if (string_buffer_realloc(s, s->len + 1, c))+            return -1;+    }+    if (s->is_wide_char) {+        s->str->u.str16[s->len++] = c;+    } else if (c < 0x100) {+        s->str->u.str8[s->len++] = c;+    } else {+        if (string_buffer_widen(s, s->size))+            return -1;+        s->str->u.str16[s->len++] = c;+    }+    return 0;+}++/* 0 <= c <= 0xff */+static int string_buffer_putc8(StringBuffer *s, uint32_t c)+{+    if (unlikely(s->len >= s->size)) {+        if (string_buffer_realloc(s, s->len + 1, c))+            return -1;+    }+    if (s->is_wide_char) {+        s->str->u.str16[s->len++] = c;+    } else {+        s->str->u.str8[s->len++] = c;+    }+    return 0;+}++/* 0 <= c <= 0xffff */+static int string_buffer_putc16(StringBuffer *s, uint32_t c)+{+    if (likely(s->len < s->size)) {+        if (s->is_wide_char) {+            s->str->u.str16[s->len++] = c;+            return 0;+        } else if (c < 0x100) {+            s->str->u.str8[s->len++] = c;+            return 0;+        }+    }+    return string_buffer_putc_slow(s, c);+}++/* 0 <= c <= 0x10ffff */+static int string_buffer_putc(StringBuffer *s, uint32_t c)+{+    if (unlikely(c >= 0x10000)) {+        /* surrogate pair */+        c -= 0x10000;+        if (string_buffer_putc16(s, (c >> 10) + 0xd800))+            return -1;+        c = (c & 0x3ff) + 0xdc00;+    }+    return string_buffer_putc16(s, c);+}++static int string_get(const JSString *p, int idx) {+    return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];+}++static int string_getc(const JSString *p, int *pidx)+{+    int idx, c, c1;+    idx = *pidx;+    if (p->is_wide_char) {+        c = p->u.str16[idx++];+        if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {+            c1 = p->u.str16[idx];+            if (c1 >= 0xdc00 && c1 < 0xe000) {+                c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+                idx++;+            }+        }+    } else {+        c = p->u.str8[idx++];+    }+    *pidx = idx;+    return c;+}++static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)+{+    int i;++    if (s->len + len > s->size) {+        if (string_buffer_realloc(s, s->len + len, 0))+            return -1;+    }+    if (s->is_wide_char) {+        for (i = 0; i < len; i++) {+            s->str->u.str16[s->len + i] = p[i];+        }+        s->len += len;+    } else {+        memcpy(&s->str->u.str8[s->len], p, len);+        s->len += len;+    }+    return 0;+}++static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)+{+    int c = 0, i;++    for (i = 0; i < len; i++) {+        c |= p[i];+    }+    if (s->len + len > s->size) {+        if (string_buffer_realloc(s, s->len + len, c))+            return -1;+    } else if (!s->is_wide_char && c >= 0x100) {+        if (string_buffer_widen(s, s->size))+            return -1;+    }+    if (s->is_wide_char) {+        memcpy(&s->str->u.str16[s->len], p, len << 1);+        s->len += len;+    } else {+        for (i = 0; i < len; i++) {+            s->str->u.str8[s->len + i] = p[i];+        }+        s->len += len;+    }+    return 0;+}++/* appending an ASCII string */+static int string_buffer_puts8(StringBuffer *s, const char *str)+{+    return string_buffer_write8(s, (const uint8_t *)str, strlen(str));+}++static int string_buffer_concat(StringBuffer *s, const JSString *p,+                                uint32_t from, uint32_t to)+{+    if (to <= from)+        return 0;+    if (p->is_wide_char)+        return string_buffer_write16(s, p->u.str16 + from, to - from);+    else+        return string_buffer_write8(s, p->u.str8 + from, to - from);+}++static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)+{+    JSString *p;+    JSValue v1;+    int res;++    if (s->error_status) {+        /* prevent exception overload */+        return -1;+    }+    if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {+        v1 = JS_ToString(s->ctx, v);+        if (JS_IsException(v1))+            return string_buffer_set_error(s);+        p = JS_VALUE_GET_STRING(v1);+        res = string_buffer_concat(s, p, 0, p->len);+        JS_FreeValue(s->ctx, v1);+        return res;+    }+    p = JS_VALUE_GET_STRING(v);+    return string_buffer_concat(s, p, 0, p->len);+}++static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)+{+    JSString *p;+    int res;++    if (s->error_status) {+        /* prevent exception overload */+        JS_FreeValue(s->ctx, v);+        return -1;+    }+    if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {+        v = JS_ToStringFree(s->ctx, v);+        if (JS_IsException(v))+            return string_buffer_set_error(s);+    }+    p = JS_VALUE_GET_STRING(v);+    res = string_buffer_concat(s, p, 0, p->len);+    JS_FreeValue(s->ctx, v);+    return res;+}++static int string_buffer_fill(StringBuffer *s, int c, int count)+{+    /* XXX: optimize */+    if (s->len + count > s->size) {+        if (string_buffer_realloc(s, s->len + count, c))+            return -1;+    }+    while (count-- > 0) {+        if (string_buffer_putc16(s, c))+            return -1;+    }+    return 0;+}++static JSValue string_buffer_end(StringBuffer *s)+{+    JSString *str;+    str = s->str;+    if (s->error_status)+        return JS_EXCEPTION;+    if (s->len == 0) {+        js_free(s->ctx, str);+        s->str = NULL;+        return JS_AtomToString(s->ctx, JS_ATOM_empty_string);+    }+    if (s->len < s->size) {+        /* smaller size so js_realloc should not fail, but OK if it does */+        /* XXX: should add some slack to avoid unnecessary calls */+        /* XXX: might need to use malloc+free to ensure smaller size */+        str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) ++                            (s->len << s->is_wide_char) + 1 - s->is_wide_char);+        if (str == NULL)+            str = s->str;+        s->str = str;+    }+    if (!s->is_wide_char)+        str->u.str8[s->len] = 0;+#ifdef DUMP_LEAKS+    list_add_tail(&str->link, &s->ctx->rt->string_list);+#endif+    str->is_wide_char = s->is_wide_char;+    str->len = s->len;+    s->str = NULL;+    return JS_MKPTR(JS_TAG_STRING, str);+}++/* create a string from a UTF-8 buffer */+JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)+{+    const uint8_t *p, *p_end, *p_start, *p_next;+    uint32_t c;+    StringBuffer b_s, *b = &b_s;+    size_t len1;+    +    p_start = (const uint8_t *)buf;+    p_end = p_start + buf_len;+    p = p_start;+    while (p < p_end && *p < 128)+        p++;+    len1 = p - p_start;+    if (len1 > JS_STRING_LEN_MAX)+        return JS_ThrowInternalError(ctx, "string too long");+    if (p == p_end) {+        /* ASCII string */+        return js_new_string8(ctx, (const uint8_t *)buf, buf_len);+    } else {+        if (string_buffer_init(ctx, b, buf_len))+            goto fail;+        string_buffer_write8(b, p_start, len1);+        while (p < p_end) {+            if (*p < 128) {+                string_buffer_putc8(b, *p++);+            } else {+                /* parse utf-8 sequence, return 0xFFFFFFFF for error */+                c = unicode_from_utf8(p, p_end - p, &p_next);+                if (c < 0x10000) {+                    p = p_next;+                } else if (c <= 0x10FFFF) {+                    p = p_next;+                    /* surrogate pair */+                    c -= 0x10000;+                    string_buffer_putc16(b, (c >> 10) + 0xd800);+                    c = (c & 0x3ff) + 0xdc00;+                } else {+                    /* invalid char */+                    c = 0xfffd;+                    /* skip the invalid chars */+                    /* XXX: seems incorrect. Why not just use c = *p++; ? */+                    while (p < p_end && (*p >= 0x80 && *p < 0xc0))+                        p++;+                    if (p < p_end) {+                        p++;+                        while (p < p_end && (*p >= 0x80 && *p < 0xc0))+                            p++;+                    }+                }+                string_buffer_putc16(b, c);+            }+        }+    }+    return string_buffer_end(b);++ fail:+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,+                                JSValue str2, const char *str3)+{+    StringBuffer b_s, *b = &b_s;+    int len1, len3;+    JSString *p;++    if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {+        str2 = JS_ToStringFree(ctx, str2);+        if (JS_IsException(str2))+            goto fail;+    }+    p = JS_VALUE_GET_STRING(str2);+    len1 = strlen(str1);+    len3 = strlen(str3);++    if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))+        goto fail;++    string_buffer_write8(b, (const uint8_t *)str1, len1);+    string_buffer_concat(b, p, 0, p->len);+    string_buffer_write8(b, (const uint8_t *)str3, len3);++    JS_FreeValue(ctx, str2);+    return string_buffer_end(b);++ fail:+    JS_FreeValue(ctx, str2);+    return JS_EXCEPTION;+}++JSValue JS_NewString(JSContext *ctx, const char *str)+{+    return JS_NewStringLen(ctx, str, strlen(str));+}++JSValue JS_NewAtomString(JSContext *ctx, const char *str)+{+    JSAtom atom = JS_NewAtom(ctx, str);+    if (atom == JS_ATOM_NULL)+        return JS_EXCEPTION;+    JSValue val = JS_AtomToString(ctx, atom);+    JS_FreeAtom(ctx, atom);+    return val;+}++/* return (NULL, 0) if exception. */+/* return pointer into a JSString with a live ref_count */+/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */+const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)+{+    JSValue val;+    JSString *str, *str_new;+    int pos, len, c, c1;+    uint8_t *q;++    if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {+        val = JS_ToString(ctx, val1);+        if (JS_IsException(val))+            goto fail;+    } else {+        val = JS_DupValue(ctx, val1);+    }++    str = JS_VALUE_GET_STRING(val);+    len = str->len;+    if (!str->is_wide_char) {+        const uint8_t *src = str->u.str8;+        int count;++        /* count the number of non-ASCII characters */+        /* Scanning the whole string is required for ASCII strings,+           and computing the number of non-ASCII bytes is less expensive+           than testing each byte, hence this method is faster for ASCII+           strings, which is the most common case.+         */+        count = 0;+        for (pos = 0; pos < len; pos++) {+            count += src[pos] >> 7;+        }+        if (count == 0) {+            if (plen)+                *plen = len;+            return (const char *)src;+        }+        str_new = js_alloc_string(ctx, len + count, 0);+        if (!str_new)+            goto fail;+        q = str_new->u.str8;+        for (pos = 0; pos < len; pos++) {+            c = src[pos];+            if (c < 0x80) {+                *q++ = c;+            } else {+                *q++ = (c >> 6) | 0xc0;+                *q++ = (c & 0x3f) | 0x80;+            }+        }+    } else {+        const uint16_t *src = str->u.str16;+        /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may+           produce 4 bytes but use 2 code points.+         */+        str_new = js_alloc_string(ctx, len * 3, 0);+        if (!str_new)+            goto fail;+        q = str_new->u.str8;+        pos = 0;+        while (pos < len) {+            c = src[pos++];+            if (c < 0x80) {+                *q++ = c;+            } else {+                if (c >= 0xd800 && c < 0xdc00) {+                    if (pos < len && !cesu8) {+                        c1 = src[pos];+                        if (c1 >= 0xdc00 && c1 < 0xe000) {+                            pos++;+                            /* surrogate pair */+                            c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+                        } else {+                            /* Keep unmatched surrogate code points */+                            /* c = 0xfffd; */ /* error */+                        }+                    } else {+                        /* Keep unmatched surrogate code points */+                        /* c = 0xfffd; */ /* error */+                    }+                }+                q += unicode_to_utf8(q, c);+            }+        }+    }++    *q = '\0';+    str_new->len = q - str_new->u.str8;+    JS_FreeValue(ctx, val);+    if (plen)+        *plen = str_new->len;+    return (const char *)str_new->u.str8;+ fail:+    if (plen)+        *plen = 0;+    return NULL;+}++void JS_FreeCString(JSContext *ctx, const char *ptr)+{+    JSString *p;+    if (!ptr)+        return;+    /* purposely removing constness */+    p = (JSString *)(void *)(ptr - offsetof(JSString, u));+    JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+}++static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)+{+    int c, i;+    for(i = 0; i < len; i++) {+        c = src1[i] - src2[i];+        if (c != 0)+            return c;+    }+    return 0;+}++static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)+{+    int c, i;+    for(i = 0; i < len; i++) {+        c = src1[i] - src2[i];+        if (c != 0)+            return c;+    }+    return 0;+}++static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)+{+    int res;++    if (likely(!p1->is_wide_char)) {+        if (likely(!p2->is_wide_char))+            res = memcmp(p1->u.str8, p2->u.str8, len);+        else+            res = -memcmp16_8(p2->u.str16, p1->u.str8, len);+    } else {+        if (!p2->is_wide_char)+            res = memcmp16_8(p1->u.str16, p2->u.str8, len);+        else+            res = memcmp16(p1->u.str16, p2->u.str16, len);+    }+    return res;+}++/* return < 0, 0 or > 0 */+static int js_string_compare(JSContext *ctx,+                             const JSString *p1, const JSString *p2)+{+    int res, len;+    len = min_int(p1->len, p2->len);+    res = js_string_memcmp(p1, p2, len);+    if (res == 0) {+        if (p1->len == p2->len)+            res = 0;+        else if (p1->len < p2->len)+            res = -1;+        else+            res = 1;+    }+    return res;+}++static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)+{+    if (p->is_wide_char) {+        memcpy(dst, p->u.str16 + offset, len * 2);+    } else {+        const uint8_t *src1 = p->u.str8 + offset;+        int i;++        for(i = 0; i < len; i++)+            dst[i] = src1[i];+    }+}++static JSValue JS_ConcatString1(JSContext *ctx,+                                const JSString *p1, const JSString *p2)+{+    JSString *p;+    uint32_t len;+    int is_wide_char;++    len = p1->len + p2->len;+    if (len > JS_STRING_LEN_MAX)+        return JS_ThrowInternalError(ctx, "string too long");+    is_wide_char = p1->is_wide_char | p2->is_wide_char;+    p = js_alloc_string(ctx, len, is_wide_char);+    if (!p)+        return JS_EXCEPTION;+    if (!is_wide_char) {+        memcpy(p->u.str8, p1->u.str8, p1->len);+        memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);+        p->u.str8[len] = '\0';+    } else {+        copy_str16(p->u.str16, p1, 0, p1->len);+        copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);+    }+    return JS_MKPTR(JS_TAG_STRING, p);+}++/* op1 and op2 are converted to strings. For convience, op1 or op2 =+   JS_EXCEPTION are accepted and return JS_EXCEPTION.  */+static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)+{+    JSValue ret;+    JSString *p1, *p2;++    if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {+        op1 = JS_ToStringFree(ctx, op1);+        if (JS_IsException(op1)) {+            JS_FreeValue(ctx, op2);+            return JS_EXCEPTION;+        }+    }+    if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {+        op2 = JS_ToStringFree(ctx, op2);+        if (JS_IsException(op2)) {+            JS_FreeValue(ctx, op1);+            return JS_EXCEPTION;+        }+    }+    p1 = JS_VALUE_GET_STRING(op1);+    p2 = JS_VALUE_GET_STRING(op2);++    /* XXX: could also check if p1 is empty */+    if (p2->len == 0) {+        goto ret_op1;+    }+    if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char+    &&  js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {+        /* Concatenate in place in available space at the end of p1 */+        if (p1->is_wide_char) {+            memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);+            p1->len += p2->len;+        } else {+            memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);+            p1->len += p2->len;+            p1->u.str8[p1->len] = '\0';+        }+    ret_op1:+        JS_FreeValue(ctx, op2);+        return op1;+    }+    ret = JS_ConcatString1(ctx, p1, p2);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    return ret;+}++/* Shape support */++static inline size_t get_shape_size(size_t hash_size, size_t prop_size)+{+    return hash_size * sizeof(uint32_t) + sizeof(JSShape) ++        prop_size * sizeof(JSShapeProperty);+}++static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)+{+    return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);+}++static inline void *get_alloc_from_shape(JSShape *sh)+{+    return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1);+}++static inline JSShapeProperty *get_shape_prop(JSShape *sh)+{+    return sh->prop;+}++static int init_shape_hash(JSRuntime *rt)+{+    rt->shape_hash_bits = 4;   /* 16 shapes */+    rt->shape_hash_size = 1 << rt->shape_hash_bits;+    rt->shape_hash_count = 0;+    rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *+                                   rt->shape_hash_size);+    if (!rt->shape_hash)+        return -1;+    return 0;+}++/* same magic hash multiplier as the Linux kernel */+static uint32_t shape_hash(uint32_t h, uint32_t val)+{+    return (h + val) * 0x9e370001;+}++/* truncate the shape hash to 'hash_bits' bits */+static uint32_t get_shape_hash(uint32_t h, int hash_bits)+{+    return h >> (32 - hash_bits);+}++static uint32_t shape_initial_hash(JSObject *proto)+{+    uint32_t h;+    h = shape_hash(1, (uintptr_t)proto);+    if (sizeof(proto) > 4)+        h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);+    return h;+}++static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)+{+    int new_shape_hash_size, i;+    uint32_t h;+    JSShape **new_shape_hash, *sh, *sh_next;++    new_shape_hash_size = 1 << new_shape_hash_bits;+    new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *+                                   new_shape_hash_size);+    if (!new_shape_hash)+        return -1;+    for(i = 0; i < rt->shape_hash_size; i++) {+        for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {+            sh_next = sh->shape_hash_next;+            h = get_shape_hash(sh->hash, new_shape_hash_bits);+            sh->shape_hash_next = new_shape_hash[h];+            new_shape_hash[h] = sh;+        }+    }+    js_free_rt(rt, rt->shape_hash);+    rt->shape_hash_bits = new_shape_hash_bits;+    rt->shape_hash_size = new_shape_hash_size;+    rt->shape_hash = new_shape_hash;+    return 0;+}++static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)+{+    uint32_t h;+    h = get_shape_hash(sh->hash, rt->shape_hash_bits);+    sh->shape_hash_next = rt->shape_hash[h];+    rt->shape_hash[h] = sh;+    rt->shape_hash_count++;+}++static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)+{+    uint32_t h;+    JSShape **psh;++    h = get_shape_hash(sh->hash, rt->shape_hash_bits);+    psh = &rt->shape_hash[h];+    while (*psh != sh)+        psh = &(*psh)->shape_hash_next;+    *psh = sh->shape_hash_next;+    rt->shape_hash_count--;+}++/* create a new empty shape with prototype 'proto' */+static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,+                                        int hash_size, int prop_size)+{+    JSRuntime *rt = ctx->rt;+    void *sh_alloc;+    JSShape *sh;++    /* resize the shape hash table if necessary */+    if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {+        resize_shape_hash(rt, rt->shape_hash_bits + 1);+    }++    sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));+    if (!sh_alloc)+        return NULL;+    sh = get_shape_from_alloc(sh_alloc, hash_size);+    sh->header.ref_count = 1;+    add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);+    if (proto)+        JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));+    sh->proto = proto;+    memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) *+           hash_size);+    sh->prop_hash_mask = hash_size - 1;+    sh->prop_size = prop_size;+    sh->prop_count = 0;+    sh->deleted_prop_count = 0;+    +    /* insert in the hash table */+    sh->hash = shape_initial_hash(proto);+    sh->is_hashed = TRUE;+    sh->has_small_array_index = FALSE;+    js_shape_hash_link(ctx->rt, sh);+    return sh;+}++static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)+{+    return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,+                         JS_PROP_INITIAL_SIZE);+}++/* The shape is cloned. The new shape is not inserted in the shape+   hash table */+static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)+{+    JSShape *sh;+    void *sh_alloc, *sh_alloc1;+    size_t size;+    JSShapeProperty *pr;+    uint32_t i, hash_size;++    hash_size = sh1->prop_hash_mask + 1;+    size = get_shape_size(hash_size, sh1->prop_size);+    sh_alloc = js_malloc(ctx, size);+    if (!sh_alloc)+        return NULL;+    sh_alloc1 = get_alloc_from_shape(sh1);+    memcpy(sh_alloc, sh_alloc1, size);+    sh = get_shape_from_alloc(sh_alloc, hash_size);+    sh->header.ref_count = 1;+    add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);+    sh->is_hashed = FALSE;+    if (sh->proto) {+        JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));+    }+    for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {+        JS_DupAtom(ctx, pr->atom);+    }+    return sh;+}++static JSShape *js_dup_shape(JSShape *sh)+{+    sh->header.ref_count++;+    return sh;+}++static void js_free_shape0(JSRuntime *rt, JSShape *sh)+{+    uint32_t i;+    JSShapeProperty *pr;++    assert(sh->header.ref_count == 0);+    if (sh->is_hashed)+        js_shape_hash_unlink(rt, sh);+    if (sh->proto != NULL) {+        JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));+    }+    pr = get_shape_prop(sh);+    for(i = 0; i < sh->prop_count; i++) {+        JS_FreeAtomRT(rt, pr->atom);+        pr++;+    }+    remove_gc_object(&sh->header);+    js_free_rt(rt, get_alloc_from_shape(sh));+}++static void js_free_shape(JSRuntime *rt, JSShape *sh)+{+    if (unlikely(--sh->header.ref_count <= 0)) {+        js_free_shape0(rt, sh);+    }+}++static void js_free_shape_null(JSRuntime *rt, JSShape *sh)+{+    if (sh)+        js_free_shape(rt, sh);+}++/* make space to hold at least 'count' properties */+static no_inline int resize_properties(JSContext *ctx, JSShape **psh,+                                       JSObject *p, uint32_t count)+{+    JSShape *sh;+    uint32_t new_size, new_hash_size, new_hash_mask, i;+    JSShapeProperty *pr;+    void *sh_alloc;+    intptr_t h;++    sh = *psh;+    new_size = max_int(count, sh->prop_size * 3 / 2);+    /* Reallocate prop array first to avoid crash or size inconsistency+       in case of memory allocation failure */+    if (p) {+        JSProperty *new_prop;+        new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);+        if (unlikely(!new_prop))+            return -1;+        p->prop = new_prop;+    }+    new_hash_size = sh->prop_hash_mask + 1;+    while (new_hash_size < new_size)+        new_hash_size = 2 * new_hash_size;+    if (new_hash_size != (sh->prop_hash_mask + 1)) {+        JSShape *old_sh;+        /* resize the hash table and the properties */+        old_sh = sh;+        sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));+        if (!sh_alloc)+            return -1;+        sh = get_shape_from_alloc(sh_alloc, new_hash_size);+        list_del(&old_sh->header.link);+        /* copy all the fields and the properties */+        memcpy(sh, old_sh,+               sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);+        list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+        new_hash_mask = new_hash_size - 1;+        sh->prop_hash_mask = new_hash_mask;+        memset(sh->prop_hash_end - new_hash_size, 0,+               sizeof(sh->prop_hash_end[0]) * new_hash_size);+        for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {+            if (pr->atom != JS_ATOM_NULL) {+                h = ((uintptr_t)pr->atom & new_hash_mask);+                pr->hash_next = sh->prop_hash_end[-h - 1];+                sh->prop_hash_end[-h - 1] = i + 1;+            }+        }+        js_free(ctx, get_alloc_from_shape(old_sh));+    } else {+        /* only resize the properties */+        list_del(&sh->header.link);+        sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),+                              get_shape_size(new_hash_size, new_size));+        if (unlikely(!sh_alloc)) {+            /* insert again in the GC list */+            list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+            return -1;+        }+        sh = get_shape_from_alloc(sh_alloc, new_hash_size);+        list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+    }+    *psh = sh;+    sh->prop_size = new_size;+    return 0;+}++/* remove the deleted properties. */+static int compact_properties(JSContext *ctx, JSObject *p)+{+    JSShape *sh, *old_sh;+    void *sh_alloc;+    intptr_t h;+    uint32_t new_hash_size, i, j, new_hash_mask, new_size;+    JSShapeProperty *old_pr, *pr;+    JSProperty *prop, *new_prop;+    +    sh = p->shape;+    assert(!sh->is_hashed);++    new_size = max_int(JS_PROP_INITIAL_SIZE,+                       sh->prop_count - sh->deleted_prop_count);+    assert(new_size <= sh->prop_size);++    new_hash_size = sh->prop_hash_mask + 1;+    while ((new_hash_size / 2) >= new_size)+        new_hash_size = new_hash_size / 2;+    new_hash_mask = new_hash_size - 1;++    /* resize the hash table and the properties */+    old_sh = sh;+    sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));+    if (!sh_alloc)+        return -1;+    sh = get_shape_from_alloc(sh_alloc, new_hash_size);+    list_del(&old_sh->header.link);+    memcpy(sh, old_sh, sizeof(JSShape));+    list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+    +    memset(sh->prop_hash_end - new_hash_size, 0,+           sizeof(sh->prop_hash_end[0]) * new_hash_size);++    j = 0;+    old_pr = old_sh->prop;+    pr = sh->prop;+    prop = p->prop;+    for(i = 0; i < sh->prop_count; i++) {+        if (old_pr->atom != JS_ATOM_NULL) {+            pr->atom = old_pr->atom;+            pr->flags = old_pr->flags;+            h = ((uintptr_t)old_pr->atom & new_hash_mask);+            pr->hash_next = sh->prop_hash_end[-h - 1];+            sh->prop_hash_end[-h - 1] = j + 1;+            prop[j] = prop[i];+            j++;+            pr++;+        }+        old_pr++;+    }+    assert(j == (sh->prop_count - sh->deleted_prop_count));+    sh->prop_hash_mask = new_hash_mask;+    sh->prop_size = new_size;+    sh->deleted_prop_count = 0;+    sh->prop_count = j;++    p->shape = sh;+    js_free(ctx, get_alloc_from_shape(old_sh));+    +    /* reduce the size of the object properties */+    new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);+    if (new_prop)+        p->prop = new_prop;+    return 0;+}++static int add_shape_property(JSContext *ctx, JSShape **psh,+                              JSObject *p, JSAtom atom, int prop_flags)+{+    JSRuntime *rt = ctx->rt;+    JSShape *sh = *psh;+    JSShapeProperty *pr, *prop;+    uint32_t hash_mask, new_shape_hash = 0;+    intptr_t h;++    /* update the shape hash */+    if (sh->is_hashed) {+        js_shape_hash_unlink(rt, sh);+        new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);+    }++    if (unlikely(sh->prop_count >= sh->prop_size)) {+        if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {+            /* in case of error, reinsert in the hash table.+               sh is still valid if resize_properties() failed */+            if (sh->is_hashed)+                js_shape_hash_link(rt, sh);+            return -1;+        }+        sh = *psh;+    }+    if (sh->is_hashed) {+        sh->hash = new_shape_hash;+        js_shape_hash_link(rt, sh);+    }+    /* Initialize the new shape property.+       The object property at p->prop[sh->prop_count] is uninitialized */+    prop = get_shape_prop(sh);+    pr = &prop[sh->prop_count++];+    pr->atom = JS_DupAtom(ctx, atom);+    pr->flags = prop_flags;+    sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);+    /* add in hash table */+    hash_mask = sh->prop_hash_mask;+    h = atom & hash_mask;+    pr->hash_next = sh->prop_hash_end[-h - 1];+    sh->prop_hash_end[-h - 1] = sh->prop_count;+    return 0;+}++/* find a hashed empty shape matching the prototype. Return NULL if+   not found */+static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)+{+    JSShape *sh1;+    uint32_t h, h1;++    h = shape_initial_hash(proto);+    h1 = get_shape_hash(h, rt->shape_hash_bits);+    for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {+        if (sh1->hash == h &&+            sh1->proto == proto &&+            sh1->prop_count == 0) {+            return sh1;+        }+    }+    return NULL;+}++/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if+   not found */+static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,+                                       JSAtom atom, int prop_flags)+{+    JSShape *sh1;+    uint32_t h, h1, i, n;++    h = sh->hash;+    h = shape_hash(h, atom);+    h = shape_hash(h, prop_flags);+    h1 = get_shape_hash(h, rt->shape_hash_bits);+    for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {+        /* we test the hash first so that the rest is done only if the+           shapes really match */+        if (sh1->hash == h &&+            sh1->proto == sh->proto &&+            sh1->prop_count == ((n = sh->prop_count) + 1)) {+            for(i = 0; i < n; i++) {+                if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||+                    unlikely(sh1->prop[i].flags != sh->prop[i].flags))+                    goto next;+            }+            if (unlikely(sh1->prop[n].atom != atom) ||+                unlikely(sh1->prop[n].flags != prop_flags))+                goto next;+            return sh1;+        }+    next: ;+    }+    return NULL;+}++static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)+{+    char atom_buf[ATOM_GET_STR_BUF_SIZE];+    int j;++    /* XXX: should output readable class prototype */+    printf("%5d %3d%c %14p %5d %5d", i,+           sh->header.ref_count, " *"[sh->is_hashed],+           (void *)sh->proto, sh->prop_size, sh->prop_count);+    for(j = 0; j < sh->prop_count; j++) {+        printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),+                                      sh->prop[j].atom));+    }+    printf("\n");+}++static __maybe_unused void JS_DumpShapes(JSRuntime *rt)+{+    int i;+    JSShape *sh;+    struct list_head *el;+    JSObject *p;+    JSGCObjectHeader *gp;+    +    printf("JSShapes: {\n");+    printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");+    for(i = 0; i < rt->shape_hash_size; i++) {+        for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {+            JS_DumpShape(rt, i, sh);+            assert(sh->is_hashed);+        }+    }+    /* dump non-hashed shapes */+    list_for_each(el, &rt->gc_obj_list) {+        gp = list_entry(el, JSGCObjectHeader, link);+        if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {+            p = (JSObject *)gp;+            if (!p->shape->is_hashed) {+                JS_DumpShape(rt, -1, p->shape);+            }+        }+    }+    printf("}\n");+}++static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)+{+    JSObject *p;++    js_trigger_gc(ctx->rt, sizeof(JSObject));+    p = js_malloc(ctx, sizeof(JSObject));+    if (unlikely(!p))+        goto fail;+    p->class_id = class_id;+    p->extensible = TRUE;+    p->free_mark = 0;+    p->is_exotic = 0;+    p->fast_array = 0;+    p->is_constructor = 0;+    p->is_uncatchable_error = 0;+    p->tmp_mark = 0;+    p->first_weak_ref = NULL;+    p->u.opaque = NULL;+    p->shape = sh;+    p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);+    if (unlikely(!p->prop)) {+        js_free(ctx, p);+    fail:+        js_free_shape(ctx->rt, sh);+        return JS_EXCEPTION;+    }++    switch(class_id) {+    case JS_CLASS_OBJECT:+        break;+    case JS_CLASS_ARRAY:+        {+            JSProperty *pr;+            p->is_exotic = 1;+            p->fast_array = 1;+            p->u.array.u.values = NULL;+            p->u.array.count = 0;+            p->u.array.u1.size = 0;+            /* the length property is always the first one */+            if (likely(sh == ctx->array_shape)) {+                pr = &p->prop[0];+            } else {+                /* only used for the first array */+                /* cannot fail */+                pr = add_property(ctx, p, JS_ATOM_length,+                                  JS_PROP_WRITABLE | JS_PROP_LENGTH);+            }+            pr->u.value = JS_NewInt32(ctx, 0);+        }+        break;+    case JS_CLASS_C_FUNCTION:+        p->prop[0].u.value = JS_UNDEFINED;+        break;+    case JS_CLASS_ARGUMENTS:+    case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY:+        p->is_exotic = 1;+        p->fast_array = 1;+        p->u.array.u.ptr = NULL;+        p->u.array.count = 0;+        break;+    case JS_CLASS_DATAVIEW:+        p->u.array.u.ptr = NULL;+        p->u.array.count = 0;+        break;+    case JS_CLASS_NUMBER:+    case JS_CLASS_STRING:+    case JS_CLASS_BOOLEAN:+    case JS_CLASS_SYMBOL:+    case JS_CLASS_DATE:+#ifdef CONFIG_BIGNUM+    case JS_CLASS_BIG_INT:+    case JS_CLASS_BIG_FLOAT:+    case JS_CLASS_BIG_DECIMAL:+#endif+        p->u.object_data = JS_UNDEFINED;+        goto set_exotic;+    case JS_CLASS_REGEXP:+        p->u.regexp.pattern = NULL;+        p->u.regexp.bytecode = NULL;+        goto set_exotic;+    default:+    set_exotic:+        if (ctx->rt->class_array[class_id].exotic) {+            p->is_exotic = 1;+        }+        break;+    }+    p->header.ref_count = 1;+    add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);+    return JS_MKPTR(JS_TAG_OBJECT, p);+}++static JSObject *get_proto_obj(JSValueConst proto_val)+{+    if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)+        return NULL;+    else+        return JS_VALUE_GET_OBJ(proto_val);+}++/* WARNING: proto must be an object or JS_NULL */+JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,+                               JSClassID class_id)+{+    JSShape *sh;+    JSObject *proto;++    proto = get_proto_obj(proto_val);+    sh = find_hashed_shape_proto(ctx->rt, proto);+    if (likely(sh)) {+        sh = js_dup_shape(sh);+    } else {+        sh = js_new_shape(ctx, proto);+        if (!sh)+            return JS_EXCEPTION;+    }+    return JS_NewObjectFromShape(ctx, sh, class_id);+}++#if 0+static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)+{+    JSObject *p;++    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        p = JS_VALUE_GET_OBJ(obj);+        switch(p->class_id) {+        case JS_CLASS_NUMBER:+        case JS_CLASS_STRING:+        case JS_CLASS_BOOLEAN:+        case JS_CLASS_SYMBOL:+        case JS_CLASS_DATE:+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT:+        case JS_CLASS_BIG_FLOAT:+        case JS_CLASS_BIG_DECIMAL:+#endif+            return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_UNDEFINED;+}+#endif++static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)+{+    JSObject *p;++    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        p = JS_VALUE_GET_OBJ(obj);+        switch(p->class_id) {+        case JS_CLASS_NUMBER:+        case JS_CLASS_STRING:+        case JS_CLASS_BOOLEAN:+        case JS_CLASS_SYMBOL:+        case JS_CLASS_DATE:+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT:+        case JS_CLASS_BIG_FLOAT:+        case JS_CLASS_BIG_DECIMAL:+#endif+            JS_FreeValue(ctx, p->u.object_data);+            p->u.object_data = val;+            return 0;+        }+    }+    JS_FreeValue(ctx, val);+    if (!JS_IsException(obj))+        JS_ThrowTypeError(ctx, "invalid object type");+    return -1;+}++JSValue JS_NewObjectClass(JSContext *ctx, int class_id)+{+    return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);+}++JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)+{+    return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);+}++JSValue JS_NewArray(JSContext *ctx)+{+    return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),+                                 JS_CLASS_ARRAY);+}++JSValue JS_NewObject(JSContext *ctx)+{+    /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */+    return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);+}++static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,+                                       JSAtom name, int len)+{+    /* ES6 feature non compatible with ES5.1: length is configurable */+    JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),+                           JS_PROP_CONFIGURABLE);+    JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,+                           JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);+}++static BOOL js_class_has_bytecode(JSClassID class_id)+{+    return (class_id == JS_CLASS_BYTECODE_FUNCTION ||+            class_id == JS_CLASS_GENERATOR_FUNCTION ||+            class_id == JS_CLASS_ASYNC_FUNCTION ||+            class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);+}++/* return NULL without exception if not a function or no bytecode */+static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return NULL;+    p = JS_VALUE_GET_OBJ(val);+    if (!js_class_has_bytecode(p->class_id))+        return NULL;+    return p->u.func.function_bytecode;+}++static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,+                                      JSValueConst home_obj)+{+    JSObject *p, *p1;+    JSFunctionBytecode *b;++    if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)+        return;+    p = JS_VALUE_GET_OBJ(func_obj);+    if (!js_class_has_bytecode(p->class_id))+        return;+    b = p->u.func.function_bytecode;+    if (b->need_home_object) {+        p1 = p->u.func.home_object;+        if (p1) {+            JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+        }+        if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)+            p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));+        else+            p1 = NULL;+        p->u.func.home_object = p1;+    }+}++static JSValue js_get_function_name(JSContext *ctx, JSAtom name)+{+    JSValue name_str;++    name_str = JS_AtomToString(ctx, name);+    if (JS_AtomSymbolHasDescription(ctx, name)) {+        name_str = JS_ConcatString3(ctx, "[", name_str, "]");+    }+    return name_str;+}++/* Modify the name of a method according to the atom and+   'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and+   JS_PROP_HAS_SET. Also set the home object of the method.+   Return < 0 if exception. */+static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,+                                    JSAtom name, int flags, JSValueConst home_obj)+{+    JSValue name_str;++    name_str = js_get_function_name(ctx, name);+    if (flags & JS_PROP_HAS_GET) {+        name_str = JS_ConcatString3(ctx, "get ", name_str, "");+    } else if (flags & JS_PROP_HAS_SET) {+        name_str = JS_ConcatString3(ctx, "set ", name_str, "");+    }+    if (JS_IsException(name_str))+        return -1;+    if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,+                               JS_PROP_CONFIGURABLE) < 0)+        return -1;+    js_method_set_home_object(ctx, func_obj, home_obj);+    return 0;+}++/* Note: at least 'length' arguments will be readable in 'argv' */+static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,+                                const char *name,+                                int length, JSCFunctionEnum cproto, int magic,+                                JSValueConst proto_val)+{+    JSValue func_obj;+    JSObject *p;+    JSAtom name_atom;+    +    func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);+    if (JS_IsException(func_obj))+        return func_obj;+    p = JS_VALUE_GET_OBJ(func_obj);+    p->u.cfunc.realm = JS_DupContext(ctx);+    p->u.cfunc.c_function.generic = func;+    p->u.cfunc.length = length;+    p->u.cfunc.cproto = cproto;+    p->u.cfunc.magic = magic;+    p->is_constructor = (cproto == JS_CFUNC_constructor ||+                         cproto == JS_CFUNC_constructor_magic ||+                         cproto == JS_CFUNC_constructor_or_func ||+                         cproto == JS_CFUNC_constructor_or_func_magic);+    if (!name)+        name = "";+    name_atom = JS_NewAtom(ctx, name);+    js_function_set_properties(ctx, func_obj, name_atom, length);+    JS_FreeAtom(ctx, name_atom);+    return func_obj;+}++/* Note: at least 'length' arguments will be readable in 'argv' */+JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,+                         const char *name,+                         int length, JSCFunctionEnum cproto, int magic)+{+    return JS_NewCFunction3(ctx, func, name, length, cproto, magic,+                            ctx->function_proto);+}++typedef struct JSCFunctionDataRecord {+    JSCFunctionData *func;+    uint8_t length;+    uint8_t data_len;+    uint16_t magic;+    JSValue data[0];+} JSCFunctionDataRecord;++static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)+{+    JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);+    int i;++    if (s) {+        for(i = 0; i < s->data_len; i++) {+            JS_FreeValueRT(rt, s->data[i]);+        }+        js_free_rt(rt, s);+    }+}++static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,+                                    JS_MarkFunc *mark_func)+{+    JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);+    int i;++    if (s) {+        for(i = 0; i < s->data_len; i++) {+            JS_MarkValue(rt, s->data[i], mark_func);+        }+    }+}++static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,+                                       JSValueConst this_val,+                                       int argc, JSValueConst *argv, int flags)+{+    JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);+    JSValueConst *arg_buf;+    int i;++    /* XXX: could add the function on the stack for debug */+    if (unlikely(argc < s->length)) {+        arg_buf = alloca(sizeof(arg_buf[0]) * s->length);+        for(i = 0; i < argc; i++)+            arg_buf[i] = argv[i];+        for(i = argc; i < s->length; i++)+            arg_buf[i] = JS_UNDEFINED;+    } else {+        arg_buf = argv;+    }++    return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);+}++JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,+                            int length, int magic, int data_len,+                            JSValueConst *data)+{+    JSCFunctionDataRecord *s;+    JSValue func_obj;+    int i;++    func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+                                      JS_CLASS_C_FUNCTION_DATA);+    if (JS_IsException(func_obj))+        return func_obj;+    s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));+    if (!s) {+        JS_FreeValue(ctx, func_obj);+        return JS_EXCEPTION;+    }+    s->func = func;+    s->length = length;+    s->data_len = data_len;+    s->magic = magic;+    for(i = 0; i < data_len; i++)+        s->data[i] = JS_DupValue(ctx, data[i]);+    JS_SetOpaque(func_obj, s);+    js_function_set_properties(ctx, func_obj,+                               JS_ATOM_empty_string, length);+    return func_obj;+}++static JSContext *js_autoinit_get_realm(JSProperty *pr)+{+    return (JSContext *)(pr->u.init.realm_and_id & ~3);+}++static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)+{+    return pr->u.init.realm_and_id & 3;+}++static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)+{+    JS_FreeContext(js_autoinit_get_realm(pr));+}++static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,+                             JS_MarkFunc *mark_func)+{+    mark_func(rt, &js_autoinit_get_realm(pr)->header);+}++static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)+{+    if (unlikely(prop_flags & JS_PROP_TMASK)) {+        if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+            if (pr->u.getset.getter)+                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+            if (pr->u.getset.setter)+                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+        } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+            free_var_ref(rt, pr->u.var_ref);+        } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+            js_autoinit_free(rt, pr);+        }+    } else {+        JS_FreeValueRT(rt, pr->u.value);+    }+}++static force_inline JSShapeProperty *find_own_property1(JSObject *p,+                                                        JSAtom atom)+{+    JSShape *sh;+    JSShapeProperty *pr, *prop;+    intptr_t h;+    sh = p->shape;+    h = (uintptr_t)atom & sh->prop_hash_mask;+    h = sh->prop_hash_end[-h - 1];+    prop = get_shape_prop(sh);+    while (h) {+        pr = &prop[h - 1];+        if (likely(pr->atom == atom)) {+            return pr;+        }+        h = pr->hash_next;+    }+    return NULL;+}++static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,+                                                       JSObject *p,+                                                       JSAtom atom)+{+    JSShape *sh;+    JSShapeProperty *pr, *prop;+    intptr_t h;+    sh = p->shape;+    h = (uintptr_t)atom & sh->prop_hash_mask;+    h = sh->prop_hash_end[-h - 1];+    prop = get_shape_prop(sh);+    while (h) {+        pr = &prop[h - 1];+        if (likely(pr->atom == atom)) {+            *ppr = &p->prop[h - 1];+            /* the compiler should be able to assume that pr != NULL here */+            return pr;+        }+        h = pr->hash_next;+    }+    *ppr = NULL;+    return NULL;+}++/* indicate that the object may be part of a function prototype cycle */+static void set_cycle_flag(JSContext *ctx, JSValueConst obj)+{+}++static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)+{+    if (var_ref) {+        assert(var_ref->header.ref_count > 0);+        if (--var_ref->header.ref_count == 0) {+            if (var_ref->is_detached) {+                JS_FreeValueRT(rt, var_ref->value);+                remove_gc_object(&var_ref->header);+            } else {+                list_del(&var_ref->header.link); /* still on the stack */+            }+            js_free_rt(rt, var_ref);+        }+    }+}++static void js_array_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    int i;++    for(i = 0; i < p->u.array.count; i++) {+        JS_FreeValueRT(rt, p->u.array.u.values[i]);+    }+    js_free_rt(rt, p->u.array.u.values);+}++static void js_array_mark(JSRuntime *rt, JSValueConst val,+                          JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    int i;++    for(i = 0; i < p->u.array.count; i++) {+        JS_MarkValue(rt, p->u.array.u.values[i], mark_func);+    }+}++static void js_object_data_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JS_FreeValueRT(rt, p->u.object_data);+    p->u.object_data = JS_UNDEFINED;+}++static void js_object_data_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JS_MarkValue(rt, p->u.object_data, mark_func);+}++static void js_c_function_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);++    if (p->u.cfunc.realm)+        JS_FreeContext(p->u.cfunc.realm);+}++static void js_c_function_mark(JSRuntime *rt, JSValueConst val,+                               JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);++    if (p->u.cfunc.realm)+        mark_func(rt, &p->u.cfunc.realm->header);+}++static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p1, *p = JS_VALUE_GET_OBJ(val);+    JSFunctionBytecode *b;+    JSVarRef **var_refs;+    int i;++    p1 = p->u.func.home_object;+    if (p1) {+        JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));+    }+    b = p->u.func.function_bytecode;+    if (b) {+        var_refs = p->u.func.var_refs;+        if (var_refs) {+            for(i = 0; i < b->closure_var_count; i++)+                free_var_ref(rt, var_refs[i]);+            js_free_rt(rt, var_refs);+        }+        JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));+    }+}++static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,+                                      JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSVarRef **var_refs = p->u.func.var_refs;+    JSFunctionBytecode *b = p->u.func.function_bytecode;+    int i;++    if (p->u.func.home_object) {+        JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),+                     mark_func);+    }+    if (b) {+        if (var_refs) {+            for(i = 0; i < b->closure_var_count; i++) {+                JSVarRef *var_ref = var_refs[i];+                if (var_ref && var_ref->is_detached) {+                    mark_func(rt, &var_ref->header);+                }+            }+        }+        /* must mark the function bytecode because template objects may be+           part of a cycle */+        JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);+    }+}++static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSBoundFunction *bf = p->u.bound_function;+    int i;++    JS_FreeValueRT(rt, bf->func_obj);+    JS_FreeValueRT(rt, bf->this_val);+    for(i = 0; i < bf->argc; i++) {+        JS_FreeValueRT(rt, bf->argv[i]);+    }+    js_free_rt(rt, bf);+}++static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSBoundFunction *bf = p->u.bound_function;+    int i;++    JS_MarkValue(rt, bf->func_obj, mark_func);+    JS_MarkValue(rt, bf->this_val, mark_func);+    for(i = 0; i < bf->argc; i++)+        JS_MarkValue(rt, bf->argv[i], mark_func);+}++static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSForInIterator *it = p->u.for_in_iterator;+    JS_FreeValueRT(rt, it->obj);+    js_free_rt(rt, it);+}++static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSForInIterator *it = p->u.for_in_iterator;+    JS_MarkValue(rt, it->obj, mark_func);+}++static void free_object(JSRuntime *rt, JSObject *p)+{+    int i;+    JSClassFinalizer *finalizer;+    JSShape *sh;+    JSShapeProperty *pr;++    p->free_mark = 1; /* used to tell the object is invalid when+                         freeing cycles */+    /* free all the fields */+    sh = p->shape;+    pr = get_shape_prop(sh);+    for(i = 0; i < sh->prop_count; i++) {+        free_property(rt, &p->prop[i], pr->flags);+        pr++;+    }+    js_free_rt(rt, p->prop);+    /* as an optimization we destroy the shape immediately without+       putting it in gc_zero_ref_count_list */+    js_free_shape(rt, sh);++    /* fail safe */+    p->shape = NULL;+    p->prop = NULL;++    if (unlikely(p->first_weak_ref)) {+        reset_weak_ref(rt, p);+    }++    finalizer = rt->class_array[p->class_id].finalizer;+    if (finalizer)+        (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));++    /* fail safe */+    p->class_id = 0;+    p->u.opaque = NULL;+    p->u.func.var_refs = NULL;+    p->u.func.home_object = NULL;++    remove_gc_object(&p->header);+    if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {+        list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);+    } else {+        js_free_rt(rt, p);+    }+}++static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)+{+    switch(gp->gc_obj_type) {+    case JS_GC_OBJ_TYPE_JS_OBJECT:+        free_object(rt, (JSObject *)gp);+        break;+    case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+        free_function_bytecode(rt, (JSFunctionBytecode *)gp);+        break;+    default:+        abort();+    }+}++static void free_zero_refcount(JSRuntime *rt)+{+    struct list_head *el;+    JSGCObjectHeader *p;+    +    rt->gc_phase = JS_GC_PHASE_DECREF;+    for(;;) {+        el = rt->gc_zero_ref_count_list.next;+        if (el == &rt->gc_zero_ref_count_list)+            break;+        p = list_entry(el, JSGCObjectHeader, link);+        assert(p->ref_count == 0);+        free_gc_object(rt, p);+    }+    rt->gc_phase = JS_GC_PHASE_NONE;+}++/* called with the ref_count of 'v' reaches zero. */+void __JS_FreeValueRT(JSRuntime *rt, JSValue v)+{+    uint32_t tag = JS_VALUE_GET_TAG(v);++#ifdef DUMP_FREE+    {+        printf("Freeing ");+        if (tag == JS_TAG_OBJECT) {+            JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));+        } else {+            JS_DumpValueShort(rt, v);+            printf("\n");+        }+    }+#endif++    switch(tag) {+    case JS_TAG_STRING:+        {+            JSString *p = JS_VALUE_GET_STRING(v);+            if (p->atom_type) {+                JS_FreeAtomStruct(rt, p);+            } else {+#ifdef DUMP_LEAKS+                list_del(&p->link);+#endif+                js_free_rt(rt, p);+            }+        }+        break;+    case JS_TAG_OBJECT:+    case JS_TAG_FUNCTION_BYTECODE:+        {+            JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);+            if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {+                list_del(&p->link);+                list_add(&p->link, &rt->gc_zero_ref_count_list);+                if (rt->gc_phase == JS_GC_PHASE_NONE) {+                    free_zero_refcount(rt);+                }+            }+        }+        break;+    case JS_TAG_MODULE:+        abort(); /* never freed here */+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *bf = JS_VALUE_GET_PTR(v);+            bf_delete(&bf->num);+            js_free_rt(rt, bf);+        }+        break;+    case JS_TAG_BIG_DECIMAL:+        {+            JSBigDecimal *bf = JS_VALUE_GET_PTR(v);+            bfdec_delete(&bf->num);+            js_free_rt(rt, bf);+        }+        break;+#endif+    case JS_TAG_SYMBOL:+        {+            JSAtomStruct *p = JS_VALUE_GET_PTR(v);+            JS_FreeAtomStruct(rt, p);+        }+        break;+    default:+        printf("__JS_FreeValue: unknown tag=%d\n", tag);+        abort();+    }+}++void __JS_FreeValue(JSContext *ctx, JSValue v)+{+    __JS_FreeValueRT(ctx->rt, v);+}++/* garbage collection */++static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,+                          JSGCObjectTypeEnum type)+{+    h->mark = 0;+    h->gc_obj_type = type;+    list_add_tail(&h->link, &rt->gc_obj_list);+}++static void remove_gc_object(JSGCObjectHeader *h)+{+    list_del(&h->link);+}++void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)+{+    if (JS_VALUE_HAS_REF_COUNT(val)) {+        switch(JS_VALUE_GET_TAG(val)) {+        case JS_TAG_OBJECT:+        case JS_TAG_FUNCTION_BYTECODE:+            mark_func(rt, JS_VALUE_GET_PTR(val));+            break;+        default:+            break;+        }+    }+}++static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,+                          JS_MarkFunc *mark_func)+{+    switch(gp->gc_obj_type) {+    case JS_GC_OBJ_TYPE_JS_OBJECT:+        {+            JSObject *p = (JSObject *)gp;+            JSShapeProperty *prs;+            JSShape *sh;+            int i;+            sh = p->shape;+            mark_func(rt, &sh->header);+            /* mark all the fields */+            prs = get_shape_prop(sh);+            for(i = 0; i < sh->prop_count; i++) {+                JSProperty *pr = &p->prop[i];+                if (prs->atom != JS_ATOM_NULL) {+                    if (prs->flags & JS_PROP_TMASK) {+                        if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+                            if (pr->u.getset.getter)+                                mark_func(rt, &pr->u.getset.getter->header);+                            if (pr->u.getset.setter)+                                mark_func(rt, &pr->u.getset.setter->header);+                        } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                            if (pr->u.var_ref->is_detached) {+                                /* Note: the tag does not matter+                                   provided it is a GC object */+                                mark_func(rt, &pr->u.var_ref->header);+                            }+                        } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                            js_autoinit_mark(rt, pr, mark_func);+                        }+                    } else {+                        JS_MarkValue(rt, pr->u.value, mark_func);+                    }+                }+                prs++;+            }++            if (p->class_id != JS_CLASS_OBJECT) {+                JSClassGCMark *gc_mark;+                gc_mark = rt->class_array[p->class_id].gc_mark;+                if (gc_mark)+                    gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);+            }+        }+        break;+    case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+        /* the template objects can be part of a cycle */+        {+            JSFunctionBytecode *b = (JSFunctionBytecode *)gp;+            int i;+            for(i = 0; i < b->cpool_count; i++) {+                JS_MarkValue(rt, b->cpool[i], mark_func);+            }+            if (b->realm)+                mark_func(rt, &b->realm->header);+        }+        break;+    case JS_GC_OBJ_TYPE_VAR_REF:+        {+            JSVarRef *var_ref = (JSVarRef *)gp;+            /* only detached variable referenced are taken into account */+            assert(var_ref->is_detached);+            JS_MarkValue(rt, *var_ref->pvalue, mark_func);+        }+        break;+    case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:+        {+            JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;+            if (s->is_active)+                async_func_mark(rt, &s->func_state, mark_func);+            JS_MarkValue(rt, s->resolving_funcs[0], mark_func);+            JS_MarkValue(rt, s->resolving_funcs[1], mark_func);+        }+        break;+    case JS_GC_OBJ_TYPE_SHAPE:+        {+            JSShape *sh = (JSShape *)gp;+            if (sh->proto != NULL) {+                mark_func(rt, &sh->proto->header);+            }+        }+        break;+    case JS_GC_OBJ_TYPE_JS_CONTEXT:+        {+            JSContext *ctx = (JSContext *)gp;+            JS_MarkContext(rt, ctx, mark_func);+        }+        break;+    default:+        abort();+    }+}++static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)+{+    assert(p->ref_count > 0);+    p->ref_count--;+    if (p->ref_count == 0 && p->mark == 1) {+        list_del(&p->link);+        list_add_tail(&p->link, &rt->tmp_obj_list);+    }+}++static void gc_decref(JSRuntime *rt)+{+    struct list_head *el, *el1;+    JSGCObjectHeader *p;+    +    init_list_head(&rt->tmp_obj_list);++    /* decrement the refcount of all the children of all the GC+       objects and move the GC objects with zero refcount to+       tmp_obj_list */+    list_for_each_safe(el, el1, &rt->gc_obj_list) {+        p = list_entry(el, JSGCObjectHeader, link);+        assert(p->mark == 0);+        mark_children(rt, p, gc_decref_child);+        p->mark = 1;+        if (p->ref_count == 0) {+            list_del(&p->link);+            list_add_tail(&p->link, &rt->tmp_obj_list);+        }+    }+}++static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)+{+    p->ref_count++;+    if (p->ref_count == 1) {+        /* ref_count was 0: remove from tmp_obj_list and add at the+           end of gc_obj_list */+        list_del(&p->link);+        list_add_tail(&p->link, &rt->gc_obj_list);+        p->mark = 0; /* reset the mark for the next GC call */+    }+}++static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)+{+    p->ref_count++;+}++static void gc_scan(JSRuntime *rt)+{+    struct list_head *el;+    JSGCObjectHeader *p;++    /* keep the objects with a refcount > 0 and their children. */+    list_for_each(el, &rt->gc_obj_list) {+        p = list_entry(el, JSGCObjectHeader, link);+        assert(p->ref_count > 0);+        p->mark = 0; /* reset the mark for the next GC call */+        mark_children(rt, p, gc_scan_incref_child);+    }+    +    /* restore the refcount of the objects to be deleted. */+    list_for_each(el, &rt->tmp_obj_list) {+        p = list_entry(el, JSGCObjectHeader, link);+        mark_children(rt, p, gc_scan_incref_child2);+    }+}++static void gc_free_cycles(JSRuntime *rt)+{+    struct list_head *el, *el1;+    JSGCObjectHeader *p;+#ifdef DUMP_GC_FREE+    BOOL header_done = FALSE;+#endif++    rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;++    for(;;) {+        el = rt->tmp_obj_list.next;+        if (el == &rt->tmp_obj_list)+            break;+        p = list_entry(el, JSGCObjectHeader, link);+        /* Only need to free the GC object associated with JS+           values. The rest will be automatically removed because they+           must be referenced by them. */+        switch(p->gc_obj_type) {+        case JS_GC_OBJ_TYPE_JS_OBJECT:+        case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+#ifdef DUMP_GC_FREE+            if (!header_done) {+                printf("Freeing cycles:\n");+                JS_DumpObjectHeader(rt);+                header_done = TRUE;+            }+            JS_DumpGCObject(rt, p);+#endif+            free_gc_object(rt, p);+            break;+        default:+            list_del(&p->link);+            list_add_tail(&p->link, &rt->gc_zero_ref_count_list);+            break;+        }+    }+    rt->gc_phase = JS_GC_PHASE_NONE;+           +    list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {+        p = list_entry(el, JSGCObjectHeader, link);+        assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||+               p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);+        js_free_rt(rt, p);+    }++    init_list_head(&rt->gc_zero_ref_count_list);+}++void JS_RunGC(JSRuntime *rt)+{+    /* decrement the reference of the children of each object. mark =+       1 after this pass. */+    gc_decref(rt);++    /* keep the GC objects with a non zero refcount and their childs */+    gc_scan(rt);++    /* free the GC objects in a cycle */+    gc_free_cycles(rt);+}++/* Return false if not an object or if the object has already been+   freed (zombie objects are visible in finalizers when freeing+   cycles). */+BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)+{+    JSObject *p;+    if (!JS_IsObject(obj))+        return FALSE;+    p = JS_VALUE_GET_OBJ(obj);+    return !p->free_mark;+}++/* Compute memory used by various object types */+/* XXX: poor man's approach to handling multiply referenced objects */+typedef struct JSMemoryUsage_helper {+    double memory_used_count;+    double str_count;+    double str_size;+    int64_t js_func_count;+    double js_func_size;+    int64_t js_func_code_size;+    int64_t js_func_pc2line_count;+    int64_t js_func_pc2line_size;+} JSMemoryUsage_helper;++static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);++static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)+{+    if (!str->atom_type) {  /* atoms are handled separately */+        double s_ref_count = str->header.ref_count;+        hp->str_count += 1 / s_ref_count;+        hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) ++                          1 - str->is_wide_char) / s_ref_count);+    }+}++static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)+{+    int memory_used_count, js_func_size, i;++    memory_used_count = 0;+    js_func_size = offsetof(JSFunctionBytecode, debug);+    if (b->vardefs) {+        js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);+    }+    if (b->cpool) {+        js_func_size += b->cpool_count * sizeof(*b->cpool);+        for (i = 0; i < b->cpool_count; i++) {+            JSValueConst val = b->cpool[i];+            compute_value_size(val, hp);+        }+    }+    if (b->closure_var) {+        js_func_size += b->closure_var_count * sizeof(*b->closure_var);+    }+    if (!b->read_only_bytecode && b->byte_code_buf) {+        hp->js_func_code_size += b->byte_code_len;+    }+    if (b->has_debug) {+        js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);+        if (b->debug.source) {+            memory_used_count++;+            js_func_size += b->debug.source_len + 1;+        }+        if (b->debug.pc2line_len) {+            memory_used_count++;+            hp->js_func_pc2line_count += 1;+            hp->js_func_pc2line_size += b->debug.pc2line_len;+        }+    }+    hp->js_func_size += js_func_size;+    hp->js_func_count += 1;+    hp->memory_used_count += memory_used_count;+}++static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)+{+    switch(JS_VALUE_GET_TAG(val)) {+    case JS_TAG_STRING:+        compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+    case JS_TAG_BIG_DECIMAL:+        /* should track JSBigFloat usage */+        break;+#endif+    }+}++void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)+{+    struct list_head *el, *el1;+    int i;+    JSMemoryUsage_helper mem = { 0 }, *hp = &mem;++    memset(s, 0, sizeof(*s));+    s->malloc_count = rt->malloc_state.malloc_count;+    s->malloc_size = rt->malloc_state.malloc_size;+    s->malloc_limit = rt->malloc_state.malloc_limit;++    s->memory_used_count = 2; /* rt + rt->class_array */+    s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;++    list_for_each(el, &rt->context_list) {+        JSContext *ctx = list_entry(el, JSContext, link);+        JSShape *sh = ctx->array_shape;+        s->memory_used_count += 2; /* ctx + ctx->class_proto */+        s->memory_used_size += sizeof(JSContext) ++            sizeof(JSValue) * rt->class_count;+        s->binary_object_count += ctx->binary_object_count;+        s->binary_object_size += ctx->binary_object_size;++        /* the hashed shapes are counted separately */+        if (sh && !sh->is_hashed) {+            int hash_size = sh->prop_hash_mask + 1;+            s->shape_count++;+            s->shape_size += get_shape_size(hash_size, sh->prop_size);+        }+        list_for_each(el1, &ctx->loaded_modules) {+            JSModuleDef *m = list_entry(el1, JSModuleDef, link);+            s->memory_used_count += 1;+            s->memory_used_size += sizeof(*m);+            if (m->req_module_entries) {+                s->memory_used_count += 1;+                s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);+            }+            if (m->export_entries) {+                s->memory_used_count += 1;+                s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);+                for (i = 0; i < m->export_entries_count; i++) {+                    JSExportEntry *me = &m->export_entries[i];+                    if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {+                        /* potential multiple count */+                        s->memory_used_count += 1;+                        compute_value_size(me->u.local.var_ref->value, hp);+                    }+                }+            }+            if (m->star_export_entries) {+                s->memory_used_count += 1;+                s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);+            }+            if (m->import_entries) {+                s->memory_used_count += 1;+                s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);+            }+            compute_value_size(m->module_ns, hp);+            compute_value_size(m->func_obj, hp);+        }+    }++    list_for_each(el, &rt->gc_obj_list) {+        JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);+        JSObject *p;+        JSShape *sh;+        JSShapeProperty *prs;++        /* XXX: could count the other GC object types too */+        if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {+            compute_bytecode_size((JSFunctionBytecode *)gp, hp);+            continue;+        } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {+            continue;+        }+        p = (JSObject *)gp;+        sh = p->shape;+        s->obj_count++;+        if (p->prop) {+            s->memory_used_count++;+            s->prop_size += sh->prop_size * sizeof(*p->prop);+            s->prop_count += sh->prop_count;+            prs = get_shape_prop(sh);+            for(i = 0; i < sh->prop_count; i++) {+                JSProperty *pr = &p->prop[i];+                if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {+                    compute_value_size(pr->u.value, hp);+                }+                prs++;+            }+        }+        /* the hashed shapes are counted separately */+        if (!sh->is_hashed) {+            int hash_size = sh->prop_hash_mask + 1;+            s->shape_count++;+            s->shape_size += get_shape_size(hash_size, sh->prop_size);+        }++        switch(p->class_id) {+        case JS_CLASS_ARRAY:             /* u.array | length */+        case JS_CLASS_ARGUMENTS:         /* u.array | length */+            s->array_count++;+            if (p->fast_array) {+                s->fast_array_count++;+                if (p->u.array.u.values) {+                    s->memory_used_count++;+                    s->memory_used_size += p->u.array.count *+                        sizeof(*p->u.array.u.values);+                    s->fast_array_elements += p->u.array.count;+                    for (i = 0; i < p->u.array.count; i++) {+                        compute_value_size(p->u.array.u.values[i], hp);+                    }+                }+            }+            break;+        case JS_CLASS_NUMBER:            /* u.object_data */+        case JS_CLASS_STRING:            /* u.object_data */+        case JS_CLASS_BOOLEAN:           /* u.object_data */+        case JS_CLASS_SYMBOL:            /* u.object_data */+        case JS_CLASS_DATE:              /* u.object_data */+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT:           /* u.object_data */+        case JS_CLASS_BIG_FLOAT:         /* u.object_data */+        case JS_CLASS_BIG_DECIMAL:         /* u.object_data */+#endif+            compute_value_size(p->u.object_data, hp);+            break;+        case JS_CLASS_C_FUNCTION:        /* u.cfunc */+            s->c_func_count++;+            break;+        case JS_CLASS_BYTECODE_FUNCTION: /* u.func */+            {+                JSFunctionBytecode *b = p->u.func.function_bytecode;+                JSVarRef **var_refs = p->u.func.var_refs;+                /* home_object: object will be accounted for in list scan */+                if (var_refs) {+                    s->memory_used_count++;+                    s->js_func_size += b->closure_var_count * sizeof(*var_refs);+                    for (i = 0; i < b->closure_var_count; i++) {+                        if (var_refs[i]) {+                            double ref_count = var_refs[i]->header.ref_count;+                            s->memory_used_count += 1 / ref_count;+                            s->js_func_size += sizeof(*var_refs[i]) / ref_count;+                            /* handle non object closed values */+                            if (var_refs[i]->pvalue == &var_refs[i]->value) {+                                /* potential multiple count */+                                compute_value_size(var_refs[i]->value, hp);+                            }+                        }+                    }+                }+            }+            break;+        case JS_CLASS_BOUND_FUNCTION:    /* u.bound_function */+            {+                JSBoundFunction *bf = p->u.bound_function;+                /* func_obj and this_val are objects */+                for (i = 0; i < bf->argc; i++) {+                    compute_value_size(bf->argv[i], hp);+                }+                s->memory_used_count += 1;+                s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);+            }+            break;+        case JS_CLASS_C_FUNCTION_DATA:   /* u.c_function_data_record */+            {+                JSCFunctionDataRecord *fd = p->u.c_function_data_record;+                if (fd) {+                    for (i = 0; i < fd->data_len; i++) {+                        compute_value_size(fd->data[i], hp);+                    }+                    s->memory_used_count += 1;+                    s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);+                }+            }+            break;+        case JS_CLASS_REGEXP:            /* u.regexp */+            compute_jsstring_size(p->u.regexp.pattern, hp);+            compute_jsstring_size(p->u.regexp.bytecode, hp);+            break;++        case JS_CLASS_FOR_IN_ITERATOR:   /* u.for_in_iterator */+            {+                JSForInIterator *it = p->u.for_in_iterator;+                if (it) {+                    compute_value_size(it->obj, hp);+                    s->memory_used_count += 1;+                    s->memory_used_size += sizeof(*it);+                }+            }+            break;+        case JS_CLASS_ARRAY_BUFFER:      /* u.array_buffer */+        case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */+            {+                JSArrayBuffer *abuf = p->u.array_buffer;+                if (abuf) {+                    s->memory_used_count += 1;+                    s->memory_used_size += sizeof(*abuf);+                    if (abuf->data) {+                        s->memory_used_count += 1;+                        s->memory_used_size += abuf->byte_length;+                    }+                }+            }+            break;+        case JS_CLASS_GENERATOR:         /* u.generator_data */+        case JS_CLASS_UINT8C_ARRAY:      /* u.typed_array / u.array */+        case JS_CLASS_INT8_ARRAY:        /* u.typed_array / u.array */+        case JS_CLASS_UINT8_ARRAY:       /* u.typed_array / u.array */+        case JS_CLASS_INT16_ARRAY:       /* u.typed_array / u.array */+        case JS_CLASS_UINT16_ARRAY:      /* u.typed_array / u.array */+        case JS_CLASS_INT32_ARRAY:       /* u.typed_array / u.array */+        case JS_CLASS_UINT32_ARRAY:      /* u.typed_array / u.array */+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT64_ARRAY:   /* u.typed_array / u.array */+        case JS_CLASS_BIG_UINT64_ARRAY:  /* u.typed_array / u.array */+#endif+        case JS_CLASS_FLOAT32_ARRAY:     /* u.typed_array / u.array */+        case JS_CLASS_FLOAT64_ARRAY:     /* u.typed_array / u.array */+        case JS_CLASS_DATAVIEW:          /* u.typed_array */+#ifdef CONFIG_BIGNUM+        case JS_CLASS_FLOAT_ENV:         /* u.float_env */+#endif+        case JS_CLASS_MAP:               /* u.map_state */+        case JS_CLASS_SET:               /* u.map_state */+        case JS_CLASS_WEAKMAP:           /* u.map_state */+        case JS_CLASS_WEAKSET:           /* u.map_state */+        case JS_CLASS_MAP_ITERATOR:      /* u.map_iterator_data */+        case JS_CLASS_SET_ITERATOR:      /* u.map_iterator_data */+        case JS_CLASS_ARRAY_ITERATOR:    /* u.array_iterator_data */+        case JS_CLASS_STRING_ITERATOR:   /* u.array_iterator_data */+        case JS_CLASS_PROXY:             /* u.proxy_data */+        case JS_CLASS_PROMISE:           /* u.promise_data */+        case JS_CLASS_PROMISE_RESOLVE_FUNCTION:  /* u.promise_function_data */+        case JS_CLASS_PROMISE_REJECT_FUNCTION:   /* u.promise_function_data */+        case JS_CLASS_ASYNC_FUNCTION_RESOLVE:    /* u.async_function_data */+        case JS_CLASS_ASYNC_FUNCTION_REJECT:     /* u.async_function_data */+        case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR:  /* u.async_from_sync_iterator_data */+        case JS_CLASS_ASYNC_GENERATOR:   /* u.async_generator_data */+            /* TODO */+        default:+            /* XXX: class definition should have an opaque block size */+            if (p->u.opaque) {+                s->memory_used_count += 1;+            }+            break;+        }+    }+    s->obj_size += s->obj_count * sizeof(JSObject);++    /* hashed shapes */+    s->memory_used_count++; /* rt->shape_hash */+    s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;+    for(i = 0; i < rt->shape_hash_size; i++) {+        JSShape *sh;+        for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {+            int hash_size = sh->prop_hash_mask + 1;+            s->shape_count++;+            s->shape_size += get_shape_size(hash_size, sh->prop_size);+        }+    }++    /* atoms */+    s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */+    s->atom_count = rt->atom_count;+    s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size ++        sizeof(rt->atom_hash[0]) * rt->atom_hash_size;+    for(i = 0; i < rt->atom_size; i++) {+        JSAtomStruct *p = rt->atom_array[i];+        if (!atom_is_free(p)) {+            s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) ++                             1 - p->is_wide_char);+        }+    }+    s->str_count = round(mem.str_count);+    s->str_size = round(mem.str_size);+    s->js_func_count = mem.js_func_count;+    s->js_func_size = round(mem.js_func_size);+    s->js_func_code_size = mem.js_func_code_size;+    s->js_func_pc2line_count = mem.js_func_pc2line_count;+    s->js_func_pc2line_size = mem.js_func_pc2line_size;+    s->memory_used_count += round(mem.memory_used_count) ++        s->atom_count + s->str_count ++        s->obj_count + s->shape_count ++        s->js_func_count + s->js_func_pc2line_count;+    s->memory_used_size += s->atom_size + s->str_size ++        s->obj_size + s->prop_size + s->shape_size ++        s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;+}++void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)+{+    fprintf(fp, "QuickJS memory usage -- "+#ifdef CONFIG_BIGNUM+            "BigNum "+#endif+            CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",+            (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);+#if 1+    if (rt) {+        static const struct {+            const char *name;+            size_t size;+        } object_types[] = {+            { "JSRuntime", sizeof(JSRuntime) },+            { "JSContext", sizeof(JSContext) },+            { "JSObject", sizeof(JSObject) },+            { "JSString", sizeof(JSString) },+            { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },+        };+        int i, usage_size_ok = 0;+        for(i = 0; i < countof(object_types); i++) {+            unsigned int size = object_types[i].size;+            void *p = js_malloc_rt(rt, size);+            if (p) {+                unsigned int size1 = js_malloc_usable_size_rt(rt, p);+                if (size1 >= size) {+                    usage_size_ok = 1;+                    fprintf(fp, "  %3u + %-2u  %s\n",+                            size, size1 - size, object_types[i].name);+                }+                js_free_rt(rt, p);+            }+        }+        if (!usage_size_ok) {+            fprintf(fp, "  malloc_usable_size unavailable\n");+        }+        {+            int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };+            int class_id;+            struct list_head *el;+            list_for_each(el, &rt->gc_obj_list) {+                JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);+                JSObject *p;+                if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {+                    p = (JSObject *)gp;+                    obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;+                }+            }+            fprintf(fp, "\n" "JSObject classes\n");+            if (obj_classes[0])+                fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[0], 0, "none");+            for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {+                if (obj_classes[class_id]) {+                    char buf[ATOM_GET_STR_BUF_SIZE];+                    fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[class_id], class_id,+                            JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));+                }+            }+            if (obj_classes[JS_CLASS_INIT_COUNT])+                fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");+        }+        fprintf(fp, "\n");+    }+#endif+    fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");++    if (s->malloc_count) {+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per block)\n",+                "memory allocated", s->malloc_count, s->malloc_size,+                (double)s->malloc_size / s->malloc_count);+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%d overhead, %0.1f average slack)\n",+                "memory used", s->memory_used_count, s->memory_used_size,+                MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /+                                  s->memory_used_count));+    }+    if (s->atom_count) {+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per atom)\n",+                "atoms", s->atom_count, s->atom_size,+                (double)s->atom_size / s->atom_count);+    }+    if (s->str_count) {+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per string)\n",+                "strings", s->str_count, s->str_size,+                (double)s->str_size / s->str_count);+    }+    if (s->obj_count) {+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",+                "objects", s->obj_count, s->obj_size,+                (double)s->obj_size / s->obj_count);+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",+                "  properties", s->prop_count, s->prop_size,+                (double)s->prop_count / s->obj_count);+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per shape)\n",+                "  shapes", s->shape_count, s->shape_size,+                (double)s->shape_size / s->shape_count);+    }+    if (s->js_func_count) {+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",+                "bytecode functions", s->js_func_count, s->js_func_size);+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",+                "  bytecode", s->js_func_count, s->js_func_code_size,+                (double)s->js_func_code_size / s->js_func_count);+        if (s->js_func_pc2line_count) {+            fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",+                    "  pc2line", s->js_func_pc2line_count,+                    s->js_func_pc2line_size,+                    (double)s->js_func_pc2line_size / s->js_func_pc2line_count);+        }+    }+    if (s->c_func_count) {+        fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);+    }+    if (s->array_count) {+        fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);+        if (s->fast_array_count) {+            fprintf(fp, "%-20s %8"PRId64"\n", "  fast arrays", s->fast_array_count);+            fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per fast array)\n",+                    "  elements", s->fast_array_elements,+                    s->fast_array_elements * (int)sizeof(JSValue),+                    (double)s->fast_array_elements / s->fast_array_count);+        }+    }+    if (s->binary_object_count) {+        fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",+                "binary objects", s->binary_object_count, s->binary_object_size);+    }+}++JSValue JS_GetGlobalObject(JSContext *ctx)+{+    return JS_DupValue(ctx, ctx->global_obj);+}++/* WARNING: obj is freed */+JSValue JS_Throw(JSContext *ctx, JSValue obj)+{+    JSRuntime *rt = ctx->rt;+    JS_FreeValue(ctx, rt->current_exception);+    rt->current_exception = obj;+    return JS_EXCEPTION;+}++/* return the pending exception (cannot be called twice). */+JSValue JS_GetException(JSContext *ctx)+{+    JSValue val;+    JSRuntime *rt = ctx->rt;+    val = rt->current_exception;+    rt->current_exception = JS_NULL;+    return val;+}++static void dbuf_put_leb128(DynBuf *s, uint32_t v)+{+    uint32_t a;+    for(;;) {+        a = v & 0x7f;+        v >>= 7;+        if (v != 0) {+            dbuf_putc(s, a | 0x80);+        } else {+            dbuf_putc(s, a);+            break;+        }+    }+}++static void dbuf_put_sleb128(DynBuf *s, int32_t v1)+{+    uint32_t v = v1;+    dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));+}++static int get_leb128(uint32_t *pval, const uint8_t *buf,+                      const uint8_t *buf_end)+{+    const uint8_t *ptr = buf;+    uint32_t v, a, i;+    v = 0;+    for(i = 0; i < 5; i++) {+        if (unlikely(ptr >= buf_end))+            break;+        a = *ptr++;+        v |= (a & 0x7f) << (i * 7);+        if (!(a & 0x80)) {+            *pval = v;+            return ptr - buf;+        }+    }+    *pval = 0;+    return -1;+}++static int get_sleb128(int32_t *pval, const uint8_t *buf,+                       const uint8_t *buf_end)+{+    int ret;+    uint32_t val;+    ret = get_leb128(&val, buf, buf_end);+    if (ret < 0) {+        *pval = 0;+        return -1;+    }+    *pval = (val >> 1) ^ -(val & 1);+    return ret;+}++static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,+                         uint32_t pc_value)+{+    const uint8_t *p_end, *p;+    int new_line_num, line_num, pc, v, ret;+    unsigned int op;++    if (!b->has_debug || !b->debug.pc2line_buf) {+        /* function was stripped */+        return -1;+    }++    p = b->debug.pc2line_buf;+    p_end = p + b->debug.pc2line_len;+    pc = 0;+    line_num = b->debug.line_num;+    while (p < p_end) {+        op = *p++;+        if (op == 0) {+            uint32_t val;+            ret = get_leb128(&val, p, p_end);+            if (ret < 0)+                goto fail;+            pc += val;+            p += ret;+            ret = get_sleb128(&v, p, p_end);+            if (ret < 0) {+            fail:+                /* should never happen */+                return b->debug.line_num;+            }+            p += ret;+            new_line_num = line_num + v;+        } else {+            op -= PC2LINE_OP_FIRST;+            pc += (op / PC2LINE_RANGE);+            new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;+        }+        if (pc_value < pc)+            return line_num;+        line_num = new_line_num;+    }+    return line_num;+}++/* in order to avoid executing arbitrary code during the stack trace+   generation, we only look at simple 'name' properties containing a+   string. */+static const char *get_func_name(JSContext *ctx, JSValueConst func)+{+    JSProperty *pr;+    JSShapeProperty *prs;+    JSValueConst val;+    +    if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)+        return NULL;+    prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);+    if (!prs)+        return NULL;+    if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)+        return NULL;+    val = pr->u.value;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)+        return NULL;+    return JS_ToCString(ctx, val);+}++#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)+/* only taken into account if filename is provided */+#define JS_BACKTRACE_FLAG_SINGLE_LEVEL     (1 << 1)++/* if filename != NULL, an additional level is added with the filename+   and line number information (used for parse error). */+static void build_backtrace(JSContext *ctx, JSValueConst error_obj,+                            const char *filename, int line_num,+                            int backtrace_flags)+{+    JSStackFrame *sf;+    JSValue str;+    DynBuf dbuf;+    const char *func_name_str;+    const char *str1;+    JSObject *p;+    BOOL backtrace_barrier;+    +    js_dbuf_init(ctx, &dbuf);+    if (filename) {+        dbuf_printf(&dbuf, "    at %s", filename);+        if (line_num != -1)+            dbuf_printf(&dbuf, ":%d", line_num);+        dbuf_putc(&dbuf, '\n');+        str = JS_NewString(ctx, filename);+        JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+        JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+        if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)+            goto done;+    }+    for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {+        if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {+            backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;+            continue;+        }+        func_name_str = get_func_name(ctx, sf->cur_func);+        if (!func_name_str || func_name_str[0] == '\0')+            str1 = "<anonymous>";+        else+            str1 = func_name_str;+        dbuf_printf(&dbuf, "    at %s", str1);+        JS_FreeCString(ctx, func_name_str);++        p = JS_VALUE_GET_OBJ(sf->cur_func);+        backtrace_barrier = FALSE;+        if (js_class_has_bytecode(p->class_id)) {+            JSFunctionBytecode *b;+            const char *atom_str;+            int line_num1;++            b = p->u.func.function_bytecode;+            backtrace_barrier = b->backtrace_barrier;+            if (b->has_debug) {+                line_num1 = find_line_num(ctx, b,+                                          sf->cur_pc - b->byte_code_buf - 1);+                atom_str = JS_AtomToCString(ctx, b->debug.filename);+                dbuf_printf(&dbuf, " (%s",+                            atom_str ? atom_str : "<null>");+                JS_FreeCString(ctx, atom_str);+                if (line_num1 != -1)+                    dbuf_printf(&dbuf, ":%d", line_num1);+                dbuf_putc(&dbuf, ')');+            }+        } else {+            dbuf_printf(&dbuf, " (native)");+        }+        dbuf_putc(&dbuf, '\n');+        /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */+        if (backtrace_barrier)+            break;+    }+ done:+    dbuf_putc(&dbuf, '\0');+    if (dbuf_error(&dbuf))+        str = JS_NULL;+    else+        str = JS_NewString(ctx, (char *)dbuf.buf);+    dbuf_free(&dbuf);+    JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+}++/* Note: it is important that no exception is returned by this function */+static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(obj);+    if (p->class_id != JS_CLASS_ERROR)+        return FALSE;+    if (find_own_property1(p, JS_ATOM_stack))+        return FALSE;+    return TRUE;+}++JSValue JS_NewError(JSContext *ctx)+{+    return JS_NewObjectClass(ctx, JS_CLASS_ERROR);+}++static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,+                              const char *fmt, va_list ap, BOOL add_backtrace)+{+    char buf[256];+    JSValue obj, ret;++    vsnprintf(buf, sizeof(buf), fmt, ap);+    obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],+                                 JS_CLASS_ERROR);+    if (unlikely(JS_IsException(obj))) {+        /* out of memory: throw JS_NULL to avoid recursing */+        obj = JS_NULL;+    } else {+        JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,+                               JS_NewString(ctx, buf),+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    }+    if (add_backtrace) {+        build_backtrace(ctx, obj, NULL, 0, 0);+    }+    ret = JS_Throw(ctx, obj);+    return ret;+}++static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,+                             const char *fmt, va_list ap)+{+    JSRuntime *rt = ctx->rt;+    JSStackFrame *sf;+    BOOL add_backtrace;++    /* the backtrace is added later if called from a bytecode function */+    sf = rt->current_stack_frame;+    add_backtrace = !rt->in_out_of_memory &&+        (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));+    return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)+{+    JSValue val;+    va_list ap;++    va_start(ap, fmt);+    val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);+    va_end(ap);+    return val;+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)+{+    JSValue val;+    va_list ap;++    va_start(ap, fmt);+    val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);+    va_end(ap);+    return val;+}++static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)+{+    va_list ap;++    if ((flags & JS_PROP_THROW) ||+        ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+        va_start(ap, fmt);+        JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);+        va_end(ap);+        return -1;+    } else {+        return FALSE;+    }+}++/* never use it directly */+static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)+{+    char buf[ATOM_GET_STR_BUF_SIZE];+    return JS_ThrowTypeError(ctx, fmt,+                             JS_AtomGetStr(ctx, buf, sizeof(buf), atom));+}++/* never use it directly */+static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)+{+    char buf[ATOM_GET_STR_BUF_SIZE];+    return JS_ThrowSyntaxError(ctx, fmt,+                             JS_AtomGetStr(ctx, buf, sizeof(buf), atom));+}++/* %s is replaced by 'atom'. The macro is used so that gcc can check+    the format string. */+#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")+#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")++static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)+{+    if ((flags & JS_PROP_THROW) ||+        ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+        JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);+        return -1;+    } else {+        return FALSE;+    }+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)+{+    JSValue val;+    va_list ap;++    va_start(ap, fmt);+    val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);+    va_end(ap);+    return val;+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)+{+    JSValue val;+    va_list ap;++    va_start(ap, fmt);+    val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);+    va_end(ap);+    return val;+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)+{+    JSValue val;+    va_list ap;++    va_start(ap, fmt);+    val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);+    va_end(ap);+    return val;+}++JSValue JS_ThrowOutOfMemory(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    if (!rt->in_out_of_memory) {+        rt->in_out_of_memory = TRUE;+        JS_ThrowInternalError(ctx, "out of memory");+        rt->in_out_of_memory = FALSE;+    }+    return JS_EXCEPTION;+}++static JSValue JS_ThrowStackOverflow(JSContext *ctx)+{+    return JS_ThrowInternalError(ctx, "stack overflow");+}++static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)+{+    return JS_ThrowTypeError(ctx, "not an object");+}++static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)+{+    return JS_ThrowTypeError(ctx, "not a symbol");+}++static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)+{+    char buf[ATOM_GET_STR_BUF_SIZE];+    return JS_ThrowReferenceError(ctx, "'%s' is not defined",+                                  JS_AtomGetStr(ctx, buf, sizeof(buf), name));+}++static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)+{+    char buf[ATOM_GET_STR_BUF_SIZE];+    return JS_ThrowReferenceError(ctx, "%s is not initialized",+                                  name == JS_ATOM_NULL ? "lexical variable" :+                                  JS_AtomGetStr(ctx, buf, sizeof(buf), name));+}++static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)+{+    JSRuntime *rt = ctx->rt;+    JSAtom name;+    name = rt->class_array[class_id].class_name;+    return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);+}++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);+            return -1;+        }+    }+    return 0;+}++static inline __exception int js_poll_interrupts(JSContext *ctx)+{+    if (unlikely(--ctx->interrupt_counter <= 0)) {+        return __js_poll_interrupts(ctx);+    } else {+        return 0;+    }+}++/* return -1 (exception) or TRUE/FALSE */+static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,+                                   JSValueConst proto_val,+                                   BOOL throw_flag)+{+    JSObject *proto, *p, *p1;+    JSShape *sh;++    if (throw_flag) {+        if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||+            JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)+            goto not_obj;+    } else {+        if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+            goto not_obj;+    }+    p = JS_VALUE_GET_OBJ(obj);+    if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {+        if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {+        not_obj:+            JS_ThrowTypeErrorNotAnObject(ctx);+            return -1;+        }+        proto = NULL;+    } else {+        proto = JS_VALUE_GET_OBJ(proto_val);+    }++    if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return TRUE;++    if (unlikely(p->class_id == JS_CLASS_PROXY))+        return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);+    sh = p->shape;+    if (sh->proto == proto)+        return TRUE;+    if (!p->extensible) {+        if (throw_flag) {+            JS_ThrowTypeError(ctx, "object is not extensible");+            return -1;+        } else {+            return FALSE;+        }+    }+    if (proto) {+        /* check if there is a cycle */+        p1 = proto;+        do {+            if (p1 == p) {+                if (throw_flag) {+                    JS_ThrowTypeError(ctx, "circular prototype chain");+                    return -1;+                } else {+                    return FALSE;+                }+            }+            /* Note: for Proxy objects, proto is NULL */+            p1 = p1->shape->proto;+        } while (p1 != NULL);+        JS_DupValue(ctx, proto_val);+    }++    if (js_shape_prepare_update(ctx, p, NULL))+        return -1;+    sh = p->shape;+    if (sh->proto)+        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));+    sh->proto = proto;+    return TRUE;+}++/* return -1 (exception) or TRUE/FALSE */+int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)+{+    return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);+}++/* Only works for primitive types, otherwise return JS_NULL. */+static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)+{+    switch(JS_VALUE_GET_NORM_TAG(val)) {+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        val = ctx->class_proto[JS_CLASS_BIG_INT];+        break;+    case JS_TAG_BIG_FLOAT:+        val = ctx->class_proto[JS_CLASS_BIG_FLOAT];+        break;+    case JS_TAG_BIG_DECIMAL:+        val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];+        break;+#endif+    case JS_TAG_INT:+    case JS_TAG_FLOAT64:+        val = ctx->class_proto[JS_CLASS_NUMBER];+        break;+    case JS_TAG_BOOL:+        val = ctx->class_proto[JS_CLASS_BOOLEAN];+        break;+    case JS_TAG_STRING:+        val = ctx->class_proto[JS_CLASS_STRING];+        break;+    case JS_TAG_SYMBOL:+        val = ctx->class_proto[JS_CLASS_SYMBOL];+        break;+    case JS_TAG_OBJECT:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+    default:+        val = JS_NULL;+        break;+    }+    return val;+}++/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)+{+    JSValue val;+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        JSObject *p;+        p = JS_VALUE_GET_OBJ(obj);+        if (unlikely(p->class_id == JS_CLASS_PROXY)) {+            val = js_proxy_getPrototypeOf(ctx, obj);+        } else {+            p = p->shape->proto;+            if (!p)+                val = JS_NULL;+            else+                val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+        }+    } else {+        val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));+    }+    return val;+}++static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)+{+    JSValue obj1;+    obj1 = JS_GetPrototype(ctx, obj);+    JS_FreeValue(ctx, obj);+    return obj1;+}++/* return TRUE, FALSE or (-1) in case of exception */+static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,+                                   JSValueConst obj)+{+    JSValue obj_proto;+    JSObject *proto;+    const JSObject *p, *proto1;+    BOOL ret;++    if (!JS_IsFunction(ctx, obj))+        return FALSE;+    p = JS_VALUE_GET_OBJ(obj);+    if (p->class_id == JS_CLASS_BOUND_FUNCTION) {+        JSBoundFunction *s = p->u.bound_function;+        return JS_IsInstanceOf(ctx, val, s->func_obj);+    }++    /* Only explicitly boxed values are instances of constructors */+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return FALSE;+    obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);+    if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {+        if (!JS_IsException(obj_proto))+            JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");+        ret = -1;+        goto done;+    }+    proto = JS_VALUE_GET_OBJ(obj_proto);+    p = JS_VALUE_GET_OBJ(val);+    for(;;) {+        proto1 = p->shape->proto;+        if (!proto1) {+            /* slow case if proxy in the prototype chain */+            if (unlikely(p->class_id == JS_CLASS_PROXY)) {+                JSValue obj1;+                obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));+                for(;;) {+                    obj1 = JS_GetPrototypeFree(ctx, obj1);+                    if (JS_IsException(obj1)) {+                        ret = -1;+                        break;+                    }+                    if (JS_IsNull(obj1)) {+                        ret = FALSE;+                        break;+                    }+                    if (proto == JS_VALUE_GET_OBJ(obj1)) {+                        JS_FreeValue(ctx, obj1);+                        ret = TRUE;+                        break;+                    }+                    /* must check for timeout to avoid infinite loop */+                    if (js_poll_interrupts(ctx)) {+                        JS_FreeValue(ctx, obj1);+                        ret = -1;+                        break;+                    }+                }+            } else {+                ret = FALSE;+            }+            break;+        }+        p = proto1;+        if (proto == p) {+            ret = TRUE;+            break;+        }+    }+done:+    JS_FreeValue(ctx, obj_proto);+    return ret;+}++/* return TRUE, FALSE or (-1) in case of exception */+int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)+{+    JSValue method;++    if (!JS_IsObject(obj))+        goto fail;+    method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);+    if (JS_IsException(method))+        return -1;+    if (!JS_IsNull(method) && !JS_IsUndefined(method)) {+        JSValue ret;+        ret = JS_CallFree(ctx, method, obj, 1, &val);+        return JS_ToBoolFree(ctx, ret);+    }++    /* legacy case */+    if (!JS_IsFunction(ctx, obj)) {+    fail:+        JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");+        return -1;+    }+    return JS_OrdinaryIsInstanceOf(ctx, val, obj);+}++typedef int JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);++static JSAutoInitFunc *js_autoinit_func_table[] = {+    js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */+    js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */+    JS_InstantiateFunctionListItem, /* JS_AUTOINIT_ID_PROP */+};++static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,+                               JSProperty *pr)+{+    int ret;+    JSContext *realm;+    JSAutoInitFunc *func;+    +    realm = js_autoinit_get_realm(pr);+    func = js_autoinit_func_table[js_autoinit_get_id(pr)];+    ret = func(realm, p, prop, pr->u.init.opaque);+    return ret;+}++JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,+                               JSAtom prop, JSValueConst this_obj,+                               BOOL throw_ref_error)+{+    JSObject *p;+    JSProperty *pr;+    JSShapeProperty *prs;+    uint32_t tag;++    tag = JS_VALUE_GET_TAG(obj);+    if (unlikely(tag != JS_TAG_OBJECT)) {+        switch(tag) {+        case JS_TAG_NULL:+            return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);+        case JS_TAG_UNDEFINED:+            return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);+        case JS_TAG_EXCEPTION:+            return JS_EXCEPTION;+        case JS_TAG_STRING:+            {+                JSString *p1 = JS_VALUE_GET_STRING(obj);+                if (__JS_AtomIsTaggedInt(prop)) {+                    uint32_t idx, ch;+                    idx = __JS_AtomToUInt32(prop);+                    if (idx < p1->len) {+                        if (p1->is_wide_char)+                            ch = p1->u.str16[idx];+                        else+                            ch = p1->u.str8[idx];+                        return js_new_string_char(ctx, ch);+                    }+                } else if (prop == JS_ATOM_length) {+                    return JS_NewInt32(ctx, p1->len);+                }+            }+            break;+        default:+            break;+        }+        /* cannot raise an exception */+        p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));+        if (!p)+            return JS_UNDEFINED;+    } else {+        p = JS_VALUE_GET_OBJ(obj);+    }++    for(;;) {+        prs = find_own_property(&pr, p, prop);+        if (prs) {+            /* found */+            if (unlikely(prs->flags & JS_PROP_TMASK)) {+                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+                    if (unlikely(!pr->u.getset.getter)) {+                        return JS_UNDEFINED;+                    } else {+                        JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);+                        /* Note: the field could be removed in the getter */+                        func = JS_DupValue(ctx, func);+                        return JS_CallFree(ctx, func, this_obj, 0, NULL);+                    }+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                    JSValue val = *pr->u.var_ref->pvalue;+                    if (unlikely(JS_IsUninitialized(val)))+                        return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+                    return JS_DupValue(ctx, val);+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                    /* Instantiate property and retry */+                    if (JS_AutoInitProperty(ctx, p, prop, pr))+                        return JS_EXCEPTION;+                    continue;+                }+            } else {+                return JS_DupValue(ctx, pr->u.value);+            }+        }+        if (unlikely(p->is_exotic)) {+            /* exotic behaviors */+            if (p->fast_array) {+                if (__JS_AtomIsTaggedInt(prop)) {+                    uint32_t idx = __JS_AtomToUInt32(prop);+                    if (idx < p->u.array.count) {+                        /* we avoid duplicating the code */+                        return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);+                    } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                               p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                        goto typed_array_oob;+                    }+                } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                           p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                    int ret;+                    ret = JS_AtomIsNumericIndex(ctx, prop);+                    if (ret != 0) {+                        if (ret < 0)+                            return JS_EXCEPTION;+                    typed_array_oob:+                        if (typed_array_is_detached(ctx, p))+                            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+                        return JS_UNDEFINED;+                    }+                }+            } else {+                const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+                if (em) {+                    if (em->get_property) {+                        JSValue obj1, retval;+                        /* XXX: should pass throw_ref_error */+                        /* Note: if 'p' is a prototype, it can be+                           freed in the called function */+                        obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+                        retval = em->get_property(ctx, obj1, prop, this_obj);+                        JS_FreeValue(ctx, obj1);+                        return retval;+                    }+                    if (em->get_own_property) {+                        JSPropertyDescriptor desc;+                        int ret;+                        JSValue obj1;++                        /* Note: if 'p' is a prototype, it can be+                           freed in the called function */+                        obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+                        ret = em->get_own_property(ctx, &desc, obj1, prop);+                        JS_FreeValue(ctx, obj1);+                        if (ret < 0)+                            return JS_EXCEPTION;+                        if (ret) {+                            if (desc.flags & JS_PROP_GETSET) {+                                JS_FreeValue(ctx, desc.setter);+                                return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);+                            } else {+                                return desc.value;+                            }+                        }+                    }+                }+            }+        }+        p = p->shape->proto;+        if (!p)+            break;+    }+    if (unlikely(throw_ref_error)) {+        return JS_ThrowReferenceErrorNotDefined(ctx, prop);+    } else {+        return JS_UNDEFINED;+    }+}++static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)+{+    return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",+                                 atom);+}++/* Private fields can be added even on non extensible objects or+   Proxies */+static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,+                                 JSValueConst name, JSValue val)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;+    JSAtom prop;++    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        goto fail;+    }+    /* safety check */+    if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {+        JS_ThrowTypeErrorNotASymbol(ctx);+        goto fail;+    }+    prop = js_symbol_to_atom(ctx, (JSValue)name);+    p = JS_VALUE_GET_OBJ(obj);+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",+                              prop);+        goto fail;+    }+    pr = add_property(ctx, p, prop, JS_PROP_C_W_E);+    if (unlikely(!pr)) {+    fail:+        JS_FreeValue(ctx, val);+        return -1;+    }+    pr->u.value = val;+    return 0;+}++static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,+                                  JSValueConst name)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;+    JSAtom prop;++    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    /* safety check */+    if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))+        return JS_ThrowTypeErrorNotASymbol(ctx);+    prop = js_symbol_to_atom(ctx, (JSValue)name);+    p = JS_VALUE_GET_OBJ(obj);+    prs = find_own_property(&pr, p, prop);+    if (!prs) {+        JS_ThrowTypeErrorPrivateNotFound(ctx, prop);+        return JS_EXCEPTION;+    }+    return JS_DupValue(ctx, pr->u.value);+}++static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,+                              JSValueConst name, JSValue val)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;+    JSAtom prop;++    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        goto fail;+    }+    /* safety check */+    if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {+        JS_ThrowTypeErrorNotASymbol(ctx);+        goto fail;+    }+    prop = js_symbol_to_atom(ctx, (JSValue)name);+    p = JS_VALUE_GET_OBJ(obj);+    prs = find_own_property(&pr, p, prop);+    if (!prs) {+        JS_ThrowTypeErrorPrivateNotFound(ctx, prop);+    fail:+        JS_FreeValue(ctx, val);+        return -1;+    }+    set_value(ctx, &pr->u.value, val);+    return 0;+}++static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)+{+    JSObject *p, *p1;+    JSShapeProperty *prs;+    JSProperty *pr;+    JSValue brand;+    JSAtom brand_atom;+    +    if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    p = JS_VALUE_GET_OBJ(home_obj);+    prs = find_own_property(&pr, p, JS_ATOM_Private_brand);+    if (!prs) {+        brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);+        if (JS_IsException(brand))+            return -1;+        /* if the brand is not present, add it */+        pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);+        if (!pr) {+            JS_FreeValue(ctx, brand);+            return -1;+        }+        pr->u.value = JS_DupValue(ctx, brand);+    } else {+        brand = JS_DupValue(ctx, pr->u.value);+    }+    brand_atom = js_symbol_to_atom(ctx, brand);+    +    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        JS_FreeAtom(ctx, brand_atom);+        return -1;+    }+    p1 = JS_VALUE_GET_OBJ(obj);+    pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);+    JS_FreeAtom(ctx, brand_atom);+    if (!pr)+        return -1;+    pr->u.value = JS_UNDEFINED;+    return 0;+}++static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)+{+    JSObject *p, *p1, *home_obj;+    JSShapeProperty *prs;+    JSProperty *pr;+    JSValueConst brand;+    +    /* get the home object of 'func' */+    if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {+    not_obj:+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    p1 = JS_VALUE_GET_OBJ(func);+    if (!js_class_has_bytecode(p1->class_id))+        goto not_obj;+    home_obj = p1->u.func.home_object;+    if (!home_obj)+        goto not_obj;+    prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);+    if (!prs) {+        JS_ThrowTypeError(ctx, "expecting <brand> private field");+        return -1;+    }+    brand = pr->u.value;+    /* safety check */+    if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))+        goto not_obj;+    +    /* get the brand array of 'obj' */+    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+        goto not_obj;+    p = JS_VALUE_GET_OBJ(obj);+    prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));+    if (!prs) {+        JS_ThrowTypeError(ctx, "invalid brand on object");+        return -1;+    }+    return 0;+}++static int num_keys_cmp(const void *p1, const void *p2, void *opaque)+{+    JSContext *ctx = opaque;+    JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;+    JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;+    uint32_t v1, v2;+    BOOL atom1_is_integer, atom2_is_integer;++    atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);+    atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);+    assert(atom1_is_integer && atom2_is_integer);+    if (v1 < v2)+        return -1;+    else if (v1 == v2)+        return 0;+    else+        return 1;+}++static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)+{+    uint32_t i;+    if (tab) {+        for(i = 0; i < len; i++)+            JS_FreeAtom(ctx, tab[i].atom);+        js_free(ctx, tab);+    }+}++/* return < 0 in case if exception, 0 if OK. ptab and its atoms must+   be freed by the user. */+static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,+                                                      JSPropertyEnum **ptab,+                                                      uint32_t *plen,+                                                      JSObject *p, int flags)+{+    int i, j;+    JSShape *sh;+    JSShapeProperty *prs;+    JSPropertyEnum *tab_atom, *tab_exotic;+    JSAtom atom;+    uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;+    uint32_t num_index, str_index, sym_index, exotic_count;+    BOOL is_enumerable, num_sorted;+    uint32_t num_key;+    JSAtomKindEnum kind;+    +    /* clear pointer for consistency in case of failure */+    *ptab = NULL;+    *plen = 0;++    /* compute the number of returned properties */+    num_keys_count = 0;+    str_keys_count = 0;+    sym_keys_count = 0;+    exotic_count = 0;+    tab_exotic = NULL;+    sh = p->shape;+    for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+        atom = prs->atom;+        if (atom != JS_ATOM_NULL) {+            is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);+            kind = JS_AtomGetKind(ctx, atom);+            if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&+                ((flags >> kind) & 1) != 0) {+                /* need to raise an exception in case of the module+                   name space (implicit GetOwnProperty) */+                if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&+                    (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {+                    JSVarRef *var_ref = p->prop[i].u.var_ref;+                    if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {+                        JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+                        return -1;+                    }+                }+                if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {+                    num_keys_count++;+                } else if (kind == JS_ATOM_KIND_STRING) {+                    str_keys_count++;+                } else {+                    sym_keys_count++;+                }+            }+        }+    }++    if (p->is_exotic) {+        if (p->fast_array) {+            /* the implicit GetOwnProperty raises an exception if the+               typed array is detached */+            if ((flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) &&+                 (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                  p->class_id <= JS_CLASS_FLOAT64_ARRAY) &&+                 typed_array_is_detached(ctx, p) &&+                typed_array_get_length(ctx, p) != 0) {+                JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+                return -1;+            }+            if (flags & JS_GPN_STRING_MASK) {+                num_keys_count += p->u.array.count;+            }+        } else {+            const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+            if (em && em->get_own_property_names) {+                if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,+                                               JS_MKPTR(JS_TAG_OBJECT, p)))+                    return -1;+                for(i = 0; i < exotic_count; i++) {+                    atom = tab_exotic[i].atom;+                    kind = JS_AtomGetKind(ctx, atom);+                    if (((flags >> kind) & 1) != 0) {+                        is_enumerable = FALSE;+                        if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {+                            JSPropertyDescriptor desc;+                            int res;+                            /* set the "is_enumerable" field if necessary */+                            res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);+                            if (res < 0) {+                                js_free_prop_enum(ctx, tab_exotic, exotic_count);+                                return -1;+                            }+                            if (res) {+                                is_enumerable =+                                    ((desc.flags & JS_PROP_ENUMERABLE) != 0);+                                js_free_desc(ctx, &desc);+                            }+                            tab_exotic[i].is_enumerable = is_enumerable;+                        }+                        if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {+                            if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {+                                num_keys_count++;+                            } else if (kind == JS_ATOM_KIND_STRING) {+                                str_keys_count++;+                            } else {+                                sym_keys_count++;+                            }+                        }+                    }+                }+            }+        }+    }++    /* fill them */++    atom_count = num_keys_count + str_keys_count + sym_keys_count;+    /* avoid allocating 0 bytes */+    tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));+    if (!tab_atom) {+        js_free_prop_enum(ctx, tab_exotic, exotic_count);+        return -1;+    }++    num_index = 0;+    str_index = num_keys_count;+    sym_index = str_index + str_keys_count;++    num_sorted = TRUE;+    sh = p->shape;+    for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+        atom = prs->atom;+        if (atom != JS_ATOM_NULL) {+            is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);+            kind = JS_AtomGetKind(ctx, atom);+            if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&+                ((flags >> kind) & 1) != 0) {+                if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {+                    j = num_index++;+                    num_sorted = FALSE;+                } else if (kind == JS_ATOM_KIND_STRING) {+                    j = str_index++;+                } else {+                    j = sym_index++;+                }+                tab_atom[j].atom = JS_DupAtom(ctx, atom);+                tab_atom[j].is_enumerable = is_enumerable;+            }+        }+    }++    if (p->is_exotic) {+        if (p->fast_array) {+            if (flags & JS_GPN_STRING_MASK) {+                for(i = 0; i < p->u.array.count; i++) {+                    tab_atom[num_index].atom = __JS_AtomFromUInt32(i);+                    if (tab_atom[num_index].atom == JS_ATOM_NULL) {+                        js_free_prop_enum(ctx, tab_exotic, exotic_count);+                        js_free_prop_enum(ctx, tab_atom, num_index);+                        return -1;+                    }+                    tab_atom[num_index].is_enumerable = TRUE;+                    num_index++;+                }+            }+        }+        if (exotic_count > 0) {+            for(i = 0; i < exotic_count; i++) {+                atom = tab_exotic[i].atom;+                is_enumerable = tab_exotic[i].is_enumerable;+                kind = JS_AtomGetKind(ctx, atom);+                if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&+                    ((flags >> kind) & 1) != 0) {+                    if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {+                        j = num_index++;+                        num_sorted = FALSE;+                    } else if (kind == JS_ATOM_KIND_STRING) {+                        j = str_index++;+                    } else {+                        j = sym_index++;+                    }+                    tab_atom[j].atom = atom;+                    tab_atom[j].is_enumerable = is_enumerable;+                } else {+                    JS_FreeAtom(ctx, atom);+                }+            }+        }+        js_free(ctx, tab_exotic);+    }++    assert(num_index == num_keys_count);+    assert(str_index == num_keys_count + str_keys_count);+    assert(sym_index == atom_count);++    if (num_keys_count != 0 && !num_sorted) {+        rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,+               ctx);+    }+    *ptab = tab_atom;+    *plen = atom_count;+    return 0;+}++int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,+                           uint32_t *plen, JSValueConst obj, int flags)+{+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,+                                          JS_VALUE_GET_OBJ(obj), flags);+}++/* Return -1 if exception,+   FALSE if the property does not exist, TRUE if it exists. If TRUE is+   returned, the property descriptor 'desc' is filled present. */+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,+                                     JSObject *p, JSAtom prop)+{+    JSShapeProperty *prs;+    JSProperty *pr;++retry:+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        if (desc) {+            desc->flags = prs->flags & JS_PROP_C_W_E;+            desc->getter = JS_UNDEFINED;+            desc->setter = JS_UNDEFINED;+            desc->value = JS_UNDEFINED;+            if (unlikely(prs->flags & JS_PROP_TMASK)) {+                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+                    desc->flags |= JS_PROP_GETSET;+                    if (pr->u.getset.getter)+                        desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+                    if (pr->u.getset.setter)+                        desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                    JSValue val = *pr->u.var_ref->pvalue;+                    if (unlikely(JS_IsUninitialized(val))) {+                        JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+                        return -1;+                    }+                    desc->value = JS_DupValue(ctx, val);+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                    /* Instantiate property and retry */+                    if (JS_AutoInitProperty(ctx, p, prop, pr))+                        return -1;+                    goto retry;+                }+            } else {+                desc->value = JS_DupValue(ctx, pr->u.value);+            }+        } else {+            /* for consistency, send the exception even if desc is NULL */+            if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {+                if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {+                    JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+                    return -1;+                }+            } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                /* nothing to do: delay instantiation until actual value and/or attributes are read */+            }+        }+        return TRUE;+    }+    if (p->is_exotic) {+        if (p->fast_array) {+            /* specific case for fast arrays */+            if (__JS_AtomIsTaggedInt(prop)) {+                uint32_t idx;+                idx = __JS_AtomToUInt32(prop);+                if (idx < p->u.array.count) {+                    if (desc) {+                        desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE;+                        if (p->class_id == JS_CLASS_ARRAY ||+                            p->class_id == JS_CLASS_ARGUMENTS)+                            desc->flags |= JS_PROP_CONFIGURABLE;+                        desc->getter = JS_UNDEFINED;+                        desc->setter = JS_UNDEFINED;+                        desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);+                    }+                    return TRUE;+                }+            }+            if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                int ret;+                ret = JS_AtomIsNumericIndex(ctx, prop);+                if (ret != 0) {+                    if (ret < 0)+                        return -1;+                    if (typed_array_is_detached(ctx, p)) {+                        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+                        return -1;+                    }+                }+            }+        } else {+            const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+            if (em && em->get_own_property) {+                return em->get_own_property(ctx, desc,+                                            JS_MKPTR(JS_TAG_OBJECT, p), prop);+            }+        }+    }+    return FALSE;+}++int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,+                      JSValueConst obj, JSAtom prop)+{+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);+}++/* return -1 if exception (Proxy object only) or TRUE/FALSE */+int JS_IsExtensible(JSContext *ctx, JSValueConst obj)+{+    JSObject *p;++    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+        return FALSE;+    p = JS_VALUE_GET_OBJ(obj);+    if (unlikely(p->class_id == JS_CLASS_PROXY))+        return js_proxy_isExtensible(ctx, obj);+    else+        return p->extensible;+}++/* return -1 if exception (Proxy object only) or TRUE/FALSE */+int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)+{+    JSObject *p;++    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+        return FALSE;+    p = JS_VALUE_GET_OBJ(obj);+    if (unlikely(p->class_id == JS_CLASS_PROXY))+        return js_proxy_preventExtensions(ctx, obj);+    p->extensible = FALSE;+    return TRUE;+}++/* return -1 if exception otherwise TRUE or FALSE */+int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)+{+    JSObject *p;+    int ret;+    JSValue obj1;++    if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+        return FALSE;+    p = JS_VALUE_GET_OBJ(obj);+    for(;;) {+        if (p->is_exotic) {+            const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+            if (em && em->has_property) {+                /* has_property can free the prototype */+                obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+                ret = em->has_property(ctx, obj1, prop);+                JS_FreeValue(ctx, obj1);+                return ret;+            }+        }+        /* JS_GetOwnPropertyInternal can free the prototype */+        JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+        ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);+        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+        if (ret != 0)+            return ret;+        if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+            p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+            ret = JS_AtomIsNumericIndex(ctx, prop);+            if (ret != 0) {+                if (ret < 0)+                    return -1;+                /* the detached array test was done in+                   JS_GetOwnPropertyInternal() */+                return FALSE;+            }+        }+        p = p->shape->proto;+        if (!p)+            break;+    }+    return FALSE;+}++/* val must be a symbol */+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)+{+    JSAtomStruct *p = JS_VALUE_GET_PTR(val);+    return js_get_atom_index(ctx->rt, p);+}++/* return JS_ATOM_NULL in case of exception */+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)+{+    JSAtom atom;+    uint32_t tag;+    tag = JS_VALUE_GET_TAG(val);+    if (tag == JS_TAG_INT &&+        (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {+        /* fast path for integer values */+        atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));+    } else if (tag == JS_TAG_SYMBOL) {+        JSAtomStruct *p = JS_VALUE_GET_PTR(val);+        atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));+    } else {+        JSValue str;+        str = JS_ToPropertyKey(ctx, val);+        if (JS_IsException(str))+            return JS_ATOM_NULL;+        if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {+            atom = js_symbol_to_atom(ctx, str);+        } else {+            atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));+        }+    }+    return atom;+}++static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,+                                   JSValue prop)+{+    JSAtom atom;+    JSValue ret;++    if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&+               JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {+        JSObject *p;+        uint32_t idx, len;+        /* fast path for array access */+        p = JS_VALUE_GET_OBJ(this_obj);+        idx = JS_VALUE_GET_INT(prop);+        len = (uint32_t)p->u.array.count;+        if (unlikely(idx >= len))+            goto slow_path;+        switch(p->class_id) {+        case JS_CLASS_ARRAY:+        case JS_CLASS_ARGUMENTS:+            return JS_DupValue(ctx, p->u.array.u.values[idx]);+        case JS_CLASS_INT8_ARRAY:+            return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);+        case JS_CLASS_UINT8C_ARRAY:+        case JS_CLASS_UINT8_ARRAY:+            return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);+        case JS_CLASS_INT16_ARRAY:+            return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);+        case JS_CLASS_UINT16_ARRAY:+            return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);+        case JS_CLASS_INT32_ARRAY:+            return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);+        case JS_CLASS_UINT32_ARRAY:+            return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT64_ARRAY:+            return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);+        case JS_CLASS_BIG_UINT64_ARRAY:+            return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);+#endif+        case JS_CLASS_FLOAT32_ARRAY:+            return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);+        case JS_CLASS_FLOAT64_ARRAY:+            return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);+        default:+            goto slow_path;+        }+    } else {+    slow_path:+        atom = JS_ValueToAtom(ctx, prop);+        JS_FreeValue(ctx, prop);+        if (unlikely(atom == JS_ATOM_NULL))+            return JS_EXCEPTION;+        ret = JS_GetProperty(ctx, this_obj, atom);+        JS_FreeAtom(ctx, atom);+        return ret;+    }+}++JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,+                             uint32_t idx)+{+    return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));+}++/* Check if an object has a generalized numeric property. Return value:+   -1 for exception,+   TRUE if property exists, stored into *pval,+   FALSE if proprty does not exist.+ */+static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)+{+    JSValue val = JS_UNDEFINED;+    JSAtom prop;+    int present;++    if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {+        /* fast path */+        present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));+        if (present > 0) {+            val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));+            if (unlikely(JS_IsException(val)))+                present = -1;+        }+    } else {+        prop = JS_NewAtomInt64(ctx, idx);+        present = -1;+        if (likely(prop != JS_ATOM_NULL)) {+            present = JS_HasProperty(ctx, obj, prop);+            if (present > 0) {+                val = JS_GetProperty(ctx, obj, prop);+                if (unlikely(JS_IsException(val)))+                    present = -1;+            }+            JS_FreeAtom(ctx, prop);+        }+    }+    *pval = val;+    return present;+}++static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)+{+    JSAtom prop;+    JSValue val;++    if ((uint64_t)idx <= INT32_MAX) {+        /* fast path for fast arrays */+        return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));+    }+    prop = JS_NewAtomInt64(ctx, idx);+    if (prop == JS_ATOM_NULL)+        return JS_EXCEPTION;++    val = JS_GetProperty(ctx, obj, prop);+    JS_FreeAtom(ctx, prop);+    return val;+}++JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,+                          const char *prop)+{+    JSAtom atom;+    JSValue ret;+    atom = JS_NewAtom(ctx, prop);+    ret = JS_GetProperty(ctx, this_obj, atom);+    JS_FreeAtom(ctx, atom);+    return ret;+}++/* Note: the property value is not initialized. Return NULL if memory+   error. */+static JSProperty *add_property(JSContext *ctx,+                                JSObject *p, JSAtom prop, int prop_flags)+{+    JSShape *sh, *new_sh;++    sh = p->shape;+    if (sh->is_hashed) {+        /* try to find an existing shape */+        new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);+        if (new_sh) {+            /* matching shape found: use it */+            /*  the property array may need to be resized */+            if (new_sh->prop_size != sh->prop_size) {+                JSProperty *new_prop;+                new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *+                                      new_sh->prop_size);+                if (!new_prop)+                    return NULL;+                p->prop = new_prop;+            }+            p->shape = js_dup_shape(new_sh);+            js_free_shape(ctx->rt, sh);+            return &p->prop[new_sh->prop_count - 1];+        } else if (sh->header.ref_count != 1) {+            /* if the shape is shared, clone it */+            new_sh = js_clone_shape(ctx, sh);+            if (!new_sh)+                return NULL;+            /* hash the cloned shape */+            new_sh->is_hashed = TRUE;+            js_shape_hash_link(ctx->rt, new_sh);+            js_free_shape(ctx->rt, p->shape);+            p->shape = new_sh;+        }+    }+    assert(p->shape->header.ref_count == 1);+    if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))+        return NULL;+    return &p->prop[p->shape->prop_count - 1];+}++/* can be called on Array or Arguments objects. return < 0 if+   memory alloc error. */+static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,+                                                             JSObject *p)+{+    JSProperty *pr;+    JSShape *sh;+    JSValue *tab;+    uint32_t i, len, new_count;++    if (js_shape_prepare_update(ctx, p, NULL))+        return -1;+    len = p->u.array.count;+    /* resize the properties once to simplify the error handling */+    sh = p->shape;+    new_count = sh->prop_count + len;+    if (new_count > sh->prop_size) {+        if (resize_properties(ctx, &p->shape, p, new_count))+            return -1;+    }++    tab = p->u.array.u.values;+    for(i = 0; i < len; i++) {+        /* add_property cannot fail here but+           __JS_AtomFromUInt32(i) fails for i > INT32_MAX */+        pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);+        pr->u.value = *tab++;+    }+    js_free(ctx, p->u.array.u.values);+    p->u.array.count = 0;+    p->u.array.u.values = NULL; /* fail safe */+    p->u.array.u1.size = 0;+    p->fast_array = 0;+    return 0;+}++static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)+{+    JSShape *sh;+    JSShapeProperty *pr, *lpr, *prop;+    JSProperty *pr1;+    uint32_t lpr_idx;+    intptr_t h, h1;++ redo:+    sh = p->shape;+    h1 = atom & sh->prop_hash_mask;+    h = sh->prop_hash_end[-h1 - 1];+    prop = get_shape_prop(sh);+    lpr = NULL;+    lpr_idx = 0;   /* prevent warning */+    while (h != 0) {+        pr = &prop[h - 1];+        if (likely(pr->atom == atom)) {+            /* found ! */+            if (!(pr->flags & JS_PROP_CONFIGURABLE))+                return FALSE;+            /* realloc the shape if needed */+            if (lpr)+                lpr_idx = lpr - get_shape_prop(sh);+            if (js_shape_prepare_update(ctx, p, &pr))+                return -1;+            sh = p->shape;+            /* remove property */+            if (lpr) {+                lpr = get_shape_prop(sh) + lpr_idx;+                lpr->hash_next = pr->hash_next;+            } else {+                sh->prop_hash_end[-h1 - 1] = pr->hash_next;+            }+            sh->deleted_prop_count++;+            /* free the entry */+            pr1 = &p->prop[h - 1];+            free_property(ctx->rt, pr1, pr->flags);+            JS_FreeAtom(ctx, pr->atom);+            /* put default values */+            pr->flags = 0;+            pr->atom = JS_ATOM_NULL;+            pr1->u.value = JS_UNDEFINED;++            /* compact the properties if too many deleted properties */+            if (sh->deleted_prop_count >= 8 &&+                sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {+                compact_properties(ctx, p);+            }+            return TRUE;+        }+        lpr = pr;+        h = pr->hash_next;+    }++    if (p->is_exotic) {+        if (p->fast_array) {+            uint32_t idx;+            if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&+                idx < p->u.array.count) {+                if (p->class_id == JS_CLASS_ARRAY ||+                    p->class_id == JS_CLASS_ARGUMENTS) {+                    /* Special case deleting the last element of a fast Array */+                    if (idx == p->u.array.count - 1) {+                        JS_FreeValue(ctx, p->u.array.u.values[idx]);+                        p->u.array.count = idx;+                        return TRUE;+                    }+                    if (convert_fast_array_to_array(ctx, p))+                        return -1;+                    goto redo;+                } else {+                    return FALSE; /* not configurable */+                }+            }+        } else {+            const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+            if (em && em->delete_property) {+                return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);+            }+        }+    }+    /* not found */+    return TRUE;+}++static int call_setter(JSContext *ctx, JSObject *setter,+                       JSValueConst this_obj, JSValue val, int flags)+{+    JSValue ret, func;+    if (likely(setter)) {+        func = JS_MKPTR(JS_TAG_OBJECT, setter);+        /* Note: the field could be removed in the setter */+        func = JS_DupValue(ctx, func);+        ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);+        JS_FreeValue(ctx, val);+        if (JS_IsException(ret))+            return -1;+        JS_FreeValue(ctx, ret);+        return TRUE;+    } else {+        JS_FreeValue(ctx, val);+        if ((flags & JS_PROP_THROW) ||+            ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+            JS_ThrowTypeError(ctx, "no setter for property");+            return -1;+        }+        return FALSE;+    }+}++/* set the array length and remove the array elements if necessary. */+static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop,+                            JSValue val, int flags)+{+    uint32_t len, idx, cur_len;+    int i, ret;++    ret = JS_ToArrayLengthFree(ctx, &len, val);+    if (ret)+        return -1;+    if (likely(p->fast_array)) {+        uint32_t old_len = p->u.array.count;+        if (len < old_len) {+            for(i = len; i < old_len; i++) {+                JS_FreeValue(ctx, p->u.array.u.values[i]);+            }+            p->u.array.count = len;+        }+        prop->u.value = JS_NewUint32(ctx, len);+    } else {+        /* Note: length is always a uint32 because the object is an+           array */+        JS_ToUint32(ctx, &cur_len, prop->u.value);+        if (len < cur_len) {+            uint32_t d;+            JSShape *sh;+            JSShapeProperty *pr;++            d = cur_len - len;+            sh = p->shape;+            if (d <= sh->prop_count) {+                JSAtom atom;++                /* faster to iterate */+                while (cur_len > len) {+                    atom = JS_NewAtomUInt32(ctx, cur_len - 1);+                    ret = delete_property(ctx, p, atom);+                    JS_FreeAtom(ctx, atom);+                    if (unlikely(!ret)) {+                        /* unlikely case: property is not+                           configurable */+                        break;+                    }+                    cur_len--;+                }+            } else {+                /* faster to iterate thru all the properties. Need two+                   passes in case one of the property is not+                   configurable */+                cur_len = len;+                for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;+                    i++, pr++) {+                    if (pr->atom != JS_ATOM_NULL &&+                        JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {+                        if (idx >= cur_len &&+                            !(pr->flags & JS_PROP_CONFIGURABLE)) {+                            cur_len = idx + 1;+                        }+                    }+                }++                for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;+                    i++, pr++) {+                    if (pr->atom != JS_ATOM_NULL &&+                        JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {+                        if (idx >= cur_len) {+                            /* remove the property */+                            delete_property(ctx, p, pr->atom);+                            /* WARNING: the shape may have been modified */+                            sh = p->shape;+                            pr = get_shape_prop(sh) + i;+                        }+                    }+                }+            }+        } else {+            cur_len = len;+        }+        set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));+        if (unlikely(cur_len > len)) {+            return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");+        }+    }+    return TRUE;+}++/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =+   TRUE and p->extensible = TRUE */+static int add_fast_array_element(JSContext *ctx, JSObject *p,+                                  JSValue val, int flags)+{+    uint32_t new_len, array_len;+    /* extend the array by one */+    /* XXX: convert to slow array if new_len > 2^31-1 elements */+    new_len = p->u.array.count + 1;+    /* update the length if necessary. We assume that if the length is+       not an integer, then if it >= 2^31.  */+    if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {+        array_len = JS_VALUE_GET_INT(p->prop[0].u.value);+        if (new_len > array_len) {+            if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {+                JS_FreeValue(ctx, val);+                return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);+            }+            p->prop[0].u.value = JS_NewInt32(ctx, new_len);+        }+    }+    if (unlikely(new_len > p->u.array.u1.size)) {+        uint32_t new_size;+        size_t slack;+        JSValue *new_array_prop;+        /* XXX: potential arithmetic overflow */+        new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);+        new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);+        if (!new_array_prop) {+            JS_FreeValue(ctx, val);+            return -1;+        }+        new_size += slack / sizeof(*new_array_prop);+        p->u.array.u.values = new_array_prop;+        p->u.array.u1.size = new_size;+    }+    p->u.array.u.values[new_len - 1] = val;+    p->u.array.count = new_len;+    return TRUE;+}++static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)+{+    JS_FreeValue(ctx, desc->getter);+    JS_FreeValue(ctx, desc->setter);+    JS_FreeValue(ctx, desc->value);+}++/* generic (and slower) version of JS_SetProperty() for Reflect.set() */+static int JS_SetPropertyGeneric(JSContext *ctx,+                                 JSObject *p, JSAtom prop,+                                 JSValue val, JSValueConst this_obj,+                                 int flags)+{+    int ret;+    JSPropertyDescriptor desc;++    while (p != NULL) {+        if (p->is_exotic) {+            const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+            if (em && em->set_property) {+                JSValue obj1;+                /* set_property can free the prototype */+                obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+                ret = em->set_property(ctx, obj1, prop,+                                       val, this_obj, flags);+                JS_FreeValue(ctx, obj1);+                JS_FreeValue(ctx, val);+                return ret;+            }+        }++        ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+        if (ret < 0)+            return ret;+        if (ret) {+            if (desc.flags & JS_PROP_GETSET) {+                JSObject *setter;+                if (JS_IsUndefined(desc.setter))+                    setter = NULL;+                else+                    setter = JS_VALUE_GET_OBJ(desc.setter);+                ret = call_setter(ctx, setter, this_obj, val, flags);+                JS_FreeValue(ctx, desc.getter);+                JS_FreeValue(ctx, desc.setter);+                return ret;+            } else {+                JS_FreeValue(ctx, desc.value);+                if (!(desc.flags & JS_PROP_WRITABLE)) {+                    goto read_only_error;+                }+            }+            break;+        }+        p = p->shape->proto;+    }++    if (!JS_IsObject(this_obj))+        return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");++    p = JS_VALUE_GET_OBJ(this_obj);++    /* modify the property in this_obj if it already exists */+    ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+    if (ret < 0)+        return ret;+    if (ret) {+        if (desc.flags & JS_PROP_GETSET) {+            JS_FreeValue(ctx, desc.getter);+            JS_FreeValue(ctx, desc.setter);+            JS_FreeValue(ctx, val);+            return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");+        } else {+            JS_FreeValue(ctx, desc.value);+            if (!(desc.flags & JS_PROP_WRITABLE) ||+                p->class_id == JS_CLASS_MODULE_NS) {+            read_only_error:+                JS_FreeValue(ctx, val);+                return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);+            }+        }+        ret = JS_DefineProperty(ctx, this_obj, prop, val,+                                JS_UNDEFINED, JS_UNDEFINED,+                                JS_PROP_HAS_VALUE);+        JS_FreeValue(ctx, val);+        return ret;+    }++    ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,+                            flags |+                            JS_PROP_HAS_VALUE |+                            JS_PROP_HAS_ENUMERABLE |+                            JS_PROP_HAS_WRITABLE |+                            JS_PROP_HAS_CONFIGURABLE |+                            JS_PROP_C_W_E);+    JS_FreeValue(ctx, val);+    return ret;+}++/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is+   freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,+   JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,+   the new property is not added and an error is raised. */+int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,+                           JSAtom prop, JSValue val, int flags)+{+    JSObject *p, *p1;+    JSShapeProperty *prs;+    JSProperty *pr;+    uint32_t tag;+    JSPropertyDescriptor desc;+    int ret;+#if 0+    printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");+#endif+    tag = JS_VALUE_GET_TAG(this_obj);+    if (unlikely(tag != JS_TAG_OBJECT)) {+        switch(tag) {+        case JS_TAG_NULL:+            JS_FreeValue(ctx, val);+            JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);+            return -1;+        case JS_TAG_UNDEFINED:+            JS_FreeValue(ctx, val);+            JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);+            return -1;+        default:+            /* even on a primitive type we can have setters on the prototype */+            p = NULL;+            p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));+            goto prototype_lookup;+        }+    }+    p = JS_VALUE_GET_OBJ(this_obj);+retry:+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |+                                  JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {+            /* fast case */+            set_value(ctx, &pr->u.value, val);+            return TRUE;+        } else if ((prs->flags & (JS_PROP_LENGTH | JS_PROP_WRITABLE)) ==+                   (JS_PROP_LENGTH | JS_PROP_WRITABLE)) {+            assert(p->class_id == JS_CLASS_ARRAY);+            assert(prop == JS_ATOM_length);+            return set_array_length(ctx, p, pr, val, flags);+        } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+            return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);+        } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+            /* JS_PROP_WRITABLE is always true for variable+               references, but they are write protected in module name+               spaces. */+            if (p->class_id == JS_CLASS_MODULE_NS)+                goto read_only_prop;+            set_value(ctx, pr->u.var_ref->pvalue, val);+            return TRUE;+        } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+            /* Instantiate property and retry (potentially useless) */+            if (JS_AutoInitProperty(ctx, p, prop, pr)) {+                JS_FreeValue(ctx, val);+                return -1;+            }+            goto retry;+        } else {+            goto read_only_prop;+        }+    }++    p1 = p;+    for(;;) {+        if (p1->is_exotic) {+            if (p1->fast_array) {+                if (__JS_AtomIsTaggedInt(prop)) {+                    uint32_t idx = __JS_AtomToUInt32(prop);+                    if (idx < p1->u.array.count) {+                        if (unlikely(p == p1))+                            return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);+                        else+                            break;+                    } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&+                               p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                        goto typed_array_oob;+                    }+                } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&+                           p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                    ret = JS_AtomIsNumericIndex(ctx, prop);+                    if (ret != 0) {+                        if (ret < 0) {+                            JS_FreeValue(ctx, val);+                            return -1;+                        }+                    typed_array_oob:+                        val = JS_ToNumberFree(ctx, val);+                        JS_FreeValue(ctx, val);+                        if (JS_IsException(val))+                            return -1;+                        if (typed_array_is_detached(ctx, p1)) {+                            JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+                            return -1;+                        }+                        return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");+                    }+                }+            } else {+                const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;+                if (em) {+                    JSValue obj1;+                    if (em->set_property) {+                        /* set_property can free the prototype */+                        obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+                        ret = em->set_property(ctx, obj1, prop,+                                               val, this_obj, flags);+                        JS_FreeValue(ctx, obj1);+                        JS_FreeValue(ctx, val);+                        return ret;+                    }+                    if (em->get_own_property) {+                        /* get_own_property can free the prototype */+                        obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+                        ret = em->get_own_property(ctx, &desc,+                                                   obj1, prop);+                        JS_FreeValue(ctx, obj1);+                        if (ret < 0) {+                            JS_FreeValue(ctx, val);+                            return ret;+                        }+                        if (ret) {+                            if (desc.flags & JS_PROP_GETSET) {+                                JSObject *setter;+                                if (JS_IsUndefined(desc.setter))+                                    setter = NULL;+                                else+                                    setter = JS_VALUE_GET_OBJ(desc.setter);+                                ret = call_setter(ctx, setter, this_obj, val, flags);+                                JS_FreeValue(ctx, desc.getter);+                                JS_FreeValue(ctx, desc.setter);+                                return ret;+                            } else {+                                JS_FreeValue(ctx, desc.value);+                                if (!(desc.flags & JS_PROP_WRITABLE))+                                    goto read_only_prop;+                                if (likely(p == p1)) {+                                    ret = JS_DefineProperty(ctx, this_obj, prop, val,+                                                            JS_UNDEFINED, JS_UNDEFINED,+                                                            JS_PROP_HAS_VALUE);+                                    JS_FreeValue(ctx, val);+                                    return ret;+                                } else {+                                    break;+                                }+                            }+                        }+                    }+                }+            }+        }+        p1 = p1->shape->proto;+    prototype_lookup:+        if (!p1)+            break;++    retry2:+        prs = find_own_property(&pr, p1, prop);+        if (prs) {+            if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+                return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);+            } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                /* Instantiate property and retry (potentially useless) */+                if (JS_AutoInitProperty(ctx, p1, prop, pr))+                    return -1;+                goto retry2;+            } else if (!(prs->flags & JS_PROP_WRITABLE)) {+            read_only_prop:+                JS_FreeValue(ctx, val);+                return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);+            }+        }+    }++    if (unlikely(flags & JS_PROP_NO_ADD)) {+        JS_FreeValue(ctx, val);+        JS_ThrowReferenceErrorNotDefined(ctx, prop);+        return -1;+    }++    if (unlikely(!p)) {+        JS_FreeValue(ctx, val);+        return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");+    }++    if (unlikely(!p->extensible)) {+        JS_FreeValue(ctx, val);+        return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");+    }++    if (p->is_exotic) {+        if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&+            __JS_AtomIsTaggedInt(prop)) {+            uint32_t idx = __JS_AtomToUInt32(prop);+            if (idx == p->u.array.count) {+                /* fast case */+                return add_fast_array_element(ctx, p, val, flags);+            } else {+                goto generic_create_prop;+            }+        } else {+        generic_create_prop:+            ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,+                                    flags |+                                    JS_PROP_HAS_VALUE |+                                    JS_PROP_HAS_ENUMERABLE |+                                    JS_PROP_HAS_WRITABLE |+                                    JS_PROP_HAS_CONFIGURABLE |+                                    JS_PROP_C_W_E);+            JS_FreeValue(ctx, val);+            return ret;+        }+    }++    pr = add_property(ctx, p, prop, JS_PROP_C_W_E);+    if (unlikely(!pr)) {+        JS_FreeValue(ctx, val);+        return -1;+    }+    pr->u.value = val;+    return TRUE;+}++/* 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)+{+    if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&+               JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {+        JSObject *p;+        uint32_t idx;+        double d;+        int32_t v;++        /* fast path for array access */+        p = JS_VALUE_GET_OBJ(this_obj);+        idx = JS_VALUE_GET_INT(prop);+        switch(p->class_id) {+        case JS_CLASS_ARRAY:+            if (unlikely(idx >= (uint32_t)p->u.array.count)) {+                JSObject *p1;+                JSShape *sh1;++                /* fast path to add an element to the array */+                if (idx != (uint32_t)p->u.array.count ||+                    !p->fast_array || !p->extensible)+                    goto slow_path;+                /* check if prototype chain has a numeric property */+                p1 = p->shape->proto;+                while (p1 != NULL) {+                    sh1 = p1->shape;+                    if (p1->class_id == JS_CLASS_ARRAY) {+                        if (unlikely(!p1->fast_array))+                            goto slow_path;+                    } else if (p1->class_id == JS_CLASS_OBJECT) {+                        if (unlikely(sh1->has_small_array_index))+                            goto slow_path;+                    } else {+                        goto slow_path;+                    }+                    p1 = sh1->proto;+                }+                /* add element */+                return add_fast_array_element(ctx, p, val, flags);+            }+            set_value(ctx, &p->u.array.u.values[idx], val);+            break;+        case JS_CLASS_ARGUMENTS:+            if (unlikely(idx >= (uint32_t)p->u.array.count))+                goto slow_path;+            set_value(ctx, &p->u.array.u.values[idx], val);+            break;+        case JS_CLASS_UINT8C_ARRAY:+            if (JS_ToUint8ClampFree(ctx, &v, val))+                return -1;+            /* Note: the conversion can detach the typed array, so the+               array bound check must be done after */+            if (unlikely(idx >= (uint32_t)p->u.array.count))+                goto ta_out_of_bound;+            p->u.array.u.uint8_ptr[idx] = v;+            break;+        case JS_CLASS_INT8_ARRAY:+        case JS_CLASS_UINT8_ARRAY:+            if (JS_ToInt32Free(ctx, &v, val))+                return -1;+            if (unlikely(idx >= (uint32_t)p->u.array.count))+                goto ta_out_of_bound;+            p->u.array.u.uint8_ptr[idx] = v;+            break;+        case JS_CLASS_INT16_ARRAY:+        case JS_CLASS_UINT16_ARRAY:+            if (JS_ToInt32Free(ctx, &v, val))+                return -1;+            if (unlikely(idx >= (uint32_t)p->u.array.count))+                goto ta_out_of_bound;+            p->u.array.u.uint16_ptr[idx] = v;+            break;+        case JS_CLASS_INT32_ARRAY:+        case JS_CLASS_UINT32_ARRAY:+            if (JS_ToInt32Free(ctx, &v, val))+                return -1;+            if (unlikely(idx >= (uint32_t)p->u.array.count))+                goto ta_out_of_bound;+            p->u.array.u.uint32_ptr[idx] = v;+            break;+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT64_ARRAY:+        case JS_CLASS_BIG_UINT64_ARRAY:+            /* XXX: need specific conversion function */+            {+                int64_t v;+                if (JS_ToBigInt64Free(ctx, &v, val))+                    return -1;+                if (unlikely(idx >= (uint32_t)p->u.array.count))+                    goto ta_out_of_bound;+                p->u.array.u.uint64_ptr[idx] = v;+            }+            break;+#endif+        case JS_CLASS_FLOAT32_ARRAY:+            if (JS_ToFloat64Free(ctx, &d, val))+                return -1;+            if (unlikely(idx >= (uint32_t)p->u.array.count))+                goto ta_out_of_bound;+            p->u.array.u.float_ptr[idx] = d;+            break;+        case JS_CLASS_FLOAT64_ARRAY:+            if (JS_ToFloat64Free(ctx, &d, val))+                return -1;+            if (unlikely(idx >= (uint32_t)p->u.array.count)) {+            ta_out_of_bound:+                if (typed_array_is_detached(ctx, p)) {+                    JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+                    return -1;+                } else {+                    return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");+                }+            }+            p->u.array.u.double_ptr[idx] = d;+            break;+        default:+            goto slow_path;+        }+        return TRUE;+    } else {+        JSAtom atom;+        int ret;+    slow_path:+        atom = JS_ValueToAtom(ctx, prop);+        JS_FreeValue(ctx, prop);+        if (unlikely(atom == JS_ATOM_NULL)) {+            JS_FreeValue(ctx, val);+            return -1;+        }+        ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);+        JS_FreeAtom(ctx, atom);+        return ret;+    }+}++int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,+                         uint32_t idx, JSValue val)+{+    return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,+                               JS_PROP_THROW);+}++int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,+                        int64_t idx, JSValue val)+{+    JSAtom prop;+    int res;++    if ((uint64_t)idx <= INT32_MAX) {+        /* fast path for fast arrays */+        return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,+                                   JS_PROP_THROW);+    }+    prop = JS_NewAtomInt64(ctx, idx);+    if (prop == JS_ATOM_NULL) {+        JS_FreeValue(ctx, val);+        return -1;+    }+    res = JS_SetProperty(ctx, this_obj, prop, val);+    JS_FreeAtom(ctx, prop);+    return res;+}++int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,+                      const char *prop, JSValue val)+{+    JSAtom atom;+    int ret;+    atom = JS_NewAtom(ctx, prop);+    ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);+    JS_FreeAtom(ctx, atom);+    return ret;+}++/* compute the property flags. For each flag: (JS_PROP_HAS_x forces+   it, otherwise def_flags is used)+   Note: makes assumption about the bit pattern of the flags+*/+static int get_prop_flags(int flags, int def_flags)+{+    int mask;+    mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;+    return (flags & mask) | (def_flags & ~mask);+}++static int JS_CreateProperty(JSContext *ctx, JSObject *p,+                             JSAtom prop, JSValueConst val,+                             JSValueConst getter, JSValueConst setter,+                             int flags)+{+    JSProperty *pr;+    int ret, prop_flags;++    /* add a new property or modify an existing exotic one */+    if (p->is_exotic) {+        if (p->class_id == JS_CLASS_ARRAY) {+            uint32_t idx, len;++            if (p->fast_array) {+                if (__JS_AtomIsTaggedInt(prop)) {+                    idx = __JS_AtomToUInt32(prop);+                    if (idx == p->u.array.count) {+                        if (!p->extensible)+                            goto not_extensible;+                        if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))+                            goto convert_to_array;+                        prop_flags = get_prop_flags(flags, 0);+                        if (prop_flags != JS_PROP_C_W_E)+                            goto convert_to_array;+                        return add_fast_array_element(ctx, p,+                                                      JS_DupValue(ctx, val), flags);+                    } else {+                        goto convert_to_array;+                    }+                } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {+                    /* convert the fast array to normal array */+                convert_to_array:+                    if (convert_fast_array_to_array(ctx, p))+                        return -1;+                    goto generic_array;+                }+            } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {+                JSProperty *plen;+                JSShapeProperty *pslen;+            generic_array:+                /* update the length field */+                plen = &p->prop[0];+                JS_ToUint32(ctx, &len, plen->u.value);+                if ((idx + 1) > len) {+                    pslen = get_shape_prop(p->shape);+                    if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))+                        return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);+                    /* XXX: should update the length after defining+                       the property */+                    len = idx + 1;+                    set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));+                }+            }+        } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                   p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+            ret = JS_AtomIsNumericIndex(ctx, prop);+            if (ret != 0) {+                if (ret < 0)+                    return -1;+                return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");+            }+        } else if (!(flags & JS_PROP_NO_EXOTIC)) {+            const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+            if (em) {+                if (em->define_own_property) {+                    return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),+                                                   prop, val, getter, setter, flags);+                }+                ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+                if (ret < 0)+                    return -1;+                if (!ret)+                    goto not_extensible;+            }+        }+    }++    if (!p->extensible) {+    not_extensible:+        return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");+    }++    if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+        prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |+            JS_PROP_GETSET;+    } else {+        prop_flags = flags & JS_PROP_C_W_E;+    }+    pr = add_property(ctx, p, prop, prop_flags);+    if (unlikely(!pr))+        return -1;+    if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+        pr->u.getset.getter = NULL;+        if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {+            pr->u.getset.getter =+                JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));+        }+        pr->u.getset.setter = NULL;+        if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {+            pr->u.getset.setter =+                JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));+        }+    } else {+        if (flags & JS_PROP_HAS_VALUE) {+            pr->u.value = JS_DupValue(ctx, val);+        } else {+            pr->u.value = JS_UNDEFINED;+        }+    }+    return TRUE;+}++/* return FALSE if not OK */+static BOOL check_define_prop_flags(int prop_flags, int flags)+{+    BOOL has_accessor, is_getset;++    if (!(prop_flags & JS_PROP_CONFIGURABLE)) {+        if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==+            (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {+            return FALSE;+        }+        if ((flags & JS_PROP_HAS_ENUMERABLE) &&+            (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))+            return FALSE;+    }+    if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |+                 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+        if (!(prop_flags & JS_PROP_CONFIGURABLE)) {+            has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);+            is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);+            if (has_accessor != is_getset)+                return FALSE;+            if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {+                /* not writable: cannot set the writable bit */+                if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==+                    (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))+                    return FALSE;+            }+        }+    }+    return TRUE;+}++/* ensure that the shape can be safely modified */+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,+                                   JSShapeProperty **pprs)+{+    JSShape *sh;+    uint32_t idx = 0;    /* prevent warning */++    sh = p->shape;+    if (sh->is_hashed) {+        if (sh->header.ref_count != 1) {+            if (pprs)+                idx = *pprs - get_shape_prop(sh);+            /* clone the shape (the resulting one is no longer hashed) */+            sh = js_clone_shape(ctx, sh);+            if (!sh)+                return -1;+            js_free_shape(ctx->rt, p->shape);+            p->shape = sh;+            if (pprs)+                *pprs = get_shape_prop(sh) + idx;+        } else {+            js_shape_hash_unlink(ctx->rt, sh);+            sh->is_hashed = FALSE;+        }+    }+    return 0;+}++static int js_update_property_flags(JSContext *ctx, JSObject *p,+                                    JSShapeProperty **pprs, int flags)+{+    if (flags != (*pprs)->flags) {+        if (js_shape_prepare_update(ctx, p, pprs))+            return -1;+        (*pprs)->flags = flags;+    }+    return 0;+}++/* allowed flags:+   JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE+   JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,+   JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,+   JS_PROP_THROW, JS_PROP_NO_EXOTIC.+   If JS_PROP_THROW is set, return an exception instead of FALSE.+   if JS_PROP_NO_EXOTIC is set, do not call the exotic+   define_own_property callback.+   return -1 (exception), FALSE or TRUE.+*/+int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,+                      JSAtom prop, JSValueConst val,+                      JSValueConst getter, JSValueConst setter, int flags)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;+    int mask, res;++    if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    p = JS_VALUE_GET_OBJ(this_obj);++ redo_prop_update:+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        /* property already exists */+        if (!check_define_prop_flags(prs->flags, flags)) {+        not_configurable:+            return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");+        }++    retry:+        if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |+                     JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+            if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+                JSObject *new_getter, *new_setter;++                if (JS_IsFunction(ctx, getter)) {+                    new_getter = JS_VALUE_GET_OBJ(getter);+                } else {+                    new_getter = NULL;+                }+                if (JS_IsFunction(ctx, setter)) {+                    new_setter = JS_VALUE_GET_OBJ(setter);+                } else {+                    new_setter = NULL;+                }++                if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {+                    if (js_shape_prepare_update(ctx, p, &prs))+                        return -1;+                    /* convert to getset */+                    if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                        free_var_ref(ctx->rt, pr->u.var_ref);+                    } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                        /* clear property and update */+                        if (js_shape_prepare_update(ctx, p, &prs))+                            return -1;+                        js_autoinit_free(ctx->rt, pr);+                        prs->flags &= ~JS_PROP_TMASK;+                        pr->u.value = JS_UNDEFINED;+                        goto retry;+                    } else {+                        JS_FreeValue(ctx, pr->u.value);+                    }+                    prs->flags = (prs->flags &+                                  (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |+                        JS_PROP_GETSET;+                    pr->u.getset.getter = NULL;+                    pr->u.getset.setter = NULL;+                } else {+                    if (!(prs->flags & JS_PROP_CONFIGURABLE)) {+                        if ((flags & JS_PROP_HAS_GET) &&+                            new_getter != pr->u.getset.getter) {+                            goto not_configurable;+                        }+                        if ((flags & JS_PROP_HAS_SET) &&+                            new_setter != pr->u.getset.setter) {+                            goto not_configurable;+                        }+                    }+                }+                if (flags & JS_PROP_HAS_GET) {+                    if (pr->u.getset.getter)+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+                    if (new_getter)+                        JS_DupValue(ctx, getter);+                    pr->u.getset.getter = new_getter;+                }+                if (flags & JS_PROP_HAS_SET) {+                    if (pr->u.getset.setter)+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+                    if (new_setter)+                        JS_DupValue(ctx, setter);+                    pr->u.getset.setter = new_setter;+                }+            } else {+                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+                    /* convert to data descriptor */+                    if (js_shape_prepare_update(ctx, p, &prs))+                        return -1;+                    if (pr->u.getset.getter)+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+                    if (pr->u.getset.setter)+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+                    prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);+                    pr->u.value = JS_UNDEFINED;+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                    /* Note: JS_PROP_VARREF is always writable */+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                    /* clear property and update */+                    if (js_shape_prepare_update(ctx, p, &prs))+                        return -1;+                    js_autoinit_free(ctx->rt, pr);+                    prs->flags &= ~JS_PROP_TMASK;+                    pr->u.value = JS_UNDEFINED;+                } else {+                    if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&+                        (flags & JS_PROP_HAS_VALUE) &&+                        !js_same_value(ctx, val, pr->u.value)) {+                        goto not_configurable;+                    }+                }+                if (prs->flags & JS_PROP_LENGTH) {+                    if (flags & JS_PROP_HAS_VALUE) {+                        res = set_array_length(ctx, p, pr, JS_DupValue(ctx, val),+                                               flags);+                    } else {+                        res = TRUE;+                    }+                    /* still need to reset the writable flag if needed.+                       The JS_PROP_LENGTH is reset to have the correct+                       read-only behavior in JS_SetProperty(). */+                    if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==+                        JS_PROP_HAS_WRITABLE) {+                        prs = get_shape_prop(p->shape);+                        if (js_update_property_flags(ctx, p, &prs,+                                                     prs->flags & ~(JS_PROP_WRITABLE | JS_PROP_LENGTH)))+                            return -1;+                    }+                    return res;+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                    if (flags & JS_PROP_HAS_VALUE) {+                        if (p->class_id == JS_CLASS_MODULE_NS) {+                            /* JS_PROP_WRITABLE is always true for variable+                               references, but they are write protected in module name+                               spaces. */+                            if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))+                                goto not_configurable;+                        }+                        /* update the reference */+                        set_value(ctx, pr->u.var_ref->pvalue,+                                  JS_DupValue(ctx, val));+                    }+                    /* if writable is set to false, no longer a+                       reference (for mapped arguments) */+                    if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {+                        JSValue val1;+                        if (js_shape_prepare_update(ctx, p, &prs))+                            return -1;+                        val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);+                        free_var_ref(ctx->rt, pr->u.var_ref);+                        pr->u.value = val1;+                        prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);+                    }+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                    /* XXX: should never happen, type was reset above */+                    abort();+                } else {+                    if (flags & JS_PROP_HAS_VALUE) {+                        JS_FreeValue(ctx, pr->u.value);+                        pr->u.value = JS_DupValue(ctx, val);+                    }+                    if (flags & JS_PROP_HAS_WRITABLE) {+                        if (js_update_property_flags(ctx, p, &prs,+                                                     (prs->flags & ~JS_PROP_WRITABLE) |+                                                     (flags & JS_PROP_WRITABLE)))+                            return -1;+                    }+                }+            }+        }+        mask = 0;+        if (flags & JS_PROP_HAS_CONFIGURABLE)+            mask |= JS_PROP_CONFIGURABLE;+        if (flags & JS_PROP_HAS_ENUMERABLE)+            mask |= JS_PROP_ENUMERABLE;+        if (js_update_property_flags(ctx, p, &prs,+                                     (prs->flags & ~mask) | (flags & mask)))+            return -1;+        return TRUE;+    }++    /* handle modification of fast array elements */+    if (p->fast_array) {+        uint32_t idx;+        uint32_t prop_flags;+        if (p->class_id == JS_CLASS_ARRAY) {+            if (__JS_AtomIsTaggedInt(prop)) {+                idx = __JS_AtomToUInt32(prop);+                if (idx < p->u.array.count) {+                    prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);+                    if (prop_flags != JS_PROP_C_W_E)+                        goto convert_to_slow_array;+                    if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+                    convert_to_slow_array:+                        if (convert_fast_array_to_array(ctx, p))+                            return -1;+                        else+                            goto redo_prop_update;+                    }+                    if (flags & JS_PROP_HAS_VALUE) {+                        set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));+                    }+                    return TRUE;+                }+            }+        } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                   p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+            JSValue num;+            int ret;++            if (!__JS_AtomIsTaggedInt(prop)) {+                /* slow path with to handle all numeric indexes */+                num = JS_AtomIsNumericIndex1(ctx, prop);+                if (JS_IsUndefined(num))+                    goto typed_array_done;+                if (JS_IsException(num))+                    return -1;+                ret = JS_NumberIsInteger(ctx, num);+                if (ret < 0) {+                    JS_FreeValue(ctx, num);+                    return -1;+                }+                if (!ret) {+                    JS_FreeValue(ctx, num);+                    return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");+                }+                ret = JS_NumberIsNegativeOrMinusZero(ctx, num);+                JS_FreeValue(ctx, num);+                if (ret) {+                    return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");+                }+                if (!__JS_AtomIsTaggedInt(prop))+                    goto typed_array_oob;+            }+            idx = __JS_AtomToUInt32(prop);+            /* if the typed array is detached, p->u.array.count = 0 */+            if (idx >= typed_array_get_length(ctx, p)) {+            typed_array_oob:+                return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");+            }+            prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);+            if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||+                prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE)) {+                return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");+            }+            if (flags & JS_PROP_HAS_VALUE) {+                return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);+            }+            return TRUE;+        typed_array_done: ;+        }+    }++    return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);+}++static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,+                                     JSAtom prop, JSAutoInitIDEnum id,+                                     void *opaque, int flags)+{+    JSObject *p;+    JSProperty *pr;++    if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)+        return FALSE;++    p = JS_VALUE_GET_OBJ(this_obj);++    if (find_own_property(&pr, p, prop)) {+        /* property already exists */+        abort();+        return FALSE;+    }++    /* Specialized CreateProperty */+    pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);+    if (unlikely(!pr))+        return -1;+    pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);+    assert((pr->u.init.realm_and_id & 3) == 0);+    assert(id <= 3);+    pr->u.init.realm_and_id |= id;+    pr->u.init.opaque = opaque;+    return TRUE;+}++/* shortcut to add or redefine a new property value */+int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,+                           JSAtom prop, JSValue val, int flags)+{+    int ret;+    ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,+                            flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);+    JS_FreeValue(ctx, val);+    return ret;+}++int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,+                                JSValue prop, JSValue val, int flags)+{+    JSAtom atom;+    int ret;+    atom = JS_ValueToAtom(ctx, prop);+    JS_FreeValue(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL)) {+        JS_FreeValue(ctx, val);+        return -1;+    }+    ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);+    JS_FreeAtom(ctx, atom);+    return ret;+}++int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,+                                 uint32_t idx, JSValue val, int flags)+{+    return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),+                                       val, flags);+}++int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,+                                int64_t idx, JSValue val, int flags)+{+    return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),+                                       val, flags);+}++int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,+                              const char *prop, JSValue val, int flags)+{+    JSAtom atom;+    int ret;+    atom = JS_NewAtom(ctx, prop);+    ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);+    JS_FreeAtom(ctx, atom);+    return ret;+}++/* shortcut to add getter & setter */+int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,+                            JSAtom prop, JSValue getter, JSValue setter,+                            int flags)+{+    int ret;+    ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,+                            flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |+                            JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);+    JS_FreeValue(ctx, getter);+    JS_FreeValue(ctx, setter);+    return ret;+}++static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,+                                       int64_t idx, JSValue val, int flags)+{+    return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),+                                       val, flags | JS_PROP_CONFIGURABLE |+                                       JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);+}+++/* return TRUE if 'obj' has a non empty 'name' string */+static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)+{+    JSProperty *pr;+    JSShapeProperty *prs;+    JSValueConst val;+    JSString *p;+    +    prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);+    if (!prs)+        return FALSE;+    if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)+        return TRUE;+    val = pr->u.value;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)+        return TRUE;+    p = JS_VALUE_GET_STRING(val);+    return (p->len != 0);+}++static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,+                               JSAtom name, int flags)+{+    if (name != JS_ATOM_NULL+    &&  JS_IsObject(obj)+    &&  !js_object_has_name(ctx, obj)+    &&  JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {+        return -1;+    }+    return 0;+}++static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,+                                       JSValueConst str, int flags)+{+    if (JS_IsObject(obj) &&+        !js_object_has_name(ctx, obj)) {+        JSAtom prop;+        JSValue name_str;+        prop = JS_ValueToAtom(ctx, str);+        if (prop == JS_ATOM_NULL)+            return -1;+        name_str = js_get_function_name(ctx, prop);+        JS_FreeAtom(ctx, prop);+        if (JS_IsException(name_str))+            return -1;+        if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)+            return -1;+    }+    return 0;+}++#define DEFINE_GLOBAL_LEX_VAR (1 << 7)+#define DEFINE_GLOBAL_FUNC_VAR (1 << 6)++static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)+{+    return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);+}++/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */+/* XXX: could support exotic global object. */+static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)+{+    JSObject *p;+    JSShapeProperty *prs;++    p = JS_VALUE_GET_OBJ(ctx->global_obj);+    prs = find_own_property1(p, prop);+    /* XXX: should handle JS_PROP_AUTOINIT */+    if (flags & DEFINE_GLOBAL_LEX_VAR) {+        if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))+            goto fail_redeclaration;+    } else {+        if (!prs && !p->extensible)+            goto define_error;+        if (flags & DEFINE_GLOBAL_FUNC_VAR) {+            if (prs) {+                if (!(prs->flags & JS_PROP_CONFIGURABLE) &&+                    ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||+                     ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=+                      (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {+                define_error:+                    JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",+                                          prop);+                    return -1;+                }+            }+        }+    }+    /* check if there already is a lexical declaration */+    p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+    prs = find_own_property1(p, prop);+    if (prs) {+    fail_redeclaration:+        JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);+        return -1;+    }+    return 0;+}++/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |+   JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */+/* XXX: could support exotic global object. */+static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;+    JSValue val;+    int flags;++    if (def_flags & DEFINE_GLOBAL_LEX_VAR) {+        p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+        flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |+            JS_PROP_CONFIGURABLE;+        val = JS_UNINITIALIZED;+    } else {+        p = JS_VALUE_GET_OBJ(ctx->global_obj);+        flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |+            (def_flags & JS_PROP_CONFIGURABLE);+        val = JS_UNDEFINED;+    }+    prs = find_own_property1(p, prop);+    if (prs)+        return 0;+    if (!p->extensible)+        return 0;+    pr = add_property(ctx, p, prop, flags);+    if (unlikely(!pr))+        return -1;+    pr->u.value = val;+    return 0;+}++/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */+/* XXX: could support exotic global object. */+static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,+                                   JSValueConst func, int def_flags)+{++    JSObject *p;+    JSShapeProperty *prs;+    int flags;++    p = JS_VALUE_GET_OBJ(ctx->global_obj);+    prs = find_own_property1(p, prop);+    flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;+    if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {+        flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |+            JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;+    }+    if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,+                          JS_UNDEFINED, JS_UNDEFINED, flags) < 0)+        return -1;+    return 0;+}++static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,+                               BOOL throw_ref_error)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;++    /* no exotic behavior is possible in global_var_obj */+    p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        /* XXX: should handle JS_PROP_TMASK properties */+        if (unlikely(JS_IsUninitialized(pr->u.value)))+            return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+        return JS_DupValue(ctx, pr->u.value);+    }+    return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,+                                 ctx->global_obj, throw_ref_error);+}++/* construct a reference to a global variable */+static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;++    /* no exotic behavior is possible in global_var_obj */+    p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        /* XXX: should handle JS_PROP_AUTOINIT properties? */+        /* XXX: conformance: do these tests in+           OP_put_var_ref/OP_get_var_ref ? */+        if (unlikely(JS_IsUninitialized(pr->u.value))) {+            JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+            return -1;+        }+        if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {+            return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);+        }+        sp[0] = JS_DupValue(ctx, ctx->global_var_obj);+    } else {+        int ret;+        ret = JS_HasProperty(ctx, ctx->global_obj, prop);+        if (ret < 0)+            return -1;+        if (ret) {+            sp[0] = JS_DupValue(ctx, ctx->global_obj);+        } else {+            sp[0] = JS_UNDEFINED;+        }+    }+    sp[1] = JS_AtomToValue(ctx, prop);+    return 0;+}++/* use for strict variable access: test if the variable exists */+static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)+{+    JSObject *p;+    JSShapeProperty *prs;+    int ret;++    /* no exotic behavior is possible in global_var_obj */+    p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+    prs = find_own_property1(p, prop);+    if (prs) {+        ret = TRUE;+    } else {+        ret = JS_HasProperty(ctx, ctx->global_obj, prop);+        if (ret < 0)+            return -1;+    }+    return ret;+}++/* flag = 0: normal variable write+   flag = 1: initialize lexical variable+   flag = 2: normal variable write, strict check was done before+*/+static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,+                           int flag)+{+    JSObject *p;+    JSShapeProperty *prs;+    JSProperty *pr;+    int flags;++    /* no exotic behavior is possible in global_var_obj */+    p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+    prs = find_own_property(&pr, p, prop);+    if (prs) {+        /* XXX: should handle JS_PROP_AUTOINIT properties? */+        if (flag != 1) {+            if (unlikely(JS_IsUninitialized(pr->u.value))) {+                JS_FreeValue(ctx, val);+                JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+                return -1;+            }+            if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {+                JS_FreeValue(ctx, val);+                return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);+            }+        }+        set_value(ctx, &pr->u.value, val);+        return 0;+    }++    flags = JS_PROP_THROW_STRICT;+    if (flag != 2 && is_strict_mode(ctx))+        flags |= JS_PROP_NO_ADD;+    return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);+}++/* return -1, FALSE or TRUE. return FALSE if not configurable or+   invalid object. return -1 in case of exception.+   flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */+int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)+{+    JSValue obj1;+    JSObject *p;+    int res;+    +    obj1 = JS_ToObject(ctx, obj);+    if (JS_IsException(obj1))+        return -1;+    p = JS_VALUE_GET_OBJ(obj1);+    res = delete_property(ctx, p, prop);+    JS_FreeValue(ctx, obj1);+    if (res != FALSE)+        return res;+    if ((flags & JS_PROP_THROW) ||+        ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+        JS_ThrowTypeError(ctx, "could not delete property");+        return -1;+    }+    return FALSE;+}++int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)+{+    JSAtom prop;+    int res;++    if ((uint64_t)idx <= JS_ATOM_MAX_INT) {+        /* fast path for fast arrays */+        return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);+    }+    prop = JS_NewAtomInt64(ctx, idx);+    if (prop == JS_ATOM_NULL)+        return -1;+    res = JS_DeleteProperty(ctx, obj, prop, flags);+    JS_FreeAtom(ctx, prop);+    return res;+}++BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(val);+    switch(p->class_id) {+    case JS_CLASS_BYTECODE_FUNCTION:+        return TRUE;+    case JS_CLASS_PROXY:+        return p->u.proxy_data->is_func;+    default:+        return (ctx->rt->class_array[p->class_id].call != NULL);+    }+}++BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(val);+    if (p->class_id == JS_CLASS_C_FUNCTION)+        return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);+    else+        return FALSE;+}++BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(val);+    return p->is_constructor;+}++BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(func_obj);+    p->is_constructor = val;+    return TRUE;+}++BOOL JS_IsError(JSContext *ctx, JSValueConst val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(val);+    return (p->class_id == JS_CLASS_ERROR);+}++/* used to avoid catching interrupt exceptions */+BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return FALSE;+    p = JS_VALUE_GET_OBJ(val);+    return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;+}++void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return;+    p = JS_VALUE_GET_OBJ(val);+    if (p->class_id == JS_CLASS_ERROR)+        p->is_uncatchable_error = flag;+}++void JS_ResetUncatchableError(JSContext *ctx)+{+    JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);+}++void JS_SetOpaque(JSValue obj, void *opaque)+{+   JSObject *p;+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        p = JS_VALUE_GET_OBJ(obj);+        p->u.opaque = opaque;+    }+}++/* return NULL if not an object of class class_id */+void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return NULL;+    p = JS_VALUE_GET_OBJ(obj);+    if (p->class_id != class_id)+        return NULL;+    return p->u.opaque;+}++void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)+{+    void *p = JS_GetOpaque(obj, class_id);+    if (unlikely(!p)) {+        JS_ThrowTypeErrorInvalidClass(ctx, class_id);+    }+    return p;+}++#define HINT_STRING  0+#define HINT_NUMBER  1+#define HINT_NONE    2+/* don't try Symbol.toPrimitive */+#define HINT_FORCE_ORDINARY (1 << 4)++static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)+{+    int i;+    BOOL force_ordinary;++    JSAtom method_name;+    JSValue method, ret;+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+        return val;+    force_ordinary = hint & HINT_FORCE_ORDINARY;+    hint &= ~HINT_FORCE_ORDINARY;+    if (!force_ordinary) {+        method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);+        if (JS_IsException(method))+            goto exception;+        /* ECMA says *If exoticToPrim is not undefined* but tests in+           test262 use null as a non callable converter */+        if (!JS_IsUndefined(method) && !JS_IsNull(method)) {+            JSAtom atom;+            JSValue arg;+            switch(hint) {+            case HINT_STRING:+                atom = JS_ATOM_string;+                break;+            case HINT_NUMBER:+                atom = JS_ATOM_number;+                break;+            default:+            case HINT_NONE:+                atom = JS_ATOM_default;+                break;+            }+            arg = JS_AtomToString(ctx, atom);+            ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);+            JS_FreeValue(ctx, arg);+            if (JS_IsException(ret))+                goto exception;+            JS_FreeValue(ctx, val);+            if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)+                return ret;+            JS_FreeValue(ctx, ret);+            return JS_ThrowTypeError(ctx, "toPrimitive");+        }+    }+    if (hint != HINT_STRING)+        hint = HINT_NUMBER;+    for(i = 0; i < 2; i++) {+        if ((i ^ hint) == 0) {+            method_name = JS_ATOM_toString;+        } else {+            method_name = JS_ATOM_valueOf;+        }+        method = JS_GetProperty(ctx, val, method_name);+        if (JS_IsException(method))+            goto exception;+        if (JS_IsFunction(ctx, method)) {+            ret = JS_CallFree(ctx, method, val, 0, NULL);+            if (JS_IsException(ret))+                goto exception;+            if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {+                JS_FreeValue(ctx, val);+                return ret;+            }+            JS_FreeValue(ctx, ret);+        } else {+            JS_FreeValue(ctx, method);+        }+    }+    JS_ThrowTypeError(ctx, "toPrimitive");+exception:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)+{+    return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);+}++static int JS_ToBoolFree(JSContext *ctx, JSValue val)+{+    uint32_t tag = JS_VALUE_GET_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+        return JS_VALUE_GET_INT(val) != 0;+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        return JS_VALUE_GET_INT(val);+    case JS_TAG_EXCEPTION:+        return -1;+    case JS_TAG_STRING:+        {+            BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;+            JS_FreeValue(ctx, val);+            return ret;+        }+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            BOOL ret;+            ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;+            JS_FreeValue(ctx, val);+            return ret;+        }+    case JS_TAG_BIG_DECIMAL:+        {+            JSBigDecimal *p = JS_VALUE_GET_PTR(val);+            BOOL ret;+            ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;+            JS_FreeValue(ctx, val);+            return ret;+        }+#endif+    default:+        if (JS_TAG_IS_FLOAT64(tag)) {+            double d = JS_VALUE_GET_FLOAT64(val);+            return !isnan(d) && d != 0;+        } else {+            JS_FreeValue(ctx, val);+            return TRUE;+        }+    }+}++int JS_ToBool(JSContext *ctx, JSValueConst val)+{+    return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));+}++static int skip_spaces(const char *pc)+{+    const uint8_t *p, *p_next, *p_start;+    uint32_t c;++    p = p_start = (const uint8_t *)pc;+    for (;;) {+        c = *p;+        if (c < 128) {+            if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))+                break;+            p++;+        } else {+            c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);+            if (!lre_is_space(c))+                break;+            p = p_next;+        }+    }+    return p - p_start;+}++static inline int to_digit(int c)+{+    if (c >= '0' && c <= '9')+        return c - '0';+    else if (c >= 'A' && c <= 'Z')+        return c - 'A' + 10;+    else if (c >= 'a' && c <= 'z')+        return c - 'a' + 10;+    else+        return 36;+}++/* XXX: remove */+static double js_strtod(const char *p, int radix, BOOL is_float)+{+    double d;+    int c;+    +    if (!is_float || radix != 10) {+        uint64_t n_max, n;+        int int_exp, is_neg;+        +        is_neg = 0;+        if (*p == '-') {+            is_neg = 1;+            p++;+        }++        /* skip leading zeros */+        while (*p == '0')+            p++;+        n = 0;+        if (radix == 10)+            n_max = ((uint64_t)-1 - 9) / 10; /* most common case */+        else+            n_max = ((uint64_t)-1 - (radix - 1)) / radix;+        /* XXX: could be more precise */+        int_exp = 0;+        while (*p != '\0') {+            c = to_digit((uint8_t)*p);+            if (c >= radix)+                break;+            if (n <= n_max) {+                n = n * radix + c;+            } else {+                int_exp++;+            }+            p++;+        }+        d = n;+        if (int_exp != 0) {+            d *= pow(radix, int_exp);+        }+        if (is_neg)+            d = -d;+    } else {+        d = strtod(p, NULL);+    }+    return d;+}++#define ATOD_INT_ONLY        (1 << 0)+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */+#define ATOD_ACCEPT_BIN_OCT  (1 << 2)+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */+#define ATOD_ACCEPT_LEGACY_OCTAL  (1 << 4)+/* accept _ between digits as a digit separator */+#define ATOD_ACCEPT_UNDERSCORES  (1 << 5)+/* allow a suffix to override the type */+#define ATOD_ACCEPT_SUFFIX    (1 << 6) +/* default type */+#define ATOD_TYPE_MASK        (3 << 7)+#define ATOD_TYPE_FLOAT64     (0 << 7)+#define ATOD_TYPE_BIG_INT     (1 << 7)+#define ATOD_TYPE_BIG_FLOAT   (2 << 7)+#define ATOD_TYPE_BIG_DECIMAL (3 << 7)+/* assume bigint mode: floats are parsed as integers if no decimal+   point nor exponent */+#define ATOD_MODE_BIGINT      (1 << 9) +/* accept -0x1 */+#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)++#ifdef CONFIG_BIGNUM+static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,+                                   int radix, int flags, slimb_t *pexponent)+{+    bf_t a_s, *a = &a_s;+    int ret;+    JSValue val;+    val = JS_NewBigInt(ctx);+    if (JS_IsException(val))+        return val;+    a = JS_GetBigInt(val);+    ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);+    if (ret & BF_ST_MEM_ERROR) {+        JS_FreeValue(ctx, val);+        return JS_ThrowOutOfMemory(ctx);+    }+    val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);+    return val;+}++static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,+                                     int radix, int flags, slimb_t *pexponent)+{+    bf_t *a;+    int ret;+    JSValue val;+    +    val = JS_NewBigFloat(ctx);+    if (JS_IsException(val))+        return val;+    a = JS_GetBigFloat(val);+    if (flags & ATOD_ACCEPT_SUFFIX) {+        /* return the exponent to get infinite precision */+        ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,+                       BF_RNDZ | BF_ATOF_EXPONENT);+    } else {+        ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,+                      ctx->fp_env.flags);+    }+    if (ret & BF_ST_MEM_ERROR) {+        JS_FreeValue(ctx, val);+        return JS_ThrowOutOfMemory(ctx);+    }+    return val;+}++static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,+                                       int radix, int flags, slimb_t *pexponent)+{+    bfdec_t *a;+    int ret;+    JSValue val;+    +    val = JS_NewBigDecimal(ctx);+    if (JS_IsException(val))+        return val;+    a = JS_GetBigDecimal(val);+    ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,+                     BF_RNDZ | BF_ATOF_NO_NAN_INF);+    if (ret & BF_ST_MEM_ERROR) {+        JS_FreeValue(ctx, val);+        return JS_ThrowOutOfMemory(ctx);+    }+    return val;+}++#endif++/* return an exception in case of memory error. Return JS_NAN if+   invalid syntax */+#ifdef CONFIG_BIGNUM+static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,+                        int radix, int flags, slimb_t *pexponent)+#else+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,+                       int radix, int flags)+#endif+{+    const char *p, *p_start;+    int sep, is_neg;+    BOOL is_float, has_legacy_octal;+    int atod_type = flags & ATOD_TYPE_MASK;+    char buf1[64], *buf;+    int i, j, len;+    BOOL buf_allocated = FALSE;+    JSValue val;+    +    /* optional separator between digits */+    sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;+    has_legacy_octal = FALSE;+    +    p = str;+    p_start = p;+    is_neg = 0;+    if (p[0] == '+') {+        p++;+        p_start++;+        if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))+            goto no_radix_prefix;+    } else if (p[0] == '-') {+        p++;+        p_start++;+        is_neg = 1;+        if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))+            goto no_radix_prefix;+    }+    if (p[0] == '0') {+        if ((p[1] == 'x' || p[1] == 'X') &&+            (radix == 0 || radix == 16)) {+            p += 2;+            radix = 16;+        } else if ((p[1] == 'o' || p[1] == 'O') &&+                   radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {+            p += 2;+            radix = 8;+        } else if ((p[1] == 'b' || p[1] == 'B') &&+                   radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {+            p += 2;+            radix = 2;+        } else if ((p[1] >= '0' && p[1] <= '9') &&+                   radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {+            int i;+            has_legacy_octal = TRUE;+            sep = 256;+            for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)+                continue;+            if (p[i] == '8' || p[i] == '9')+                goto no_prefix;+            p += 1;+            radix = 8;+        } else {+            goto no_prefix;+        }+        /* there must be a digit after the prefix */+        if (to_digit((uint8_t)*p) >= radix)+            goto fail;+    no_prefix: ;+    } else {+ no_radix_prefix:+        if (!(flags & ATOD_INT_ONLY) &&+            (atod_type == ATOD_TYPE_FLOAT64 ||+             atod_type == ATOD_TYPE_BIG_FLOAT) &&+            strstart(p, "Infinity", &p)) {+#ifdef CONFIG_BIGNUM+            if (atod_type == ATOD_TYPE_BIG_FLOAT) {+                bf_t *a;+                val = JS_NewBigFloat(ctx);+                if (JS_IsException(val))+                    goto done;+                a = JS_GetBigFloat(val);+                bf_set_inf(a, is_neg);+            } else+#endif+            {+                double d = 1.0 / 0.0;+                if (is_neg)+                    d = -d;+                val = JS_NewFloat64(ctx, d);+            }+            goto done;+        }+    }+    if (radix == 0)+        radix = 10;+    is_float = FALSE;+    p_start = p;+    while (to_digit((uint8_t)*p) < radix+           ||  (*p == sep && (radix != 10 ||+                              p != p_start + 1 || p[-1] != '0') &&+                to_digit((uint8_t)p[1]) < radix)) {+        p++;+    }+    if (!(flags & ATOD_INT_ONLY)) {+        if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {+            is_float = TRUE;+            p++;+            if (*p == sep)+                goto fail;+            while (to_digit((uint8_t)*p) < radix ||+                   (*p == sep && to_digit((uint8_t)p[1]) < radix))+                p++;+        }+        if (p > p_start &&+            (((*p == 'e' || *p == 'E') && radix == 10) ||+             ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {+            const char *p1 = p + 1;+            is_float = TRUE;+            if (*p1 == '+') {+                p1++;+            } else if (*p1 == '-') {+                p1++;+            }+            if (is_digit((uint8_t)*p1)) {+                p = p1 + 1;+                while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))+                    p++;+            }+        }+    }+    if (p == p_start)+        goto fail;++    buf = buf1;+    buf_allocated = FALSE;+    len = p - p_start;+    if (unlikely((len + 2) > sizeof(buf1))) {+        buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */+        if (!buf)+            goto mem_error;+        buf_allocated = TRUE;+    }+    /* remove the separators and the radix prefixes */+    j = 0;+    if (is_neg)+        buf[j++] = '-';+    for (i = 0; i < len; i++) {+        if (p_start[i] != '_')+            buf[j++] = p_start[i];+    }+    buf[j] = '\0';++#ifdef CONFIG_BIGNUM+    if (flags & ATOD_ACCEPT_SUFFIX) {+        if (*p == 'n') {+            p++;+            atod_type = ATOD_TYPE_BIG_INT;+        } else if (*p == 'l') {+            p++;+            atod_type = ATOD_TYPE_BIG_FLOAT;+        } else if (*p == 'm') {+            p++;+            atod_type = ATOD_TYPE_BIG_DECIMAL;+        } else {+            if (flags & ATOD_MODE_BIGINT) {+                if (!is_float)+                    atod_type = ATOD_TYPE_BIG_INT;+                if (has_legacy_octal)+                    goto fail;+            } else {+                if (is_float && radix != 10)+                    goto fail;+            }+        }+    } else {+        if (atod_type == ATOD_TYPE_FLOAT64) {+            if (flags & ATOD_MODE_BIGINT) {+                if (!is_float)+                    atod_type = ATOD_TYPE_BIG_INT;+                if (has_legacy_octal)+                    goto fail;+            } else {+                if (is_float && radix != 10)+                    goto fail;+            }+        }+    }++    switch(atod_type) {+    case ATOD_TYPE_FLOAT64:+        {+            double d;+            d = js_strtod(buf, radix, is_float);+            /* return int or float64 */+            val = JS_NewFloat64(ctx, d);+        }+        break;+    case ATOD_TYPE_BIG_INT:+        if (has_legacy_octal || is_float)+            goto fail;+        val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);+        break;+    case ATOD_TYPE_BIG_FLOAT:+        if (has_legacy_octal)+            goto fail;+        val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,+                                                pexponent);+        break;+    case ATOD_TYPE_BIG_DECIMAL:+        if (radix != 10)+            goto fail;+        val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);+        break;+    default:+        abort();+    }+#else+    {+        double d;+        (void)has_legacy_octal;+        if (is_float && radix != 10)+            goto fail;+        d = js_strtod(buf, radix, is_float);+        val = JS_NewFloat64(ctx, d);+    }+#endif+    +done:+    if (buf_allocated)+        js_free_rt(ctx->rt, buf);+    if (pp)+        *pp = p;+    return val;+ fail:+    val = JS_NAN;+    goto done;+ mem_error:+    val = JS_ThrowOutOfMemory(ctx);+    goto done;+}++#ifdef CONFIG_BIGNUM+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,+                       int radix, int flags)+{+    return js_atof2(ctx, str, pp, radix, flags, NULL);+}+#endif++typedef enum JSToNumberHintEnum {+    TON_FLAG_NUMBER,+    TON_FLAG_NUMERIC,+} JSToNumberHintEnum;++static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,+                                   JSToNumberHintEnum flag)+{+    uint32_t tag;+    JSValue ret;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_DECIMAL:+        if (flag != TON_FLAG_NUMERIC) {+            JS_FreeValue(ctx, val);+            return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");+        }+        ret = val;+        break;+    case JS_TAG_BIG_INT:+        if (flag != TON_FLAG_NUMERIC) {+            JS_FreeValue(ctx, val);+            return JS_ThrowTypeError(ctx, "cannot convert bigint to number");+        }+        ret = val;+        break;+    case JS_TAG_BIG_FLOAT:+        if (flag != TON_FLAG_NUMERIC) {+            JS_FreeValue(ctx, val);+            return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");+        }+        ret = val;+        break;+#endif+    case JS_TAG_FLOAT64:+    case JS_TAG_INT:+    case JS_TAG_EXCEPTION:+        ret = val;+        break;+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+        ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));+        break;+    case JS_TAG_UNDEFINED:+        ret = JS_NAN;+        break;+    case JS_TAG_OBJECT:+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+        if (JS_IsException(val))+            return JS_EXCEPTION;+        goto redo;+    case JS_TAG_STRING:+        {+            const char *str;+            const char *p;+            size_t len;+            +            str = JS_ToCStringLen(ctx, &len, val);+            JS_FreeValue(ctx, val);+            if (!str)+                return JS_EXCEPTION;+            p = str;+            p += skip_spaces(p);+            if ((p - str) == len) {+                ret = JS_NewInt32(ctx, 0);+            } else {+                int flags = ATOD_ACCEPT_BIN_OCT;+                ret = js_atof(ctx, p, &p, 0, flags);+                if (!JS_IsException(ret)) {+                    p += skip_spaces(p);+                    if ((p - str) != len) {+                        JS_FreeValue(ctx, ret);+                        ret = JS_NAN;+                    }+                }+            }+            JS_FreeCString(ctx, str);+        }+        break;+    case JS_TAG_SYMBOL:+        JS_FreeValue(ctx, val);+        return JS_ThrowTypeError(ctx, "cannot convert symbol to number");+    default:+        JS_FreeValue(ctx, val);+        ret = JS_NAN;+        break;+    }+    return ret;+}++static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)+{+    return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);+}++static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)+{+    return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);+}++static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)+{+    return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));+}++static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,+                                          JSValue val)+{+    double d;+    uint32_t tag;++    val = JS_ToNumberFree(ctx, val);+    if (JS_IsException(val)) {+        *pres = JS_FLOAT64_NAN;+        return -1;+    }+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+        d = JS_VALUE_GET_INT(val);+        break;+    case JS_TAG_FLOAT64:+        d = JS_VALUE_GET_FLOAT64(val);+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            /* XXX: there can be a double rounding issue with some+               primitives (such as JS_ToUint8ClampFree()), but it is+               not critical to fix it. */+            bf_get_float64(&p->num, &d, BF_RNDN);+            JS_FreeValue(ctx, val);+        }+        break;+#endif+    default:+        abort();+    }+    *pres = d;+    return 0;+}++static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)+{+    uint32_t tag;++    tag = JS_VALUE_GET_TAG(val);+    if (tag <= JS_TAG_NULL) {+        *pres = JS_VALUE_GET_INT(val);+        return 0;+    } else if (JS_TAG_IS_FLOAT64(tag)) {+        *pres = JS_VALUE_GET_FLOAT64(val);+        return 0;+    } else {+        return __JS_ToFloat64Free(ctx, pres, val);+    }+}++int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)+{+    return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));+}++static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)+{+    return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));+}++/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */+static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)+{+    uint32_t tag;+    JSValue ret;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));+        break;+    case JS_TAG_FLOAT64:+        {+            double d = JS_VALUE_GET_FLOAT64(val);+            if (isnan(d)) {+                ret = JS_NewInt32(ctx, 0);+            } else {+                /* convert -0 to +0 */+                d = trunc(d) + 0.0;+                ret = JS_NewFloat64(ctx, d);+            }+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+        {+            bf_t a_s, *a, r_s, *r = &r_s;+            BOOL is_nan;++            a = JS_ToBigFloat(ctx, &a_s, val);+            if (!bf_is_finite(a)) {+                is_nan = bf_is_nan(a);+                if (is_nan)+                    ret = JS_NewInt32(ctx, 0);+                else+                    ret = JS_DupValue(ctx, val);+            } else {+                ret = JS_NewBigInt(ctx);+                if (!JS_IsException(ret)) {+                    r = JS_GetBigInt(ret);+                    bf_set(r, a);+                    bf_rint(r, BF_RNDZ);+                    ret = JS_CompactBigInt(ctx, ret);+                }+            }+            if (a == &a_s)+                bf_delete(a);+            JS_FreeValue(ctx, val);+        }+        break;+#endif+    default:+        val = JS_ToNumberFree(ctx, val);+        if (JS_IsException(val))+            return val;+        goto redo;+    }+    return ret;+}++/* Note: the integer value is satured to 32 bits */+static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)+{+    uint32_t tag;+    int ret;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        ret = JS_VALUE_GET_INT(val);+        break;+    case JS_TAG_EXCEPTION:+        *pres = 0;+        return -1;+    case JS_TAG_FLOAT64:+        {+            double d = JS_VALUE_GET_FLOAT64(val);+            if (isnan(d)) {+                ret = 0;+            } else {+                if (d < INT32_MIN)+                    ret = INT32_MIN;+                else if (d > INT32_MAX)+                    ret = INT32_MAX;+                else+                    ret = (int)d;+            }+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            bf_get_int32(&ret, &p->num, 0);+            JS_FreeValue(ctx, val);+        }+        break;+#endif+    default:+        val = JS_ToNumberFree(ctx, val);+        if (JS_IsException(val)) {+            *pres = 0;+            return -1;+        }+        goto redo;+    }+    *pres = ret;+    return 0;+}++int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)+{+    return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));+}++int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,+                    int min, int max, int min_offset)+{+    int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));+    if (res == 0) {+        if (*pres < min) {+            *pres += min_offset;+            if (*pres < min)+                *pres = min;+        } else {+            if (*pres > max)+                *pres = max;+        }+    }+    return res;+}++static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)+{+    uint32_t tag;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        *pres = JS_VALUE_GET_INT(val);+        return 0;+    case JS_TAG_EXCEPTION:+        *pres = 0;+        return -1;+    case JS_TAG_FLOAT64:+        {+            double d = JS_VALUE_GET_FLOAT64(val);+            if (isnan(d)) {+                *pres = 0;+            } else {+                if (d < INT64_MIN)+                    *pres = INT64_MIN;+                else if (d > INT64_MAX)+                    *pres = INT64_MAX;+                else+                    *pres = (int64_t)d;+            }+        }+        return 0;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            bf_get_int64(pres, &p->num, 0);+            JS_FreeValue(ctx, val);+        }+        return 0;+#endif+    default:+        val = JS_ToNumberFree(ctx, val);+        if (JS_IsException(val)) {+            *pres = 0;+            return -1;+        }+        goto redo;+    }+}++int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)+{+    return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));+}++int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,+                    int64_t min, int64_t max, int64_t neg_offset)+{+    int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));+    if (res == 0) {+        if (*pres < 0)+            *pres += neg_offset;+        if (*pres < min)+            *pres = min;+        else if (*pres > max)+            *pres = max;+    }+    return res;+}++/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)+   in case of exception */+static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)+{+    uint32_t tag;+    int64_t ret;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        ret = JS_VALUE_GET_INT(val);+        break;+    case JS_TAG_FLOAT64:+        {+            JSFloat64Union u;+            double d;+            int e;+            d = JS_VALUE_GET_FLOAT64(val);+            u.d = d;+            /* we avoid doing fmod(x, 2^64) */+            e = (u.u64 >> 52) & 0x7ff;+            if (likely(e <= (1023 + 62))) {+                /* fast case */+                ret = (int64_t)d;+            } else if (e <= (1023 + 62 + 53)) {+                uint64_t v;+                /* remainder modulo 2^64 */+                v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);+                ret = v << ((e - 1023) - 52);+                /* take the sign into account */+                if (u.u64 >> 63)+                    ret = -ret;+            } else {+                ret = 0; /* also handles NaN and +inf */+            }+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);+            JS_FreeValue(ctx, val);+        }+        break;+#endif+    default:+        val = JS_ToNumberFree(ctx, val);+        if (JS_IsException(val)) {+            *pres = 0;+            return -1;+        }+        goto redo;+    }+    *pres = ret;+    return 0;+}++int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)+{+    return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));+}++int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)+{+    if (JS_IsBigInt(ctx, val))+        return JS_ToBigInt64(ctx, pres, val);+    else+        return JS_ToInt64(ctx, pres, val);+}++/* return (<0, 0) in case of exception */+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)+{+    uint32_t tag;+    int32_t ret;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        ret = JS_VALUE_GET_INT(val);+        break;+    case JS_TAG_FLOAT64:+        {+            JSFloat64Union u;+            double d;+            int e;+            d = JS_VALUE_GET_FLOAT64(val);+            u.d = d;+            /* we avoid doing fmod(x, 2^32) */+            e = (u.u64 >> 52) & 0x7ff;+            if (likely(e <= (1023 + 30))) {+                /* fast case */+                ret = (int32_t)d;+            } else if (e <= (1023 + 30 + 53)) {+                uint64_t v;+                /* remainder modulo 2^32 */+                v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);+                v = v << ((e - 1023) - 52 + 32);+                ret = v >> 32;+                /* take the sign into account */+                if (u.u64 >> 63)+                    ret = -ret;+            } else {+                ret = 0; /* also handles NaN and +inf */+            }+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);+            JS_FreeValue(ctx, val);+        }+        break;+#endif+    default:+        val = JS_ToNumberFree(ctx, val);+        if (JS_IsException(val)) {+            *pres = 0;+            return -1;+        }+        goto redo;+    }+    *pres = ret;+    return 0;+}++int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)+{+    return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));+}++static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)+{+    return JS_ToInt32Free(ctx, (int32_t *)pres, val);+}++static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)+{+    uint32_t tag;+    int res;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        res = JS_VALUE_GET_INT(val);+#ifdef CONFIG_BIGNUM+    int_clamp:+#endif+        res = max_int(0, min_int(255, res));+        break;+    case JS_TAG_FLOAT64:+        {+            double d = JS_VALUE_GET_FLOAT64(val);+            if (isnan(d)) {+                res = 0;+            } else {+                if (d < 0)+                    res = 0;+                else if (d > 255)+                    res = 255;+                else+                    res = lrint(d);+            }+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            bf_t r_s, *r = &r_s;+            bf_init(ctx->bf_ctx, r);+            bf_set(r, &p->num);+            bf_rint(r, BF_RNDN);+            bf_get_int32(&res, r, 0);+            bf_delete(r);+            JS_FreeValue(ctx, val);+        }+        goto int_clamp;+#endif+    default:+        val = JS_ToNumberFree(ctx, val);+        if (JS_IsException(val)) {+            *pres = 0;+            return -1;+        }+        goto redo;+    }+    *pres = res;+    return 0;+}++static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,+                                            JSValue val)+{+    uint32_t tag, len;++ redo:+    tag = JS_VALUE_GET_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+        {+            int v;+            v = JS_VALUE_GET_INT(val);+            if (v < 0)+                goto fail;+            len = v;+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            bf_t a;+            BOOL res;+            bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);+            bf_init(ctx->bf_ctx, &a);+            bf_set_ui(&a, len);+            res = bf_cmp_eq(&a, &p->num);+            bf_delete(&a);+            JS_FreeValue(ctx, val);+            if (!res)+                goto fail;+        }+        break;+#endif+    default:+        if (JS_TAG_IS_FLOAT64(tag)) {+            double d;+            d = JS_VALUE_GET_FLOAT64(val);+            len = (uint32_t)d;+            if (len != d) {+            fail:+                JS_ThrowRangeError(ctx, "invalid array length");+                return -1;+            }+        } else {+            val = JS_ToNumberFree(ctx, val);+            if (JS_IsException(val))+                return -1;+            goto redo;+        }+        break;+    }+    *plen = len;+    return 0;+}++#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)++static BOOL is_safe_integer(double d)+{+    return isfinite(d) && floor(d) == d &&+        fabs(d) <= (double)MAX_SAFE_INTEGER;+}++int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)+{+    int64_t v;+    if (JS_ToInt64Sat(ctx, &v, val))+        return -1;+    if (v < 0 || v > MAX_SAFE_INTEGER) {+        JS_ThrowRangeError(ctx, "invalid array index");+        *plen = 0;+        return -1;+    }+    *plen = v;+    return 0;+}++/* convert a value to a length between 0 and MAX_SAFE_INTEGER.+   return -1 for exception */+static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,+                                       JSValue val)+{+    int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);+    JS_FreeValue(ctx, val);+    return res;+}++/* Note: can return an exception */+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)+{+    double d;+    if (!JS_IsNumber(val))+        return FALSE;+    if (unlikely(JS_ToFloat64(ctx, &d, val)))+        return -1;+    return isfinite(d) && floor(d) == d;+}++static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)+{+    uint32_t tag;++    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+        {+            int v;+            v = JS_VALUE_GET_INT(val);+            return (v < 0);+        }+    case JS_TAG_FLOAT64:+        {+            JSFloat64Union u;+            u.d = JS_VALUE_GET_FLOAT64(val);+            return (u.u64 >> 63);+        }+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            /* Note: integer zeros are not necessarily positive */+            return p->num.sign && !bf_is_zero(&p->num);+        }+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            return p->num.sign;+        }+        break;+    case JS_TAG_BIG_DECIMAL:+        {+            JSBigDecimal *p = JS_VALUE_GET_PTR(val);+            return p->num.sign;+        }+        break;+#endif+    default:+        return FALSE;+    }+}++#ifdef CONFIG_BIGNUM++static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)+{+    JSValue ret;+    bf_t a_s, *a;+    char *str;+    int saved_sign;++    a = JS_ToBigInt(ctx, &a_s, val);+    if (!a)+        return JS_EXCEPTION;+    saved_sign = a->sign;+    if (a->expn == BF_EXP_ZERO)+        a->sign = 0;+    str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |+                  BF_FTOA_JS_QUIRKS);+    a->sign = saved_sign;+    JS_FreeBigInt(ctx, a, &a_s);+    if (!str)+        return JS_ThrowOutOfMemory(ctx);+    ret = JS_NewString(ctx, str);+    bf_free(ctx->bf_ctx, str);+    return ret;+}++static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)+{+    return js_bigint_to_string1(ctx, val, 10);+}++static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,+                       limb_t prec, bf_flags_t flags)+{+    JSValue val, ret;+    bf_t a_s, *a;+    char *str;+    int saved_sign;++    val = JS_ToNumeric(ctx, val1);+    if (JS_IsException(val))+        return val;+    a = JS_ToBigFloat(ctx, &a_s, val);+    saved_sign = a->sign;+    if (a->expn == BF_EXP_ZERO)+        a->sign = 0;+    flags |= BF_FTOA_JS_QUIRKS;+    if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {+        /* Note: for floating point numbers with a radix which is not+           a power of two, the current precision is used to compute+           the number of digits. */+        if ((radix & (radix - 1)) != 0) {+            bf_t r_s, *r = &r_s;+            int prec, flags1;+            /* must round first */+            if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {+                prec = ctx->fp_env.prec;+                flags1 = ctx->fp_env.flags &+                    (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));+            } else {+                prec = 53;+                flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;+            }+            bf_init(ctx->bf_ctx, r);+            bf_set(r, a);+            bf_round(r, prec, flags1 | BF_RNDN);+            str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);+            bf_delete(r);+        } else {+            str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);+        }+    } else {+        str = bf_ftoa(NULL, a, radix, prec, flags);+    }+    a->sign = saved_sign;+    if (a == &a_s)+        bf_delete(a);+    JS_FreeValue(ctx, val);+    if (!str)+        return JS_ThrowOutOfMemory(ctx);+    ret = JS_NewString(ctx, str);+    bf_free(ctx->bf_ctx, str);+    return ret;+}++static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)+{+    return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);+}++static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,+                                        limb_t prec, int flags)+{+    JSValue ret;+    bfdec_t *a;+    char *str;+    int saved_sign;++    a = JS_ToBigDecimal(ctx, val);+    saved_sign = a->sign;+    if (a->expn == BF_EXP_ZERO)+        a->sign = 0;+    str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);+    a->sign = saved_sign;+    if (!str)+        return JS_ThrowOutOfMemory(ctx);+    ret = JS_NewString(ctx, str);+    bf_free(ctx->bf_ctx, str);+    return ret;+}++static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)+{+    return js_bigdecimal_to_string1(ctx, val, 0,+                                    BF_RNDZ | BF_FTOA_FORMAT_FREE);+}++#endif /* CONFIG_BIGNUM */++/* 2 <= base <= 36 */+static char *i64toa(char *buf_end, int64_t n, unsigned int base)+{+    char *q = buf_end;+    int digit, is_neg;++    is_neg = 0;+    if (n < 0) {+        is_neg = 1;+        n = -n;+    }+    *--q = '\0';+    do {+        digit = (uint64_t)n % base;+        n = (uint64_t)n / base;+        if (digit < 10)+            digit += '0';+        else+            digit += 'a' - 10;+        *--q = digit;+    } while (n != 0);+    if (is_neg)+        *--q = '-';+    return q;+}++/* buf1 contains the printf result */+static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,+                     int rounding_mode, char *buf1, int buf1_size)+{+    if (rounding_mode != FE_TONEAREST)+        fesetround(rounding_mode);+    snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);+    if (rounding_mode != FE_TONEAREST)+        fesetround(FE_TONEAREST);+    *sign = (buf1[0] == '-');+    /* mantissa */+    buf[0] = buf1[1];+    if (n_digits > 1)+        memcpy(buf + 1, buf1 + 3, n_digits - 1);+    buf[n_digits] = '\0';+    /* exponent */+    *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;+}++/* maximum buffer size for js_dtoa */+#define JS_DTOA_BUF_SIZE 128++/* needed because ecvt usually limits the number of digits to+   17. Return the number of digits. */+static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,+                   BOOL is_fixed)+{+    int rounding_mode;+    char buf_tmp[JS_DTOA_BUF_SIZE];++    if (!is_fixed) {+        unsigned int n_digits_min, n_digits_max;+        /* find the minimum amount of digits (XXX: inefficient but simple) */+        n_digits_min = 1;+        n_digits_max = 17;+        while (n_digits_min < n_digits_max) {+            n_digits = (n_digits_min + n_digits_max) / 2;+            js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,+                     buf_tmp, sizeof(buf_tmp));+            if (strtod(buf_tmp, NULL) == d) {+                /* no need to keep the trailing zeros */+                while (n_digits >= 2 && buf[n_digits - 1] == '0')+                    n_digits--;+                n_digits_max = n_digits;+            } else {+                n_digits_min = n_digits + 1;+            }+        }+        n_digits = n_digits_max;+        rounding_mode = FE_TONEAREST;+    } else {+        rounding_mode = FE_TONEAREST;+#ifdef CONFIG_PRINTF_RNDN+        {+            char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];+            int decpt1, sign1, decpt2, sign2;+            /* The JS rounding is specified as round to nearest ties away+               from zero (RNDNA), but in printf the "ties" case is not+               specified (for example it is RNDN for glibc, RNDNA for+               Windows), so we must round manually. */+            js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,+                     buf_tmp, sizeof(buf_tmp));+            /* XXX: could use 2 digits to reduce the average running time */+            if (buf1[n_digits] == '5') {+                js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,+                         buf_tmp, sizeof(buf_tmp));+                js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,+                         buf_tmp, sizeof(buf_tmp));+                if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {+                    /* exact result: round away from zero */+                    if (sign1)+                        rounding_mode = FE_DOWNWARD;+                    else+                        rounding_mode = FE_UPWARD;+                }+            }+        }+#endif /* CONFIG_PRINTF_RNDN */+    }+    js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,+             buf_tmp, sizeof(buf_tmp));+    return n_digits;+}++static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,+                    int rounding_mode)+{+    int n;+    if (rounding_mode != FE_TONEAREST)+        fesetround(rounding_mode);+    n = snprintf(buf, buf_size, "%.*f", n_digits, d);+    if (rounding_mode != FE_TONEAREST)+        fesetround(FE_TONEAREST);+    assert(n < buf_size);+    return n;+}++static void js_fcvt(char *buf, int buf_size, double d, int n_digits)+{+    int rounding_mode;+    rounding_mode = FE_TONEAREST;+#ifdef CONFIG_PRINTF_RNDN+    {+        int n1, n2;+        char buf1[JS_DTOA_BUF_SIZE];+        char buf2[JS_DTOA_BUF_SIZE];++        /* The JS rounding is specified as round to nearest ties away from+           zero (RNDNA), but in printf the "ties" case is not specified+           (for example it is RNDN for glibc, RNDNA for Windows), so we+           must round manually. */+        n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);+        rounding_mode = FE_TONEAREST;+        /* XXX: could use 2 digits to reduce the average running time */+        if (buf1[n1 - 1] == '5') {+            n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);+            n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);+            if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {+                /* exact result: round away from zero */+                if (buf1[0] == '-')+                    rounding_mode = FE_DOWNWARD;+                else+                    rounding_mode = FE_UPWARD;+            }+        }+    }+#endif /* CONFIG_PRINTF_RNDN */+    js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);+}++/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */+/* use as many digits as necessary */+#define JS_DTOA_VAR_FORMAT   (0 << 0)+/* use n_digits significant digits (1 <= n_digits <= 101) */+#define JS_DTOA_FIXED_FORMAT (1 << 0)+/* force fractional format: [-]dd.dd with n_digits fractional digits */+#define JS_DTOA_FRAC_FORMAT  (2 << 0)+/* force exponential notation either in fixed or variable format */+#define JS_DTOA_FORCE_EXP    (1 << 2)++/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.+   XXX: radix != 10 is only supported for small integers+*/+static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)+{+    char *q;++    if (!isfinite(d)) {+        if (isnan(d)) {+            strcpy(buf, "NaN");+        } else {+            q = buf;+            if (d < 0)+                *q++ = '-';+            strcpy(q, "Infinity");+        }+    } else if (flags == JS_DTOA_VAR_FORMAT) {+        int64_t i64;+        char buf1[70], *ptr;+        i64 = (int64_t)d;+        if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)+            goto generic_conv;+        /* fast path for integers */+        ptr = i64toa(buf1 + sizeof(buf1), i64, radix);+        strcpy(buf, ptr);+    } else {+        if (d == 0.0)+            d = 0.0; /* convert -0 to 0 */+        if (flags == JS_DTOA_FRAC_FORMAT) {+            js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);+        } else {+            char buf1[JS_DTOA_BUF_SIZE];+            int sign, decpt, k, n, i, p, n_max;+            BOOL is_fixed;+        generic_conv:+            is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);+            if (is_fixed) {+                n_max = n_digits;+            } else {+                n_max = 21;+            }+            /* the number has k digits (k >= 1) */+            k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);+            n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */+            q = buf;+            if (sign)+                *q++ = '-';+            if (flags & JS_DTOA_FORCE_EXP)+                goto force_exp;+            if (n >= 1 && n <= n_max) {+                if (k <= n) {+                    memcpy(q, buf1, k);+                    q += k;+                    for(i = 0; i < (n - k); i++)+                        *q++ = '0';+                    *q = '\0';+                } else {+                    /* k > n */+                    memcpy(q, buf1, n);+                    q += n;+                    *q++ = '.';+                    for(i = 0; i < (k - n); i++)+                        *q++ = buf1[n + i];+                    *q = '\0';+                }+            } else if (n >= -5 && n <= 0) {+                *q++ = '0';+                *q++ = '.';+                for(i = 0; i < -n; i++)+                    *q++ = '0';+                memcpy(q, buf1, k);+                q += k;+                *q = '\0';+            } else {+            force_exp:+                /* exponential notation */+                *q++ = buf1[0];+                if (k > 1) {+                    *q++ = '.';+                    for(i = 1; i < k; i++)+                        *q++ = buf1[i];+                }+                *q++ = 'e';+                p = n - 1;+                if (p >= 0)+                    *q++ = '+';+                sprintf(q, "%d", p);+            }+        }+    }+}++static JSValue js_dtoa(JSContext *ctx,+                       double d, int radix, int n_digits, int flags)+{+    char buf[JS_DTOA_BUF_SIZE];+    js_dtoa1(buf, d, radix, n_digits, flags);+    return JS_NewString(ctx, buf);+}++JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)+{+    uint32_t tag;+    const char *str;+    char buf[32];++    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_STRING:+        return JS_DupValue(ctx, val);+    case JS_TAG_INT:+        snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));+        str = buf;+        goto new_string;+    case JS_TAG_BOOL:+        return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?+                          JS_ATOM_true : JS_ATOM_false);+    case JS_TAG_NULL:+        return JS_AtomToString(ctx, JS_ATOM_null);+    case JS_TAG_UNDEFINED:+        return JS_AtomToString(ctx, JS_ATOM_undefined);+    case JS_TAG_EXCEPTION:+        return JS_EXCEPTION;+    case JS_TAG_OBJECT:+        {+            JSValue val1, ret;+            val1 = JS_ToPrimitive(ctx, val, HINT_STRING);+            if (JS_IsException(val1))+                return val1;+            ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);+            JS_FreeValue(ctx, val1);+            return ret;+        }+        break;+    case JS_TAG_FUNCTION_BYTECODE:+        str = "[function bytecode]";+        goto new_string;+    case JS_TAG_SYMBOL:+        if (is_ToPropertyKey) {+            return JS_DupValue(ctx, val);+        } else {+            return JS_ThrowTypeError(ctx, "cannot convert symbol to string");+        }+    case JS_TAG_FLOAT64:+        return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,+                       JS_DTOA_VAR_FORMAT);+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        return ctx->rt->bigint_ops.to_string(ctx, val);+    case JS_TAG_BIG_FLOAT:+        return ctx->rt->bigfloat_ops.to_string(ctx, val);+    case JS_TAG_BIG_DECIMAL:+        return ctx->rt->bigdecimal_ops.to_string(ctx, val);+#endif+    default:+        str = "[unsupported type]";+    new_string:+        return JS_NewString(ctx, str);+    }+}++JSValue JS_ToString(JSContext *ctx, JSValueConst val)+{+    return JS_ToStringInternal(ctx, val, FALSE);+}++static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)+{+    JSValue ret;+    ret = JS_ToString(ctx, val);+    JS_FreeValue(ctx, val);+    return ret;+}++static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)+{+    if (JS_IsUndefined(val) || JS_IsNull(val))+        return JS_ToStringFree(ctx, val);+    return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);+}++JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)+{+    return JS_ToStringInternal(ctx, val, TRUE);+}++static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)+{+    uint32_t tag = JS_VALUE_GET_TAG(val);+    if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)+        return JS_ThrowTypeError(ctx, "null or undefined are forbidden");+    return JS_ToString(ctx, val);+}++static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)+{+    JSValue val;+    JSString *p;+    int i;+    uint32_t c;+    StringBuffer b_s, *b = &b_s;+    char buf[16];++    val = JS_ToStringCheckObject(ctx, val1);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_STRING(val);++    if (string_buffer_init(ctx, b, p->len + 2))+        goto fail;++    if (string_buffer_putc8(b, '\"'))+        goto fail;+    for(i = 0; i < p->len; ) {+        c = string_getc(p, &i);+        switch(c) {+        case '\t':+            c = 't';+            goto quote;+        case '\r':+            c = 'r';+            goto quote;+        case '\n':+            c = 'n';+            goto quote;+        case '\b':+            c = 'b';+            goto quote;+        case '\f':+            c = 'f';+            goto quote;+        case '\"':+        case '\\':+        quote:+            if (string_buffer_putc8(b, '\\'))+                goto fail;+            if (string_buffer_putc8(b, c))+                goto fail;+            break;+        default:+            if (c < 32 || (c >= 0xd800 && c < 0xe000)) {+                snprintf(buf, sizeof(buf), "\\u%04x", c);+                if (string_buffer_puts8(b, buf))+                    goto fail;+            } else {+                if (string_buffer_putc(b, c))+                    goto fail;+            }+            break;+        }+    }+    if (string_buffer_putc8(b, '\"'))+        goto fail;+    JS_FreeValue(ctx, val);+    return string_buffer_end(b);+ fail:+    JS_FreeValue(ctx, val);+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)+{+    printf("%14s %4s %4s %14s %10s %s\n",+           "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");+}++/* for debug only: dump an object without side effect */+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)+{+    uint32_t i;+    char atom_buf[ATOM_GET_STR_BUF_SIZE];+    JSShape *sh;+    JSShapeProperty *prs;+    JSProperty *pr;+    BOOL is_first = TRUE;++    /* XXX: should encode atoms with special characters */+    sh = p->shape; /* the shape can be NULL while freeing an object */+    printf("%14p %4d ",+           (void *)p,+           p->header.ref_count);+    if (sh) {+        printf("%3d%c %14p ",+               sh->header.ref_count,+               " *"[sh->is_hashed],+               (void *)sh->proto);+    } else {+        printf("%3s  %14s ", "-", "-");+    }+    printf("%10s ",+           JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));+    if (p->is_exotic && p->fast_array) {+        printf("[ ");+        for(i = 0; i < p->u.array.count; i++) {+            if (i != 0)+                printf(", ");+            switch (p->class_id) {+            case JS_CLASS_ARRAY:+            case JS_CLASS_ARGUMENTS:+                JS_DumpValueShort(rt, p->u.array.u.values[i]);+                break;+            case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY:+                {+                    int size = 1 << typed_array_size_log2(p->class_id);+                    const uint8_t *b = p->u.array.u.uint8_ptr + i * size;+                    while (size-- > 0)+                        printf("%02X", *b++);+                }+                break;+            }+        }+        printf(" ] ");+    }++    if (sh) {+        printf("{ ");+        for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+            if (prs->atom != JS_ATOM_NULL) {+                pr = &p->prop[i];+                if (!is_first)+                    printf(", ");+                printf("%s: ",+                       JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));+                if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+                    printf("[getset %p %p]", (void *)pr->u.getset.getter,+                           (void *)pr->u.getset.setter);+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+                    printf("[varref %p]", (void *)pr->u.var_ref);+                } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+                    printf("[autoinit %p %d %p]",+                           (void *)js_autoinit_get_realm(pr),+                           js_autoinit_get_id(pr),+                           (void *)pr->u.init.opaque);+                } else {+                    JS_DumpValueShort(rt, pr->u.value);+                }+                is_first = FALSE;+            }+        }+        printf(" }");+    }+    +    if (js_class_has_bytecode(p->class_id)) {+        JSFunctionBytecode *b = p->u.func.function_bytecode;+        JSVarRef **var_refs;+        if (b->closure_var_count) {+            var_refs = p->u.func.var_refs;+            printf(" Closure:");+            for(i = 0; i < b->closure_var_count; i++) {+                printf(" ");+                JS_DumpValueShort(rt, var_refs[i]->value);+            }+            if (p->u.func.home_object) {+                printf(" HomeObject: ");+                JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));+            }+        }+    }+    printf("\n");+}++static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)+{+    if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {+        JS_DumpObject(rt, (JSObject *)p);+    } else {+        printf("%14p %4d ",+               (void *)p,+               p->ref_count);+        switch(p->gc_obj_type) {+        case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+            printf("[function bytecode]");+            break;+        case JS_GC_OBJ_TYPE_SHAPE:+            printf("[shape]");+            break;+        case JS_GC_OBJ_TYPE_VAR_REF:+            printf("[var_ref]");+            break;+        case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:+            printf("[async_function]");+            break;+        case JS_GC_OBJ_TYPE_JS_CONTEXT:+            printf("[js_context]");+            break;+        default:+            printf("[unknown %d]", p->gc_obj_type);+            break;+        }+        printf("\n");+    }+}++static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,+                                                      JSValueConst val)+{+    uint32_t tag = JS_VALUE_GET_NORM_TAG(val);+    const char *str;++    switch(tag) {+    case JS_TAG_INT:+        printf("%d", JS_VALUE_GET_INT(val));+        break;+    case JS_TAG_BOOL:+        if (JS_VALUE_GET_BOOL(val))+            str = "true";+        else+            str = "false";+        goto print_str;+    case JS_TAG_NULL:+        str = "null";+        goto print_str;+    case JS_TAG_EXCEPTION:+        str = "exception";+        goto print_str;+    case JS_TAG_UNINITIALIZED:+        str = "uninitialized";+        goto print_str;+    case JS_TAG_UNDEFINED:+        str = "undefined";+    print_str:+        printf("%s", str);+        break;+    case JS_TAG_FLOAT64:+        printf("%.14g", JS_VALUE_GET_FLOAT64(val));+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            char *str;+            str = bf_ftoa(NULL, &p->num, 10, 0,+                          BF_RNDZ | BF_FTOA_FORMAT_FRAC);+            printf("%sn", str);+            bf_realloc(&rt->bf_ctx, str, 0);+        }+        break;+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            char *str;+            str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,+                          BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);+            printf("%sl", str);+            bf_free(&rt->bf_ctx, str);+        }+        break;+    case JS_TAG_BIG_DECIMAL:+        {+            JSBigDecimal *p = JS_VALUE_GET_PTR(val);+            char *str;+            str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,+                             BF_RNDZ | BF_FTOA_FORMAT_FREE);+            printf("%sm", str);+            bf_free(&rt->bf_ctx, str);+        }+        break;+#endif+    case JS_TAG_STRING:+        {+            JSString *p;+            p = JS_VALUE_GET_STRING(val);+            JS_DumpString(rt, p);+        }+        break;+    case JS_TAG_FUNCTION_BYTECODE:+        {+            JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);+            char buf[ATOM_GET_STR_BUF_SIZE];+            printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));+        }+        break;+    case JS_TAG_OBJECT:+        {+            JSObject *p = JS_VALUE_GET_OBJ(val);+            JSAtom atom = rt->class_array[p->class_id].class_name;+            char atom_buf[ATOM_GET_STR_BUF_SIZE];+            printf("[%s %p]",+                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);+        }+        break;+    case JS_TAG_SYMBOL:+        {+            JSAtomStruct *p = JS_VALUE_GET_PTR(val);+            char atom_buf[ATOM_GET_STR_BUF_SIZE];+            printf("Symbol(%s)",+                   JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));+        }+        break;+    case JS_TAG_MODULE:+        printf("[module]");+        break;+    default:+        printf("[unknown tag %d]", tag);+        break;+    }+}++static __maybe_unused void JS_DumpValue(JSContext *ctx,+                                                 JSValueConst val)+{+    JS_DumpValueShort(ctx->rt, val);+}++static __maybe_unused void JS_PrintValue(JSContext *ctx,+                                                  const char *str,+                                                  JSValueConst val)+{+    printf("%s=", str);+    JS_DumpValueShort(ctx->rt, val);+    printf("\n");+}++/* return -1 if exception (proxy case) or TRUE/FALSE */+int JS_IsArray(JSContext *ctx, JSValueConst val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {+        p = JS_VALUE_GET_OBJ(val);+        if (unlikely(p->class_id == JS_CLASS_PROXY))+            return js_proxy_isArray(ctx, val);+        else+            return p->class_id == JS_CLASS_ARRAY;+    } else {+        return FALSE;+    }+}++static double js_pow(double a, double b)+{+    if (unlikely(!isfinite(b)) && fabs(a) == 1) {+        /* not compatible with IEEE 754 */+        return JS_FLOAT64_NAN;+    } else {+        return pow(a, b);+    }+}++#ifdef CONFIG_BIGNUM++JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)+{+    JSValue val;+    bf_t *a;+    val = JS_NewBigInt(ctx);+    if (JS_IsException(val))+        return val;+    a = JS_GetBigInt(val);+    if (bf_set_si(a, v)) {+        JS_FreeValue(ctx, val);+        return JS_ThrowOutOfMemory(ctx);+    }+    return val;+}++JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)+{+    if (is_math_mode(ctx) &&+        v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {+        return JS_NewInt64(ctx, v);+    } else {+        return JS_NewBigInt64_1(ctx, v);+    }+}++JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)+{+    JSValue val;+    if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {+        val = JS_NewInt64(ctx, v);+    } else {+        bf_t *a;+        val = JS_NewBigInt(ctx);+        if (JS_IsException(val))+            return val;+        a = JS_GetBigInt(val);+        if (bf_set_ui(a, v)) {+            JS_FreeValue(ctx, val);+            return JS_ThrowOutOfMemory(ctx);+        }+    }+    return val;+}++/* if the returned bigfloat is allocated it is equal to+   'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return+   NULL in case of error. */+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)+{+    uint32_t tag;+    bf_t *r;+    JSBigFloat *p;++    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+        r = buf;+        bf_init(ctx->bf_ctx, r);+        if (bf_set_si(r, JS_VALUE_GET_INT(val)))+            goto fail;+        break;+    case JS_TAG_FLOAT64:+        r = buf;+        bf_init(ctx->bf_ctx, r);+        if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {+        fail:+            bf_delete(r);+            return NULL;+        }+        break;+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+        p = JS_VALUE_GET_PTR(val);+        r = &p->num;+        break;+    case JS_TAG_UNDEFINED:+    default:+        r = buf;+        bf_init(ctx->bf_ctx, r);+        bf_set_nan(r);+        break;+    }+    return r;+}++/* return NULL if invalid type */+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)+{+    uint32_t tag;+    JSBigDecimal *p;+    bfdec_t *r;+    +    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_BIG_DECIMAL:+        p = JS_VALUE_GET_PTR(val);+        r = &p->num;+        break;+    default:+        JS_ThrowTypeError(ctx, "bigdecimal expected");+        r = NULL;+        break;+    }+    return r;+}++/* return NaN if bad bigint literal */+static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)+{+    const char *str, *p;+    size_t len;+    int flags;+    +    str = JS_ToCStringLen(ctx, &len, val);+    JS_FreeValue(ctx, val);+    if (!str)+        return JS_EXCEPTION;+    p = str;+    p += skip_spaces(p);+    if ((p - str) == len) {+        val = JS_NewBigInt64(ctx, 0);+    } else {+        flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;+        if (is_math_mode(ctx))+            flags |= ATOD_MODE_BIGINT;+        val = js_atof(ctx, p, &p, 0, flags);+        p += skip_spaces(p);+        if (!JS_IsException(val)) {+            if ((p - str) != len) {+                JS_FreeValue(ctx, val);+                val = JS_NAN;+            }+        }+    }+    JS_FreeCString(ctx, str);+    return val;+}++static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)+{+    val = JS_StringToBigInt(ctx, val);+    if (JS_VALUE_IS_NAN(val))+        return JS_ThrowSyntaxError(ctx, "invalid bigint literal");+    return val;+}++/* if the returned bigfloat is allocated it is equal to+   'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */+static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)+{+    uint32_t tag;+    bf_t *r;+    JSBigFloat *p;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        if (!is_math_mode(ctx))+            goto fail;+        /* fall tru */+    case JS_TAG_BOOL:+        r = buf;+        bf_init(ctx->bf_ctx, r);+        bf_set_si(r, JS_VALUE_GET_INT(val));+        break;+    case JS_TAG_FLOAT64:+        {+            double d = JS_VALUE_GET_FLOAT64(val);+            if (!is_math_mode(ctx))+                goto fail;+            if (!isfinite(d))+                goto fail;+            r = buf;+            bf_init(ctx->bf_ctx, r);+            d = trunc(d);+            bf_set_float64(r, d);+        }+        break;+    case JS_TAG_BIG_INT:+        p = JS_VALUE_GET_PTR(val);+        r = &p->num;+        break;+    case JS_TAG_BIG_FLOAT:+        if (!is_math_mode(ctx))+            goto fail;+        p = JS_VALUE_GET_PTR(val);+        if (!bf_is_finite(&p->num))+            goto fail;+        r = buf;+        bf_init(ctx->bf_ctx, r);+        bf_set(r, &p->num);+        bf_rint(r, BF_RNDZ);+        JS_FreeValue(ctx, val);+        break;+    case JS_TAG_STRING:+        val = JS_StringToBigIntErr(ctx, val);+        if (JS_IsException(val))+            return NULL;+        goto redo;+    case JS_TAG_OBJECT:+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+        if (JS_IsException(val))+            return NULL;+        goto redo;+    default:+    fail:+        JS_FreeValue(ctx, val);+        JS_ThrowTypeError(ctx, "cannot convert to bigint");+        return NULL;+    }+    return r;+}++static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)+{+    return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));+}++static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)+{+    if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {+        return val;+    } else {+        bf_t a_s, *a, *r;+        int ret;+        JSValue res; ++        res = JS_NewBigInt(ctx);+        if (JS_IsException(res))+            return JS_EXCEPTION;+        a = JS_ToBigIntFree(ctx, &a_s, val);+        if (!a) {+            JS_FreeValue(ctx, res);+            return JS_EXCEPTION;+        }+        r = JS_GetBigInt(res);+        ret = bf_set(r, a);+        JS_FreeBigInt(ctx, a, &a_s);+        if (ret) {+            JS_FreeValue(ctx, res);+            return JS_ThrowOutOfMemory(ctx);+        }+        return JS_CompactBigInt(ctx, res);+    }+}++/* free the bf_t allocated by JS_ToBigInt */+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)+{+    if (a == buf) {+        bf_delete(a);+    } else {+        JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -+                                       offsetof(JSBigFloat, num));+        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));+    }+}++/* XXX: merge with JS_ToInt64Free with a specific flag */+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)+{+    bf_t a_s, *a;++    a = JS_ToBigIntFree(ctx, &a_s, val);+    if (!a) {+        *pres = 0;+        return -1;+    }+    bf_get_int64(pres, a, BF_GET_INT_MOD);+    JS_FreeBigInt(ctx, a, &a_s);+    return 0;+}++int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)+{+    return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));+}++static JSBigFloat *js_new_bf(JSContext *ctx)+{+    JSBigFloat *p;+    p = js_malloc(ctx, sizeof(*p));+    if (!p)+        return NULL;+    p->header.ref_count = 1;+    bf_init(ctx->bf_ctx, &p->num);+    return p;+}++static JSValue JS_NewBigFloat(JSContext *ctx)+{+    JSBigFloat *p;+    p = js_malloc(ctx, sizeof(*p));+    if (!p)+        return JS_EXCEPTION;+    p->header.ref_count = 1;+    bf_init(ctx->bf_ctx, &p->num);+    return JS_MKPTR(JS_TAG_BIG_FLOAT, p);+}++static JSValue JS_NewBigDecimal(JSContext *ctx)+{+    JSBigDecimal *p;+    p = js_malloc(ctx, sizeof(*p));+    if (!p)+        return JS_EXCEPTION;+    p->header.ref_count = 1;+    bfdec_init(ctx->bf_ctx, &p->num);+    return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);+}++static JSValue JS_NewBigInt(JSContext *ctx)+{+    JSBigFloat *p;+    p = js_malloc(ctx, sizeof(*p));+    if (!p)+        return JS_EXCEPTION;+    p->header.ref_count = 1;+    bf_init(ctx->bf_ctx, &p->num);+    return JS_MKPTR(JS_TAG_BIG_INT, p);+}++static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,+                                 BOOL convert_to_safe_integer)+{+    int64_t v;+    bf_t *a;+    +    if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)+        return val; /* fail safe */+    a = JS_GetBigInt(val);+    if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&+        v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {+        JS_FreeValue(ctx, val);+        return JS_NewInt64(ctx, v);+    } else if (a->expn == BF_EXP_ZERO && a->sign) {+        JSBigFloat *p = JS_VALUE_GET_PTR(val);+        assert(p->header.ref_count == 1);+        a->sign = 0;+    }+    return val;+}++/* Convert the big int to a safe integer if in math mode. normalize+   the zero representation. Could also be used to convert the bigint+   to a short bigint value. The reference count of the value must be+   1. Cannot fail */+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)+{+    return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));+}++/* must be kept in sync with JSOverloadableOperatorEnum */+/* XXX: use atoms ? */+static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {+    "+",+    "-",+    "*",+    "/",+    "%",+    "**",+    "|",+    "&",+    "^",+    "<<",+    ">>",+    ">>>",+    "==",+    "<",+    "pos",+    "neg",+    "++",+    "--",+    "~",+};++static int get_ovop_from_opcode(OPCodeEnum op)+{+    switch(op) {+    case OP_add:+        return JS_OVOP_ADD;+    case OP_sub:+        return JS_OVOP_SUB;+    case OP_mul:+        return JS_OVOP_MUL;+    case OP_div:+        return JS_OVOP_DIV;+    case OP_mod:+    case OP_math_mod:+        return JS_OVOP_MOD;+    case OP_pow:+        return JS_OVOP_POW;+    case OP_or:+        return JS_OVOP_OR;+    case OP_and:+        return JS_OVOP_AND;+    case OP_xor:+        return JS_OVOP_XOR;+    case OP_shl:+        return JS_OVOP_SHL;+    case OP_sar:+        return JS_OVOP_SAR;+    case OP_shr:+        return JS_OVOP_SHR;+    case OP_eq:+    case OP_neq:+        return JS_OVOP_EQ;+    case OP_lt:+    case OP_lte:+    case OP_gt:+    case OP_gte:+        return JS_OVOP_LESS;+    case OP_plus:+        return JS_OVOP_POS;+    case OP_neg:+        return JS_OVOP_NEG;+    case OP_inc:+        return JS_OVOP_INC;+    case OP_dec:+        return JS_OVOP_DEC;+    default:+        abort();+    }+}++/* return NULL if not present */+static JSObject *find_binary_op(JSBinaryOperatorDef *def,+                                uint32_t operator_index,+                                JSOverloadableOperatorEnum op)+{+    JSBinaryOperatorDefEntry *ent;+    int i;+    for(i = 0; i < def->count; i++) {+        ent = &def->tab[i];+        if (ent->operator_index == operator_index)+            return ent->ops[op];+    }+    return NULL;+}++/* return -1 if exception, 0 if no operator overloading, 1 if+   overloaded operator called */+static __exception int js_call_binary_op_fallback(JSContext *ctx,+                                                  JSValue *pret,+                                                  JSValueConst op1,+                                                  JSValueConst op2,+                                                  OPCodeEnum op,+                                                  BOOL is_numeric,+                                                  int hint)+{+    JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;+    JSOperatorSetData *opset1, *opset2;+    JSOverloadableOperatorEnum ovop;+    JSObject *p;+    JSValueConst args[2];+    +    if (!ctx->allow_operator_overloading)+        return 0;+    +    opset2_obj = JS_UNDEFINED;+    opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);+    if (JS_IsException(opset1_obj))+        goto exception;+    if (JS_IsUndefined(opset1_obj))+        return 0;+    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);+    if (!opset1)+        goto exception;++    opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);+    if (JS_IsException(opset2_obj))+        goto exception;+    if (JS_IsUndefined(opset2_obj)) {+        JS_FreeValue(ctx, opset1_obj);+        return 0;+    }+    opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);+    if (!opset2)+        goto exception;++    if (opset1->is_primitive && opset2->is_primitive) {+        JS_FreeValue(ctx, opset1_obj);+        JS_FreeValue(ctx, opset2_obj);+        return 0;+    }++    ovop = get_ovop_from_opcode(op);+    +    if (opset1->operator_counter == opset2->operator_counter) {+        p = opset1->self_ops[ovop];+    } else if (opset1->operator_counter > opset2->operator_counter) {+        p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);+    } else {+        p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);+    }+    if (!p) {+        JS_ThrowTypeError(ctx, "operator %s: no function defined",+                          js_overloadable_operator_names[ovop]);+        goto exception;+    }++    if (opset1->is_primitive) {+        if (is_numeric) {+            new_op1 = JS_ToNumeric(ctx, op1);+        } else {+            new_op1 = JS_ToPrimitive(ctx, op1, hint);+        }+        if (JS_IsException(new_op1))+            goto exception;+    } else {+        new_op1 = JS_DupValue(ctx, op1);+    }+    +    if (opset2->is_primitive) {+        if (is_numeric) {+            new_op2 = JS_ToNumeric(ctx, op2);+        } else {+            new_op2 = JS_ToPrimitive(ctx, op2, hint);+        }+        if (JS_IsException(new_op2)) {+            JS_FreeValue(ctx, new_op1);+            goto exception;+        }+    } else {+        new_op2 = JS_DupValue(ctx, op2);+    }++    /* XXX: could apply JS_ToPrimitive() if primitive type so that the+       operator function does not get a value object */+    +    method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+    if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {+        args[0] = new_op2;+        args[1] = new_op1;+    } else {+        args[0] = new_op1;+        args[1] = new_op2;+    }+    ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);+    JS_FreeValue(ctx, new_op1);+    JS_FreeValue(ctx, new_op2);+    if (JS_IsException(ret))+        goto exception;+    if (ovop == JS_OVOP_EQ) {+        BOOL res = JS_ToBoolFree(ctx, ret);+        if (op == OP_neq)+            res ^= 1;+        ret = JS_NewBool(ctx, res);+    } else if (ovop == JS_OVOP_LESS) {+        if (JS_IsUndefined(ret)) {+            ret = JS_FALSE;+        } else {+            BOOL res = JS_ToBoolFree(ctx, ret);+            if (op == OP_lte || op == OP_gte)+                res ^= 1;+            ret = JS_NewBool(ctx, res);+        }+    }+    JS_FreeValue(ctx, opset1_obj);+    JS_FreeValue(ctx, opset2_obj);+    *pret = ret;+    return 1;+ exception:+    JS_FreeValue(ctx, opset1_obj);+    JS_FreeValue(ctx, opset2_obj);+    *pret = JS_UNDEFINED;+    return -1;+}++/* try to call the operation on the operatorSet field of 'obj'. Only+   used for "/" and "**" on the BigInt prototype in math mode */+static __exception int js_call_binary_op_simple(JSContext *ctx,+                                                JSValue *pret,+                                                JSValueConst obj,+                                                JSValueConst op1,+                                                JSValueConst op2,+                                                OPCodeEnum op)+{+    JSValue opset1_obj, method, ret, new_op1, new_op2;+    JSOperatorSetData *opset1;+    JSOverloadableOperatorEnum ovop;+    JSObject *p;+    JSValueConst args[2];++    opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);+    if (JS_IsException(opset1_obj))+        goto exception;+    if (JS_IsUndefined(opset1_obj))+        return 0;+    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);+    if (!opset1)+        goto exception;+    ovop = get_ovop_from_opcode(op);++    p = opset1->self_ops[ovop];+    if (!p) {+        JS_FreeValue(ctx, opset1_obj);+        return 0;+    }++    new_op1 = JS_ToNumeric(ctx, op1);+    if (JS_IsException(new_op1))+        goto exception;+    new_op2 = JS_ToNumeric(ctx, op2);+    if (JS_IsException(new_op2)) {+        JS_FreeValue(ctx, new_op1);+        goto exception;+    }++    method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+    args[0] = new_op1;+    args[1] = new_op2;+    ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);+    JS_FreeValue(ctx, new_op1);+    JS_FreeValue(ctx, new_op2);+    if (JS_IsException(ret))+        goto exception;+    JS_FreeValue(ctx, opset1_obj);+    *pret = ret;+    return 1;+ exception:+    JS_FreeValue(ctx, opset1_obj);+    *pret = JS_UNDEFINED;+    return -1;+}++/* return -1 if exception, 0 if no operator overloading, 1 if+   overloaded operator called */+static __exception int js_call_unary_op_fallback(JSContext *ctx,+                                                 JSValue *pret,+                                                 JSValueConst op1,+                                                 OPCodeEnum op)+{+    JSValue opset1_obj, method, ret;+    JSOperatorSetData *opset1;+    JSOverloadableOperatorEnum ovop;+    JSObject *p;++    if (!ctx->allow_operator_overloading)+        return 0;+    +    opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);+    if (JS_IsException(opset1_obj))+        goto exception;+    if (JS_IsUndefined(opset1_obj))+        return 0;+    opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);+    if (!opset1)+        goto exception;+    if (opset1->is_primitive) {+        JS_FreeValue(ctx, opset1_obj);+        return 0;+    }++    ovop = get_ovop_from_opcode(op);++    p = opset1->self_ops[ovop];+    if (!p) {+        JS_ThrowTypeError(ctx, "no overloaded operator %s",+                          js_overloadable_operator_names[ovop]);+        goto exception;+    }+    method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+    ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);+    if (JS_IsException(ret))+        goto exception;+    JS_FreeValue(ctx, opset1_obj);+    *pret = ret;+    return 1;+ exception:+    JS_FreeValue(ctx, opset1_obj);+    *pret = JS_UNDEFINED;+    return -1;+}++static JSValue throw_bf_exception(JSContext *ctx, int status)+{+    const char *str;+    if (status & BF_ST_MEM_ERROR)+        return JS_ThrowOutOfMemory(ctx);+    if (status & BF_ST_DIVIDE_ZERO) {+        str = "division by zero";+    } else if (status & BF_ST_INVALID_OP) {+        str = "invalid operation";+    } else {+        str = "integer overflow";+    }+    return JS_ThrowRangeError(ctx, "%s", str);+}++static int js_unary_arith_bigint(JSContext *ctx,+                                 JSValue *pres, OPCodeEnum op, JSValue op1)+{+    bf_t a_s, *r, *a;+    int ret, v;+    JSValue res;+    +    if (op == OP_plus && !is_math_mode(ctx)) {+        JS_ThrowTypeError(ctx, "bigint argument with unary +");+        JS_FreeValue(ctx, op1);+        return -1;+    }+    res = JS_NewBigInt(ctx);+    if (JS_IsException(res)) {+        JS_FreeValue(ctx, op1);+        return -1;+    }+    r = JS_GetBigInt(res);+    a = JS_ToBigInt(ctx, &a_s, op1);+    ret = 0;+    switch(op) {+    case OP_inc:+    case OP_dec:+        v = 2 * (op - OP_dec) - 1;+        ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_plus:+        ret = bf_set(r, a);+        break;+    case OP_neg:+        ret = bf_set(r, a);+        bf_neg(r);+        break;+    case OP_not:+        ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);+        bf_neg(r);+        break;+    default:+        abort();+    }+    JS_FreeBigInt(ctx, a, &a_s);+    JS_FreeValue(ctx, op1);+    if (unlikely(ret)) {+        JS_FreeValue(ctx, res);+        throw_bf_exception(ctx, ret);+        return -1;+    }+    res = JS_CompactBigInt(ctx, res);+    *pres = res;+    return 0;+}++static int js_unary_arith_bigfloat(JSContext *ctx,+                                   JSValue *pres, OPCodeEnum op, JSValue op1)+{+    bf_t a_s, *r, *a;+    int ret, v;+    JSValue res;+    +    if (op == OP_plus && !is_math_mode(ctx)) {+        JS_ThrowTypeError(ctx, "bigfloat argument with unary +");+        JS_FreeValue(ctx, op1);+        return -1;+    }++    res = JS_NewBigFloat(ctx);+    if (JS_IsException(res)) {+        JS_FreeValue(ctx, op1);+        return -1;+    }+    r = JS_GetBigFloat(res);+    a = JS_ToBigFloat(ctx, &a_s, op1);+    ret = 0;+    switch(op) {+    case OP_inc:+    case OP_dec:+        v = 2 * (op - OP_dec) - 1;+        ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case OP_plus:+        ret = bf_set(r, a);+        break;+    case OP_neg:+        ret = bf_set(r, a);+        bf_neg(r);+        break;+    default:+        abort();+    }+    if (a == &a_s)+        bf_delete(a);+    JS_FreeValue(ctx, op1);+    if (unlikely(ret & BF_ST_MEM_ERROR)) {+        JS_FreeValue(ctx, res);+        throw_bf_exception(ctx, ret);+        return -1;+    }+    *pres = res;+    return 0;+}++static int js_unary_arith_bigdecimal(JSContext *ctx,+                                     JSValue *pres, OPCodeEnum op, JSValue op1)+{+    bfdec_t *r, *a;+    int ret, v;+    JSValue res;+    +    if (op == OP_plus && !is_math_mode(ctx)) {+        JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");+        JS_FreeValue(ctx, op1);+        return -1;+    }++    res = JS_NewBigDecimal(ctx);+    if (JS_IsException(res)) {+        JS_FreeValue(ctx, op1);+        return -1;+    }+    r = JS_GetBigDecimal(res);+    a = JS_ToBigDecimal(ctx, op1);+    ret = 0;+    switch(op) {+    case OP_inc:+    case OP_dec:+        v = 2 * (op - OP_dec) - 1;+        ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_plus:+        ret = bfdec_set(r, a);+        break;+    case OP_neg:+        ret = bfdec_set(r, a);+        bfdec_neg(r);+        break;+    default:+        abort();+    }+    JS_FreeValue(ctx, op1);+    if (unlikely(ret)) {+        JS_FreeValue(ctx, res);+        throw_bf_exception(ctx, ret);+        return -1;+    }+    *pres = res;+    return 0;+}++static no_inline __exception int js_unary_arith_slow(JSContext *ctx,+                                                     JSValue *sp,+                                                     OPCodeEnum op)+{+    JSValue op1, val;+    int v, ret;+    uint32_t tag;++    op1 = sp[-1];+    /* fast path for float64 */+    if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))+        goto handle_float64;+    if (JS_IsObject(op1)) {+        ret = js_call_unary_op_fallback(ctx, &val, op1, op);+        if (ret < 0)+            return -1;+        if (ret) {+            JS_FreeValue(ctx, op1);+            sp[-1] = val;+            return 0;+        }+    }++    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1))+        goto exception;+    tag = JS_VALUE_GET_TAG(op1);+    switch(tag) {+    case JS_TAG_INT:+        {+            int64_t v64;+            v64 = JS_VALUE_GET_INT(op1);+            switch(op) {+            case OP_inc:+            case OP_dec:+                v = 2 * (op - OP_dec) - 1;+                v64 += v;+                break;+            case OP_plus:+                break;+            case OP_neg:+                if (v64 == 0) {+                    sp[-1] = __JS_NewFloat64(ctx, -0.0);+                    return 0;+                } else {+                    v64 = -v64;+                }+                break;+            default:+                abort();+            }+            sp[-1] = JS_NewInt64(ctx, v64);+        }+        break;+    case JS_TAG_BIG_INT:+    handle_bigint:+        if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))+            goto exception;+        break;+    case JS_TAG_BIG_FLOAT:+        if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))+            goto exception;+        break;+    case JS_TAG_BIG_DECIMAL:+        if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))+            goto exception;+        break;+    default:+    handle_float64:+        {+            double d;+            if (is_math_mode(ctx))+                goto handle_bigint;+            d = JS_VALUE_GET_FLOAT64(op1);+            switch(op) {+            case OP_inc:+            case OP_dec:+                v = 2 * (op - OP_dec) - 1;+                d += v;+                break;+            case OP_plus:+                break;+            case OP_neg:+                d = -d;+                break;+            default:+                abort();+            }+            sp[-1] = __JS_NewFloat64(ctx, d);+        }+        break;+    }+    return 0;+ exception:+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static __exception int js_post_inc_slow(JSContext *ctx,+                                        JSValue *sp, OPCodeEnum op)+{+    JSValue op1;++    /* XXX: allow custom operators */+    op1 = sp[-1];+    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1)) {+        sp[-1] = JS_UNDEFINED;+        return -1;+    }+    sp[-1] = op1;+    sp[0] = JS_DupValue(ctx, op1);+    return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);+}++static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)+{+    JSValue op1, val;+    int ret;+    +    op1 = sp[-1];+    if (JS_IsObject(op1)) {+        ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);+        if (ret < 0)+            return -1;+        if (ret) {+            JS_FreeValue(ctx, op1);+            sp[-1] = val;+            return 0;+        }+    }++    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1))+        goto exception;+    if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {+        if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))+            goto exception;+    } else {+        int32_t v1;+        if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))+            goto exception;+        sp[-1] = JS_NewInt32(ctx, ~v1);+    }+    return 0;+ exception:+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,+                                    JSValue *pres, JSValue op1, JSValue op2)+{+    bf_t a_s, b_s, *r, *a, *b;+    int ret;+    JSValue res;+    +    res = JS_NewBigFloat(ctx);+    if (JS_IsException(res)) {+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        return -1;+    }+    r = JS_GetBigFloat(res);+    a = JS_ToBigFloat(ctx, &a_s, op1);+    b = JS_ToBigFloat(ctx, &b_s, op2);+    bf_init(ctx->bf_ctx, r);+    switch(op) {+    case OP_add:+        ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case OP_sub:+        ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case OP_mul:+        ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case OP_div:+        ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case OP_math_mod:+        /* Euclidian remainder */+        ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,+                     BF_DIVREM_EUCLIDIAN);+        break;+    case OP_mod:+        ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,+                     BF_RNDZ);+        break;+    case OP_pow:+        ret = bf_pow(r, a, b, ctx->fp_env.prec,+                     ctx->fp_env.flags | BF_POW_JS_QUIRKS);+        break;+    default:+        abort();+    }+    if (a == &a_s)+        bf_delete(a);+    if (b == &b_s)+        bf_delete(b);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    if (unlikely(ret & BF_ST_MEM_ERROR)) {+        JS_FreeValue(ctx, res);+        throw_bf_exception(ctx, ret);+        return -1;+    }+    *pres = res;+    return 0;+}++static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,+                                  JSValue *pres, JSValue op1, JSValue op2)+{+    bf_t a_s, b_s, *r, *a, *b;+    int ret;+    JSValue res;+    +    res = JS_NewBigInt(ctx);+    if (JS_IsException(res))+        goto fail;+    a = JS_ToBigInt(ctx, &a_s, op1);+    if (!a)+        goto fail;+    b = JS_ToBigInt(ctx, &b_s, op2);+    if (!b) {+        JS_FreeBigInt(ctx, a, &a_s);+        goto fail;+    }+    r = JS_GetBigInt(res);+    ret = 0;+    switch(op) {+    case OP_add:+        ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_sub:+        ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_mul:+        ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_div:+        if (!is_math_mode(ctx)) {+            bf_t rem_s, *rem = &rem_s;+            bf_init(ctx->bf_ctx, rem);+            ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,+                            BF_RNDZ);+            bf_delete(rem);+        } else {+            goto math_mode_div_pow;+        }+        break;+    case OP_math_mod:+        /* Euclidian remainder */+        ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,+                     BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;+        break;+    case OP_mod:+        ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,+                     BF_RNDZ) & BF_ST_INVALID_OP;+        break;+    case OP_pow:+        if (b->sign) {+            if (!is_math_mode(ctx)) {+                ret = BF_ST_INVALID_OP;+            } else {+            math_mode_div_pow:+                JS_FreeValue(ctx, res);+                ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);+                if (ret != 0) {+                    JS_FreeBigInt(ctx, a, &a_s);+                    JS_FreeBigInt(ctx, b, &b_s);+                    JS_FreeValue(ctx, op1);+                    JS_FreeValue(ctx, op2);+                    if (ret < 0) {+                        return -1;+                    } else {+                        *pres = res;+                        return 0;+                    }+                }+                /* if no BigInt power operator defined, return a+                   bigfloat */+                res = JS_NewBigFloat(ctx);+                if (JS_IsException(res)) {+                    JS_FreeBigInt(ctx, a, &a_s);+                    JS_FreeBigInt(ctx, b, &b_s);+                    goto fail;+                }+                r = JS_GetBigFloat(res);+                if (op == OP_div) {+                    ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;+                } else {+                    ret = bf_pow(r, a, b, ctx->fp_env.prec,+                                 ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;+                }+                JS_FreeBigInt(ctx, a, &a_s);+                JS_FreeBigInt(ctx, b, &b_s);+                JS_FreeValue(ctx, op1);+                JS_FreeValue(ctx, op2);+                if (unlikely(ret)) {+                    JS_FreeValue(ctx, res);+                    throw_bf_exception(ctx, ret);+                    return -1;+                }+                *pres = res;+                return 0;+            }+        } else {+            ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);+        }+        break;++        /* logical operations */+    case OP_shl:+    case OP_sar:+        {+            slimb_t v2;+#if LIMB_BITS == 32+            bf_get_int32(&v2, b, 0);+            if (v2 == INT32_MIN)+                v2 = INT32_MIN + 1;+#else+            bf_get_int64(&v2, b, 0);+            if (v2 == INT64_MIN)+                v2 = INT64_MIN + 1;+#endif+            if (op == OP_sar)+                v2 = -v2;+            ret = bf_set(r, a);+            ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);+            if (v2 < 0) {+                ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);+            }+        }+        break;+    case OP_and:+        ret = bf_logic_and(r, a, b);+        break;+    case OP_or:+        ret = bf_logic_or(r, a, b);+        break;+    case OP_xor:+        ret = bf_logic_xor(r, a, b);+        break;+    default:+        abort();+    }+    JS_FreeBigInt(ctx, a, &a_s);+    JS_FreeBigInt(ctx, b, &b_s);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    if (unlikely(ret)) {+        JS_FreeValue(ctx, res);+        throw_bf_exception(ctx, ret);+        return -1;+    }+    *pres = JS_CompactBigInt(ctx, res);+    return 0;+ fail:+    JS_FreeValue(ctx, res);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    return -1;+}++/* b must be a positive integer */+static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)+{+    bfdec_t b1;+    int32_t b2;+    int ret;++    bfdec_init(b->ctx, &b1);+    ret = bfdec_set(&b1, b);+    if (ret) {+        bfdec_delete(&b1);+        return ret;+    }+    ret = bfdec_rint(&b1, BF_RNDZ);+    if (ret) {+        bfdec_delete(&b1);+        return BF_ST_INVALID_OP; /* must be an integer */+    }+    ret = bfdec_get_int32(&b2, &b1);+    bfdec_delete(&b1);+    if (ret)+        return ret; /* overflow */+    if (b2 < 0)+        return BF_ST_INVALID_OP; /* must be positive */+    return bfdec_pow_ui(r, a, b2);+}++static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,+                                      JSValue *pres, JSValue op1, JSValue op2)+{+    bfdec_t *r, *a, *b;+    int ret;+    JSValue res;++    res = JS_NewBigDecimal(ctx);+    if (JS_IsException(res))+        goto fail;+    r = JS_GetBigDecimal(res);+    +    a = JS_ToBigDecimal(ctx, op1);+    if (!a)+        goto fail;+    b = JS_ToBigDecimal(ctx, op2);+    if (!b)+        goto fail;+    switch(op) {+    case OP_add:+        ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_sub:+        ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_mul:+        ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_div:+        ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);+        break;+    case OP_math_mod:+        /* Euclidian remainder */+        ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);+        break;+    case OP_mod:+        ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);+        break;+    case OP_pow:+        ret = js_bfdec_pow(r, a, b);+        break;+    default:+        abort();+    }+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    if (unlikely(ret)) {+        JS_FreeValue(ctx, res);+        throw_bf_exception(ctx, ret);+        return -1;+    }+    *pres = res;+    return 0;+ fail:+    JS_FreeValue(ctx, res);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    return -1;+}++static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,+                                                      OPCodeEnum op)+{+    JSValue op1, op2, res;+    uint32_t tag1, tag2;+    int ret;+    double d1, d2;++    op1 = sp[-2];+    op2 = sp[-1];+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);+    /* fast path for float operations */+    if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {+        d1 = JS_VALUE_GET_FLOAT64(op1);+        d2 = JS_VALUE_GET_FLOAT64(op2);+        goto handle_float64;+    }++    /* try to call an overloaded operator */+    if ((tag1 == JS_TAG_OBJECT &&+         (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||+        (tag2 == JS_TAG_OBJECT &&+         (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {+        ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);+        if (ret != 0) {+            JS_FreeValue(ctx, op1);+            JS_FreeValue(ctx, op2);+            if (ret < 0) {+                goto exception;+            } else {+                sp[-2] = res;+                return 0;+            }+        }+    }++    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    op2 = JS_ToNumericFree(ctx, op2);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        goto exception;+    }+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);++    if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {+        int32_t v1, v2;+        int64_t v;+        v1 = JS_VALUE_GET_INT(op1);+        v2 = JS_VALUE_GET_INT(op2);+        switch(op) {+        case OP_sub:+            v = (int64_t)v1 - (int64_t)v2;+            break;+        case OP_mul:+            v = (int64_t)v1 * (int64_t)v2;+            if (is_math_mode(ctx) &&+                (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))+                goto handle_bigint;+            if (v == 0 && (v1 | v2) < 0) {+                sp[-2] = __JS_NewFloat64(ctx, -0.0);+                return 0;+            }+            break;+        case OP_div:+            if (is_math_mode(ctx))+                goto handle_bigint;+            sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);+            return 0;+        case OP_math_mod:+            if (unlikely(v2 == 0)) {+                throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);+                goto exception;+            }+            v = (int64_t)v1 % (int64_t)v2;+            if (v < 0) {+                if (v2 < 0)+                    v -= v2;+                else+                    v += v2;+            }+            break;+        case OP_mod:+            if (v1 < 0 || v2 <= 0) {+                sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));+                return 0;+            } else {+                v = (int64_t)v1 % (int64_t)v2;+            }+            break;+        case OP_pow:+            if (!is_math_mode(ctx)) {+                sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));+                return 0;+            } else {+                goto handle_bigint;+            }+            break;+        default:+            abort();+        }+        sp[-2] = JS_NewInt64(ctx, v);+    } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+        if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))+            goto exception;+    } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+        if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))+            goto exception;+    } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+    handle_bigint:+        if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))+            goto exception;+    } else {+        double dr;+        /* float64 result */+        if (JS_ToFloat64Free(ctx, &d1, op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        if (JS_ToFloat64Free(ctx, &d2, op2))+            goto exception;+    handle_float64:+        if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))+            goto handle_bigint;+        switch(op) {+        case OP_sub:+            dr = d1 - d2;+            break;+        case OP_mul:+            dr = d1 * d2;+            break;+        case OP_div:+            dr = d1 / d2;+            break;+        case OP_mod:+            dr = fmod(d1, d2);+            break;+        case OP_math_mod:+            d2 = fabs(d2);+            dr = fmod(d1, d2);+            /* XXX: loss of accuracy if dr < 0 */+            if (dr < 0)+                dr += d2;+            break;+        case OP_pow:+            dr = js_pow(d1, d2);+            break;+        default:+            abort();+        }+        sp[-2] = __JS_NewFloat64(ctx, dr);+    }+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2, res;+    uint32_t tag1, tag2;+    int ret;++    op1 = sp[-2];+    op2 = sp[-1];++    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);+    /* fast path for float64 */+    if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {+        double d1, d2;+        d1 = JS_VALUE_GET_FLOAT64(op1);+        d2 = JS_VALUE_GET_FLOAT64(op2);+        sp[-2] = __JS_NewFloat64(ctx, d1 + d2);+        return 0;+    }++    if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {+        /* try to call an overloaded operator */+        if ((tag1 == JS_TAG_OBJECT &&+             (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&+              tag2 != JS_TAG_STRING)) ||+            (tag2 == JS_TAG_OBJECT &&+             (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&+              tag1 != JS_TAG_STRING))) {+            ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,+                                             FALSE, HINT_NONE);+            if (ret != 0) {+                JS_FreeValue(ctx, op1);+                JS_FreeValue(ctx, op2);+                if (ret < 0) {+                    goto exception;+                } else {+                    sp[-2] = res;+                    return 0;+                }+            }+        }++        op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+        if (JS_IsException(op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }++        op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+        if (JS_IsException(op2)) {+            JS_FreeValue(ctx, op1);+            goto exception;+        }+        tag1 = JS_VALUE_GET_NORM_TAG(op1);+        tag2 = JS_VALUE_GET_NORM_TAG(op2);+    }++    if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {+        sp[-2] = JS_ConcatString(ctx, op1, op2);+        if (JS_IsException(sp[-2]))+            goto exception;+        return 0;+    }++    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    op2 = JS_ToNumericFree(ctx, op2);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        goto exception;+    }+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);++    if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {+        int32_t v1, v2;+        int64_t v;+        v1 = JS_VALUE_GET_INT(op1);+        v2 = JS_VALUE_GET_INT(op2);+        v = (int64_t)v1 + (int64_t)v2;+        sp[-2] = JS_NewInt64(ctx, v);+    } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+        if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))+            goto exception;+    } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+        if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))+            goto exception;+    } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+    handle_bigint:+        if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))+            goto exception;+    } else {+        double d1, d2;+        /* float64 result */+        if (JS_ToFloat64Free(ctx, &d1, op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        if (JS_ToFloat64Free(ctx, &d2, op2))+            goto exception;+        if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))+            goto handle_bigint;+        sp[-2] = __JS_NewFloat64(ctx, d1 + d2);+    }+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline __exception int js_binary_logic_slow(JSContext *ctx,+                                                      JSValue *sp,+                                                      OPCodeEnum op)+{+    JSValue op1, op2, res;+    int ret;+    uint32_t tag1, tag2;+    uint32_t v1, v2, r;++    op1 = sp[-2];+    op2 = sp[-1];+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);++    /* try to call an overloaded operator */+    if ((tag1 == JS_TAG_OBJECT &&+         (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||+        (tag2 == JS_TAG_OBJECT &&+         (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {+        ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);+        if (ret != 0) {+            JS_FreeValue(ctx, op1);+            JS_FreeValue(ctx, op2);+            if (ret < 0) {+                goto exception;+            } else {+                sp[-2] = res;+                return 0;+            }+        }+    }++    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    op2 = JS_ToNumericFree(ctx, op2);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        goto exception;+    }++    if (is_math_mode(ctx))+        goto bigint_op;++    tag1 = JS_VALUE_GET_TAG(op1);+    tag2 = JS_VALUE_GET_TAG(op2);+    if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+        if (tag1 != tag2) {+            JS_FreeValue(ctx, op1);+            JS_FreeValue(ctx, op2);+            JS_ThrowTypeError(ctx, "both operands must be bigint");+            goto exception;+        } else {+        bigint_op:+            if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))+                goto exception;+        }+    } else {+        if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))+            goto exception;+        switch(op) {+        case OP_shl:+            r = v1 << (v2 & 0x1f);+            break;+        case OP_sar:+            r = (int)v1 >> (v2 & 0x1f);+            break;+        case OP_and:+            r = v1 & v2;+            break;+        case OP_or:+            r = v1 | v2;+            break;+        case OP_xor:+            r = v1 ^ v2;+            break;+        default:+            abort();+        }+        sp[-2] = JS_NewInt32(ctx, r);+    }+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++/* Note: also used for bigint */+static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,+                               JSValue op1, JSValue op2)+{+    bf_t a_s, b_s, *a, *b;+    int res;+    +    a = JS_ToBigFloat(ctx, &a_s, op1);+    if (!a) {+        JS_FreeValue(ctx, op2);+        return -1;+    }+    b = JS_ToBigFloat(ctx, &b_s, op2);+    if (!b) {+        if (a == &a_s)+            bf_delete(a);+        JS_FreeValue(ctx, op1);+        return -1;+    }+    switch(op) {+    case OP_lt:+        res = bf_cmp_lt(a, b); /* if NaN return false */+        break;+    case OP_lte:+        res = bf_cmp_le(a, b); /* if NaN return false */+        break;+    case OP_gt:+        res = bf_cmp_lt(b, a); /* if NaN return false */+        break;+    case OP_gte:+        res = bf_cmp_le(b, a); /* if NaN return false */+        break;+    case OP_eq:+        res = bf_cmp_eq(a, b); /* if NaN return false */+        break;+    default:+        abort();+    }+    if (a == &a_s)+        bf_delete(a);+    if (b == &b_s)+        bf_delete(b);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    return res;+}++static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,+                                 JSValue op1, JSValue op2)+{+    bfdec_t *a, *b;+    int res;++    /* Note: binary floats are converted to bigdecimal with+       toString(). It is not mathematically correct but is consistent+       with the BigDecimal() constructor behavior */+    op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        return -1;+    }+    op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        return -1;+    }+    a = JS_ToBigDecimal(ctx, op1);+    b = JS_ToBigDecimal(ctx, op2);+    +    switch(op) {+    case OP_lt:+        res = bfdec_cmp_lt(a, b); /* if NaN return false */+        break;+    case OP_lte:+        res = bfdec_cmp_le(a, b); /* if NaN return false */+        break;+    case OP_gt:+        res = bfdec_cmp_lt(b, a); /* if NaN return false */+        break;+    case OP_gte:+        res = bfdec_cmp_le(b, a); /* if NaN return false */+        break;+    case OP_eq:+        res = bfdec_cmp_eq(a, b); /* if NaN return false */+        break;+    default:+        abort();+    }+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    return res;+}++static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,+                                        OPCodeEnum op)+{+    JSValue op1, op2, ret;+    int res;+    uint32_t tag1, tag2;++    op1 = sp[-2];+    op2 = sp[-1];+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);+    /* try to call an overloaded operator */+    if ((tag1 == JS_TAG_OBJECT &&+         (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||+        (tag2 == JS_TAG_OBJECT &&+         (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {+        res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,+                                         FALSE, HINT_NUMBER);+        if (res != 0) {+            JS_FreeValue(ctx, op1);+            JS_FreeValue(ctx, op2);+            if (res < 0) {+                goto exception;+            } else {+                sp[-2] = ret;+                return 0;+            }+        }+    }+    op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        goto exception;+    }+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);++    if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {+        JSString *p1, *p2;+        p1 = JS_VALUE_GET_STRING(op1);+        p2 = JS_VALUE_GET_STRING(op2);+        res = js_string_compare(ctx, p1, p2);+        switch(op) {+        case OP_lt:+            res = (res < 0);+            break;+        case OP_lte:+            res = (res <= 0);+            break;+        case OP_gt:+            res = (res > 0);+            break;+        default:+        case OP_gte:+            res = (res >= 0);+            break;+        }+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+    } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&+               (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {+        /* can use floating point comparison */+        double d1, d2;+        if (tag1 == JS_TAG_FLOAT64) {+            d1 = JS_VALUE_GET_FLOAT64(op1);+        } else {+            d1 = JS_VALUE_GET_INT(op1);+        }+        if (tag2 == JS_TAG_FLOAT64) {+            d2 = JS_VALUE_GET_FLOAT64(op2);+        } else {+            d2 = JS_VALUE_GET_INT(op2);+        }+        switch(op) {+        case OP_lt:+            res = (d1 < d2); /* if NaN return false */+            break;+        case OP_lte:+            res = (d1 <= d2); /* if NaN return false */+            break;+        case OP_gt:+            res = (d1 > d2); /* if NaN return false */+            break;+        default:+        case OP_gte:+            res = (d1 >= d2); /* if NaN return false */+            break;+        }+    } else {+        if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||+             (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&+            !is_math_mode(ctx)) {+            if (tag1 == JS_TAG_STRING) {+                op1 = JS_StringToBigInt(ctx, op1);+                if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)+                    goto invalid_bigint_string;+            }+            if (tag2 == JS_TAG_STRING) {+                op2 = JS_StringToBigInt(ctx, op2);+                if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {+                invalid_bigint_string:+                    JS_FreeValue(ctx, op1);+                    JS_FreeValue(ctx, op2);+                    res = FALSE;+                    goto done;+                }+            }+        } else {+            op1 = JS_ToNumericFree(ctx, op1);+            if (JS_IsException(op1)) {+                JS_FreeValue(ctx, op2);+                goto exception;+            }+            op2 = JS_ToNumericFree(ctx, op2);+            if (JS_IsException(op2)) {+                JS_FreeValue(ctx, op1);+                goto exception;+            }+        }++        if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_DECIMAL ||+            JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_DECIMAL) {+            res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);+            if (res < 0)+                goto exception;+        } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_FLOAT ||+                   JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_FLOAT) {+            res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);+            if (res < 0)+                goto exception;+        } else {+            res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);+            if (res < 0)+                goto exception;+        }+    }+ done:+    sp[-2] = JS_NewBool(ctx, res);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static BOOL tag_is_number(uint32_t tag)+{+    return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||+            tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||+            tag == JS_TAG_BIG_DECIMAL);+}++static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,+                                            BOOL is_neq)+{+    JSValue op1, op2, ret;+    int res;+    uint32_t tag1, tag2;++    op1 = sp[-2];+    op2 = sp[-1];+ redo:+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);+    if (tag_is_number(tag1) && tag_is_number(tag2)) {+        if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {+            res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);+        } else if ((tag1 == JS_TAG_FLOAT64 &&+                    (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||+                   (tag2 == JS_TAG_FLOAT64 &&+                    (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {+            double d1, d2;+            if (tag1 == JS_TAG_FLOAT64) {+                d1 = JS_VALUE_GET_FLOAT64(op1);+            } else {+                d1 = JS_VALUE_GET_INT(op1);+            }+            if (tag2 == JS_TAG_FLOAT64) {+                d2 = JS_VALUE_GET_FLOAT64(op2);+            } else {+                d2 = JS_VALUE_GET_INT(op2);+            }+            res = (d1 == d2);+        } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+            res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);+            if (res < 0)+                goto exception;+        } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+            res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);+            if (res < 0)+                goto exception;+        } else {+            res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);+            if (res < 0)+                goto exception;+        }+    } else if (tag1 == tag2) {+        if (tag1 == JS_TAG_OBJECT) {+            /* try the fallback operator */+            res = js_call_binary_op_fallback(ctx, &ret, op1, op2,+                                             is_neq ? OP_neq : OP_eq,+                                             FALSE, HINT_NONE);+            if (res != 0) {+                JS_FreeValue(ctx, op1);+                JS_FreeValue(ctx, op2);+                if (res < 0) {+                    goto exception;+                } else {+                    sp[-2] = ret;+                    return 0;+                }+            }+        }+        res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);+    } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||+               (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {+        res = TRUE;+    } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||+               (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {++        if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&+            !is_math_mode(ctx)) {+            if (tag1 == JS_TAG_STRING) {+                op1 = JS_StringToBigInt(ctx, op1);+                if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)+                    goto invalid_bigint_string;+            }+            if (tag2 == JS_TAG_STRING) {+                op2 = JS_StringToBigInt(ctx, op2);+                if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {+                invalid_bigint_string:+                    JS_FreeValue(ctx, op1);+                    JS_FreeValue(ctx, op2);+                    res = FALSE;+                    goto done;+                }+            }+        } else {+            op1 = JS_ToNumericFree(ctx, op1);+            if (JS_IsException(op1)) {+                JS_FreeValue(ctx, op2);+                goto exception;+            }+            op2 = JS_ToNumericFree(ctx, op2);+            if (JS_IsException(op2)) {+                JS_FreeValue(ctx, op1);+                goto exception;+            }+        }+        res = js_strict_eq(ctx, op1, op2);+    } else if (tag1 == JS_TAG_BOOL) {+        op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));+        goto redo;+    } else if (tag2 == JS_TAG_BOOL) {+        op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));+        goto redo;+    } else if ((tag1 == JS_TAG_OBJECT &&+                (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||+               (tag2 == JS_TAG_OBJECT &&+                (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {++        /* try the fallback operator */+        res = js_call_binary_op_fallback(ctx, &ret, op1, op2,+                                         is_neq ? OP_neq : OP_eq,+                                         FALSE, HINT_NONE);+        if (res != 0) {+            JS_FreeValue(ctx, op1);+            JS_FreeValue(ctx, op2);+            if (res < 0) {+                goto exception;+            } else {+                sp[-2] = ret;+                return 0;+            }+        }++        op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+        if (JS_IsException(op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+        if (JS_IsException(op2)) {+            JS_FreeValue(ctx, op1);+            goto exception;+        }+        goto redo;+    } else {+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        res = FALSE;+    }+ done:+    sp[-2] = JS_NewBool(ctx, res ^ is_neq);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2;+    uint32_t v1, v2, r;++    op1 = sp[-2];+    op2 = sp[-1];+    op1 = JS_ToNumericFree(ctx, op1);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    op2 = JS_ToNumericFree(ctx, op2);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        goto exception;+    }+    /* XXX: could forbid >>> in bignum mode */+    if (!is_math_mode(ctx) &&+        (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||+         JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {+        JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    /* cannot give an exception */+    JS_ToUint32Free(ctx, &v1, op1);+    JS_ToUint32Free(ctx, &v2, op2);+    r = v1 >> (v2 & 0x1f);+    sp[-2] = JS_NewUint32(ctx, r);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,+                                       int64_t exponent)+{+    bf_t r_s, *r = &r_s;+    double d;+    int ret;+    +    /* always convert to Float64 */+    bf_init(ctx->bf_ctx, r);+    ret = bf_mul_pow_radix(r, a, 10, exponent,+                           53, bf_set_exp_bits(11) | BF_RNDN |+                           BF_FLAG_SUBNORMAL);+    bf_get_float64(r, &d, BF_RNDN);+    bf_delete(r);+    if (ret & BF_ST_MEM_ERROR)+        return JS_ThrowOutOfMemory(ctx);+    else+        return __JS_NewFloat64(ctx, d);+}++static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)+{+    bf_t a_s, *a, *r;+    JSValue op1, op2, res;+    int64_t e;+    int ret;++    res = JS_NewBigFloat(ctx);+    if (JS_IsException(res))+        return -1;+    r = JS_GetBigFloat(res);+    op1 = sp[-2];+    op2 = sp[-1];+    a = JS_ToBigFloat(ctx, &a_s, op1);+    if (!a)+        return -1;+    if (JS_IsBigInt(ctx, op2)) {+        ret = JS_ToBigInt64(ctx, &e, op2);+    } else {+        ret = JS_ToInt64(ctx, &e, op2);+    }+    if (ret) {+        if (a == &a_s)+            bf_delete(a);+        JS_FreeValue(ctx, res);+        return -1;+    }++    bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);+    if (a == &a_s)+        bf_delete(a);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    sp[-2] = res;+    return 0;+}++#else /* !CONFIG_BIGNUM */++static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)+{+    return JS_ThrowTypeError(ctx, "bigint is not supported");+}++JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)+{+    return JS_ThrowUnsupportedBigint(ctx);+}++JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)+{+    return JS_ThrowUnsupportedBigint(ctx);+}++int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)+{+    JS_ThrowUnsupportedBigint(ctx);+    *pres = 0;+    return -1;+}++static no_inline __exception int js_unary_arith_slow(JSContext *ctx,+                                                     JSValue *sp,+                                                     OPCodeEnum op)+{+    JSValue op1;+    double d;++    op1 = sp[-1];+    if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {+        sp[-1] = JS_UNDEFINED;+        return -1;+    }+    switch(op) {+    case OP_inc:+        d++;+        break;+    case OP_dec:+        d--;+        break;+    case OP_plus:+        break;+    case OP_neg:+        d = -d;+        break;+    default:+        abort();+    }+    sp[-1] = JS_NewFloat64(ctx, d);+    return 0;+}++/* specific case necessary for correct return value semantics */+static __exception int js_post_inc_slow(JSContext *ctx,+                                        JSValue *sp, OPCodeEnum op)+{+    JSValue op1;+    double d, r;++    op1 = sp[-1];+    if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {+        sp[-1] = JS_UNDEFINED;+        return -1;+    }+    r = d + 2 * (op - OP_post_dec) - 1;+    sp[0] = JS_NewFloat64(ctx, r);+    sp[-1] = JS_NewFloat64(ctx, d);+    return 0;+}++static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,+                                                      OPCodeEnum op)+{+    JSValue op1, op2;+    double d1, d2, r;++    op1 = sp[-2];+    op2 = sp[-1];+    if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {+        goto exception;+    }+    switch(op) {+    case OP_sub:+        r = d1 - d2;+        break;+    case OP_mul:+        r = d1 * d2;+        break;+    case OP_div:+        r = d1 / d2;+        break;+    case OP_mod:+        r = fmod(d1, d2);+        break;+    case OP_pow:+        r = js_pow(d1, d2);+        break;+    default:+        abort();+    }+    sp[-2] = JS_NewFloat64(ctx, r);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2;+    uint32_t tag1, tag2;++    op1 = sp[-2];+    op2 = sp[-1];+    tag1 = JS_VALUE_GET_TAG(op1);+    tag2 = JS_VALUE_GET_TAG(op2);+    if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&+        (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {+        goto add_numbers;+    } else {+        op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+        if (JS_IsException(op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+        if (JS_IsException(op2)) {+            JS_FreeValue(ctx, op1);+            goto exception;+        }+        tag1 = JS_VALUE_GET_TAG(op1);+        tag2 = JS_VALUE_GET_TAG(op2);+        if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {+            sp[-2] = JS_ConcatString(ctx, op1, op2);+            if (JS_IsException(sp[-2]))+                goto exception;+        } else {+            double d1, d2;+        add_numbers:+            if (JS_ToFloat64Free(ctx, &d1, op1)) {+                JS_FreeValue(ctx, op2);+                goto exception;+            }+            if (JS_ToFloat64Free(ctx, &d2, op2))+                goto exception;+            sp[-2] = JS_NewFloat64(ctx, d1 + d2);+        }+    }+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline __exception int js_binary_logic_slow(JSContext *ctx,+                                                      JSValue *sp,+                                                      OPCodeEnum op)+{+    JSValue op1, op2;+    uint32_t v1, v2, r;++    op1 = sp[-2];+    op2 = sp[-1];+    if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))+        goto exception;+    switch(op) {+    case OP_shl:+        r = v1 << (v2 & 0x1f);+        break;+    case OP_sar:+        r = (int)v1 >> (v2 & 0x1f);+        break;+    case OP_and:+        r = v1 & v2;+        break;+    case OP_or:+        r = v1 | v2;+        break;+    case OP_xor:+        r = v1 ^ v2;+        break;+    default:+        abort();+    }+    sp[-2] = JS_NewInt32(ctx, r);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)+{+    int32_t v1;++    if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {+        sp[-1] = JS_UNDEFINED;+        return -1;+    }+    sp[-1] = JS_NewInt32(ctx, ~v1);+    return 0;+}++static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,+                                        OPCodeEnum op)+{+    JSValue op1, op2;+    int res;++    op1 = sp[-2];+    op2 = sp[-1];+    op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);+    if (JS_IsException(op1)) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        goto exception;+    }+    if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&+        JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {+        JSString *p1, *p2;+        p1 = JS_VALUE_GET_STRING(op1);+        p2 = JS_VALUE_GET_STRING(op2);+        res = js_string_compare(ctx, p1, p2);+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        switch(op) {+        case OP_lt:+            res = (res < 0);+            break;+        case OP_lte:+            res = (res <= 0);+            break;+        case OP_gt:+            res = (res > 0);+            break;+        default:+        case OP_gte:+            res = (res >= 0);+            break;+        }+    } else {+        double d1, d2;+        if (JS_ToFloat64Free(ctx, &d1, op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        if (JS_ToFloat64Free(ctx, &d2, op2))+            goto exception;+        switch(op) {+        case OP_lt:+            res = (d1 < d2); /* if NaN return false */+            break;+        case OP_lte:+            res = (d1 <= d2); /* if NaN return false */+            break;+        case OP_gt:+            res = (d1 > d2); /* if NaN return false */+            break;+        default:+        case OP_gte:+            res = (d1 >= d2); /* if NaN return false */+            break;+        }+    }+    sp[-2] = JS_NewBool(ctx, res);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,+                                            BOOL is_neq)+{+    JSValue op1, op2;+    int tag1, tag2;+    BOOL res;++    op1 = sp[-2];+    op2 = sp[-1];+ redo:+    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);+    if (tag1 == tag2 ||+        (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||+        (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {+        res = js_strict_eq(ctx, op1, op2);+    } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||+               (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {+        res = TRUE;+    } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||+                                   tag2 == JS_TAG_FLOAT64)) ||+        (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||+                                   tag1 == JS_TAG_FLOAT64))) {+        double d1;+        double d2;+        if (JS_ToFloat64Free(ctx, &d1, op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        if (JS_ToFloat64Free(ctx, &d2, op2))+            goto exception;+        res = (d1 == d2);+    } else if (tag1 == JS_TAG_BOOL) {+        op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));+        goto redo;+    } else if (tag2 == JS_TAG_BOOL) {+        op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));+        goto redo;+    } else if (tag1 == JS_TAG_OBJECT &&+               (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {+        op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+        if (JS_IsException(op1)) {+            JS_FreeValue(ctx, op2);+            goto exception;+        }+        goto redo;+    } else if (tag2 == JS_TAG_OBJECT &&+               (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {+        op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+        if (JS_IsException(op2)) {+            JS_FreeValue(ctx, op1);+            goto exception;+        }+        goto redo;+    } else {+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        res = FALSE;+    }+    sp[-2] = JS_NewBool(ctx, res ^ is_neq);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2;+    uint32_t v1, v2, r;++    op1 = sp[-2];+    op2 = sp[-1];+    if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {+        JS_FreeValue(ctx, op2);+        goto exception;+    }+    if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))+        goto exception;+    r = v1 >> (v2 & 0x1f);+    sp[-2] = JS_NewUint32(ctx, r);+    return 0;+ exception:+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++#endif /* !CONFIG_BIGNUM */++/* XXX: Should take JSValueConst arguments */+static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,+                          JSStrictEqModeEnum eq_mode)+{+    BOOL res;+    int tag1, tag2;+    double d1, d2;++    tag1 = JS_VALUE_GET_NORM_TAG(op1);+    tag2 = JS_VALUE_GET_NORM_TAG(op2);+    switch(tag1) {+    case JS_TAG_BOOL:+        if (tag1 != tag2) {+            res = FALSE;+        } else {+            res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);+            goto done_no_free;+        }+        break;+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        res = (tag1 == tag2);+        break;+    case JS_TAG_STRING:+        {+            JSString *p1, *p2;+            if (tag1 != tag2) {+                res = FALSE;+            } else {+                p1 = JS_VALUE_GET_STRING(op1);+                p2 = JS_VALUE_GET_STRING(op2);+                res = (js_string_compare(ctx, p1, p2) == 0);+            }+        }+        break;+    case JS_TAG_SYMBOL:+        {+            JSAtomStruct *p1, *p2;+            if (tag1 != tag2) {+                res = FALSE;+            } else {+                p1 = JS_VALUE_GET_PTR(op1);+                p2 = JS_VALUE_GET_PTR(op2);+                res = (p1 == p2);+            }+        }+        break;+    case JS_TAG_OBJECT:+        if (tag1 != tag2)+            res = FALSE;+        else+            res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);+        break;+    case JS_TAG_INT:+        d1 = JS_VALUE_GET_INT(op1);+        if (tag2 == JS_TAG_INT) {+            d2 = JS_VALUE_GET_INT(op2);+            goto number_test;+        } else if (tag2 == JS_TAG_FLOAT64) {+            d2 = JS_VALUE_GET_FLOAT64(op2);+            goto number_test;+        } else {+            res = FALSE;+        }+        break;+    case JS_TAG_FLOAT64:+        d1 = JS_VALUE_GET_FLOAT64(op1);+        if (tag2 == JS_TAG_FLOAT64) {+            d2 = JS_VALUE_GET_FLOAT64(op2);+        } else if (tag2 == JS_TAG_INT) {+            d2 = JS_VALUE_GET_INT(op2);+        } else {+            res = FALSE;+            break;+        }+    number_test:+        if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {+            JSFloat64Union u1, u2;+            /* NaN is not always normalized, so this test is necessary */+            if (isnan(d1) || isnan(d2)) {+                res = isnan(d1) == isnan(d2);+            } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {+                res = (d1 == d2); /* +0 == -0 */+            } else {+                u1.d = d1;+                u2.d = d2;+                res = (u1.u64 == u2.u64); /* +0 != -0 */+            }+        } else {+            res = (d1 == d2); /* if NaN return false and +0 == -0 */+        }+        goto done_no_free;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        {+            bf_t a_s, *a, b_s, *b;+            if (tag1 != tag2) {+                res = FALSE;+                break;+            }+            a = JS_ToBigFloat(ctx, &a_s, op1);+            b = JS_ToBigFloat(ctx, &b_s, op2);+            res = bf_cmp_eq(a, b);+            if (a == &a_s)+                bf_delete(a);+            if (b == &b_s)+                bf_delete(b);+        }+        break;+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p1, *p2;+            const bf_t *a, *b;+            if (tag1 != tag2) {+                res = FALSE;+                break;+            }+            p1 = JS_VALUE_GET_PTR(op1);+            p2 = JS_VALUE_GET_PTR(op2);+            a = &p1->num;+            b = &p2->num;+            if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {+                if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&+                           a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {+                    res = TRUE;+                } else {+                    res = (bf_cmp_full(a, b) == 0);+                }+            } else {+                res = bf_cmp_eq(a, b);+            }+        }+        break;+    case JS_TAG_BIG_DECIMAL:+        {+            JSBigDecimal *p1, *p2;+            const bfdec_t *a, *b;+            if (tag1 != tag2) {+                res = FALSE;+                break;+            }+            p1 = JS_VALUE_GET_PTR(op1);+            p2 = JS_VALUE_GET_PTR(op2);+            a = &p1->num;+            b = &p2->num;+            res = bfdec_cmp_eq(a, b);+        }+        break;+#endif+    default:+        res = FALSE;+        break;+    }+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+ done_no_free:+    return res;+}++static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)+{+    return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);+}++static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)+{+    return js_strict_eq2(ctx,+                         JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),+                         JS_EQ_SAME_VALUE);+}++static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)+{+    return js_strict_eq2(ctx,+                         JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),+                         JS_EQ_SAME_VALUE_ZERO);+}++static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,+                                       BOOL is_neq)+{+    BOOL res;+    res = js_strict_eq(ctx, sp[-2], sp[-1]);+    sp[-2] = JS_NewBool(ctx, res ^ is_neq);+    return 0;+}++static __exception int js_operator_in(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2;+    JSAtom atom;+    int ret;++    op1 = sp[-2];+    op2 = sp[-1];++    if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {+        JS_ThrowTypeError(ctx, "invalid 'in' operand");+        return -1;+    }+    atom = JS_ValueToAtom(ctx, op1);+    if (unlikely(atom == JS_ATOM_NULL))+        return -1;+    ret = JS_HasProperty(ctx, op2, atom);+    JS_FreeAtom(ctx, atom);+    if (ret < 0)+        return -1;+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    sp[-2] = JS_NewBool(ctx, ret);+    return 0;+}++static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,+                                         JSAtom atom)+{+    JSValue arr, val;+    int ret;++    arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);+    if (JS_IsException(arr))+        return -1;+    ret = 0;+    if (JS_IsObject(arr)) {+        val = JS_GetProperty(ctx, arr, atom);+        ret = JS_ToBoolFree(ctx, val);+    }+    JS_FreeValue(ctx, arr);+    return ret;+}++static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2;+    BOOL ret;++    op1 = sp[-2];+    op2 = sp[-1];+    ret = JS_IsInstanceOf(ctx, op1, op2);+    if (ret < 0)+        return ret;+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    sp[-2] = JS_NewBool(ctx, ret);+    return 0;+}++static __exception int js_operator_typeof(JSContext *ctx, JSValue op1)+{+    JSAtom atom;+    uint32_t tag;++    tag = JS_VALUE_GET_NORM_TAG(op1);+    switch(tag) {+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        atom = JS_ATOM_bigint;+        break;+    case JS_TAG_BIG_FLOAT:+        atom = JS_ATOM_bigfloat;+        break;+    case JS_TAG_BIG_DECIMAL:+        atom = JS_ATOM_bigdecimal;+        break;+#endif+    case JS_TAG_INT:+    case JS_TAG_FLOAT64:+        atom = JS_ATOM_number;+        break;+    case JS_TAG_UNDEFINED:+        atom = JS_ATOM_undefined;+        break;+    case JS_TAG_BOOL:+        atom = JS_ATOM_boolean;+        break;+    case JS_TAG_STRING:+        atom = JS_ATOM_string;+        break;+    case JS_TAG_OBJECT:+        if (JS_IsFunction(ctx, op1))+            atom = JS_ATOM_function;+        else+            goto obj_type;+        break;+    case JS_TAG_NULL:+    obj_type:+        atom = JS_ATOM_object;+        break;+    case JS_TAG_SYMBOL:+        atom = JS_ATOM_symbol;+        break;+    default:+        atom = JS_ATOM_unknown;+        break;+    }+    return atom;+}++static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)+{+    JSValue op1, op2;+    JSAtom atom;+    int ret;++    op1 = sp[-2];+    op2 = sp[-1];+    atom = JS_ValueToAtom(ctx, op2);+    if (unlikely(atom == JS_ATOM_NULL))+        return -1;+    ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);+    JS_FreeAtom(ctx, atom);+    if (unlikely(ret < 0))+        return -1;+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    sp[-2] = JS_NewBool(ctx, ret);+    return 0;+}++static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    return JS_ThrowTypeError(ctx, "invalid property access");+}++/* XXX: not 100% compatible, but mozilla seems to use a similar+   implementation to ensure that caller in non strict mode does not+   throw (ES5 compatibility) */+static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);+    if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {+        return js_throw_type_error(ctx, this_val, 0, NULL);+    }+    return JS_UNDEFINED;+}++static JSValue js_function_proto_fileName(JSContext *ctx,+                                          JSValueConst this_val)+{+    JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);+    if (b && b->has_debug) {+        return JS_AtomToString(ctx, b->debug.filename);+    }+    return JS_UNDEFINED;+}++static JSValue js_function_proto_lineNumber(JSContext *ctx,+                                            JSValueConst this_val)+{+    JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);+    if (b && b->has_debug) {+        return JS_NewInt32(ctx, b->debug.line_num);+    }+    return JS_UNDEFINED;+}++static int js_arguments_define_own_property(JSContext *ctx,+                                            JSValueConst this_obj,+                                            JSAtom prop, JSValueConst val,+                                            JSValueConst getter, JSValueConst setter, int flags)+{+    JSObject *p;+    uint32_t idx;+    p = JS_VALUE_GET_OBJ(this_obj);+    /* convert to normal array when redefining an existing numeric field */+    if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&+        idx < p->u.array.count) {+        if (convert_fast_array_to_array(ctx, p))+            return -1;+    }+    /* run the default define own property */+    return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,+                             flags | JS_PROP_NO_EXOTIC);+}++static const JSClassExoticMethods js_arguments_exotic_methods = {+    .define_own_property = js_arguments_define_own_property,+};++static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)+{+    JSValue val, *tab;+    JSProperty *pr;+    JSObject *p;+    int i;++    val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                                 JS_CLASS_ARGUMENTS);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_OBJ(val);++    /* add the length field (cannot fail) */+    pr = add_property(ctx, p, JS_ATOM_length,+                      JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    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;+        }+        for(i = 0; i < argc; i++) {+            tab[i] = JS_DupValue(ctx, argv[i]);+        }+    }+    p->u.array.u.values = tab;+    p->u.array.count = argc;++    JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,+                           JS_DupValue(ctx, ctx->array_proto_values),+                           JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+    /* add callee property to throw a TypeError in strict mode */+    JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,+                      ctx->throw_type_error, ctx->throw_type_error,+                      JS_PROP_HAS_GET | JS_PROP_HAS_SET);+    return val;+}++#define GLOBAL_VAR_OFFSET 0x40000000+#define ARGUMENT_VAR_OFFSET 0x20000000++/* legacy arguments object: add references to the function arguments */+static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,+                                         JSValueConst *argv,+                                         JSStackFrame *sf, int arg_count)+{+    JSValue val;+    JSProperty *pr;+    JSObject *p;+    int i;++    val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                                 JS_CLASS_MAPPED_ARGUMENTS);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_OBJ(val);++    /* add the length field (cannot fail) */+    pr = add_property(ctx, p, JS_ATOM_length,+                      JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    pr->u.value = JS_NewInt32(ctx, argc);++    for(i = 0; i < arg_count; i++) {+        JSVarRef *var_ref;+        var_ref = get_var_ref(ctx, sf, i, TRUE);+        if (!var_ref)+            goto fail;+        pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);+        if (!pr) {+            free_var_ref(ctx->rt, var_ref);+            goto fail;+        }+        pr->u.var_ref = var_ref;+    }++    /* the arguments not mapped to the arguments of the function can+       be normal properties */+    for(i = arg_count; i < argc; i++) {+        if (JS_DefinePropertyValueUint32(ctx, val, i,+                                         JS_DupValue(ctx, argv[i]),+                                         JS_PROP_C_W_E) < 0)+            goto fail;+    }++    JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,+                           JS_DupValue(ctx, ctx->array_proto_values),+                           JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+    /* callee returns this function in non strict mode */+    JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,+                           JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),+                           JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+    return val;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)+{+    JSValue val;+    int i, ret;++    val = JS_NewArray(ctx);+    if (JS_IsException(val))+        return val;+    for (i = first; i < argc; i++) {+        ret = JS_DefinePropertyValueUint32(ctx, val, i - first,+                                           JS_DupValue(ctx, argv[i]),+                                           JS_PROP_C_W_E);+        if (ret < 0) {+            JS_FreeValue(ctx, val);+            return JS_EXCEPTION;+        }+    }+    return val;+}++static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)+{+    JSObject *p, *p1;+    JSPropertyEnum *tab_atom;+    int i;+    JSValue enum_obj;+    JSForInIterator *it;+    uint32_t tag, tab_atom_count;++    tag = JS_VALUE_GET_TAG(obj);+    if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {+        obj = JS_ToObjectFree(ctx, obj);+    }++    it = js_malloc(ctx, sizeof(*it));+    if (!it) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);+    if (JS_IsException(enum_obj)) {+        js_free(ctx, it);+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    it->is_array = FALSE;+    it->obj = obj;+    it->idx = 0;+    p = JS_VALUE_GET_OBJ(enum_obj);+    p->u.for_in_iterator = it;++    if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)+        return enum_obj;++    p = JS_VALUE_GET_OBJ(obj);++    /* fast path: assume no enumerable properties in the prototype chain */+    p1 = p->shape->proto;+    while (p1 != NULL) {+        if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p1,+                                   JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))+            goto fail;+        js_free_prop_enum(ctx, tab_atom, tab_atom_count);+        if (tab_atom_count != 0) {+            goto slow_path;+        }+        p1 = p1->shape->proto;+    }+    if (p->fast_array) {+        JSShape *sh;+        JSShapeProperty *prs;+        /* check that there are no enumerable normal fields */+        sh = p->shape;+        for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+            if (prs->flags & JS_PROP_ENUMERABLE)+                goto normal_case;+        }+        /* the implicit GetOwnProperty raises an exception if the+           typed array is detached */+        if ((p->class_id >= JS_CLASS_UINT8C_ARRAY &&+             p->class_id <= JS_CLASS_FLOAT64_ARRAY) &&+            typed_array_is_detached(ctx, p) &&+            typed_array_get_length(ctx, p) != 0) {+            JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+            goto fail;+        }+        /* for fast arrays, we only store the number of elements */+        it->is_array = TRUE;+        it->array_length = p->u.array.count;+    } else {+    normal_case:+        if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,+                                   JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))+            goto fail;+        for(i = 0; i < tab_atom_count; i++) {+            JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);+        }+        js_free_prop_enum(ctx, tab_atom, tab_atom_count);+    }+    return enum_obj;++ slow_path:+    /* non enumerable properties hide the enumerables ones in the+       prototype chain */+    while (p != NULL) {+        if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,+                                   JS_GPN_STRING_MASK | JS_GPN_SET_ENUM))+            goto fail;+        for(i = 0; i < tab_atom_count; i++) {+            JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,+                                   (tab_atom[i].is_enumerable ?+                                    JS_PROP_ENUMERABLE : 0));+        }+        js_free_prop_enum(ctx, tab_atom, tab_atom_count);+        p = p->shape->proto;+    }+    return enum_obj;++ fail:+    JS_FreeValue(ctx, enum_obj);+    return JS_EXCEPTION;+}++/* obj -> enum_obj */+static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)+{+    sp[-1] = build_for_in_iterator(ctx, sp[-1]);+    if (JS_IsException(sp[-1]))+        return -1;+    return 0;+}++/* enum_obj -> enum_obj value done */+static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)+{+    JSValueConst enum_obj;+    JSObject *p;+    JSAtom prop;+    JSForInIterator *it;+    int ret;++    enum_obj = sp[-1];+    /* fail safe */+    if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)+        goto done;+    p = JS_VALUE_GET_OBJ(enum_obj);+    if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)+        goto done;+    it = p->u.for_in_iterator;++    for(;;) {+        if (it->is_array) {+            if (it->idx >= it->array_length)+                goto done;+            prop = __JS_AtomFromUInt32(it->idx);+            it->idx++;+        } else {+            JSShape *sh = p->shape;+            JSShapeProperty *prs;+            if (it->idx >= sh->prop_count)+                goto done;+            prs = get_shape_prop(sh) + it->idx;+            prop = prs->atom;+            it->idx++;+            if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))+                continue;+        }+        /* check if the property was deleted */+        ret = JS_HasProperty(ctx, it->obj, prop);+        if (ret < 0)+            return ret;+        if (ret)+            break;+    }+    /* return the property */+    sp[0] = JS_AtomToValue(ctx, prop);+    sp[1] = JS_FALSE;+    return 0;+ done:+    /* return the end */+    sp[0] = JS_UNDEFINED;+    sp[1] = JS_TRUE;+    return 0;+}++static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,+                               JSValueConst method)+{+    JSValue enum_obj;++    enum_obj = JS_Call(ctx, method, obj, 0, NULL);+    if (JS_IsException(enum_obj))+        return enum_obj;+    if (!JS_IsObject(enum_obj)) {+        JS_FreeValue(ctx, enum_obj);+        return JS_ThrowTypeErrorNotAnObject(ctx);+    }+    return enum_obj;+}++static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)+{+    JSValue method, ret, sync_iter;++    if (is_async) {+        method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);+        if (JS_IsException(method))+            return method;+        if (JS_IsUndefined(method) || JS_IsNull(method)) {+            method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);+            if (JS_IsException(method))+                return method;+            sync_iter = JS_GetIterator2(ctx, obj, method);+            JS_FreeValue(ctx, method);+            if (JS_IsException(sync_iter))+                return sync_iter;+            ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);+            JS_FreeValue(ctx, sync_iter);+            return ret;+        }+    } else {+        method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);+        if (JS_IsException(method))+            return method;+    }+    if (!JS_IsFunction(ctx, method)) {+        JS_FreeValue(ctx, method);+        return JS_ThrowTypeError(ctx, "value is not iterable");+    }+    ret = JS_GetIterator2(ctx, obj, method);+    JS_FreeValue(ctx, method);+    return ret;+}++/* return *pdone = 2 if the iterator object is not parsed */+static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,+                                JSValueConst method,+                                int argc, JSValueConst *argv, int *pdone)+{+    JSValue obj;++    /* fast path for the built-in iterators (avoid creating the+       intermediate result object) */+    if (JS_IsObject(method)) {+        JSObject *p = JS_VALUE_GET_OBJ(method);+        if (p->class_id == JS_CLASS_C_FUNCTION &&+            p->u.cfunc.cproto == JS_CFUNC_iterator_next) {+            JSCFunctionType func;+            JSValueConst args[1];++            /* in case the function expects one argument */+            if (argc == 0) {+                args[0] = JS_UNDEFINED;+                argv = args;+            }+            func = p->u.cfunc.c_function;+            return func.iterator_next(ctx, enum_obj, argc, argv,+                                      pdone, p->u.cfunc.magic);+        }+    }+    obj = JS_Call(ctx, method, enum_obj, argc, argv);+    if (JS_IsException(obj))+        goto fail;+    if (!JS_IsObject(obj)) {+        JS_FreeValue(ctx, obj);+        JS_ThrowTypeError(ctx, "iterator must return an object");+        goto fail;+    }+    *pdone = 2;+    return obj;+ fail:+    *pdone = FALSE;+    return JS_EXCEPTION;+}++static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,+                               JSValueConst method,+                               int argc, JSValueConst *argv, BOOL *pdone)+{+    JSValue obj, value, done_val;+    int done;++    obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);+    if (JS_IsException(obj))+        goto fail;+    if (done != 2) {+        *pdone = done;+        return obj;+    } else {+        done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);+        if (JS_IsException(done_val))+            goto fail;+        *pdone = JS_ToBoolFree(ctx, done_val);+        value = JS_UNDEFINED;+        if (!*pdone) {+            value = JS_GetProperty(ctx, obj, JS_ATOM_value);+        }+        JS_FreeValue(ctx, obj);+        return value;+    }+ fail:+    JS_FreeValue(ctx, obj);+    *pdone = FALSE;+    return JS_EXCEPTION;+}++/* return < 0 in case of exception */+static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,+                            BOOL is_exception_pending)+{+    JSValue method, ret, ex_obj;+    int res;++    if (is_exception_pending) {+        ex_obj = ctx->rt->current_exception;+        ctx->rt->current_exception = JS_NULL;+        res = -1;+    } else {+        ex_obj = JS_UNDEFINED;+        res = 0;+    }+    method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);+    if (JS_IsException(method)) {+        res = -1;+        goto done;+    }+    if (JS_IsUndefined(method) || JS_IsNull(method)) {+        goto done;+    }+    ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);+    if (!is_exception_pending) {+        if (JS_IsException(ret)) {+            res = -1;+        } else if (!JS_IsObject(ret)) {+            JS_ThrowTypeErrorNotAnObject(ctx);+            res = -1;+        }+    }+    JS_FreeValue(ctx, ret);+ done:+    if (is_exception_pending) {+        JS_Throw(ctx, ex_obj);+    }+    return res;+}++/* obj -> enum_rec (3 slots) */+static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,+                                       BOOL is_async)+{+    JSValue op1, obj, method;+    op1 = sp[-1];+    obj = JS_GetIterator(ctx, op1, is_async);+    if (JS_IsException(obj))+        return -1;+    JS_FreeValue(ctx, op1);+    sp[-1] = obj;+    method = JS_GetProperty(ctx, obj, JS_ATOM_next);+    if (JS_IsException(method))+        return -1;+    sp[0] = method;+    return 0;+}++/* enum_rec -> enum_rec value done */+static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)+{+    JSValue value = JS_UNDEFINED;+    int done = 1;++    if (likely(!JS_IsUndefined(sp[offset]))) {+        value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);+        if (JS_IsException(value))+            done = -1;+        if (done) {+            /* value is JS_UNDEFINED or JS_EXCEPTION */+            /* replace the iteration object with undefined */+            JS_FreeValue(ctx, sp[offset]);+            sp[offset] = JS_UNDEFINED;+            if (done < 0)+                return -1;+        }+    }+    sp[0] = value;+    sp[1] = JS_NewBool(ctx, done);+    return 0;+}++static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp)+{+    JSValue result;+    result = JS_Call(ctx, sp[-2], sp[-3], 0, NULL);+    if (JS_IsException(result))+        return -1;+    sp[0] = result;+    return 0;+}++static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,+                                           BOOL *pdone)+{+    JSValue done_val, value;+    BOOL done;+    done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);+    if (JS_IsException(done_val))+        goto fail;+    done = JS_ToBoolFree(ctx, done_val);+    value = JS_GetProperty(ctx, obj, JS_ATOM_value);+    if (JS_IsException(value))+        goto fail;+    *pdone = done;+    return value;+ fail:+    *pdone = FALSE;+    return JS_EXCEPTION;+}++static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)+{+    JSValue obj, value;+    BOOL done;+    obj = sp[-1];+    if (!JS_IsObject(obj)) {+        JS_ThrowTypeError(ctx, "iterator must return an object");+        return -1;+    }+    value = JS_IteratorGetCompleteValue(ctx, obj, &done);+    if (JS_IsException(value))+        return -1;+    JS_FreeValue(ctx, obj);+    sp[-1] = value;+    sp[0] = JS_NewBool(ctx, done);+    return 0;+}++static JSValue js_create_iterator_result(JSContext *ctx,+                                         JSValue val,+                                         BOOL done)+{+    JSValue obj;+    obj = JS_NewObject(ctx);+    if (JS_IsException(obj)) {+        JS_FreeValue(ctx, val);+        return obj;+    }+    if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,+                               val, JS_PROP_C_W_E) < 0) {+        goto fail;+    }+    if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,+                               JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {+    fail:+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    return obj;+}++static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv,+                                      BOOL *pdone, int magic);++static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv, int magic);++static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)+{+    /* Try and handle fast arrays explicitly */+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(obj);+        if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {+            return TRUE;+        }+    }+    return FALSE;+}++/* Access an Array's internal JSValue array if available */+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,+                              JSValue **arrpp, uint32_t *countp)+{+    /* Try and handle fast arrays explicitly */+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(obj);+        if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {+            *countp = p->u.array.count;+            *arrpp = p->u.array.u.values;+            return TRUE;+        }+    }+    return FALSE;+}++static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)+{+    JSValue iterator, enumobj, method, value;+    int pos, is_array_iterator;+    JSValue *arrp;+    uint32_t i, count32;++    if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {+        JS_ThrowInternalError(ctx, "invalid index for append");+        return -1;+    }++    pos = JS_VALUE_GET_INT(sp[-2]);++    /* XXX: further optimisations:+       - use ctx->array_proto_values?+       - check if array_iterator_prototype next method is built-in and+         avoid constructing actual iterator object?+       - build this into js_for_of_start and use in all `for (x of o)` loops+     */+    iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);+    if (JS_IsException(iterator))+        return -1;+    is_array_iterator = JS_IsCFunction(ctx, iterator,+                                       (JSCFunction *)js_create_array_iterator,+                                       JS_ITERATOR_KIND_VALUE);+    JS_FreeValue(ctx, iterator);++    enumobj = JS_GetIterator(ctx, sp[-1], FALSE);+    if (JS_IsException(enumobj))+        return -1;+    method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);+    if (JS_IsException(method)) {+        JS_FreeValue(ctx, enumobj);+        return -1;+    }+    if (is_array_iterator+    &&  JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)+    &&  js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {+        int64_t len;+        /* Handle fast arrays explicitly */+        if (js_get_length64(ctx, &len, sp[-1]))+            goto exception;+        for (i = 0; i < count32; i++) {+            if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,+                                             JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)+                goto exception;+        }+        if (len > count32) {+            /* This is not strictly correct because the trailing elements are+               empty instead of undefined. Append undefined entries instead.+             */+            pos += len - count32;+            if (JS_SetProperty(ctx, sp[-3], JS_ATOM_length, JS_NewUint32(ctx, pos)) < 0)+                goto exception;+        }+    } else {+        for (;;) {+            BOOL done;+            value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);+            if (JS_IsException(value))+                goto exception;+            if (done) {+                /* value is JS_UNDEFINED */+                break;+            }+            if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)+                goto exception;+        }+    }+    sp[-2] = JS_NewInt32(ctx, pos);+    JS_FreeValue(ctx, enumobj);+    JS_FreeValue(ctx, method);+    return 0;++exception:+    JS_IteratorClose(ctx, enumobj, TRUE);+    JS_FreeValue(ctx, enumobj);+    JS_FreeValue(ctx, method);+    return -1;+}++static __exception int JS_CopyDataProperties(JSContext *ctx,+                                             JSValueConst target,+                                             JSValueConst source,+                                             JSValueConst excluded,+                                             BOOL setprop)+{+    JSPropertyEnum *tab_atom;+    JSValue val;+    uint32_t i, tab_atom_count;+    JSObject *p;+    JSObject *pexcl = NULL;+    int ret = 0, flags;++    if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)+        return 0;++    if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)+        pexcl = JS_VALUE_GET_OBJ(excluded);++    p = JS_VALUE_GET_OBJ(source);+    if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,+                               JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK |+                               JS_GPN_ENUM_ONLY))+        return -1;++    flags = JS_PROP_C_W_E;++    for (i = 0; i < tab_atom_count; i++) {+        if (pexcl) {+            ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);+            if (ret) {+                if (ret < 0)+                    break;+                ret = 0;+                continue;+            }+        }+        ret = -1;+        val = JS_GetProperty(ctx, source, tab_atom[i].atom);+        if (JS_IsException(val))+            break;+        if (setprop)+            ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);+        else+            ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, flags);+        if (ret < 0)+            break;+        ret = 0;+    }+    js_free_prop_enum(ctx, tab_atom, tab_atom_count);+    return ret;+}++/* only valid inside C functions */+static JSValueConst JS_GetActiveFunction(JSContext *ctx)+{+    return ctx->rt->current_stack_frame->cur_func;+}++static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,+                             int var_idx, BOOL is_arg)+{+    JSVarRef *var_ref;+    struct list_head *el;++    list_for_each(el, &sf->var_ref_list) {+        var_ref = list_entry(el, JSVarRef, header.link);+        if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {+            var_ref->header.ref_count++;+            return var_ref;+        }+    }+    /* create a new one */+    var_ref = js_malloc(ctx, sizeof(JSVarRef));+    if (!var_ref)+        return NULL;+    var_ref->header.ref_count = 1;+    var_ref->is_detached = FALSE;+    var_ref->is_arg = is_arg;+    var_ref->var_idx = var_idx;+    list_add_tail(&var_ref->header.link, &sf->var_ref_list);+    if (is_arg)+        var_ref->pvalue = &sf->arg_buf[var_idx];+    else+        var_ref->pvalue = &sf->var_buf[var_idx];+    var_ref->value = JS_UNDEFINED;+    return var_ref;+}++static JSValue js_closure2(JSContext *ctx, JSValue func_obj,+                           JSFunctionBytecode *b,+                           JSVarRef **cur_var_refs,+                           JSStackFrame *sf)+{+    JSObject *p;+    JSVarRef **var_refs;+    int i;++    p = JS_VALUE_GET_OBJ(func_obj);+    p->u.func.function_bytecode = b;+    p->u.func.home_object = NULL;+    p->u.func.var_refs = NULL;+    if (b->closure_var_count) {+        var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);+        if (!var_refs)+            goto fail;+        p->u.func.var_refs = var_refs;+        for(i = 0; i < b->closure_var_count; i++) {+            JSClosureVar *cv = &b->closure_var[i];+            JSVarRef *var_ref;+            if (cv->is_local) {+                /* reuse the existing variable reference if it already exists */+                var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);+                if (!var_ref)+                    goto fail;+            } else {+                var_ref = cur_var_refs[cv->var_idx];+                var_ref->header.ref_count++;+            }+            var_refs[i] = var_ref;+        }+    }+    return func_obj;+ fail:+    /* bfunc is freed when func_obj is freed */+    JS_FreeValue(ctx, func_obj);+    return JS_EXCEPTION;+}++static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)+{+    JSValue obj, this_val;+    int ret;++    this_val = JS_MKPTR(JS_TAG_OBJECT, p);+    obj = JS_NewObject(ctx);+    if (JS_IsException(obj))+        return -1;+    set_cycle_flag(ctx, obj);+    set_cycle_flag(ctx, this_val);+    ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,+                                 JS_DupValue(ctx, this_val),+                                 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    if (JS_DefinePropertyValue(ctx, this_val, atom, obj, JS_PROP_WRITABLE) < 0 || ret < 0)+        return -1;+    return 0;+}++static const uint16_t func_kind_to_class_id[] = {+    [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,+    [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,+    [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,+    [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,+};++static JSValue js_closure(JSContext *ctx, JSValue bfunc,+                          JSVarRef **cur_var_refs,+                          JSStackFrame *sf)+{+    JSFunctionBytecode *b;+    JSValue func_obj;+    JSAtom name_atom;++    b = JS_VALUE_GET_PTR(bfunc);+    func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);+    if (JS_IsException(func_obj)) {+        JS_FreeValue(ctx, bfunc);+        return JS_EXCEPTION;+    }+    func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);+    if (JS_IsException(func_obj)) {+        /* bfunc has been freed */+        goto fail;+    }+    name_atom = b->func_name;+    if (name_atom == JS_ATOM_NULL)+        name_atom = JS_ATOM_empty_string;+    js_function_set_properties(ctx, func_obj, name_atom,+                               b->defined_arg_count);++    if (b->func_kind & JS_FUNC_GENERATOR) {+        JSValue proto;+        int proto_class_id;+        /* generators have a prototype field which is used as+           prototype for the generator object */+        if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)+            proto_class_id = JS_CLASS_ASYNC_GENERATOR;+        else+            proto_class_id = JS_CLASS_GENERATOR;+        proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);+        if (JS_IsException(proto))+            goto fail;+        JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,+                               JS_PROP_WRITABLE);+    } else if (b->has_prototype) {+        /* add the 'prototype' property: delay instantiation to avoid+           creating cycles for every javascript function. The prototype+           object is created on the fly when first accessed */+        JS_SetConstructorBit(ctx, func_obj, TRUE);+        JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,+                                  JS_AUTOINIT_ID_PROTOTYPE, NULL,+                                  JS_PROP_WRITABLE);+    }+    return func_obj;+ fail:+    /* bfunc is freed when func_obj is freed */+    JS_FreeValue(ctx, func_obj);+    return JS_EXCEPTION;+}++#define JS_DEFINE_CLASS_HAS_HERITAGE     (1 << 0)++static int js_op_define_class(JSContext *ctx, JSValue *sp,+                              JSAtom class_name, int class_flags,+                              JSVarRef **cur_var_refs,+                              JSStackFrame *sf, BOOL is_computed_name)+{+    JSValue bfunc, parent_class, proto = JS_UNDEFINED;+    JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;+    JSFunctionBytecode *b;++    parent_class = sp[-2];+    bfunc = sp[-1];++    if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {+        if (JS_IsNull(parent_class)) {+            parent_proto = JS_NULL;+            parent_class = JS_DupValue(ctx, ctx->function_proto);+        } else {+            if (!JS_IsConstructor(ctx, parent_class)) {+                JS_ThrowTypeError(ctx, "parent class must be constructor");+                goto fail;+            }+            parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);+            if (JS_IsException(parent_proto))+                goto fail;+            if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {+                JS_ThrowTypeError(ctx, "parent prototype must be an object or null");+                goto fail;+            }+        }+    } else {+        /* parent_class is JS_UNDEFINED in this case */+        parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);+        parent_class = JS_DupValue(ctx, ctx->function_proto);+    }+    proto = JS_NewObjectProto(ctx, parent_proto);+    if (JS_IsException(proto))+        goto fail;++    b = JS_VALUE_GET_PTR(bfunc);+    assert(b->func_kind == JS_FUNC_NORMAL);+    ctor = JS_NewObjectProtoClass(ctx, parent_class,+                                  JS_CLASS_BYTECODE_FUNCTION);+    if (JS_IsException(ctor))+        goto fail;+    ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);+    bfunc = JS_UNDEFINED;+    if (JS_IsException(ctor))+        goto fail;+    js_method_set_home_object(ctx, ctor, proto);+    JS_SetConstructorBit(ctx, ctor, TRUE);++    JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,+                           JS_NewInt32(ctx, b->defined_arg_count),+                           JS_PROP_CONFIGURABLE);++    if (is_computed_name) {+        if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],+                                        JS_PROP_CONFIGURABLE) < 0)+            goto fail;+    } else {+        if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)+            goto fail;+    }++    /* the constructor property must be first. It can be overriden by+       computed property names */+    if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,+                               JS_DupValue(ctx, ctor),+                               JS_PROP_CONFIGURABLE |+                               JS_PROP_WRITABLE | JS_PROP_THROW) < 0)+        goto fail;+    /* set the prototype property */+    if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,+                               JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)+        goto fail;+    set_cycle_flag(ctx, ctor);+    set_cycle_flag(ctx, proto);++    JS_FreeValue(ctx, parent_proto);+    JS_FreeValue(ctx, parent_class);++    sp[-2] = ctor;+    sp[-1] = proto;+    return 0;+ fail:+    JS_FreeValue(ctx, parent_class);+    JS_FreeValue(ctx, parent_proto);+    JS_FreeValue(ctx, bfunc);+    JS_FreeValue(ctx, proto);+    JS_FreeValue(ctx, ctor);+    sp[-2] = JS_UNDEFINED;+    sp[-1] = JS_UNDEFINED;+    return -1;+}++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, header.link);+        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->pvalue = &var_ref->value;+        /* the reference is no longer to a local variable */+        var_ref->is_detached = TRUE;+        add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);+    }+}++static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)+{+    struct list_head *el, *el1;+    JSVarRef *var_ref;+    int var_idx = idx;++    list_for_each_safe(el, el1, &sf->var_ref_list) {+        var_ref = list_entry(el, JSVarRef, header.link);+        if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {+            var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);+            var_ref->pvalue = &var_ref->value;+            list_del(&var_ref->header.link);+            /* the reference is no longer to a local variable */+            var_ref->is_detached = TRUE;+            add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);+        }+    }+}++#define JS_CALL_FLAG_COPY_ARGV   (1 << 1)+#define JS_CALL_FLAG_GENERATOR   (1 << 2)++static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,+                                  JSValueConst this_obj,+                                  int argc, JSValueConst *argv, int flags)+{+    JSRuntime *rt = ctx->rt;+    JSCFunctionType func;+    JSObject *p;+    JSStackFrame sf_s, *sf = &sf_s, *prev_sf;+    JSValue ret_val;+    JSValueConst *arg_buf;+    int arg_count, i;+    JSCFunctionEnum cproto;++    p = JS_VALUE_GET_OBJ(func_obj);+    cproto = p->u.cfunc.cproto;+    arg_count = p->u.cfunc.length;++    /* better to always check stack overflow */+    if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))+        return JS_ThrowStackOverflow(ctx);++    prev_sf = rt->current_stack_frame;+    sf->prev_frame = prev_sf;+    rt->current_stack_frame = sf;+    ctx = p->u.cfunc.realm; /* change the current realm */+    +#ifdef CONFIG_BIGNUM+    /* we only propagate the bignum mode as some runtime functions+       test it */+    if (prev_sf)+        sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;+    else+        sf->js_mode = 0;+#else+    sf->js_mode = 0;+#endif+    sf->cur_func = (JSValue)func_obj;+    sf->arg_count = argc;+    arg_buf = argv;++    if (unlikely(argc < arg_count)) {+        /* ensure that at least argc_count arguments are readable */+        arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);+        for(i = 0; i < argc; i++)+            arg_buf[i] = argv[i];+        for(i = argc; i < arg_count; i++)+            arg_buf[i] = JS_UNDEFINED;+        sf->arg_count = arg_count;+    }+    sf->arg_buf = (JSValue*)arg_buf;++    func = p->u.cfunc.c_function;+    switch(cproto) {+    case JS_CFUNC_constructor:+    case JS_CFUNC_constructor_or_func:+        if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {+            if (cproto == JS_CFUNC_constructor) {+            not_a_constructor:+                ret_val = JS_ThrowTypeError(ctx, "must be called with new");+                break;+            } else {+                this_obj = JS_UNDEFINED;+            }+        }+        /* here this_obj is new_target */+        /* fall thru */+    case JS_CFUNC_generic:+        ret_val = func.generic(ctx, this_obj, argc, arg_buf);+        break;+    case JS_CFUNC_constructor_magic:+    case JS_CFUNC_constructor_or_func_magic:+        if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {+            if (cproto == JS_CFUNC_constructor_magic) {+                goto not_a_constructor;+            } else {+                this_obj = JS_UNDEFINED;+            }+        }+        /* fall thru */+    case JS_CFUNC_generic_magic:+        ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,+                                     p->u.cfunc.magic);+        break;+    case JS_CFUNC_getter:+        ret_val = func.getter(ctx, this_obj);+        break;+    case JS_CFUNC_setter:+        ret_val = func.setter(ctx, this_obj, arg_buf[0]);+        break;+    case JS_CFUNC_getter_magic:+        ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);+        break;+    case JS_CFUNC_setter_magic:+        ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);+        break;+    case JS_CFUNC_f_f:+        {+            double d1;++            if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {+                ret_val = JS_EXCEPTION;+                break;+            }+            ret_val = JS_NewFloat64(ctx, func.f_f(d1));+        }+        break;+    case JS_CFUNC_f_f_f:+        {+            double d1, d2;++            if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {+                ret_val = JS_EXCEPTION;+                break;+            }+            if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {+                ret_val = JS_EXCEPTION;+                break;+            }+            ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));+        }+        break;+    case JS_CFUNC_iterator_next:+        {+            int done;+            ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,+                                         &done, p->u.cfunc.magic);+            if (!JS_IsException(ret_val) && done != 2) {+                ret_val = js_create_iterator_result(ctx, ret_val, done);+            }+        }+        break;+    default:+        abort();+    }++    rt->current_stack_frame = sf->prev_frame;+    return ret_val;+}++static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,+                                      JSValueConst this_obj,+                                      int argc, JSValueConst *argv, int flags)+{+    JSObject *p;+    JSBoundFunction *bf;+    JSValueConst *arg_buf, new_target;+    int arg_count, i;++    p = JS_VALUE_GET_OBJ(func_obj);+    bf = p->u.bound_function;+    arg_count = bf->argc + argc;+    if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))+        return JS_ThrowStackOverflow(ctx);+    arg_buf = alloca(sizeof(JSValue) * arg_count);+    for(i = 0; i < bf->argc; i++) {+        arg_buf[i] = bf->argv[i];+    }+    for(i = 0; i < argc; i++) {+        arg_buf[bf->argc + i] = argv[i];+    }+    if (flags & JS_CALL_FLAG_CONSTRUCTOR) {+        new_target = this_obj;+        if (js_same_value(ctx, func_obj, new_target))+            new_target = bf->func_obj;+        return JS_CallConstructor2(ctx, bf->func_obj, new_target,+                                   arg_count, arg_buf);+    } else {+        return JS_Call(ctx, bf->func_obj, bf->this_val,+                       arg_count, arg_buf);+    }+}++/* argument of OP_special_object */+typedef enum {+    OP_SPECIAL_OBJECT_ARGUMENTS,+    OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,+    OP_SPECIAL_OBJECT_THIS_FUNC,+    OP_SPECIAL_OBJECT_NEW_TARGET,+    OP_SPECIAL_OBJECT_HOME_OBJECT,+    OP_SPECIAL_OBJECT_VAR_OBJECT,+    OP_SPECIAL_OBJECT_IMPORT_META,+} OPSpecialObjectEnum;++#define FUNC_RET_AWAIT      0+#define FUNC_RET_YIELD      1+#define FUNC_RET_YIELD_STAR 2++/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */+static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,+                               JSValueConst this_obj, JSValueConst new_target,+                               int argc, JSValue *argv, int flags)+{+    JSRuntime *rt = caller_ctx->rt;+    JSContext *ctx;+    JSObject *p;+    JSFunctionBytecode *b;+    JSStackFrame sf_s, *sf = &sf_s;+    const uint8_t *pc;+    int opcode, arg_allocated_size, i;+    JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;+    JSVarRef **var_refs;+    size_t alloca_size;++#if !DIRECT_DISPATCH+#define SWITCH(pc)      switch (opcode = *pc++)+#define CASE(op)        case op+#define DEFAULT         default+#define BREAK           break+#else+    static const void * const dispatch_table[256] = {+#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,+#if SHORT_OPCODES+#define def(id, size, n_pop, n_push, f)+#else                                                     +#define def(id, size, n_pop, n_push, f) && case_default,+#endif+#include "quickjs-opcode.h"+        [ OP_COUNT ... 255 ] = &&case_default+    };+#define SWITCH(pc)      goto *dispatch_table[opcode = *pc++];+#define CASE(op)        case_ ## op+#define DEFAULT         case_default+#define BREAK           SWITCH(pc)+#endif++    if (js_poll_interrupts(caller_ctx))+        return JS_EXCEPTION;+    if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {+        if (flags & JS_CALL_FLAG_GENERATOR) {+            JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);+            /* func_obj get contains a pointer to JSFuncAsyncState */+            /* the stack frame is already allocated */+            sf = &s->frame;+            p = JS_VALUE_GET_OBJ(sf->cur_func);+            b = p->u.func.function_bytecode;+            ctx = b->realm;+            var_refs = p->u.func.var_refs;+            local_buf = arg_buf = sf->arg_buf;+            var_buf = sf->var_buf;+            stack_buf = sf->var_buf + b->var_count;+            sp = sf->cur_sp;+            sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */+            pc = sf->cur_pc;+            sf->prev_frame = rt->current_stack_frame;+            rt->current_stack_frame = sf;+            if (s->throw_flag)+                goto exception;+            else+                goto restart;+        } else {+            goto not_a_function;+        }+    }+    p = JS_VALUE_GET_OBJ(func_obj);+    if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {+        JSClassCall *call_func;+        call_func = rt->class_array[p->class_id].call;+        if (!call_func) {+        not_a_function:+            return JS_ThrowTypeError(caller_ctx, "not a function");+        }+        return call_func(caller_ctx, func_obj, this_obj, argc,+                         (JSValueConst *)argv, flags);+    }+    b = p->u.func.function_bytecode;++    if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {+        arg_allocated_size = b->arg_count;+    } else {+        arg_allocated_size = 0;+    }++    alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count ++                                     b->stack_size);+    if (js_check_stack_overflow(rt, alloca_size))+        return JS_ThrowStackOverflow(caller_ctx);++    sf->js_mode = b->js_mode;+    arg_buf = argv;+    sf->arg_count = argc;+    sf->cur_func = (JSValue)func_obj;+    init_list_head(&sf->var_ref_list);+    var_refs = p->u.func.var_refs;++    local_buf = alloca(alloca_size);+    if (unlikely(arg_allocated_size)) {+        int n = min_int(argc, b->arg_count);+        arg_buf = local_buf;+        for(i = 0; i < n; i++)+            arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);+        for(; i < b->arg_count; i++)+            arg_buf[i] = JS_UNDEFINED;+        sf->arg_count = b->arg_count;+    }+    var_buf = local_buf + arg_allocated_size;+    sf->var_buf = var_buf;+    sf->arg_buf = arg_buf;++    for(i = 0; i < b->var_count; i++)+        var_buf[i] = JS_UNDEFINED;++    stack_buf = var_buf + b->var_count;+    sp = stack_buf;+    pc = b->byte_code_buf;+    sf->prev_frame = rt->current_stack_frame;+    rt->current_stack_frame = sf;+    ctx = b->realm; /* set the current realm */+    + restart:+    for(;;) {+        int call_argc;+        JSValue *call_argv;++        SWITCH(pc) {+        CASE(OP_push_i32):+            *sp++ = JS_NewInt32(ctx, get_u32(pc));+            pc += 4;+            BREAK;+        CASE(OP_push_const):+            *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);+            pc += 4;+            BREAK;+#if SHORT_OPCODES+        CASE(OP_push_minus1):+        CASE(OP_push_0):+        CASE(OP_push_1):+        CASE(OP_push_2):+        CASE(OP_push_3):+        CASE(OP_push_4):+        CASE(OP_push_5):+        CASE(OP_push_6):+        CASE(OP_push_7):+            *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);+            BREAK;+        CASE(OP_push_i8):+            *sp++ = JS_NewInt32(ctx, get_i8(pc));+            pc += 1;+            BREAK;+        CASE(OP_push_i16):+            *sp++ = JS_NewInt32(ctx, get_i16(pc));+            pc += 2;+            BREAK;+        CASE(OP_push_const8):+            *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);+            BREAK;+        CASE(OP_fclosure8):+            *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);+            if (unlikely(JS_IsException(sp[-1])))+                goto exception;+            BREAK;+        CASE(OP_push_empty_string):+            *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);+            BREAK;+        CASE(OP_get_length):+            {+                JSValue val;++                val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);+                if (unlikely(JS_IsException(val)))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = val;+            }+            BREAK;+#endif+        CASE(OP_push_atom_value):+            *sp++ = JS_AtomToValue(ctx, get_u32(pc));+            pc += 4;+            BREAK;+        CASE(OP_undefined):+            *sp++ = JS_UNDEFINED;+            BREAK;+        CASE(OP_null):+            *sp++ = JS_NULL;+            BREAK;+        CASE(OP_push_this):+            /* OP_push_this is only called at the start of a function */+            {+                JSValue val;+                if (!(b->js_mode & JS_MODE_STRICT)) {+                    uint32_t tag = JS_VALUE_GET_TAG(this_obj);+                    if (likely(tag == JS_TAG_OBJECT))+                        goto normal_this;+                    if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {+                        val = JS_DupValue(ctx, ctx->global_obj);+                    } else {+                        val = JS_ToObject(ctx, this_obj);+                        if (JS_IsException(val))+                            goto exception;+                    }+                } else {+                normal_this:+                    val = JS_DupValue(ctx, this_obj);+                }+                *sp++ = val;+            }+            BREAK;+        CASE(OP_push_false):+            *sp++ = JS_FALSE;+            BREAK;+        CASE(OP_push_true):+            *sp++ = JS_TRUE;+            BREAK;+        CASE(OP_object):+            *sp++ = JS_NewObject(ctx);+            if (unlikely(JS_IsException(sp[-1])))+                goto exception;+            BREAK;+        CASE(OP_special_object):+            {+                int arg = *pc++;+                switch(arg) {+                case OP_SPECIAL_OBJECT_ARGUMENTS:+                    *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);+                    if (unlikely(JS_IsException(sp[-1])))+                        goto exception;+                    break;+                case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:+                    *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,+                                                      sf, min_int(argc, b->arg_count));+                    if (unlikely(JS_IsException(sp[-1])))+                        goto exception;+                    break;+                case OP_SPECIAL_OBJECT_THIS_FUNC:+                    *sp++ = JS_DupValue(ctx, sf->cur_func);+                    break;+                case OP_SPECIAL_OBJECT_NEW_TARGET:+                    *sp++ = JS_DupValue(ctx, new_target);+                    break;+                case OP_SPECIAL_OBJECT_HOME_OBJECT:+                    {+                        JSObject *p1;+                        p1 = p->u.func.home_object;+                        if (unlikely(!p1))+                            *sp++ = JS_UNDEFINED;+                        else+                            *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+                    }+                    break;+                case OP_SPECIAL_OBJECT_VAR_OBJECT:+                    *sp++ = JS_NewObjectProto(ctx, JS_NULL);+                    if (unlikely(JS_IsException(sp[-1])))+                        goto exception;+                    break;+                case OP_SPECIAL_OBJECT_IMPORT_META:+                    *sp++ = js_import_meta(ctx);+                    if (unlikely(JS_IsException(sp[-1])))+                        goto exception;+                    break;+                default:+                    abort();+                }+            }+            BREAK;+        CASE(OP_rest):+            {+                int first = get_u16(pc);+                pc += 2;+                *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);+                if (unlikely(JS_IsException(sp[-1])))+                    goto exception;+            }+            BREAK;++        CASE(OP_drop):+            JS_FreeValue(ctx, sp[-1]);+            sp--;+            BREAK;+        CASE(OP_nip):+            JS_FreeValue(ctx, sp[-2]);+            sp[-2] = sp[-1];+            sp--;+            BREAK;+        CASE(OP_nip1): /* a b c -> b c */+            JS_FreeValue(ctx, sp[-3]);+            sp[-3] = sp[-2];+            sp[-2] = sp[-1];+            sp--;+            BREAK;+        CASE(OP_dup):+            sp[0] = JS_DupValue(ctx, sp[-1]);+            sp++;+            BREAK;+        CASE(OP_dup2): /* a b -> a b a b */+            sp[0] = JS_DupValue(ctx, sp[-2]);+            sp[1] = JS_DupValue(ctx, sp[-1]);+            sp += 2;+            BREAK;+        CASE(OP_dup3): /* a b c -> a b c a b c */+            sp[0] = JS_DupValue(ctx, sp[-3]);+            sp[1] = JS_DupValue(ctx, sp[-2]);+            sp[2] = JS_DupValue(ctx, sp[-1]);+            sp += 3;+            BREAK;+        CASE(OP_dup1): /* a b -> a a b */+            sp[0] = sp[-1];+            sp[-1] = JS_DupValue(ctx, sp[-2]);+            sp++;+            BREAK;+        CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */+            sp[0] = sp[-1];+            sp[-1] = sp[-2];+            sp[-2] = JS_DupValue(ctx, sp[0]);+            sp++;+            BREAK;+        CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */+            sp[0] = sp[-1];+            sp[-1] = sp[-2];+            sp[-2] = sp[-3];+            sp[-3] = JS_DupValue(ctx, sp[0]);+            sp++;+            BREAK;+        CASE(OP_insert4): /* this obj prop a -> a this obj prop a */+            sp[0] = sp[-1];+            sp[-1] = sp[-2];+            sp[-2] = sp[-3];+            sp[-3] = sp[-4];+            sp[-4] = JS_DupValue(ctx, sp[0]);+            sp++;+            BREAK;+        CASE(OP_perm3): /* obj a b -> a obj b (213) */+            {+                JSValue tmp;+                tmp = sp[-2];+                sp[-2] = sp[-3];+                sp[-3] = tmp;+            }+            BREAK;+        CASE(OP_rot3l): /* x a b -> a b x (231) */+            {+                JSValue tmp;+                tmp = sp[-3];+                sp[-3] = sp[-2];+                sp[-2] = sp[-1];+                sp[-1] = tmp;+            }+            BREAK;+        CASE(OP_rot4l): /* x a b c -> a b c x */+            {+                JSValue tmp;+                tmp = sp[-4];+                sp[-4] = sp[-3];+                sp[-3] = sp[-2];+                sp[-2] = sp[-1];+                sp[-1] = tmp;+            }+            BREAK;+        CASE(OP_rot5l): /* x a b c d -> a b c d x */+            {+                JSValue tmp;+                tmp = sp[-5];+                sp[-5] = sp[-4];+                sp[-4] = sp[-3];+                sp[-3] = sp[-2];+                sp[-2] = sp[-1];+                sp[-1] = tmp;+            }+            BREAK;+        CASE(OP_rot3r): /* a b x -> x a b (312) */+            {+                JSValue tmp;+                tmp = sp[-1];+                sp[-1] = sp[-2];+                sp[-2] = sp[-3];+                sp[-3] = tmp;+            }+            BREAK;+        CASE(OP_perm4): /* obj prop a b -> a obj prop b */+            {+                JSValue tmp;+                tmp = sp[-2];+                sp[-2] = sp[-3];+                sp[-3] = sp[-4];+                sp[-4] = tmp;+            }+            BREAK;+        CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */+            {+                JSValue tmp;+                tmp = sp[-2];+                sp[-2] = sp[-3];+                sp[-3] = sp[-4];+                sp[-4] = sp[-5];+                sp[-5] = tmp;+            }+            BREAK;+        CASE(OP_swap): /* a b -> b a */+            {+                JSValue tmp;+                tmp = sp[-2];+                sp[-2] = sp[-1];+                sp[-1] = tmp;+            }+            BREAK;+        CASE(OP_swap2): /* a b c d -> c d a b */+            {+                JSValue tmp1, tmp2;+                tmp1 = sp[-4];+                tmp2 = sp[-3];+                sp[-4] = sp[-2];+                sp[-3] = sp[-1];+                sp[-2] = tmp1;+                sp[-1] = tmp2;+            }+            BREAK;++        CASE(OP_fclosure):+            {+                JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);+                pc += 4;+                *sp++ = js_closure(ctx, bfunc, var_refs, sf);+                if (unlikely(JS_IsException(sp[-1])))+                    goto exception;+            }+            BREAK;+#if SHORT_OPCODES+        CASE(OP_call0):+        CASE(OP_call1):+        CASE(OP_call2):+        CASE(OP_call3):+            call_argc = opcode - OP_call0;+            goto has_call_argc;+#endif+        CASE(OP_call):+        CASE(OP_tail_call):+            {+                call_argc = get_u16(pc);+                pc += 2;+                goto has_call_argc;+            has_call_argc:+                call_argv = sp - call_argc;+                sf->cur_pc = pc;+                ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,+                                          JS_UNDEFINED, call_argc, call_argv, 0);+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                if (opcode == OP_tail_call)+                    goto done;+                for(i = -1; i < call_argc; i++)+                    JS_FreeValue(ctx, call_argv[i]);+                sp -= call_argc + 1;+                *sp++ = ret_val;+            }+            BREAK;+        CASE(OP_call_constructor):+            {+                call_argc = get_u16(pc);+                pc += 2;+                call_argv = sp - call_argc;+                sf->cur_pc = pc;+                ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],+                                                     call_argv[-1],+                                                     call_argc, call_argv, 0);+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                for(i = -2; i < call_argc; i++)+                    JS_FreeValue(ctx, call_argv[i]);+                sp -= call_argc + 2;+                *sp++ = ret_val;+            }+            BREAK;+        CASE(OP_call_method):+        CASE(OP_tail_call_method):+            {+                call_argc = get_u16(pc);+                pc += 2;+                call_argv = sp - call_argc;+                sf->cur_pc = pc;+                ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],+                                          JS_UNDEFINED, call_argc, call_argv, 0);+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                if (opcode == OP_tail_call_method)+                    goto done;+                for(i = -2; i < call_argc; i++)+                    JS_FreeValue(ctx, call_argv[i]);+                sp -= call_argc + 2;+                *sp++ = ret_val;+            }+            BREAK;+        CASE(OP_array_from):+            {+                int i, ret;++                call_argc = get_u16(pc);+                pc += 2;+                ret_val = JS_NewArray(ctx);+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                call_argv = sp - call_argc;+                for(i = 0; i < call_argc; i++) {+                    ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],+                                                 JS_PROP_C_W_E | JS_PROP_THROW);+                    call_argv[i] = JS_UNDEFINED;+                    if (ret < 0) {+                        JS_FreeValue(ctx, ret_val);+                        goto exception;+                    }+                }+                sp -= call_argc;+                *sp++ = ret_val;+            }+            BREAK;++        CASE(OP_apply):+            {+                int magic;+                magic = get_u16(pc);+                pc += 2;++                ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                JS_FreeValue(ctx, sp[-3]);+                JS_FreeValue(ctx, sp[-2]);+                JS_FreeValue(ctx, sp[-1]);+                sp -= 3;+                *sp++ = ret_val;+            }+            BREAK;+        CASE(OP_return):+            ret_val = *--sp;+            goto done;+        CASE(OP_return_undef):+            ret_val = JS_UNDEFINED;+            goto done;++        CASE(OP_check_ctor_return):+            /* return TRUE if 'this' should be returned */+            if (!JS_IsObject(sp[-1])) {+                if (!JS_IsUndefined(sp[-1])) {+                    JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");+                    goto exception;+                }+                sp[0] = JS_TRUE;+            } else {+                sp[0] = JS_FALSE;+            }+            sp++;+            BREAK;+        CASE(OP_check_ctor):+            if (JS_IsUndefined(new_target)) {+                JS_ThrowTypeError(caller_ctx, "class constructors must be invoked with 'new'");+                goto exception;+            }+            BREAK;+        CASE(OP_check_brand):+            if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)+                goto exception;+            BREAK;+        CASE(OP_add_brand):+            if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)+                goto exception;+            JS_FreeValue(ctx, sp[-2]);+            JS_FreeValue(ctx, sp[-1]);+            sp -= 2;+            BREAK;+            +        CASE(OP_throw):+            JS_Throw(ctx, *--sp);+            goto exception;++        CASE(OP_throw_var):+#define JS_THROW_VAR_RO             0+#define JS_THROW_VAR_REDECL         1+#define JS_THROW_VAR_UNINITIALIZED  2+#define JS_THROW_VAR_DELETE_SUPER   3+            {+                JSAtom atom;+                int type;+                atom = get_u32(pc);+                type = pc[4];+                pc += 5;+                if (type == JS_THROW_VAR_RO)+                    JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);+                else+                if (type == JS_THROW_VAR_REDECL)+                    JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);+                else+                if (type == JS_THROW_VAR_UNINITIALIZED)+                    JS_ThrowReferenceErrorUninitialized(ctx, atom);+                else+                if (type == JS_THROW_VAR_DELETE_SUPER)+                    JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");+                else+                    JS_ThrowInternalError(ctx, "invalid throw var type %d", type);+            }+            goto exception;++        CASE(OP_eval):+            {+                JSValueConst obj;+                int scope_idx;+                call_argc = get_u16(pc);+                scope_idx = get_u16(pc + 2) - 1;+                pc += 4;+                call_argv = sp - call_argc;+                sf->cur_pc = pc;+                if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {+                    if (call_argc >= 1)+                        obj = call_argv[0];+                    else+                        obj = JS_UNDEFINED;+                    ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,+                                            JS_EVAL_TYPE_DIRECT, scope_idx);+                } else {+                    ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,+                                              JS_UNDEFINED, call_argc, call_argv, 0);+                }+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                for(i = -1; i < call_argc; i++)+                    JS_FreeValue(ctx, call_argv[i]);+                sp -= call_argc + 1;+                *sp++ = ret_val;+            }+            BREAK;+            /* could merge with OP_apply */+        CASE(OP_apply_eval):+            {+                int scope_idx;+                uint32_t len;+                JSValue *tab;+                JSValueConst obj;++                scope_idx = get_u16(pc) - 1;+                pc += 2;+                tab = build_arg_list(ctx, &len, sp[-1]);+                if (!tab)+                    goto exception;+                if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {+                    if (len >= 1)+                        obj = tab[0];+                    else+                        obj = JS_UNDEFINED;+                    ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,+                                            JS_EVAL_TYPE_DIRECT, scope_idx);+                } else {+                    ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,+                                      (JSValueConst *)tab);+                }+                free_arg_list(ctx, tab, len);+                if (unlikely(JS_IsException(ret_val)))+                    goto exception;+                JS_FreeValue(ctx, sp[-2]);+                JS_FreeValue(ctx, sp[-1]);+                sp -= 2;+                *sp++ = ret_val;+            }+            BREAK;++        CASE(OP_regexp):+            {+                sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,+                                                        sp[-2], sp[-1]);+                sp--;+            }+            BREAK;++        CASE(OP_get_super):+            {+                JSValue proto;+                proto = JS_GetPrototype(ctx, sp[-1]);+                if (JS_IsException(proto))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = proto;+            }+            BREAK;++        CASE(OP_import):+            {+                JSValue val;+                val = js_dynamic_import(ctx, sp[-1]);+                if (JS_IsException(val))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = val;+            }+            BREAK;++        CASE(OP_check_var):+            {+                int ret;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                ret = JS_CheckGlobalVar(ctx, atom);+                if (ret < 0)+                    goto exception;+                *sp++ = JS_NewBool(ctx, ret);+            }+            BREAK;++        CASE(OP_get_var_undef):+        CASE(OP_get_var):+            {+                JSValue val;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);+                if (unlikely(JS_IsException(val)))+                    goto exception;+                *sp++ = val;+            }+            BREAK;++        CASE(OP_put_var):+        CASE(OP_put_var_init):+            {+                int ret;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);+                sp--;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_put_var_strict):+            {+                int ret;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                /* sp[-2] is JS_TRUE or JS_FALSE */+                if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {+                    JS_ThrowReferenceErrorNotDefined(ctx, atom);+                    goto exception;+                }+                ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);+                sp -= 2;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_check_define_var):+            {+                JSAtom atom;+                int flags;+                atom = get_u32(pc);+                flags = pc[4];+                pc += 5;+                if (JS_CheckDefineGlobalVar(ctx, atom, flags))+                    goto exception;+            }+            BREAK;+        CASE(OP_define_var):+            {+                JSAtom atom;+                int flags;+                atom = get_u32(pc);+                flags = pc[4];+                pc += 5;+                if (JS_DefineGlobalVar(ctx, atom, flags))+                    goto exception;+            }+            BREAK;+        CASE(OP_define_func):+            {+                JSAtom atom;+                int flags;+                atom = get_u32(pc);+                flags = pc[4];+                pc += 5;+                if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp--;+            }+            BREAK;++        CASE(OP_get_loc):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                sp[0] = JS_DupValue(ctx, var_buf[idx]);+                sp++;+            }+            BREAK;+        CASE(OP_put_loc):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, &var_buf[idx], sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_set_loc):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));+            }+            BREAK;+        CASE(OP_get_arg):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                sp[0] = JS_DupValue(ctx, arg_buf[idx]);+                sp++;+            }+            BREAK;+        CASE(OP_put_arg):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, &arg_buf[idx], sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_set_arg):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));+            }+            BREAK;++#if SHORT_OPCODES+        CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;+        CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;+        CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;++        CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;+        CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;+        CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;+        CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;+        CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;+        CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;+        CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;+        CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;+        CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;+        CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;+        CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;+        CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;+        CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;+        CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;+        CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;+        CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;+        CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;+        CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;+        CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;+        CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;+        CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;+        CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;+        CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;+        CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;+        CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+        CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+#endif++        CASE(OP_get_var_ref):+            {+                int idx;+                JSValue val;+                idx = get_u16(pc);+                pc += 2;+                val = *var_refs[idx]->pvalue;+                sp[0] = JS_DupValue(ctx, val);+                sp++;+            }+            BREAK;+        CASE(OP_put_var_ref):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, var_refs[idx]->pvalue, sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_set_var_ref):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));+            }+            BREAK;+        CASE(OP_get_var_ref_check):+            {+                int idx;+                JSValue val;+                idx = get_u16(pc);+                pc += 2;+                val = *var_refs[idx]->pvalue;+                if (unlikely(JS_IsUninitialized(val))) {+                    JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);+                    goto exception;+                }+                sp[0] = JS_DupValue(ctx, val);+                sp++;+            }+            BREAK;+        CASE(OP_put_var_ref_check):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {+                    JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);+                    goto exception;+                }+                set_value(ctx, var_refs[idx]->pvalue, sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_put_var_ref_check_init):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {+                    JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);+                    goto exception;+                }+                set_value(ctx, var_refs[idx]->pvalue, sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_set_loc_uninitialized):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);+            }+            BREAK;+        CASE(OP_get_loc_check):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                if (unlikely(JS_IsUninitialized(var_buf[idx]))) {+                    JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);+                    goto exception;+                }+                sp[0] = JS_DupValue(ctx, var_buf[idx]);+                sp++;+            }+            BREAK;+        CASE(OP_put_loc_check):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                if (unlikely(JS_IsUninitialized(var_buf[idx]))) {+                    JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);+                    goto exception;+                }+                set_value(ctx, &var_buf[idx], sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_put_loc_check_init):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {+                    JS_ThrowReferenceError(ctx, "'this' can be initialized only once");+                    goto exception;+                }+                set_value(ctx, &var_buf[idx], sp[-1]);+                sp--;+            }+            BREAK;+        CASE(OP_close_loc):+            {+                int idx;+                idx = get_u16(pc);+                pc += 2;+                close_lexical_var(ctx, sf, idx, FALSE);+            }+            BREAK;++        CASE(OP_make_loc_ref):+        CASE(OP_make_arg_ref):+        CASE(OP_make_var_ref_ref):+            {+                JSVarRef *var_ref;+                JSProperty *pr;+                JSAtom atom;+                int idx;+                atom = get_u32(pc);+                idx = get_u16(pc + 4);+                pc += 6;+                *sp++ = JS_NewObjectProto(ctx, JS_NULL);+                if (unlikely(JS_IsException(sp[-1])))+                    goto exception;+                if (opcode == OP_make_var_ref_ref) {+                    var_ref = var_refs[idx];+                    var_ref->header.ref_count++;+                } else {+                    var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);+                    if (!var_ref)+                        goto exception;+                }+                pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,+                                  JS_PROP_WRITABLE | JS_PROP_VARREF);+                if (!pr) {+                    free_var_ref(rt, var_ref);+                    goto exception;+                }+                pr->u.var_ref = var_ref;+                *sp++ = JS_AtomToValue(ctx, atom);+            }+            BREAK;+        CASE(OP_make_var_ref):+            {+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                if (JS_GetGlobalVarRef(ctx, atom, sp))+                    goto exception;+                sp += 2;+            }+            BREAK;++        CASE(OP_goto):+            pc += (int32_t)get_u32(pc);+            if (unlikely(js_poll_interrupts(ctx)))+                goto exception;+            BREAK;+#if SHORT_OPCODES+        CASE(OP_goto16):+            pc += (int16_t)get_u16(pc);+            if (unlikely(js_poll_interrupts(ctx)))+                goto exception;+            BREAK;+        CASE(OP_goto8):+            pc += (int8_t)pc[0];+            if (unlikely(js_poll_interrupts(ctx)))+                goto exception;+            BREAK;+#endif+        CASE(OP_if_true):+            {+                int res;+                JSValue op1;++                op1 = sp[-1];+                pc += 4;+                if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+                    res = JS_VALUE_GET_INT(op1);+                } else {+                    res = JS_ToBoolFree(ctx, op1);+                }+                sp--;+                if (res) {+                    pc += (int32_t)get_u32(pc - 4) - 4;+                }+                if (unlikely(js_poll_interrupts(ctx)))+                    goto exception;+            }+            BREAK;+        CASE(OP_if_false):+            {+                int res;+                JSValue op1;++                op1 = sp[-1];+                pc += 4;+                if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+                    res = JS_VALUE_GET_INT(op1);+                } else {+                    res = JS_ToBoolFree(ctx, op1);+                }+                sp--;+                if (!res) {+                    pc += (int32_t)get_u32(pc - 4) - 4;+                }+                if (unlikely(js_poll_interrupts(ctx)))+                    goto exception;+            }+            BREAK;+#if SHORT_OPCODES+        CASE(OP_if_true8):+            {+                int res;+                JSValue op1;++                op1 = sp[-1];+                pc += 1;+                if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+                    res = JS_VALUE_GET_INT(op1);+                } else {+                    res = JS_ToBoolFree(ctx, op1);+                }+                sp--;+                if (res) {+                    pc += (int8_t)pc[-1] - 1;+                }+                if (unlikely(js_poll_interrupts(ctx)))+                    goto exception;+            }+            BREAK;+        CASE(OP_if_false8):+            {+                int res;+                JSValue op1;++                op1 = sp[-1];+                pc += 1;+                if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+                    res = JS_VALUE_GET_INT(op1);+                } else {+                    res = JS_ToBoolFree(ctx, op1);+                }+                sp--;+                if (!res) {+                    pc += (int8_t)pc[-1] - 1;+                }+                if (unlikely(js_poll_interrupts(ctx)))+                    goto exception;+            }+            BREAK;+#endif+        CASE(OP_catch):+            {+                int32_t diff;+                diff = get_u32(pc);+                sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);+                sp++;+                pc += 4;+            }+            BREAK;+        CASE(OP_gosub):+            {+                int32_t diff;+                diff = get_u32(pc);+                /* XXX: should have a different tag to avoid security flaw */+                sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);+                sp++;+                pc += diff;+            }+            BREAK;+        CASE(OP_ret):+            {+                JSValue op1;+                uint32_t pos;+                op1 = sp[-1];+                if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))+                    goto ret_fail;+                pos = JS_VALUE_GET_INT(op1);+                if (unlikely(pos >= b->byte_code_len)) {+                ret_fail:+                    JS_ThrowInternalError(ctx, "invalid ret value");+                    goto exception;+                }+                sp--;+                pc = b->byte_code_buf + pos;+            }+            BREAK;++        CASE(OP_for_in_start):+            if (js_for_in_start(ctx, sp))+                goto exception;+            BREAK;+        CASE(OP_for_in_next):+            if (js_for_in_next(ctx, sp))+                goto exception;+            sp += 2;+            BREAK;+        CASE(OP_for_of_start):+            if (js_for_of_start(ctx, sp, FALSE))+                goto exception;+            sp += 1;+            *sp++ = JS_NewCatchOffset(ctx, 0);+            BREAK;+        CASE(OP_for_of_next):+            {+                int offset = -3 - pc[0];+                pc += 1;+                if (js_for_of_next(ctx, sp, offset))+                    goto exception;+                sp += 2;+            }+            BREAK;+        CASE(OP_for_await_of_start):+            if (js_for_of_start(ctx, sp, TRUE))+                goto exception;+            sp += 1;+            *sp++ = JS_NewCatchOffset(ctx, 0);+            BREAK;+        CASE(OP_for_await_of_next):+            if (js_for_await_of_next(ctx, sp))+                goto exception;+            sp += 1;+            BREAK;+        CASE(OP_iterator_get_value_done):+            if (js_iterator_get_value_done(ctx, sp))+                goto exception;+            sp += 1;+            BREAK;++        CASE(OP_iterator_close):+            /* iter_obj next catch_offset -> */+            sp--; /* drop the catch offset to avoid getting caught by exception */+            JS_FreeValue(ctx, sp[-1]); /* drop the next method */+            sp--;+            if (!JS_IsUndefined(sp[-1])) {+                if (JS_IteratorClose(ctx, sp[-1], FALSE))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+            }+            sp--;+            BREAK;+        CASE(OP_iterator_close_return):+            {+                JSValue ret_val;+                /* iter_obj next catch_offset ... ret_val ->+                   ret_eval iter_obj next catch_offset */+                ret_val = *--sp;+                while (sp > stack_buf &&+                       JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {+                    JS_FreeValue(ctx, *--sp);+                }+                if (unlikely(sp < stack_buf + 3)) {+                    JS_ThrowInternalError(ctx, "iterator_close_return");+                    JS_FreeValue(ctx, ret_val);+                    goto exception;+                }+                sp[0] = sp[-1];+                sp[-1] = sp[-2];+                sp[-2] = sp[-3];+                sp[-3] = ret_val;+                sp++;+            }+            BREAK;++        CASE(OP_async_iterator_close):+            /* iter_obj next catch_offset -> value flag */+            {+                JSValue ret, method;+                int ret_flag;+                sp--; /* remove the catch offset */+                method = JS_GetProperty(ctx, sp[-2], JS_ATOM_return);+                if (JS_IsException(method))+                    goto exception;+                if (JS_IsUndefined(method) || JS_IsNull(method)) {+                    ret = JS_UNDEFINED;+                    ret_flag = TRUE;+                } else {+                    ret = JS_CallFree(ctx, method, sp[-2], 0, NULL);+                    if (JS_IsException(ret))+                        goto exception;+                    if (!JS_IsObject(ret)) {+                        JS_FreeValue(ctx, ret);+                        JS_ThrowTypeErrorNotAnObject(ctx);+                        goto exception;+                    }+                    ret_flag = FALSE;+                }+                JS_FreeValue(ctx, sp[-2]);+                JS_FreeValue(ctx, sp[-1]);+                sp[-2] = ret;+                sp[-1] = JS_NewBool(ctx, ret_flag);+            }+            BREAK;++        CASE(OP_async_iterator_next):+            /* stack: iter_obj next catch_offset val */+            {+                JSValue ret;+                ret = JS_Call(ctx, sp[-3], sp[-4],+                              1, (JSValueConst *)(sp - 1));+                if (JS_IsException(ret))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = ret;+            }+            BREAK;++        CASE(OP_async_iterator_get):+            /* stack: iter_obj next catch_offset val */+            {+                JSValue method, ret;+                BOOL ret_flag;+                int flags;+                flags = *pc++;+                /* XXX: use another opcode such as OP_throw_var */+                if (flags == 2) {+                    JS_ThrowTypeError(ctx, "iterator does not have a throw method");+                    goto exception;+                }+                method = JS_GetProperty(ctx, sp[-4], flags ? JS_ATOM_throw : JS_ATOM_return);+                if (JS_IsException(method))+                    goto exception;+                if (JS_IsUndefined(method) || JS_IsNull(method)) {+                    ret_flag = TRUE;+                } else {+                    ret = JS_CallFree(ctx, method, sp[-4],+                                      1, (JSValueConst *)(sp - 1));+                    if (JS_IsException(ret))+                        goto exception;+                    JS_FreeValue(ctx, sp[-1]);+                    sp[-1] = ret;+                    ret_flag = FALSE;+                }+                sp[0] = JS_NewBool(ctx, ret_flag);+                sp += 1;+            }+            BREAK;++        CASE(OP_lnot):+            {+                int res;+                JSValue op1;++                op1 = sp[-1];+                if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+                    res = JS_VALUE_GET_INT(op1) != 0;+                } else {+                    res = JS_ToBoolFree(ctx, op1);+                }+                sp[-1] = JS_NewBool(ctx, !res);+            }+            BREAK;++        CASE(OP_get_field):+            {+                JSValue val;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                val = JS_GetProperty(ctx, sp[-1], atom);+                if (unlikely(JS_IsException(val)))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = val;+            }+            BREAK;++        CASE(OP_get_field2):+            {+                JSValue val;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                val = JS_GetProperty(ctx, sp[-1], atom);+                if (unlikely(JS_IsException(val)))+                    goto exception;+                *sp++ = val;+            }+            BREAK;++        CASE(OP_put_field):+            {+                int ret;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],+                                             JS_PROP_THROW_STRICT);+                JS_FreeValue(ctx, sp[-2]);+                sp -= 2;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_private_symbol):+            {+                JSAtom atom;+                JSValue val;+                +                atom = get_u32(pc);+                pc += 4;+                val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);+                if (JS_IsException(val))+                    goto exception;+                *sp++ = val;+            }+            BREAK;+            +        CASE(OP_get_private_field):+            {+                JSValue val;++                val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);+                JS_FreeValue(ctx, sp[-1]);+                JS_FreeValue(ctx, sp[-2]);+                sp[-2] = val;+                sp--;+                if (unlikely(JS_IsException(val)))+                    goto exception;+            }+            BREAK;++        CASE(OP_put_private_field):+            {+                int ret;+                ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);+                JS_FreeValue(ctx, sp[-3]);+                JS_FreeValue(ctx, sp[-1]);+                sp -= 3;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_define_private_field):+            {+                int ret;+                ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);+                JS_FreeValue(ctx, sp[-2]);+                sp -= 2;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_define_field):+            {+                int ret;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],+                                             JS_PROP_C_W_E | JS_PROP_THROW);+                sp--;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_set_name):+            {+                int ret;+                JSAtom atom;+                atom = get_u32(pc);+                pc += 4;++                ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;+        CASE(OP_set_name_computed):+            {+                int ret;+                ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;+        CASE(OP_set_proto):+            {+                JSValue proto;+                proto = sp[-1];+                if (JS_IsObject(proto) || JS_IsNull(proto)) {+                    if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)+                        goto exception;+                }+                JS_FreeValue(ctx, proto);+                sp--;+            }+            BREAK;+        CASE(OP_set_home_object):+            js_method_set_home_object(ctx, sp[-1], sp[-2]);+            BREAK;+        CASE(OP_define_method):+        CASE(OP_define_method_computed):+            {+                JSValue getter, setter, value;+                JSValueConst obj;+                JSAtom atom;+                int flags, ret, op_flags;+                BOOL is_computed;+#define OP_DEFINE_METHOD_METHOD 0+#define OP_DEFINE_METHOD_GETTER 1+#define OP_DEFINE_METHOD_SETTER 2+#define OP_DEFINE_METHOD_ENUMERABLE 4++                is_computed = (opcode == OP_define_method_computed);+                if (is_computed) {+                    atom = JS_ValueToAtom(ctx, sp[-2]);+                    if (unlikely(atom == JS_ATOM_NULL))+                        goto exception;+                    opcode += OP_define_method - OP_define_method_computed;+                } else {+                    atom = get_u32(pc);+                    pc += 4;+                }+                op_flags = *pc++;++                obj = sp[-2 - is_computed];+                flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |+                    JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;+                if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)+                    flags |= JS_PROP_ENUMERABLE;+                op_flags &= 3;+                value = JS_UNDEFINED;+                getter = JS_UNDEFINED;+                setter = JS_UNDEFINED;+                if (op_flags == OP_DEFINE_METHOD_METHOD) {+                    value = sp[-1];+                    flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;+                } else if (op_flags == OP_DEFINE_METHOD_GETTER) {+                    getter = sp[-1];+                    flags |= JS_PROP_HAS_GET;+                } else {+                    setter = sp[-1];+                    flags |= JS_PROP_HAS_SET;+                }+                ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);+                if (ret >= 0) {+                    ret = JS_DefineProperty(ctx, obj, atom, value,+                                            getter, setter, flags);+                }+                JS_FreeValue(ctx, sp[-1]);+                if (is_computed) {+                    JS_FreeAtom(ctx, atom);+                    JS_FreeValue(ctx, sp[-2]);+                }+                sp -= 1 + is_computed;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_define_class):+        CASE(OP_define_class_computed):+            {+                int class_flags;+                JSAtom atom;+                +                atom = get_u32(pc);+                class_flags = pc[4];+                pc += 5;+                if (js_op_define_class(ctx, sp, atom, class_flags,+                                       var_refs, sf,+                                       (opcode == OP_define_class_computed)) < 0)+                    goto exception;+            }+            BREAK;++        CASE(OP_get_array_el):+            {+                JSValue val;++                val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);+                JS_FreeValue(ctx, sp[-2]);+                sp[-2] = val;+                sp--;+                if (unlikely(JS_IsException(val)))+                    goto exception;+            }+            BREAK;++        CASE(OP_get_array_el2):+            {+                JSValue val;++                val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);+                sp[-1] = val;+                if (unlikely(JS_IsException(val)))+                    goto exception;+            }+            BREAK;++        CASE(OP_get_ref_value):+            {+                JSValue val;+                if (unlikely(JS_IsUndefined(sp[-2]))) {+                    JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);+                    if (atom != JS_ATOM_NULL) {+                        JS_ThrowReferenceErrorNotDefined(ctx, atom);+                        JS_FreeAtom(ctx, atom);+                    }+                    goto exception;+                }+                val = JS_GetPropertyValue(ctx, sp[-2],+                                          JS_DupValue(ctx, sp[-1]));+                if (unlikely(JS_IsException(val)))+                    goto exception;+                sp[0] = val;+                sp++;+            }+            BREAK;++        CASE(OP_get_super_value):+            {+                JSValue val;+                JSAtom atom;+                atom = JS_ValueToAtom(ctx, sp[-1]);+                if (unlikely(atom == JS_ATOM_NULL))+                    goto exception;+                val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);+                JS_FreeAtom(ctx, atom);+                if (unlikely(JS_IsException(val)))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                JS_FreeValue(ctx, sp[-2]);+                JS_FreeValue(ctx, sp[-3]);+                sp[-3] = val;+                sp -= 2;+            }+            BREAK;++        CASE(OP_put_array_el):+            {+                int ret;++                ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);+                JS_FreeValue(ctx, sp[-3]);+                sp -= 3;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_put_ref_value):+            {+                int ret;+                if (unlikely(JS_IsUndefined(sp[-3]))) {+                    if (is_strict_mode(ctx)) {+                        JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);+                        if (atom != JS_ATOM_NULL) {+                            JS_ThrowReferenceErrorNotDefined(ctx, atom);+                            JS_FreeAtom(ctx, atom);+                        }+                        goto exception;+                    } else {+                        sp[-3] = JS_DupValue(ctx, ctx->global_obj);+                    }+                }+                ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);+                JS_FreeValue(ctx, sp[-3]);+                sp -= 3;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_put_super_value):+            {+                int ret;+                JSAtom atom;+                if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {+                    JS_ThrowTypeErrorNotAnObject(ctx);+                    goto exception;+                }+                atom = JS_ValueToAtom(ctx, sp[-2]);+                if (unlikely(atom == JS_ATOM_NULL))+                    goto exception;+                ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(sp[-3]),+                                            atom, sp[-1], sp[-4],+                                            JS_PROP_THROW_STRICT);+                JS_FreeAtom(ctx, atom);+                JS_FreeValue(ctx, sp[-4]);+                JS_FreeValue(ctx, sp[-3]);+                JS_FreeValue(ctx, sp[-2]);+                sp -= 4;+                if (ret < 0)+                    goto exception;+            }+            BREAK;++        CASE(OP_define_array_el):+            {+                int ret;+                ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],+                                                  JS_PROP_C_W_E | JS_PROP_THROW);+                sp -= 1;+                if (unlikely(ret < 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_append):    /* array pos enumobj -- array pos */+            {+                if (js_append_enumerate(ctx, sp))+                    goto exception;+                JS_FreeValue(ctx, *--sp);+            }+            BREAK;++        CASE(OP_copy_data_properties):    /* target source excludeList */+            {+                /* stack offsets (-1 based):+                   2 bits for target,+                   3 bits for source,+                   2 bits for exclusionList */+                int mask;++                mask = *pc++;+                if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],+                                          sp[-1 - ((mask >> 2) & 7)],+                                          sp[-1 - ((mask >> 5) & 7)], 0))+                    goto exception;+            }+            BREAK;++        CASE(OP_add):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    int64_t r;+                    r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);+                    if (unlikely((int)r != r))+                        goto add_slow;+                    sp[-2] = JS_NewInt32(ctx, r);+                    sp--;+                } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {+                    sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) ++                                             JS_VALUE_GET_FLOAT64(op2));+                    sp--;+                } else {+                add_slow:+                    if (js_add_slow(ctx, sp))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+        CASE(OP_add_loc):+            {+                JSValue ops[2];+                int idx;+                idx = *pc;+                pc += 1;++                ops[0] = var_buf[idx];+                ops[1] = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(ops[0], ops[1]))) {+                    int64_t r;+                    r = (int64_t)JS_VALUE_GET_INT(ops[0]) + JS_VALUE_GET_INT(ops[1]);+                    if (unlikely((int)r != r))+                        goto add_loc_slow;+                    var_buf[idx] = JS_NewInt32(ctx, r);+                    sp--;+                } else if (JS_VALUE_GET_TAG(ops[0]) == JS_TAG_STRING) {+                    sp--;+                    ops[1] = JS_ToPrimitiveFree(ctx, ops[1], HINT_NONE);+                    if (JS_IsException(ops[1])) {+                        goto exception;+                    }+                    /* XXX: should not modify the variable in case of+                       exception */+                    ops[0] = JS_ConcatString(ctx, ops[0], ops[1]);+                    if (JS_IsException(ops[0])) {+                        var_buf[idx] = JS_UNDEFINED;+                        goto exception;+                    }+                    var_buf[idx] = ops[0];+                } else {+                add_loc_slow:+                    /* XXX: should not modify the variable in case of+                       exception */+                    sp--;+                    /* In case of exception, js_add_slow frees ops[0]+                       and ops[1]. */+                    /* XXX: change API */+                    if (js_add_slow(ctx, ops + 2)) {+                        var_buf[idx] = JS_UNDEFINED;+                        goto exception;+                    }+                    var_buf[idx] = ops[0];+                }+            }+            BREAK;+        CASE(OP_sub):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    int64_t r;+                    r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);+                    if (unlikely((int)r != r))+                        goto binary_arith_slow;+                    sp[-2] = JS_NewInt32(ctx, r);+                    sp--;+                } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {+                    sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -+                                             JS_VALUE_GET_FLOAT64(op2));+                    sp--;+                } else {+                    goto binary_arith_slow;+                }+            }+            BREAK;+        CASE(OP_mul):+            {+                JSValue op1, op2;+                double d;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    int32_t v1, v2;+                    int64_t r;+                    v1 = JS_VALUE_GET_INT(op1);+                    v2 = JS_VALUE_GET_INT(op2);+                    r = (int64_t)v1 * v2;+                    if (unlikely((int)r != r)) {+#ifdef CONFIG_BIGNUM+                        if (unlikely(sf->js_mode & JS_MODE_MATH) &&+                            (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))+                            goto binary_arith_slow;+#endif+                        d = (double)r;+                        goto mul_fp_res;+                    }+                    /* need to test zero case for -0 result */+                    if (unlikely(r == 0 && (v1 | v2) < 0)) {+                        d = -0.0;+                        goto mul_fp_res;+                    }+                    sp[-2] = JS_NewInt32(ctx, r);+                    sp--;+                } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {+#ifdef CONFIG_BIGNUM+                    if (unlikely(sf->js_mode & JS_MODE_MATH))+                        goto binary_arith_slow;+#endif+                    d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);+                mul_fp_res:+                    sp[-2] = __JS_NewFloat64(ctx, d);+                    sp--;+                } else {+                    goto binary_arith_slow;+                }+            }+            BREAK;+        CASE(OP_div):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    int v1, v2;+                    if (unlikely(sf->js_mode & JS_MODE_MATH))+                        goto binary_arith_slow;+                    v1 = JS_VALUE_GET_INT(op1);+                    v2 = JS_VALUE_GET_INT(op2);+                    sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);+                    sp--;+                } else {+                    goto binary_arith_slow;+                }+            }+            BREAK;+        CASE(OP_mod):+#ifdef CONFIG_BIGNUM+        CASE(OP_math_mod):+#endif+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    int v1, v2, r;+                    v1 = JS_VALUE_GET_INT(op1);+                    v2 = JS_VALUE_GET_INT(op2);+                    /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =+                       -1 and the cases where the result is -0. */+                    if (unlikely(v1 < 0 || v2 <= 0))+                        goto binary_arith_slow;+                    r = v1 % v2;+                    sp[-2] = JS_NewInt32(ctx, r);+                    sp--;+                } else {+                    goto binary_arith_slow;+                }+            }+            BREAK;+        CASE(OP_pow):+        binary_arith_slow:+            if (js_binary_arith_slow(ctx, sp, opcode))+                goto exception;+            sp--;+            BREAK;++        CASE(OP_plus):+            {+                JSValue op1;+                uint32_t tag;+                op1 = sp[-1];+                tag = JS_VALUE_GET_TAG(op1);+                if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {+                } else {+                    if (js_unary_arith_slow(ctx, sp, opcode))+                        goto exception;+                }+            }+            BREAK;+        CASE(OP_neg):+            {+                JSValue op1;+                uint32_t tag;+                int val;+                double d;+                op1 = sp[-1];+                tag = JS_VALUE_GET_TAG(op1);+                if (tag == JS_TAG_INT) {+                    val = JS_VALUE_GET_INT(op1);+                    /* Note: -0 cannot be expressed as integer */+                    if (unlikely(val == 0)) {+                        d = -0.0;+                        goto neg_fp_res;+                    }+                    if (unlikely(val == INT32_MIN)) {+                        d = -(double)val;+                        goto neg_fp_res;+                    }+                    sp[-1] = JS_NewInt32(ctx, -val);+                } else if (JS_TAG_IS_FLOAT64(tag)) {+                    d = -JS_VALUE_GET_FLOAT64(op1);+                neg_fp_res:+                    sp[-1] = __JS_NewFloat64(ctx, d);+                } else {+                    if (js_unary_arith_slow(ctx, sp, opcode))+                        goto exception;+                }+            }+            BREAK;+        CASE(OP_inc):+            {+                JSValue op1;+                int val;+                op1 = sp[-1];+                if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+                    val = JS_VALUE_GET_INT(op1);+                    if (unlikely(val == INT32_MAX))+                        goto inc_slow;+                    sp[-1] = JS_NewInt32(ctx, val + 1);+                } else {+                inc_slow:+                    if (js_unary_arith_slow(ctx, sp, opcode))+                        goto exception;+                }+            }+            BREAK;+        CASE(OP_dec):+            {+                JSValue op1;+                int val;+                op1 = sp[-1];+                if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+                    val = JS_VALUE_GET_INT(op1);+                    if (unlikely(val == INT32_MIN))+                        goto dec_slow;+                    sp[-1] = JS_NewInt32(ctx, val - 1);+                } else {+                dec_slow:+                    if (js_unary_arith_slow(ctx, sp, opcode))+                        goto exception;+                }+            }+            BREAK;+        CASE(OP_post_inc):+        CASE(OP_post_dec):+            if (js_post_inc_slow(ctx, sp, opcode))+                goto exception;+            sp++;+            BREAK;+        CASE(OP_inc_loc):+            {+                JSValue op1;+                int val;+                int idx;+                idx = *pc;+                pc += 1;++                op1 = var_buf[idx];+                if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+                    val = JS_VALUE_GET_INT(op1);+                    if (unlikely(val == INT32_MAX))+                        goto inc_loc_slow;+                    var_buf[idx] = JS_NewInt32(ctx, val + 1);+                } else {+                inc_loc_slow:+                    if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_inc))+                        goto exception;+                }+            }+            BREAK;+        CASE(OP_dec_loc):+            {+                JSValue op1;+                int val;+                int idx;+                idx = *pc;+                pc += 1;++                op1 = var_buf[idx];+                if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+                    val = JS_VALUE_GET_INT(op1);+                    if (unlikely(val == INT32_MIN))+                        goto dec_loc_slow;+                    var_buf[idx] = JS_NewInt32(ctx, val - 1);+                } else {+                dec_loc_slow:+                    if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_dec))+                        goto exception;+                }+            }+            BREAK;+        CASE(OP_not):+            {+                JSValue op1;+                op1 = sp[-1];+                if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+                    sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));+                } else {+                    if (js_not_slow(ctx, sp))+                        goto exception;+                }+            }+            BREAK;++        CASE(OP_shl):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    uint32_t v1, v2;+                    v1 = JS_VALUE_GET_INT(op1);+                    v2 = JS_VALUE_GET_INT(op2);+#ifdef CONFIG_BIGNUM+                    {+                        int64_t r;+                        if (unlikely(sf->js_mode & JS_MODE_MATH)) {+                            if (v2 > 0x1f)+                                goto shl_slow;+                            r = (int64_t)v1 << v2;+                            if ((int)r != r)+                                goto shl_slow;+                        } else {+                            v2 &= 0x1f;+                        }+                    }+#else+                    v2 &= 0x1f;+#endif+                    sp[-2] = JS_NewInt32(ctx, v1 << v2);+                    sp--;+                } else {+#ifdef CONFIG_BIGNUM+                shl_slow:+#endif+                    if (js_binary_logic_slow(ctx, sp, opcode))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+        CASE(OP_shr):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    uint32_t v2;+                    v2 = JS_VALUE_GET_INT(op2);+                    /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */+                    v2 &= 0x1f;+                    sp[-2] = JS_NewUint32(ctx,+                                          (uint32_t)JS_VALUE_GET_INT(op1) >>+                                          v2);+                    sp--;+                } else {+                    if (js_shr_slow(ctx, sp))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+        CASE(OP_sar):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    uint32_t v2;+                    v2 = JS_VALUE_GET_INT(op2);+#ifdef CONFIG_BIGNUM+                    if (unlikely(v2 > 0x1f)) {+                        if (unlikely(sf->js_mode & JS_MODE_MATH))+                            goto sar_slow;+                        else+                            v2 &= 0x1f;+                    }+#else+                    v2 &= 0x1f;+#endif+                    sp[-2] = JS_NewInt32(ctx,+                                          (int)JS_VALUE_GET_INT(op1) >> v2);+                    sp--;+                } else {+#ifdef CONFIG_BIGNUM+                sar_slow:+#endif+                    if (js_binary_logic_slow(ctx, sp, opcode))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+        CASE(OP_and):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    sp[-2] = JS_NewInt32(ctx,+                                         JS_VALUE_GET_INT(op1) &+                                         JS_VALUE_GET_INT(op2));+                    sp--;+                } else {+                    if (js_binary_logic_slow(ctx, sp, opcode))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+        CASE(OP_or):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    sp[-2] = JS_NewInt32(ctx,+                                         JS_VALUE_GET_INT(op1) |+                                         JS_VALUE_GET_INT(op2));+                    sp--;+                } else {+                    if (js_binary_logic_slow(ctx, sp, opcode))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+        CASE(OP_xor):+            {+                JSValue op1, op2;+                op1 = sp[-2];+                op2 = sp[-1];+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+                    sp[-2] = JS_NewInt32(ctx,+                                         JS_VALUE_GET_INT(op1) ^+                                         JS_VALUE_GET_INT(op2));+                    sp--;+                } else {+                    if (js_binary_logic_slow(ctx, sp, opcode))+                        goto exception;+                    sp--;+                }+            }+            BREAK;+++#define OP_CMP(opcode, binary_op, slow_call)              \+            CASE(opcode):                                 \+                {                                         \+                JSValue op1, op2;                         \+                op1 = sp[-2];                             \+                op2 = sp[-1];                                   \+                if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {           \+                    sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \+                    sp--;                                               \+                } else {                                                \+                    if (slow_call)                                      \+                        goto exception;                                 \+                    sp--;                                               \+                }                                                       \+                }                                                       \+            BREAK++            OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));+            OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));+            OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));+            OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));+            OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));+            OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));+            OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));+            OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));++#ifdef CONFIG_BIGNUM+        CASE(OP_mul_pow10):+            if (rt->bigfloat_ops.mul_pow10(ctx, sp))+                goto exception;+            sp--;+            BREAK;+#endif+        CASE(OP_in):+            if (js_operator_in(ctx, sp))+                goto exception;+            sp--;+            BREAK;+        CASE(OP_instanceof):+            if (js_operator_instanceof(ctx, sp))+                goto exception;+            sp--;+            BREAK;+        CASE(OP_typeof):+            {+                JSValue op1;+                JSAtom atom;++                op1 = sp[-1];+                atom = js_operator_typeof(ctx, op1);+                JS_FreeValue(ctx, op1);+                sp[-1] = JS_AtomToString(ctx, atom);+            }+            BREAK;+        CASE(OP_delete):+            if (js_operator_delete(ctx, sp))+                goto exception;+            sp--;+            BREAK;+        CASE(OP_delete_var):+            {+                JSAtom atom;+                int ret;++                atom = get_u32(pc);+                pc += 4;++                ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);+                if (unlikely(ret < 0))+                    goto exception;+                *sp++ = JS_NewBool(ctx, ret);+            }+            BREAK;++        CASE(OP_to_object):+            if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {+                ret_val = JS_ToObject(ctx, sp[-1]);+                if (JS_IsException(ret_val))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = ret_val;+            }+            BREAK;++        CASE(OP_to_propkey):+            switch (JS_VALUE_GET_TAG(sp[-1])) {+            case JS_TAG_INT:+            case JS_TAG_STRING:+            case JS_TAG_SYMBOL:+                break;+            default:+                ret_val = JS_ToPropertyKey(ctx, sp[-1]);+                if (JS_IsException(ret_val))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = ret_val;+                break;+            }+            BREAK;++        CASE(OP_to_propkey2):+            /* must be tested first */+            if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {+                JS_ThrowTypeError(ctx, "value has no property");+                goto exception;+            }+            switch (JS_VALUE_GET_TAG(sp[-1])) {+            case JS_TAG_INT:+            case JS_TAG_STRING:+            case JS_TAG_SYMBOL:+                break;+            default:+                ret_val = JS_ToPropertyKey(ctx, sp[-1]);+                if (JS_IsException(ret_val))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = ret_val;+                break;+            }+            BREAK;+#if 0+        CASE(OP_to_string):+            if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {+                ret_val = JS_ToString(ctx, sp[-1]);+                if (JS_IsException(ret_val))+                    goto exception;+                JS_FreeValue(ctx, sp[-1]);+                sp[-1] = ret_val;+            }+            BREAK;+#endif+        CASE(OP_with_get_var):+        CASE(OP_with_put_var):+        CASE(OP_with_delete_var):+        CASE(OP_with_make_ref):+        CASE(OP_with_get_ref):+        CASE(OP_with_get_ref_undef):+            {+                JSAtom atom;+                int32_t diff;+                JSValue obj, val;+                int ret, is_with;+                atom = get_u32(pc);+                diff = get_u32(pc + 4);+                is_with = pc[8];+                pc += 9;++                obj = sp[-1];+                ret = JS_HasProperty(ctx, obj, atom);+                if (unlikely(ret < 0))+                    goto exception;+                if (ret) {+                    if (is_with) {+                        ret = js_has_unscopable(ctx, obj, atom);+                        if (unlikely(ret < 0))+                            goto exception;+                        if (ret)+                            goto no_with;+                    }+                    switch (opcode) {+                    case OP_with_get_var:+                        val = JS_GetProperty(ctx, obj, atom);+                        if (unlikely(JS_IsException(val)))+                            goto exception;+                        set_value(ctx, &sp[-1], val);+                        break;+                    case OP_with_put_var:+                        ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],+                                                     JS_PROP_THROW_STRICT);+                        JS_FreeValue(ctx, sp[-1]);+                        sp -= 2;+                        if (unlikely(ret < 0))+                            goto exception;+                        break;+                    case OP_with_delete_var:+                        ret = JS_DeleteProperty(ctx, obj, atom, 0);+                        if (unlikely(ret < 0))+                            goto exception;+                        JS_FreeValue(ctx, sp[-1]);+                        sp[-1] = JS_NewBool(ctx, ret);+                        break;+                    case OP_with_make_ref:+                        /* produce a pair object/propname on the stack */+                        *sp++ = JS_AtomToValue(ctx, atom);+                        break;+                    case OP_with_get_ref:+                        /* produce a pair object/method on the stack */+                        val = JS_GetProperty(ctx, obj, atom);+                        if (unlikely(JS_IsException(val)))+                            goto exception;+                        *sp++ = val;+                        break;+                    case OP_with_get_ref_undef:+                        /* produce a pair undefined/function on the stack */+                        val = JS_GetProperty(ctx, obj, atom);+                        if (unlikely(JS_IsException(val)))+                            goto exception;+                        JS_FreeValue(ctx, sp[-1]);+                        sp[-1] = JS_UNDEFINED;+                        *sp++ = val;+                        break;+                    }+                    pc += diff - 5;+                } else {+                no_with:+                    /* if not jumping, drop the object argument */+                    JS_FreeValue(ctx, sp[-1]);+                    sp--;+                }+            }+            BREAK;++        CASE(OP_await):+            ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);+            goto done_generator;+        CASE(OP_yield):+            ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);+            goto done_generator;+        CASE(OP_yield_star):+        CASE(OP_async_yield_star):+            ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);+            goto done_generator;+        CASE(OP_return_async):+        CASE(OP_initial_yield):+            ret_val = JS_UNDEFINED;+            goto done_generator;++        CASE(OP_nop):+            BREAK;+        CASE(OP_is_undefined_or_null):+            if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||+                JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {+                goto set_true;+            } else {+                goto free_and_set_false;+            }+#if SHORT_OPCODES+        CASE(OP_is_undefined):+            if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {+                goto set_true;+            } else {+                goto free_and_set_false;+            }+        CASE(OP_is_null):+            if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {+                goto set_true;+            } else {+                goto free_and_set_false;+            }+        CASE(OP_is_function):+            if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {+                goto free_and_set_true;+            } else {+                goto free_and_set_false;+            }+        free_and_set_true:+            JS_FreeValue(ctx, sp[-1]);+#endif+        set_true:+            sp[-1] = JS_TRUE;+            BREAK;+        free_and_set_false:+            JS_FreeValue(ctx, sp[-1]);+            sp[-1] = JS_FALSE;+            BREAK;+        CASE(OP_invalid):+        DEFAULT:+            JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",+                                  (int)(pc - b->byte_code_buf - 1), opcode);+            goto exception;+        }+    }+ exception:+    if (is_backtrace_needed(ctx, rt->current_exception)) {+        /* add the backtrace information now (it is not done+           before if the exception happens in a bytecode+           operation */+        sf->cur_pc = pc;+        build_backtrace(ctx, rt->current_exception, NULL, 0, 0);+    }+    if (!JS_IsUncatchableError(ctx, rt->current_exception)) {+        while (sp > stack_buf) {+            JSValue val = *--sp;+            JS_FreeValue(ctx, val);+            if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {+                int pos = JS_VALUE_GET_INT(val);+                if (pos == 0) {+                    /* enumerator: close it with a throw */+                    JS_FreeValue(ctx, sp[-1]); /* drop the next method */+                    sp--;+                    JS_IteratorClose(ctx, sp[-1], TRUE);+                } else {+                    *sp++ = rt->current_exception;+                    rt->current_exception = JS_NULL;+                    pc = b->byte_code_buf + pos;+                    goto restart;+                }+            }+        }+    }+    ret_val = JS_EXCEPTION;+    /* the local variables are freed by the caller in the generator+       case. Hence the label 'done' should never be reached in a+       generator function. */+    if (b->func_kind != JS_FUNC_NORMAL) {+    done_generator:+        sf->cur_pc = pc;+        sf->cur_sp = sp;+    } else {+    done:+        if (unlikely(!list_empty(&sf->var_ref_list))) {+            /* variable references reference the stack: must close them */+            close_var_refs(rt, sf);+        }+        /* free the local variables and stack */+        for(pval = local_buf; pval < sp; pval++) {+            JS_FreeValue(ctx, *pval);+        }+    }+    rt->current_stack_frame = sf->prev_frame;+    return ret_val;+}++JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,+                int argc, JSValueConst *argv)+{+    return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,+                           argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);+}++static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,+                           int argc, JSValueConst *argv)+{+    JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,+                                  argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);+    JS_FreeValue(ctx, func_obj);+    return res;+}++/* warning: the refcount of the context is not incremented. Return+   NULL in case of exception (case of revoked proxy only) */+static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)+{+    JSObject *p;+    JSContext *realm;+    +    if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)+        return ctx;+    p = JS_VALUE_GET_OBJ(func_obj);+    switch(p->class_id) {+    case JS_CLASS_C_FUNCTION:+        realm = p->u.cfunc.realm;+        break;+    case JS_CLASS_BYTECODE_FUNCTION:+    case JS_CLASS_GENERATOR_FUNCTION:+    case JS_CLASS_ASYNC_FUNCTION:+    case JS_CLASS_ASYNC_GENERATOR_FUNCTION:+        {+            JSFunctionBytecode *b;+            b = p->u.func.function_bytecode;+            realm = b->realm;+        }+        break;+    case JS_CLASS_PROXY:+        {+            JSProxyData *s = p->u.opaque;+            if (!s)+                return ctx;+            if (s->is_revoked) {+                JS_ThrowTypeErrorRevokedProxy(ctx);+                return NULL;+            } else {+                realm = JS_GetFunctionRealm(ctx, s->target);+            }+        }+        break;+    case JS_CLASS_BOUND_FUNCTION:+        {+            JSBoundFunction *bf = p->u.bound_function;+            realm = JS_GetFunctionRealm(ctx, bf->func_obj);+        }+        break;+    default:+        realm = ctx;+        break;+    }+    return realm;+}++static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,+                                   int class_id)+{+    JSValue proto, obj;+    JSContext *realm;+    +    if (JS_IsUndefined(ctor)) {+        proto = JS_DupValue(ctx, ctx->class_proto[class_id]);+    } else {+        proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);+        if (JS_IsException(proto))+            return proto;+        if (!JS_IsObject(proto)) {+            JS_FreeValue(ctx, proto);+            realm = JS_GetFunctionRealm(ctx, ctor);+            if (!realm)+                return JS_EXCEPTION;+            proto = JS_DupValue(ctx, realm->class_proto[class_id]);+        }+    }+    obj = JS_NewObjectProtoClass(ctx, proto, class_id);+    JS_FreeValue(ctx, proto);+    return obj;+}++/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */+static JSValue JS_CallConstructorInternal(JSContext *ctx,+                                          JSValueConst func_obj,+                                          JSValueConst new_target,+                                          int argc, JSValue *argv, int flags)+{+    JSObject *p;+    JSFunctionBytecode *b;++    if (js_poll_interrupts(ctx))+        return JS_EXCEPTION;+    flags |= JS_CALL_FLAG_CONSTRUCTOR;+    if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))+        goto not_a_function;+    p = JS_VALUE_GET_OBJ(func_obj);+    if (unlikely(!p->is_constructor))+        return JS_ThrowTypeError(ctx, "not a constructor");+    if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {+        JSClassCall *call_func;+        call_func = ctx->rt->class_array[p->class_id].call;+        if (!call_func) {+        not_a_function:+            return JS_ThrowTypeError(ctx, "not a function");+        }+        return call_func(ctx, func_obj, new_target, argc,+                         (JSValueConst *)argv, flags);+    }++    b = p->u.func.function_bytecode;+    if (b->is_derived_class_constructor) {+        return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);+    } else {+        JSValue obj, ret;+        /* legacy constructor behavior */+        obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);+        if (JS_IsException(obj))+            return JS_EXCEPTION;+        ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);+        if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||+            JS_IsException(ret)) {+            JS_FreeValue(ctx, obj);+            return ret;+        } else {+            JS_FreeValue(ctx, ret);+            return obj;+        }+    }+}++JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,+                            JSValueConst new_target,+                            int argc, JSValueConst *argv)+{+    return JS_CallConstructorInternal(ctx, func_obj, new_target,+                                      argc, (JSValue *)argv,+                                      JS_CALL_FLAG_COPY_ARGV);+}++JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,+                           int argc, JSValueConst *argv)+{+    return JS_CallConstructorInternal(ctx, func_obj, func_obj,+                                      argc, (JSValue *)argv,+                                      JS_CALL_FLAG_COPY_ARGV);+}++JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,+                  int argc, JSValueConst *argv)+{+    JSValue func_obj;+    func_obj = JS_GetProperty(ctx, this_val, atom);+    if (JS_IsException(func_obj))+        return func_obj;+    return JS_CallFree(ctx, func_obj, this_val, argc, argv);+}++static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,+                             int argc, JSValueConst *argv)+{+    JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);+    JS_FreeValue(ctx, this_val);+    return res;+}++/* JSAsyncFunctionState (used by generator and async functions) */+static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,+                                       JSValueConst func_obj, JSValueConst this_obj,+                                       int argc, JSValueConst *argv)+{+    JSObject *p;+    JSFunctionBytecode *b;+    JSStackFrame *sf;+    int local_count, i, arg_buf_len, n;++    sf = &s->frame;+    init_list_head(&sf->var_ref_list);+    p = JS_VALUE_GET_OBJ(func_obj);+    b = p->u.func.function_bytecode;+    sf->js_mode = b->js_mode;+    sf->cur_pc = b->byte_code_buf;+    arg_buf_len = max_int(b->arg_count, argc);+    local_count = arg_buf_len + b->var_count + b->stack_size;+    sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));+    if (!sf->arg_buf)+        return -1;+    sf->cur_func = JS_DupValue(ctx, func_obj);+    s->this_val = JS_DupValue(ctx, this_obj);+    s->argc = argc;+    sf->arg_count = arg_buf_len;+    sf->var_buf = sf->arg_buf + arg_buf_len;+    sf->cur_sp = sf->var_buf + b->var_count;+    for(i = 0; i < argc; i++)+        sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);+    n = arg_buf_len + b->var_count;+    for(i = argc; i < n; i++)+        sf->arg_buf[i] = JS_UNDEFINED;+    return 0;+}++static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,+                            JS_MarkFunc *mark_func)+{+    JSStackFrame *sf;+    JSValue *sp;++    sf = &s->frame;+    JS_MarkValue(rt, sf->cur_func, mark_func);+    JS_MarkValue(rt, s->this_val, mark_func);+    if (sf->cur_sp) {+        /* if the function is running, cur_sp is not known so we+           cannot mark the stack. Marking the variables is not needed+           because a running function cannot be part of a removable+           cycle */+        for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)+            JS_MarkValue(rt, *sp, mark_func);+    }+}++static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)+{+    JSStackFrame *sf;+    JSValue *sp;++    sf = &s->frame;++    /* close the closure variables. */+    close_var_refs(rt, sf);+    +    if (sf->arg_buf) {+        /* cannot free the function if it is running */+        assert(sf->cur_sp != NULL);+        for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {+            JS_FreeValueRT(rt, *sp);+        }+        js_free_rt(rt, sf->arg_buf);+    }+    JS_FreeValueRT(rt, sf->cur_func);+    JS_FreeValueRT(rt, s->this_val);+}++static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)+{+    JSValue func_obj;++    if (js_check_stack_overflow(ctx->rt, 0))+        return JS_ThrowStackOverflow(ctx);++    /* the tag does not matter provided it is not an object */+    func_obj = JS_MKPTR(JS_TAG_INT, s);+    return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,+                           s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);+}+++/* Generators */++typedef enum JSGeneratorStateEnum {+    JS_GENERATOR_STATE_SUSPENDED_START,+    JS_GENERATOR_STATE_SUSPENDED_YIELD,+    JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,+    JS_GENERATOR_STATE_EXECUTING,+    JS_GENERATOR_STATE_COMPLETED,+} JSGeneratorStateEnum;++typedef struct JSGeneratorData {+    JSGeneratorStateEnum state;+    JSAsyncFunctionState func_state;+} JSGeneratorData;++static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)+{+    if (s->state == JS_GENERATOR_STATE_COMPLETED)+        return;+    async_func_free(rt, &s->func_state);+    s->state = JS_GENERATOR_STATE_COMPLETED;+}++static void js_generator_finalizer(JSRuntime *rt, JSValue obj)+{+    JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);++    if (s) {+        free_generator_stack_rt(rt, s);+        js_free_rt(rt, s);+    }+}++static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)+{+    free_generator_stack_rt(ctx->rt, s);+}++static void js_generator_mark(JSRuntime *rt, JSValueConst val,+                              JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSGeneratorData *s = p->u.generator_data;++    if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)+        return;+    async_func_mark(rt, &s->func_state, mark_func);+}++/* XXX: use enum */+#define GEN_MAGIC_NEXT   0+#define GEN_MAGIC_RETURN 1+#define GEN_MAGIC_THROW  2++static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv,+                                 BOOL *pdone, int magic)+{+    JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);+    JSStackFrame *sf;+    JSValue ret, func_ret;+    JSValueConst iter_args[1];++    *pdone = TRUE;+    if (!s)+        return JS_ThrowTypeError(ctx, "not a generator");+    sf = &s->func_state.frame;+ redo:+    switch(s->state) {+    default:+    case JS_GENERATOR_STATE_SUSPENDED_START:+        if (magic == GEN_MAGIC_NEXT) {+            goto exec_no_arg;+        } else {+            free_generator_stack(ctx, s);+            goto done;+        }+        break;+    case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:+        {+            int done;+            JSValue method, iter_obj;++            iter_obj = sf->cur_sp[-2];+            if (magic == GEN_MAGIC_NEXT) {+                method = JS_DupValue(ctx, sf->cur_sp[-1]);+            } else {+                method = JS_GetProperty(ctx, iter_obj,+                                        magic == GEN_MAGIC_RETURN ?+                                        JS_ATOM_return : JS_ATOM_throw);+                if (JS_IsException(method))+                    goto iter_exception;+            }+            if (magic != GEN_MAGIC_NEXT &&+                (JS_IsUndefined(method) || JS_IsNull(method))) {+                /* default action */+                if (magic == GEN_MAGIC_RETURN) {+                    ret = JS_DupValue(ctx, argv[0]);+                    goto iter_done;+                } else {+                    if (JS_IteratorClose(ctx, iter_obj, FALSE))+                        goto iter_exception;+                    JS_ThrowTypeError(ctx, "iterator does not have a throw method");+                    goto iter_exception;+                }+            }+            ret = JS_IteratorNext2(ctx, iter_obj, method, argc, argv, &done);+            JS_FreeValue(ctx, method);+            if (JS_IsException(ret)) {+            iter_exception:+                goto exec_throw;+            }+            /* if not done, the iterator returns the exact object+               returned by 'method' */+            if (done == 2) {+                JSValue done_val, value;+                done_val = JS_GetProperty(ctx, ret, JS_ATOM_done);+                if (JS_IsException(done_val)) {+                    JS_FreeValue(ctx, ret);+                    goto iter_exception;+                }+                done = JS_ToBoolFree(ctx, done_val);+                if (done) {+                    value = JS_GetProperty(ctx, ret, JS_ATOM_value);+                    JS_FreeValue(ctx, ret);+                    if (JS_IsException(value))+                        goto iter_exception;+                    ret = value;+                    goto iter_done;+                } else {+                    *pdone = 2;+                }+            } else {+                if (done) {+                    /* 'yield *' returns the value associated to done = true */+                iter_done:+                    JS_FreeValue(ctx, sf->cur_sp[-2]);+                    JS_FreeValue(ctx, sf->cur_sp[-1]);+                    sf->cur_sp--;+                    goto exec_arg;+                } else {+                    *pdone = FALSE;+                }+            }+            break;+        }+        break;+    case JS_GENERATOR_STATE_SUSPENDED_YIELD:+        /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */+        ret = JS_DupValue(ctx, argv[0]);+        if (magic == GEN_MAGIC_THROW) {+            JS_Throw(ctx, ret);+        exec_throw:+            s->func_state.throw_flag = TRUE;+        } else {+        exec_arg:+            sf->cur_sp[-1] = ret;+            sf->cur_sp[0] = JS_NewBool(ctx, (magic == GEN_MAGIC_RETURN));+            sf->cur_sp++;+        exec_no_arg:+            s->func_state.throw_flag = FALSE;+        }+        s->state = JS_GENERATOR_STATE_EXECUTING;+        func_ret = async_func_resume(ctx, &s->func_state);+        s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;+        if (JS_IsException(func_ret)) {+            /* finalize the execution in case of exception */+            free_generator_stack(ctx, s);+            return func_ret;+        }+        if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {+            if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {+                /* 'yield *' */+                s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;+                iter_args[0] = JS_UNDEFINED;+                argc = 1;+                argv = iter_args;+                goto redo;+            } else {+                /* get the return the yield value at the top of the stack */+                ret = sf->cur_sp[-1];+                sf->cur_sp[-1] = JS_UNDEFINED;+                *pdone = FALSE;+            }+        } else {+            /* end of iterator */+            ret = sf->cur_sp[-1];+            sf->cur_sp[-1] = JS_UNDEFINED;+            JS_FreeValue(ctx, func_ret);+            free_generator_stack(ctx, s);+        }+        break;+    case JS_GENERATOR_STATE_COMPLETED:+    done:+        /* execution is finished */+        switch(magic) {+        default:+        case GEN_MAGIC_NEXT:+            ret = JS_UNDEFINED;+            break;+        case GEN_MAGIC_RETURN:+            ret = JS_DupValue(ctx, argv[0]);+            break;+        case GEN_MAGIC_THROW:+            ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));+            break;+        }+        break;+    case JS_GENERATOR_STATE_EXECUTING:+        ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");+        break;+    }+    return ret;+}++static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,+                                          JSValueConst this_obj,+                                          int argc, JSValueConst *argv,+                                          int flags)+{+    JSValue obj, func_ret;+    JSGeneratorData *s;++    s = js_mallocz(ctx, sizeof(*s));+    if (!s)+        return JS_EXCEPTION;+    s->state = JS_GENERATOR_STATE_SUSPENDED_START;+    if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {+        s->state = JS_GENERATOR_STATE_COMPLETED;+        goto fail;+    }++    /* execute the function up to 'OP_initial_yield' */+    func_ret = async_func_resume(ctx, &s->func_state);+    if (JS_IsException(func_ret))+        goto fail;+    JS_FreeValue(ctx, func_ret);++    obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);+    if (JS_IsException(obj))+        goto fail;+    JS_SetOpaque(obj, s);+    return obj;+ fail:+    free_generator_stack_rt(ctx->rt, s);+    js_free(ctx, s);+    return JS_EXCEPTION;+}++/* AsyncFunction */++static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)+{+    if (s->is_active) {+        async_func_free(rt, &s->func_state);+        s->is_active = FALSE;+    }+}++static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)+{+    js_async_function_terminate(rt, s);+    JS_FreeValueRT(rt, s->resolving_funcs[0]);+    JS_FreeValueRT(rt, s->resolving_funcs[1]);+    remove_gc_object(&s->header);+    js_free_rt(rt, s);+}++static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)+{+    if (--s->header.ref_count == 0) {+        js_async_function_free0(rt, s);+    }+}++static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSAsyncFunctionData *s = p->u.async_function_data;+    if (s) {+        js_async_function_free(rt, s);+    }+}++static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,+                                           JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSAsyncFunctionData *s = p->u.async_function_data;+    if (s) {+        mark_func(rt, &s->header);+    }+}++static int js_async_function_resolve_create(JSContext *ctx,+                                            JSAsyncFunctionData *s,+                                            JSValue *resolving_funcs)+{+    int i;+    JSObject *p;++    for(i = 0; i < 2; i++) {+        resolving_funcs[i] =+            JS_NewObjectProtoClass(ctx, ctx->function_proto,+                                   JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);+        if (JS_IsException(resolving_funcs[i])) {+            if (i == 1)+                JS_FreeValue(ctx, resolving_funcs[0]);+            return -1;+        }+        p = JS_VALUE_GET_OBJ(resolving_funcs[i]);+        s->header.ref_count++;+        p->u.async_function_data = s;+    }+    return 0;+}++static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)+{+    JSValue func_ret, ret2;++    func_ret = async_func_resume(ctx, &s->func_state);+    if (JS_IsException(func_ret)) {+        JSValue error;+    fail:+        error = JS_GetException(ctx);+        ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,+                       1, (JSValueConst *)&error);+        JS_FreeValue(ctx, error);+        js_async_function_terminate(ctx->rt, s);+        JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */+    } else {+        JSValue value;+        value = s->func_state.frame.cur_sp[-1];+        s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;+        if (JS_IsUndefined(func_ret)) {+            /* function returned */+            ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,+                           1, (JSValueConst *)&value);+            JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */+            JS_FreeValue(ctx, value);+            js_async_function_terminate(ctx->rt, s);+        } else {+            JSValue promise, resolving_funcs[2], resolving_funcs1[2];+            int i, res;++            /* await */+            JS_FreeValue(ctx, func_ret); /* not used */+            promise = js_promise_resolve(ctx, ctx->promise_ctor,+                                         1, (JSValueConst *)&value, 0);+            JS_FreeValue(ctx, value);+            if (JS_IsException(promise))+                goto fail;+            if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {+                JS_FreeValue(ctx, promise);+                goto fail;+            }++            /* Note: no need to create 'thrownawayCapability' as in+               the spec */+            for(i = 0; i < 2; i++)+                resolving_funcs1[i] = JS_UNDEFINED;+            res = perform_promise_then(ctx, promise,+                                       (JSValueConst *)resolving_funcs,+                                       (JSValueConst *)resolving_funcs1);+            JS_FreeValue(ctx, promise);+            for(i = 0; i < 2; i++)+                JS_FreeValue(ctx, resolving_funcs[i]);+            if (res)+                goto fail;+        }+    }+}++static JSValue js_async_function_resolve_call(JSContext *ctx,+                                              JSValueConst func_obj,+                                              JSValueConst this_obj,+                                              int argc, JSValueConst *argv,+                                              int flags)+{+    JSObject *p = JS_VALUE_GET_OBJ(func_obj);+    JSAsyncFunctionData *s = p->u.async_function_data;+    BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;+    JSValueConst arg;++    if (argc > 0)+        arg = argv[0];+    else+        arg = JS_UNDEFINED;+    s->func_state.throw_flag = is_reject;+    if (is_reject) {+        JS_Throw(ctx, JS_DupValue(ctx, arg));+    } else {+        /* return value of await */+        s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);+    }+    js_async_function_resume(ctx, s);+    return JS_UNDEFINED;+}++static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,+                                      JSValueConst this_obj,+                                      int argc, JSValueConst *argv, int flags)+{+    JSValue promise;+    JSAsyncFunctionData *s;++    s = js_mallocz(ctx, sizeof(*s));+    if (!s)+        return JS_EXCEPTION;+    s->header.ref_count = 1;+    add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);+    s->is_active = FALSE;+    s->resolving_funcs[0] = JS_UNDEFINED;+    s->resolving_funcs[1] = JS_UNDEFINED;++    promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);+    if (JS_IsException(promise))+        goto fail;++    if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {+    fail:+        JS_FreeValue(ctx, promise);+        js_async_function_free(ctx->rt, s);+        return JS_EXCEPTION;+    }+    s->is_active = TRUE;++    js_async_function_resume(ctx, s);++    js_async_function_free(ctx->rt, s);++    return promise;+}++/* AsyncGenerator */++typedef enum JSAsyncGeneratorStateEnum {+    JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,+    JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,+    JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,+    JS_ASYNC_GENERATOR_STATE_EXECUTING,+    JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,+    JS_ASYNC_GENERATOR_STATE_COMPLETED,+} JSAsyncGeneratorStateEnum;++typedef struct JSAsyncGeneratorRequest {+    struct list_head link;+    /* completion */+    int completion_type; /* GEN_MAGIC_x */+    JSValue result;+    /* promise capability */+    JSValue promise;+    JSValue resolving_funcs[2];+} JSAsyncGeneratorRequest;++typedef struct JSAsyncGeneratorData {+    JSObject *generator; /* back pointer to the object (const) */+    JSAsyncGeneratorStateEnum state;+    JSAsyncFunctionState func_state;+    struct list_head queue; /* list of JSAsyncGeneratorRequest.link */+} JSAsyncGeneratorData;++static void js_async_generator_free(JSRuntime *rt,+                                    JSAsyncGeneratorData *s)+{+    struct list_head *el, *el1;+    JSAsyncGeneratorRequest *req;++    list_for_each_safe(el, el1, &s->queue) {+        req = list_entry(el, JSAsyncGeneratorRequest, link);+        JS_FreeValueRT(rt, req->result);+        JS_FreeValueRT(rt, req->promise);+        JS_FreeValueRT(rt, req->resolving_funcs[0]);+        JS_FreeValueRT(rt, req->resolving_funcs[1]);+        js_free_rt(rt, req);+    }+    if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&+        s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {+        async_func_free(rt, &s->func_state);+    }+    js_free_rt(rt, s);+}++static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)+{+    JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);++    if (s) {+        js_async_generator_free(rt, s);+    }+}++static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,+                                    JS_MarkFunc *mark_func)+{+    JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);+    struct list_head *el;+    JSAsyncGeneratorRequest *req;+    if (s) {+        list_for_each(el, &s->queue) {+            req = list_entry(el, JSAsyncGeneratorRequest, link);+            JS_MarkValue(rt, req->result, mark_func);+            JS_MarkValue(rt, req->promise, mark_func);+            JS_MarkValue(rt, req->resolving_funcs[0], mark_func);+            JS_MarkValue(rt, req->resolving_funcs[1], mark_func);+        }+        if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&+            s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {+            async_func_mark(rt, &s->func_state, mark_func);+        }+    }+}++static JSValue js_async_generator_resolve_function(JSContext *ctx,+                                          JSValueConst this_obj,+                                          int argc, JSValueConst *argv,+                                          int magic, JSValue *func_data);++static int js_async_generator_resolve_function_create(JSContext *ctx,+                                                      JSValueConst generator,+                                                      JSValue *resolving_funcs,+                                                      BOOL is_resume_next)+{+    int i;+    JSValue func;++    for(i = 0; i < 2; i++) {+        func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,+                                   i + is_resume_next * 2, 1, &generator);+        if (JS_IsException(func)) {+            if (i == 1)+                JS_FreeValue(ctx, resolving_funcs[0]);+            return -1;+        }+        resolving_funcs[i] = func;+    }+    return 0;+}++static int js_async_generator_await(JSContext *ctx,+                                    JSAsyncGeneratorData *s,+                                    JSValueConst value)+{+    JSValue promise, resolving_funcs[2], resolving_funcs1[2];+    int i, res;++    promise = js_promise_resolve(ctx, ctx->promise_ctor,+                                 1, &value, 0);+    if (JS_IsException(promise))+        goto fail;++    if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),+                                                   resolving_funcs, FALSE)) {+        JS_FreeValue(ctx, promise);+        goto fail;+    }++    /* Note: no need to create 'thrownawayCapability' as in+       the spec */+    for(i = 0; i < 2; i++)+        resolving_funcs1[i] = JS_UNDEFINED;+    res = perform_promise_then(ctx, promise,+                               (JSValueConst *)resolving_funcs,+                               (JSValueConst *)resolving_funcs1);+    JS_FreeValue(ctx, promise);+    for(i = 0; i < 2; i++)+        JS_FreeValue(ctx, resolving_funcs[i]);+    if (res)+        goto fail;+    return 0;+ fail:+    return -1;+}++static void js_async_generator_resolve_or_reject(JSContext *ctx,+                                                 JSAsyncGeneratorData *s,+                                                 JSValueConst result,+                                                 int is_reject)+{+    JSAsyncGeneratorRequest *next;+    JSValue ret;++    next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);+    list_del(&next->link);+    ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,+                  &result);+    JS_FreeValue(ctx, ret);+    JS_FreeValue(ctx, next->result);+    JS_FreeValue(ctx, next->promise);+    JS_FreeValue(ctx, next->resolving_funcs[0]);+    JS_FreeValue(ctx, next->resolving_funcs[1]);+    js_free(ctx, next);+}++static void js_async_generator_resolve(JSContext *ctx,+                                       JSAsyncGeneratorData *s,+                                       JSValueConst value,+                                       BOOL done)+{+    JSValue result;+    result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);+    /* XXX: better exception handling ? */+    js_async_generator_resolve_or_reject(ctx, s, result, 0);+    JS_FreeValue(ctx, result);+ }++static void js_async_generator_reject(JSContext *ctx,+                                       JSAsyncGeneratorData *s,+                                       JSValueConst exception)+{+    js_async_generator_resolve_or_reject(ctx, s, exception, 1);+}++static void js_async_generator_complete(JSContext *ctx,+                                        JSAsyncGeneratorData *s)+{+    if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {+        s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;+        async_func_free(ctx->rt, &s->func_state);+    }+}++static int js_async_generator_completed_return(JSContext *ctx,+                                               JSAsyncGeneratorData *s,+                                               JSValueConst value)+{+    JSValue promise, resolving_funcs[2], resolving_funcs1[2];+    int res;++    promise = js_promise_resolve(ctx, ctx->promise_ctor,+                                 1, (JSValueConst *)&value, 0);+    if (JS_IsException(promise))+        return -1;+    if (js_async_generator_resolve_function_create(ctx,+                                                   JS_MKPTR(JS_TAG_OBJECT, s->generator),+                                                   resolving_funcs1,+                                                   TRUE)) {+        JS_FreeValue(ctx, promise);+        return -1;+    }+    resolving_funcs[0] = JS_UNDEFINED;+    resolving_funcs[1] = JS_UNDEFINED;+    res = perform_promise_then(ctx, promise,+                               (JSValueConst *)resolving_funcs1,+                               (JSValueConst *)resolving_funcs);+    JS_FreeValue(ctx, resolving_funcs1[0]);+    JS_FreeValue(ctx, resolving_funcs1[1]);+    JS_FreeValue(ctx, promise);+    return res;+}++static void js_async_generator_resume_next(JSContext *ctx,+                                           JSAsyncGeneratorData *s)+{+    JSAsyncGeneratorRequest *next;+    JSValue func_ret, value;++    for(;;) {+        if (list_empty(&s->queue))+            break;+        next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);+        switch(s->state) {+        case JS_ASYNC_GENERATOR_STATE_EXECUTING:+            /* only happens when restarting execution after await() */+            goto resume_exec;+        case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:+            goto done;+        case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:+            if (next->completion_type == GEN_MAGIC_NEXT) {+                goto exec_no_arg;+            } else {+                js_async_generator_complete(ctx, s);+            }+            break;+        case JS_ASYNC_GENERATOR_STATE_COMPLETED:+            if (next->completion_type == GEN_MAGIC_NEXT) {+                js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);+            } else if (next->completion_type == GEN_MAGIC_RETURN) {+                s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;+                js_async_generator_completed_return(ctx, s, next->result);+                goto done;+            } else {+                js_async_generator_reject(ctx, s, next->result);+            }+            goto done;+        case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:+        case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:+            value = JS_DupValue(ctx, next->result);+            if (next->completion_type == GEN_MAGIC_THROW &&+                s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {+                JS_Throw(ctx, value);+                s->func_state.throw_flag = TRUE;+            } else {+                /* 'yield' returns a value. 'yield *' also returns a value+                   in case the 'throw' method is called */+                s->func_state.frame.cur_sp[-1] = value;+                s->func_state.frame.cur_sp[0] =+                    JS_NewInt32(ctx, next->completion_type);+                s->func_state.frame.cur_sp++;+            exec_no_arg:+                s->func_state.throw_flag = FALSE;+            }+            s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;+        resume_exec:+            func_ret = async_func_resume(ctx, &s->func_state);+            if (JS_IsException(func_ret)) {+                value = JS_GetException(ctx);+                js_async_generator_complete(ctx, s);+                js_async_generator_reject(ctx, s, value);+                JS_FreeValue(ctx, value);+            } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {+                int func_ret_code;+                value = s->func_state.frame.cur_sp[-1];+                s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;+                func_ret_code = JS_VALUE_GET_INT(func_ret);+                switch(func_ret_code) {+                case FUNC_RET_YIELD:+                case FUNC_RET_YIELD_STAR:+                    if (func_ret_code == FUNC_RET_YIELD_STAR)+                        s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;+                    else+                        s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;+                    js_async_generator_resolve(ctx, s, value, FALSE);+                    JS_FreeValue(ctx, value);+                    break;+                case FUNC_RET_AWAIT:+                    js_async_generator_await(ctx, s, value);+                    JS_FreeValue(ctx, value);+                    goto done;+                default:+                    abort();+                }+            } else {+                assert(JS_IsUndefined(func_ret));+                /* end of function */+                value = s->func_state.frame.cur_sp[-1];+                s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;+                js_async_generator_complete(ctx, s);+                js_async_generator_resolve(ctx, s, value, TRUE);+                JS_FreeValue(ctx, value);+            }+            break;+        default:+            abort();+        }+    }+ done: ;+}++static JSValue js_async_generator_resolve_function(JSContext *ctx,+                                                   JSValueConst this_obj,+                                                   int argc, JSValueConst *argv,+                                                   int magic, JSValue *func_data)+{+    BOOL is_reject = magic & 1;+    JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);+    JSValueConst arg = argv[0];++    /* XXX: what if s == NULL */++    if (magic >= 2) {+        /* resume next case in AWAITING_RETURN state */+        assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||+               s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);+        s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;+        if (is_reject) {+            js_async_generator_reject(ctx, s, arg);+        } else {+            js_async_generator_resolve(ctx, s, arg, TRUE);+        }+    } else {+        /* restart function execution after await() */+        assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);+        s->func_state.throw_flag = is_reject;+        if (is_reject) {+            JS_Throw(ctx, JS_DupValue(ctx, arg));+        } else {+            /* return value of await */+            s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);+        }+        js_async_generator_resume_next(ctx, s);+    }+    return JS_UNDEFINED;+}++/* magic = GEN_MAGIC_x */+static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv,+                                       int magic)+{+    JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);+    JSValue promise, resolving_funcs[2];+    JSAsyncGeneratorRequest *req;++    promise = JS_NewPromiseCapability(ctx, resolving_funcs);+    if (JS_IsException(promise))+        return JS_EXCEPTION;+    if (!s) {+        JSValue err, res2;+        JS_ThrowTypeError(ctx, "not an AsyncGenerator object");+        err = JS_GetException(ctx);+        res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,+                       1, (JSValueConst *)&err);+        JS_FreeValue(ctx, err);+        JS_FreeValue(ctx, res2);+        JS_FreeValue(ctx, resolving_funcs[0]);+        JS_FreeValue(ctx, resolving_funcs[1]);+        return promise;+    }+    req = js_mallocz(ctx, sizeof(*req));+    if (!req)+        goto fail;+    req->completion_type = magic;+    req->result = JS_DupValue(ctx, argv[0]);+    req->promise = JS_DupValue(ctx, promise);+    req->resolving_funcs[0] = resolving_funcs[0];+    req->resolving_funcs[1] = resolving_funcs[1];+    list_add_tail(&req->link, &s->queue);+    if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {+        js_async_generator_resume_next(ctx, s);+    }+    return promise;+ fail:+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    JS_FreeValue(ctx, promise);+    return JS_EXCEPTION;+}++static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,+                                                JSValueConst this_obj,+                                                int argc, JSValueConst *argv,+                                                int flags)+{+    JSValue obj, func_ret;+    JSAsyncGeneratorData *s;++    s = js_mallocz(ctx, sizeof(*s));+    if (!s)+        return JS_EXCEPTION;+    s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;+    init_list_head(&s->queue);+    if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {+        s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;+        goto fail;+    }++    /* execute the function up to 'OP_initial_yield' (no yield nor+       await are possible) */+    func_ret = async_func_resume(ctx, &s->func_state);+    if (JS_IsException(func_ret))+        goto fail;+    JS_FreeValue(ctx, func_ret);++    obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);+    if (JS_IsException(obj))+        goto fail;+    s->generator = JS_VALUE_GET_OBJ(obj);+    JS_SetOpaque(obj, s);+    return obj;+ fail:+    js_async_generator_free(ctx->rt, s);+    return JS_EXCEPTION;+}++/* JS parser */++enum {+    TOK_NUMBER = -128,+    TOK_STRING,+    TOK_TEMPLATE,+    TOK_IDENT,+    TOK_REGEXP,+    /* warning: order matters (see js_parse_assign_expr) */+    TOK_MUL_ASSIGN,+    TOK_DIV_ASSIGN,+    TOK_MOD_ASSIGN,+    TOK_PLUS_ASSIGN,+    TOK_MINUS_ASSIGN,+    TOK_SHL_ASSIGN,+    TOK_SAR_ASSIGN,+    TOK_SHR_ASSIGN,+    TOK_AND_ASSIGN,+    TOK_XOR_ASSIGN,+    TOK_OR_ASSIGN,+#ifdef CONFIG_BIGNUM+    TOK_MATH_POW_ASSIGN,+#endif+    TOK_POW_ASSIGN,+    TOK_DEC,+    TOK_INC,+    TOK_SHL,+    TOK_SAR,+    TOK_SHR,+    TOK_LT,+    TOK_LTE,+    TOK_GT,+    TOK_GTE,+    TOK_EQ,+    TOK_STRICT_EQ,+    TOK_NEQ,+    TOK_STRICT_NEQ,+    TOK_LAND,+    TOK_LOR,+#ifdef CONFIG_BIGNUM+    TOK_MATH_POW,+#endif+    TOK_POW,+    TOK_ARROW,+    TOK_ELLIPSIS,+    TOK_DOUBLE_QUESTION_MARK,+    TOK_QUESTION_MARK_DOT,+    TOK_ERROR,+    TOK_PRIVATE_NAME,+    TOK_EOF,+    /* keywords: WARNING: same order as atoms */+    TOK_NULL, /* must be first */+    TOK_FALSE,+    TOK_TRUE,+    TOK_IF,+    TOK_ELSE,+    TOK_RETURN,+    TOK_VAR,+    TOK_THIS,+    TOK_DELETE,+    TOK_VOID,+    TOK_TYPEOF,+    TOK_NEW,+    TOK_IN,+    TOK_INSTANCEOF,+    TOK_DO,+    TOK_WHILE,+    TOK_FOR,+    TOK_BREAK,+    TOK_CONTINUE,+    TOK_SWITCH,+    TOK_CASE,+    TOK_DEFAULT,+    TOK_THROW,+    TOK_TRY,+    TOK_CATCH,+    TOK_FINALLY,+    TOK_FUNCTION,+    TOK_DEBUGGER,+    TOK_WITH,+    /* FutureReservedWord */+    TOK_CLASS,+    TOK_CONST,+    TOK_ENUM,+    TOK_EXPORT,+    TOK_EXTENDS,+    TOK_IMPORT,+    TOK_SUPER,+    /* FutureReservedWords when parsing strict mode code */+    TOK_IMPLEMENTS,+    TOK_INTERFACE,+    TOK_LET,+    TOK_PACKAGE,+    TOK_PRIVATE,+    TOK_PROTECTED,+    TOK_PUBLIC,+    TOK_STATIC,+    TOK_YIELD,+    TOK_AWAIT, /* must be last */+    TOK_OF,     /* only used for js_parse_skip_parens_token() */+};++#define TOK_FIRST_KEYWORD   TOK_NULL+#define TOK_LAST_KEYWORD    TOK_AWAIT++/* unicode code points */+#define CP_NBSP 0x00a0+#define CP_BOM  0xfeff++#define CP_LS   0x2028+#define CP_PS   0x2029++typedef struct BlockEnv {+    struct BlockEnv *prev;+    JSAtom label_name; /* JS_ATOM_NULL if none */+    int label_break; /* -1 if none */+    int label_cont; /* -1 if none */+    int drop_count; /* number of stack elements to drop */+    int label_finally; /* -1 if none */+    int scope_level;+    int has_iterator;+} BlockEnv;++typedef struct JSHoistedDef {+    int cpool_idx; /* -1 means variable global definition */+    uint8_t force_init : 1; /* initialize to undefined */+    uint8_t is_lexical : 1; /* global let/const definition */+    uint8_t is_const   : 1; /* const definition */+    int var_idx;   /* function object index if cpool_idx >= 0 */+    int scope_level;    /* scope of definition */+    JSAtom var_name;  /* variable name if cpool_idx < 0 */+} JSHoistedDef;++typedef struct RelocEntry {+    struct RelocEntry *next;+    uint32_t addr; /* address to patch */+    int size;   /* address size: 1, 2 or 4 bytes */+} RelocEntry;++typedef struct JumpSlot {+    int op;+    int size;+    int pos;+    int label;+} JumpSlot;++typedef struct LabelSlot {+    int ref_count;+    int pos;    /* phase 1 address, -1 means not resolved yet */+    int pos2;   /* phase 2 address, -1 means not resolved yet */+    int addr;   /* phase 3 address, -1 means not resolved yet */+    RelocEntry *first_reloc;+} LabelSlot;++typedef struct LineNumberSlot {+    uint32_t pc;+    int line_num;+} LineNumberSlot;++typedef enum JSParseFunctionEnum {+    JS_PARSE_FUNC_STATEMENT,+    JS_PARSE_FUNC_VAR,+    JS_PARSE_FUNC_EXPR,+    JS_PARSE_FUNC_ARROW,+    JS_PARSE_FUNC_GETTER,+    JS_PARSE_FUNC_SETTER,+    JS_PARSE_FUNC_METHOD,+    JS_PARSE_FUNC_CLASS_CONSTRUCTOR,+    JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,+} JSParseFunctionEnum;++typedef enum JSParseExportEnum {+    JS_PARSE_EXPORT_NONE,+    JS_PARSE_EXPORT_NAMED,+    JS_PARSE_EXPORT_DEFAULT,+} JSParseExportEnum;++typedef struct JSFunctionDef {+    JSContext *ctx;+    struct JSFunctionDef *parent;+    int parent_cpool_idx; /* index in the constant pool of the parent+                             or -1 if none */+    int parent_scope_level; /* scope level in parent at point of definition */+    struct list_head child_list; /* list of JSFunctionDef.link */+    struct list_head link;++    BOOL is_eval; /* TRUE if eval code */+    int eval_type; /* only valid if is_eval = TRUE */+    BOOL is_global_var; /* TRUE if variables are not defined locally:+                           eval global, eval module or non strict eval */+    BOOL is_func_expr; /* TRUE if function expression */+    BOOL has_home_object; /* TRUE if the home object is available */+    BOOL has_prototype; /* true if a prototype field is necessary */+    BOOL has_simple_parameter_list;+    BOOL has_use_strict; /* to reject directive in special cases */+    BOOL has_eval_call; /* true if the function contains a call to eval() */+    BOOL has_arguments_binding; /* true if the 'arguments' binding is+                                   available in the function */+    BOOL has_this_binding; /* true if the 'this' and new.target binding are+                              available in the function */+    BOOL new_target_allowed; /* true if the 'new.target' does not+                                throw a syntax error */+    BOOL super_call_allowed; /* true if super() is allowed */+    BOOL super_allowed; /* true if super. or super[] is allowed */+    BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */+    BOOL is_derived_class_constructor;+    BOOL in_function_body;+    BOOL backtrace_barrier;+    JSFunctionKindEnum func_kind : 8;+    JSParseFunctionEnum func_type : 8;+    uint8_t js_mode; /* bitmap of JS_MODE_x */+    JSAtom func_name; /* JS_ATOM_NULL if no name */++    JSVarDef *vars;+    int var_size; /* allocated size for vars[] */+    int var_count;+    JSVarDef *args;+    int arg_size; /* allocated size for args[] */+    int arg_count; /* number of arguments */+    int defined_arg_count;+    int var_object_idx; /* -1 if none */+    int arguments_var_idx; /* -1 if none */+    int func_var_idx; /* variable containing the current function (-1+                         if none, only used if is_func_expr is true) */+    int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */+    int this_var_idx; /* variable containg the 'this' value, -1 if none */+    int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */+    int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */+    int home_object_var_idx;+    BOOL need_home_object;+    +    int scope_level;    /* index into fd->scopes if the current lexical scope */+    int scope_first;    /* index into vd->vars of first lexically scoped variable */+    int scope_size;     /* allocated size of fd->scopes array */+    int scope_count;    /* number of entries used in the fd->scopes array */+    JSVarScope *scopes;+    JSVarScope def_scope_array[4];++    int hoisted_def_count;+    int hoisted_def_size;+    JSHoistedDef *hoisted_def;++    DynBuf byte_code;+    int last_opcode_pos; /* -1 if no last opcode */+    int last_opcode_line_num;+    BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */+    +    LabelSlot *label_slots;+    int label_size; /* allocated size for label_slots[] */+    int label_count;+    BlockEnv *top_break; /* break/continue label stack */++    /* constant pool (strings, functions, numbers) */+    JSValue *cpool;+    int cpool_count;+    int cpool_size;++    /* list of variables in the closure */+    int closure_var_count;+    int closure_var_size;+    JSClosureVar *closure_var;++    JumpSlot *jump_slots;+    int jump_size;+    int jump_count;++    LineNumberSlot *line_number_slots;+    int line_number_size;+    int line_number_count;+    int line_number_last;+    int line_number_last_pc;++    /* pc2line table */+    JSAtom filename;+    int line_num;+    DynBuf pc2line;++    char *source;  /* raw source, utf-8 encoded */+    int source_len;++    JSModuleDef *module; /* != NULL when parsing a module */+} JSFunctionDef;++typedef struct JSToken {+    int val;+    int line_num;   /* line number of token start */+    const uint8_t *ptr;+    union {+        struct {+            JSValue str;+            int sep;+        } str;+        struct {+            JSValue val;+#ifdef CONFIG_BIGNUM+            slimb_t exponent; /* may be != 0 only if val is a float */+#endif+        } num;+        struct {+            JSAtom atom;+            BOOL has_escape;+            BOOL is_reserved;+        } ident;+        struct {+            JSValue body;+            JSValue flags;+        } regexp;+    } u;+} JSToken;++typedef struct JSParseState {+    JSContext *ctx;+    int last_line_num;  /* line number of last token */+    int line_num;       /* line number of current offset */+    const char *filename;+    JSToken token;+    BOOL got_lf; /* true if got line feed before the current token */+    const uint8_t *last_ptr;+    const uint8_t *buf_ptr;+    const uint8_t *buf_end;++    /* current function code */+    JSFunctionDef *cur_func;+    BOOL is_module; /* parsing a module */+    BOOL allow_html_comments;+    BOOL ext_json; /* true if accepting JSON superset */+} JSParseState;++typedef struct JSOpCode {+#ifdef DUMP_BYTECODE+    const char *name;+#endif+    uint8_t size; /* in bytes */+    /* the opcodes remove n_pop items from the top of the stack, then+       pushes n_push items */+    uint8_t n_pop;+    uint8_t n_push;+    uint8_t fmt;+} JSOpCode;++static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {+#define FMT(f)+#ifdef DUMP_BYTECODE+#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },+#else+#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },+#endif+#include "quickjs-opcode.h"+#undef DEF+#undef FMT+};++#if SHORT_OPCODES+/* After the final compilation pass, short opcodes are used. Their+   opcodes overlap with the temporary opcodes which cannot appear in+   the final bytecode. Their description is after the temporary+   opcodes in opcode_info[]. */+#define short_opcode_info(op)           \+    opcode_info[(op) >= OP_TEMP_START ? \+                (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]+#else+#define short_opcode_info(op) opcode_info[op]+#endif++static __exception int next_token(JSParseState *s);++static void free_token(JSParseState *s, JSToken *token)+{+    switch(token->val) {+#ifdef CONFIG_BIGNUM+    case TOK_NUMBER:+        JS_FreeValue(s->ctx, token->u.num.val);+        break;+#endif+    case TOK_STRING:+    case TOK_TEMPLATE:+        JS_FreeValue(s->ctx, token->u.str.str);+        break;+    case TOK_REGEXP:+        JS_FreeValue(s->ctx, token->u.regexp.body);+        JS_FreeValue(s->ctx, token->u.regexp.flags);+        break;+    case TOK_IDENT:+    case TOK_FIRST_KEYWORD ... TOK_LAST_KEYWORD:+    case TOK_PRIVATE_NAME:+        JS_FreeAtom(s->ctx, token->u.ident.atom);+        break;+    default:+        break;+    }+}++static void __attribute((unused)) dump_token(JSParseState *s,+                                             const JSToken *token)+{+    switch(token->val) {+    case TOK_NUMBER:+        {+            double d;+            JS_ToFloat64(s->ctx, &d, token->u.num.val);  /* no exception possible */+            printf("number: %.14g\n", d);+        }+        break;+    case TOK_IDENT:+    dump_atom:+        {+            char buf[ATOM_GET_STR_BUF_SIZE];+            printf("ident: '%s'\n",+                   JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));+        }+        break;+    case TOK_STRING:+        {+            const char *str;+            /* XXX: quote the string */+            str = JS_ToCString(s->ctx, token->u.str.str);+            printf("string: '%s'\n", str);+            JS_FreeCString(s->ctx, str);+        }+        break;+    case TOK_TEMPLATE:+        {+            const char *str;+            str = JS_ToCString(s->ctx, token->u.str.str);+            printf("template: `%s`\n", str);+            JS_FreeCString(s->ctx, str);+        }+        break;+    case TOK_REGEXP:+        {+            const char *str, *str2;+            str = JS_ToCString(s->ctx, token->u.regexp.body);+            str2 = JS_ToCString(s->ctx, token->u.regexp.flags);+            printf("regexp: '%s' '%s'\n", str, str2);+            JS_FreeCString(s->ctx, str);+            JS_FreeCString(s->ctx, str2);+        }+        break;+    case TOK_EOF:+        printf("eof\n");+        break;+    default:+        if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {+            goto dump_atom;+        } else if (s->token.val >= 256) {+            printf("token: %d\n", token->val);+        } else {+            printf("token: '%c'\n", token->val);+        }+        break;+    }+}++int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)+{+    JSContext *ctx = s->ctx;+    va_list ap;+    int backtrace_flags;+    +    va_start(ap, fmt);+    JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);+    va_end(ap);+    backtrace_flags = 0;+    if (s->cur_func && s->cur_func->backtrace_barrier)+        backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;+    build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,+                    backtrace_flags);+    return -1;+}++static int js_parse_expect(JSParseState *s, int tok)+{+    if (s->token.val != tok) {+        /* XXX: dump token correctly in all cases */+        return js_parse_error(s, "expecting '%c'", tok);+    }+    return next_token(s);+}++static int js_parse_expect_semi(JSParseState *s)+{+    if (s->token.val != ';') {+        /* automatic insertion of ';' */+        if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {+            return 0;+        }+        return js_parse_error(s, "expecting '%c'", ';');+    }+    return next_token(s);+}++static int js_parse_error_reserved_identifier(JSParseState *s)+{+    char buf1[ATOM_GET_STR_BUF_SIZE];+    return js_parse_error(s, "'%s' is a reserved identifier",+                          JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),+                                        s->token.u.ident.atom));+}++static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)+{+    uint32_t c;+    StringBuffer b_s, *b = &b_s;++    /* p points to the first byte of the template part */+    if (string_buffer_init(s->ctx, b, 32))+        goto fail;+    for(;;) {+        if (p >= s->buf_end)+            goto unexpected_eof;+        c = *p++;+        if (c == '`') {+            /* template end part */+            break;+        }+        if (c == '$' && *p == '{') {+            /* template start or middle part */+            p++;+            break;+        }+        if (c == '\\') {+            if (string_buffer_putc8(b, c))+                goto fail;+            if (p >= s->buf_end)+                goto unexpected_eof;+            c = *p++;+        }+        /* newline sequences are normalized as single '\n' bytes */+        if (c == '\r') {+            if (*p == '\n')+                p++;+            c = '\n';+        }+        if (c == '\n') {+            s->line_num++;+        } else if (c >= 0x80) {+            const uint8_t *p_next;+            c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+            if (c > 0x10FFFF) {+                js_parse_error(s, "invalid UTF-8 sequence");+                goto fail;+            }+            p = p_next;+        }+        if (string_buffer_putc(b, c))+            goto fail;+    }+    s->token.val = TOK_TEMPLATE;+    s->token.u.str.sep = c;+    s->token.u.str.str = string_buffer_end(b);+    s->buf_ptr = p;+    return 0;++ unexpected_eof:+    js_parse_error(s, "unexpected end of string");+ fail:+    string_buffer_free(b);+    return -1;+}++static __exception int js_parse_string(JSParseState *s, int sep,+                                       BOOL do_throw, const uint8_t *p,+                                       JSToken *token, const uint8_t **pp)+{+    int ret;+    uint32_t c;+    StringBuffer b_s, *b = &b_s;++    /* string */+    if (string_buffer_init(s->ctx, b, 32))+        goto fail;+    for(;;) {+        if (p >= s->buf_end)+            goto invalid_char;+        c = *p;+        if (c < 0x20) {+            if (!s->cur_func) {+                if (do_throw)+                    js_parse_error(s, "invalid character in a JSON string");+                goto fail;+            }+            if (sep == '`') {+                if (c == '\r') {+                    if (p[1] == '\n')+                        p++;+                    c = '\n';+                }+                /* do not update s->line_num */+            } else if (c == '\n' || c == '\r')+                goto invalid_char;+        }+        p++;+        if (c == sep)+            break;+        if (c == '$' && *p == '{' && sep == '`') {+            /* template start or middle part */+            p++;+            break;+        }+        if (c == '\\') {+            c = *p;+            switch(c) {+            case '\0':+                if (p >= s->buf_end)+                    goto invalid_char;+                p++;+                break;+            case '\'':+            case '\"':+            case '\\':+                p++;+                break;+            case '\r':  /* accept DOS and MAC newline sequences */+                if (p[1] == '\n') {+                    p++;+                }+                /* fall thru */+            case '\n':+                /* ignore escaped newline sequence */+                p++;+                if (sep != '`')+                    s->line_num++;+                continue;+            default:+                if (c >= '0' && c <= '7') {+                    if (!s->cur_func)+                        goto invalid_octal; /* JSON case */+                    if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')+                        goto parse_escape;+                    if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {+                        p++;+                        c = '\0';+                    } else {+                    invalid_octal:+                        if (do_throw)+                            js_parse_error(s, "invalid octal syntax in strict mode");+                        goto fail;+                    }+                } else if (c >= 0x80) {+                    const uint8_t *p_next;+                    c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);+                    if (c > 0x10FFFF) {+                        goto invalid_utf8;+                    }+                    p = p_next;+                    /* LS or PS are skipped */+                    if (c == CP_LS || c == CP_PS)+                        continue;+                } else {+                parse_escape:+                    ret = lre_parse_escape(&p, TRUE);+                    if (ret == -1) {+                        if (do_throw)+                            js_parse_error(s, "malformed escape sequence in string literal");+                        goto fail;+                    } else if (ret < 0) {+                        /* ignore the '\' (could output a warning) */+                        p++;+                    } else {+                        c = ret;+                    }+                }+                break;+            }+        } else if (c >= 0x80) {+            const uint8_t *p_next;+            c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+            if (c > 0x10FFFF)+                goto invalid_utf8;+            p = p_next;+        }+        if (string_buffer_putc(b, c))+            goto fail;+    }+    token->val = TOK_STRING;+    token->u.str.sep = c;+    token->u.str.str = string_buffer_end(b);+    *pp = p;+    return 0;++ invalid_utf8:+    if (do_throw)+        js_parse_error(s, "invalid UTF-8 sequence");+    goto fail;+ invalid_char:+    if (do_throw)+        js_parse_error(s, "unexpected end of string");+ fail:+    string_buffer_free(b);+    return -1;+}++static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {+    return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&+        !s->token.u.ident.has_escape;+}++static __exception int js_parse_regexp(JSParseState *s)+{+    const uint8_t *p;+    BOOL in_class;+    StringBuffer b_s, *b = &b_s;+    StringBuffer b2_s, *b2 = &b2_s;+    uint32_t c;++    p = s->buf_ptr;+    p++;+    in_class = FALSE;+    if (string_buffer_init(s->ctx, b, 32))+        return -1;+    if (string_buffer_init(s->ctx, b2, 1))+        goto fail;+    for(;;) {+        if (p >= s->buf_end) {+        eof_error:+            js_parse_error(s, "unexpected end of regexp");+            goto fail;+        }+        c = *p++;+        if (c == '\n' || c == '\r') {+            goto eol_error;+        } else if (c == '/') {+            if (!in_class)+                break;+        } else if (c == '[') {+            in_class = TRUE;+        } else if (c == ']') {+            /* XXX: incorrect as the first character in a class */+            in_class = FALSE;+        } else if (c == '\\') {+            if (string_buffer_putc8(b, c))+                goto fail;+            c = *p++;+            if (c == '\n' || c == '\r')+                goto eol_error;+            else if (c == '\0' && p >= s->buf_end)+                goto eof_error;+            else if (c >= 0x80) {+                const uint8_t *p_next;+                c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+                if (c > 0x10FFFF) {+                    goto invalid_utf8;+                }+                p = p_next;+                if (c == CP_LS || c == CP_PS)+                    goto eol_error;+            }+        } else if (c >= 0x80) {+            const uint8_t *p_next;+            c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+            if (c > 0x10FFFF) {+            invalid_utf8:+                js_parse_error(s, "invalid UTF-8 sequence");+                goto fail;+            }+            p = p_next;+            /* LS or PS are considered as line terminator */+            if (c == CP_LS || c == CP_PS) {+            eol_error:+                js_parse_error(s, "unexpected line terminator in regexp");+                goto fail;+            }+        }+        if (string_buffer_putc(b, c))+            goto fail;+    }++    /* flags */+    for(;;) {+        const uint8_t *p_next = p;+        c = *p_next++;+        if (c >= 0x80) {+            c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);+            if (c > 0x10FFFF) {+                goto invalid_utf8;+            }+        }+        if (!lre_js_is_ident_next(c))+            break;+        if (string_buffer_putc(b2, c))+            goto fail;+        p = p_next;+    }++    s->token.val = TOK_REGEXP;+    s->token.u.regexp.body = string_buffer_end(b);+    s->token.u.regexp.flags = string_buffer_end(b2);+    s->buf_ptr = p;+    return 0;+ fail:+    string_buffer_free(b);+    string_buffer_free(b2);+    return -1;+}++static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,+                                     char *static_buf)+{+    char *buf, *new_buf;+    size_t size, new_size;+    +    buf = *pbuf;+    size = *psize;+    if (size >= (SIZE_MAX / 3) * 2)+        new_size = SIZE_MAX;+    else+        new_size = size + (size >> 1);+    if (buf == static_buf) {+        new_buf = js_malloc(ctx, new_size);+        if (!new_buf)+            return -1;+        memcpy(new_buf, buf, size);+    } else {+        new_buf = js_realloc(ctx, buf, new_size);+        if (!new_buf)+            return -1;+    }+    *pbuf = new_buf;+    *psize = new_size;+    return 0;+}++/* 'c' is the first character. Return JS_ATOM_NULL in case of error */+static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,+                          BOOL *pident_has_escape, int c, BOOL is_private)+{+    const uint8_t *p, *p1;+    char ident_buf[128], *buf;+    size_t ident_size, ident_pos;+    JSAtom atom;+    +    p = *pp;+    buf = ident_buf;+    ident_size = sizeof(ident_buf);+    ident_pos = 0;+    if (is_private)+        buf[ident_pos++] = '#';+    for(;;) {+        p1 = p;+        +        if (c < 128) {+            buf[ident_pos++] = c;+        } else {+            ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);+        }+        c = *p1++;+        if (c == '\\' && *p1 == 'u') {+            c = lre_parse_escape(&p1, TRUE);+            *pident_has_escape = TRUE;+        } else if (c >= 128) {+            c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);+        }+        if (!lre_js_is_ident_next(c))+            break;+        p = p1;+        if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {+            if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {+                atom = JS_ATOM_NULL;+                goto done;+            }+        }+    }+    atom = JS_NewAtomLen(s->ctx, buf, ident_pos);+ done:+    if (unlikely(buf != ident_buf))+        js_free(s->ctx, buf);+    *pp = p;+    return atom;+}+++static __exception int next_token(JSParseState *s)+{+    const uint8_t *p;+    int c;+    BOOL ident_has_escape;+    JSAtom atom;+    +    if (js_check_stack_overflow(s->ctx->rt, 0)) {+        return js_parse_error(s, "stack overflow");+    }+    +    free_token(s, &s->token);++    p = s->last_ptr = s->buf_ptr;+    s->got_lf = FALSE;+    s->last_line_num = s->token.line_num;+ redo:+    s->token.line_num = s->line_num;+    s->token.ptr = p;+    c = *p;+    switch(c) {+    case 0:+        if (p >= s->buf_end) {+            s->token.val = TOK_EOF;+        } else {+            goto def_token;+        }+        break;+    case '`':+        if (js_parse_template_part(s, p + 1))+            goto fail;+        p = s->buf_ptr;+        break;+    case '\'':+    case '\"':+        if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))+            goto fail;+        break;+    case '\r':  /* accept DOS and MAC newline sequences */+        if (p[1] == '\n') {+            p++;+        }+        /* fall thru */+    case '\n':+        p++;+    line_terminator:+        s->got_lf = TRUE;+        s->line_num++;+        goto redo;+    case '\f':+    case '\v':+    case ' ':+    case '\t':+        p++;+        goto redo;+    case '/':+        if (p[1] == '*') {+            /* comment */+            p += 2;+            for(;;) {+                if (*p == '\0' && p >= s->buf_end) {+                    js_parse_error(s, "unexpected end of comment");+                    goto fail;+                }+                if (p[0] == '*' && p[1] == '/') {+                    p += 2;+                    break;+                }+                if (*p == '\n') {+                    s->line_num++;+                    s->got_lf = TRUE; /* considered as LF for ASI */+                    p++;+                } else if (*p == '\r') {+                    s->got_lf = TRUE; /* considered as LF for ASI */+                    p++;+                } else if (*p >= 0x80) {+                    c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+                    if (c == CP_LS || c == CP_PS) {+                        s->got_lf = TRUE; /* considered as LF for ASI */+                    } else if (c == -1) {+                        p++; /* skip invalid UTF-8 */+                    }+                } else {+                    p++;+                }+            }+            goto redo;+        } else if (p[1] == '/') {+            /* line comment */+            p += 2;+        skip_line_comment:+            for(;;) {+                if (*p == '\0' && p >= s->buf_end)+                    break;+                if (*p == '\r' || *p == '\n')+                    break;+                if (*p >= 0x80) {+                    c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+                    /* LS or PS are considered as line terminator */+                    if (c == CP_LS || c == CP_PS) {+                        break;+                    } else if (c == -1) {+                        p++; /* skip invalid UTF-8 */+                    }+                } else {+                    p++;+                }+            }+            goto redo;+        } else if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_DIV_ASSIGN;+        } else {+            p++;+            s->token.val = c;+        }+        break;+    case '\\':+        if (p[1] == 'u') {+            const uint8_t *p1 = p + 1;+            int c1 = lre_parse_escape(&p1, TRUE);+            if (c1 >= 0 && lre_js_is_ident_first(c1)) {+                c = c1;+                p = p1;+                ident_has_escape = TRUE;+                goto has_ident;+            } else {+                /* XXX: syntax error? */+            }+        }+        goto def_token;+    case 'a' ... 'z':+    case 'A' ... 'Z':+    case '_':+    case '$':+        /* identifier */+        p++;+        ident_has_escape = FALSE;+    has_ident:+        atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);+        if (atom == JS_ATOM_NULL)+            goto fail;+        s->token.u.ident.atom = atom;+        s->token.u.ident.has_escape = ident_has_escape;+        s->token.u.ident.is_reserved = FALSE;+        if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||+            (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&+             (s->cur_func->js_mode & JS_MODE_STRICT)) ||+            (s->token.u.ident.atom == JS_ATOM_yield &&+             ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||+              (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&+               !s->cur_func->in_function_body && s->cur_func->parent &&+               (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||+            (s->token.u.ident.atom == JS_ATOM_await &&+             (s->is_module ||+              (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||+                (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&+                 !s->cur_func->in_function_body && s->cur_func->parent &&+                 (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {+                  if (ident_has_escape) {+                      s->token.u.ident.is_reserved = TRUE;+                      s->token.val = TOK_IDENT;+                  } else {+                      /* The keywords atoms are pre allocated */+                      s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;+                  }+        } else {+            s->token.val = TOK_IDENT;+        }+        break;+    case '#':+        /* private name */+        {+            const uint8_t *p1;+            p++;+            p1 = p;+            c = *p1++;+            if (c == '\\' && *p1 == 'u') {+                c = lre_parse_escape(&p1, TRUE);+            } else if (c >= 128) {+                c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);+            }+            if (!lre_js_is_ident_first(c)) {+                js_parse_error(s, "invalid first character of private name");+                goto fail;+            }+            p = p1;+            ident_has_escape = FALSE; /* not used */+            atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);+            if (atom == JS_ATOM_NULL)+                goto fail;+            s->token.u.ident.atom = atom;+            s->token.val = TOK_PRIVATE_NAME;+        }+        break;+    case '.':+        if (p[1] == '.' && p[2] == '.') {+            p += 3;+            s->token.val = TOK_ELLIPSIS;+            break;+        }+        if (p[1] >= '0' && p[1] <= '9') {+            goto parse_number;+        } else {+            goto def_token;+        }+        break;+    case '0':+        /* in strict mode, octal literals are not accepted */+        if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {+            js_parse_error(s, "octal literals are deprecated in strict mode");+            goto fail;+        }+        goto parse_number;+    case '1' ... '9':+        /* number */+    parse_number:+        {+            JSValue ret;+            const uint8_t *p1;+            int flags, radix;+            flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |+                ATOD_ACCEPT_UNDERSCORES;+#ifdef CONFIG_BIGNUM+            flags |= ATOD_ACCEPT_SUFFIX;+            if (s->cur_func->js_mode & JS_MODE_MATH) {+                flags |= ATOD_MODE_BIGINT;+                if (s->cur_func->js_mode & JS_MODE_MATH)+                    flags |= ATOD_TYPE_BIG_FLOAT;+            }+#endif+            radix = 0;+#ifdef CONFIG_BIGNUM+            s->token.u.num.exponent = 0;+            ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,+                           flags, &s->token.u.num.exponent);+#else+            ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,+                          flags);+#endif+            if (JS_IsException(ret))+                goto fail;+            /* reject `10instanceof Number` */+            if (JS_VALUE_IS_NAN(ret) ||+                lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {+                JS_FreeValue(s->ctx, ret);+                js_parse_error(s, "invalid number literal");+                goto fail;+            }+            s->token.val = TOK_NUMBER;+            s->token.u.num.val = ret;+        }+        break;+    case '*':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_MUL_ASSIGN;+        } else if (p[1] == '*') {+            if (p[2] == '=') {+                p += 3;+                s->token.val = TOK_POW_ASSIGN;+            } else {+                p += 2;+                s->token.val = TOK_POW;+            }+        } else {+            goto def_token;+        }+        break;+    case '%':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_MOD_ASSIGN;+        } else {+            goto def_token;+        }+        break;+    case '+':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_PLUS_ASSIGN;+        } else if (p[1] == '+') {+            p += 2;+            s->token.val = TOK_INC;+        } else {+            goto def_token;+        }+        break;+    case '-':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_MINUS_ASSIGN;+        } else if (p[1] == '-') {+            if (s->allow_html_comments &&+                p[2] == '>' && s->last_line_num != s->line_num) {+                /* Annex B: `-->` at beginning of line is an html comment end.+                   It extends to the end of the line.+                 */+                goto skip_line_comment;+            }+            p += 2;+            s->token.val = TOK_DEC;+        } else {+            goto def_token;+        }+        break;+    case '<':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_LTE;+        } else if (p[1] == '<') {+            if (p[2] == '=') {+                p += 3;+                s->token.val = TOK_SHL_ASSIGN;+            } else {+                p += 2;+                s->token.val = TOK_SHL;+            }+        } else if (s->allow_html_comments &&+                   p[1] == '!' && p[2] == '-' && p[3] == '-') {+            /* Annex B: handle `<!--` single line html comments */+            goto skip_line_comment;+        } else {+            goto def_token;+        }+        break;+    case '>':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_GTE;+        } else if (p[1] == '>') {+            if (p[2] == '>') {+                if (p[3] == '=') {+                    p += 4;+                    s->token.val = TOK_SHR_ASSIGN;+                } else {+                    p += 3;+                    s->token.val = TOK_SHR;+                }+            } else if (p[2] == '=') {+                p += 3;+                s->token.val = TOK_SAR_ASSIGN;+            } else {+                p += 2;+                s->token.val = TOK_SAR;+            }+        } else {+            goto def_token;+        }+        break;+    case '=':+        if (p[1] == '=') {+            if (p[2] == '=') {+                p += 3;+                s->token.val = TOK_STRICT_EQ;+            } else {+                p += 2;+                s->token.val = TOK_EQ;+            }+        } else if (p[1] == '>') {+            p += 2;+            s->token.val = TOK_ARROW;+        } else {+            goto def_token;+        }+        break;+    case '!':+        if (p[1] == '=') {+            if (p[2] == '=') {+                p += 3;+                s->token.val = TOK_STRICT_NEQ;+            } else {+                p += 2;+                s->token.val = TOK_NEQ;+            }+        } else {+            goto def_token;+        }+        break;+    case '&':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_AND_ASSIGN;+        } else if (p[1] == '&') {+            p += 2;+            s->token.val = TOK_LAND;+        } else {+            goto def_token;+        }+        break;+#ifdef CONFIG_BIGNUM+        /* in math mode, '^' is the power operator. '^^' is always the+           xor operator and '**' is always the power operator */+    case '^':+        if (p[1] == '=') {+            p += 2;+            if (s->cur_func->js_mode & JS_MODE_MATH)+                s->token.val = TOK_MATH_POW_ASSIGN;+            else+                s->token.val = TOK_XOR_ASSIGN;+        } else if (p[1] == '^') {+            if (p[2] == '=') {+                p += 3;+                s->token.val = TOK_XOR_ASSIGN;+            } else {+                p += 2;+                s->token.val = '^';+            }+        } else {+            p++;+            if (s->cur_func->js_mode & JS_MODE_MATH)+                s->token.val = TOK_MATH_POW;+            else+                s->token.val = '^';+        }+        break;+#else+    case '^':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_XOR_ASSIGN;+        } else {+            goto def_token;+        }+        break;+#endif+    case '|':+        if (p[1] == '=') {+            p += 2;+            s->token.val = TOK_OR_ASSIGN;+        } else if (p[1] == '|') {+            p += 2;+            s->token.val = TOK_LOR;+        } else {+            goto def_token;+        }+        break;+    case '?':+        if (p[1] == '?') {+            p += 2;+            s->token.val = TOK_DOUBLE_QUESTION_MARK;+        } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {+            p += 2;+            s->token.val = TOK_QUESTION_MARK_DOT;+        } else {+            goto def_token;+        }+        break;+    default:+        if (c >= 128) {+            /* unicode value */+            c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+            switch(c) {+            case CP_PS:+            case CP_LS:+                /* XXX: should avoid incrementing line_number, but+                   needed to handle HTML comments */+                goto line_terminator; +            default:+                if (lre_is_space(c)) {+                    goto redo;+                } else if (lre_js_is_ident_first(c)) {+                    ident_has_escape = FALSE;+                    goto has_ident;+                } else {+                    js_parse_error(s, "unexpected character");+                    goto fail;+                }+            }+        }+    def_token:+        s->token.val = c;+        p++;+        break;+    }+    s->buf_ptr = p;++    //    dump_token(s, &s->token);+    return 0;++ fail:+    s->token.val = TOK_ERROR;+    return -1;+}++/* 'c' is the first character. Return JS_ATOM_NULL in case of error */+static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)+{+    const uint8_t *p;+    char ident_buf[128], *buf;+    size_t ident_size, ident_pos;+    JSAtom atom;+    +    p = *pp;+    buf = ident_buf;+    ident_size = sizeof(ident_buf);+    ident_pos = 0;+    for(;;) {+        buf[ident_pos++] = c;+        c = *p;+        if (c >= 128 ||+            !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))+            break;+        p++;+        if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {+            if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {+                atom = JS_ATOM_NULL;+                goto done;+            }+        }+    }+    atom = JS_NewAtomLen(s->ctx, buf, ident_pos);+ done:+    if (unlikely(buf != ident_buf))+        js_free(s->ctx, buf);+    *pp = p;+    return atom;+}++static __exception int json_next_token(JSParseState *s)+{+    const uint8_t *p;+    int c;+    JSAtom atom;+    +    if (js_check_stack_overflow(s->ctx->rt, 0)) {+        return js_parse_error(s, "stack overflow");+    }+    +    free_token(s, &s->token);++    p = s->last_ptr = s->buf_ptr;+    s->last_line_num = s->token.line_num;+ redo:+    s->token.line_num = s->line_num;+    s->token.ptr = p;+    c = *p;+    switch(c) {+    case 0:+        if (p >= s->buf_end) {+            s->token.val = TOK_EOF;+        } else {+            goto def_token;+        }+        break;+    case '\'':+        if (!s->ext_json) {+            /* JSON does not accept single quoted strings */+            goto def_token;+        }+        /* fall through */+    case '\"':+        if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))+            goto fail;+        break;+    case '\r':  /* accept DOS and MAC newline sequences */+        if (p[1] == '\n') {+            p++;+        }+        /* fall thru */+    case '\n':+        p++;+        s->line_num++;+        goto redo;+    case '\f':+    case '\v':+        if (!s->ext_json) {+            /* JSONWhitespace does not match <VT>, nor <FF> */+            goto def_token;+        }+        /* fall through */+    case ' ':+    case '\t':+        p++;+        goto redo;+    case '/':+        if (!s->ext_json) {+            /* JSON does not accept comments */+            goto def_token;+        }+        if (p[1] == '*') {+            /* comment */+            p += 2;+            for(;;) {+                if (*p == '\0' && p >= s->buf_end) {+                    js_parse_error(s, "unexpected end of comment");+                    goto fail;+                }+                if (p[0] == '*' && p[1] == '/') {+                    p += 2;+                    break;+                }+                if (*p == '\n') {+                    s->line_num++;+                    p++;+                } else if (*p == '\r') {+                    p++;+                } else if (*p >= 0x80) {+                    c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+                    if (c == -1) {+                        p++; /* skip invalid UTF-8 */+                    }+                } else {+                    p++;+                }+            }+            goto redo;+        } else if (p[1] == '/') {+            /* line comment */+            p += 2;+            for(;;) {+                if (*p == '\0' && p >= s->buf_end)+                    break;+                if (*p == '\r' || *p == '\n')+                    break;+                if (*p >= 0x80) {+                    c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+                    /* LS or PS are considered as line terminator */+                    if (c == CP_LS || c == CP_PS) {+                        break;+                    } else if (c == -1) {+                        p++; /* skip invalid UTF-8 */+                    }+                } else {+                    p++;+                }+            }+            goto redo;+        } else {+            goto def_token;+        }+        break;+    case 'a' ... 'z':+    case 'A' ... 'Z':+    case '_':+    case '$':+        /* identifier : only pure ascii characters are accepted */+        p++;+        atom = json_parse_ident(s, &p, c);+        if (atom == JS_ATOM_NULL)+            goto fail;+        s->token.u.ident.atom = atom;+        s->token.u.ident.has_escape = FALSE;+        s->token.u.ident.is_reserved = FALSE;+        s->token.val = TOK_IDENT;+        break;+    case '+':+        if (!s->ext_json || !is_digit(p[1]))+            goto def_token;+        goto parse_number;+    case '0':+        if (is_digit(p[1]))+            goto def_token;+        goto parse_number;+    case '-':+        if (!is_digit(p[1]))+            goto def_token;+        goto parse_number;+    case '1' ... '9':+        /* number */+    parse_number:+        {+            JSValue ret;+            int flags, radix;+            if (!s->ext_json) {+                flags = 0;+                radix = 10;+            } else {+                flags = ATOD_ACCEPT_BIN_OCT;+                radix = 0;+            }+            ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,+                          flags);+            if (JS_IsException(ret))+                goto fail;+            s->token.val = TOK_NUMBER;+            s->token.u.num.val = ret;+        }+        break;+    default:+        if (c >= 128) {+            js_parse_error(s, "unexpected character");+            goto fail;+        }+    def_token:+        s->token.val = c;+        p++;+        break;+    }+    s->buf_ptr = p;++    //    dump_token(s, &s->token);+    return 0;++ fail:+    s->token.val = TOK_ERROR;+    return -1;+}++/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is+   only set if TOK_IMPORT is returned */+/* XXX: handle all unicode cases */+static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)+{+    const uint8_t *p;+    uint32_t c;+    +    /* skip spaces and comments */+    p = *pp;+    for (;;) {+        switch(c = *p++) {+        case '\r':+        case '\n':+            if (no_line_terminator)+                return '\n';+            continue;+        case ' ':+        case '\t':+        case '\v':+        case '\f':+            continue;+        case '/':+            if (*p == '/') {+                if (no_line_terminator)+                    return '\n';+                while (*p && *p != '\r' && *p != '\n')+                    p++;+                continue;+            }+            if (*p == '*') {+                while (*++p) {+                    if ((*p == '\r' || *p == '\n') && no_line_terminator)+                        return '\n';+                    if (*p == '*' && p[1] == '/') {+                        p += 2;+                        break;+                    }+                }+                continue;+            }+            break;+        case '=':+            if (*p == '>')+                return TOK_ARROW;+            break;+        default:+            if (lre_js_is_ident_first(c)) {+                if (c == 'i') {+                    if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {+                        return TOK_IN;+                    }+                    if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&+                        p[3] == 'r' && p[4] == 't' &&+                        !lre_js_is_ident_next(p[5])) {+                        *pp = p + 5;+                        return TOK_IMPORT;+                    }+                } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {+                    return TOK_OF;+                } else if (c == 'e' &&+                           p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&+                           p[3] == 'r' && p[4] == 't' &&+                           !lre_js_is_ident_next(p[5])) {+                    *pp = p + 5;+                    return TOK_EXPORT;+                } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&+                         p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&+                         p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {+                    return TOK_FUNCTION;+                }+                return TOK_IDENT;+            }+            break;+        }+        return c;+    }+}++static int peek_token(JSParseState *s, BOOL no_line_terminator)+{+    const uint8_t *p = s->buf_ptr;+    return simple_next_token(&p, no_line_terminator);+}++/* return true if 'input' contains the source of a module+   (heuristic). 'input' must be a zero terminated.++   Heuristic: skip comments and expect 'import' keyword not followed+   by '(' or '.' or export keyword.+*/+BOOL JS_DetectModule(const char *input, size_t input_len)+{+    const uint8_t *p = (const uint8_t *)input;+    int tok;+    switch(simple_next_token(&p, FALSE)) {+    case TOK_IMPORT:+        tok = simple_next_token(&p, FALSE);+        return (tok != '.' && tok != '(');+    case TOK_EXPORT:+        return TRUE;+    default:+        return FALSE;+    }+}++static inline int get_prev_opcode(JSFunctionDef *fd) {+    if (fd->last_opcode_pos < 0)+        return OP_invalid;+    else+        return fd->byte_code.buf[fd->last_opcode_pos];+}++static BOOL js_is_live_code(JSParseState *s) {+    switch (get_prev_opcode(s->cur_func)) {+    case OP_tail_call:+    case OP_tail_call_method:+    case OP_return:+    case OP_return_undef:+    case OP_return_async:+    case OP_throw:+    case OP_throw_var:+    case OP_goto:+#if SHORT_OPCODES+    case OP_goto8:+    case OP_goto16:+#endif+    case OP_ret:+        return FALSE;+    default:+        return TRUE;+    }+}++static void emit_u8(JSParseState *s, uint8_t val)+{+    dbuf_putc(&s->cur_func->byte_code, val);+}++static void emit_u16(JSParseState *s, uint16_t val)+{+    dbuf_put_u16(&s->cur_func->byte_code, val);+}++static void emit_u32(JSParseState *s, uint32_t val)+{+    dbuf_put_u32(&s->cur_func->byte_code, val);+}++static void emit_op(JSParseState *s, uint8_t val)+{+    JSFunctionDef *fd = s->cur_func;+    DynBuf *bc = &fd->byte_code;++    /* Use the line number of the last token used, not the next token,+       nor the current offset in the source file.+     */+    if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {+        dbuf_putc(bc, OP_line_num);+        dbuf_put_u32(bc, s->last_line_num);+        fd->last_opcode_line_num = s->last_line_num;+    }+    fd->last_opcode_pos = bc->size;+    dbuf_putc(bc, val);+}++static void emit_atom(JSParseState *s, JSAtom name)+{+    emit_u32(s, JS_DupAtom(s->ctx, name));+}++static int update_label(JSFunctionDef *s, int label, int delta)+{+    LabelSlot *ls;++    assert(label >= 0 && label < s->label_count);+    ls = &s->label_slots[label];+    ls->ref_count += delta;+    assert(ls->ref_count >= 0);+    return ls->ref_count;+}++static int new_label_fd(JSFunctionDef *fd, int label)+{+    LabelSlot *ls;++    if (label < 0) {+        if (js_resize_array(fd->ctx, (void *)&fd->label_slots,+                            sizeof(fd->label_slots[0]),+                            &fd->label_size, fd->label_count + 1))+            return -1;+        label = fd->label_count++;+        ls = &fd->label_slots[label];+        ls->ref_count = 0;+        ls->pos = -1;+        ls->pos2 = -1;+        ls->addr = -1;+        ls->first_reloc = NULL;+    }+    return label;+}++static int new_label(JSParseState *s)+{+    return new_label_fd(s->cur_func, -1);+}++/* return the label ID offset */+static int emit_label(JSParseState *s, int label)+{+    if (label >= 0) {+        emit_op(s, OP_label);+        emit_u32(s, label);+        s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;+        return s->cur_func->byte_code.size - 4;+    } else {+        return -1;+    }+}++/* return label or -1 if dead code */+static int emit_goto(JSParseState *s, int opcode, int label)+{+    if (js_is_live_code(s)) {+        if (label < 0)+            label = new_label(s);+        emit_op(s, opcode);+        emit_u32(s, label);+        s->cur_func->label_slots[label].ref_count++;+        return label;+    }+    return -1;+}++/* return the constant pool index. 'val' is not duplicated. */+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))+        return -1;+    fd->cpool[fd->cpool_count++] = val;+    return fd->cpool_count - 1;+}++static __exception int emit_push_const(JSParseState *s, JSValueConst val,+                                       BOOL as_atom)+{+    int idx;++    if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {+        JSAtom atom;+        /* warning: JS_NewAtomStr frees the string value */+        JS_DupValue(s->ctx, val);+        atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));+        if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {+            emit_op(s, OP_push_atom_value);+            emit_u32(s, atom);+            return 0;+        }+    }++    idx = cpool_add(s, JS_DupValue(s->ctx, val));+    if (idx < 0)+        return -1;+    emit_op(s, OP_push_const);+    emit_u32(s, idx);+    return 0;+}++/* return the variable index or -1 if not found,+   add ARGUMENT_VAR_OFFSET for argument variables */+static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+    int i;+    for(i = fd->arg_count; i-- > 0;) {+        if (fd->args[i].var_name == name)+            return i | ARGUMENT_VAR_OFFSET;+    }+    return -1;+}++static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+    int i;+    for(i = fd->var_count; i-- > 0;) {+        if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)+            return i;+    }+    return find_arg(ctx, fd, name);+}++/* return true if scope == parent_scope or if scope is a child of+   parent_scope */+static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,+                           int scope, int parent_scope)+{+    while (scope >= 0) {+        if (scope == parent_scope)+            return TRUE;+        scope = fd->scopes[scope].parent;+    }+    return FALSE;+}++/* find a 'var' declaration in the same scope or a child scope */+static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,+                                   JSAtom name, int scope_level)+{+    int i;+    for(i = 0; i < fd->var_count; i++) {+        JSVarDef *vd = &fd->vars[i];+        if (vd->var_name == name && vd->scope_level == 0) {+            if (is_child_scope(ctx, fd, vd->func_pool_or_scope_idx,+                               scope_level))+                return i;+        }+    }+    return -1;+}+++static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name)+{+    int i;+    for(i = 0; i < fd->hoisted_def_count; i++) {+        JSHoistedDef *hf = &fd->hoisted_def[i];+        if (hf->var_name == name)+            return hf;+    }+    return NULL;++}++static JSHoistedDef *find_lexical_hoisted_def(JSFunctionDef *fd, JSAtom name)+{+    JSHoistedDef *hf = find_hoisted_def(fd, name);+    if (hf && hf->is_lexical)+        return hf;+    else+        return NULL;+}++static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,+                             int scope_idx, BOOL check_catch_var)+{+    while (scope_idx >= 0) {+        JSVarDef *vd = &fd->vars[scope_idx];+        if (vd->var_name == name &&+            (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&+                                check_catch_var)))+            return scope_idx;+        scope_idx = vd->scope_next;+    }++    if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {+        if (find_lexical_hoisted_def(fd, name))+            return GLOBAL_VAR_OFFSET;+    }+    return -1;+}++static int push_scope(JSParseState *s) {+    if (s->cur_func) {+        JSFunctionDef *fd = s->cur_func;+        int scope = fd->scope_count;+        /* XXX: should check for scope overflow */+        if ((fd->scope_count + 1) > fd->scope_size) {+            int new_size;+            size_t slack;+            JSVarScope *new_buf;+            /* XXX: potential arithmetic overflow */+            new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);+            if (fd->scopes == fd->def_scope_array) {+                new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);+                if (!new_buf)+                    return -1;+                memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));+            } else {+                new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);+                if (!new_buf)+                    return -1;+            }+            new_size += slack / sizeof(*new_buf);+            fd->scopes = new_buf;+            fd->scope_size = new_size;+        }+        fd->scope_count++;+        fd->scopes[scope].parent = fd->scope_level;+        fd->scopes[scope].first = fd->scope_first;+        emit_op(s, OP_enter_scope);+        emit_u16(s, scope);+        return fd->scope_level = scope;+    }+    return 0;+}++static int get_first_lexical_var(JSFunctionDef *fd, int scope)+{+    while (scope >= 0) {+        int scope_idx = fd->scopes[scope].first;+        if (scope_idx >= 0)+            return scope_idx;+        scope = fd->scopes[scope].parent;+    }+    return -1;+}++static void pop_scope(JSParseState *s) {+    if (s->cur_func) {+        /* disable scoped variables */+        JSFunctionDef *fd = s->cur_func;+        int scope = fd->scope_level;+        emit_op(s, OP_leave_scope);+        emit_u16(s, scope);+        fd->scope_level = fd->scopes[scope].parent;+        fd->scope_first = get_first_lexical_var(fd, fd->scope_level);+    }+}++static void close_scopes(JSParseState *s, int scope, int scope_stop)+{+    while (scope > scope_stop) {+        emit_op(s, OP_leave_scope);+        emit_u16(s, scope);+        scope = s->cur_func->scopes[scope].parent;+    }+}++/* return the variable index or -1 if error */+static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+    JSVarDef *vd;++    /* the local variable indexes are currently stored on 16 bits */+    if (fd->var_count >= JS_MAX_LOCAL_VARS) {+        JS_ThrowInternalError(ctx, "too many local variables");+        return -1;+    }+    if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),+                        &fd->var_size, fd->var_count + 1))+        return -1;+    vd = &fd->vars[fd->var_count++];+    memset(vd, 0, sizeof(*vd));+    vd->var_name = JS_DupAtom(ctx, name);+    return fd->var_count - 1;+}++static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,+                         JSVarKindEnum var_kind)+{+    int idx = add_var(ctx, fd, name);+    if (idx >= 0) {+        JSVarDef *vd = &fd->vars[idx];+        vd->var_kind = var_kind;+        vd->scope_level = fd->scope_level;+        vd->scope_next = fd->scope_first;+        fd->scopes[fd->scope_level].first = idx;+        fd->scope_first = idx;+    }+    return idx;+}++static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+    int idx = fd->func_var_idx;+    if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {+        fd->func_var_idx = idx;+        fd->vars[idx].is_func_var = TRUE;+        if (fd->js_mode & JS_MODE_STRICT)+            fd->vars[idx].is_const = TRUE;+    }+    return idx;+}++static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+    int idx = fd->arguments_var_idx;+    if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {+        fd->arguments_var_idx = idx;+    }+    return idx;+}++static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+    JSVarDef *vd;++    /* the local variable indexes are currently stored on 16 bits */+    if (fd->arg_count >= JS_MAX_LOCAL_VARS) {+        JS_ThrowInternalError(ctx, "too many arguments");+        return -1;+    }+    if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),+                        &fd->arg_size, fd->arg_count + 1))+        return -1;+    vd = &fd->args[fd->arg_count++];+    memset(vd, 0, sizeof(*vd));+    vd->var_name = JS_DupAtom(ctx, name);+    return fd->arg_count - 1;+}++/* add a Hoisted definition for a function (cpool_idx >= 0) or a+   global variable (cpool_idx = -1) */+static JSHoistedDef *add_hoisted_def(JSContext *ctx,+                                     JSFunctionDef *s, int cpool_idx,+                                     JSAtom name, int var_idx, BOOL is_lexical)+{+    JSHoistedDef *hf;++    if (js_resize_array(ctx, (void **)&s->hoisted_def,+                        sizeof(s->hoisted_def[0]),+                        &s->hoisted_def_size, s->hoisted_def_count + 1))+        return NULL;+    hf = &s->hoisted_def[s->hoisted_def_count++];+    hf->cpool_idx = cpool_idx;+    hf->force_init = 0;+    hf->is_lexical = is_lexical;+    hf->is_const = FALSE;+    hf->var_idx = var_idx;+    hf->scope_level = s->scope_level;+    hf->var_name = JS_ATOM_NULL;+    if (name != JS_ATOM_NULL) {+        hf->var_name = JS_DupAtom(ctx, name);+    }+    return hf;+}++typedef enum {+    JS_VAR_DEF_WITH,+    JS_VAR_DEF_LET,+    JS_VAR_DEF_CONST,+    JS_VAR_DEF_FUNCTION_DECL, /* function declaration */+    JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */+    JS_VAR_DEF_CATCH,+    JS_VAR_DEF_VAR,+} JSVarDefEnum;++static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,+                      JSVarDefEnum var_def_type)+{+    JSContext *ctx = s->ctx;+    JSVarDef *vd;+    int idx;++    switch (var_def_type) {+    case JS_VAR_DEF_WITH:+        idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);+        break;++    case JS_VAR_DEF_LET:+    case JS_VAR_DEF_CONST:+    case JS_VAR_DEF_FUNCTION_DECL:+    case JS_VAR_DEF_NEW_FUNCTION_DECL:+        idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);+        if (idx >= 0) {+            if (idx < GLOBAL_VAR_OFFSET) {+                if (fd->vars[idx].scope_level == fd->scope_level) {+                    /* same scope: in non strict mode, functions+                       can be redefined (annex B.3.3.4). */+                    if (!(!(fd->js_mode & JS_MODE_STRICT) &&+                          var_def_type == JS_VAR_DEF_FUNCTION_DECL &&+                          fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {+                        goto redef_lex_error;+                    }+                } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {+                    goto redef_lex_error;+                }+            } else {+                if (fd->scope_level == 1) {+                redef_lex_error:+                    /* redefining a scoped var in the same scope: error */+                    return js_parse_error(s, "invalid redefinition of lexical identifier");+                }+            }+        }+        if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&+            var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&+            fd->scope_level == 1 &&+            find_arg(ctx, fd, name) >= 0) {+            /* lexical variable redefines a parameter name */+            return js_parse_error(s, "invalid redefinition of parameter name");+        }++        if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {+            return js_parse_error(s, "invalid redefinition of a variable");+        }+        +        if (fd->is_global_var) {+            JSHoistedDef *hf;+            hf = find_hoisted_def(fd, name);+            if (hf && is_child_scope(ctx, fd, hf->scope_level,+                                     fd->scope_level)) {+                return js_parse_error(s, "invalid redefinition of global identifier");+            }+        }+        +        if (fd->is_eval &&+            (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||+             fd->eval_type == JS_EVAL_TYPE_MODULE) &&+            fd->scope_level == 1) {+            JSHoistedDef *hf;+            hf = add_hoisted_def(s->ctx, fd, -1, name, -1, TRUE);+            if (!hf)+                return -1;+            hf->is_const = (var_def_type == JS_VAR_DEF_CONST);+            idx = GLOBAL_VAR_OFFSET;+        } else {+            JSVarKindEnum var_kind;+            if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)+                var_kind = JS_VAR_FUNCTION_DECL;+            else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)+                var_kind = JS_VAR_NEW_FUNCTION_DECL;+            else+                var_kind = JS_VAR_NORMAL;+            idx = add_scope_var(ctx, fd, name, var_kind);+            if (idx >= 0) {+                vd = &fd->vars[idx];+                vd->is_lexical = 1;+                vd->is_const = (var_def_type == JS_VAR_DEF_CONST);+            }+        }+        break;++    case JS_VAR_DEF_CATCH:+        idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);+        break;++    case JS_VAR_DEF_VAR:+        if (find_lexical_decl(ctx, fd, name, fd->scope_first,+                              FALSE) >= 0) {+       invalid_lexical_redefinition:+            /* error to redefine a var that inside a lexical scope */+            return js_parse_error(s, "invalid redefinition of lexical identifier");+        }+        if (fd->is_global_var) {+            JSHoistedDef *hf;+            hf = find_hoisted_def(fd, name);+            if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&+                fd->eval_type == JS_EVAL_TYPE_MODULE) {+                goto invalid_lexical_redefinition;+            }+            hf = add_hoisted_def(s->ctx, fd, -1, name, -1, FALSE);+            if (!hf)+                return -1;+            idx = GLOBAL_VAR_OFFSET;+        } else {+            /* if the variable already exists, don't add it again  */+            idx = find_var(ctx, fd, name);+            if (idx >= 0)+                break;+            idx = add_var(ctx, fd, name);+            if (idx >= 0) {+                if (name == JS_ATOM_arguments && fd->has_arguments_binding)+                    fd->arguments_var_idx = idx;+                fd->vars[idx].func_pool_or_scope_idx = fd->scope_level;+            }+        }+        break;+    default:+        abort();+    }+    return idx;+}++/* add a private field variable in the current scope */+static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,+                                   JSAtom name, JSVarKindEnum var_kind)+{+    JSContext *ctx = s->ctx;+    JSVarDef *vd;+    int idx;++    idx = add_scope_var(ctx, fd, name, var_kind);+    if (idx < 0)+        return idx;+    vd = &fd->vars[idx];+    vd->is_lexical = 1;+    vd->is_const = 1;+    return idx;+}++static __exception int js_parse_expr(JSParseState *s);+static __exception int js_parse_function_decl(JSParseState *s,+                                              JSParseFunctionEnum func_type,+                                              JSFunctionKindEnum func_kind,+                                              JSAtom func_name, const uint8_t *ptr,+                                              int start_line);+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);+static __exception int js_parse_function_decl2(JSParseState *s,+                                               JSParseFunctionEnum func_type,+                                               JSFunctionKindEnum func_kind,+                                               JSAtom func_name,+                                               const uint8_t *ptr,+                                               int function_line_num,+                                               JSParseExportEnum export_flag,+                                               JSFunctionDef **pfd);+static __exception int js_parse_assign_expr(JSParseState *s, int in_accepted);+static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag);+static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,+                             JSAtom label_name,+                             int label_break, int label_cont,+                             int drop_count);+static void pop_break_entry(JSFunctionDef *fd);+static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,+                                       JSAtom local_name, JSAtom export_name,+                                       JSExportTypeEnum export_type);++/* Note: all the fields are already sealed except length */+static int seal_template_obj(JSContext *ctx, JSValueConst obj)+{+    JSObject *p;+    JSShapeProperty *prs;++    p = JS_VALUE_GET_OBJ(obj);+    prs = find_own_property1(p, JS_ATOM_length);+    if (prs) {+        if (js_update_property_flags(ctx, p, &prs,+                                     prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))+            return -1;+    }+    p->extensible = FALSE;+    return 0;+}++static __exception int js_parse_template(JSParseState *s, int call, int *argc)+{+    JSContext *ctx = s->ctx;+    JSValue raw_array, template_object;+    JSToken cooked;+    int depth, ret;++    raw_array = JS_UNDEFINED; /* avoid warning */+    template_object = JS_UNDEFINED; /* avoid warning */+    if (call) {+        /* Create a template object: an array of cooked strings */+        /* Create an array of raw strings and store it to the raw property */+        template_object = JS_NewArray(ctx);+        if (JS_IsException(template_object))+            return -1;+        //        pool_idx = s->cur_func->cpool_count;+        ret = emit_push_const(s, template_object, 0);+        JS_FreeValue(ctx, template_object);+        if (ret)+            return -1;+        raw_array = JS_NewArray(ctx);+        if (JS_IsException(raw_array))+            return -1;+        if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,+                                   raw_array, JS_PROP_THROW) < 0) {+            return -1;+        }+    }++    depth = 0;+    while (s->token.val == TOK_TEMPLATE) {+        const uint8_t *p = s->token.ptr + 1;+        cooked = s->token;+        if (call) {+            if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,+                                             JS_DupValue(ctx, s->token.u.str.str),+                                             JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {+                return -1;+            }+            /* re-parse the string with escape sequences but do not throw a+               syntax error if it contains invalid sequences+             */+            if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {+                cooked.u.str.str = JS_UNDEFINED;+            }+            if (JS_DefinePropertyValueUint32(ctx, template_object, depth,+                                             cooked.u.str.str,+                                             JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {+                return -1;+            }+        } else {+            JSString *str;+            /* re-parse the string with escape sequences and throw a+               syntax error if it contains invalid sequences+             */+            JS_FreeValue(ctx, s->token.u.str.str);+            s->token.u.str.str = JS_UNDEFINED;+            if (js_parse_string(s, '`', TRUE, p, &cooked, &p))+                return -1;+            str = JS_VALUE_GET_STRING(cooked.u.str.str);+            if (str->len != 0 || depth == 0) {+                ret = emit_push_const(s, cooked.u.str.str, 1);+                JS_FreeValue(s->ctx, cooked.u.str.str);+                if (ret)+                    return -1;+                if (depth == 0) {+                    if (s->token.u.str.sep == '`')+                        goto done1;+                    emit_op(s, OP_get_field2);+                    emit_atom(s, JS_ATOM_concat);+                }+                depth++;+            } else {+                JS_FreeValue(s->ctx, cooked.u.str.str);+            }+        }+        if (s->token.u.str.sep == '`')+            goto done;+        if (next_token(s))+            return -1;+        if (js_parse_expr(s))+            return -1;+        depth++;+        if (s->token.val != '}') {+            return js_parse_error(s, "expected '}' after template expression");+        }+        /* XXX: should convert to string at this stage? */+        free_token(s, &s->token);+        /* Resume TOK_TEMPLATE parsing (s->token.line_num and+         * s->token.ptr are OK) */+        s->got_lf = FALSE;+        s->last_line_num = s->token.line_num;+        if (js_parse_template_part(s, s->buf_ptr))+            return -1;+    }+    return js_parse_expect(s, TOK_TEMPLATE);++ done:+    if (call) {+        /* Seal the objects */+        seal_template_obj(ctx, raw_array);+        seal_template_obj(ctx, template_object);+        *argc = depth + 1;+    } else {+        emit_op(s, OP_call_method);+        emit_u16(s, depth - 1);+    }+ done1:+    return next_token(s);+}+++#define PROP_TYPE_IDENT 0+#define PROP_TYPE_VAR   1+#define PROP_TYPE_GET   2+#define PROP_TYPE_SET   3+#define PROP_TYPE_STAR  4+#define PROP_TYPE_ASYNC 5+#define PROP_TYPE_ASYNC_STAR 6++#define PROP_TYPE_PRIVATE (1 << 4)++static BOOL token_is_ident(int tok)+{+    /* Accept keywords and reserved words as property names */+    return (tok == TOK_IDENT ||+            (tok >= TOK_FIRST_KEYWORD &&+             tok <= TOK_LAST_KEYWORD));+}++/* if the property is an expression, name = JS_ATOM_NULL */+static int __exception js_parse_property_name(JSParseState *s,+                                              JSAtom *pname,+                                              BOOL allow_method, BOOL allow_var,+                                              BOOL allow_private)+{+    int is_private = 0;+    BOOL is_non_reserved_ident;+    JSAtom name;+    int prop_type;+    +    prop_type = PROP_TYPE_IDENT;+    if (allow_method) {+        if (token_is_pseudo_keyword(s, JS_ATOM_get)+        ||  token_is_pseudo_keyword(s, JS_ATOM_set)) {+            /* get x(), set x() */+            name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+            if (next_token(s))+                goto fail1;+            if (s->token.val == ':' || s->token.val == ',' ||+                s->token.val == '}' || s->token.val == '(') {+                is_non_reserved_ident = TRUE;+                goto ident_found;+            }+            prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);+            JS_FreeAtom(s->ctx, name);+        } else if (s->token.val == '*') {+            if (next_token(s))+                goto fail;+            prop_type = PROP_TYPE_STAR;+        } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&+                   peek_token(s, TRUE) != '\n') {+            name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+            if (next_token(s))+                goto fail1;+            if (s->token.val == ':' || s->token.val == ',' ||+                s->token.val == '}' || s->token.val == '(') {+                is_non_reserved_ident = TRUE;+                goto ident_found;+            }+            JS_FreeAtom(s->ctx, name);+            if (s->token.val == '*') {+                if (next_token(s))+                    goto fail;+                prop_type = PROP_TYPE_ASYNC_STAR;+            } else {+                prop_type = PROP_TYPE_ASYNC;+            }+        }+    }++    if (token_is_ident(s->token.val)) {+        /* variable can only be a non-reserved identifier */+        is_non_reserved_ident =+            (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);+        /* keywords and reserved words have a valid atom */+        name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+        if (next_token(s))+            goto fail1;+    ident_found:+        if (is_non_reserved_ident &&+            prop_type == PROP_TYPE_IDENT && allow_var) {+            if (!(s->token.val == ':' ||+                  (s->token.val == '(' && allow_method))) {+                prop_type = PROP_TYPE_VAR;+            }+        }+    } else if (s->token.val == TOK_STRING) {+        name = JS_ValueToAtom(s->ctx, s->token.u.str.str);+        if (name == JS_ATOM_NULL)+            goto fail;+        if (next_token(s))+            goto fail1;+    } else if (s->token.val == TOK_NUMBER) {+        JSValue val;+        val = s->token.u.num.val;+#ifdef CONFIG_BIGNUM+        if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            val = s->ctx->rt->bigfloat_ops.+                mul_pow10_to_float64(s->ctx, &p->num,+                                     s->token.u.num.exponent);+            if (JS_IsException(val))+                goto fail;+            name = JS_ValueToAtom(s->ctx, val);+            JS_FreeValue(s->ctx, val);+        } else+#endif+        {+            name = JS_ValueToAtom(s->ctx, val);+        }+        if (name == JS_ATOM_NULL)+            goto fail;+        if (next_token(s))+            goto fail1;+    } else if (s->token.val == '[') {+        if (next_token(s))+            goto fail;+        if (js_parse_expr(s))+            goto fail;+        if (js_parse_expect(s, ']'))+            goto fail;+        name = JS_ATOM_NULL;+    } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {+        name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+        if (next_token(s))+            goto fail1;+        is_private = PROP_TYPE_PRIVATE;+    } else {+        goto invalid_prop;+    }+    if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&+        s->token.val != '(') {+        JS_FreeAtom(s->ctx, name);+    invalid_prop:+        js_parse_error(s, "invalid property name");+        goto fail;+    }+    *pname = name;+    return prop_type | is_private;+ fail1:+    JS_FreeAtom(s->ctx, name);+ fail:+    *pname = JS_ATOM_NULL;+    return -1;+}++typedef struct JSParsePos {+    int last_line_num;+    int line_num;+    BOOL got_lf;+    const uint8_t *ptr;+} JSParsePos;++static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)+{+    sp->last_line_num = s->last_line_num;+    sp->line_num = s->token.line_num;+    sp->ptr = s->token.ptr;+    sp->got_lf = s->got_lf;+    return 0;+}++static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)+{+    s->token.line_num = sp->last_line_num;+    s->line_num = sp->line_num;+    s->buf_ptr = sp->ptr;+    s->got_lf = sp->got_lf;+    return next_token(s);+}++/* return TRUE if a regexp literal is allowed after this token */+static BOOL is_regexp_allowed(int tok)+{+    switch (tok) {+    case TOK_NUMBER:+    case TOK_STRING:+    case TOK_REGEXP:+    case TOK_DEC:+    case TOK_INC:+    case TOK_NULL:+    case TOK_FALSE:+    case TOK_TRUE:+    case TOK_THIS:+    case ')':+    case ']':+    case '}': /* XXX: regexp may occur after */+    case TOK_IDENT:+        return FALSE;+    default:+        return TRUE;+    }+}++#define SKIP_HAS_SEMI      (1 << 0)+#define SKIP_HAS_ELLIPSIS  (1 << 1)++/* XXX: improve speed with early bailout */+/* XXX: no longer works if regexps are present. Could use previous+   regexp parsing heuristics to handle most cases */+static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)+{+    char state[256];+    size_t level = 0;+    JSParsePos pos;+    int last_tok, tok = TOK_EOF;+    int c, tok_len, bits = 0;++    /* protect from underflow */+    state[level++] = 0;++    js_parse_get_pos(s, &pos);+    last_tok = 0;+    for (;;) {+        switch(s->token.val) {+        case '(':+        case '[':+        case '{':+            if (level >= sizeof(state))+                goto done;+            state[level++] = s->token.val;+            break;+        case ')':+            if (state[--level] != '(')+                goto done;+            break;+        case ']':+            if (state[--level] != '[')+                goto done;+            break;+        case '}':+            c = state[--level];+            if (c == '`') {+                /* continue the parsing of the template */+                free_token(s, &s->token);+                /* Resume TOK_TEMPLATE parsing (s->token.line_num and+                 * s->token.ptr are OK) */+                s->got_lf = FALSE;+                s->last_line_num = s->token.line_num;+                if (js_parse_template_part(s, s->buf_ptr))+                    goto done;+                goto handle_template;+            } else if (c != '{') {+                goto done;+            }+            break;+        case TOK_TEMPLATE:+        handle_template:+            if (s->token.u.str.sep != '`') {+                /* '${' inside the template : closing '}' and continue+                   parsing the template */+                if (level >= sizeof(state))+                    goto done;+                state[level++] = '`';+            } +            break;+        case TOK_EOF:+            goto done;+        case ';':+            if (level == 2) {+                bits |= SKIP_HAS_SEMI;+            }+            break;+        case TOK_ELLIPSIS:+            if (level == 2) {+                bits |= SKIP_HAS_ELLIPSIS;+            }+            break;++        case TOK_DIV_ASSIGN:+            tok_len = 2;+            goto parse_regexp;+        case '/':+            tok_len = 1;+        parse_regexp:+            if (is_regexp_allowed(last_tok)) {+                s->buf_ptr -= tok_len;+                if (js_parse_regexp(s)) {+                    /* XXX: should clear the exception */+                    goto done;+                }+            }+            break;+        }+        /* last_tok is only used to recognize regexps */+        if (s->token.val == TOK_IDENT &&+            (token_is_pseudo_keyword(s, JS_ATOM_of) ||+             token_is_pseudo_keyword(s, JS_ATOM_yield))) {+            last_tok = TOK_OF;+        } else {+            last_tok = s->token.val;+        }+        if (next_token(s)) {+            /* XXX: should clear the exception generated by next_token() */+            break;+        }+        if (level <= 1) {+            tok = s->token.val;+            if (token_is_pseudo_keyword(s, JS_ATOM_of))+                tok = TOK_OF;+            if (no_line_terminator && s->last_line_num != s->token.line_num)+                tok = '\n';+            break;+        }+    }+ done:+    if (pbits) {+        *pbits = bits;+    }+    if (js_parse_seek_token(s, &pos))+        return -1;+    return tok;+}++static void set_object_name(JSParseState *s, JSAtom name)+{+    JSFunctionDef *fd = s->cur_func;+    int opcode;++    opcode = get_prev_opcode(fd);+    if (opcode == OP_set_name) {+        /* XXX: should free atom after OP_set_name? */+        fd->byte_code.size = fd->last_opcode_pos;+        fd->last_opcode_pos = -1;+        emit_op(s, OP_set_name);+        emit_atom(s, name);+    } else if (opcode == OP_set_class_name) {+        int define_class_pos;+        JSAtom atom;+        define_class_pos = fd->last_opcode_pos + 1 -+            get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+        assert(fd->byte_code.buf[define_class_pos] == OP_define_class);+        /* for consistency we free the previous atom which is+           JS_ATOM_empty_string */+        atom = get_u32(fd->byte_code.buf + define_class_pos + 1);+        JS_FreeAtom(s->ctx, atom);+        put_u32(fd->byte_code.buf + define_class_pos + 1,+                JS_DupAtom(s->ctx, name));+        fd->last_opcode_pos = -1;+    }+}++static void set_object_name_computed(JSParseState *s)+{+    JSFunctionDef *fd = s->cur_func;+    int opcode;++    opcode = get_prev_opcode(fd);+    if (opcode == OP_set_name) {+        /* XXX: should free atom after OP_set_name? */+        fd->byte_code.size = fd->last_opcode_pos;+        fd->last_opcode_pos = -1;+        emit_op(s, OP_set_name_computed);+    } else if (opcode == OP_set_class_name) {+        int define_class_pos;+        define_class_pos = fd->last_opcode_pos + 1 -+            get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+        assert(fd->byte_code.buf[define_class_pos] == OP_define_class);+        fd->byte_code.buf[define_class_pos] = OP_define_class_computed;+        fd->last_opcode_pos = -1;+    }+}++static __exception int js_parse_object_literal(JSParseState *s)+{+    JSAtom name = JS_ATOM_NULL;+    const uint8_t *start_ptr;+    int start_line, prop_type;+    BOOL has_proto;++    if (next_token(s))+        goto fail;+    /* XXX: add an initial length that will be patched back */+    emit_op(s, OP_object);+    has_proto = FALSE;+    while (s->token.val != '}') {+        /* specific case for getter/setter */+        start_ptr = s->token.ptr;+        start_line = s->token.line_num;++        if (s->token.val == TOK_ELLIPSIS) {+            if (next_token(s))+                return -1;+            if (js_parse_assign_expr(s, TRUE))+                return -1;+            emit_op(s, OP_null);  /* dummy excludeList */+            emit_op(s, OP_copy_data_properties);+            emit_u8(s, 2 | (1 << 2) | (0 << 5));+            emit_op(s, OP_drop); /* pop excludeList */+            emit_op(s, OP_drop); /* pop src object */+            goto next;+        }++        prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);+        if (prop_type < 0)+            goto fail;++        if (prop_type == PROP_TYPE_VAR) {+            /* shortcut for x: x */+            emit_op(s, OP_scope_get_var);+            emit_atom(s, name);+            emit_u16(s, s->cur_func->scope_level);+            emit_op(s, OP_define_field);+            emit_atom(s, name);+        } else if (s->token.val == '(') {+            BOOL is_getset = (prop_type == PROP_TYPE_GET ||+                              prop_type == PROP_TYPE_SET);+            JSParseFunctionEnum func_type;+            JSFunctionKindEnum func_kind;+            int op_flags;++            func_kind = JS_FUNC_NORMAL;+            if (is_getset) {+                func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;+            } else {+                func_type = JS_PARSE_FUNC_METHOD;+                if (prop_type == PROP_TYPE_STAR)+                    func_kind = JS_FUNC_GENERATOR;+                else if (prop_type == PROP_TYPE_ASYNC)+                    func_kind = JS_FUNC_ASYNC;+                else if (prop_type == PROP_TYPE_ASYNC_STAR)+                    func_kind = JS_FUNC_ASYNC_GENERATOR;+            }+            if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,+                                       start_ptr, start_line))+                goto fail;+            if (name == JS_ATOM_NULL) {+                emit_op(s, OP_define_method_computed);+            } else {+                emit_op(s, OP_define_method);+                emit_atom(s, name);+            }+            if (is_getset) {+                op_flags = OP_DEFINE_METHOD_GETTER ++                    prop_type - PROP_TYPE_GET;+            } else {+                op_flags = OP_DEFINE_METHOD_METHOD;+            }+            emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);+        } else {+            if (js_parse_expect(s, ':'))+                goto fail;+            if (js_parse_assign_expr(s, TRUE))+                goto fail;+            if (name == JS_ATOM_NULL) {+                set_object_name_computed(s);+                emit_op(s, OP_define_array_el);+                emit_op(s, OP_drop);+            } else if (name == JS_ATOM___proto__) {+                if (has_proto) {+                    js_parse_error(s, "duplicate __proto__ property name");+                    goto fail;+                }+                emit_op(s, OP_set_proto);+                has_proto = TRUE;+            } else {+                set_object_name(s, name);+                emit_op(s, OP_define_field);+                emit_atom(s, name);+            }+        }+        JS_FreeAtom(s->ctx, name);+    next:+        name = JS_ATOM_NULL;+        if (s->token.val != ',')+            break;+        if (next_token(s))+            goto fail;+    }+    if (js_parse_expect(s, '}'))+        goto fail;+    return 0;+ fail:+    JS_FreeAtom(s->ctx, name);+    return -1;+}++static __exception int js_parse_postfix_expr(JSParseState *s,+                                             BOOL accept_lparen);++/* XXX: is there is nicer solution ? */+static __exception int js_parse_class_default_ctor(JSParseState *s,+                                                   BOOL has_super,+                                                   JSFunctionDef **pfd)+{+    JSParsePos pos;+    const char *str;+    int ret, line_num;+    JSParseFunctionEnum func_type;+    const uint8_t *saved_buf_end;+    +    js_parse_get_pos(s, &pos);+    if (has_super) {+        str = "(...a){super(...a);}";+        func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;+    } else {+        str = "(){}";+        func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;+    }+    line_num = s->token.line_num;+    saved_buf_end = s->buf_end;+    s->buf_ptr = (uint8_t *)str;+    s->buf_end = (uint8_t *)(str + strlen(str));+    ret = next_token(s);+    if (!ret) {+        ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,+                                      JS_ATOM_NULL, (uint8_t *)str,+                                      line_num, JS_PARSE_EXPORT_NONE, pfd);+    }+    s->buf_end = saved_buf_end;+    ret |= js_parse_seek_token(s, &pos);+    return ret;+}++/* find field in the current scope */+static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,+                                    JSAtom name, int scope_level)+{+    int idx;+    idx = fd->scopes[scope_level].first;+    while (idx != -1) {+        if (fd->vars[idx].scope_level != scope_level)+            break;+        if (fd->vars[idx].var_name == name)+            return idx;+        idx = fd->vars[idx].scope_next;+    }+    return -1;+}++/* initialize the class fields, called by the constructor. Note:+   super() can be called in an arrow function, so <this> and+   <class_fields_init> can be variable references */+static void emit_class_field_init(JSParseState *s)+{+    int label_next;+    +    emit_op(s, OP_scope_get_var);+    emit_atom(s, JS_ATOM_class_fields_init);+    emit_u16(s, s->cur_func->scope_level);++    /* no need to call the class field initializer if not defined */+    emit_op(s, OP_dup);+    label_next = emit_goto(s, OP_if_false, -1);+    +    emit_op(s, OP_scope_get_var);+    emit_atom(s, JS_ATOM_this);+    emit_u16(s, 0);+    +    emit_op(s, OP_swap);+    +    emit_op(s, OP_call_method);+    emit_u16(s, 0);++    emit_label(s, label_next);+    emit_op(s, OP_drop);+}++/* build a private setter function name from the private getter name */+static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)+{+    return js_atom_concat_str(ctx, name, "<set>");+}++typedef struct {+    JSFunctionDef *fields_init_fd;+    int computed_fields_count;+    BOOL has_brand;+    int brand_push_pos;+} ClassFieldsDef;++static __exception int emit_class_init_start(JSParseState *s,+                                             ClassFieldsDef *cf)+{+    int label_add_brand;+    +    cf->fields_init_fd = js_parse_function_class_fields_init(s);+    if (!cf->fields_init_fd)+        return -1;++    s->cur_func = cf->fields_init_fd;+    +    /* XXX: would be better to add the code only if needed, maybe in a+       later pass */+    emit_op(s, OP_push_false); /* will be patched later */+    cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;+    label_add_brand = emit_goto(s, OP_if_false, -1);+    +    emit_op(s, OP_scope_get_var);+    emit_atom(s, JS_ATOM_this);+    emit_u16(s, 0);+    +    emit_op(s, OP_scope_get_var);+    emit_atom(s, JS_ATOM_home_object);+    emit_u16(s, 0);+    +    emit_op(s, OP_add_brand);+    +    emit_label(s, label_add_brand);++    s->cur_func = s->cur_func->parent;+    return 0;+}++static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)+{+    if (!cf->has_brand) {+        /* define the brand field in 'this' of the initializer */+        if (!cf->fields_init_fd) {+            if (emit_class_init_start(s, cf))+                return -1;+        }+        /* patch the start of the function to enable the OP_add_brand code */+        cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;+        +        cf->has_brand = TRUE;+    }+    return 0;+}++static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)+{+    int cpool_idx;+        +    s->cur_func = cf->fields_init_fd;+    emit_op(s, OP_return_undef);+    s->cur_func = s->cur_func->parent;+    +    cpool_idx = cpool_add(s, JS_NULL);+    cf->fields_init_fd->parent_cpool_idx = cpool_idx;+    emit_op(s, OP_fclosure);+    emit_u32(s, cpool_idx);+    emit_op(s, OP_set_home_object);+}+++static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,+                                      JSParseExportEnum export_flag)+{+    JSContext *ctx = s->ctx;+    JSFunctionDef *fd = s->cur_func;+    JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;+    JSAtom class_var_name = JS_ATOM_NULL;+    JSFunctionDef *method_fd, *ctor_fd;+    int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;+    int class_flags = 0, i, define_class_offset;+    BOOL is_static, is_private;+    const uint8_t *class_start_ptr = s->token.ptr;+    const uint8_t *start_ptr;+    ClassFieldsDef class_fields[2];+        +    /* classes are parsed and executed in strict mode */+    saved_js_mode = fd->js_mode;+    fd->js_mode |= JS_MODE_STRICT;+    if (next_token(s))+        goto fail;+    if (s->token.val == TOK_IDENT) {+        if (s->token.u.ident.is_reserved) {+            js_parse_error_reserved_identifier(s);+            goto fail;+        }+        class_name = JS_DupAtom(ctx, s->token.u.ident.atom);+        if (next_token(s))+            goto fail;+    } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {+        js_parse_error(s, "class statement requires a name");+        goto fail;+    }+    if (!is_class_expr) {+        if (class_name == JS_ATOM_NULL)+            class_var_name = JS_ATOM__default_; /* export default */+        else+            class_var_name = class_name;+        class_var_name = JS_DupAtom(ctx, class_var_name);+    }++    push_scope(s);++    if (s->token.val == TOK_EXTENDS) {+        class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;+        if (next_token(s))+            goto fail;+        /* XXX: the grammar only allows LeftHandSideExpression */+        if (js_parse_postfix_expr(s, TRUE))+            goto fail;+    } else {+        emit_op(s, OP_undefined);+    }++    /* add a 'const' definition for the class name */+    if (class_name != JS_ATOM_NULL) {+        class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);+        if (class_name_var_idx < 0)+            goto fail;+    }++    if (js_parse_expect(s, '{'))+        goto fail;++    /* this scope contains the private fields */+    push_scope(s);++    emit_op(s, OP_push_const);+    ctor_cpool_offset = fd->byte_code.size;+    emit_u32(s, 0); /* will be patched at the end of the class parsing */++    if (class_name == JS_ATOM_NULL) {+        if (class_var_name != JS_ATOM_NULL)+            class_name1 = JS_ATOM_default;+        else+            class_name1 = JS_ATOM_empty_string;+    } else {+        class_name1 = class_name;+    }+    +    emit_op(s, OP_define_class);+    emit_atom(s, class_name1);+    emit_u8(s, class_flags);+    define_class_offset = fd->last_opcode_pos;++    for(i = 0; i < 2; i++) {+        ClassFieldsDef *cf = &class_fields[i];+        cf->fields_init_fd = NULL;+        cf->computed_fields_count = 0;+        cf->has_brand = FALSE;+    }+    +    ctor_fd = NULL;+    while (s->token.val != '}') {+        if (s->token.val == ';') {+            if (next_token(s))+                goto fail;+            continue;+        }+        is_static = (s->token.val == TOK_STATIC);+        prop_type = -1;+        if (is_static) {+            if (next_token(s))+                goto fail;+            /* allow "static" field name */+            if (s->token.val == ';' || s->token.val == '=') {+                is_static = FALSE;+                name = JS_DupAtom(ctx, JS_ATOM_static);+                prop_type = PROP_TYPE_IDENT;+            }+        }+        if (is_static)+            emit_op(s, OP_swap);+        start_ptr = s->token.ptr;+        if (prop_type < 0) {+            prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);+            if (prop_type < 0)+                goto fail;+        }+        is_private = prop_type & PROP_TYPE_PRIVATE;+        prop_type &= ~PROP_TYPE_PRIVATE;+        +        if ((name == JS_ATOM_constructor && !is_static &&+             prop_type != PROP_TYPE_IDENT) ||+            (name == JS_ATOM_prototype && is_static) ||+            name == JS_ATOM_hash_constructor) {+            js_parse_error(s, "invalid method name");+            goto fail;+        }+        if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {+            BOOL is_set = prop_type - PROP_TYPE_GET;+            JSFunctionDef *method_fd;++            if (is_private) {+                int idx, var_kind;+                idx = find_private_class_field(ctx, fd, name, fd->scope_level);+                if (idx >= 0) {+                    var_kind = fd->vars[idx].var_kind;+                    if (var_kind == JS_VAR_PRIVATE_FIELD ||+                        var_kind == JS_VAR_PRIVATE_METHOD ||+                        var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||+                        var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {+                        goto private_field_already_defined;+                    }+                    fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;+                } else {+                    if (add_private_class_field(s, fd, name,+                                                JS_VAR_PRIVATE_GETTER + is_set) < 0)+                        goto fail;+                }+                if (add_brand(s, &class_fields[is_static]) < 0)+                    goto fail;+            }++            if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,+                                        JS_FUNC_NORMAL, JS_ATOM_NULL,+                                        start_ptr, s->token.line_num,+                                        JS_PARSE_EXPORT_NONE, &method_fd))+                goto fail;+            if (is_private) {+                method_fd->need_home_object = TRUE; /* needed for brand check */+                emit_op(s, OP_set_home_object);+                /* XXX: missing function name */+                emit_op(s, OP_scope_put_var_init);+                if (is_set) {+                    JSAtom setter_name;+                    int ret;+                    +                    setter_name = get_private_setter_name(ctx, name);+                    if (setter_name == JS_ATOM_NULL)+                        goto fail;+                    emit_atom(s, setter_name);+                    ret = add_private_class_field(s, fd, setter_name,+                                                  JS_VAR_PRIVATE_SETTER);+                    JS_FreeAtom(ctx, setter_name);+                    if (ret < 0)+                        goto fail;+                } else {+                    emit_atom(s, name);+                }+                emit_u16(s, s->cur_func->scope_level);+            } else {+                if (name == JS_ATOM_NULL) {+                    emit_op(s, OP_define_method_computed);+                } else {+                    emit_op(s, OP_define_method);+                    emit_atom(s, name);+                }+                emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);+            }+        } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {+            ClassFieldsDef *cf = &class_fields[is_static];+            JSAtom field_var_name = JS_ATOM_NULL;+            +            /* class field */++            /* XXX: spec: not consistent with method name checks */+            if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {+                js_parse_error(s, "invalid field name");+                goto fail;+            }+            +            if (is_private) {+                if (find_private_class_field(ctx, fd, name,+                                             fd->scope_level) >= 0) {+                    goto private_field_already_defined;+                }+                if (add_private_class_field(s, fd, name,+                                            JS_VAR_PRIVATE_FIELD) < 0)+                    goto fail;+                emit_op(s, OP_private_symbol);+                emit_atom(s, name);+                emit_op(s, OP_scope_put_var_init);+                emit_atom(s, name);+                emit_u16(s, s->cur_func->scope_level);+            }++            if (!cf->fields_init_fd) {+                if (emit_class_init_start(s, cf))+                    goto fail;+            }+            if (name == JS_ATOM_NULL ) {+                /* save the computed field name into a variable */+                field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);+                if (field_var_name == JS_ATOM_NULL)+                    goto fail;+                if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {+                    JS_FreeAtom(ctx, field_var_name);+                    goto fail;+                }+                emit_op(s, OP_to_propkey);+                emit_op(s, OP_scope_put_var_init);+                emit_atom(s, field_var_name);+                emit_u16(s, s->cur_func->scope_level);+            }+            s->cur_func = cf->fields_init_fd;+            emit_op(s, OP_scope_get_var);+            emit_atom(s, JS_ATOM_this);+            emit_u16(s, 0);++            if (name == JS_ATOM_NULL) {+                emit_op(s, OP_scope_get_var);+                emit_atom(s, field_var_name);+                emit_u16(s, s->cur_func->scope_level);+                cf->computed_fields_count++;+                JS_FreeAtom(ctx, field_var_name);+            } else if (is_private) {+                emit_op(s, OP_scope_get_var);+                emit_atom(s, name);+                emit_u16(s, s->cur_func->scope_level);+            }+            +            if (s->token.val == '=') {+                if (next_token(s))+                    goto fail;+                if (js_parse_assign_expr(s, TRUE))+                    goto fail;+            } else {+                emit_op(s, OP_undefined);+            }+            if (is_private) {+                set_object_name_computed(s);+                emit_op(s, OP_define_private_field);+            } else if (name == JS_ATOM_NULL) {+                set_object_name_computed(s);+                emit_op(s, OP_define_array_el);+                emit_op(s, OP_drop);+            } else {+                set_object_name(s, name);+                emit_op(s, OP_define_field);+                emit_atom(s, name);+            }+            s->cur_func = s->cur_func->parent;+            if (js_parse_expect_semi(s))+                goto fail;+        } else {+            JSParseFunctionEnum func_type;+            JSFunctionKindEnum func_kind;+            +            func_type = JS_PARSE_FUNC_METHOD;+            func_kind = JS_FUNC_NORMAL;+            if (prop_type == PROP_TYPE_STAR) {+                func_kind = JS_FUNC_GENERATOR;+            } else if (prop_type == PROP_TYPE_ASYNC) {+                func_kind = JS_FUNC_ASYNC;+            } else if (prop_type == PROP_TYPE_ASYNC_STAR) {+                func_kind = JS_FUNC_ASYNC_GENERATOR;+            } else if (name == JS_ATOM_constructor && !is_static) {+                if (ctor_fd) {+                    js_parse_error(s, "property constructor appears more than once");+                    goto fail;+                }+                if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)+                    func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;+                else+                    func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;+            }+            if (is_private) {+                if (add_brand(s, &class_fields[is_static]) < 0)+                    goto fail;+            }+            if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))+                goto fail;+            if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||+                func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {+                ctor_fd = method_fd;+            } else if (is_private) {+                method_fd->need_home_object = TRUE; /* needed for brand check */+                if (find_private_class_field(ctx, fd, name,+                                             fd->scope_level) >= 0) {+                private_field_already_defined:+                    js_parse_error(s, "private class field is already defined");+                    goto fail;+                }+                if (add_private_class_field(s, fd, name,+                                            JS_VAR_PRIVATE_METHOD) < 0)+                    goto fail;+                emit_op(s, OP_set_home_object);+                emit_op(s, OP_set_name);+                emit_atom(s, name);+                emit_op(s, OP_scope_put_var_init);+                emit_atom(s, name);+                emit_u16(s, s->cur_func->scope_level);+            } else {+                if (name == JS_ATOM_NULL) {+                    emit_op(s, OP_define_method_computed);+                } else {+                    emit_op(s, OP_define_method);+                    emit_atom(s, name);+                }+                emit_u8(s, OP_DEFINE_METHOD_METHOD);+            }+        }+        if (is_static)+            emit_op(s, OP_swap);+        JS_FreeAtom(ctx, name);+        name = JS_ATOM_NULL;+    }++    if (s->token.val != '}') {+        js_parse_error(s, "expecting '%c'", '}');+        goto fail;+    }++    if (!ctor_fd) {+        if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))+            goto fail;+    }+    /* patch the constant pool index for the constructor */+    put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);++    /* store the class source code in the constructor. */+    if (!(fd->js_mode & JS_MODE_STRIP)) {+        js_free(ctx, ctor_fd->source);+        ctor_fd->source_len = s->buf_ptr - class_start_ptr;+        ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,+                                     ctor_fd->source_len);+        if (!ctor_fd->source)+            goto fail;+    }++    /* consume the '}' */+    if (next_token(s))+        goto fail;++    /* store the function to initialize the fields to that it can be+       referenced by the constructor */+    {+        ClassFieldsDef *cf = &class_fields[0];+        int var_idx;+        +        var_idx = define_var(s, fd, JS_ATOM_class_fields_init,+                             JS_VAR_DEF_CONST);+        if (var_idx < 0)+            goto fail;+        if (cf->fields_init_fd) {+            emit_class_init_end(s, cf);+        } else {+            emit_op(s, OP_undefined);+        }+        emit_op(s, OP_scope_put_var_init);+        emit_atom(s, JS_ATOM_class_fields_init);+        emit_u16(s, s->cur_func->scope_level);+    }++    /* drop the prototype */+    emit_op(s, OP_drop);++    /* initialize the static fields */+    if (class_fields[1].fields_init_fd != NULL) {+        ClassFieldsDef *cf = &class_fields[1];+        emit_op(s, OP_dup);+        emit_class_init_end(s, cf);+        emit_op(s, OP_call_method);+        emit_u16(s, 0);+        emit_op(s, OP_drop);+    }+    +    if (class_name != JS_ATOM_NULL) {+        /* store the class name in the scoped class name variable (it+           is independent from the class statement variable+           definition) */+        emit_op(s, OP_dup);+        emit_op(s, OP_scope_put_var_init);+        emit_atom(s, class_name);+        emit_u16(s, fd->scope_level);+    }+    pop_scope(s);+    pop_scope(s);++    /* the class statements have a block level scope */+    if (class_var_name != JS_ATOM_NULL) {+        if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)+            goto fail;+        emit_op(s, OP_scope_put_var_init);+        emit_atom(s, class_var_name);+        emit_u16(s, fd->scope_level);+    } else {+        if (class_name == JS_ATOM_NULL) {+            /* cannot use OP_set_name because the name of the class+               must be defined before the static initializers are+               executed */+            emit_op(s, OP_set_class_name);+            emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);+        }+    }++    if (export_flag != JS_PARSE_EXPORT_NONE) {+        if (!add_export_entry(s, fd->module,+                              class_var_name,+                              export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,+                              JS_EXPORT_TYPE_LOCAL))+            goto fail;+    }++    JS_FreeAtom(ctx, class_name);+    JS_FreeAtom(ctx, class_var_name);+    fd->js_mode = saved_js_mode;+    return 0;+ fail:+    JS_FreeAtom(ctx, name);+    JS_FreeAtom(ctx, class_name);+    JS_FreeAtom(ctx, class_var_name);+    fd->js_mode = saved_js_mode;+    return -1;+}++static __exception int js_parse_array_literal(JSParseState *s)+{+    uint32_t idx;+    BOOL need_length;++    if (next_token(s))+        return -1;+    /* small regular arrays are created on the stack */+    idx = 0;+    while (s->token.val != ']' && idx < 32) {+        if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)+            break;+        if (js_parse_assign_expr(s, TRUE))+            return -1;+        idx++;+        /* accept trailing comma */+        if (s->token.val == ',') {+            if (next_token(s))+                return -1;+        } else+        if (s->token.val != ']')+            goto done;+    }+    emit_op(s, OP_array_from);+    emit_u16(s, idx);++    /* larger arrays and holes are handled with explicit indices */+    need_length = FALSE;+    while (s->token.val != ']' && idx < 0x7fffffff) {+        if (s->token.val == TOK_ELLIPSIS)+            break;+        need_length = TRUE;+        if (s->token.val != ',') {+            if (js_parse_assign_expr(s, TRUE))+                return -1;+            emit_op(s, OP_define_field);+            emit_u32(s, __JS_AtomFromUInt32(idx));+            need_length = FALSE;+        }+        idx++;+        /* accept trailing comma */+        if (s->token.val == ',') {+            if (next_token(s))+                return -1;+        }+    }+    if (s->token.val == ']') {+        if (need_length) {+            /* Set the length: Cannot use OP_define_field because+               length is not configurable */+            emit_op(s, OP_dup);+            emit_op(s, OP_push_i32);+            emit_u32(s, idx);+            emit_op(s, OP_put_field);+            emit_atom(s, JS_ATOM_length);+        }+        goto done;+    }++    /* huge arrays and spread elements require a dynamic index on the stack */+    emit_op(s, OP_push_i32);+    emit_u32(s, idx);++    /* stack has array, index */+    while (s->token.val != ']') {+        if (s->token.val == TOK_ELLIPSIS) {+            if (next_token(s))+                return -1;+            if (js_parse_assign_expr(s, TRUE))+                return -1;+#if 1+            emit_op(s, OP_append);+#else+            int label_next, label_done;+            label_next = new_label(s);+            label_done = new_label(s);+            /* enumerate object */+            emit_op(s, OP_for_of_start);+            emit_op(s, OP_rot5l);+            emit_op(s, OP_rot5l);+            emit_label(s, label_next);+            /* on stack: enum_rec array idx */+            emit_op(s, OP_for_of_next);+            emit_u8(s, 2);+            emit_goto(s, OP_if_true, label_done);+            /* append element */+            /* enum_rec array idx val -> enum_rec array new_idx */+            emit_op(s, OP_define_array_el);+            emit_op(s, OP_inc);+            emit_goto(s, OP_goto, label_next);+            emit_label(s, label_done);+            /* close enumeration */+            emit_op(s, OP_drop); /* drop undef val */+            emit_op(s, OP_nip1); /* drop enum_rec */+            emit_op(s, OP_nip1);+            emit_op(s, OP_nip1);+#endif+        } else {+            need_length = TRUE;+            if (s->token.val != ',') {+                if (js_parse_assign_expr(s, TRUE))+                    return -1;+                /* a idx val */+                emit_op(s, OP_define_array_el);+                need_length = FALSE;+            }+            emit_op(s, OP_inc);+        }+        if (s->token.val != ',')+            break;+        if (next_token(s))+            return -1;+    }+    if (need_length) {+        /* Set the length: cannot use OP_define_field because+           length is not configurable */+        emit_op(s, OP_dup1);    /* array length - array array length */+        emit_op(s, OP_put_field);+        emit_atom(s, JS_ATOM_length);+    } else {+        emit_op(s, OP_drop);    /* array length - array */+    }+done:+    return js_parse_expect(s, ']');+}++/* XXX: remove */+static BOOL has_with_scope(JSFunctionDef *s, int scope_level)+{+    /* check if scope chain contains a with statement */+    while (s) {+        int scope_idx = s->scopes[scope_level].first;+        while (scope_idx >= 0) {+            JSVarDef *vd = &s->vars[scope_idx];++            if (vd->var_name == JS_ATOM__with_)+                return TRUE;+            scope_idx = vd->scope_next;+        }+        /* check parent scopes */+        scope_level = s->parent_scope_level;+        s = s->parent;+    }+    return FALSE;+}++typedef struct JSLValue {+    int opcode;+    int scope;+    int label;+    int depth;+    int tok;+    JSAtom name;+} JSLValue;++static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,+                                  JSAtom *pname, int *plabel, int *pdepth, BOOL keep,+                                  int tok)+{+    JSFunctionDef *fd;+    int opcode, scope, label, depth;+    JSAtom name;++    /* we check the last opcode to get the lvalue type */+    fd = s->cur_func;+    scope = 0;+    name = JS_ATOM_NULL;+    label = -1;+    depth = 0;+    switch(opcode = get_prev_opcode(fd)) {+    case OP_scope_get_var:+        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+        scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);+        if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&+            (fd->js_mode & JS_MODE_STRICT)) {+            return js_parse_error(s, "invalid lvalue in strict mode");+        }+        if (name == JS_ATOM_this || name == JS_ATOM_new_target)+            goto invalid_lvalue;+        depth = 2;  /* will generate OP_get_ref_value */+        break;+    case OP_get_field:+        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+        depth = 1;+        break;+    case OP_scope_get_private_field:+        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+        scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);+        depth = 1;+        break;+    case OP_get_array_el:+        depth = 2;+        break;+    case OP_get_super_value:+        depth = 3;+        break;+    default:+    invalid_lvalue:+        if (tok == TOK_FOR) {+            return js_parse_error(s, "invalid for in/of left hand-side");+        } else if (tok == TOK_INC || tok == TOK_DEC) {+            return js_parse_error(s, "invalid increment/decrement operand");+        } else if (tok == '[' || tok == '{') {+            return js_parse_error(s, "invalid destructuring target");+        } else {+            return js_parse_error(s, "invalid assignment left-hand side");+        }+    }+    /* remove the last opcode */+    fd->byte_code.size = fd->last_opcode_pos;+    fd->last_opcode_pos = -1;++    if (keep) {+        /* get the value but keep the object/fields on the stack */+        switch(opcode) {+        case OP_scope_get_var:+            label = new_label(s);+            emit_op(s, OP_scope_make_ref);+            emit_atom(s, name);+            emit_u32(s, label);+            emit_u16(s, scope);+            update_label(fd, label, 1);+            emit_op(s, OP_get_ref_value);+            opcode = OP_get_ref_value;+            break;+        case OP_get_field:+            emit_op(s, OP_get_field2);+            emit_atom(s, name);+            break;+        case OP_scope_get_private_field:+            emit_op(s, OP_scope_get_private_field2);+            emit_atom(s, name);+            emit_u16(s, scope);+            break;+        case OP_get_array_el:+            /* XXX: replace by a single opcode ? */+            emit_op(s, OP_to_propkey2);+            emit_op(s, OP_dup2);+            emit_op(s, OP_get_array_el);+            break;+        case OP_get_super_value:+            emit_op(s, OP_to_propkey);+            emit_op(s, OP_dup3);+            emit_op(s, OP_get_super_value);+            break;+        default:+            abort();+        }+    } else {+        switch(opcode) {+        case OP_scope_get_var:+            label = new_label(s);+            emit_op(s, OP_scope_make_ref);+            emit_atom(s, name);+            emit_u32(s, label);+            emit_u16(s, scope);+            update_label(fd, label, 1);+            opcode = OP_get_ref_value;+            break;+        case OP_get_array_el:+            emit_op(s, OP_to_propkey2);+            break;+        case OP_get_super_value:+            emit_op(s, OP_to_propkey);+            break;+        }+    }++    *popcode = opcode;+    *pscope = scope;+    /* name has refcount for OP_get_field and OP_get_ref_value,+       and JS_ATOM_NULL for other opcodes */+    *pname = name;+    *plabel = label;+    if (pdepth)+        *pdepth = depth;+    return 0;+}++/* if special = TRUE: specific post inc/dec case */+/* name has a live reference */+static void put_lvalue(JSParseState *s, int opcode, int scope,+                       JSAtom name, int label, BOOL special)+{+    switch(opcode) {+    case OP_get_field:+        if (!special)+            emit_op(s, OP_insert2); /* obj v -> v obj v */+        else+            emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */+        emit_op(s, OP_put_field);+        emit_u32(s, name);  /* name has refcount */+        break;+    case OP_scope_get_private_field:+        if (!special)+            emit_op(s, OP_insert2); /* obj v -> v obj v */+        else+            emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */+        emit_op(s, OP_scope_put_private_field);+        emit_u32(s, name);  /* name has refcount */+        emit_u16(s, scope);+        break;+    case OP_get_array_el:+        if (!special)+            emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */+        else+            emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */+        emit_op(s, OP_put_array_el);+        break;+    case OP_get_ref_value:+        JS_FreeAtom(s->ctx, name);+        emit_label(s, label);+        if (!special)+            emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */+        else+            emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */+        emit_op(s, OP_put_ref_value);+        break;+    case OP_get_super_value:+        if (!special)+            emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */+        else+            emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */+        emit_op(s, OP_put_super_value);+        break;+    default:+        abort();+    }+}++static void put_lvalue_nokeep(JSParseState *s, int opcode, int scope,+                              JSAtom name, int label, int var_tok)+{+    switch(opcode) {+    case OP_scope_get_var:  /* val -- */+        emit_op(s, (var_tok == TOK_CONST || var_tok == TOK_LET) ?+                OP_scope_put_var_init : OP_scope_put_var);+        emit_u32(s, name);  /* has refcount */+        emit_u16(s, scope);+        break;+    case OP_get_field:      /* obj val -- */+        emit_op(s, OP_put_field);+        emit_u32(s, name);  /* has refcount */+        break;+    case OP_scope_get_private_field:+        emit_op(s, OP_scope_put_private_field);+        emit_u32(s, name);  /* has refcount */+        emit_u16(s, scope);+        break;+    case OP_get_array_el:   /* obj prop val -- */+        emit_op(s, OP_put_array_el);+        break;+    case OP_get_ref_value:   /* obj prop val -- */+        /* XXX: currently this reference is never optimized */+        JS_FreeAtom(s->ctx, name);+        emit_label(s, label);+        //emit_op(s, OP_nop);   /* emit 2 bytes for optimizer */+        emit_op(s, OP_put_ref_value);+        break;+    case OP_get_super_value:+        emit_op(s, OP_put_super_value);+        break;+    default:+        abort();+    }+}++static __exception int js_parse_expr_paren(JSParseState *s)+{+    if (js_parse_expect(s, '('))+        return -1;+    if (js_parse_expr(s))+        return -1;+    if (js_parse_expect(s, ')'))+        return -1;+    return 0;+}++static int js_unsupported_keyword(JSParseState *s, JSAtom atom)+{+    char buf[ATOM_GET_STR_BUF_SIZE];+    return js_parse_error(s, "unsupported keyword: %s",+                          JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));+}++static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)+{+    JSFunctionDef *fd = s->cur_func;+    JSVarDefEnum var_def_type;+    +    if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {+        return js_parse_error(s, "yield is a reserved identifier");+    }+    if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)+    &&  (fd->js_mode & JS_MODE_STRICT)) {+        return js_parse_error(s, "invalid variable name in strict mode");+    }+    if ((name == JS_ATOM_let || name == JS_ATOM_undefined)+    &&  (tok == TOK_LET || tok == TOK_CONST)) {+        return js_parse_error(s, "invalid lexical variable name");+    }+    switch(tok) {+    case TOK_LET:+        var_def_type = JS_VAR_DEF_LET;+        break;+    case TOK_CONST:+        var_def_type = JS_VAR_DEF_CONST;+        break;+    case TOK_VAR:+        var_def_type = JS_VAR_DEF_VAR;+        break;+    case TOK_CATCH:+        var_def_type = JS_VAR_DEF_CATCH;+        break;+    default:+        abort();+    }+    if (define_var(s, fd, name, var_def_type) < 0)+        return -1;+    return 0;+}++static void js_emit_spread_code(JSParseState *s, int depth)+{+    int label_rest_next, label_rest_done;++    /* XXX: could check if enum object is an actual array and optimize+       slice extraction. enumeration record and target array are in a+       different order from OP_append case. */+    /* enum_rec xxx -- enum_rec xxx array 0 */+    emit_op(s, OP_array_from);+    emit_u16(s, 0);+    emit_op(s, OP_push_i32);+    emit_u32(s, 0);+    emit_label(s, label_rest_next = new_label(s));+    emit_op(s, OP_for_of_next);+    emit_u8(s, 2 + depth);+    label_rest_done = emit_goto(s, OP_if_true, -1);+    /* array idx val -- array idx */+    emit_op(s, OP_define_array_el);+    emit_op(s, OP_inc);+    emit_goto(s, OP_goto, label_rest_next);+    emit_label(s, label_rest_done);+    /* enum_rec xxx array idx undef -- enum_rec xxx array */+    emit_op(s, OP_drop);+    emit_op(s, OP_drop);+}++static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)+{+    /* Check for duplicate parameter names */+    JSFunctionDef *fd = s->cur_func;+    int i;+    for (i = 0; i < fd->arg_count; i++) {+        if (fd->args[i].var_name == name)+            goto duplicate;+    }+    for (i = 0; i < fd->var_count; i++) {+        if (fd->vars[i].var_name == name)+            goto duplicate;+    }+    return 0;++duplicate:+    return js_parse_error(s, "duplicate parameter names not allowed in this context");+}++static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)+{+    JSAtom name;++    if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)+    ||  ((s->cur_func->js_mode & JS_MODE_STRICT) &&+         (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {+        js_parse_error(s, "invalid destructuring target");+        return JS_ATOM_NULL;+    }+    name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+    if (is_arg && js_parse_check_duplicate_parameter(s, name))+        goto fail;+    if (next_token(s))+        goto fail;++    return name;+fail:+    JS_FreeAtom(s->ctx, name);+    return JS_ATOM_NULL;+}++static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,+                                        int hasval, int has_ellipsis,+                                        BOOL allow_initializer)+{+    int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;+    int start_addr, assign_addr;+    JSAtom prop_name, var_name;+    int opcode, scope, tok1, skip_bits;++    if (has_ellipsis < 0) {+        /* pre-parse destructuration target for spread detection */+        js_parse_skip_parens_token(s, &skip_bits, FALSE);+        has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;+    }++    label_parse = new_label(s);+    label_assign = new_label(s);++    start_addr = s->cur_func->byte_code.size;+    if (hasval) {+        /* consume value from the stack */+        emit_op(s, OP_dup);+        emit_op(s, OP_undefined);+        emit_op(s, OP_strict_eq);+        emit_goto(s, OP_if_true, label_parse);+        emit_label(s, label_assign);+    } else {+        emit_goto(s, OP_goto, label_parse);+        emit_label(s, label_assign);+        /* leave value on the stack */+        emit_op(s, OP_dup);+    }+    assign_addr = s->cur_func->byte_code.size;+    if (s->token.val == '{') {+        if (next_token(s))+            return -1;+        /* throw an exception if the value cannot be converted to an object */+        emit_op(s, OP_to_object);+        if (has_ellipsis) {+            /* add excludeList on stack just below src object */+            emit_op(s, OP_object);+            emit_op(s, OP_swap);+        }+        while (s->token.val != '}') {+            int prop_type;+            if (s->token.val == TOK_ELLIPSIS) {+                if (!has_ellipsis) {+                    JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");+                    return -1;+                }+                if (next_token(s))+                    return -1;+                if (tok) {+                    var_name = js_parse_destructuring_var(s, tok, is_arg);+                    if (var_name == JS_ATOM_NULL)+                        return -1;+                    opcode = OP_scope_get_var;+                    scope = s->cur_func->scope_level;+                    label_lvalue = -1;+                    depth_lvalue = 0;+                } else {+                    if (js_parse_postfix_expr(s, TRUE))+                        return -1;++                    if (get_lvalue(s, &opcode, &scope, &var_name,+                                   &label_lvalue, &depth_lvalue, FALSE, '{'))+                        return -1;+                }+                if (s->token.val != '}') {+                    js_parse_error(s, "assignment rest property must be last");+                    goto var_error;+                }+                emit_op(s, OP_object);  /* target */+                emit_op(s, OP_copy_data_properties);+                emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));+                goto set_val;+            }+            prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);+            if (prop_type < 0)+                return -1;+            var_name = JS_ATOM_NULL;+            opcode = OP_scope_get_var;+            scope = s->cur_func->scope_level;+            label_lvalue = -1;+            depth_lvalue = 0;+            if (prop_type == PROP_TYPE_IDENT) {+                if (next_token(s))+                    goto prop_error;+                if ((s->token.val == '[' || s->token.val == '{')+                    &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||+                         tok1 == '=' || tok1 == '}')) {+                    if (prop_name == JS_ATOM_NULL) {+                        /* computed property name on stack */+                        if (has_ellipsis) {+                            /* define the property in excludeList */+                            emit_op(s, OP_to_propkey); /* avoid calling ToString twice */+                            emit_op(s, OP_perm3); /* TOS: src excludeList prop */+                            emit_op(s, OP_null); /* TOS: src excludeList prop null */+                            emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */+                            emit_op(s, OP_perm3); /* TOS: excludeList src prop */+                        }+                        /* get the computed property from the source object */+                        emit_op(s, OP_get_array_el2);+                    } else {+                        /* named property */+                        if (has_ellipsis) {+                            /* define the property in excludeList */+                            emit_op(s, OP_swap); /* TOS: src excludeList */+                            emit_op(s, OP_null); /* TOS: src excludeList null */+                            emit_op(s, OP_define_field); /* TOS: src excludeList */+                            emit_atom(s, prop_name);+                            emit_op(s, OP_swap); /* TOS: excludeList src */+                        }+                        /* get the named property from the source object */+                        emit_op(s, OP_get_field2);+                        emit_u32(s, prop_name);+                    }+                    if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE))+                        return -1;+                    if (s->token.val == '}')+                        break;+                    /* accept a trailing comma before the '}' */+                    if (js_parse_expect(s, ','))+                        return -1;+                    continue;+                }+                if (prop_name == JS_ATOM_NULL) {+                    emit_op(s, OP_to_propkey2);+                    if (has_ellipsis) {+                        /* define the property in excludeList */+                        emit_op(s, OP_perm3);+                        emit_op(s, OP_null);+                        emit_op(s, OP_define_array_el);+                        emit_op(s, OP_perm3);+                    }+                    /* source prop -- source source prop */+                    emit_op(s, OP_dup1);+                } else {+                    if (has_ellipsis) {+                        /* define the property in excludeList */+                        emit_op(s, OP_swap);+                        emit_op(s, OP_null);+                        emit_op(s, OP_define_field);+                        emit_atom(s, prop_name);+                        emit_op(s, OP_swap);+                    }+                    /* source -- source source */+                    emit_op(s, OP_dup);+                }+                if (tok) {+                    var_name = js_parse_destructuring_var(s, tok, is_arg);+                    if (var_name == JS_ATOM_NULL)+                        goto prop_error;+                } else {+                    if (js_parse_postfix_expr(s, TRUE))+                        goto prop_error;+                lvalue:+                    if (get_lvalue(s, &opcode, &scope, &var_name,+                                   &label_lvalue, &depth_lvalue, FALSE, '{'))+                        goto prop_error;+                    /* swap ref and lvalue object if any */+                    if (prop_name == JS_ATOM_NULL) {+                        switch(depth_lvalue) {+                        case 1:+                            /* source prop x -> x source prop */+                            emit_op(s, OP_rot3r);+                            break;+                        case 2:+                            /* source prop x y -> x y source prop */+                            emit_op(s, OP_swap2);   /* t p2 s p1 */+                            break;+                        case 3:+                            /* source prop x y z -> x y z source prop */+                            emit_op(s, OP_rot5l);+                            emit_op(s, OP_rot5l);+                            break;+                        }+                    } else {+                        switch(depth_lvalue) {+                        case 1:+                            /* source x -> x source */+                            emit_op(s, OP_swap);+                            break;+                        case 2:+                            /* source x y -> x y source */+                            emit_op(s, OP_rot3l);+                            break;+                        case 3:+                            /* source x y z -> x y z source */+                            emit_op(s, OP_rot4l);+                            break;+                        }+                    }+                }+                if (prop_name == JS_ATOM_NULL) {+                    /* computed property name on stack */+                    /* XXX: should have OP_get_array_el2x with depth */+                    /* source prop -- val */+                    emit_op(s, OP_get_array_el);+                } else {+                    /* named property */+                    /* XXX: should have OP_get_field2x with depth */+                    /* source -- val */+                    emit_op(s, OP_get_field);+                    emit_u32(s, prop_name);+                }+            } else {+                /* prop_type = PROP_TYPE_VAR, cannot be a computed property */+                if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))+                    goto prop_error;+                if ((s->cur_func->js_mode & JS_MODE_STRICT) &&+                    (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {+                    js_parse_error(s, "invalid destructuring target");+                    goto prop_error;+                }+                if (has_ellipsis) {+                    /* define the property in excludeList */+                    emit_op(s, OP_swap);+                    emit_op(s, OP_null);+                    emit_op(s, OP_define_field);+                    emit_atom(s, prop_name);+                    emit_op(s, OP_swap);+                }+                if (!tok || tok == TOK_VAR) {+                    /* generate reference */+                    /* source -- source source */+                    emit_op(s, OP_dup);+                    emit_op(s, OP_scope_get_var);+                    emit_atom(s, prop_name);+                    emit_u16(s, s->cur_func->scope_level);+                    goto lvalue;+                }+                var_name = JS_DupAtom(s->ctx, prop_name);+                /* source -- source val */+                emit_op(s, OP_get_field2);+                emit_u32(s, prop_name);+            }+        set_val:+            if (tok) {+                if (js_define_var(s, var_name, tok))+                    goto var_error;+                scope = s->cur_func->scope_level;+            }+            if (s->token.val == '=') {  /* handle optional default value */+                int label_hasval;+                emit_op(s, OP_dup);+                emit_op(s, OP_undefined);+                emit_op(s, OP_strict_eq);+                label_hasval = emit_goto(s, OP_if_false, -1);+                if (next_token(s))+                    goto var_error;+                emit_op(s, OP_drop);+                if (js_parse_assign_expr(s, TRUE))+                    goto var_error;+                if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)+                    set_object_name(s, var_name);+                emit_label(s, label_hasval);+            }+            /* store value into lvalue object */+            put_lvalue_nokeep(s, opcode, scope, var_name, label_lvalue, tok);+            if (s->token.val == '}')+                break;+            /* accept a trailing comma before the '}' */+            if (js_parse_expect(s, ','))+                return -1;+        }+        /* drop the source object */+        emit_op(s, OP_drop);+        if (has_ellipsis) {+            emit_op(s, OP_drop); /* pop excludeList */+        }+        if (next_token(s))+            return -1;+    } else if (s->token.val == '[') {+        BOOL has_spread;+        int enum_depth;+        BlockEnv block_env;++        if (next_token(s))+            return -1;+        /* the block environment is only needed in generators in case+           'yield' triggers a 'return' */+        push_break_entry(s->cur_func, &block_env,+                         JS_ATOM_NULL, -1, -1, 2);+        block_env.has_iterator = TRUE;+        emit_op(s, OP_for_of_start);+        has_spread = FALSE;+        while (s->token.val != ']') {+            /* get the next value */+            if (s->token.val == TOK_ELLIPSIS) {+                if (next_token(s))+                    return -1;+                if (s->token.val == ',' || s->token.val == ']')+                    return js_parse_error(s, "missing binding pattern...");+                has_spread = TRUE;+            }+            if (s->token.val == ',') {+                /* do nothing, skip the value, has_spread is false */+                emit_op(s, OP_for_of_next);+                emit_u8(s, 0);+                emit_op(s, OP_drop);+                emit_op(s, OP_drop);+            } else if ((s->token.val == '[' || s->token.val == '{')+                   &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||+                        tok1 == '=' || tok1 == ']')) {+                if (has_spread) {+                    if (tok1 == '=')+                        return js_parse_error(s, "rest element cannot have a default value");+                    js_emit_spread_code(s, 0);+                } else {+                    emit_op(s, OP_for_of_next);+                    emit_u8(s, 0);+                    emit_op(s, OP_drop);+                }+                if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))+                    return -1;+            } else {+                var_name = JS_ATOM_NULL;+                enum_depth = 0;+                if (tok) {+                    var_name = js_parse_destructuring_var(s, tok, is_arg);+                    if (var_name == JS_ATOM_NULL)+                        goto var_error;+                    if (js_define_var(s, var_name, tok))+                        goto var_error;+                    opcode = OP_scope_get_var;+                    scope = s->cur_func->scope_level;+                } else {+                    if (js_parse_postfix_expr(s, TRUE))+                        return -1;+                    if (get_lvalue(s, &opcode, &scope, &var_name,+                                   &label_lvalue, &enum_depth, FALSE, '[')) {+                        return -1;+                    }+                }+                if (has_spread) {+                    js_emit_spread_code(s, enum_depth);+                } else {+                    emit_op(s, OP_for_of_next);+                    emit_u8(s, enum_depth);+                    emit_op(s, OP_drop);+                }+                if (s->token.val == '=' && !has_spread) {+                    /* handle optional default value */+                    int label_hasval;+                    emit_op(s, OP_dup);+                    emit_op(s, OP_undefined);+                    emit_op(s, OP_strict_eq);+                    label_hasval = emit_goto(s, OP_if_false, -1);+                    if (next_token(s))+                        goto var_error;+                    emit_op(s, OP_drop);+                    if (js_parse_assign_expr(s, TRUE))+                        goto var_error;+                    if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)+                        set_object_name(s, var_name);+                    emit_label(s, label_hasval);+                }+                /* store value into lvalue object */+                put_lvalue_nokeep(s, opcode, scope, var_name,+                                  label_lvalue, tok);+            }+            if (s->token.val == ']')+                break;+            if (has_spread)+                return js_parse_error(s, "rest element must be the last one");+            /* accept a trailing comma before the ']' */+            if (js_parse_expect(s, ','))+                return -1;+        }+        /* close iterator object:+           if completed, enum_obj has been replaced by undefined */+        emit_op(s, OP_iterator_close);+        pop_break_entry(s->cur_func);+        if (next_token(s))+            return -1;+    } else {+        return js_parse_error(s, "invalid assignment syntax");+    }+    if (s->token.val == '=' && allow_initializer) {+        label_done = emit_goto(s, OP_goto, -1);+        if (next_token(s))+            return -1;+        emit_label(s, label_parse);+        if (hasval)+            emit_op(s, OP_drop);+        if (js_parse_assign_expr(s, TRUE))+            return -1;+        emit_goto(s, OP_goto, label_assign);+        emit_label(s, label_done);+    } else {+        /* normally hasval is true except if+           js_parse_skip_parens_token() was wrong in the parsing */+        //        assert(hasval);+        if (!hasval) {+            js_parse_error(s, "too complicated destructuring expression");+            return -1;+        }+        /* remove test and decrement label ref count */+        memset(s->cur_func->byte_code.buf + start_addr, OP_nop,+               assign_addr - start_addr);+        s->cur_func->label_slots[label_parse].ref_count--;+    }+    return 0;++ prop_error:+    JS_FreeAtom(s->ctx, prop_name);+ var_error:+    JS_FreeAtom(s->ctx, var_name);+    return -1;+}++typedef enum FuncCallType {+    FUNC_CALL_NORMAL,+    FUNC_CALL_NEW,+    FUNC_CALL_SUPER_CTOR,+    FUNC_CALL_TEMPLATE,+} FuncCallType;++static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,+                                int drop_count)+{+    int label_next, i;+    if (*poptional_chaining_label < 0)+        *poptional_chaining_label = new_label(s);+   /* XXX: could be more efficient with a specific opcode */+    emit_op(s, OP_dup);+    emit_op(s, OP_is_undefined_or_null);+    label_next = emit_goto(s, OP_if_false, -1);+    for(i = 0; i < drop_count; i++)+        emit_op(s, OP_drop);+    emit_op(s, OP_undefined);+    emit_goto(s, OP_goto, *poptional_chaining_label);+    emit_label(s, label_next);+}++static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen)+{+    FuncCallType call_type;+    int optional_chaining_label;+    +    call_type = FUNC_CALL_NORMAL;+    switch(s->token.val) {+    case TOK_NUMBER:+        {+            JSValue val;+            val = s->token.u.num.val;++            if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {+                emit_op(s, OP_push_i32);+                emit_u32(s, JS_VALUE_GET_INT(val));+            } else+#ifdef CONFIG_BIGNUM+            if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {+                slimb_t e;+                int ret;++                /* need a runtime conversion */+                /* XXX: could add a cache and/or do it once at+                   the start of the function */+                if (emit_push_const(s, val, 0) < 0)+                    return -1;+                e = s->token.u.num.exponent;+                if (e == (int32_t)e) {+                    emit_op(s, OP_push_i32);+                    emit_u32(s, e);+                } else {+                    val = JS_NewBigInt64_1(s->ctx, e);+                    if (JS_IsException(val))+                        return -1;+                    ret = emit_push_const(s, val, 0);+                    JS_FreeValue(s->ctx, val);+                    if (ret < 0)+                        return -1;+                }+                emit_op(s, OP_mul_pow10);+            } else+#endif+            {+                if (emit_push_const(s, val, 0) < 0)+                    return -1;+            }+        }+        if (next_token(s))+            return -1;+        break;+    case TOK_TEMPLATE:+        if (js_parse_template(s, 0, NULL))+            return -1;+        break;+    case TOK_STRING:+        if (emit_push_const(s, s->token.u.str.str, 1))+            return -1;+        if (next_token(s))+            return -1;+        break;+        +    case TOK_DIV_ASSIGN:+        s->buf_ptr -= 2;+        goto parse_regexp;+    case '/':+        s->buf_ptr--;+    parse_regexp:+        {+            JSValue str;+            int ret, backtrace_flags;+            if (!s->ctx->compile_regexp)+                return js_parse_error(s, "RegExp are not supported");+            /* the previous token is '/' or '/=', so no need to free */+            if (js_parse_regexp(s))+                return -1;+            ret = emit_push_const(s, s->token.u.regexp.body, 0);+            str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,+                                         s->token.u.regexp.flags);+            if (JS_IsException(str)) {+                /* add the line number info */+                backtrace_flags = 0;+                if (s->cur_func && s->cur_func->backtrace_barrier)+                    backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;+                build_backtrace(s->ctx, s->ctx->rt->current_exception,+                                s->filename, s->token.line_num,+                                backtrace_flags);+                return -1;+            }+            ret = emit_push_const(s, str, 0);+            JS_FreeValue(s->ctx, str);+            if (ret)+                return -1;+            /* we use a specific opcode to be sure the correct+               function is called (otherwise the bytecode would have+               to be verified by the RegExp constructor) */+            emit_op(s, OP_regexp);+            if (next_token(s))+                return -1;+        }+        break;+    case '(':+        if (js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {+            if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,+                                       JS_FUNC_NORMAL, JS_ATOM_NULL,+                                       s->token.ptr, s->token.line_num))+                return -1;+        } else {+            if (js_parse_expr_paren(s))+                return -1;+        }+        break;+    case TOK_FUNCTION:+        if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,+                                   JS_FUNC_NORMAL, JS_ATOM_NULL,+                                   s->token.ptr, s->token.line_num))+            return -1;+        break;+    case TOK_CLASS:+        if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))+            return -1;+        break;+    case TOK_NULL:+        if (next_token(s))+            return -1;+        emit_op(s, OP_null);+        break;+    case TOK_THIS:+        if (next_token(s))+            return -1;+        emit_op(s, OP_scope_get_var);+        emit_atom(s, JS_ATOM_this);+        emit_u16(s, 0);+        break;+    case TOK_FALSE:+        if (next_token(s))+            return -1;+        emit_op(s, OP_push_false);+        break;+    case TOK_TRUE:+        if (next_token(s))+            return -1;+        emit_op(s, OP_push_true);+        break;+    case TOK_IDENT:+        {+            JSAtom name;+            if (s->token.u.ident.is_reserved) {+                return js_parse_error_reserved_identifier(s);+            }+            if (peek_token(s, TRUE) == TOK_ARROW) {+                if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,+                                           JS_FUNC_NORMAL, JS_ATOM_NULL,+                                           s->token.ptr, s->token.line_num))+                    return -1;+            } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&+                       peek_token(s, TRUE) != '\n') {+                const uint8_t *source_ptr;+                int source_line_num;++                source_ptr = s->token.ptr;+                source_line_num = s->token.line_num;+                if (next_token(s))+                    return -1;+                if (s->token.val == TOK_FUNCTION) {+                    if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,+                                               JS_FUNC_ASYNC, JS_ATOM_NULL,+                                               source_ptr, source_line_num))+                        return -1;+                } else if ((s->token.val == '(' &&+                            js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||+                           (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&+                            peek_token(s, TRUE) == TOK_ARROW)) {+                    if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,+                                               JS_FUNC_ASYNC, JS_ATOM_NULL,+                                               source_ptr, source_line_num))+                        return -1;+                } else {+                    name = JS_DupAtom(s->ctx, JS_ATOM_async);+                    goto do_get_var;+                }+            } else {+                if (s->token.u.ident.atom == JS_ATOM_arguments &&+                    !s->cur_func->arguments_allowed) {+                    js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");+                    return -1;+                }+                name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+                if (next_token(s))  /* update line number before emitting code */+                    return -1;+            do_get_var:+                emit_op(s, OP_scope_get_var);+                emit_u32(s, name);+                emit_u16(s, s->cur_func->scope_level);+            }+        }+        break;+    case '{':+    case '[':+        {+            int skip_bits;+            if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {+                if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))+                    return -1;+            } else {+                if (s->token.val == '{') {+                    if (js_parse_object_literal(s))+                        return -1;+                } else {+                    if (js_parse_array_literal(s))+                        return -1;+                }+            }+        }+        break;+    case TOK_NEW:+        if (next_token(s))+            return -1;+        if (s->token.val == '.') {+            if (next_token(s))+                return -1;+            if (!token_is_pseudo_keyword(s, JS_ATOM_target))+                return js_parse_error(s, "expecting target");+            if (!s->cur_func->new_target_allowed)+                return js_parse_error(s, "new.target only allowed within functions");+            if (next_token(s))+                return -1;+            emit_op(s, OP_scope_get_var);+            emit_atom(s, JS_ATOM_new_target);+            emit_u16(s, 0);+        } else {+            if (js_parse_postfix_expr(s, FALSE))+                return -1;+            accept_lparen = TRUE;+            if (s->token.val != '(') {+                /* new operator on an object */+                emit_op(s, OP_dup);+                emit_op(s, OP_call_constructor);+                emit_u16(s, 0);+            } else {+                call_type = FUNC_CALL_NEW;+            }+        }+        break;+    case TOK_SUPER:+        if (next_token(s))+            return -1;+        if (s->token.val == '(') {+            if (!s->cur_func->super_call_allowed)+                return js_parse_error(s, "super() is only valid in a derived class constructor");+            call_type = FUNC_CALL_SUPER_CTOR;+        } else if (s->token.val == '.' || s->token.val == '[') {+            if (!s->cur_func->super_allowed)+                return js_parse_error(s, "'super' is only valid in a method");+            emit_op(s, OP_scope_get_var);+            emit_atom(s, JS_ATOM_this);+            emit_u16(s, 0);+            emit_op(s, OP_scope_get_var);+            emit_atom(s, JS_ATOM_home_object);+            emit_u16(s, 0);+            emit_op(s, OP_get_super);+        } else {+            return js_parse_error(s, "invalid use of 'super'");+        }+        break;+    case TOK_IMPORT:+        if (next_token(s))+            return -1;+        if (s->token.val == '.') {+            if (next_token(s))+                return -1;+            if (!token_is_pseudo_keyword(s, JS_ATOM_meta))+                return js_parse_error(s, "meta expected");+            if (!s->is_module)+                return js_parse_error(s, "import.meta only valid in module code");+            if (next_token(s))+                return -1;+            emit_op(s, OP_special_object);+            emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);+        } else {+            if (js_parse_expect(s, '('))+                return -1;+            if (!accept_lparen)+                return js_parse_error(s, "invalid use of 'import()'");+            if (js_parse_assign_expr(s, TRUE))+                return -1;+            if (js_parse_expect(s, ')'))+                return -1;+            emit_op(s, OP_import);+        }+        break;+    default:+        return js_parse_error(s, "unexpected token in expression: '%.*s'",+                              (int)(s->buf_ptr - s->token.ptr), s->token.ptr);+    }++    optional_chaining_label = -1;+    for(;;) {+        JSFunctionDef *fd = s->cur_func;+        BOOL has_optional_chain = FALSE;+        +        if (s->token.val == TOK_QUESTION_MARK_DOT) {+            /* optional chaining */+            if (next_token(s))+                return -1;+            has_optional_chain = TRUE;+            if (s->token.val == '(' && accept_lparen) {+                goto parse_func_call;+            } else if (s->token.val == '[') {+                goto parse_array_access;+            } else {+                goto parse_property;+            }+        } else if (s->token.val == TOK_TEMPLATE &&+                   call_type == FUNC_CALL_NORMAL) {+            if (optional_chaining_label >= 0) {+                return js_parse_error(s, "template literal cannot appear in an optional chain");+            }+            call_type = FUNC_CALL_TEMPLATE;+            goto parse_func_call2;+        } else if (s->token.val == '(' && accept_lparen) {+            int opcode, arg_count, drop_count;++            /* function call */+        parse_func_call:+            if (next_token(s))+                return -1;++            if (call_type == FUNC_CALL_NORMAL) {+            parse_func_call2:+                switch(opcode = get_prev_opcode(fd)) {+                case OP_get_field:+                    /* keep the object on the stack */+                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;+                    drop_count = 2;+                    break;+                case OP_scope_get_private_field:+                    /* keep the object on the stack */+                    fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;+                    drop_count = 2;+                    break;+                case OP_get_array_el:+                    /* keep the object on the stack */+                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;+                    drop_count = 2;+                    break;+                case OP_scope_get_var:+                    {+                        JSAtom name;+                        int scope;+                        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+                        scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);+                        if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {+                            /* direct 'eval' */+                            opcode = OP_eval;+                        } else {+                            /* verify if function name resolves to a simple+                               get_loc/get_arg: a function call inside a `with`+                               statement can resolve to a method call of the+                               `with` context object+                             */+                            /* XXX: always generate the OP_scope_get_ref+                               and remove it in variable resolution+                               pass ? */+                            if (has_with_scope(fd, scope)) {+                                opcode = OP_scope_get_ref;+                                fd->byte_code.buf[fd->last_opcode_pos] = opcode;+                            }+                        }+                        drop_count = 1;+                    }+                    break;+                case OP_get_super_value:+                    fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;+                    /* on stack: this func_obj */+                    opcode = OP_get_array_el;+                    drop_count = 2;+                    break;+                default:+                    opcode = OP_invalid;+                    drop_count = 1;+                    break;+                }+                if (has_optional_chain) {+                    optional_chain_test(s, &optional_chaining_label,+                                        drop_count);+                }+            } else {+                opcode = OP_invalid;+            }++            if (call_type == FUNC_CALL_TEMPLATE) {+                if (js_parse_template(s, 1, &arg_count))+                    return -1;+                goto emit_func_call;+            } else if (call_type == FUNC_CALL_SUPER_CTOR) {+                emit_op(s, OP_scope_get_var);+                emit_atom(s, JS_ATOM_this_active_func);+                emit_u16(s, 0);++                emit_op(s, OP_get_super);++                emit_op(s, OP_scope_get_var);+                emit_atom(s, JS_ATOM_new_target);+                emit_u16(s, 0);+            } else if (call_type == FUNC_CALL_NEW) {+                emit_op(s, OP_dup); /* new.target = function */+            }++            /* parse arguments */+            arg_count = 0;+            while (s->token.val != ')') {+                if (arg_count >= 65535) {+                    return js_parse_error(s, "Too many call arguments");+                }+                if (s->token.val == TOK_ELLIPSIS)+                    break;+                if (js_parse_assign_expr(s, TRUE))+                    return -1;+                arg_count++;+                if (s->token.val == ')')+                    break;+                /* accept a trailing comma before the ')' */+                if (js_parse_expect(s, ','))+                    return -1;+            }+            if (s->token.val == TOK_ELLIPSIS) {+                emit_op(s, OP_array_from);+                emit_u16(s, arg_count);+                emit_op(s, OP_push_i32);+                emit_u32(s, arg_count);++                /* on stack: array idx */+                while (s->token.val != ')') {+                    if (s->token.val == TOK_ELLIPSIS) {+                        if (next_token(s))+                            return -1;+                        if (js_parse_assign_expr(s, TRUE))+                            return -1;+#if 1+                        /* XXX: could pass is_last indicator? */+                        emit_op(s, OP_append);+#else+                        int label_next, label_done;+                        label_next = new_label(s);+                        label_done = new_label(s);+                        /* push enumerate object below array/idx pair */+                        emit_op(s, OP_for_of_start);+                        emit_op(s, OP_rot5l);+                        emit_op(s, OP_rot5l);+                        emit_label(s, label_next);+                        /* on stack: enum_rec array idx */+                        emit_op(s, OP_for_of_next);+                        emit_u8(s, 2);+                        emit_goto(s, OP_if_true, label_done);+                        /* append element */+                        /* enum_rec array idx val -> enum_rec array new_idx */+                        emit_op(s, OP_define_array_el);+                        emit_op(s, OP_inc);+                        emit_goto(s, OP_goto, label_next);+                        emit_label(s, label_done);+                        /* close enumeration, drop enum_rec and idx */+                        emit_op(s, OP_drop); /* drop undef */+                        emit_op(s, OP_nip1); /* drop enum_rec */+                        emit_op(s, OP_nip1);+                        emit_op(s, OP_nip1);+#endif+                    } else {+                        if (js_parse_assign_expr(s, TRUE))+                            return -1;+                        /* array idx val */+                        emit_op(s, OP_define_array_el);+                        emit_op(s, OP_inc);+                    }+                    if (s->token.val == ')')+                        break;+                    /* accept a trailing comma before the ')' */+                    if (js_parse_expect(s, ','))+                        return -1;+                }+                if (next_token(s))+                    return -1;+                /* drop the index */+                emit_op(s, OP_drop);++                /* apply function call */+                switch(opcode) {+                case OP_get_field:+                case OP_scope_get_private_field:+                case OP_get_array_el:+                case OP_scope_get_ref:+                    /* obj func array -> func obj array */+                    emit_op(s, OP_perm3);+                    emit_op(s, OP_apply);+                    emit_u16(s, call_type == FUNC_CALL_NEW);+                    break;+                case OP_eval:+                    emit_op(s, OP_apply_eval);+                    emit_u16(s, fd->scope_level);+                    fd->has_eval_call = TRUE;+                    break;+                default:+                    if (call_type == FUNC_CALL_SUPER_CTOR) {+                        emit_op(s, OP_apply);+                        emit_u16(s, 1);+                        /* set the 'this' value */+                        emit_op(s, OP_dup);+                        emit_op(s, OP_scope_put_var_init);+                        emit_atom(s, JS_ATOM_this);+                        emit_u16(s, 0);++                        emit_class_field_init(s);+                    } else if (call_type == FUNC_CALL_NEW) {+                        /* obj func array -> func obj array */+                        emit_op(s, OP_perm3);+                        emit_op(s, OP_apply);+                        emit_u16(s, 1);+                    } else {+                        /* func array -> func undef array */+                        emit_op(s, OP_undefined);+                        emit_op(s, OP_swap);+                        emit_op(s, OP_apply);+                        emit_u16(s, 0);+                    }+                    break;+                }+            } else {+                if (next_token(s))+                    return -1;+            emit_func_call:+                switch(opcode) {+                case OP_get_field:+                case OP_scope_get_private_field:+                case OP_get_array_el:+                case OP_scope_get_ref:+                    emit_op(s, OP_call_method);+                    emit_u16(s, arg_count);+                    break;+                case OP_eval:+                    emit_op(s, OP_eval);+                    emit_u16(s, arg_count);+                    emit_u16(s, fd->scope_level);+                    fd->has_eval_call = TRUE;+                    break;+                default:+                    if (call_type == FUNC_CALL_SUPER_CTOR) {+                        emit_op(s, OP_call_constructor);+                        emit_u16(s, arg_count);++                        /* set the 'this' value */+                        emit_op(s, OP_dup);+                        emit_op(s, OP_scope_put_var_init);+                        emit_atom(s, JS_ATOM_this);+                        emit_u16(s, 0);++                        emit_class_field_init(s);+                    } else if (call_type == FUNC_CALL_NEW) {+                        emit_op(s, OP_call_constructor);+                        emit_u16(s, arg_count);+                    } else {+                        emit_op(s, OP_call);+                        emit_u16(s, arg_count);+                    }+                    break;+                }+            }+            call_type = FUNC_CALL_NORMAL;+        } else if (s->token.val == '.') {+            if (next_token(s))+                return -1;+            if (s->token.val == TOK_PRIVATE_NAME) {+                /* private class field */+                if (get_prev_opcode(fd) == OP_get_super) {+                    return js_parse_error(s, "private class field forbidden after super");+                }+                emit_op(s, OP_scope_get_private_field);+                emit_atom(s, s->token.u.ident.atom);+                emit_u16(s, s->cur_func->scope_level);+            } else {+            parse_property:+                if (!token_is_ident(s->token.val)) {+                    return js_parse_error(s, "expecting field name");+                }+                if (get_prev_opcode(fd) == OP_get_super) {+                    JSValue val;+                    int ret;+                    val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);+                    ret = emit_push_const(s, val, 1);+                    JS_FreeValue(s->ctx, val);+                    if (ret)+                        return -1;+                    emit_op(s, OP_get_super_value);+                } else {+                    if (has_optional_chain) {+                        optional_chain_test(s, &optional_chaining_label, 1);+                    }+                    emit_op(s, OP_get_field);+                    emit_atom(s, s->token.u.ident.atom);+                }+            }+            if (next_token(s))+                return -1;+        } else if (s->token.val == '[') {+            int prev_op;++        parse_array_access:+            prev_op = get_prev_opcode(fd);+            if (has_optional_chain) {+                optional_chain_test(s, &optional_chaining_label, 1);+            }+            if (next_token(s))+                return -1;+            if (js_parse_expr(s))+                return -1;+            if (js_parse_expect(s, ']'))+                return -1;+            if (prev_op == OP_get_super) {+                emit_op(s, OP_get_super_value);+            } else {+                emit_op(s, OP_get_array_el);+            }+        } else {+            break;+        }+    }+    if (optional_chaining_label >= 0)+        emit_label(s, optional_chaining_label);+    return 0;+}++static __exception int js_parse_delete(JSParseState *s)+{+    JSFunctionDef *fd = s->cur_func;+    JSAtom name;+    int opcode;++    if (next_token(s))+        return -1;+    if (js_parse_unary(s, -1))+        return -1;+    switch(opcode = get_prev_opcode(fd)) {+    case OP_get_field:+        {+            JSValue val;+            int ret;++            name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+            fd->byte_code.size = fd->last_opcode_pos;+            fd->last_opcode_pos = -1;+            val = JS_AtomToValue(s->ctx, name);+            ret = emit_push_const(s, val, 1);+            JS_FreeValue(s->ctx, val);+            JS_FreeAtom(s->ctx, name);+            if (ret)+                return ret;+        }+        goto do_delete;+    case OP_get_array_el:+        fd->byte_code.size = fd->last_opcode_pos;+        fd->last_opcode_pos = -1;+    do_delete:+        emit_op(s, OP_delete);+        break;+    case OP_scope_get_var:+        /* 'delete this': this is not a reference */+        name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+        if (name == JS_ATOM_this || name == JS_ATOM_new_target)+            goto ret_true;+        if (fd->js_mode & JS_MODE_STRICT) {+            return js_parse_error(s, "cannot delete a direct reference in strict mode");+        } else {+            fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;+        }+        break;+    case OP_scope_get_private_field:+        return js_parse_error(s, "cannot delete a private class field");+    case OP_get_super_value:+        emit_op(s, OP_throw_var);+        emit_atom(s, JS_ATOM_NULL);+        emit_u8(s, JS_THROW_VAR_DELETE_SUPER);+        break;+    default:+    ret_true:+        emit_op(s, OP_drop);+        emit_op(s, OP_push_true);+        break;+    }+    return 0;+}++static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag)+{+    int op;++    switch(s->token.val) {+    case '+':+    case '-':+    case '!':+    case '~':+    case TOK_VOID:+        op = s->token.val;+        if (next_token(s))+            return -1;+        if (js_parse_unary(s, -1))+            return -1;+        switch(op) {+        case '-':+            emit_op(s, OP_neg);+            break;+        case '+':+            emit_op(s, OP_plus);+            break;+        case '!':+            emit_op(s, OP_lnot);+            break;+        case '~':+            emit_op(s, OP_not);+            break;+        case TOK_VOID:+            emit_op(s, OP_drop);+            emit_op(s, OP_undefined);+            break;+        default:+            abort();+        }+        exponentiation_flag = 0;+        break;+    case TOK_DEC:+    case TOK_INC:+        {+            int opcode, op, scope, label;+            JSAtom name;+            op = s->token.val;+            if (next_token(s))+                return -1;+            /* XXX: should parse LeftHandSideExpression */+            if (js_parse_unary(s, 0))+                return -1;+            if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))+                return -1;+            emit_op(s, OP_dec + op - TOK_DEC);+            put_lvalue(s, opcode, scope, name, label, FALSE);+        }+        break;+    case TOK_TYPEOF:+        {+            JSFunctionDef *fd;+            if (next_token(s))+                return -1;+            if (js_parse_unary(s, -1))+                return -1;+            /* reference access should not return an exception, so we+               patch the get_var */+            fd = s->cur_func;+            if (get_prev_opcode(fd) == OP_scope_get_var) {+                fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;+            }+            emit_op(s, OP_typeof);+            exponentiation_flag = 0;+        }+        break;+    case TOK_DELETE:+        if (js_parse_delete(s))+            return -1;+        exponentiation_flag = 0;+        break;+    case TOK_AWAIT:+        if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))+            return js_parse_error(s, "unexpected 'await' keyword");+        if (!s->cur_func->in_function_body)+            return js_parse_error(s, "await in default expression");+        if (next_token(s))+            return -1;+        if (js_parse_unary(s, -1))+            return -1;+        emit_op(s, OP_await);+        exponentiation_flag = 0;+        break;+    default:+        if (js_parse_postfix_expr(s, TRUE))+            return -1;+        if (!s->got_lf &&+            (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {+            int opcode, op, scope, label;+            JSAtom name;+            op = s->token.val;+            if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))+                return -1;+            emit_op(s, OP_post_dec + op - TOK_DEC);+            put_lvalue(s, opcode, scope, name, label, TRUE);+            if (next_token(s))+                return -1;        +        }+        break;+    }+    if (exponentiation_flag) {+#ifdef CONFIG_BIGNUM+        if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {+            /* Extended exponentiation syntax rules: we extend the ES7+               grammar in order to have more intuitive semantics:+               -2**2 evaluates to -4. */+            if (!(s->cur_func->js_mode & JS_MODE_MATH)) {+                if (exponentiation_flag < 0) {+                    JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");+                    return -1;+                }+            }+            if (next_token(s))+                return -1;+            if (js_parse_unary(s, 1))+                return -1;+            emit_op(s, OP_pow);+        }+#else+        if (s->token.val == TOK_POW) {+            /* Strict ES7 exponentiation syntax rules: To solve+               conficting semantics between different implementations+               regarding the precedence of prefix operators and the+               postifx exponential, ES7 specifies that -2**2 is a+               syntax error. */+            if (exponentiation_flag < 0) {+                JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");+                return -1;+            }+            if (next_token(s))+                return -1;+            if (js_parse_unary(s, 1))+                return -1;+            emit_op(s, OP_pow);+        }+#endif+    }+    return 0;+}++static __exception int js_parse_expr_binary(JSParseState *s, int level,+                                            BOOL in_accepted)+{+    int op, opcode;++    if (level == 0) {+        return js_parse_unary(s, 1);+    }+    if (js_parse_expr_binary(s, level - 1, in_accepted))+        return -1;+    for(;;) {+        op = s->token.val;+        switch(level) {+        case 1:+            switch(op) {+            case '*':+                opcode = OP_mul;+                break;+            case '/':+                opcode = OP_div;+                break;+            case '%':+#ifdef CONFIG_BIGNUM+                if (s->cur_func->js_mode & JS_MODE_MATH)+                    opcode = OP_math_mod;+                else+#endif+                    opcode = OP_mod;+                break;+            default:+                return 0;+            }+            break;+        case 2:+            switch(op) {+            case '+':+                opcode = OP_add;+                break;+            case '-':+                opcode = OP_sub;+                break;+            default:+                return 0;+            }+            break;+        case 3:+            switch(op) {+            case TOK_SHL:+                opcode = OP_shl;+                break;+            case TOK_SAR:+                opcode = OP_sar;+                break;+            case TOK_SHR:+                opcode = OP_shr;+                break;+            default:+                return 0;+            }+            break;+        case 4:+            switch(op) {+            case '<':+                opcode = OP_lt;+                break;+            case '>':+                opcode = OP_gt;+                break;+            case TOK_LTE:+                opcode = OP_lte;+                break;+            case TOK_GTE:+                opcode = OP_gte;+                break;+            case TOK_INSTANCEOF:+                opcode = OP_instanceof;+                break;+            case TOK_IN:+                if (in_accepted) {+                    opcode = OP_in;+                } else {+                    return 0;+                }+                break;+            default:+                return 0;+            }+            break;+        case 5:+            switch(op) {+            case TOK_EQ:+                opcode = OP_eq;+                break;+            case TOK_NEQ:+                opcode = OP_neq;+                break;+            case TOK_STRICT_EQ:+                opcode = OP_strict_eq;+                break;+            case TOK_STRICT_NEQ:+                opcode = OP_strict_neq;+                break;+            default:+                return 0;+            }+            break;+        case 6:+            switch(op) {+            case '&':+                opcode = OP_and;+                break;+            default:+                return 0;+            }+            break;+        case 7:+            switch(op) {+            case '^':+                opcode = OP_xor;+                break;+            default:+                return 0;+            }+            break;+        case 8:+            switch(op) {+            case '|':+                opcode = OP_or;+                break;+            default:+                return 0;+            }+            break;+        default:+            abort();+        }+        if (next_token(s))+            return -1;+        if (js_parse_expr_binary(s, level - 1, in_accepted))+            return -1;+        emit_op(s, opcode);+    }+    return 0;+}++static __exception int js_parse_logical_and_or(JSParseState *s, int op,+                                               BOOL in_accepted)+{+    int label1;++    if (op == TOK_LAND) {+        if (js_parse_expr_binary(s, 8, in_accepted))+            return -1;+    } else {+        if (js_parse_logical_and_or(s, TOK_LAND, in_accepted))+            return -1;+    }+    if (s->token.val == op) {+        label1 = new_label(s);++        for(;;) {+            if (next_token(s))+                return -1;+            emit_op(s, OP_dup);+            emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);+            emit_op(s, OP_drop);++            if (op == TOK_LAND) {+                if (js_parse_expr_binary(s, 8, in_accepted))+                    return -1;+            } else {+                if (js_parse_logical_and_or(s, TOK_LAND, in_accepted))+                    return -1;+            }+            if (s->token.val != op) {+                if (s->token.val == TOK_DOUBLE_QUESTION_MARK)+                    return js_parse_error(s, "cannot mix ?? with && or ||");+                break;+            }+        }++        emit_label(s, label1);+    }+    return 0;+}++static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted)+{+    int label1;+    +    if (js_parse_logical_and_or(s, TOK_LOR, in_accepted))+        return -1;+    if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {+        label1 = new_label(s);+        for(;;) {+            if (next_token(s))+                return -1;+            +            emit_op(s, OP_dup);+            emit_op(s, OP_is_undefined_or_null);+            emit_goto(s, OP_if_false, label1);+            emit_op(s, OP_drop);+            +            if (js_parse_expr_binary(s, 8, in_accepted))+                return -1;+            if (s->token.val != TOK_DOUBLE_QUESTION_MARK)+                break;+        }+        emit_label(s, label1);+    }+    return 0;+}++static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted)+{+    int label1, label2;++    if (js_parse_coalesce_expr(s, in_accepted))+        return -1;+    if (s->token.val == '?') {+        if (next_token(s))+            return -1;+        label1 = emit_goto(s, OP_if_false, -1);++        if (js_parse_assign_expr(s, TRUE))+            return -1;+        if (js_parse_expect(s, ':'))+            return -1;++        label2 = emit_goto(s, OP_goto, -1);++        emit_label(s, label1);++        if (js_parse_assign_expr(s, in_accepted))+            return -1;++        emit_label(s, label2);+    }+    return 0;+}++static void emit_return(JSParseState *s, BOOL hasval);++static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted)+{+    int opcode, op, scope;+    JSAtom name0 = JS_ATOM_NULL;+    JSAtom name;++    if (s->token.val == TOK_YIELD) {+        BOOL is_star = FALSE;+        if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))+            return js_parse_error(s, "unexpected 'yield' keyword");+        if (!s->cur_func->in_function_body)+            return js_parse_error(s, "yield in default expression");+        if (next_token(s))+            return -1;+        /* XXX: is there a better method to detect 'yield' without+           parameters ? */+        if (s->token.val != ';' && s->token.val != ')' &&+            s->token.val != ']' && s->token.val != '}' &&+            s->token.val != ',' && s->token.val != ':' && !s->got_lf) {+            if (s->token.val == '*') {+                is_star = TRUE;+                if (next_token(s))+                    return -1;+            }+            if (js_parse_assign_expr(s, in_accepted))+                return -1;+        } else {+            emit_op(s, OP_undefined);+        }+        if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {+            int label_loop, label_return, label_next;+            int label_return1, label_yield, label_throw, label_throw1;+            int label_throw2;++            if (is_star) {+                label_loop = new_label(s);+                label_yield = new_label(s);++                emit_op(s, OP_for_await_of_start);++                /* remove the catch offset (XXX: could avoid pushing back+                   undefined) */+                emit_op(s, OP_drop);+                emit_op(s, OP_undefined);++                emit_op(s, OP_undefined); /* initial value */++                emit_label(s, label_loop);+                emit_op(s, OP_async_iterator_next);+                emit_op(s, OP_await);+                emit_op(s, OP_iterator_get_value_done);+                label_next = emit_goto(s, OP_if_true, -1); /* end of loop */+                emit_op(s, OP_await);+                emit_label(s, label_yield);+                emit_op(s, OP_async_yield_star);+                emit_op(s, OP_dup);+                label_return = emit_goto(s, OP_if_true, -1);+                emit_op(s, OP_drop);+                emit_goto(s, OP_goto, label_loop);++                emit_label(s, label_return);+                emit_op(s, OP_push_i32);+                emit_u32(s, 2);+                emit_op(s, OP_strict_eq);+                label_throw = emit_goto(s, OP_if_true, -1);++                /* return handling */+                emit_op(s, OP_await);+                emit_op(s, OP_async_iterator_get);+                emit_u8(s, 0);+                label_return1 = emit_goto(s, OP_if_true, -1);+                emit_op(s, OP_await);+                emit_op(s, OP_iterator_get_value_done);+                /* XXX: the spec does not indicate that an await should be+                   performed in case done = true, but the tests assume it */+                emit_goto(s, OP_if_false, label_yield);++                emit_label(s, label_return1);+                emit_op(s, OP_nip);+                emit_op(s, OP_nip);+                emit_op(s, OP_nip);+                emit_return(s, TRUE);++                /* throw handling */+                emit_label(s, label_throw);+                emit_op(s, OP_async_iterator_get);+                emit_u8(s, 1);+                label_throw1 = emit_goto(s, OP_if_true, -1);+                emit_op(s, OP_await);+                emit_op(s, OP_iterator_get_value_done);+                emit_goto(s, OP_if_false, label_yield);+                /* XXX: the spec does not indicate that an await should be+                   performed in case done = true, but the tests assume it */+                emit_op(s, OP_await);+                emit_goto(s, OP_goto, label_next);+                /* close the iterator and throw a type error exception */+                emit_label(s, label_throw1);+                emit_op(s, OP_async_iterator_get);+                emit_u8(s, 0);+                label_throw2 = emit_goto(s, OP_if_true, -1);+                emit_op(s, OP_await);+                emit_label(s, label_throw2);+                emit_op(s, OP_async_iterator_get);+                emit_u8(s, 2); /* throw the type error exception */+                emit_op(s, OP_drop); /* never reached */++                emit_label(s, label_next);+                emit_op(s, OP_nip); /* keep the value associated with+                                       done = true */+                emit_op(s, OP_nip);+                emit_op(s, OP_nip);+            } else {+                emit_op(s, OP_await);+                emit_op(s, OP_yield);+                label_next = emit_goto(s, OP_if_false, -1);+                emit_return(s, TRUE);+                emit_label(s, label_next);+            }+        } else {+            int label_next;+            if (is_star) {+                emit_op(s, OP_for_of_start);+                emit_op(s, OP_drop);    /* drop the catch offset */+                emit_op(s, OP_yield_star);+            } else {+                emit_op(s, OP_yield);+            }+            label_next = emit_goto(s, OP_if_false, -1);+            emit_return(s, TRUE);+            emit_label(s, label_next);+        }+        return 0;+    }+    if (s->token.val == TOK_IDENT) {+        /* name0 is used to check for OP_set_name pattern, not duplicated */+        name0 = s->token.u.ident.atom;+    }+    if (js_parse_cond_expr(s, in_accepted))+        return -1;++    op = s->token.val;+    if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {+        int label;+        if (next_token(s))+            return -1;+        if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)+            return -1;++        if (js_parse_assign_expr(s, in_accepted)) {+            JS_FreeAtom(s->ctx, name);+            return -1;+        }++        if (op == '=') {+            if (opcode == OP_get_ref_value && name == name0) {+                set_object_name(s, name);+            }+        } else {+            static const uint8_t assign_opcodes[] = {+                OP_mul, OP_div, OP_mod, OP_add, OP_sub,+                OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,+#ifdef CONFIG_BIGNUM+                OP_pow,+#endif+                OP_pow,+            };+            op = assign_opcodes[op - TOK_MUL_ASSIGN];+#ifdef CONFIG_BIGNUM+            if (s->cur_func->js_mode & JS_MODE_MATH) {+                if (op == OP_mod)+                    op = OP_math_mod;+            }+#endif+            emit_op(s, op);+        }+        put_lvalue(s, opcode, scope, name, label, FALSE);+    }+    return 0;+}++static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted)+{+    BOOL comma = FALSE;+    for(;;) {+        if (js_parse_assign_expr(s, in_accepted))+            return -1;+        if (comma) {+            /* prevent get_lvalue from using the last expression+               as an lvalue. This also prevents the conversion of+               of get_var to get_ref for method lookup in function+               call inside `with` statement.+             */+            s->cur_func->last_opcode_pos = -1;+        }+        if (s->token.val != ',')+            break;+        comma = TRUE;+        if (next_token(s))+            return -1;+        emit_op(s, OP_drop);+    }+    return 0;+}++static __exception int js_parse_expr(JSParseState *s)+{+    return js_parse_expr2(s, TRUE);+}++static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,+                             JSAtom label_name,+                             int label_break, int label_cont,+                             int drop_count)+{+    be->prev = fd->top_break;+    fd->top_break = be;+    be->label_name = label_name;+    be->label_break = label_break;+    be->label_cont = label_cont;+    be->drop_count = drop_count;+    be->label_finally = -1;+    be->scope_level = fd->scope_level;+    be->has_iterator = FALSE;+}++static void pop_break_entry(JSFunctionDef *fd)+{+    BlockEnv *be;+    be = fd->top_break;+    fd->top_break = be->prev;+}++static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)+{+    BlockEnv *top;+    int i, scope_level;++    scope_level = s->cur_func->scope_level;+    top = s->cur_func->top_break;+    while (top != NULL) {+        close_scopes(s, scope_level, top->scope_level);+        scope_level = top->scope_level;+        if (is_cont &&+            top->label_cont != -1 &&+            (name == JS_ATOM_NULL || top->label_name == name)) {+            /* continue stays inside the same block */+            emit_goto(s, OP_goto, top->label_cont);+            return 0;+        }+        if (!is_cont &&+            top->label_break != -1 &&+            (name == JS_ATOM_NULL || top->label_name == name)) {+            emit_goto(s, OP_goto, top->label_break);+            return 0;+        }+        i = 0;+        if (top->has_iterator) {+            emit_op(s, OP_iterator_close);+            i += 3;+        }+        for(; i < top->drop_count; i++)+            emit_op(s, OP_drop);+        if (top->label_finally != -1) {+            /* must push dummy value to keep same stack depth */+            emit_op(s, OP_undefined);+            emit_goto(s, OP_gosub, top->label_finally);+            emit_op(s, OP_drop);+        }+        top = top->prev;+    }+    if (name == JS_ATOM_NULL) {+        if (is_cont)+            return js_parse_error(s, "continue must be inside loop");+        else+            return js_parse_error(s, "break must be inside loop or switch");+    } else {+        return js_parse_error(s, "break/continue label not found");+    }+}++/* execute the finally blocks before return */+static void emit_return(JSParseState *s, BOOL hasval)+{+    BlockEnv *top;+    int drop_count;++    drop_count = 0;+    top = s->cur_func->top_break;+    while (top != NULL) {+        /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not+           required as all local variables will be closed upon returning+           from JS_CallInternal, but not in the same order. */+        if (top->has_iterator) {+            /* with 'yield', the exact number of OP_drop to emit is+               unknown, so we use a specific operation to look for+               the catch offset */+            if (!hasval) {+                emit_op(s, OP_undefined);+                hasval = TRUE;+            }+            emit_op(s, OP_iterator_close_return);+            if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {+                int label_next;+                emit_op(s, OP_async_iterator_close);+                label_next = emit_goto(s, OP_if_true, -1);+                emit_op(s, OP_await);+                emit_label(s, label_next);+                emit_op(s, OP_drop);+            } else {+                emit_op(s, OP_iterator_close);+            }+            drop_count = -3;+        }+        drop_count += top->drop_count;+        if (top->label_finally != -1) {+            while(drop_count) {+                /* must keep the stack top if hasval */+                emit_op(s, hasval ? OP_nip : OP_drop);+                drop_count--;+            }+            if (!hasval) {+                /* must push return value to keep same stack size */+                emit_op(s, OP_undefined);+                hasval = TRUE;+            }+            emit_goto(s, OP_gosub, top->label_finally);+        }+        top = top->prev;+    }+    if (s->cur_func->is_derived_class_constructor) {+        int label_return;++        /* 'this' can be uninitialized, so it may be accessed only if+           the derived class constructor does not return an object */+        if (hasval) {+            emit_op(s, OP_check_ctor_return);+            label_return = emit_goto(s, OP_if_false, -1);+            emit_op(s, OP_drop);+        } else {+            label_return = -1;+        }++        /* XXX: if this is not initialized, should throw the+           ReferenceError in the caller realm */+        emit_op(s, OP_scope_get_var);+        emit_atom(s, JS_ATOM_this);+        emit_u16(s, 0);++        emit_label(s, label_return);+        emit_op(s, OP_return);+    } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {+        if (!hasval) {+            emit_op(s, OP_undefined);+        } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {+            emit_op(s, OP_await);+        }+        emit_op(s, OP_return_async);+    } else {+        emit_op(s, hasval ? OP_return : OP_return_undef);+    }+}++#define DECL_MASK_FUNC  (1 << 0) /* allow normal function declaration */+/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */+#define DECL_MASK_FUNC_WITH_LABEL (1 << 1)+#define DECL_MASK_OTHER (1 << 2) /* all other declarations */+#define DECL_MASK_ALL   (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)++static __exception int js_parse_statement_or_decl(JSParseState *s,+                                                  int decl_mask);++static __exception int js_parse_statement(JSParseState *s)+{+    return js_parse_statement_or_decl(s, 0);+}++static __exception int js_parse_block(JSParseState *s)+{+    if (js_parse_expect(s, '{'))+        return -1;+    if (s->token.val != '}') {+        push_scope(s);+        for(;;) {+            if (js_parse_statement_or_decl(s, DECL_MASK_ALL))+                return -1;+            if (s->token.val == '}')+                break;+        }+        pop_scope(s);+    }+    if (next_token(s))+        return -1;+    return 0;+}++static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok,+                                    BOOL export_flag)+{+    JSContext *ctx = s->ctx;+    JSFunctionDef *fd = s->cur_func;+    JSAtom name = JS_ATOM_NULL;++    for (;;) {+        if (s->token.val == TOK_IDENT) {+            if (s->token.u.ident.is_reserved) {+                return js_parse_error_reserved_identifier(s);+            }+            name = JS_DupAtom(ctx, s->token.u.ident.atom);+            if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {+                js_parse_error(s, "'let' is not a valid lexical identifier");+                goto var_error;+            }+            if (next_token(s))+                goto var_error;+            if (js_define_var(s, name, tok))+                goto var_error;+            if (export_flag) {+                if (!add_export_entry(s, s->cur_func->module, name, name,+                                      JS_EXPORT_TYPE_LOCAL))+                    goto var_error;+            }++            if (s->token.val == '=') {+                if (next_token(s))+                    goto var_error;+                if (tok == TOK_VAR) {+                    /* Must make a reference for proper `with` semantics */+                    int opcode, scope, label;+                    JSAtom name1;++                    emit_op(s, OP_scope_get_var);+                    emit_atom(s, name);+                    emit_u16(s, fd->scope_level);+                    if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)+                        goto var_error;+                    if (js_parse_assign_expr(s, in_accepted)) {+                        JS_FreeAtom(ctx, name1);+                        goto var_error;+                    }+                    set_object_name(s, name);+                    put_lvalue(s, opcode, scope, name1, label, FALSE);+                    emit_op(s, OP_drop);+                } else {+                    if (js_parse_assign_expr(s, in_accepted))+                        goto var_error;+                    set_object_name(s, name);+                    emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?+                        OP_scope_put_var_init : OP_scope_put_var);+                    emit_atom(s, name);+                    emit_u16(s, fd->scope_level);+                }+            } else {+                if (tok == TOK_CONST) {+                    js_parse_error(s, "missing initializer for const variable");+                    goto var_error;+                }+                if (tok == TOK_LET) {+                    /* initialize lexical variable upon entering its scope */+                    emit_op(s, OP_undefined);+                    emit_op(s, OP_scope_put_var_init);+                    emit_atom(s, name);+                    emit_u16(s, fd->scope_level);+                }+            }+            JS_FreeAtom(ctx, name);+        } else {+            int skip_bits;+            if ((s->token.val == '[' || s->token.val == '{')+            &&  js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {+                emit_op(s, OP_undefined);+                if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))+                    return -1;+            } else {+                return js_parse_error(s, "variable name expected");+            }+        }+        if (s->token.val != ',')+            break;+        if (next_token(s))+            return -1;+    }+    return 0;++ var_error:+    JS_FreeAtom(ctx, name);+    return -1;+}++/* test if the current token is a label. Use simplistic look-ahead scanner */+static BOOL is_label(JSParseState *s)+{+    return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&+            peek_token(s, FALSE) == ':');+}++/* test if the current token is a let keyword. Use simplistic look-ahead scanner */+static int is_let(JSParseState *s, int decl_mask)+{+    int res = FALSE;++    if (token_is_pseudo_keyword(s, JS_ATOM_let)) {+#if 1+        JSParsePos pos;+        js_parse_get_pos(s, &pos);+        for (;;) {+            if (next_token(s)) {+                res = -1;+                break;+            }+            if (s->token.val == '[') {+                /* let [ is a syntax restriction:+                   it never introduces an ExpressionStatement */+                res = TRUE;+                break;+            }+            if (s->token.val == '{' ||+                (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||+                s->token.val == TOK_LET ||+                s->token.val == TOK_YIELD ||+                s->token.val == TOK_AWAIT) {+                /* Check for possible ASI if not scanning for Declaration */+                /* XXX: should also check that `{` introduces a BindingPattern,+                   but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */+                if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {+                    res = TRUE;+                    break;+                }+                break;+            }+            break;+        }+        if (js_parse_seek_token(s, &pos)) {+            res = -1;+        }+#else+        int tok = peek_token(s, TRUE);+        if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {+            res = TRUE;+        }+#endif+    }+    return res;+}++/* XXX: handle IteratorClose when exiting the loop before the+   enumeration is done */+static __exception int js_parse_for_in_of(JSParseState *s, int label_name,+                                          BOOL is_async)+{+    JSContext *ctx = s->ctx;+    JSFunctionDef *fd = s->cur_func;+    JSAtom var_name;+    BOOL has_initializer, is_for_of, has_destructuring;+    int tok, tok1, opcode, scope, block_scope_level;+    int label_next, label_expr, label_cont, label_body, label_break;+    int pos_next, pos_expr;+    BlockEnv break_entry;++    has_initializer = FALSE;+    has_destructuring = FALSE;+    is_for_of = FALSE;+    block_scope_level = fd->scope_level;+    label_cont = new_label(s);+    label_body = new_label(s);+    label_break = new_label(s);+    label_next = new_label(s);++    /* create scope for the lexical variables declared in the enumeration+       expressions. XXX: Not completely correct because of weird capturing+       semantics in `for (i of o) a.push(function(){return i})` */+    push_scope(s);++    /* local for_in scope starts here so individual elements+       can be closed in statement. */+    push_break_entry(s->cur_func, &break_entry,+                     label_name, label_break, label_cont, 1);+    break_entry.scope_level = block_scope_level;++    label_expr = emit_goto(s, OP_goto, -1);++    pos_next = s->cur_func->byte_code.size;+    emit_label(s, label_next);++    tok = s->token.val;+    switch (is_let(s, DECL_MASK_OTHER)) {+    case TRUE:+        tok = TOK_LET;+        break;+    case FALSE:+        break;+    default:+        return -1;+    }+    if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {+        if (next_token(s))+            return -1;++        if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {+            if (s->token.val == '[' || s->token.val == '{') {+                if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE))+                    return -1;+                has_destructuring = TRUE;+            } else {+                return js_parse_error(s, "variable name expected");+            }+            var_name = JS_ATOM_NULL;+        } else {+            var_name = JS_DupAtom(ctx, s->token.u.ident.atom);+            if (next_token(s)) {+                JS_FreeAtom(s->ctx, var_name);+                return -1;+            }+            if (js_define_var(s, var_name, tok)) {+                JS_FreeAtom(s->ctx, var_name);+                return -1;+            }+            emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?+                    OP_scope_put_var_init : OP_scope_put_var);+            emit_atom(s, var_name);+            emit_u16(s, fd->scope_level);+        }+    } else {+        int skip_bits;+        if ((s->token.val == '[' || s->token.val == '{')+        &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {+            if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))+                return -1;+        } else {+            int lvalue_label, depth;+            if (js_parse_postfix_expr(s, TRUE))+                return -1;+            if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,+                           &depth, FALSE, TOK_FOR))+                return -1;+            /* swap value and lvalue object and store it into lvalue object */+            /* enum_rec val [depth] -- enum_rec */+            switch(depth) {+            case 1:+                emit_op(s, OP_swap);+                break;+            case 2:+                emit_op(s, OP_rot3l);+                break;+            case 3:+                emit_op(s, OP_rot4l);+                break;+            default:+                abort();+            }+            put_lvalue_nokeep(s, opcode, scope, var_name, lvalue_label,+                              TOK_FOR /* not used */);+        }+        var_name = JS_ATOM_NULL;+    }+    emit_goto(s, OP_goto, label_body);++    pos_expr = s->cur_func->byte_code.size;+    emit_label(s, label_expr);+    if (s->token.val == '=') {+        /* XXX: potential scoping issue if inside `with` statement */+        has_initializer = TRUE;+        /* parse and evaluate initializer prior to evaluating the+           object (only used with "for in" with a non lexical variable+           in non strict mode */+        if (next_token(s) || js_parse_assign_expr(s, FALSE)) {+            JS_FreeAtom(ctx, var_name);+            return -1;+        }+        if (var_name != JS_ATOM_NULL) {+            emit_op(s, OP_scope_put_var);+            emit_atom(s, var_name);+            emit_u16(s, fd->scope_level);+        }+    }+    JS_FreeAtom(ctx, var_name);++    if (token_is_pseudo_keyword(s, JS_ATOM_of)) {+        break_entry.has_iterator = is_for_of = TRUE;+        break_entry.drop_count += 2;+        if (has_initializer)+            goto initializer_error;+    } else if (s->token.val == TOK_IN) {+        if (is_async)+            return js_parse_error(s, "'for await' loop should be used with 'of'");+        if (has_initializer &&+            (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||+             has_destructuring)) {+        initializer_error:+            return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",+                                  is_for_of ? "of" : "in");+        }+    } else {+        return js_parse_error(s, "expected 'of' or 'in' in for control expression");+    }+    if (next_token(s))+        return -1;+    if (is_for_of) {+        if (js_parse_assign_expr(s, TRUE))+            return -1;+    } else {+        if (js_parse_expr(s))+            return -1;+    }+    /* close the scope after having evaluated the expression so that+       the TDZ values are in the closures */+    close_scopes(s, s->cur_func->scope_level, block_scope_level);+    if (is_for_of) {+        if (is_async)+            emit_op(s, OP_for_await_of_start);+        else+            emit_op(s, OP_for_of_start);+        /* on stack: enum_rec */+    } else {+        emit_op(s, OP_for_in_start);+        /* on stack: enum_obj */+    }+    emit_goto(s, OP_goto, label_cont);++    if (js_parse_expect(s, ')'))+        return -1;++    if (OPTIMIZE) {+        /* move the `next` code here */+        DynBuf *bc = &s->cur_func->byte_code;+        int chunk_size = pos_expr - pos_next;+        int offset = bc->size - pos_next;+        int i;+        dbuf_realloc(bc, bc->size + chunk_size);+        dbuf_put(bc, bc->buf + pos_next, chunk_size);+        memset(bc->buf + pos_next, OP_nop, chunk_size);+        /* `next` part ends with a goto */+        s->cur_func->last_opcode_pos = bc->size - 5;+        /* relocate labels */+        for (i = label_cont; i < s->cur_func->label_count; i++) {+            LabelSlot *ls = &s->cur_func->label_slots[i];+            if (ls->pos >= pos_next && ls->pos < pos_expr)+                ls->pos += offset;+        }+    }++    emit_label(s, label_body);+    if (js_parse_statement(s))+        return -1;++    close_scopes(s, s->cur_func->scope_level, block_scope_level);++    emit_label(s, label_cont);+    if (is_for_of) {+        if (is_async) {+            /* call the next method */+            emit_op(s, OP_for_await_of_next);+            /* get the result of the promise */+            emit_op(s, OP_await);+            /* unwrap the value and done values */+            emit_op(s, OP_iterator_get_value_done);+        } else {+            emit_op(s, OP_for_of_next);+            emit_u8(s, 0);+        }+    } else {+        emit_op(s, OP_for_in_next);+    }+    /* on stack: enum_rec / enum_obj value bool */+    emit_goto(s, OP_if_false, label_next);+    /* drop the undefined value from for_xx_next */+    emit_op(s, OP_drop);++    emit_label(s, label_break);+    if (is_for_of) {+        /* close and drop enum_rec */+        emit_op(s, OP_iterator_close);+    } else {+        emit_op(s, OP_drop);+    }+    pop_break_entry(s->cur_func);+    pop_scope(s);+    return 0;+}++static void set_eval_ret_undefined(JSParseState *s)+{+    if (s->cur_func->eval_ret_idx >= 0) {+        emit_op(s, OP_undefined);+        emit_op(s, OP_put_loc);+        emit_u16(s, s->cur_func->eval_ret_idx);+    }+}++static __exception int js_parse_statement_or_decl(JSParseState *s,+                                                  int decl_mask)+{+    JSContext *ctx = s->ctx;+    JSAtom label_name;+    int tok;++    /* specific label handling */+    /* XXX: support multiple labels on loop statements */+    label_name = JS_ATOM_NULL;+    if (is_label(s)) {+        BlockEnv *be;++        label_name = JS_DupAtom(ctx, s->token.u.ident.atom);++        for (be = s->cur_func->top_break; be; be = be->prev) {+            if (be->label_name == label_name) {+                js_parse_error(s, "duplicate label name");+                goto fail;+            }+        }++        if (next_token(s))+            goto fail;+        if (js_parse_expect(s, ':'))+            goto fail;+        if (s->token.val != TOK_FOR+        &&  s->token.val != TOK_DO+        &&  s->token.val != TOK_WHILE) {+            /* labelled regular statement */+            int label_break, mask;+            BlockEnv break_entry;++            label_break = new_label(s);+            push_break_entry(s->cur_func, &break_entry,+                             label_name, label_break, -1, 0);+            if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&+                (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {+                mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;+            } else {+                mask = 0;+            }+            if (js_parse_statement_or_decl(s, mask))+                goto fail;+            emit_label(s, label_break);+            pop_break_entry(s->cur_func);+            goto done;+        }+    }++    switch(tok = s->token.val) {+    case '{':+        if (js_parse_block(s))+            goto fail;+        break;+    case TOK_RETURN:+        if (s->cur_func->is_eval) {+            js_parse_error(s, "return not in a function");+            goto fail;+        }+        if (next_token(s))+            goto fail;+        if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {+            if (js_parse_expr(s))+                goto fail;+            emit_return(s, TRUE);+        } else {+            emit_return(s, FALSE);+        }+        if (js_parse_expect_semi(s))+            goto fail;+        break;+    case TOK_THROW:+        if (next_token(s))+            goto fail;+        if (s->got_lf) {+            js_parse_error(s, "line terminator not allowed after throw");+            goto fail;+        }+        if (js_parse_expr(s))+            goto fail;+        emit_op(s, OP_throw);+        if (js_parse_expect_semi(s))+            goto fail;+        break;+    case TOK_LET:+    case TOK_CONST:+    haslet:+        if (!(decl_mask & DECL_MASK_OTHER)) {+            js_parse_error(s, "lexical declarations can't appear in single-statement context");+            goto fail;+        }+        /* fall thru */+    case TOK_VAR:+        if (next_token(s))+            goto fail;+        if (js_parse_var(s, TRUE, tok, FALSE))+            goto fail;+        if (js_parse_expect_semi(s))+            goto fail;+        break;+    case TOK_IF:+        {+            int label1, label2, mask;+            if (next_token(s))+                goto fail;+            /* create a new scope for `let f;if(1) function f(){}` */+            push_scope(s);+            set_eval_ret_undefined(s);+            if (js_parse_expr_paren(s))+                goto fail;+            label1 = emit_goto(s, OP_if_false, -1);+            if (s->cur_func->js_mode & JS_MODE_STRICT)+                mask = 0;+            else+                mask = DECL_MASK_FUNC; /* Annex B.3.4 */++            if (js_parse_statement_or_decl(s, mask))+                goto fail;++            if (s->token.val == TOK_ELSE) {+                label2 = emit_goto(s, OP_goto, -1);+                if (next_token(s))+                    goto fail;++                emit_label(s, label1);+                if (js_parse_statement_or_decl(s, mask))+                    goto fail;++                label1 = label2;+            }+            emit_label(s, label1);+            pop_scope(s);+        }+        break;+    case TOK_WHILE:+        {+            int label_cont, label_break;+            BlockEnv break_entry;++            label_cont = new_label(s);+            label_break = new_label(s);++            push_break_entry(s->cur_func, &break_entry,+                             label_name, label_break, label_cont, 0);++            if (next_token(s))+                goto fail;++            set_eval_ret_undefined(s);++            emit_label(s, label_cont);+            if (js_parse_expr_paren(s))+                goto fail;+            emit_goto(s, OP_if_false, label_break);++            if (js_parse_statement(s))+                goto fail;+            emit_goto(s, OP_goto, label_cont);++            emit_label(s, label_break);++            pop_break_entry(s->cur_func);+        }+        break;+    case TOK_DO:+        {+            int label_cont, label_break, label1;+            BlockEnv break_entry;++            label_cont = new_label(s);+            label_break = new_label(s);+            label1 = new_label(s);++            push_break_entry(s->cur_func, &break_entry,+                             label_name, label_break, label_cont, 0);++            if (next_token(s))+                goto fail;++            emit_label(s, label1);++            set_eval_ret_undefined(s);++            if (js_parse_statement(s))+                goto fail;++            emit_label(s, label_cont);+            if (js_parse_expect(s, TOK_WHILE))+                goto fail;+            if (js_parse_expr_paren(s))+                goto fail;+            /* Insert semicolon if missing */+            if (s->token.val == ';') {+                if (next_token(s))+                    goto fail;+            }+            emit_goto(s, OP_if_true, label1);++            emit_label(s, label_break);++            pop_break_entry(s->cur_func);+        }+        break;+    case TOK_FOR:+        {+            int label_cont, label_break, label_body, label_test;+            int pos_cont, pos_body, block_scope_level;+            BlockEnv break_entry;+            int tok, bits;+            BOOL is_async;++            if (next_token(s))+                goto fail;++            set_eval_ret_undefined(s);+            bits = 0;+            is_async = FALSE;+            if (s->token.val == '(') {+                js_parse_skip_parens_token(s, &bits, FALSE);+            } else if (s->token.val == TOK_AWAIT) {+                if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {+                    js_parse_error(s, "for await is only valid in asynchronous functions");+                    goto fail;+                }+                is_async = TRUE;+                if (next_token(s))+                    goto fail;+            }+            if (js_parse_expect(s, '('))+                goto fail;++            if (!(bits & SKIP_HAS_SEMI)) {+                /* parse for/in or for/of */+                if (js_parse_for_in_of(s, label_name, is_async))+                    goto fail;+                break;+            }+            block_scope_level = s->cur_func->scope_level;++            /* create scope for the lexical variables declared in the initial,+               test and increment expressions */+            push_scope(s);+            /* initial expression */+            tok = s->token.val;+            if (tok != ';') {+                switch (is_let(s, DECL_MASK_OTHER)) {+                case TRUE:+                    tok = TOK_LET;+                    break;+                case FALSE:+                    break;+                default:+                    goto fail;+                }+                if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {+                    if (next_token(s))+                        goto fail;+                    if (js_parse_var(s, FALSE, tok, FALSE))+                        goto fail;+                } else {+                    if (js_parse_expr2(s, FALSE))+                        goto fail;+                    emit_op(s, OP_drop);+                }++                /* close the closures before the first iteration */+                close_scopes(s, s->cur_func->scope_level, block_scope_level);+            }+            if (js_parse_expect(s, ';'))+                goto fail;++            label_test = new_label(s);+            label_cont = new_label(s);+            label_body = new_label(s);+            label_break = new_label(s);++            push_break_entry(s->cur_func, &break_entry,+                             label_name, label_break, label_cont, 0);++            /* test expression */+            if (s->token.val == ';') {+                /* no test expression */+                label_test = label_body;+            } else {+                emit_label(s, label_test);+                if (js_parse_expr(s))+                    goto fail;+                emit_goto(s, OP_if_false, label_break);+            }+            if (js_parse_expect(s, ';'))+                goto fail;++            if (s->token.val == ')') {+                /* no end expression */+                break_entry.label_cont = label_cont = label_test;+                pos_cont = 0; /* avoid warning */+            } else {+                /* skip the end expression */+                emit_goto(s, OP_goto, label_body);++                pos_cont = s->cur_func->byte_code.size;+                emit_label(s, label_cont);+                if (js_parse_expr(s))+                    goto fail;+                emit_op(s, OP_drop);+                if (label_test != label_body)+                    emit_goto(s, OP_goto, label_test);+            }+            if (js_parse_expect(s, ')'))+                goto fail;++            pos_body = s->cur_func->byte_code.size;+            emit_label(s, label_body);+            if (js_parse_statement(s))+                goto fail;++            /* close the closures before the next iteration */+            /* XXX: check continue case */+            close_scopes(s, s->cur_func->scope_level, block_scope_level);++            if (OPTIMIZE && label_test != label_body && label_cont != label_test) {+                /* move the increment code here */+                DynBuf *bc = &s->cur_func->byte_code;+                int chunk_size = pos_body - pos_cont;+                int offset = bc->size - pos_cont;+                int i;+                dbuf_realloc(bc, bc->size + chunk_size);+                dbuf_put(bc, bc->buf + pos_cont, chunk_size);+                memset(bc->buf + pos_cont, OP_nop, chunk_size);+                /* increment part ends with a goto */+                s->cur_func->last_opcode_pos = bc->size - 5;+                /* relocate labels */+                for (i = label_cont; i < s->cur_func->label_count; i++) {+                    LabelSlot *ls = &s->cur_func->label_slots[i];+                    if (ls->pos >= pos_cont && ls->pos < pos_body)+                        ls->pos += offset;+                }+            } else {+                emit_goto(s, OP_goto, label_cont);+            }++            emit_label(s, label_break);++            pop_break_entry(s->cur_func);+            pop_scope(s);+        }+        break;+    case TOK_BREAK:+    case TOK_CONTINUE:+        {+            int is_cont = s->token.val - TOK_BREAK;+            int label;++            if (next_token(s))+                goto fail;+            if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)+                label = s->token.u.ident.atom;+            else+                label = JS_ATOM_NULL;+            if (emit_break(s, label, is_cont))+                goto fail;+            if (label != JS_ATOM_NULL) {+                if (next_token(s))+                    goto fail;+            }+            if (js_parse_expect_semi(s))+                goto fail;+        }+        break;+    case TOK_SWITCH:+        {+            int label_case, label_break, label1;+            int default_label_pos;+            BlockEnv break_entry;++            if (next_token(s))+                goto fail;++            set_eval_ret_undefined(s);+            if (js_parse_expr_paren(s))+                goto fail;++            push_scope(s);+            label_break = new_label(s);+            push_break_entry(s->cur_func, &break_entry,+                             label_name, label_break, -1, 1);++            if (js_parse_expect(s, '{'))+                goto fail;++            default_label_pos = -1;+            label_case = -1;+            while (s->token.val != '}') {+                if (s->token.val == TOK_CASE) {+                    label1 = -1;+                    if (label_case >= 0) {+                        /* skip the case if needed */+                        label1 = emit_goto(s, OP_goto, -1);+                    }+                    emit_label(s, label_case);+                    label_case = -1;+                    for (;;) {+                        /* parse a sequence of case clauses */+                        if (next_token(s))+                            goto fail;+                        emit_op(s, OP_dup);+                        if (js_parse_expr(s))+                            goto fail;+                        if (js_parse_expect(s, ':'))+                            goto fail;+                        emit_op(s, OP_strict_eq);+                        if (s->token.val == TOK_CASE) {+                            label1 = emit_goto(s, OP_if_true, label1);+                        } else {+                            label_case = emit_goto(s, OP_if_false, -1);+                            emit_label(s, label1);+                            break;+                        }+                    }+                } else if (s->token.val == TOK_DEFAULT) {+                    if (next_token(s))+                        goto fail;+                    if (js_parse_expect(s, ':'))+                        goto fail;+                    if (default_label_pos >= 0) {+                        js_parse_error(s, "duplicate default");+                        goto fail;+                    }+                    if (label_case < 0) {+                        /* falling thru direct from switch expression */+                        label_case = emit_goto(s, OP_goto, -1);+                    }+                    /* Emit a dummy label opcode. Label will be patched after+                       the end of the switch body. Do not use emit_label(s, 0)+                       because it would clobber label 0 address, preventing+                       proper optimizer operation.+                     */+                    emit_op(s, OP_label);+                    emit_u32(s, 0);+                    default_label_pos = s->cur_func->byte_code.size - 4;+                } else {+                    if (label_case < 0) {+                        /* falling thru direct from switch expression */+                        js_parse_error(s, "invalid switch statement");+                        goto fail;+                    }+                    if (js_parse_statement_or_decl(s, DECL_MASK_ALL))+                        goto fail;+                }+            }+            if (js_parse_expect(s, '}'))+                goto fail;+            if (default_label_pos >= 0) {+                /* Ugly patch for the the `default` label, shameful and risky */+                put_u32(s->cur_func->byte_code.buf + default_label_pos,+                        label_case);+                s->cur_func->label_slots[label_case].pos = default_label_pos + 4;+            } else {+                emit_label(s, label_case);+            }+            emit_label(s, label_break);+            emit_op(s, OP_drop); /* drop the switch expression */++            pop_break_entry(s->cur_func);+            pop_scope(s);+        }+        break;+    case TOK_TRY:+        {+            int label_catch, label_catch2, label_finally, label_end;+            JSAtom name;+            BlockEnv block_env;++            set_eval_ret_undefined(s);+            if (next_token(s))+                goto fail;+            label_catch = new_label(s);+            label_catch2 = new_label(s);+            label_finally = new_label(s);+            label_end = new_label(s);++            emit_goto(s, OP_catch, label_catch);++            push_break_entry(s->cur_func, &block_env,+                             JS_ATOM_NULL, -1, -1, 1);+            block_env.label_finally = label_finally;++            if (js_parse_block(s))+                goto fail;++            pop_break_entry(s->cur_func);++            if (js_is_live_code(s)) {+                /* drop the catch offset */+                emit_op(s, OP_drop);+                /* must push dummy value to keep same stack size */+                emit_op(s, OP_undefined);+                emit_goto(s, OP_gosub, label_finally);+                emit_op(s, OP_drop);++                emit_goto(s, OP_goto, label_end);+            }++            if (s->token.val == TOK_CATCH) {+                if (next_token(s))+                    goto fail;++                push_scope(s);  /* catch variable */+                emit_label(s, label_catch);++                if (s->token.val == '{') {+                    /* support optional-catch-binding feature */+                    emit_op(s, OP_drop);    /* pop the exception object */+                } else {+                    if (js_parse_expect(s, '('))+                        goto fail;+                    if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {+                        if (s->token.val == '[' || s->token.val == '{') {+                            /* XXX: TOK_LET is not completely correct */+                            if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE))+                                goto fail;+                        } else {+                            js_parse_error(s, "identifier expected");+                            goto fail;+                        }+                    } else {+                        name = JS_DupAtom(ctx, s->token.u.ident.atom);+                        if (next_token(s)+                        ||  js_define_var(s, name, TOK_CATCH) < 0) {+                            JS_FreeAtom(ctx, name);+                            goto fail;+                        }+                        /* store the exception value in the catch variable */+                        emit_op(s, OP_scope_put_var);+                        emit_u32(s, name);+                        emit_u16(s, s->cur_func->scope_level);+                    }+                    if (js_parse_expect(s, ')'))+                        goto fail;+                }+                /* XXX: should keep the address to nop it out if there is no finally block */+                emit_goto(s, OP_catch, label_catch2);++                push_scope(s);  /* catch block */+                push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,+                                 -1, -1, 1);+                block_env.label_finally = label_finally;++                if (js_parse_block(s))+                    goto fail;++                pop_break_entry(s->cur_func);+                pop_scope(s);  /* catch block */+                pop_scope(s);  /* catch variable */++                if (js_is_live_code(s)) {+                    /* drop the catch2 offset */+                    emit_op(s, OP_drop);+                    /* XXX: should keep the address to nop it out if there is no finally block */+                    /* must push dummy value to keep same stack size */+                    emit_op(s, OP_undefined);+                    emit_goto(s, OP_gosub, label_finally);+                    emit_op(s, OP_drop);+                    emit_goto(s, OP_goto, label_end);+                }+                /* catch exceptions thrown in the catch block to execute the+                 * finally clause and rethrow the exception */+                emit_label(s, label_catch2);+                /* catch value is at TOS, no need to push undefined */+                emit_goto(s, OP_gosub, label_finally);+                emit_op(s, OP_throw);++            } else if (s->token.val == TOK_FINALLY) {+                /* finally without catch : execute the finally clause+                 * and rethrow the exception */+                emit_label(s, label_catch);+                /* catch value is at TOS, no need to push undefined */+                emit_goto(s, OP_gosub, label_finally);+                emit_op(s, OP_throw);+            } else {+                js_parse_error(s, "expecting catch or finally");+                goto fail;+            }+            emit_label(s, label_finally);+            if (s->token.val == TOK_FINALLY) {+                int saved_eval_ret_idx;+                if (next_token(s))+                    goto fail;+                /* on the stack: ret_value gosub_ret_value */+                push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,+                                 -1, -1, 2);+                saved_eval_ret_idx = s->cur_func->eval_ret_idx;+                s->cur_func->eval_ret_idx = -1;+                /* 'finally' does not update eval_ret */+                if (js_parse_block(s))+                    goto fail;+                s->cur_func->eval_ret_idx = saved_eval_ret_idx;+                pop_break_entry(s->cur_func);+            }+            emit_op(s, OP_ret);+            emit_label(s, label_end);+        }+        break;+    case ';':+        /* empty statement */+        if (next_token(s))+            goto fail;+        break;+    case TOK_WITH:+        if (s->cur_func->js_mode & JS_MODE_STRICT) {+            js_parse_error(s, "invalid keyword: with");+            goto fail;+        } else {+            int with_idx;++            if (next_token(s))+                goto fail;++            if (js_parse_expr_paren(s))+                goto fail;++            push_scope(s);+            with_idx = define_var(s, s->cur_func, JS_ATOM__with_,+                                  JS_VAR_DEF_WITH);+            if (with_idx < 0)+                goto fail;+            emit_op(s, OP_to_object);+            emit_op(s, OP_put_loc);+            emit_u16(s, with_idx);++            set_eval_ret_undefined(s);+            if (js_parse_statement(s))+                goto fail;++            /* Popping scope drops lexical context for the with object variable */+            pop_scope(s);+        }+        break;+    case TOK_FUNCTION:+        /* ES6 Annex B.3.2 and B.3.3 semantics */+        if (!(decl_mask & DECL_MASK_FUNC))+            goto func_decl_error;+        if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')+            goto func_decl_error;+        goto parse_func_var;+    case TOK_IDENT:+        if (s->token.u.ident.is_reserved) {+            js_parse_error_reserved_identifier(s);+            goto fail;+        }+        /* Determine if `let` introduces a Declaration or an ExpressionStatement */+        switch (is_let(s, decl_mask)) {+        case TRUE:+            tok = TOK_LET;+            goto haslet;+        case FALSE:+            break;+        default:+            goto fail;+        }+        if (token_is_pseudo_keyword(s, JS_ATOM_async) &&+            peek_token(s, TRUE) == TOK_FUNCTION) {+            if (!(decl_mask & DECL_MASK_OTHER)) {+            func_decl_error:+                js_parse_error(s, "function declarations can't appear in single-statement context");+                goto fail;+            }+        parse_func_var:+            if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,+                                       JS_FUNC_NORMAL, JS_ATOM_NULL,+                                       s->token.ptr, s->token.line_num))+                goto fail;+            break;+        }+        goto hasexpr;++    case TOK_CLASS:+        if (!(decl_mask & DECL_MASK_OTHER)) {+            js_parse_error(s, "class declarations can't appear in single-statement context");+            goto fail;+        }+        if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))+            return -1;+        break;++    case TOK_DEBUGGER:+        /* currently no debugger, so just skip the keyword */+        if (next_token(s))+            goto fail;+        if (js_parse_expect_semi(s))+            goto fail;+        break;+        +    case TOK_ENUM:+    case TOK_EXPORT:+    case TOK_EXTENDS:+        js_unsupported_keyword(s, s->token.u.ident.atom);+        goto fail;++    default:+    hasexpr:+        if (js_parse_expr(s))+            goto fail;+        if (s->cur_func->eval_ret_idx >= 0) {+            /* store the expression value so that it can be returned+               by eval() */+            emit_op(s, OP_put_loc);+            emit_u16(s, s->cur_func->eval_ret_idx);+        } else {+            emit_op(s, OP_drop); /* drop the result */+        }+        if (js_parse_expect_semi(s))+            goto fail;+        break;+    }+done:+    JS_FreeAtom(ctx, label_name);+    return 0;+fail:+    JS_FreeAtom(ctx, label_name);+    return -1;+}++/* 'name' is freed */+static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)+{+    JSModuleDef *m;+    m = js_mallocz(ctx, sizeof(*m));+    if (!m) {+        JS_FreeAtom(ctx, name);+        return NULL;+    }+    m->header.ref_count = 1;+    m->module_name = name;+    m->module_ns = JS_UNDEFINED;+    m->func_obj = JS_UNDEFINED;+    m->eval_exception = JS_UNDEFINED;+    m->meta_obj = JS_UNDEFINED;+    list_add_tail(&m->link, &ctx->loaded_modules);+    return m;+}++static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,+                               JS_MarkFunc *mark_func)+{+    int i;++    for(i = 0; i < m->export_entries_count; i++) {+        JSExportEntry *me = &m->export_entries[i];+        if (me->export_type == JS_EXPORT_TYPE_LOCAL &&+            me->u.local.var_ref) {+            mark_func(rt, &me->u.local.var_ref->header);+        }+    }++    JS_MarkValue(rt, m->module_ns, mark_func);+    JS_MarkValue(rt, m->func_obj, mark_func);+    JS_MarkValue(rt, m->eval_exception, mark_func);+    JS_MarkValue(rt, m->meta_obj, mark_func);+}++static void js_free_module_def(JSContext *ctx, JSModuleDef *m)+{+    int i;++    JS_FreeAtom(ctx, m->module_name);++    for(i = 0; i < m->req_module_entries_count; i++) {+        JSReqModuleEntry *rme = &m->req_module_entries[i];+        JS_FreeAtom(ctx, rme->module_name);+    }+    js_free(ctx, m->req_module_entries);++    for(i = 0; i < m->export_entries_count; i++) {+        JSExportEntry *me = &m->export_entries[i];+        if (me->export_type == JS_EXPORT_TYPE_LOCAL)+            free_var_ref(ctx->rt, me->u.local.var_ref);+        JS_FreeAtom(ctx, me->export_name);+        JS_FreeAtom(ctx, me->local_name);+    }+    js_free(ctx, m->export_entries);++    js_free(ctx, m->star_export_entries);++    for(i = 0; i < m->import_entries_count; i++) {+        JSImportEntry *mi = &m->import_entries[i];+        JS_FreeAtom(ctx, mi->import_name);+    }+    js_free(ctx, m->import_entries);++    JS_FreeValue(ctx, m->module_ns);+    JS_FreeValue(ctx, m->func_obj);+    JS_FreeValue(ctx, m->eval_exception);+    JS_FreeValue(ctx, m->meta_obj);+    list_del(&m->link);+    js_free(ctx, m);+}++static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,+                                JSAtom module_name)+{+    JSReqModuleEntry *rme;+    int i;++    /* no need to add the module request if it is already present */+    for(i = 0; i < m->req_module_entries_count; i++) {+        rme = &m->req_module_entries[i];+        if (rme->module_name == module_name)+            return i;+    }++    if (js_resize_array(ctx, (void **)&m->req_module_entries,+                        sizeof(JSReqModuleEntry),+                        &m->req_module_entries_size,+                        m->req_module_entries_count + 1))+        return -1;+    rme = &m->req_module_entries[m->req_module_entries_count++];+    rme->module_name = JS_DupAtom(ctx, module_name);+    rme->module = NULL;+    return i;+}++static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,+                                        JSAtom export_name)+{+    JSExportEntry *me;+    int i;+    for(i = 0; i < m->export_entries_count; i++) {+        me = &m->export_entries[i];+        if (me->export_name == export_name)+            return me;+    }+    return NULL;+}++static JSExportEntry *add_export_entry2(JSContext *ctx,+                                        JSParseState *s, JSModuleDef *m,+                                       JSAtom local_name, JSAtom export_name,+                                       JSExportTypeEnum export_type)+{+    JSExportEntry *me;++    if (find_export_entry(ctx, m, export_name)) {+        char buf1[ATOM_GET_STR_BUF_SIZE];+        if (s) {+            js_parse_error(s, "duplicate exported name '%s'",+                           JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));+        } else {+            JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);+        }+        return NULL;+    }++    if (js_resize_array(ctx, (void **)&m->export_entries,+                        sizeof(JSExportEntry),+                        &m->export_entries_size,+                        m->export_entries_count + 1))+        return NULL;+    me = &m->export_entries[m->export_entries_count++];+    memset(me, 0, sizeof(*me));+    me->local_name = JS_DupAtom(ctx, local_name);+    me->export_name = JS_DupAtom(ctx, export_name);+    me->export_type = export_type;+    return me;+}++static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,+                                       JSAtom local_name, JSAtom export_name,+                                       JSExportTypeEnum export_type)+{+    return add_export_entry2(s->ctx, s, m, local_name, export_name,+                             export_type);+}++static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,+                                 int req_module_idx)+{+    JSStarExportEntry *se;++    if (js_resize_array(ctx, (void **)&m->star_export_entries,+                        sizeof(JSStarExportEntry),+                        &m->star_export_entries_size,+                        m->star_export_entries_count + 1))+        return -1;+    se = &m->star_export_entries[m->star_export_entries_count++];+    se->req_module_idx = req_module_idx;+    return 0;+}++/* create a C module */+JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,+                           JSModuleInitFunc *func)+{+    JSModuleDef *m;+    JSAtom name;+    name = JS_NewAtom(ctx, name_str);+    if (name == JS_ATOM_NULL)+        return NULL;+    m = js_new_module_def(ctx, name);+    m->init_func = func;+    return m;+}++int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)+{+    JSExportEntry *me;+    JSAtom name;+    name = JS_NewAtom(ctx, export_name);+    if (name == JS_ATOM_NULL)+        return -1;+    me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,+                           JS_EXPORT_TYPE_LOCAL);+    JS_FreeAtom(ctx, name);+    if (!me)+        return -1;+    else+        return 0;+}++int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,+                       JSValue val)+{+    JSExportEntry *me;+    JSAtom name;+    name = JS_NewAtom(ctx, export_name);+    if (name == JS_ATOM_NULL)+        goto fail;+    me = find_export_entry(ctx, m, name);+    JS_FreeAtom(ctx, name);+    if (!me)+        goto fail;+    set_value(ctx, me->u.local.var_ref->pvalue, val);+    return 0;+ fail:+    JS_FreeValue(ctx, val);+    return -1;+}++void JS_SetModuleLoaderFunc(JSRuntime *rt,+                            JSModuleNormalizeFunc *module_normalize,+                            JSModuleLoaderFunc *module_loader, void *opaque)+{+    rt->module_normalize_func = module_normalize;+    rt->module_loader_func = module_loader;+    rt->module_loader_opaque = opaque;+}++/* default module filename normalizer */+static char *js_default_module_normalize_name(JSContext *ctx,+                                              const char *base_name,+                                              const char *name)+{+    char *filename, *p;+    const char *r;+    int len;++    if (name[0] != '.') {+        /* if no initial dot, the module name is not modified */+        return js_strdup(ctx, name);+    }++    p = strrchr(base_name, '/');+    if (p)+        len = p - base_name;+    else+        len = 0;++    filename = js_malloc(ctx, len + strlen(name) + 1 + 1);+    if (!filename)+        return NULL;+    memcpy(filename, base_name, len);+    filename[len] = '\0';++    /* we only normalize the leading '..' or '.' */+    r = name;+    for(;;) {+        if (r[0] == '.' && r[1] == '/') {+            r += 2;+        } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {+            /* remove the last path element of filename, except if "."+               or ".." */+            if (filename[0] == '\0')+                break;+            p = strrchr(filename, '/');+            if (!p)+                p = filename;+            else+                p++;+            if (!strcmp(p, ".") || !strcmp(p, ".."))+                break;+            if (p > filename)+                p--;+            *p = '\0';+            r += 3;+        } else {+            break;+        }+    }+    if (filename[0] != '\0')+        strcat(filename, "/");+    strcat(filename, r);+    //    printf("normalize: %s %s -> %s\n", base_name, name, filename);+    return filename;+}++static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)+{+    struct list_head *el;+    JSModuleDef *m;+    +    /* first look at the loaded modules */+    list_for_each(el, &ctx->loaded_modules) {+        m = list_entry(el, JSModuleDef, link);+        if (m->module_name == name)+            return m;+    }+    return NULL;+}++/* return NULL in case of exception (e.g. module could not be loaded) */+static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,+                                                    JSAtom base_module_name,+                                                    JSAtom module_name1)+{+    JSRuntime *rt = ctx->rt;+    JSModuleDef *m;+    char *cname;+    const char *base_cname, *cname1;+    JSAtom module_name;++    base_cname = JS_AtomToCString(ctx, base_module_name);+    if (!base_cname)+        return NULL;+    cname1 = JS_AtomToCString(ctx, module_name1);+    if (!cname1) {+        JS_FreeCString(ctx, base_cname);+        return NULL;+    }++    if (!rt->module_normalize_func) {+        cname = js_default_module_normalize_name(ctx, base_cname, cname1);+    } else {+        cname = rt->module_normalize_func(ctx, base_cname, cname1,+                                          rt->module_loader_opaque);+    }+    JS_FreeCString(ctx, base_cname);+    JS_FreeCString(ctx, cname1);+    if (!cname)+        return NULL;++    module_name = JS_NewAtom(ctx, cname);+    if (module_name == JS_ATOM_NULL) {+        js_free(ctx, cname);+        return NULL;+    }++    /* first look at the loaded modules */+    m = js_find_loaded_module(ctx, module_name);+    if (m) {+        js_free(ctx, cname);+        JS_FreeAtom(ctx, module_name);+        return m;+    }++    JS_FreeAtom(ctx, module_name);++    /* load the module */+    if (!rt->module_loader_func) {+        /* XXX: use a syntax error ? */+        JS_ThrowReferenceError(ctx, "could not load module '%s'",+                               cname);+        js_free(ctx, cname);+        return NULL;+    }++    m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);+    js_free(ctx, cname);+    return m;+}++typedef struct JSResolveEntry {+    JSModuleDef *module;+    JSAtom name;+} JSResolveEntry;++typedef struct JSResolveState {+    JSResolveEntry *array;+    int size;+    int count;+} JSResolveState;++static int find_resolve_entry(JSResolveState *s,+                              JSModuleDef *m, JSAtom name)+{+    int i;+    for(i = 0; i < s->count; i++) {+        JSResolveEntry *re = &s->array[i];+        if (re->module == m && re->name == name)+            return i;+    }+    return -1;+}++static int add_resolve_entry(JSContext *ctx, JSResolveState *s,+                             JSModuleDef *m, JSAtom name)+{+    JSResolveEntry *re;++    if (js_resize_array(ctx, (void **)&s->array,+                        sizeof(JSResolveEntry),+                        &s->size, s->count + 1))+        return -1;+    re = &s->array[s->count++];+    re->module = m;+    re->name = JS_DupAtom(ctx, name);+    return 0;+}++typedef enum JSResolveResultEnum {+    JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */+    JS_RESOLVE_RES_FOUND = 0,+    JS_RESOLVE_RES_NOT_FOUND,+    JS_RESOLVE_RES_CIRCULAR,+    JS_RESOLVE_RES_AMBIGUOUS,+} JSResolveResultEnum;++static JSResolveResultEnum js_resolve_export1(JSContext *ctx,+                                              JSModuleDef **pmodule,+                                              JSExportEntry **pme,+                                              JSModuleDef *m,+                                              JSAtom export_name,+                                              JSResolveState *s)+{+    JSExportEntry *me;++    *pmodule = NULL;+    *pme = NULL;+    if (find_resolve_entry(s, m, export_name) >= 0)+        return JS_RESOLVE_RES_CIRCULAR;+    if (add_resolve_entry(ctx, s, m, export_name) < 0)+        return JS_RESOLVE_RES_EXCEPTION;+    me = find_export_entry(ctx, m, export_name);+    if (me) {+        if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+            /* local export */+            *pmodule = m;+            *pme = me;+            return JS_RESOLVE_RES_FOUND;+        } else {+            /* indirect export */+            JSModuleDef *m1;+            m1 = m->req_module_entries[me->u.req_module_idx].module;+            if (me->local_name == JS_ATOM__star_) {+                /* export ns from */+                *pmodule = m;+                *pme = me;+                return JS_RESOLVE_RES_FOUND;+            } else {+                return js_resolve_export1(ctx, pmodule, pme, m1,+                                          me->local_name, s);+            }+        }+    } else {+        if (export_name != JS_ATOM_default) {+            /* not found in direct or indirect exports: try star exports */+            int i;++            for(i = 0; i < m->star_export_entries_count; i++) {+                JSStarExportEntry *se = &m->star_export_entries[i];+                JSModuleDef *m1, *res_m;+                JSExportEntry *res_me;+                JSResolveResultEnum ret;++                m1 = m->req_module_entries[se->req_module_idx].module;+                ret = js_resolve_export1(ctx, &res_m, &res_me, m1,+                                         export_name, s);+                if (ret == JS_RESOLVE_RES_AMBIGUOUS ||+                    ret == JS_RESOLVE_RES_EXCEPTION) {+                    return ret;+                } else if (ret == JS_RESOLVE_RES_FOUND) {+                    if (*pme != NULL) {+                        if (*pmodule != res_m ||+                            res_me->local_name != (*pme)->local_name) {+                            *pmodule = NULL;+                            *pme = NULL;+                            return JS_RESOLVE_RES_AMBIGUOUS;+                        }+                    } else {+                        *pmodule = res_m;+                        *pme = res_me;+                    }+                }+            }+            if (*pme != NULL)+                return JS_RESOLVE_RES_FOUND;+        }+        return JS_RESOLVE_RES_NOT_FOUND;+    }+}++/* If the return value is JS_RESOLVE_RES_FOUND, return the module+  (*pmodule) and the corresponding local export entry+  (*pme). Otherwise return (NULL, NULL) */+static JSResolveResultEnum js_resolve_export(JSContext *ctx,+                                             JSModuleDef **pmodule,+                                             JSExportEntry **pme,+                                             JSModuleDef *m,+                                             JSAtom export_name)+{+    JSResolveState ss, *s = &ss;+    int i;+    JSResolveResultEnum ret;++    s->array = NULL;+    s->size = 0;+    s->count = 0;++    ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);++    for(i = 0; i < s->count; i++)+        JS_FreeAtom(ctx, s->array[i].name);+    js_free(ctx, s->array);++    return ret;+}++static void js_resolve_export_throw_error(JSContext *ctx,+                                          JSResolveResultEnum res,+                                          JSModuleDef *m, JSAtom export_name)+{+    char buf1[ATOM_GET_STR_BUF_SIZE];+    char buf2[ATOM_GET_STR_BUF_SIZE];+    switch(res) {+    case JS_RESOLVE_RES_EXCEPTION:+        break;+    default:+    case JS_RESOLVE_RES_NOT_FOUND:+        JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",+                            JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),+                            JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));+        break;+    case JS_RESOLVE_RES_CIRCULAR:+        JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",+                            JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),+                            JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));+        break;+    case JS_RESOLVE_RES_AMBIGUOUS:+        JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",+                            JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),+                            JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));+        break;+    }+}+++typedef enum {+    EXPORTED_NAME_AMBIGUOUS,+    EXPORTED_NAME_NORMAL,+    EXPORTED_NAME_NS,+} ExportedNameEntryEnum;++typedef struct ExportedNameEntry {+    JSAtom export_name;+    ExportedNameEntryEnum export_type;+    union {+        JSExportEntry *me; /* using when the list is built */+        JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */+        JSModuleDef *module; /* for EXPORTED_NAME_NS */+    } u;+} ExportedNameEntry;++typedef struct GetExportNamesState {+    JSModuleDef **modules;+    int modules_size;+    int modules_count;++    ExportedNameEntry *exported_names;+    int exported_names_size;+    int exported_names_count;+} GetExportNamesState;++static int find_exported_name(GetExportNamesState *s, JSAtom name)+{+    int i;+    for(i = 0; i < s->exported_names_count; i++) {+        if (s->exported_names[i].export_name == name)+            return i;+    }+    return -1;+}++static __exception int get_exported_names(JSContext *ctx,+                                          GetExportNamesState *s,+                                          JSModuleDef *m, BOOL from_star)+{+    ExportedNameEntry *en;+    int i, j;++    /* check circular reference */+    for(i = 0; i < s->modules_count; i++) {+        if (s->modules[i] == m)+            return 0;+    }+    if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),+                        &s->modules_size, s->modules_count + 1))+        return -1;+    s->modules[s->modules_count++] = m;++    for(i = 0; i < m->export_entries_count; i++) {+        JSExportEntry *me = &m->export_entries[i];+        if (from_star && me->export_name == JS_ATOM_default)+            continue;+        j = find_exported_name(s, me->export_name);+        if (j < 0) {+            if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),+                                &s->exported_names_size,+                                s->exported_names_count + 1))+                return -1;+            en = &s->exported_names[s->exported_names_count++];+            en->export_name = me->export_name;+            /* avoid a second lookup for simple module exports */+            if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)+                en->u.me = NULL;+            else+                en->u.me = me;+        } else {+            en = &s->exported_names[j];+            en->u.me = NULL;+        }+    }+    for(i = 0; i < m->star_export_entries_count; i++) {+        JSStarExportEntry *se = &m->star_export_entries[i];+        JSModuleDef *m1;+        m1 = m->req_module_entries[se->req_module_idx].module;+        if (get_exported_names(ctx, s, m1, TRUE))+            return -1;+    }+    return 0;+}++/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */+static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)+{+    return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);+}++static const JSClassExoticMethods js_module_ns_exotic_methods = {+    .has_property = js_module_ns_has,+};++static int exported_names_cmp(const void *p1, const void *p2, void *opaque)+{+    JSContext *ctx = opaque;+    const ExportedNameEntry *me1 = p1;+    const ExportedNameEntry *me2 = p2;+    JSValue str1, str2;+    int ret;++    /* XXX: should avoid allocation memory in atom comparison */+    str1 = JS_AtomToString(ctx, me1->export_name);+    str2 = JS_AtomToString(ctx, me2->export_name);+    if (JS_IsException(str1) || JS_IsException(str2)) {+        /* XXX: raise an error ? */+        ret = 0;+    } else {+        ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),+                                JS_VALUE_GET_STRING(str2));+    }+    JS_FreeValue(ctx, str1);+    JS_FreeValue(ctx, str2);+    return ret;+}++static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);++static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,+                                 void *opaque)+{+    JSModuleDef *m = opaque;+    JSValue val, this_val = JS_MKPTR(JS_TAG_OBJECT, p);+    +    val = js_get_module_ns(ctx, m);+    if (JS_IsException(val))+        return -1;+    if (JS_DefinePropertyValue(ctx, this_val, atom, val,+                               JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)+        return -1;+    return 0;+}++static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)+{+    JSValue obj;+    JSObject *p;+    GetExportNamesState s_s, *s = &s_s;+    int i, ret;+    JSProperty *pr;++    obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);+    if (JS_IsException(obj))+        return obj;+    p = JS_VALUE_GET_OBJ(obj);++    memset(s, 0, sizeof(*s));+    ret = get_exported_names(ctx, s, m, FALSE);+    js_free(ctx, s->modules);+    if (ret)+        goto fail;++    /* Resolve the exported names. The ambiguous exports are removed */+    for(i = 0; i < s->exported_names_count; i++) {+        ExportedNameEntry *en = &s->exported_names[i];+        JSResolveResultEnum res;+        JSExportEntry *res_me;+        JSModuleDef *res_m;++        if (en->u.me) {+            res_me = en->u.me; /* fast case: no resolution needed */+            res_m = m;+            res = JS_RESOLVE_RES_FOUND;+        } else {+            res = js_resolve_export(ctx, &res_m, &res_me, m,+                                    en->export_name);+        }+        if (res != JS_RESOLVE_RES_FOUND) {+            if (res != JS_RESOLVE_RES_AMBIGUOUS) {+                js_resolve_export_throw_error(ctx, res, m, en->export_name);+                goto fail;+            }+            en->export_type = EXPORTED_NAME_AMBIGUOUS;+        } else {+            if (res_me->local_name == JS_ATOM__star_) {+                en->export_type = EXPORTED_NAME_NS;+                en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;+            } else {+                en->export_type = EXPORTED_NAME_NORMAL;+                if (res_me->u.local.var_ref) {+                    en->u.var_ref = res_me->u.local.var_ref;+                } else {+                    JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);+                    p1 = JS_VALUE_GET_OBJ(res_m->func_obj);+                    en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];+                }+            }+        }+    }++    /* sort the exported names */+    rqsort(s->exported_names, s->exported_names_count,+           sizeof(s->exported_names[0]), exported_names_cmp, ctx);++    for(i = 0; i < s->exported_names_count; i++) {+        ExportedNameEntry *en = &s->exported_names[i];+        switch(en->export_type) {+        case EXPORTED_NAME_NORMAL:+            {+                JSVarRef *var_ref = en->u.var_ref;+                pr = add_property(ctx, p, en->export_name,+                                  JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |+                                  JS_PROP_VARREF);+                if (!pr)+                    goto fail;+                var_ref->header.ref_count++;+                pr->u.var_ref = var_ref;+            }+            break;+        case EXPORTED_NAME_NS:+            /* the exported namespace must be created on demand */+            if (JS_DefineAutoInitProperty(ctx, obj,+                                          en->export_name,+                                          JS_AUTOINIT_ID_MODULE_NS,+                                          en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)+                goto fail;+            break;+        default:+            break;+        }+    }++    js_free(ctx, s->exported_names);++    JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,+                           JS_AtomToString(ctx, JS_ATOM_Module),+                           0);++    p->extensible = FALSE;+    return obj;+ fail:+    js_free(ctx, s->exported_names);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)+{+    if (JS_IsUndefined(m->module_ns)) {+        JSValue val;+        val = js_build_module_ns(ctx, m);+        if (JS_IsException(val))+            return JS_EXCEPTION;+        m->module_ns = val;+    }+    return JS_DupValue(ctx, m->module_ns);+}++/* Load all the required modules for module 'm' */+static int js_resolve_module(JSContext *ctx, JSModuleDef *m)+{+    int i;+    JSModuleDef *m1;++    if (m->resolved)+        return 0;+#ifdef DUMP_MODULE_RESOLVE+    {+        char buf1[ATOM_GET_STR_BUF_SIZE];+        printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));+    }+#endif+    m->resolved = TRUE;+    /* resolve each requested module */+    for(i = 0; i < m->req_module_entries_count; i++) {+        JSReqModuleEntry *rme = &m->req_module_entries[i];+        m1 = js_host_resolve_imported_module(ctx, m->module_name,+                                             rme->module_name);+        if (!m1)+            return -1;+        rme->module = m1;+        /* already done in js_host_resolve_imported_module() except if+           the module was loaded with JS_EvalBinary() */+        if (js_resolve_module(ctx, m1) < 0)+            return -1;+    }+    return 0;+}++static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)+{+    JSVarRef *var_ref;+    var_ref = js_malloc(ctx, sizeof(JSVarRef));+    if (!var_ref)+        return NULL;+    var_ref->header.ref_count = 1;+    if (is_lexical)+        var_ref->value = JS_UNINITIALIZED;+    else+        var_ref->value = JS_UNDEFINED;+    var_ref->pvalue = &var_ref->value;+    var_ref->is_detached = TRUE;+    add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);+    return var_ref;+}++/* Create the <eval> function associated with the module */+static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)+{+    JSFunctionBytecode *b;+    int i;+    JSVarRef **var_refs;+    JSValue func_obj, bfunc;+    JSObject *p;++    bfunc = m->func_obj;+    func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+                                      JS_CLASS_BYTECODE_FUNCTION);++    if (JS_IsException(func_obj))+        return -1;+    b = JS_VALUE_GET_PTR(bfunc);++    p = JS_VALUE_GET_OBJ(func_obj);+    p->u.func.function_bytecode = b;+    b->header.ref_count++;+    p->u.func.home_object = NULL;+    p->u.func.var_refs = NULL;+    if (b->closure_var_count) {+        var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);+        if (!var_refs)+            goto fail;+        p->u.func.var_refs = var_refs;++        /* create the global variables. The other variables are+           imported from other modules */+        for(i = 0; i < b->closure_var_count; i++) {+            JSClosureVar *cv = &b->closure_var[i];+            JSVarRef *var_ref;+            if (cv->is_local) {+                var_ref = js_create_module_var(ctx, cv->is_lexical);+                if (!var_ref)+                    goto fail;+#ifdef DUMP_MODULE_RESOLVE+                printf("local %d: %p\n", i, var_ref);+#endif+                var_refs[i] = var_ref;+            }+        }+    }+    m->func_obj = func_obj;+    JS_FreeValue(ctx, bfunc);+    return 0;+ fail:+    JS_FreeValue(ctx, func_obj);+    return -1;+}++/* must be done before js_instantiate_module() because of cyclic references */+static int js_create_module_function(JSContext *ctx, JSModuleDef *m)+{+    BOOL is_c_module;+    int i;+    JSVarRef *var_ref;+    +    if (m->func_created)+        return 0;++    is_c_module = (m->init_func != NULL);++    if (is_c_module) {+        /* initialize the exported variables */+        for(i = 0; i < m->export_entries_count; i++) {+            JSExportEntry *me = &m->export_entries[i];+            if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+                var_ref = js_create_module_var(ctx, FALSE);+                if (!var_ref)+                    return -1;+                me->u.local.var_ref = var_ref;+            }+        }+    } else {+        if (js_create_module_bytecode_function(ctx, m))+            return -1;+    }+    m->func_created = TRUE;++    /* do it on the dependencies */+    +    for(i = 0; i < m->req_module_entries_count; i++) {+        JSReqModuleEntry *rme = &m->req_module_entries[i];+        if (js_create_module_function(ctx, rme->module) < 0)+            return -1;+    }++    return 0;+}    ++    +/* Prepare a module to be executed by resolving all the imported+   variables. */+static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)+{+    int i;+    JSImportEntry *mi;+    JSModuleDef *m1;+    JSVarRef **var_refs, *var_ref;+    JSObject *p;+    BOOL is_c_module;++    if (m->instantiated)+        return 0;+    m->instantiated = TRUE;++#ifdef DUMP_MODULE_RESOLVE+    {+        char buf1[ATOM_GET_STR_BUF_SIZE];+        printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));+    }+#endif++    for(i = 0; i < m->req_module_entries_count; i++) {+        JSReqModuleEntry *rme = &m->req_module_entries[i];+        if (js_instantiate_module(ctx, rme->module) < 0)+            goto fail;+    }++#ifdef DUMP_MODULE_RESOLVE+    {+        char buf1[ATOM_GET_STR_BUF_SIZE];+        printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));+    }+#endif+    /* check the indirect exports */+    for(i = 0; i < m->export_entries_count; i++) {+        JSExportEntry *me = &m->export_entries[i];+        if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&+            me->local_name != JS_ATOM__star_) {+            JSResolveResultEnum ret;+            JSExportEntry *res_me;+            JSModuleDef *res_m, *m1;+            m1 = m->req_module_entries[me->u.req_module_idx].module;+            ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);+            if (ret != JS_RESOLVE_RES_FOUND) {+                js_resolve_export_throw_error(ctx, ret, m, me->export_name);+                goto fail;+            }+        }+    }++#ifdef DUMP_MODULE_RESOLVE+    {+        printf("exported bindings:\n");+        for(i = 0; i < m->export_entries_count; i++) {+            JSExportEntry *me = &m->export_entries[i];+            printf(" name="); print_atom(ctx, me->export_name);+            printf(" local="); print_atom(ctx, me->local_name);+            printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);+        }+    }+#endif++    is_c_module = (m->init_func != NULL);++    if (!is_c_module) {+        p = JS_VALUE_GET_OBJ(m->func_obj);+        var_refs = p->u.func.var_refs;++        for(i = 0; i < m->import_entries_count; i++) {+            mi = &m->import_entries[i];+#ifdef DUMP_MODULE_RESOLVE+            printf("import var_idx=%d name=", mi->var_idx);+            print_atom(ctx, mi->import_name);+            printf(": ");+#endif+            m1 = m->req_module_entries[mi->req_module_idx].module;+            if (mi->import_name == JS_ATOM__star_) {+                JSValue val;+                /* name space import */+                val = js_get_module_ns(ctx, m1);+                if (JS_IsException(val))+                    goto fail;+                set_value(ctx, &var_refs[mi->var_idx]->value, val);+#ifdef DUMP_MODULE_RESOLVE+                printf("namespace\n");+#endif+            } else {+                JSResolveResultEnum ret;+                JSExportEntry *res_me;+                JSModuleDef *res_m;+                JSObject *p1;++                ret = js_resolve_export(ctx, &res_m,+                                        &res_me, m1, mi->import_name);+                if (ret != JS_RESOLVE_RES_FOUND) {+                    js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);+                    goto fail;+                }+                if (res_me->local_name == JS_ATOM__star_) {+                    JSValue val;+                    JSModuleDef *m2;+                    /* name space import from */+                    m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;+                    val = js_get_module_ns(ctx, m2);+                    if (JS_IsException(val))+                        goto fail;+                    var_ref = js_create_module_var(ctx, TRUE);+                    if (!var_ref) {+                        JS_FreeValue(ctx, val);+                        goto fail;+                    }+                    set_value(ctx, &var_ref->value, val);+                    var_refs[mi->var_idx] = var_ref;+#ifdef DUMP_MODULE_RESOLVE+                    printf("namespace from\n");+#endif+                } else {+                    var_ref = res_me->u.local.var_ref;+                    if (!var_ref) {+                        p1 = JS_VALUE_GET_OBJ(res_m->func_obj);+                        var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];+                    }+                    var_ref->header.ref_count++;+                    var_refs[mi->var_idx] = var_ref;+#ifdef DUMP_MODULE_RESOLVE+                    printf("local export (var_ref=%p)\n", var_ref);+#endif+                }+            }+        }++        /* keep the exported variables in the module export entries (they+           are used when the eval function is deleted and cannot be+           initialized before in case imports are exported) */+        for(i = 0; i < m->export_entries_count; i++) {+            JSExportEntry *me = &m->export_entries[i];+            if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+                var_ref = var_refs[me->u.local.var_idx];+                var_ref->header.ref_count++;+                me->u.local.var_ref = var_ref;+            }+        }+    }++#ifdef DUMP_MODULE_RESOLVE+    printf("done instantiate\n");+#endif+    return 0;+ fail:+    return -1;+}++/* warning: the returned atom is not allocated */+static JSAtom js_get_script_or_module_name(JSContext *ctx)+{+    JSStackFrame *sf;+    JSFunctionBytecode *b;+    JSObject *p;+    /* XXX: currently we just use the filename of the englobing+       function. It does not work for eval(). Need to add a+       ScriptOrModule info in JSFunctionBytecode */+    sf = ctx->rt->current_stack_frame;+    assert(sf != NULL);+    assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);+    p = JS_VALUE_GET_OBJ(sf->cur_func);+    assert(js_class_has_bytecode(p->class_id));+    b = p->u.func.function_bytecode;+    if (!b->has_debug)+        return JS_ATOM_NULL;+    return b->debug.filename;+}++JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)+{+    return JS_DupAtom(ctx, m->module_name);+}++JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)+{+    JSValue obj;+    /* allocate meta_obj only if requested to save memory */+    obj = m->meta_obj;+    if (JS_IsUndefined(obj)) {+        obj = JS_NewObjectProto(ctx, JS_NULL);+        if (JS_IsException(obj))+            return JS_EXCEPTION;+        m->meta_obj = obj;+    }+    return JS_DupValue(ctx, obj);+}++static JSValue js_import_meta(JSContext *ctx)+{+    JSAtom filename;+    JSModuleDef *m;+    +    filename = js_get_script_or_module_name(ctx);+    if (filename == JS_ATOM_NULL)+        goto fail;++    /* XXX: inefficient, need to add a module or script pointer in+       JSFunctionBytecode */+    m = js_find_loaded_module(ctx, filename);+    if (!m) {+    fail:+        JS_ThrowTypeError(ctx, "import.meta not supported in this context");+        return JS_EXCEPTION;+    }+    return JS_GetImportMeta(ctx, m);+}++static JSValue js_dynamic_import_job(JSContext *ctx,+                                     int argc, JSValueConst *argv)+{+    JSValueConst *resolving_funcs = argv;+    JSValueConst basename_val = argv[2];+    JSValueConst specifier = argv[3];+    JSModuleDef *m;+    JSAtom basename = JS_ATOM_NULL, filename;+    JSValue specifierString, ret, func_obj, err, ns;++    if (!JS_IsString(basename_val)) {+        JS_ThrowTypeError(ctx, "no function filename for import()");+        goto exception;+    }+    basename = JS_ValueToAtom(ctx, basename_val);+    if (basename == JS_ATOM_NULL)+        goto exception;++    specifierString = JS_ToString(ctx, specifier);+    if (JS_IsException(specifierString))+        goto exception;+    filename = JS_ValueToAtom(ctx, specifierString);+    JS_FreeValue(ctx, specifierString);+    if (filename == JS_ATOM_NULL)+        goto exception;+                     +    m = js_host_resolve_imported_module(ctx, basename, filename);+    JS_FreeAtom(ctx, filename);+    if (!m) {+        goto exception;+    }+    +    if (js_resolve_module(ctx, m) < 0) {+        js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);+        goto exception;+    }++    /* Evaluate the module code */+    func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+    ret = JS_EvalFunction(ctx, func_obj);+    if (JS_IsException(ret))+        goto exception;+    JS_FreeValue(ctx, ret);++    /* return the module namespace */+    ns = js_get_module_ns(ctx, m);+    if (JS_IsException(ret))+        goto exception;++    ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,+                   1, (JSValueConst *)&ns);+    JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */+    JS_FreeValue(ctx, ns);+    JS_FreeAtom(ctx, basename);+    return JS_UNDEFINED;+ exception:++    err = JS_GetException(ctx);+    ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,+                   1, (JSValueConst *)&err);+    JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */+    JS_FreeValue(ctx, err);+    JS_FreeAtom(ctx, basename);+    return JS_UNDEFINED;+}++static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)+{+    JSAtom basename;+    JSValue promise, resolving_funcs[2], basename_val;+    JSValueConst args[4];++    basename = js_get_script_or_module_name(ctx);+    if (basename == JS_ATOM_NULL)+        basename_val = JS_NULL;+    else+        basename_val = JS_AtomToValue(ctx, basename);+    if (JS_IsException(basename_val))+        return basename_val;+    +    promise = JS_NewPromiseCapability(ctx, resolving_funcs);+    if (JS_IsException(promise)) {+        JS_FreeValue(ctx, basename_val);+        return promise;+    }++    args[0] = resolving_funcs[0];+    args[1] = resolving_funcs[1];+    args[2] = basename_val;+    args[3] = specifier;+    +    JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);++    JS_FreeValue(ctx, basename_val);+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    return promise;+}++/* Run the <eval> function of the module and of all its requested+   modules. */+static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)+{+    JSModuleDef *m1;+    int i;+    JSValue ret_val;++    if (m->eval_mark)+        return JS_UNDEFINED; /* avoid cycles */++    if (m->evaluated) {+        /* if the module was already evaluated, rethrow the exception+           it raised */+        if (m->eval_has_exception) {+            return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));+        } else {+            return JS_UNDEFINED;+        }+    }++    m->eval_mark = TRUE;++    for(i = 0; i < m->req_module_entries_count; i++) {+        JSReqModuleEntry *rme = &m->req_module_entries[i];+        m1 = rme->module;+        if (!m1->eval_mark) {+            ret_val = js_evaluate_module(ctx, m1);+            if (JS_IsException(ret_val)) {+                m->eval_mark = FALSE;+                return ret_val;+            }+            JS_FreeValue(ctx, ret_val);+        }+    }++    if (m->init_func) {+        /* C module init */+        if (m->init_func(ctx, m) < 0)+            ret_val = JS_EXCEPTION;+        else+            ret_val = JS_UNDEFINED;+    } else {+        ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);+        m->func_obj = JS_UNDEFINED;+    }+    if (JS_IsException(ret_val)) {+        /* save the thrown exception value */+        m->eval_has_exception = TRUE;+        m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);+    }+    m->eval_mark = FALSE;+    m->evaluated = TRUE;+    return ret_val;+}++static __exception JSAtom js_parse_from_clause(JSParseState *s)+{+    JSAtom module_name;+    if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {+        js_parse_error(s, "from clause expected");+        return JS_ATOM_NULL;+    }+    if (next_token(s))+        return JS_ATOM_NULL;+    if (s->token.val != TOK_STRING) {+        js_parse_error(s, "string expected");+        return JS_ATOM_NULL;+    }+    module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);+    if (module_name == JS_ATOM_NULL)+        return JS_ATOM_NULL;+    if (next_token(s)) {+        JS_FreeAtom(s->ctx, module_name);+        return JS_ATOM_NULL;+    }+    return module_name;+}++static __exception int js_parse_export(JSParseState *s)+{+    JSContext *ctx = s->ctx;+    JSModuleDef *m = s->cur_func->module;+    JSAtom local_name, export_name;+    int first_export, idx, i, tok;+    JSAtom module_name;+    JSExportEntry *me;++    if (next_token(s))+        return -1;++    tok = s->token.val;+    if (tok == TOK_CLASS) {+        return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);+    } else if (tok == TOK_FUNCTION ||+               (token_is_pseudo_keyword(s, JS_ATOM_async) &&+                peek_token(s, TRUE) == TOK_FUNCTION)) {+        return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,+                                       JS_FUNC_NORMAL, JS_ATOM_NULL,+                                       s->token.ptr, s->token.line_num,+                                       JS_PARSE_EXPORT_NAMED, NULL);+    }++    if (next_token(s))+        return -1;++    switch(tok) {+    case '{':+        first_export = m->export_entries_count;+        while (s->token.val != '}') {+            if (!token_is_ident(s->token.val)) {+                js_parse_error(s, "identifier expected");+                return -1;+            }+            local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+            export_name = JS_ATOM_NULL;+            if (next_token(s))+                goto fail;+            if (token_is_pseudo_keyword(s, JS_ATOM_as)) {+                if (next_token(s))+                    goto fail;+                if (!token_is_ident(s->token.val)) {+                    js_parse_error(s, "identifier expected");+                    goto fail;+                }+                export_name = JS_DupAtom(ctx, s->token.u.ident.atom);+                if (next_token(s)) {+                fail:+                    JS_FreeAtom(ctx, local_name);+                fail1:+                    JS_FreeAtom(ctx, export_name);+                    return -1;+                }+            } else {+                export_name = JS_DupAtom(ctx, local_name);+            }+            me = add_export_entry(s, m, local_name, export_name,+                                  JS_EXPORT_TYPE_LOCAL);+            JS_FreeAtom(ctx, local_name);+            JS_FreeAtom(ctx, export_name);+            if (!me)+                return -1;+            if (s->token.val != ',')+                break;+            if (next_token(s))+                return -1;+        }+        if (js_parse_expect(s, '}'))+            return -1;+        if (token_is_pseudo_keyword(s, JS_ATOM_from)) {+            module_name = js_parse_from_clause(s);+            if (module_name == JS_ATOM_NULL)+                return -1;+            idx = add_req_module_entry(ctx, m, module_name);+            JS_FreeAtom(ctx, module_name);+            if (idx < 0)+                return -1;+            for(i = first_export; i < m->export_entries_count; i++) {+                me = &m->export_entries[i];+                me->export_type = JS_EXPORT_TYPE_INDIRECT;+                me->u.req_module_idx = idx;+            }+        }+        break;+    case '*':+        if (token_is_pseudo_keyword(s, JS_ATOM_as)) {+            /* export ns from */+            if (next_token(s))+                return -1;+            if (!token_is_ident(s->token.val)) {+                js_parse_error(s, "identifier expected");+                return -1;+            }+            export_name = JS_DupAtom(ctx, s->token.u.ident.atom);+            if (next_token(s))+                goto fail1;+            module_name = js_parse_from_clause(s);+            if (module_name == JS_ATOM_NULL)+                goto fail1;+            idx = add_req_module_entry(ctx, m, module_name);+            JS_FreeAtom(ctx, module_name);+            if (idx < 0)+                goto fail1;+            me = add_export_entry(s, m, JS_ATOM__star_, export_name,+                                  JS_EXPORT_TYPE_INDIRECT);+            JS_FreeAtom(ctx, export_name);+            if (!me)+                return -1;+            me->u.req_module_idx = idx;+        } else {+            module_name = js_parse_from_clause(s);+            if (module_name == JS_ATOM_NULL)+                return -1;+            idx = add_req_module_entry(ctx, m, module_name);+            JS_FreeAtom(ctx, module_name);+            if (idx < 0)+                return -1;+            if (add_star_export_entry(ctx, m, idx) < 0)+                return -1;+        }+        break;+    case TOK_DEFAULT:+        if (s->token.val == TOK_CLASS) {+            return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);+        } else if (s->token.val == TOK_FUNCTION ||+                   (token_is_pseudo_keyword(s, JS_ATOM_async) &&+                    peek_token(s, TRUE) == TOK_FUNCTION)) {+            return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,+                                           JS_FUNC_NORMAL, JS_ATOM_NULL,+                                           s->token.ptr, s->token.line_num,+                                           JS_PARSE_EXPORT_DEFAULT, NULL);+        } else {+            if (js_parse_assign_expr(s, TRUE))+                return -1;+        }+        /* set the name of anonymous functions */+        set_object_name(s, JS_ATOM_default);++        /* store the value in the _default_ global variable and export+           it */+        local_name = JS_ATOM__default_;+        if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)+            return -1;+        emit_op(s, OP_scope_put_var_init);+        emit_atom(s, local_name);+        emit_u16(s, 0);++        if (!add_export_entry(s, m, local_name, JS_ATOM_default,+                              JS_EXPORT_TYPE_LOCAL))+            return -1;+        break;+    case TOK_VAR:+    case TOK_LET:+    case TOK_CONST:+        return js_parse_var(s, TRUE, tok, TRUE);+    default:+        return js_parse_error(s, "invalid export syntax");+    }+    return js_parse_expect_semi(s);+}++static int add_closure_var(JSContext *ctx, JSFunctionDef *s,+                           BOOL is_local, BOOL is_arg,+                           int var_idx, JSAtom var_name,+                           BOOL is_const, BOOL is_lexical,+                           JSVarKindEnum var_kind);++static int add_import(JSParseState *s, JSModuleDef *m,+                      JSAtom local_name, JSAtom import_name)+{+    JSContext *ctx = s->ctx;+    int i, var_idx;+    JSImportEntry *mi;+    BOOL is_local;++    if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)+        return js_parse_error(s, "invalid import binding");++    if (local_name != JS_ATOM_default) {+        for (i = 0; i < s->cur_func->closure_var_count; i++) {+            if (s->cur_func->closure_var[i].var_name == local_name)+                return js_parse_error(s, "duplicate import binding");+        }+    }++    is_local = (import_name == JS_ATOM__star_);+    var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,+                              m->import_entries_count,+                              local_name, TRUE, TRUE, FALSE);+    if (var_idx < 0)+        return -1;+    if (js_resize_array(ctx, (void **)&m->import_entries,+                        sizeof(JSImportEntry),+                        &m->import_entries_size,+                        m->import_entries_count + 1))+        return -1;+    mi = &m->import_entries[m->import_entries_count++];+    mi->import_name = JS_DupAtom(ctx, import_name);+    mi->var_idx = var_idx;+    return 0;+}++static __exception int js_parse_import(JSParseState *s)+{+    JSContext *ctx = s->ctx;+    JSModuleDef *m = s->cur_func->module;+    JSAtom local_name, import_name, module_name;+    int first_import, i, idx;++    if (next_token(s))+        return -1;++    first_import = m->import_entries_count;+    if (s->token.val == TOK_STRING) {+        module_name = JS_ValueToAtom(ctx, s->token.u.str.str);+        if (module_name == JS_ATOM_NULL)+            return -1;+        if (next_token(s)) {+            JS_FreeAtom(ctx, module_name);+            return -1;+        }+    } else {+        if (s->token.val == TOK_IDENT) {+            if (s->token.u.ident.is_reserved) {+                return js_parse_error_reserved_identifier(s);+            }+            /* "default" import */+            local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+            import_name = JS_ATOM_default;+            if (next_token(s))+                goto fail;+            if (add_import(s, m, local_name, import_name))+                goto fail;+            JS_FreeAtom(ctx, local_name);++            if (s->token.val != ',')+                goto end_import_clause;+            if (next_token(s))+                return -1;+        }++        if (s->token.val == '*') {+            /* name space import */+            if (next_token(s))+                return -1;+            if (!token_is_pseudo_keyword(s, JS_ATOM_as))+                return js_parse_error(s, "expecting 'as'");+            if (next_token(s))+                return -1;+            if (!token_is_ident(s->token.val)) {+                js_parse_error(s, "identifier expected");+                return -1;+            }+            local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+            import_name = JS_ATOM__star_;+            if (next_token(s))+                goto fail;+            if (add_import(s, m, local_name, import_name))+                goto fail;+            JS_FreeAtom(ctx, local_name);+        } else if (s->token.val == '{') {+            if (next_token(s))+                return -1;++            while (s->token.val != '}') {+                if (!token_is_ident(s->token.val)) {+                    js_parse_error(s, "identifier expected");+                    return -1;+                }+                import_name = JS_DupAtom(ctx, s->token.u.ident.atom);+                local_name = JS_ATOM_NULL;+                if (next_token(s))+                    goto fail;+                if (token_is_pseudo_keyword(s, JS_ATOM_as)) {+                    if (next_token(s))+                        goto fail;+                    if (!token_is_ident(s->token.val)) {+                        js_parse_error(s, "identifier expected");+                        goto fail;+                    }+                    local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+                    if (next_token(s)) {+                    fail:+                        JS_FreeAtom(ctx, local_name);+                        JS_FreeAtom(ctx, import_name);+                        return -1;+                    }+                } else {+                    local_name = JS_DupAtom(ctx, import_name);+                }+                if (add_import(s, m, local_name, import_name))+                    goto fail;+                JS_FreeAtom(ctx, local_name);+                JS_FreeAtom(ctx, import_name);+                if (s->token.val != ',')+                    break;+                if (next_token(s))+                    return -1;+            }+            if (js_parse_expect(s, '}'))+                return -1;+        }+    end_import_clause:+        module_name = js_parse_from_clause(s);+        if (module_name == JS_ATOM_NULL)+            return -1;+    }+    idx = add_req_module_entry(ctx, m, module_name);+    JS_FreeAtom(ctx, module_name);+    if (idx < 0)+        return -1;+    for(i = first_import; i < m->import_entries_count; i++)+        m->import_entries[i].req_module_idx = idx;++    return js_parse_expect_semi(s);+}++static __exception int js_parse_source_element(JSParseState *s)+{+    JSFunctionDef *fd = s->cur_func;+    int tok;+    +    if (s->token.val == TOK_FUNCTION ||+        (token_is_pseudo_keyword(s, JS_ATOM_async) &&+         peek_token(s, TRUE) == TOK_FUNCTION)) {+        if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,+                                   JS_FUNC_NORMAL, JS_ATOM_NULL,+                                   s->token.ptr, s->token.line_num))+            return -1;+    } else if (s->token.val == TOK_EXPORT && fd->module) {+        if (js_parse_export(s))+            return -1;+    } else if (s->token.val == TOK_IMPORT && fd->module &&+               ((tok = peek_token(s, FALSE)) != '(' && tok != '.'))  {+        /* the peek_token is needed to avoid confusion with ImportCall+           (dynamic import) or import.meta */+        if (js_parse_import(s))+            return -1;+    } else {+        if (js_parse_statement_or_decl(s, DECL_MASK_ALL))+            return -1;+    }+    return 0;+}++static JSFunctionDef *js_new_function_def(JSContext *ctx,+                                          JSFunctionDef *parent,+                                          BOOL is_eval,+                                          BOOL is_func_expr,+                                          const char *filename, int line_num)+{+    JSFunctionDef *fd;++    fd = js_mallocz(ctx, sizeof(*fd));+    if (!fd)+        return NULL;++    fd->ctx = ctx;+    init_list_head(&fd->child_list);++    /* insert in parent list */+    fd->parent = parent;+    fd->parent_cpool_idx = -1;+    if (parent) {+        list_add_tail(&fd->link, &parent->child_list);+        fd->js_mode = parent->js_mode;+        fd->parent_scope_level = parent->scope_level;+    }++    fd->is_eval = is_eval;+    fd->is_func_expr = is_func_expr;+    js_dbuf_init(ctx, &fd->byte_code);+    fd->last_opcode_pos = -1;+    fd->func_name = JS_ATOM_NULL;+    fd->var_object_idx = -1;+    fd->arguments_var_idx = -1;+    fd->func_var_idx = -1;+    fd->eval_ret_idx = -1;+    fd->this_var_idx = -1;+    fd->new_target_var_idx = -1;+    fd->this_active_func_var_idx = -1;+    fd->home_object_var_idx = -1;++    /* XXX: should distinguish arg, var and var object and body scopes */+    fd->scope_level = 0;  /* 0: var/arg scope, 1:body scope */+    fd->scope_first = -1;+    fd->scopes = fd->def_scope_array;+    fd->scope_size = countof(fd->def_scope_array);+    fd->scope_count = 1;+    fd->scopes[0].first = -1;+    fd->scopes[0].parent = -1;++    fd->filename = JS_NewAtom(ctx, filename);+    fd->line_num = line_num;++    js_dbuf_init(ctx, &fd->pc2line);+    //fd->pc2line_last_line_num = line_num;+    //fd->pc2line_last_pc = 0;+    fd->last_opcode_line_num = line_num;++    return fd;+}++static void free_bytecode_atoms(JSRuntime *rt,+                                const uint8_t *bc_buf, int bc_len,+                                BOOL use_short_opcodes)+{+    int pos, len, op;+    JSAtom atom;+    const JSOpCode *oi;+    +    pos = 0;+    while (pos < bc_len) {+        op = bc_buf[pos];+        if (use_short_opcodes)+            oi = &short_opcode_info(op);+        else+            oi = &opcode_info[op];+            +        len = oi->size;+        switch(oi->fmt) {+        case OP_FMT_atom:+        case OP_FMT_atom_u8:+        case OP_FMT_atom_u16:+        case OP_FMT_atom_label_u8:+        case OP_FMT_atom_label_u16:+            atom = get_u32(bc_buf + pos + 1);+            JS_FreeAtomRT(rt, atom);+            break;+        default:+            break;+        }+        pos += len;+    }+}++static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)+{+    int i;+    struct list_head *el, *el1;++    /* free the child functions */+    list_for_each_safe(el, el1, &fd->child_list) {+        JSFunctionDef *fd1;+        fd1 = list_entry(el, JSFunctionDef, link);+        js_free_function_def(ctx, fd1);+    }++    free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,+                        fd->use_short_opcodes);+    dbuf_free(&fd->byte_code);+    js_free(ctx, fd->jump_slots);+    js_free(ctx, fd->label_slots);+    js_free(ctx, fd->line_number_slots);++    for(i = 0; i < fd->cpool_count; i++) {+        JS_FreeValue(ctx, fd->cpool[i]);+    }+    js_free(ctx, fd->cpool);++    JS_FreeAtom(ctx, fd->func_name);++    for(i = 0; i < fd->var_count; i++) {+        JS_FreeAtom(ctx, fd->vars[i].var_name);+    }+    js_free(ctx, fd->vars);+    for(i = 0; i < fd->arg_count; i++) {+        JS_FreeAtom(ctx, fd->args[i].var_name);+    }+    js_free(ctx, fd->args);++    for(i = 0; i < fd->hoisted_def_count; i++) {+        JS_FreeAtom(ctx, fd->hoisted_def[i].var_name);+    }+    js_free(ctx, fd->hoisted_def);++    for(i = 0; i < fd->closure_var_count; i++) {+        JSClosureVar *cv = &fd->closure_var[i];+        JS_FreeAtom(ctx, cv->var_name);+    }+    js_free(ctx, fd->closure_var);++    if (fd->scopes != fd->def_scope_array)+        js_free(ctx, fd->scopes);++    JS_FreeAtom(ctx, fd->filename);+    dbuf_free(&fd->pc2line);++    js_free(ctx, fd->source);++    if (fd->parent) {+        /* remove in parent list */+        list_del(&fd->link);+    }+    js_free(ctx, fd);+}++#ifdef DUMP_BYTECODE+static const char *skip_lines(const char *p, int n) {+    while (n-- > 0 && *p) {+        while (*p && *p++ != '\n')+            continue;+    }+    return p;+}++static void print_lines(const char *source, int line, int line1) {+    const char *s = source;+    const char *p = skip_lines(s, line);+    if (*p) {+        while (line++ < line1) {+            p = skip_lines(s = p, 1);+            printf(";; %.*s", (int)(p - s), s);+            if (!*p) {+                if (p[-1] != '\n')+                    printf("\n");+                break;+            }+        }+    }+}++static void dump_byte_code(JSContext *ctx, int pass,+                           const uint8_t *tab, int len,+                           const JSVarDef *args, int arg_count,+                           const JSVarDef *vars, int var_count,+                           const JSClosureVar *closure_var, int closure_var_count,+                           const JSValue *cpool, uint32_t cpool_count,+                           const char *source, int line_num,+                           const LabelSlot *label_slots, JSFunctionBytecode *b)+{+    const JSOpCode *oi;+    int pos, pos_next, op, size, idx, addr, line, line1, in_source;+    uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));+    BOOL use_short_opcodes = (b != NULL);++    /* scan for jump targets */+    for (pos = 0; pos < len; pos = pos_next) {+        op = tab[pos];+        if (use_short_opcodes)+            oi = &short_opcode_info(op);+        else+            oi = &opcode_info[op];+        pos_next = pos + oi->size;+        if (op < OP_COUNT) {+            switch (oi->fmt) {+#if SHORT_OPCODES+            case OP_FMT_label8:+                pos++;+                addr = (int8_t)tab[pos];+                goto has_addr;+            case OP_FMT_label16:+                pos++;+                addr = (int16_t)get_u16(tab + pos);+                goto has_addr;+#endif+            case OP_FMT_atom_label_u8:+            case OP_FMT_atom_label_u16:+                pos += 4;+                /* fall thru */+            case OP_FMT_label:+            case OP_FMT_label_u16:+                pos++;+                addr = get_u32(tab + pos);+                goto has_addr;+            has_addr:+                if (pass == 1)+                    addr = label_slots[addr].pos;+                if (pass == 2)+                    addr = label_slots[addr].pos2;+                if (pass == 3)+                    addr += pos;+                if (addr >= 0 && addr < len)+                    bits[addr] |= 1;+                break;+            }+        }+    }+    in_source = 0;+    if (source) {+        /* Always print first line: needed if single line */+        print_lines(source, 0, 1);+        in_source = 1;+    }+    line1 = line = 1;+    pos = 0;+    while (pos < len) {+        op = tab[pos];+        if (source) {+            if (b) {+                line1 = find_line_num(ctx, b, pos) - line_num + 1;+            } else if (op == OP_line_num) {+                line1 = get_u32(tab + pos + 1) - line_num + 1;+            }+            if (line1 > line) {+                if (!in_source)+                    printf("\n");+                in_source = 1;+                print_lines(source, line, line1);+                line = line1;+                //bits[pos] |= 2;+            }+        }+        if (in_source)+            printf("\n");+        in_source = 0;+        if (op >= OP_COUNT) {+            printf("invalid opcode (0x%02x)\n", op);+            pos++;+            continue;+        }+        if (use_short_opcodes)+            oi = &short_opcode_info(op);+        else+            oi = &opcode_info[op];+        size = oi->size;+        if (pos + size > len) {+            printf("truncated opcode (0x%02x)\n", op);+            break;+        }+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)+        {+            int i, x, x0;+            x = x0 = printf("%5d ", pos);+            for (i = 0; i < size; i++) {+                if (i == 6) {+                    printf("\n%*s", x = x0, "");+                }+                x += printf(" %02X", tab[pos + i]);+            }+            printf("%*s", x0 + 20 - x, "");+        }+#endif+        if (bits[pos]) {+            printf("%5d:  ", pos);+        } else {+            printf("        ");+        }+        printf("%s", oi->name);+        pos++;+        switch(oi->fmt) {+        case OP_FMT_none_int:+            printf(" %d", op - OP_push_0);+            break;+        case OP_FMT_npopx:+            printf(" %d", op - OP_call0);+            break;+        case OP_FMT_u8:+            printf(" %u", get_u8(tab + pos));+            break;+        case OP_FMT_i8:+            printf(" %d", get_i8(tab + pos));+            break;+        case OP_FMT_u16:+        case OP_FMT_npop:+            printf(" %u", get_u16(tab + pos));+            break;+        case OP_FMT_npop_u16:+            printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));+            break;+        case OP_FMT_i16:+            printf(" %d", get_i16(tab + pos));+            break;+        case OP_FMT_i32:+            printf(" %d", get_i32(tab + pos));+            break;+        case OP_FMT_u32:+            printf(" %u", get_u32(tab + pos));+            break;+#if SHORT_OPCODES+        case OP_FMT_label8:+            addr = get_i8(tab + pos);+            goto has_addr1;+        case OP_FMT_label16:+            addr = get_i16(tab + pos);+            goto has_addr1;+#endif+        case OP_FMT_label:+            addr = get_u32(tab + pos);+            goto has_addr1;+        has_addr1:+            if (pass == 1)+                printf(" %u:%u", addr, label_slots[addr].pos);+            if (pass == 2)+                printf(" %u:%u", addr, label_slots[addr].pos2);+            if (pass == 3)+                printf(" %u", addr + pos);+            break;+        case OP_FMT_label_u16:+            addr = get_u32(tab + pos);+            if (pass == 1)+                printf(" %u:%u", addr, label_slots[addr].pos);+            if (pass == 2)+                printf(" %u:%u", addr, label_slots[addr].pos2);+            if (pass == 3)+                printf(" %u", addr + pos);+            printf(",%u", get_u16(tab + pos + 4));+            break;+#if SHORT_OPCODES+        case OP_FMT_const8:+            idx = get_u8(tab + pos);+            goto has_pool_idx;+#endif+        case OP_FMT_const:+            idx = get_u32(tab + pos);+            goto has_pool_idx;+        has_pool_idx:+            printf(" %u: ", idx);+            if (idx < cpool_count) {+                JS_DumpValue(ctx, cpool[idx]);+            }+            break;+        case OP_FMT_atom:+            printf(" ");+            print_atom(ctx, get_u32(tab + pos));+            break;+        case OP_FMT_atom_u8:+            printf(" ");+            print_atom(ctx, get_u32(tab + pos));+            printf(",%d", get_u8(tab + pos + 4));+            break;+        case OP_FMT_atom_u16:+            printf(" ");+            print_atom(ctx, get_u32(tab + pos));+            printf(",%d", get_u16(tab + pos + 4));+            break;+        case OP_FMT_atom_label_u8:+        case OP_FMT_atom_label_u16:+            printf(" ");+            print_atom(ctx, get_u32(tab + pos));+            addr = get_u32(tab + pos + 4);+            if (pass == 1)+                printf(",%u:%u", addr, label_slots[addr].pos);+            if (pass == 2)+                printf(",%u:%u", addr, label_slots[addr].pos2);+            if (pass == 3)+                printf(",%u", addr + pos + 4);+            if (oi->fmt == OP_FMT_atom_label_u8)+                printf(",%u", get_u8(tab + pos + 8));+            else+                printf(",%u", get_u16(tab + pos + 8));+            break;+        case OP_FMT_none_loc:+            idx = (op - OP_get_loc0) % 4;+            goto has_loc;+        case OP_FMT_loc8:+            idx = get_u8(tab + pos);+            goto has_loc;+        case OP_FMT_loc:+            idx = get_u16(tab + pos);+        has_loc:+            printf(" %d: ", idx);+            if (idx < var_count) {+                print_atom(ctx, vars[idx].var_name);+            }+            break;+        case OP_FMT_none_arg:+            idx = (op - OP_get_arg0) % 4;+            goto has_arg;+        case OP_FMT_arg:+            idx = get_u16(tab + pos);+        has_arg:+            printf(" %d: ", idx);+            if (idx < arg_count) {+                print_atom(ctx, args[idx].var_name);+            }+            break;+        case OP_FMT_none_var_ref:+            idx = (op - OP_get_var_ref0) % 4;+            goto has_var_ref;+        case OP_FMT_var_ref:+            idx = get_u16(tab + pos);+        has_var_ref:+            printf(" %d: ", idx);+            if (idx < closure_var_count) {+                print_atom(ctx, closure_var[idx].var_name);+            }+            break;+        default:+            break;+        }+        printf("\n");+        pos += oi->size - 1;+    }+    if (source) {+        if (!in_source)+            printf("\n");+        print_lines(source, line, INT32_MAX);+    }+    js_free(ctx, bits);+}++static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,+                                                 int line_num)+{+    const uint8_t *p_end, *p_next, *p;+    int pc, v;+    unsigned int op;++    if (len <= 0)+        return;++    printf("%5s %5s\n", "PC", "LINE");++    p = buf;+    p_end = buf + len;+    pc = 0;+    while (p < p_end) {+        op = *p++;+        if (op == 0) {+            v = unicode_from_utf8(p, p_end - p, &p_next);+            if (v < 0)+                goto fail;+            pc += v;+            p = p_next;+            v = unicode_from_utf8(p, p_end - p, &p_next);+            if (v < 0) {+            fail:+                printf("invalid pc2line encode pos=%d\n", (int)(p - buf));+                return;+            }+            if (!(v & 1)) {+                v = v >> 1;+            } else {+                v = -(v >> 1) - 1;+            }+            line_num += v;+            p = p_next;+        } else {+            op -= PC2LINE_OP_FIRST;+            pc += (op / PC2LINE_RANGE);+            line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;+        }+        printf("%5d %5d\n", pc, line_num);+    }+}++static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)+{+    int i;+    char atom_buf[ATOM_GET_STR_BUF_SIZE];+    const char *str;++    if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {+        str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);+        printf("%s:%d: ", str, b->debug.line_num);+    }++    str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);+    printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);+    if (b->js_mode) {+        printf("  mode:");+        if (b->js_mode & JS_MODE_STRICT)+            printf(" strict");+#ifdef CONFIG_BIGNUM+        if (b->js_mode & JS_MODE_MATH)+            printf(" math");+#endif+        printf("\n");+    }+    if (b->arg_count && b->vardefs) {+        printf("  args:");+        for(i = 0; i < b->arg_count; i++) {+            printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),+                                        b->vardefs[i].var_name));+        }+        printf("\n");+    }+    if (b->var_count && b->vardefs) {+        printf("  locals:\n");+        for(i = 0; i < b->var_count; i++) {+            JSVarDef *vd = &b->vardefs[b->arg_count + i];+            printf("%5d: %s %s", i,+                   vd->var_kind == JS_VAR_CATCH ? "catch" :+                   (vd->var_kind == JS_VAR_FUNCTION_DECL ||+                    vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :+                   vd->is_const ? "const" :+                   vd->is_lexical ? "let" : "var",+                   JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));+            if (vd->scope_level)+                printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);+            printf("\n");+        }+    }+    if (b->closure_var_count) {+        printf("  closure vars:\n");+        for(i = 0; i < b->closure_var_count; i++) {+            JSClosureVar *cv = &b->closure_var[i];+            printf("%5d: %s %s:%s%d %s\n", i,+                   JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),+                   cv->is_local ? "local" : "parent",+                   cv->is_arg ? "arg" : "loc", cv->var_idx,+                   cv->is_const ? "const" :+                   cv->is_lexical ? "let" : "var");+        }+    }+    printf("  stack_size: %d\n", b->stack_size);+    printf("  opcodes:\n");+    dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,+                   b->vardefs, b->arg_count,+                   b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,+                   b->closure_var, b->closure_var_count,+                   b->cpool, b->cpool_count,+                   b->has_debug ? b->debug.source : NULL,+                   b->has_debug ? b->debug.line_num : -1, NULL, b);+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)+    if (b->has_debug)+        dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);+#endif+    printf("\n");+}+#endif++static int add_closure_var(JSContext *ctx, JSFunctionDef *s,+                           BOOL is_local, BOOL is_arg,+                           int var_idx, JSAtom var_name,+                           BOOL is_const, BOOL is_lexical,+                           JSVarKindEnum var_kind)+{+    JSClosureVar *cv;++    /* the closure variable indexes are currently stored on 16 bits */+    if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {+        JS_ThrowInternalError(ctx, "too many closure variables");+        return -1;+    }++    if (js_resize_array(ctx, (void **)&s->closure_var,+                        sizeof(s->closure_var[0]),+                        &s->closure_var_size, s->closure_var_count + 1))+        return -1;+    cv = &s->closure_var[s->closure_var_count++];+    cv->is_local = is_local;+    cv->is_arg = is_arg;+    cv->is_const = is_const;+    cv->is_lexical = is_lexical;+    cv->var_kind = var_kind;+    cv->var_idx = var_idx;+    cv->var_name = JS_DupAtom(ctx, var_name);+    return s->closure_var_count - 1;+}++static int find_closure_var(JSContext *ctx, JSFunctionDef *s,+                            JSAtom var_name)+{+    int i;+    for(i = 0; i < s->closure_var_count; i++) {+        JSClosureVar *cv = &s->closure_var[i];+        if (cv->var_name == var_name)+            return i;+    }+    return -1;+}++/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a+   local variable (is_local = TRUE) or a closure (is_local = FALSE) in+   'fd' */+static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,+                            JSFunctionDef *fd, BOOL is_local,+                            BOOL is_arg, int var_idx, JSAtom var_name,+                            BOOL is_const, BOOL is_lexical,+                            JSVarKindEnum var_kind)+{+    int i;++    if (fd != s->parent) {+        var_idx = get_closure_var2(ctx, s->parent, fd, is_local,+                                   is_arg, var_idx, var_name,+                                   is_const, is_lexical, var_kind);+        if (var_idx < 0)+            return -1;+        is_local = FALSE;+    }+    for(i = 0; i < s->closure_var_count; i++) {+        JSClosureVar *cv = &s->closure_var[i];+        if (cv->var_idx == var_idx && cv->is_arg == is_arg &&+            cv->is_local == is_local)+            return i;+    }+    return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,+                           is_const, is_lexical, var_kind);+}++static int get_closure_var(JSContext *ctx, JSFunctionDef *s,+                           JSFunctionDef *fd, BOOL is_arg,+                           int var_idx, JSAtom var_name,+                           BOOL is_const, BOOL is_lexical,+                           JSVarKindEnum var_kind)+{+    return get_closure_var2(ctx, s, fd, TRUE, is_arg,+                            var_idx, var_name, is_const, is_lexical,+                            var_kind);+}++static int get_with_scope_opcode(int op)+{+    if (op == OP_scope_get_var_undef)+        return OP_with_get_var;+    else+        return OP_with_get_var + (op - OP_scope_get_var);+}++static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)+{+    int opcode = bc_buf[pos];+    return (bc_buf[pos + 1] == OP_put_ref_value &&+            (opcode == OP_insert3 ||+             opcode == OP_perm4 ||+             //opcode == OP_nop ||+             opcode == OP_rot3l));+}++static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)+{+    int opcode = bc_buf[pos];+    return (bc_buf[pos + 1] == OP_put_ref_value &&+            (opcode == OP_insert3 ||+             opcode == OP_perm4 ||+             //opcode == OP_nop ||+             opcode == OP_rot3l));+}++static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,+                                   DynBuf *bc, uint8_t *bc_buf,+                                   LabelSlot *ls, int pos_next,+                                   int get_op, int var_idx)+{+    int label_pos, end_pos, pos;++    /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`+       but only if expr does not modify `a`.+       should scan the code between pos_next and label_pos+       for operations that can potentially change `a`:+       OP_scope_make_ref(a), function calls, jumps and gosub.+     */+    /* replace the reference get/put with normal variable+       accesses */+    if (bc_buf[pos_next] == OP_get_ref_value) {+        dbuf_putc(bc, get_op);+        dbuf_put_u16(bc, var_idx);+        pos_next++;+    }+    /* remove the OP_label to make room for replacement */+    /* label should have a refcount of 0 anyway */+    /* XXX: should avoid this patch by inserting nops in phase 1 */+    label_pos = ls->pos;+    pos = label_pos - 5;+    assert(bc_buf[pos] == OP_label);+    /* label points to an instruction pair:+       - insert3 / put_ref_value+       - perm4 / put_ref_value+       - rot3l / put_ref_value+       - nop / put_ref_value+     */+    end_pos = label_pos + 2;+    if (bc_buf[label_pos] == OP_insert3)+        bc_buf[pos++] = OP_dup;+    bc_buf[pos] = get_op + 1;+    put_u16(bc_buf + pos + 1, var_idx);+    pos += 3;+    /* pad with OP_nop */+    while (pos < end_pos)+        bc_buf[pos++] = OP_nop;+    return pos_next;+}++static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,+                                          DynBuf *bc, uint8_t *bc_buf,+                                          LabelSlot *ls, int pos_next,+                                          JSAtom var_name)+{+    int label_pos, end_pos, pos, op;+    BOOL is_strict;+    is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);++    /* replace the reference get/put with normal variable+       accesses */+    if (is_strict) {+        /* need to check if the variable exists before evaluating the right+           expression */+        /* XXX: need an extra OP_true if destructuring an array */+        dbuf_putc(bc, OP_check_var);+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+    } else {+        /* XXX: need 2 extra OP_true if destructuring an array */+    }+    if (bc_buf[pos_next] == OP_get_ref_value) {+        dbuf_putc(bc, OP_get_var);+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        pos_next++;+    }+    /* remove the OP_label to make room for replacement */+    /* label should have a refcount of 0 anyway */+    /* XXX: should have emitted several OP_nop to avoid this kludge */+    label_pos = ls->pos;+    pos = label_pos - 5;+    assert(bc_buf[pos] == OP_label);+    end_pos = label_pos + 2;+    op = bc_buf[label_pos];+    if (is_strict) {+        switch(op) {+        case OP_insert3:+            op = OP_insert2;+            break;+        case OP_perm4:+            op = OP_perm3;+            break;+        case OP_rot3l:+            op = OP_swap;+            break;+        default:+            abort();+        }+        bc_buf[pos++] = op;+    } else {+        if (op == OP_insert3)+            bc_buf[pos++] = OP_dup;+    }+    if (is_strict) {+        bc_buf[pos] = OP_put_var_strict;+        /* XXX: need 1 extra OP_drop if destructuring an array */+    } else {+        bc_buf[pos] = OP_put_var;+        /* XXX: need 2 extra OP_drop if destructuring an array */+    }+    put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));+    pos += 5;+    /* pad with OP_nop */+    while (pos < end_pos)+        bc_buf[pos++] = OP_nop;+    return pos_next;+}++static int add_var_this(JSContext *ctx, JSFunctionDef *fd)+{+    int idx;+    idx = add_var(ctx, fd, JS_ATOM_this);+    if (idx >= 0 && fd->is_derived_class_constructor) {+        JSVarDef *vd = &fd->vars[idx];+        /* XXX: should have is_this flag or var type */+        vd->is_lexical = 1; /* used to trigger 'uninitialized' checks+                               in a derived class constructor */+    }+    return idx;+}++static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,+                               JSAtom var_name)+{+    int var_idx;++    if (!s->has_this_binding)+        return -1;+    switch(var_name) {+    case JS_ATOM_home_object:+        /* 'home_object' pseudo variable */+        var_idx = s->home_object_var_idx = add_var(ctx, s, var_name);+        break;+    case JS_ATOM_this_active_func:+        /* 'this.active_func' pseudo variable */+        var_idx = s->this_active_func_var_idx = add_var(ctx, s, var_name);+        break;+    case JS_ATOM_new_target:+        /* 'new.target' pseudo variable */+        var_idx = s->new_target_var_idx = add_var(ctx, s, var_name);+        break;+    case JS_ATOM_this:+        /* 'this' pseudo variable */+        var_idx = s->this_var_idx = add_var_this(ctx, s);+        break;+    default:+        var_idx = -1;+        break;+    }+    return var_idx;+}++/* return the position of the next opcode */+static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,+                             JSAtom var_name, int scope_level, int op,+                             DynBuf *bc, uint8_t *bc_buf,+                             LabelSlot *ls, int pos_next, int arg_valid)+{+    int idx, var_idx, is_put;+    int label_done;+    BOOL is_func_var = FALSE;+    JSFunctionDef *fd;+    JSVarDef *vd;+    BOOL is_pseudo_var;++    label_done = -1;++    /* XXX: could be simpler to use a specific function to+       resolve the pseudo variables */+    is_pseudo_var = (var_name == JS_ATOM_home_object ||+                     var_name == JS_ATOM_this_active_func ||+                     var_name == JS_ATOM_new_target ||+                     var_name == JS_ATOM_this);++    /* resolve local scoped variables */+    var_idx = -1;+    for (idx = s->scopes[scope_level].first; idx >= 0;) {+        vd = &s->vars[idx];+        if (vd->var_name == var_name) {+            if (op == OP_scope_put_var || op == OP_scope_make_ref) {+                if (vd->is_const) {+                    dbuf_putc(bc, OP_throw_var);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                    dbuf_putc(bc, JS_THROW_VAR_RO);+                    goto done;+                }+                is_func_var = vd->is_func_var;+            }+            var_idx = idx;+            break;+        } else+        if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {+            dbuf_putc(bc, OP_get_loc);+            dbuf_put_u16(bc, idx);+            dbuf_putc(bc, get_with_scope_opcode(op));+            dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+            label_done = new_label_fd(s, label_done);+            dbuf_put_u32(bc, label_done);+            dbuf_putc(bc, 1);+            update_label(s, label_done, 1);+            s->jump_size++;+        }+        idx = vd->scope_next;+    }+    if (var_idx < 0) {+        /* XXX: scoping issues:+           should not resolve vars from the function body during argument parse,+           `arguments` and function-name should not be hidden by later vars.+         */+        var_idx = find_var(ctx, s, var_name);+        if (var_idx >= 0) {+            if (scope_level == 0+            &&  (var_idx & ARGUMENT_VAR_OFFSET)+            &&  (var_idx - ARGUMENT_VAR_OFFSET) >= arg_valid) {+                /* referring to an uninitialized argument */+                dbuf_putc(bc, OP_throw_var);+                dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                dbuf_putc(bc, JS_THROW_VAR_UNINITIALIZED);+            }+            if (!(var_idx & ARGUMENT_VAR_OFFSET))+                is_func_var = s->vars[var_idx].is_func_var;+        }++        if (var_idx < 0 && is_pseudo_var)+            var_idx = resolve_pseudo_var(ctx, s, var_name);++        if (var_idx < 0 && var_name == JS_ATOM_arguments &&+            s->has_arguments_binding) {+            /* 'arguments' pseudo variable */+            var_idx = add_arguments_var(ctx, s, var_name);+        }+        if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {+            /* add a new variable with the function name */+            var_idx = add_func_var(ctx, s, var_name);+            is_func_var = TRUE;+        }+    }+    if (var_idx >= 0) {+        /* OP_scope_put_var_init is only used to initialize a+           lexical variable, so it is never used in a with or var object. It+           can be used with a closure (module global variable case). */+        switch (op) {+        case OP_scope_make_ref:+            if (is_func_var) {+                /* Create a dummy object reference for the func_var */+                dbuf_putc(bc, OP_object);+                dbuf_putc(bc, OP_get_loc);+                dbuf_put_u16(bc, var_idx);+                dbuf_putc(bc, OP_define_field);+                dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                dbuf_putc(bc, OP_push_atom_value);+                dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+            } else+            if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {+                int get_op;+                if (var_idx & ARGUMENT_VAR_OFFSET) {+                    get_op = OP_get_arg;+                    var_idx -= ARGUMENT_VAR_OFFSET;+                } else {+                    if (s->vars[var_idx].is_lexical)+                        get_op = OP_get_loc_check;+                    else+                        get_op = OP_get_loc;+                }+                pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,+                                                   pos_next, get_op, var_idx);+            } else {+                /* Create a dummy object with a named slot that is+                   a reference to the local variable */+                if (var_idx & ARGUMENT_VAR_OFFSET) {+                    dbuf_putc(bc, OP_make_arg_ref);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                    dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);+                } else {+                    dbuf_putc(bc, OP_make_loc_ref);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                    dbuf_put_u16(bc, var_idx);+                }+            }+            break;+        case OP_scope_get_ref:+            dbuf_putc(bc, OP_undefined);+            /* fall thru */+        case OP_scope_get_var_undef:+        case OP_scope_get_var:+        case OP_scope_put_var:+        case OP_scope_put_var_init:+            is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);+            if (var_idx & ARGUMENT_VAR_OFFSET) {+                dbuf_putc(bc, OP_get_arg + is_put);+                dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);+                /* XXX: should test if argument reference needs TDZ check */+            } else {+                if (is_put) {+                    if (s->vars[var_idx].is_lexical) {+                        if (op == OP_scope_put_var_init) {+                            /* 'this' can only be initialized once */+                            if (var_name == JS_ATOM_this)+                                dbuf_putc(bc, OP_put_loc_check_init);+                            else+                                dbuf_putc(bc, OP_put_loc);+                        } else {+                            dbuf_putc(bc, OP_put_loc_check);+                        }+                    } else {+                        dbuf_putc(bc, OP_put_loc);+                    }+                } else {+                    if (s->vars[var_idx].is_lexical) {+                        dbuf_putc(bc, OP_get_loc_check);+                    } else {+                        dbuf_putc(bc, OP_get_loc);+                    }+                }+                dbuf_put_u16(bc, var_idx);+            }+            break;+        case OP_scope_delete_var:+            dbuf_putc(bc, OP_push_false);+            break;+        }+        goto done;+    }+    /* check eval object */+    if (s->var_object_idx >= 0 && !is_pseudo_var) {+        dbuf_putc(bc, OP_get_loc);+        dbuf_put_u16(bc, s->var_object_idx);+        dbuf_putc(bc, get_with_scope_opcode(op));+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        label_done = new_label_fd(s, label_done);+        dbuf_put_u32(bc, label_done);+        dbuf_putc(bc, 0);+        update_label(s, label_done, 1);+        s->jump_size++;+    }+    /* check parent scopes */+    for (fd = s; fd->parent;) {+        scope_level = fd->parent_scope_level;+        fd = fd->parent;+        if (scope_level == 0) {+            /* function is defined as part of the argument parsing: hide vars+               from the function body.+               XXX: variables created from argument destructuring might need+               to be visible, should refine this method.+             */+            var_idx = find_arg(ctx, fd, var_name);+            goto check_idx;+        }+        for (idx = fd->scopes[scope_level].first; idx >= 0;) {+            vd = &fd->vars[idx];+            if (vd->var_name == var_name) {+                if (op == OP_scope_put_var || op == OP_scope_make_ref) {+                    if (vd->is_const) {+                        dbuf_putc(bc, OP_throw_var);+                        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                        dbuf_putc(bc, JS_THROW_VAR_RO);+                        goto done;+                    }+                    is_func_var = vd->is_func_var;+                }+                var_idx = idx;+                break;+            } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {+                vd->is_captured = 1;+                idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);+                if (idx >= 0) {+                    dbuf_putc(bc, OP_get_var_ref);+                    dbuf_put_u16(bc, idx);+                    dbuf_putc(bc, get_with_scope_opcode(op));+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                    label_done = new_label_fd(s, label_done);+                    dbuf_put_u32(bc, label_done);+                    dbuf_putc(bc, 1);+                    update_label(s, label_done, 1);+                    s->jump_size++;+                }+            }+            idx = vd->scope_next;+        }+        if (var_idx >= 0)+            break;++        var_idx = find_var(ctx, fd, var_name);+    check_idx:+        if (var_idx >= 0) {+            if (!(var_idx & ARGUMENT_VAR_OFFSET))+                is_func_var = fd->vars[var_idx].is_func_var;+            break;+        }+        if (is_pseudo_var) {+            var_idx = resolve_pseudo_var(ctx, fd, var_name);+            if (var_idx >= 0)+                break;+        }+        if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {+            var_idx = add_arguments_var(ctx, fd, var_name);+            break;+        }+        if (fd->is_func_expr && fd->func_name == var_name) {+            /* add a new variable with the function name */+            var_idx = add_func_var(ctx, fd, var_name);+            is_func_var = TRUE;+            break;+        }++        /* check eval object */+        if (fd->var_object_idx >= 0 && !is_pseudo_var) {+            fd->vars[fd->var_object_idx].is_captured = 1;+            idx = get_closure_var(ctx, s, fd, FALSE,+                                  fd->var_object_idx, JS_ATOM__var_,+                                  FALSE, FALSE, JS_VAR_NORMAL);+            dbuf_putc(bc, OP_get_var_ref);+            dbuf_put_u16(bc, idx);+            dbuf_putc(bc, get_with_scope_opcode(op));+            dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+            label_done = new_label_fd(s, label_done);+            dbuf_put_u32(bc, label_done);+            dbuf_putc(bc, 0);+            update_label(s, label_done, 1);+            s->jump_size++;+        }++        if (fd->is_eval)+            break; /* it it necessarily the top level function */+    }++    /* check direct eval scope (in the closure of the eval function+       which is necessarily at the top level) */+    if (!fd)+        fd = s;+    if (var_idx < 0 && fd->is_eval) {+        int idx1;+        for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {+            JSClosureVar *cv = &fd->closure_var[idx1];+            if (var_name == cv->var_name) {+                if (fd != s) {+                    idx = get_closure_var2(ctx, s, fd,+                                           FALSE,+                                           cv->is_arg, idx1,+                                           cv->var_name, cv->is_const,+                                           cv->is_lexical, cv->var_kind);+                } else {+                    idx = idx1;+                }+                goto has_idx;+            } else if ((cv->var_name == JS_ATOM__var_ ||+                        cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {+                int is_with = (cv->var_name == JS_ATOM__with_);+                if (fd != s) {+                    idx = get_closure_var2(ctx, s, fd,+                                           FALSE,+                                           cv->is_arg, idx1,+                                           cv->var_name, FALSE, FALSE,+                                           JS_VAR_NORMAL);+                } else {+                    idx = idx1;+                }+                dbuf_putc(bc, OP_get_var_ref);+                dbuf_put_u16(bc, idx);+                dbuf_putc(bc, get_with_scope_opcode(op));+                dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                label_done = new_label_fd(s, label_done);+                dbuf_put_u32(bc, label_done);+                dbuf_putc(bc, is_with);+                update_label(s, label_done, 1);+                s->jump_size++;+            }+        }+    }++    if (var_idx >= 0) {+        /* find the corresponding closure variable */+        if (var_idx & ARGUMENT_VAR_OFFSET) {+            fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;+            idx = get_closure_var(ctx, s, fd,+                                  TRUE, var_idx - ARGUMENT_VAR_OFFSET,+                                  var_name, FALSE, FALSE, JS_VAR_NORMAL);+        } else {+            fd->vars[var_idx].is_captured = 1;+            idx = get_closure_var(ctx, s, fd,+                                  FALSE, var_idx,+                                  var_name,+                                  fd->vars[var_idx].is_const,+                                  fd->vars[var_idx].is_lexical,+                                  fd->vars[var_idx].var_kind);+        }+        if (idx >= 0) {+        has_idx:+            if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&+                s->closure_var[idx].is_const) {+                dbuf_putc(bc, OP_throw_var);+                dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                dbuf_putc(bc, JS_THROW_VAR_RO);+                goto done;+            }+            switch (op) {+            case OP_scope_make_ref:+                if (is_func_var) {+                    /* Create a dummy object reference for the func_var */+                    dbuf_putc(bc, OP_object);+                    dbuf_putc(bc, OP_get_var_ref);+                    dbuf_put_u16(bc, idx);+                    dbuf_putc(bc, OP_define_field);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                    dbuf_putc(bc, OP_push_atom_value);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                } else+                if (label_done == -1 &&+                    can_opt_put_ref_value(bc_buf, ls->pos)) {+                    int get_op;+                    if (s->closure_var[idx].is_lexical)+                        get_op = OP_get_var_ref_check;+                    else+                        get_op = OP_get_var_ref;+                    pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,+                                                       pos_next,+                                                       get_op, idx);+                } else {+                    /* Create a dummy object with a named slot that is+                       a reference to the closure variable */+                    dbuf_putc(bc, OP_make_var_ref_ref);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+                    dbuf_put_u16(bc, idx);+                }+                break;+            case OP_scope_get_ref:+                /* XXX: should create a dummy object with a named slot that is+                   a reference to the closure variable */+                dbuf_putc(bc, OP_undefined);+                /* fall thru */+            case OP_scope_get_var_undef:+            case OP_scope_get_var:+            case OP_scope_put_var:+            case OP_scope_put_var_init:+                is_put = (op == OP_scope_put_var ||+                          op == OP_scope_put_var_init);+                if (is_put) {+                    if (s->closure_var[idx].is_lexical) {+                        if (op == OP_scope_put_var_init) {+                            /* 'this' can only be initialized once */+                            if (var_name == JS_ATOM_this)+                                dbuf_putc(bc, OP_put_var_ref_check_init);+                            else+                                dbuf_putc(bc, OP_put_var_ref);+                        } else {+                            dbuf_putc(bc, OP_put_var_ref_check);+                        }+                    } else {+                        dbuf_putc(bc, OP_put_var_ref);+                    }+                } else {+                    if (s->closure_var[idx].is_lexical) {+                        dbuf_putc(bc, OP_get_var_ref_check);+                    } else {+                        dbuf_putc(bc, OP_get_var_ref);+                    }+                }+                dbuf_put_u16(bc, idx);+                break;+            case OP_scope_delete_var:+                dbuf_putc(bc, OP_push_false);+                break;+            }+            goto done;+        }+    }++    /* global variable access */++    switch (op) {+    case OP_scope_make_ref:+        if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {+            pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,+                                                      pos_next, var_name);+        } else {+            dbuf_putc(bc, OP_make_var_ref);+            dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        }+        break;+    case OP_scope_get_ref:+        /* XXX: should create a dummy object with a named slot that is+           a reference to the global variable */+        dbuf_putc(bc, OP_undefined);+        dbuf_putc(bc, OP_get_var);+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        break;+    case OP_scope_get_var_undef:+    case OP_scope_get_var:+    case OP_scope_put_var:+        dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        break;+    case OP_scope_put_var_init:+        dbuf_putc(bc, OP_put_var_init);+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        break;+    case OP_scope_delete_var:+        dbuf_putc(bc, OP_delete_var);+        dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+        break;+    }+done:+    if (label_done >= 0) {+        dbuf_putc(bc, OP_label);+        dbuf_put_u32(bc, label_done);+        s->label_slots[label_done].pos2 = bc->size;+    }+    return pos_next;+}++/* search in all scopes */+static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,+                                        JSAtom name, int scope_level)+{+    int idx;++    idx = fd->scopes[scope_level].first;+    while (idx >= 0) {+        if (fd->vars[idx].var_name == name)+            return idx;+        idx = fd->vars[idx].scope_next;+    }+    return -1;+}++static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)+{+    /* if the field is not initialized, the error is catched when+       accessing it */+    if (is_ref) +        dbuf_putc(bc, OP_get_var_ref);+    else+        dbuf_putc(bc, OP_get_loc);+    dbuf_put_u16(bc, idx);+}++static int resolve_scope_private_field1(JSContext *ctx,+                                        BOOL *pis_ref, int *pvar_kind,+                                        JSFunctionDef *s,+                                        JSAtom var_name, int scope_level)+{+    int idx, var_kind;+    JSFunctionDef *fd;+    BOOL is_ref;+    +    fd = s;+    is_ref = FALSE;+    for(;;) {+        idx = find_private_class_field_all(ctx, fd, var_name, scope_level);+        if (idx >= 0) {+            var_kind = fd->vars[idx].var_kind;+            if (is_ref) {+                idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,+                                      TRUE, TRUE, JS_VAR_NORMAL);+                if (idx < 0)+                    return -1;+            }+            break;+        }+        scope_level = fd->parent_scope_level;+        if (!fd->parent) {+            if (fd->is_eval) {+                /* closure of the eval function (top level) */+                for (idx = 0; idx < fd->closure_var_count; idx++) {+                    JSClosureVar *cv = &fd->closure_var[idx];+                    if (cv->var_name == var_name) {+                        var_kind = cv->var_kind;+                        is_ref = TRUE;+                        if (fd != s) {+                            idx = get_closure_var2(ctx, s, fd,+                                                   FALSE,+                                                   cv->is_arg, idx,+                                                   cv->var_name, cv->is_const,+                                                   cv->is_lexical,+                                                   cv->var_kind);+                            if (idx < 0)+                                return -1;+                        }+                        goto done;+                    }+                }+            }+            /* XXX: no line number info */+            JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",+                                    var_name);+            return -1;+        } else {+            fd = fd->parent;+        }+        is_ref = TRUE;+    }+ done:+    *pis_ref = is_ref;+    *pvar_kind = var_kind;+    return idx;+}++/* return 0 if OK or -1 if the private field could not be resolved */+static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,+                                       JSAtom var_name, int scope_level, int op,+                                       DynBuf *bc)+{+    int idx, var_kind;+    BOOL is_ref;++    idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,+                                       var_name, scope_level);+    if (idx < 0)+        return -1;+    assert(var_kind != JS_VAR_NORMAL);+    switch (op) {+    case OP_scope_get_private_field:+    case OP_scope_get_private_field2:+        switch(var_kind) {+        case JS_VAR_PRIVATE_FIELD:+            if (op == OP_scope_get_private_field2)+                dbuf_putc(bc, OP_dup);+            get_loc_or_ref(bc, is_ref, idx);+            dbuf_putc(bc, OP_get_private_field);+            break;+        case JS_VAR_PRIVATE_METHOD:+            get_loc_or_ref(bc, is_ref, idx);+            dbuf_putc(bc, OP_check_brand);+            if (op != OP_scope_get_private_field2)+                dbuf_putc(bc, OP_nip);+            break;+        case JS_VAR_PRIVATE_GETTER:+        case JS_VAR_PRIVATE_GETTER_SETTER:+            if (op == OP_scope_get_private_field2)+                dbuf_putc(bc, OP_dup);+            get_loc_or_ref(bc, is_ref, idx);+            dbuf_putc(bc, OP_check_brand);+            dbuf_putc(bc, OP_call_method);+            dbuf_put_u16(bc, 0);+            break;+        case JS_VAR_PRIVATE_SETTER:+            /* XXX: add clearer error message */+            dbuf_putc(bc, OP_throw_var);+            dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+            dbuf_putc(bc, JS_THROW_VAR_RO);+            break;+        default:+            abort();+        }+        break;+    case OP_scope_put_private_field:+        switch(var_kind) {+        case JS_VAR_PRIVATE_FIELD:+            get_loc_or_ref(bc, is_ref, idx);+            dbuf_putc(bc, OP_put_private_field);+            break;+        case JS_VAR_PRIVATE_METHOD:+        case JS_VAR_PRIVATE_GETTER:+            /* XXX: add clearer error message */+            dbuf_putc(bc, OP_throw_var);+            dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+            dbuf_putc(bc, JS_THROW_VAR_RO);+            break;+        case JS_VAR_PRIVATE_SETTER:+        case JS_VAR_PRIVATE_GETTER_SETTER:+            {+                JSAtom setter_name = get_private_setter_name(ctx, var_name);+                if (setter_name == JS_ATOM_NULL)+                    return -1;+                idx = resolve_scope_private_field1(ctx, &is_ref,+                                                   &var_kind, s,+                                                   setter_name, scope_level);+                JS_FreeAtom(ctx, setter_name);+                if (idx < 0)+                    return -1;+                assert(var_kind == JS_VAR_PRIVATE_SETTER);+                get_loc_or_ref(bc, is_ref, idx);+                dbuf_putc(bc, OP_swap);+                /* obj func value */+                dbuf_putc(bc, OP_rot3r);+                /* value obj func */+                dbuf_putc(bc, OP_check_brand);+                dbuf_putc(bc, OP_rot3l);+                /* obj func value */+                dbuf_putc(bc, OP_call_method);+                dbuf_put_u16(bc, 1);+            }+            break;+        default:+            abort();+        }+        break;+    default:+        abort();+    }+    return 0;+}++static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,+                                         int scope_level)+{+    int idx;+    JSVarDef *vd;++    for (idx = s->scopes[scope_level].first; idx >= 0;) {+        vd = &s->vars[idx];+        vd->is_captured = 1;+        idx = vd->scope_next;+    }+}++static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)+{+    JSFunctionDef *fd;+    JSVarDef *vd;+    int i, scope_level, scope_idx;+    BOOL has_arguments_binding, has_this_binding;++    /* in non strict mode, variables are created in the caller's+       environment object */+    if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {+        s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);+    }++    /* eval can potentially use 'arguments' so we must define it */+    has_this_binding = s->has_this_binding;+    if (has_this_binding) {+        if (s->this_var_idx < 0)+            s->this_var_idx = add_var_this(ctx, s);+        if (s->new_target_var_idx < 0)+            s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);+        if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)+            s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);+        if (s->has_home_object && s->home_object_var_idx < 0)+            s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);+    }+    has_arguments_binding = s->has_arguments_binding;+    if (has_arguments_binding)+        add_arguments_var(ctx, s, JS_ATOM_arguments);+    if (s->is_func_expr && s->func_name != JS_ATOM_NULL)+        add_func_var(ctx, s, s->func_name);++    /* eval can use all the variables of the enclosing functions, so+       they must be all put in the closure. The closure variables are+       ordered by scope. It works only because no closure are created+       before. */+    assert(s->is_eval || s->closure_var_count == 0);++    /* XXX: inefficient, but eval performance is less critical */+    fd = s;+    for(;;) {+        scope_level = fd->parent_scope_level;+        fd = fd->parent;+        if (!fd)+            break;+        scope_idx = fd->scopes[scope_level].first;+        /* add 'this' if it was not previously added */+        if (!has_this_binding && fd->has_this_binding) {+            if (fd->this_var_idx < 0)+                fd->this_var_idx = add_var_this(ctx, fd);+            if (fd->new_target_var_idx < 0)+                fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);+            if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)+                fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);+            if (fd->has_home_object && fd->home_object_var_idx < 0)+                fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);+            has_this_binding = TRUE;+        }+        /* add 'arguments' if it was not previously added */+        if (!has_arguments_binding && fd->has_arguments_binding) {+            add_arguments_var(ctx, fd, JS_ATOM_arguments);+            has_arguments_binding = TRUE;+        }+        /* add function name */+        if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)+            add_func_var(ctx, fd, fd->func_name);++        /* add lexical variables */+        while (scope_idx >= 0) {+            vd = &fd->vars[scope_idx];+            vd->is_captured = 1;+            get_closure_var(ctx, s, fd, FALSE, scope_idx,+                            vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);+            scope_idx = vd->scope_next;+        }+        /* add unscoped variables */+        for(i = 0; i < fd->arg_count; i++) {+            vd = &fd->args[i];+            if (vd->var_name != JS_ATOM_NULL) {+                get_closure_var(ctx, s, fd,+                                TRUE, i, vd->var_name, FALSE, FALSE,+                                JS_VAR_NORMAL);+            }+        }+        for(i = 0; i < fd->var_count; i++) {+            vd = &fd->vars[i];+            /* do not close top level last result */+            if (vd->scope_level == 0 &&+                vd->var_name != JS_ATOM__ret_ &&+                vd->var_name != JS_ATOM_NULL) {+                get_closure_var(ctx, s, fd,+                                FALSE, i, vd->var_name, FALSE, FALSE,+                                JS_VAR_NORMAL);+            }+        }+        if (fd->is_eval) {+            int idx;+            /* add direct eval variables (we are necessarily at the+               top level) */+            for (idx = 0; idx < fd->closure_var_count; idx++) {+                JSClosureVar *cv = &fd->closure_var[idx];+                get_closure_var2(ctx, s, fd,+                                 FALSE, cv->is_arg,+                                 idx, cv->var_name, cv->is_const,+                                 cv->is_lexical, cv->var_kind);+            }+        }+    }+}++/* for direct eval compilation: add references to the variables of the+   calling function */+static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,+                                             JSFunctionBytecode *b, int scope_idx)+{+    int i, count;+    JSVarDef *vd;++    count = b->arg_count + b->var_count + b->closure_var_count;+    s->closure_var = NULL;+    s->closure_var_count = 0;+    s->closure_var_size = count;+    if (count == 0)+        return 0;+    s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);+    if (!s->closure_var)+        return -1;+    /* Add lexical variables in scope at the point of evaluation */+    for (i = scope_idx; i >= 0;) {+        vd = &b->vardefs[b->arg_count + i];+        if (vd->scope_level > 0) {+            JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+            cv->is_local = TRUE;+            cv->is_arg = FALSE;+            cv->is_const = vd->is_const;+            cv->is_lexical = vd->is_lexical;+            cv->var_kind = vd->var_kind;+            cv->var_idx = i;+            cv->var_name = JS_DupAtom(ctx, vd->var_name);+        }+        i = vd->scope_next;+    }+    /* Add argument variables */+    for(i = 0; i < b->arg_count; i++) {+        JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+        vd = &b->vardefs[i];+        cv->is_local = TRUE;+        cv->is_arg = TRUE;+        cv->is_const = FALSE;+        cv->is_lexical = FALSE;+        cv->var_kind = JS_VAR_NORMAL;+        cv->var_idx = i;+        cv->var_name = JS_DupAtom(ctx, vd->var_name);+    }+    /* Add local non lexical variables */+    for(i = 0; i < b->var_count; i++) {+        vd = &b->vardefs[b->arg_count + i];+        if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {+            JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+            cv->is_local = TRUE;+            cv->is_arg = FALSE;+            cv->is_const = FALSE;+            cv->is_lexical = FALSE;+            cv->var_kind = JS_VAR_NORMAL;+            cv->var_idx = i;+            cv->var_name = JS_DupAtom(ctx, vd->var_name);+        }+    }+    for(i = 0; i < b->closure_var_count; i++) {+        JSClosureVar *cv0 = &b->closure_var[i];+        JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+        cv->is_local = FALSE;+        cv->is_arg = cv0->is_arg;+        cv->is_const = cv0->is_const;+        cv->is_lexical = cv0->is_lexical;+        cv->var_kind = cv0->var_kind;+        cv->var_idx = i;+        cv->var_name = JS_DupAtom(ctx, cv0->var_name);+    }+    return 0;+}++typedef struct CodeContext {+    const uint8_t *bc_buf; /* code buffer */+    int bc_len;   /* length of the code buffer */+    int pos;      /* position past the matched code pattern */+    int line_num; /* last visited OP_line_num parameter or -1 */+    int op;+    int idx;+    int label;+    int val;+    JSAtom atom;+} CodeContext;++#define M2(op1, op2)            ((op1) | ((op2) << 8))+#define M3(op1, op2, op3)       ((op1) | ((op2) << 8) | ((op3) << 16))+#define M4(op1, op2, op3, op4)  ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))++static BOOL code_match(CodeContext *s, int pos, ...)+{+    const uint8_t *tab = s->bc_buf;+    int op, len, op1, line_num, pos_next;+    va_list ap;+    BOOL ret = FALSE;++    line_num = -1;+    va_start(ap, pos);++    for(;;) {+        op1 = va_arg(ap, int);+        if (op1 == -1) {+            s->pos = pos;+            s->line_num = line_num;+            ret = TRUE;+            break;+        }+        for (;;) {+            if (pos >= s->bc_len)+                goto done;+            op = tab[pos];+            len = opcode_info[op].size;+            pos_next = pos + len;+            if (pos_next > s->bc_len)+                goto done;+            if (op == OP_line_num) {+                line_num = get_u32(tab + pos + 1);+                pos = pos_next;+            } else {+                break;+            }+        }+        if (op != op1) {+            if (op1 == (uint8_t)op1 || !op)+                break;+            if (op != (uint8_t)op1+            &&  op != (uint8_t)(op1 >> 8)+            &&  op != (uint8_t)(op1 >> 16)+            &&  op != (uint8_t)(op1 >> 24)) {+                break;+            }+            s->op = op;+        }++        pos++;+        switch(opcode_info[op].fmt) {+        case OP_FMT_loc8:+        case OP_FMT_u8:+            {+                int idx = tab[pos];+                int arg = va_arg(ap, int);+                if (arg == -1) {+                    s->idx = idx;+                } else {+                    if (arg != idx)+                        goto done;+                }+                break;+            }+        case OP_FMT_u16:+        case OP_FMT_npop:+        case OP_FMT_loc:+        case OP_FMT_arg:+        case OP_FMT_var_ref:+            {+                int idx = get_u16(tab + pos);+                int arg = va_arg(ap, int);+                if (arg == -1) {+                    s->idx = idx;+                } else {+                    if (arg != idx)+                        goto done;+                }+                break;+            }+        case OP_FMT_i32:+        case OP_FMT_u32:+        case OP_FMT_label:+        case OP_FMT_const:+            {+                s->label = get_u32(tab + pos);+                break;+            }+        case OP_FMT_label_u16:+            {+                s->label = get_u32(tab + pos);+                s->val = get_u16(tab + pos + 4);+                break;+            }+        case OP_FMT_atom:+            {+                s->atom = get_u32(tab + pos);+                break;+            }+        case OP_FMT_atom_u8:+            {+                s->atom = get_u32(tab + pos);+                s->val = get_u8(tab + pos + 4);+                break;+            }+        case OP_FMT_atom_u16:+            {+                s->atom = get_u32(tab + pos);+                s->val = get_u16(tab + pos + 4);+                break;+            }+        case OP_FMT_atom_label_u8:+            {+                s->atom = get_u32(tab + pos);+                s->label = get_u32(tab + pos + 4);+                s->val = get_u8(tab + pos + 8);+                break;+            }+        default:+            break;+        }+        pos = pos_next;+    }+ done:+    va_end(ap);+    return ret;+}++static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)+{+    int i, idx, var_idx;++    /* add the hoisted functions and variables */+    for(i = 0; i < s->hoisted_def_count; i++) {+        JSHoistedDef *hf = &s->hoisted_def[i];+        int has_closure = 0;+        BOOL force_init = hf->force_init;+        if (s->is_global_var && hf->var_name != JS_ATOM_NULL) {+            /* we are in an eval, so the closure contains all the+               enclosing variables */+            /* If the outer function has a variable environment,+               create a property for the variable there */+            for(idx = 0; idx < s->closure_var_count; idx++) {+                JSClosureVar *cv = &s->closure_var[idx];+                if (cv->var_name == hf->var_name) {+                    has_closure = 2;+                    force_init = FALSE;+                    break;+                }+                if (cv->var_name == JS_ATOM__var_) {+                    dbuf_putc(bc, OP_get_var_ref);+                    dbuf_put_u16(bc, idx);+                    has_closure = 1;+                    force_init = TRUE;+                    break;+                }+            }+            if (!has_closure) {+                int flags;++                flags = 0;+                if (s->eval_type != JS_EVAL_TYPE_GLOBAL)+                    flags |= JS_PROP_CONFIGURABLE;+                if (hf->cpool_idx >= 0 && !hf->is_lexical) {+                    /* global function definitions need a specific handling */+                    dbuf_putc(bc, OP_fclosure);+                    dbuf_put_u32(bc, hf->cpool_idx);++                    dbuf_putc(bc, OP_define_func);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+                    dbuf_putc(bc, flags);++                    goto done_hoisted_def;+                } else {+                    if (hf->is_lexical) {+                        flags |= DEFINE_GLOBAL_LEX_VAR;+                        if (!hf->is_const)+                            flags |= JS_PROP_WRITABLE;+                    }+                    dbuf_putc(bc, OP_define_var);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+                    dbuf_putc(bc, flags);+                }+            }+        }+        if (hf->cpool_idx >= 0 || force_init) {+            if (hf->cpool_idx >= 0) {+                dbuf_putc(bc, OP_fclosure);+                dbuf_put_u32(bc, hf->cpool_idx);+                if (hf->var_name == JS_ATOM__default_) {+                    /* set default export function name */+                    dbuf_putc(bc, OP_set_name);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));+                }+            } else {+                dbuf_putc(bc, OP_undefined);+            }+            if (s->is_global_var) {+                if (has_closure == 2) {+                    dbuf_putc(bc, OP_put_var_ref);+                    dbuf_put_u16(bc, idx);+                } else if (has_closure == 1) {+                    dbuf_putc(bc, OP_define_field);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+                    dbuf_putc(bc, OP_drop);+                } else {+                    /* XXX: Check if variable is writable and enumerable */+                    dbuf_putc(bc, OP_put_var);+                    dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+                }+            } else {+                var_idx = hf->var_idx;+                if (var_idx & ARGUMENT_VAR_OFFSET) {+                    dbuf_putc(bc, OP_put_arg);+                    dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);+                } else {+                    dbuf_putc(bc, OP_put_loc);+                    dbuf_put_u16(bc, var_idx);+                }+            }+        }+    done_hoisted_def:+        JS_FreeAtom(ctx, hf->var_name);+    }+    js_free(ctx, s->hoisted_def);+    s->hoisted_def = NULL;+    s->hoisted_def_count = 0;+    s->hoisted_def_size = 0;+}++static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,+                          int pos, int *linep)+{+    int op, len, label;++    for (; pos < bc_len; pos += len) {+        op = bc_buf[pos];+        len = opcode_info[op].size;+        if (op == OP_line_num) {+            *linep = get_u32(bc_buf + pos + 1);+        } else+        if (op == OP_label) {+            label = get_u32(bc_buf + pos + 1);+            if (update_label(s, label, 0) > 0)+                break;+#if 0+            if (s->label_slots[label].first_reloc) {+                printf("line %d: unreferenced label %d:%d has relocations\n",+                       *linep, label, s->label_slots[label].pos2);+            }+#endif+            assert(s->label_slots[label].first_reloc == NULL);+        } else {+            /* XXX: output a warning for unreachable code? */+            JSAtom atom;+            switch(opcode_info[op].fmt) {+            case OP_FMT_label:+            case OP_FMT_label_u16:+                label = get_u32(bc_buf + pos + 1);+                update_label(s, label, -1);+                break;+            case OP_FMT_atom_label_u8:+            case OP_FMT_atom_label_u16:+                label = get_u32(bc_buf + pos + 5);+                update_label(s, label, -1);+                /* fall thru */+            case OP_FMT_atom:+            case OP_FMT_atom_u8:+            case OP_FMT_atom_u16:+                atom = get_u32(bc_buf + pos + 1);+                JS_FreeAtom(s->ctx, atom);+                break;+            default:+                break;+            }+        }+    }+    return pos;+}++static int get_label_pos(JSFunctionDef *s, int label)+{+    int i, pos;+    for (i = 0; i < 20; i++) {+        pos = s->label_slots[label].pos;+        for (;;) {+            switch (s->byte_code.buf[pos]) {+            case OP_line_num:+            case OP_label:+                pos += 5;+                continue;+            case OP_goto:+                label = get_u32(s->byte_code.buf + pos + 1);+                break;+            default:+                return pos;+            }+            break;+        }+    }+    return pos;+}++/* convert global variable accesses to local variables or closure+   variables when necessary */+static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)+{+    int pos, pos_next, bc_len, op, len, i, idx, arg_valid, line_num;+    uint8_t *bc_buf;+    JSAtom var_name;+    DynBuf bc_out;+    CodeContext cc;+    int scope;++    cc.bc_buf = bc_buf = s->byte_code.buf;+    cc.bc_len = bc_len = s->byte_code.size;+    js_dbuf_init(ctx, &bc_out);++    /* first pass for runtime checks (must be done before the+       variables are created) */+    if (s->is_global_var) {+        for(i = 0; i < s->hoisted_def_count; i++) {+            JSHoistedDef *hf = &s->hoisted_def[i];+            int flags;++            if (hf->var_name != JS_ATOM_NULL) {+                /* check if global variable (XXX: simplify) */+                for(idx = 0; idx < s->closure_var_count; idx++) {+                    JSClosureVar *cv = &s->closure_var[idx];+                    if (cv->var_name == hf->var_name) {+                        if (s->eval_type == JS_EVAL_TYPE_DIRECT &&+                            cv->is_lexical) {+                            /* Check if a lexical variable is+                               redefined as 'var'. XXX: Could abort+                               compilation here, but for consistency+                               with the other checks, we delay the+                               error generation. */+                            dbuf_putc(&bc_out, OP_throw_var);+                            dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));+                            dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);+                        }+                        goto next;+                    }+                    if (cv->var_name == JS_ATOM__var_)+                        goto next;+                }++                dbuf_putc(&bc_out, OP_check_define_var);+                dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));+                flags = 0;+                if (hf->is_lexical)+                    flags |= DEFINE_GLOBAL_LEX_VAR;+                if (hf->cpool_idx >= 0)+                    flags |= DEFINE_GLOBAL_FUNC_VAR;+                dbuf_putc(&bc_out, flags);+            }+        next: ;+        }+    }++    arg_valid = 0;+    line_num = 0; /* avoid warning */+    for (pos = 0; pos < bc_len; pos = pos_next) {+        op = bc_buf[pos];+        len = opcode_info[op].size;+        pos_next = pos + len;+        switch(op) {+        case OP_line_num:+            line_num = get_u32(bc_buf + pos + 1);+            s->line_number_size++;+            goto no_change;++        case OP_set_arg_valid_upto:+            arg_valid = get_u16(bc_buf + pos + 1);+            break;+        case OP_eval: /* convert scope index to adjusted variable index */+            {+                int call_argc = get_u16(bc_buf + pos + 1);+                scope = get_u16(bc_buf + pos + 1 + 2);+                mark_eval_captured_variables(ctx, s, scope);+                dbuf_putc(&bc_out, op);+                dbuf_put_u16(&bc_out, call_argc);+                dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);+            }+            break;+        case OP_apply_eval: /* convert scope index to adjusted variable index */+            scope = get_u16(bc_buf + pos + 1);+            mark_eval_captured_variables(ctx, s, scope);+            dbuf_putc(&bc_out, op);+            dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);+            break;+        case OP_scope_get_var_undef:+        case OP_scope_get_var:+        case OP_scope_put_var:+        case OP_scope_delete_var:+        case OP_scope_get_ref:+        case OP_scope_put_var_init:+            var_name = get_u32(bc_buf + pos + 1);+            scope = get_u16(bc_buf + pos + 5);+            pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,+                                         NULL, NULL, pos_next, arg_valid);+            JS_FreeAtom(ctx, var_name);+            break;+        case OP_scope_make_ref:+            {+                int label;+                LabelSlot *ls;+                var_name = get_u32(bc_buf + pos + 1);+                label = get_u32(bc_buf + pos + 5);+                scope = get_u16(bc_buf + pos + 9);+                ls = &s->label_slots[label];+                ls->ref_count--;  /* always remove label reference */+                pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,+                                             bc_buf, ls, pos_next, arg_valid);+                JS_FreeAtom(ctx, var_name);+            }+            break;+        case OP_scope_get_private_field:+        case OP_scope_get_private_field2:+        case OP_scope_put_private_field:+            {+                int ret;+                var_name = get_u32(bc_buf + pos + 1);+                scope = get_u16(bc_buf + pos + 5);+                ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);+                if (ret < 0)+                    goto fail;+                JS_FreeAtom(ctx, var_name);+            }+            break;+        case OP_gosub:+            s->jump_size++;+            if (OPTIMIZE) {+                /* remove calls to empty finalizers  */+                int label;+                LabelSlot *ls;++                label = get_u32(bc_buf + pos + 1);+                assert(label >= 0 && label < s->label_count);+                ls = &s->label_slots[label];+                if (code_match(&cc, ls->pos, OP_ret, -1)) {+                    ls->ref_count--;+                    break;+                }+            }+            goto no_change;+        case OP_drop:+            if (0) {+                /* remove drops before return_undef */+                /* do not perform this optimization in pass2 because+                   it breaks patterns recognised in resolve_labels */+                int pos1 = pos_next;+                int line1 = line_num;+                while (code_match(&cc, pos1, OP_drop, -1)) {+                    if (cc.line_num >= 0) line1 = cc.line_num;+                    pos1 = cc.pos;+                }+                if (code_match(&cc, pos1, OP_return_undef, -1)) {+                    pos_next = pos1;+                    if (line1 != -1 && line1 != line_num) {+                        line_num = line1;+                        s->line_number_size++;+                        dbuf_putc(&bc_out, OP_line_num);+                        dbuf_put_u32(&bc_out, line_num);+                    }+                    break;+                }+            }+            goto no_change;+        case OP_insert3:+            if (OPTIMIZE) {+                /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */+                if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {+                    dbuf_putc(&bc_out, cc.op);+                    pos_next = cc.pos;+                    if (cc.line_num != -1 && cc.line_num != line_num) {+                        line_num = cc.line_num;+                        s->line_number_size++;+                        dbuf_putc(&bc_out, OP_line_num);+                        dbuf_put_u32(&bc_out, line_num);+                    }+                    break;+                }+            }+            goto no_change;++        case OP_goto:+            s->jump_size++;+            /* fall thru */+        case OP_tail_call:+        case OP_tail_call_method:+        case OP_return:+        case OP_return_undef:+        case OP_throw:+        case OP_throw_var:+        case OP_ret:+            if (OPTIMIZE) {+                /* remove dead code */+                int line = -1;+                dbuf_put(&bc_out, bc_buf + pos, len);+                pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);+                pos_next = pos;+                if (pos < bc_len && line >= 0 && line_num != line) {+                    line_num = line;+                    s->line_number_size++;+                    dbuf_putc(&bc_out, OP_line_num);+                    dbuf_put_u32(&bc_out, line_num);+                }+                break;+            }+            goto no_change;++        case OP_label:+            {+                int label;+                LabelSlot *ls;++                label = get_u32(bc_buf + pos + 1);+                assert(label >= 0 && label < s->label_count);+                ls = &s->label_slots[label];+                ls->pos2 = bc_out.size + opcode_info[op].size;+            }+            goto no_change;++        case OP_enter_scope:+            {+                int scope_idx, scope = get_u16(bc_buf + pos + 1);++                if (scope == 1) {+                    instantiate_hoisted_definitions(ctx, s, &bc_out);+                }++                for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {+                    JSVarDef *vd = &s->vars[scope_idx];+                    if (vd->scope_level == scope) {+                        if (vd->var_kind == JS_VAR_FUNCTION_DECL ||+                            vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {+                            /* Initialize lexical variable upon entering scope */+                            dbuf_putc(&bc_out, OP_fclosure);+                            dbuf_put_u32(&bc_out, vd->func_pool_or_scope_idx);+                            dbuf_putc(&bc_out, OP_put_loc);+                            dbuf_put_u16(&bc_out, scope_idx);+                        } else {+                            /* XXX: should check if variable can be used+                               before initialization */+                            dbuf_putc(&bc_out, OP_set_loc_uninitialized);+                            dbuf_put_u16(&bc_out, scope_idx);+                        }+                        scope_idx = vd->scope_next;+                    } else {+                        break;+                    }+                }+            }+            break;++        case OP_leave_scope:+            {+                int scope_idx, scope = get_u16(bc_buf + pos + 1);++                for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {+                    JSVarDef *vd = &s->vars[scope_idx];+                    if (vd->scope_level == scope) {+                        if (vd->is_captured) {+                            dbuf_putc(&bc_out, OP_close_loc);+                            dbuf_put_u16(&bc_out, scope_idx);+                        }+                        scope_idx = vd->scope_next;+                    } else {+                        break;+                    }+                }+            }+            break;++        case OP_set_name:+            {+                /* remove dummy set_name opcodes */+                JSAtom name = get_u32(bc_buf + pos + 1);+                if (name == JS_ATOM_NULL)+                    break;+            }+            goto no_change;++        case OP_if_false:+        case OP_if_true:+        case OP_catch:+            s->jump_size++;+            goto no_change;++        case OP_dup:+            if (OPTIMIZE) {+                /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */+                /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */+                if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {+                    int lab0, lab1, op1, pos1, line1, pos2;+                    lab0 = lab1 = cc.label;+                    assert(lab1 >= 0 && lab1 < s->label_count);+                    op1 = cc.op;+                    pos1 = cc.pos;+                    line1 = cc.line_num;+                    while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {+                        lab1 = cc.label;+                    }+                    if (code_match(&cc, pos2, op1, -1)) {+                        s->jump_size++;+                        update_label(s, lab0, -1);+                        update_label(s, cc.label, +1);+                        dbuf_putc(&bc_out, op1);+                        dbuf_put_u32(&bc_out, cc.label);+                        pos_next = pos1;+                        if (line1 != -1 && line1 != line_num) {+                            line_num = line1;+                            s->line_number_size++;+                            dbuf_putc(&bc_out, OP_line_num);+                            dbuf_put_u32(&bc_out, line_num);+                        }+                        break;+                    }+                }+            }+            goto no_change;++        case OP_nop:+            /* remove erased code */+            break;+        case OP_set_class_name:+            /* only used during parsing */+            break;+            +        default:+        no_change:+            dbuf_put(&bc_out, bc_buf + pos, len);+            break;+        }+    }++    /* set the new byte code */+    dbuf_free(&s->byte_code);+    s->byte_code = bc_out;+    if (dbuf_error(&s->byte_code)) {+        JS_ThrowOutOfMemory(ctx);+        return -1;+    }+    return 0;+ fail:+    /* continue the copy to keep the atom refcounts consistent */+    /* XXX: find a better solution ? */+    for (; pos < bc_len; pos = pos_next) {+        op = bc_buf[pos];+        len = opcode_info[op].size;+        pos_next = pos + len;+        dbuf_put(&bc_out, bc_buf + pos, len);+    }+    dbuf_free(&s->byte_code);+    s->byte_code = bc_out;+    return -1;+}++/* the pc2line table gives a line number for each PC value */+static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)+{+    if (s->line_number_slots != NULL+    &&  s->line_number_count < s->line_number_size+    &&  pc >= s->line_number_last_pc+    &&  line_num != s->line_number_last) {+        s->line_number_slots[s->line_number_count].pc = pc;+        s->line_number_slots[s->line_number_count].line_num = line_num;+        s->line_number_count++;+        s->line_number_last_pc = pc;+        s->line_number_last = line_num;+    }+}++static void compute_pc2line_info(JSFunctionDef *s)+{+    if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {+        int last_line_num = s->line_num;+        uint32_t last_pc = 0;+        int i;++        js_dbuf_init(s->ctx, &s->pc2line);+        for (i = 0; i < s->line_number_count; i++) {+            uint32_t pc = s->line_number_slots[i].pc;+            int line_num = s->line_number_slots[i].line_num;+            int diff_pc, diff_line;++            if (line_num < 0)+                continue;++            diff_pc = pc - last_pc;+            diff_line = line_num - last_line_num;+            if (diff_line == 0 || diff_pc < 0)+                continue;++            if (diff_line >= PC2LINE_BASE &&+                diff_line < PC2LINE_BASE + PC2LINE_RANGE &&+                diff_pc <= PC2LINE_DIFF_PC_MAX) {+                dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) ++                          diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);+            } else {+                /* longer encoding */+                dbuf_putc(&s->pc2line, 0);+                dbuf_put_leb128(&s->pc2line, diff_pc);+                dbuf_put_sleb128(&s->pc2line, diff_line);+            }+            last_pc = pc;+            last_line_num = line_num;+        }+    }+}++static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)+{+    RelocEntry *re;+    re = js_malloc(ctx, sizeof(*re));+    if (!re)+        return NULL;+    re->addr = addr;+    re->size = size;+    re->next = ls->first_reloc;+    ls->first_reloc = re;+    return re;+}++static BOOL code_has_label(CodeContext *s, int pos, int label)+{+    while (pos < s->bc_len) {+        int op = s->bc_buf[pos];+        if (op == OP_line_num) {+            pos += 5;+            continue;+        }+        if (op == OP_label) {+            int lab = get_u32(s->bc_buf + pos + 1);+            if (lab == label)+                return TRUE;+            pos += 5;+            continue;+        }+        if (op == OP_goto) {+            int lab = get_u32(s->bc_buf + pos + 1);+            if (lab == label)+                return TRUE;+        }+        break;+    }+    return FALSE;+}++/* return the target label, following the OP_goto jumps+   the first opcode at destination is stored in *pop+ */+static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)+{+    int i, pos, op;++    update_label(s, label, -1);+    for (i = 0; i < 10; i++) {+        assert(label >= 0 && label < s->label_count);+        pos = s->label_slots[label].pos2;+        for (;;) {+            switch(op = s->byte_code.buf[pos]) {+            case OP_line_num:+                if (pline)+                    *pline = get_u32(s->byte_code.buf + pos + 1);+                /* fall thru */+            case OP_label:+                pos += opcode_info[op].size;+                continue;+            case OP_goto:+                label = get_u32(s->byte_code.buf + pos + 1);+                break;+            case OP_drop:+                /* ignore drop opcodes if followed by OP_return_undef */+                while (s->byte_code.buf[++pos] == OP_drop)+                    continue;+                if (s->byte_code.buf[pos] == OP_return_undef)+                    op = OP_return_undef;+                /* fall thru */+            default:+                goto done;+            }+            break;+        }+    }+    /* cycle detected, could issue a warning */+ done:+    *pop = op;+    update_label(s, label, +1);+    return label;+}++static void push_short_int(DynBuf *bc_out, int val)+{+#if SHORT_OPCODES+    if (val >= -1 && val <= 7) {+        dbuf_putc(bc_out, OP_push_0 + val);+        return;+    }+    if (val == (int8_t)val) {+        dbuf_putc(bc_out, OP_push_i8);+        dbuf_putc(bc_out, val);+        return;+    }+    if (val == (int16_t)val) {+        dbuf_putc(bc_out, OP_push_i16);+        dbuf_put_u16(bc_out, val);+        return;+    }+#endif+    dbuf_putc(bc_out, OP_push_i32);+    dbuf_put_u32(bc_out, val);+}++static void put_short_code(DynBuf *bc_out, int op, int idx)+{+#if SHORT_OPCODES+    if (idx < 4) {+        switch (op) {+        case OP_get_loc:+            dbuf_putc(bc_out, OP_get_loc0 + idx);+            return;+        case OP_put_loc:+            dbuf_putc(bc_out, OP_put_loc0 + idx);+            return;+        case OP_set_loc:+            dbuf_putc(bc_out, OP_set_loc0 + idx);+            return;+        case OP_get_arg:+            dbuf_putc(bc_out, OP_get_arg0 + idx);+            return;+        case OP_put_arg:+            dbuf_putc(bc_out, OP_put_arg0 + idx);+            return;+        case OP_set_arg:+            dbuf_putc(bc_out, OP_set_arg0 + idx);+            return;+        case OP_get_var_ref:+            dbuf_putc(bc_out, OP_get_var_ref0 + idx);+            return;+        case OP_put_var_ref:+            dbuf_putc(bc_out, OP_put_var_ref0 + idx);+            return;+        case OP_set_var_ref:+            dbuf_putc(bc_out, OP_set_var_ref0 + idx);+            return;+        case OP_call:+            dbuf_putc(bc_out, OP_call0 + idx);+            return;+        }+    }+    if (idx < 256) {+        switch (op) {+        case OP_get_loc:+            dbuf_putc(bc_out, OP_get_loc8);+            dbuf_putc(bc_out, idx);+            return;+        case OP_put_loc:+            dbuf_putc(bc_out, OP_put_loc8);+            dbuf_putc(bc_out, idx);+            return;+        case OP_set_loc:+            dbuf_putc(bc_out, OP_set_loc8);+            dbuf_putc(bc_out, idx);+            return;+        }+    }+#endif+    dbuf_putc(bc_out, op);+    dbuf_put_u16(bc_out, idx);+}++/* peephole optimizations and resolve goto/labels */+static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)+{+    int pos, pos_next, bc_len, op, op1, len, i, line_num;+    const uint8_t *bc_buf;+    DynBuf bc_out;+    LabelSlot *label_slots, *ls;+    RelocEntry *re, *re_next;+    CodeContext cc;+    int label;+#if SHORT_OPCODES+    JumpSlot *jp;+#endif++    label_slots = s->label_slots;++    line_num = s->line_num;++    cc.bc_buf = bc_buf = s->byte_code.buf;+    cc.bc_len = bc_len = s->byte_code.size;+    js_dbuf_init(ctx, &bc_out);++#if SHORT_OPCODES+    if (s->jump_size) {+        s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);+        if (s->jump_slots == NULL)+            return -1;+    }+#endif+    /* XXX: Should skip this phase if not generating SHORT_OPCODES */+    if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {+        s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);+        if (s->line_number_slots == NULL)+            return -1;+        s->line_number_last = s->line_num;+        s->line_number_last_pc = 0;+    }++    /* initialize the 'home_object' variable if needed */+    if (s->home_object_var_idx >= 0) {+        dbuf_putc(&bc_out, OP_special_object);+        dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);+        put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);+    }+    /* initialize the 'this.active_func' variable if needed */+    if (s->this_active_func_var_idx >= 0) {+        dbuf_putc(&bc_out, OP_special_object);+        dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);+        put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);+    }+    /* initialize the 'new.target' variable if needed */+    if (s->new_target_var_idx >= 0) {+        dbuf_putc(&bc_out, OP_special_object);+        dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);+        put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);+    }+    /* initialize the 'this' variable if needed. In a derived class+       constructor, this is initially uninitialized. */+    if (s->this_var_idx >= 0) {+        if (s->is_derived_class_constructor) {+            dbuf_putc(&bc_out, OP_set_loc_uninitialized);+            dbuf_put_u16(&bc_out, s->this_var_idx);+        } else {+            dbuf_putc(&bc_out, OP_push_this);+            put_short_code(&bc_out, OP_put_loc, s->this_var_idx);+        }+    }+    /* initialize the 'arguments' variable if needed */+    if (s->arguments_var_idx >= 0) {+        if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {+            dbuf_putc(&bc_out, OP_special_object);+            dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);+        } else {+            dbuf_putc(&bc_out, OP_special_object);+            dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);+        }+        put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);+    }+    /* initialize a reference to the current function if needed */+    if (s->func_var_idx >= 0) {+        dbuf_putc(&bc_out, OP_special_object);+        dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);+        put_short_code(&bc_out, OP_put_loc, s->func_var_idx);+    }+    /* initialize the variable environment object if needed */+    if (s->var_object_idx >= 0) {+        dbuf_putc(&bc_out, OP_special_object);+        dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);+        put_short_code(&bc_out, OP_put_loc, s->var_object_idx);+    }++    for (pos = 0; pos < bc_len; pos = pos_next) {+        int val;+        op = bc_buf[pos];+        len = opcode_info[op].size;+        pos_next = pos + len;+        switch(op) {+        case OP_line_num:+            /* line number info (for debug). We put it in a separate+               compressed table to reduce memory usage and get better+               performance */+            line_num = get_u32(bc_buf + pos + 1);+            break;++        case OP_label:+            {+                label = get_u32(bc_buf + pos + 1);+                assert(label >= 0 && label < s->label_count);+                ls = &label_slots[label];+                assert(ls->addr == -1);+                ls->addr = bc_out.size;+                /* resolve the relocation entries */+                for(re = ls->first_reloc; re != NULL; re = re_next) {+                    int diff = ls->addr - re->addr;+                    re_next = re->next;+                    switch (re->size) {+                    case 4:+                        put_u32(bc_out.buf + re->addr, diff);+                        break;+                    case 2:+                        assert(diff == (int16_t)diff);+                        put_u16(bc_out.buf + re->addr, diff);+                        break;+                    case 1:+                        assert(diff == (int8_t)diff);+                        put_u8(bc_out.buf + re->addr, diff);+                        break;+                    }+                    js_free(ctx, re);+                }+                ls->first_reloc = NULL;+            }+            break;++        case OP_call:+        case OP_call_method:+            {+                /* detect and transform tail calls */+                int argc;+                argc = get_u16(bc_buf + pos + 1);+                if (code_match(&cc, pos_next, OP_return, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    put_short_code(&bc_out, op + 1, argc);+                    pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);+                    break;+                }+                add_pc2line_info(s, bc_out.size, line_num);+                put_short_code(&bc_out, op, argc);+                break;+            }+            goto no_change;++        case OP_return:+        case OP_return_undef:+        case OP_return_async:+        case OP_throw:+        case OP_throw_var:+            pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);+            goto no_change;++        case OP_goto:+            label = get_u32(bc_buf + pos + 1);+        has_goto:+            if (OPTIMIZE) {+                int line1 = -1;+                /* Use custom matcher because multiple labels can follow */+                label = find_jump_target(s, label, &op1, &line1);+                if (code_has_label(&cc, pos_next, label)) {+                    /* jump to next instruction: remove jump */+                    update_label(s, label, -1);+                    break;+                }+                if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {+                    /* jump to return/throw: remove jump, append return/throw */+                    /* updating the line number obfuscates assembly listing */+                    //if (line1 >= 0) line_num = line1;+                    update_label(s, label, -1);+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, op1);+                    pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);+                    break;+                }+                /* XXX: should duplicate single instructions followed by goto or return */+                /* For example, can match one of these followed by return:+                   push_i32 / push_const / push_atom_value / get_var /+                   undefined / null / push_false / push_true / get_ref_value /+                   get_loc / get_arg / get_var_ref+                 */+            }+            goto has_label;++        case OP_gosub:+            label = get_u32(bc_buf + pos + 1);+            if (0 && OPTIMIZE) {+                label = find_jump_target(s, label, &op1, NULL);+                if (op1 == OP_ret) {+                    update_label(s, label, -1);+                    /* empty finally clause: remove gosub */+                    break;+                }+            }+            goto has_label;++        case OP_catch:+            label = get_u32(bc_buf + pos + 1);+            goto has_label;++        case OP_if_true:+        case OP_if_false:+            label = get_u32(bc_buf + pos + 1);+            if (OPTIMIZE) {+                label = find_jump_target(s, label, &op1, NULL);+                /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */+                if (code_has_label(&cc, pos_next, label)) {+                    update_label(s, label, -1);+                    dbuf_putc(&bc_out, OP_drop);+                    break;+                }+                /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */+                if (code_match(&cc, pos_next, OP_goto, -1)) {+                    int pos1 = cc.pos;+                    int line1 = cc.line_num;+                    if (code_has_label(&cc, pos1, label)) {+                        if (line1 >= 0) line_num = line1;+                        pos_next = pos1;+                        update_label(s, label, -1);+                        label = cc.label;+                        op ^= OP_if_true ^ OP_if_false;+                    }+                }+            }+        has_label:+            add_pc2line_info(s, bc_out.size, line_num);+            if (op == OP_goto) {+                pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);+            }+            assert(label >= 0 && label < s->label_count);+            ls = &label_slots[label];+#if SHORT_OPCODES+            jp = &s->jump_slots[s->jump_count++];+            jp->op = op;+            jp->size = 4;+            jp->pos = bc_out.size + 1;+            jp->label = label;++            if (ls->addr == -1) {+                int diff = ls->pos2 - pos - 1;+                if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {+                    jp->size = 1;+                    jp->op = OP_if_false8 + (op - OP_if_false);+                    dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));+                    dbuf_putc(&bc_out, 0);+                    if (!add_reloc(ctx, ls, bc_out.size - 1, 1))+                        goto fail;+                    break;+                }+                if (diff < 32768 && op == OP_goto) {+                    jp->size = 2;+                    jp->op = OP_goto16;+                    dbuf_putc(&bc_out, OP_goto16);+                    dbuf_put_u16(&bc_out, 0);+                    if (!add_reloc(ctx, ls, bc_out.size - 2, 2))+                        goto fail;+                    break;+                }+            } else {+                int diff = ls->addr - bc_out.size - 1;+                if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {+                    jp->size = 1;+                    jp->op = OP_if_false8 + (op - OP_if_false);+                    dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));+                    dbuf_putc(&bc_out, diff);+                    break;+                }+                if (diff == (int16_t)diff && op == OP_goto) {+                    jp->size = 2;+                    jp->op = OP_goto16;+                    dbuf_putc(&bc_out, OP_goto16);+                    dbuf_put_u16(&bc_out, diff);+                    break;+                }+            }+#endif+            dbuf_putc(&bc_out, op);+            dbuf_put_u32(&bc_out, ls->addr - bc_out.size);+            if (ls->addr == -1) {+                /* unresolved yet: create a new relocation entry */+                if (!add_reloc(ctx, ls, bc_out.size - 4, 4))+                    goto fail;+            }+            break;+        case OP_with_get_var:+        case OP_with_put_var:+        case OP_with_delete_var:+        case OP_with_make_ref:+        case OP_with_get_ref:+        case OP_with_get_ref_undef:+            {+                JSAtom atom;+                int is_with;++                atom = get_u32(bc_buf + pos + 1);+                label = get_u32(bc_buf + pos + 5);+                is_with = bc_buf[pos + 9];+                if (OPTIMIZE) {+                    label = find_jump_target(s, label, &op1, NULL);+                }+                assert(label >= 0 && label < s->label_count);+                ls = &label_slots[label];+                add_pc2line_info(s, bc_out.size, line_num);+#if SHORT_OPCODES+                jp = &s->jump_slots[s->jump_count++];+                jp->op = op;+                jp->size = 4;+                jp->pos = bc_out.size + 5;+                jp->label = label;+#endif+                dbuf_putc(&bc_out, op);+                dbuf_put_u32(&bc_out, atom);+                dbuf_put_u32(&bc_out, ls->addr - bc_out.size);+                if (ls->addr == -1) {+                    /* unresolved yet: create a new relocation entry */+                    if (!add_reloc(ctx, ls, bc_out.size - 4, 4))+                        goto fail;+                }+                dbuf_putc(&bc_out, is_with);+            }+            break;++        case OP_drop:+            if (OPTIMIZE) {+                /* remove useless drops before return */+                if (code_match(&cc, pos_next, OP_return_undef, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    break;+                }+            }+            goto no_change;++        case OP_null:+#if SHORT_OPCODES+            if (OPTIMIZE) {+                /* transform null strict_eq into is_null */+                if (code_match(&cc, pos_next, OP_strict_eq, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_is_null);+                    pos_next = cc.pos;+                    break;+                }+                /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */+                if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_is_null);+                    pos_next = cc.pos;+                    label = cc.label;+                    op = cc.op ^ OP_if_false ^ OP_if_true;+                    goto has_label;+                }+            }+#endif+            /* fall thru */+        case OP_push_false:+        case OP_push_true:+            if (OPTIMIZE) {+                val = (op == OP_push_true);+                if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {+                has_constant_test:+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    if (val == cc.op - OP_if_false) {+                        /* transform null if_false(l1) -> goto l1 */+                        /* transform false if_false(l1) -> goto l1 */+                        /* transform true if_true(l1) -> goto l1 */+                        pos_next = cc.pos;+                        op = OP_goto;+                        label = cc.label;+                        goto has_goto;+                    } else {+                        /* transform null if_true(l1) -> nop */+                        /* transform false if_true(l1) -> nop */+                        /* transform true if_false(l1) -> nop */+                        pos_next = cc.pos;+                        update_label(s, cc.label, -1);+                        break;+                    }+                }+            }+            goto no_change;++        case OP_push_i32:+            if (OPTIMIZE) {+                /* transform i32(val) neg -> i32(-val) */+                val = get_i32(bc_buf + pos + 1);+                if ((val != INT32_MIN && val != 0)+                &&  code_match(&cc, pos_next, OP_neg, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    if (code_match(&cc, cc.pos, OP_drop, -1)) {+                        if (cc.line_num >= 0) line_num = cc.line_num;+                    } else {+                        add_pc2line_info(s, bc_out.size, line_num);+                        push_short_int(&bc_out, -val);+                    }+                    pos_next = cc.pos;+                    break;+                }+                /* remove push/drop pairs generated by the parser */+                if (code_match(&cc, pos_next, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    pos_next = cc.pos;+                    break;+                }+                /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */+                if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {+                    val = (val != 0);+                    goto has_constant_test;+                }+                add_pc2line_info(s, bc_out.size, line_num);+                push_short_int(&bc_out, val);+                break;+            }+            goto no_change;++#if SHORT_OPCODES+        case OP_push_const:+        case OP_fclosure:+            if (OPTIMIZE) {+                int idx = get_u32(bc_buf + pos + 1);+                if (idx < 256) {+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);+                    dbuf_putc(&bc_out, idx);+                    break;+                }+            }+            goto no_change;++        case OP_get_field:+            if (OPTIMIZE) {+                JSAtom atom = get_u32(bc_buf + pos + 1);+                if (atom == JS_ATOM_length) {+                    JS_FreeAtom(ctx, atom);+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_get_length);+                    break;+                }+            }+            goto no_change;+#endif+        case OP_push_atom_value:+            if (OPTIMIZE) {+                JSAtom atom = get_u32(bc_buf + pos + 1);+                /* remove push/drop pairs generated by the parser */+                if (code_match(&cc, pos_next, OP_drop, -1)) {+                    JS_FreeAtom(ctx, atom);+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    pos_next = cc.pos;+                    break;+                }+#if SHORT_OPCODES+                if (atom == JS_ATOM_empty_string) {+                    JS_FreeAtom(ctx, atom);+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_push_empty_string);+                    break;+                }+#endif+            }+            goto no_change;++        case OP_to_propkey:+        case OP_to_propkey2:+            if (OPTIMIZE) {+                /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */+                if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)+                ||  code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)+                ||  code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {+                    break;+                }+            }+            goto no_change;++        case OP_undefined:+            if (OPTIMIZE) {+                /* remove push/drop pairs generated by the parser */+                if (code_match(&cc, pos_next, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    pos_next = cc.pos;+                    break;+                }+                /* transform undefined return -> return_undefined */+                if (code_match(&cc, pos_next, OP_return, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_return_undef);+                    pos_next = cc.pos;+                    break;+                }+                /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */+                if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {+                    val = 0;+                    goto has_constant_test;+                }+#if SHORT_OPCODES+                /* transform undefined strict_eq -> is_undefined */+                if (code_match(&cc, pos_next, OP_strict_eq, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_is_undefined);+                    pos_next = cc.pos;+                    break;+                }+                /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */+                if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_is_undefined);+                    pos_next = cc.pos;+                    label = cc.label;+                    op = cc.op ^ OP_if_false ^ OP_if_true;+                    goto has_label;+                }+#endif+            }+            goto no_change;++        case OP_insert2:+            if (OPTIMIZE) {+                /* Transformation:+                   insert2 put_field(a) drop -> put_field(a)+                   insert2 put_var_strict(a) drop -> put_var_strict(a)+                */+                if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, cc.op);+                    dbuf_put_u32(&bc_out, cc.atom);+                    pos_next = cc.pos;+                    break;+                }+            }+            goto no_change;++        case OP_dup:+            if (OPTIMIZE) {+                /* 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 (cc.line_num >= 0) line_num = cc.line_num;+                    op1 = cc.op + 1;  /* put_x -> set_x */+                    pos_next = cc.pos;+                    if (code_match(&cc, cc.pos, OP_drop, -1)) {+                        if (cc.line_num >= 0) line_num = cc.line_num;+                        op1 -= 1; /* set_x drop -> put_x */+                        pos_next = cc.pos;+                        if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {+                            line2 = cc.line_num; /* delay line number update */+                            op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */+                            pos_next = cc.pos;+                        }+                    }+                    add_pc2line_info(s, bc_out.size, line_num);+                    put_short_code(&bc_out, op1, cc.idx);+                    if (line2 >= 0) line_num = line2;+                    break;+                }+            }+            goto no_change;++        case OP_get_loc:+            if (OPTIMIZE) {+                /* transformation:+                   get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)+                   get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)+                   get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)+                   get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)+                 */+                int idx;+                idx = get_u16(bc_buf + pos + 1);+                if (idx >= 256)+                    goto no_change;+                if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||+                    code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);+                    dbuf_putc(&bc_out, idx);+                    pos_next = cc.pos;+                    break;+                }+                /* transformation:+                   get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)+                 */+                if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+#if SHORT_OPCODES+                    if (cc.atom == JS_ATOM_empty_string) {+                        JS_FreeAtom(ctx, cc.atom);+                        dbuf_putc(&bc_out, OP_push_empty_string);+                    } else+#endif+                    {+                        dbuf_putc(&bc_out, OP_push_atom_value);+                        dbuf_put_u32(&bc_out, cc.atom);+                    }+                    dbuf_putc(&bc_out, OP_add_loc);+                    dbuf_putc(&bc_out, idx);+                    pos_next = cc.pos;+                    break;+                }+                /* transformation:+                   get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)+                 */+                if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    push_short_int(&bc_out, cc.label);+                    dbuf_putc(&bc_out, OP_add_loc);+                    dbuf_putc(&bc_out, idx);+                    pos_next = cc.pos;+                    break;+                }+                /* transformation: XXX: also do these:+                   get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)+                   get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)+                   get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)+                 */+                if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    put_short_code(&bc_out, cc.op, cc.idx);+                    dbuf_putc(&bc_out, OP_add_loc);+                    dbuf_putc(&bc_out, idx);+                    pos_next = cc.pos;+                    break;+                }+                add_pc2line_info(s, bc_out.size, line_num);+                put_short_code(&bc_out, op, idx);+                break;+            }+            goto no_change;+#if SHORT_OPCODES+        case OP_get_arg:+        case OP_get_var_ref:+            if (OPTIMIZE) {+                int idx;+                idx = get_u16(bc_buf + pos + 1);+                add_pc2line_info(s, bc_out.size, line_num);+                put_short_code(&bc_out, op, idx);+                break;+            }+            goto no_change;+#endif+        case OP_put_loc:+        case OP_put_arg:+        case OP_put_var_ref:+            if (OPTIMIZE) {+                /* transformation: put_x(n) get_x(n) -> set_x(n) */+                int idx;+                idx = get_u16(bc_buf + pos + 1);+                if (code_match(&cc, pos_next, op - 1, idx, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    put_short_code(&bc_out, op + 1, idx);+                    pos_next = cc.pos;+                    break;+                }+                add_pc2line_info(s, bc_out.size, line_num);+                put_short_code(&bc_out, op, idx);+                break;+            }+            goto no_change;++        case OP_post_inc:+        case OP_post_dec:+            if (OPTIMIZE) {+                /* transformation:+                   post_inc put_x drop -> inc put_x+                   post_inc perm3 put_field drop -> inc put_field+                   post_inc perm3 put_var_strict drop -> inc put_var_strict+                   post_inc perm4 put_array_el drop -> inc put_array_el+                 */+                int op1, idx;+                if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    op1 = cc.op;+                    idx = cc.idx;+                    pos_next = cc.pos;+                    if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {+                        if (cc.line_num >= 0) line_num = cc.line_num;+                        op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */+                        pos_next = cc.pos;+                    }+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));+                    put_short_code(&bc_out, op1, idx);+                    break;+                }+                if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));+                    dbuf_putc(&bc_out, cc.op);+                    dbuf_put_u32(&bc_out, cc.atom);+                    pos_next = cc.pos;+                    break;+                }+                if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    add_pc2line_info(s, bc_out.size, line_num);+                    dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));+                    dbuf_putc(&bc_out, OP_put_array_el);+                    pos_next = cc.pos;+                    break;+                }+            }+            goto no_change;++        case OP_typeof:+            if (OPTIMIZE) {+                /* simplify typeof tests */+                if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {+                    if (cc.line_num >= 0) line_num = cc.line_num;+                    int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;+#if SHORT_OPCODES+                    int op2 = -1;+                    switch (cc.atom) {+                    case JS_ATOM_undefined:+                        op2 = OP_is_undefined;+                        break;+                    case JS_ATOM_function:+                        op2 = OP_is_function;+                        break;+                    }+                    if (op2 >= 0) {+                        /* transform typeof(s) == "<type>" into is_<type> */+                        if (op1 == OP_strict_eq) {+                            add_pc2line_info(s, bc_out.size, line_num);+                            dbuf_putc(&bc_out, op2);+                            JS_FreeAtom(ctx, cc.atom);+                            pos_next = cc.pos;+                            break;+                        }+                        if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {+                            /* transform typeof(s) != "<type>" if_false into is_<type> if_true */+                            if (cc.line_num >= 0) line_num = cc.line_num;+                            add_pc2line_info(s, bc_out.size, line_num);+                            dbuf_putc(&bc_out, op2);+                            JS_FreeAtom(ctx, cc.atom);+                            pos_next = cc.pos;+                            label = cc.label;+                            op = OP_if_true;+                            goto has_label;+                        }+                    }+#endif+                    if (cc.atom == JS_ATOM_undefined) {+                        /* transform typeof(s) == "undefined" into s === void 0 */+                        add_pc2line_info(s, bc_out.size, line_num);+                        dbuf_putc(&bc_out, OP_undefined);+                        dbuf_putc(&bc_out, op1);+                        JS_FreeAtom(ctx, cc.atom);+                        pos_next = cc.pos;+                        break;+                    }+                }+            }+            goto no_change;++        default:+        no_change:+            add_pc2line_info(s, bc_out.size, line_num);+            dbuf_put(&bc_out, bc_buf + pos, len);+            break;+        }+    }++    /* check that there were no missing labels */+    for(i = 0; i < s->label_count; i++) {+        assert(label_slots[i].first_reloc == NULL);+    }+#if SHORT_OPCODES+    if (OPTIMIZE) {+        /* more jump optimizations */+        int patch_offsets = 0;+        for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {+            LabelSlot *ls;+            JumpSlot *jp1;+            int j, pos, diff, delta;++            delta = 3;+            switch (op = jp->op) {+            case OP_goto16:+                delta = 1;+                /* fall thru */+            case OP_if_false:+            case OP_if_true:+            case OP_goto:+                pos = jp->pos;+                diff = s->label_slots[jp->label].addr - pos;+                if (diff >= -128 && diff <= 127 + delta) {+                    //put_u8(bc_out.buf + pos, diff);+                    jp->size = 1;+                    if (op == OP_goto16) {+                        bc_out.buf[pos - 1] = jp->op = OP_goto8;+                    } else {+                        bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);+                    }+                    goto shrink;+                } else+                if (diff == (int16_t)diff && op == OP_goto) {+                    //put_u16(bc_out.buf + pos, diff);+                    jp->size = 2;+                    delta = 2;+                    bc_out.buf[pos - 1] = jp->op = OP_goto16;+                shrink:+                    /* XXX: should reduce complexity, using 2 finger copy scheme */+                    memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,+                            bc_out.size - pos - jp->size - delta);+                    bc_out.size -= delta;+                    patch_offsets++;+                    for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {+                        if (ls->addr > pos)+                            ls->addr -= delta;+                    }+                    for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {+                        if (jp1->pos > pos)+                            jp1->pos -= delta;+                    }+                    for (j = 0; j < s->line_number_count; j++) {+                        if (s->line_number_slots[j].pc > pos)+                            s->line_number_slots[j].pc -= delta;+                    }+                    continue;+                }+                break;+            }+        }+        if (patch_offsets) {+            JumpSlot *jp1;+            int j;+            for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {+                int diff1 = s->label_slots[jp1->label].addr - jp1->pos;+                switch (jp1->size) {+                case 1:+                    put_u8(bc_out.buf + jp1->pos, diff1);+                    break;+                case 2:+                    put_u16(bc_out.buf + jp1->pos, diff1);+                    break;+                case 4:+                    put_u32(bc_out.buf + jp1->pos, diff1);+                    break;+                }+            }+        }+    }+    js_free(ctx, s->jump_slots);+    s->jump_slots = NULL;+#endif+    js_free(ctx, s->label_slots);+    s->label_slots = NULL;+    /* XXX: should delay until copying to runtime bytecode function */+    compute_pc2line_info(s);+    js_free(ctx, s->line_number_slots);+    s->line_number_slots = NULL;+    /* set the new byte code */+    dbuf_free(&s->byte_code);+    s->byte_code = bc_out;+    s->use_short_opcodes = TRUE;+    if (dbuf_error(&s->byte_code)) {+        JS_ThrowOutOfMemory(ctx);+        return -1;+    }+    return 0;+ fail:+    /* XXX: not safe */+    dbuf_free(&bc_out);+    return -1;+}++/* compute the maximum stack size needed by the function */++typedef struct StackSizeState {+    int stack_len_max;+    uint16_t *stack_level_tab;+} StackSizeState;++static __exception int compute_stack_size_rec(JSContext *ctx,+                                              JSFunctionDef *fd,+                                              StackSizeState *s,+                                              int pos, int op, int stack_len)+{+    int bc_len, diff, n_pop, pos_next;+    const JSOpCode *oi;+    const uint8_t *bc_buf;++    if (stack_len > s->stack_len_max) {+        s->stack_len_max = stack_len;+        if (s->stack_len_max > JS_STACK_SIZE_MAX)+            goto stack_overflow;+    }+    bc_buf = fd->byte_code.buf;+    bc_len = fd->byte_code.size;+    for(;;) {+        if ((unsigned)pos >= bc_len)+            goto buf_overflow;+#if 0+        printf("%5d: %d\n", pos, stack_len);+#endif+        if (s->stack_level_tab[pos] != 0xffff) {+            /* already explored: check that the stack size is consistent */+            if (s->stack_level_tab[pos] != stack_len) {+                JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",+                                      s->stack_level_tab[pos], stack_len, pos);+                return -1;+            } else {+                return 0;+            }+        } else {+            s->stack_level_tab[pos] = stack_len;+        }++        op = bc_buf[pos];+        if (op == 0 || op >= OP_COUNT) {+            JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);+            return -1;+        }+        oi = &short_opcode_info(op);+        pos_next = pos + oi->size;+        if (pos_next > bc_len) {+        buf_overflow:+            JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);+            return -1;+        }+        n_pop = oi->n_pop;+        /* call pops a variable number of arguments */+        if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {+            n_pop += get_u16(bc_buf + pos + 1);+        } else {+#if SHORT_OPCODES+            if (oi->fmt == OP_FMT_npopx) {+                n_pop += op - OP_call0;+            }+#endif+        }++        if (stack_len < n_pop) {+            JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);+            return -1;+        }+        stack_len += oi->n_push - n_pop;+        if (stack_len > s->stack_len_max) {+            s->stack_len_max = stack_len;+            if (s->stack_len_max > JS_STACK_SIZE_MAX)+                goto stack_overflow;+        }+        switch(op) {+        case OP_tail_call:+        case OP_tail_call_method:+        case OP_return:+        case OP_return_undef:+        case OP_return_async:+        case OP_throw:+        case OP_throw_var:+        case OP_ret:+            goto done;+        case OP_goto:+            diff = get_u32(bc_buf + pos + 1);+            pos_next = pos + 1 + diff;+            break;+#if SHORT_OPCODES+        case OP_goto16:+            diff = (int16_t)get_u16(bc_buf + pos + 1);+            pos_next = pos + 1 + diff;+            break;+        case OP_goto8:+            diff = (int8_t)bc_buf[pos + 1];+            pos_next = pos + 1 + diff;+            break;+        case OP_if_true8:+        case OP_if_false8:+            diff = (int8_t)bc_buf[pos + 1];+            if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len))+                return -1;+            break;+#endif+        case OP_if_true:+        case OP_if_false:+        case OP_catch:+            diff = get_u32(bc_buf + pos + 1);+            if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len))+                return -1;+            break;+        case OP_gosub:+            diff = get_u32(bc_buf + pos + 1);+            if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len + 1))+                return -1;+            break;+        case OP_with_get_var:+        case OP_with_delete_var:+            diff = get_u32(bc_buf + pos + 5);+            if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 1))+                return -1;+            break;+        case OP_with_make_ref:+        case OP_with_get_ref:+        case OP_with_get_ref_undef:+            diff = get_u32(bc_buf + pos + 5);+            if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 2))+                return -1;+            break;+        case OP_with_put_var:+            diff = get_u32(bc_buf + pos + 5);+            if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len - 1))+                return -1;+            break;++        default:+            break;+        }+        pos = pos_next;+    }+ done:+    return 0;++ stack_overflow:+    JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);+    return -1;+}++static __exception int compute_stack_size(JSContext *ctx,+                                          JSFunctionDef *fd,+                                          int *pstack_size)+{+    StackSizeState s_s, *s = &s_s;+    int bc_len, i, ret;++    bc_len = fd->byte_code.size;+    /* bc_len > 0 */+    s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * bc_len);+    if (!s->stack_level_tab)+        return -1;+    for(i = 0; i < bc_len; i++)+        s->stack_level_tab[i] = 0xffff;+    s->stack_len_max = 0;+    ret = compute_stack_size_rec(ctx, fd, s, 0, OP_invalid, 0);+    js_free(ctx, s->stack_level_tab);+    *pstack_size = s->stack_len_max;+    return ret;+}++static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)+{+    int i, idx;+    JSModuleDef *m = fd->module;+    JSExportEntry *me;+    JSHoistedDef *hf;++    /* The imported global variables were added as closure variables+       in js_parse_import(). We add here the module global+       variables. */++    for(i = 0; i < fd->hoisted_def_count; i++) {+        hf = &fd->hoisted_def[i];+        if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,+                            hf->is_lexical, FALSE) < 0)+            return -1;+    }++    /* resolve the variable names of the local exports */+    for(i = 0; i < m->export_entries_count; i++) {+        me = &m->export_entries[i];+        if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+            idx = find_closure_var(ctx, fd, me->local_name);+            if (idx < 0) {+                JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",+                                        me->local_name);+                return -1;+            }+            me->u.local.var_idx = idx;+        }+    }+    return 0;+}++/* create a function object from a function definition. The function+   definition is freed. All the child functions are also created. It+   must be done this way to resolve all the variables. */+static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)+{+    JSValue func_obj;+    JSFunctionBytecode *b;+    struct list_head *el, *el1;+    int stack_size, scope, idx;+    int function_size, byte_code_offset, cpool_offset;+    int closure_var_offset, vardefs_offset;++    /* recompute scope linkage */+    for (scope = 0; scope < fd->scope_count; scope++) {+        fd->scopes[scope].first = -1;+    }+    for (idx = 0; idx < fd->var_count; idx++) {+        JSVarDef *vd = &fd->vars[idx];+        vd->scope_next = fd->scopes[vd->scope_level].first;+        fd->scopes[vd->scope_level].first = idx;+    }+    for (scope = 2; scope < fd->scope_count; scope++) {+        JSVarScope *sd = &fd->scopes[scope];+        if (sd->first == -1)+            sd->first = fd->scopes[sd->parent].first;+    }+    for (idx = 0; idx < fd->var_count; idx++) {+        JSVarDef *vd = &fd->vars[idx];+        if (vd->scope_next == -1 && vd->scope_level > 1) {+            scope = fd->scopes[vd->scope_level].parent;+            vd->scope_next = fd->scopes[scope].first;+        }+    }++    /* if the function contains an eval call, the closure variables+       are used to compile the eval and they must be ordered by scope,+       so it is necessary to create the closure variables before any+       other variable lookup is done. */+    if (fd->has_eval_call)+        add_eval_variables(ctx, fd);++    /* add the module global variables in the closure */+    if (fd->module) {+        if (add_module_variables(ctx, fd))+            goto fail;+    }++    /* first create all the child functions */+    list_for_each_safe(el, el1, &fd->child_list) {+        JSFunctionDef *fd1;+        int cpool_idx;++        fd1 = list_entry(el, JSFunctionDef, link);+        cpool_idx = fd1->parent_cpool_idx;+        func_obj = js_create_function(ctx, fd1);+        if (JS_IsException(func_obj))+            goto fail;+        /* save it in the constant pool */+        assert(cpool_idx >= 0);+        fd->cpool[cpool_idx] = func_obj;+    }++#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)+    if (!(fd->js_mode & JS_MODE_STRIP)) {+        printf("pass 1\n");+        dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,+                       fd->args, fd->arg_count, fd->vars, fd->var_count,+                       fd->closure_var, fd->closure_var_count,+                       fd->cpool, fd->cpool_count, fd->source, fd->line_num,+                       fd->label_slots, NULL);+        printf("\n");+    }+#endif++    if (resolve_variables(ctx, fd))+        goto fail;++#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)+    if (!(fd->js_mode & JS_MODE_STRIP)) {+        printf("pass 2\n");+        dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,+                       fd->args, fd->arg_count, fd->vars, fd->var_count,+                       fd->closure_var, fd->closure_var_count,+                       fd->cpool, fd->cpool_count, fd->source, fd->line_num,+                       fd->label_slots, NULL);+        printf("\n");+    }+#endif++    if (resolve_labels(ctx, fd))+        goto fail;++    if (compute_stack_size(ctx, fd, &stack_size) < 0)+        goto fail;++    if (fd->js_mode & JS_MODE_STRIP) {+        function_size = offsetof(JSFunctionBytecode, debug);+    } else {+        function_size = sizeof(*b);+    }+    cpool_offset = function_size;+    function_size += fd->cpool_count * sizeof(*fd->cpool);+    vardefs_offset = function_size;+    if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {+        function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);+    }+    closure_var_offset = function_size;+    function_size += fd->closure_var_count * sizeof(*fd->closure_var);+    byte_code_offset = function_size;+    function_size += fd->byte_code.size;++    b = js_mallocz(ctx, function_size);+    if (!b)+        goto fail;+    b->header.ref_count = 1;++    b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);+    b->byte_code_len = fd->byte_code.size;+    memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);+    js_free(ctx, fd->byte_code.buf);+    fd->byte_code.buf = NULL;++    b->func_name = fd->func_name;+    if (fd->arg_count + fd->var_count > 0) {+        if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {+            /* Strip variable definitions not needed at runtime */+            int i;+            for(i = 0; i < fd->var_count; i++) {+                JS_FreeAtom(ctx, fd->vars[i].var_name);+            }+            for(i = 0; i < fd->arg_count; i++) {+                JS_FreeAtom(ctx, fd->args[i].var_name);+            }+            for(i = 0; i < fd->closure_var_count; i++) {+                JS_FreeAtom(ctx, fd->closure_var[i].var_name);+                fd->closure_var[i].var_name = JS_ATOM_NULL;+            }+        } else {+            b->vardefs = (void *)((uint8_t*)b + vardefs_offset);+            memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));+            memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));+        }+        b->var_count = fd->var_count;+        b->arg_count = fd->arg_count;+        b->defined_arg_count = fd->defined_arg_count;+        js_free(ctx, fd->args);+        js_free(ctx, fd->vars);+    }+    b->cpool_count = fd->cpool_count;+    if (b->cpool_count) {+        b->cpool = (void *)((uint8_t*)b + cpool_offset);+        memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));+    }+    js_free(ctx, fd->cpool);+    fd->cpool = NULL;++    b->stack_size = stack_size;++    if (fd->js_mode & JS_MODE_STRIP) {+        JS_FreeAtom(ctx, fd->filename);+        dbuf_free(&fd->pc2line);    // probably useless+    } else {+        /* XXX: source and pc2line info should be packed at the end of the+           JSFunctionBytecode structure, avoiding allocation overhead+         */+        b->has_debug = 1;+        b->debug.filename = fd->filename;+        b->debug.line_num = fd->line_num;++        //DynBuf pc2line;+        //compute_pc2line_info(fd, &pc2line);+        //js_free(ctx, fd->line_number_slots)+        b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);+        if (!b->debug.pc2line_buf)+            b->debug.pc2line_buf = fd->pc2line.buf;+        b->debug.pc2line_len = fd->pc2line.size;+        b->debug.source = fd->source;+        b->debug.source_len = fd->source_len;+    }+    if (fd->scopes != fd->def_scope_array)+        js_free(ctx, fd->scopes);++    b->closure_var_count = fd->closure_var_count;+    if (b->closure_var_count) {+        b->closure_var = (void *)((uint8_t*)b + closure_var_offset);+        memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));+    }+    js_free(ctx, fd->closure_var);+    fd->closure_var = NULL;++    b->has_prototype = fd->has_prototype;+    b->has_simple_parameter_list = fd->has_simple_parameter_list;+    b->js_mode = fd->js_mode;+    b->is_derived_class_constructor = fd->is_derived_class_constructor;+    b->func_kind = fd->func_kind;+    b->need_home_object = (fd->home_object_var_idx >= 0 ||+                           fd->need_home_object);+    b->new_target_allowed = fd->new_target_allowed;+    b->super_call_allowed = fd->super_call_allowed;+    b->super_allowed = fd->super_allowed;+    b->arguments_allowed = fd->arguments_allowed;+    b->backtrace_barrier = fd->backtrace_barrier;+    b->realm = JS_DupContext(ctx);++    add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);+    +#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)+    if (!(fd->js_mode & JS_MODE_STRIP)) {+        js_dump_function_bytecode(ctx, b);+    }+#endif++    if (fd->parent) {+        /* remove from parent list */+        list_del(&fd->link);+    }++    js_free(ctx, fd);+    return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);+ fail:+    js_free_function_def(ctx, fd);+    return JS_EXCEPTION;+}++static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)+{+    int i;++#if 0+    {+        char buf[ATOM_GET_STR_BUF_SIZE];+        printf("freeing %s\n",+               JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));+    }+#endif+    free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);++    if (b->vardefs) {+        for(i = 0; i < b->arg_count + b->var_count; i++) {+            JS_FreeAtomRT(rt, b->vardefs[i].var_name);+        }+    }+    for(i = 0; i < b->cpool_count; i++)+        JS_FreeValueRT(rt, b->cpool[i]);++    for(i = 0; i < b->closure_var_count; i++) {+        JSClosureVar *cv = &b->closure_var[i];+        JS_FreeAtomRT(rt, cv->var_name);+    }+    if (b->realm)+        JS_FreeContext(b->realm);++    JS_FreeAtomRT(rt, b->func_name);+    if (b->has_debug) {+        JS_FreeAtomRT(rt, b->debug.filename);+        js_free_rt(rt, b->debug.pc2line_buf);+        js_free_rt(rt, b->debug.source);+    }++    remove_gc_object(&b->header);+    if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {+        list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);+    } else {+        js_free_rt(rt, b);+    }+}++static __exception int js_parse_directives(JSParseState *s)+{+    char str[20];+    JSParsePos pos;+    BOOL has_semi;++    if (s->token.val != TOK_STRING)+        return 0;++    js_parse_get_pos(s, &pos);++    while(s->token.val == TOK_STRING) {+        /* Copy actual source string representation */+        snprintf(str, sizeof str, "%.*s",+                 (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);++        if (next_token(s))+            return -1;++        has_semi = FALSE;+        switch (s->token.val) {+        case ';':+            if (next_token(s))+                return -1;+            has_semi = TRUE;+            break;+        case '}':+        case TOK_EOF:+            has_semi = TRUE;+            break;+        case TOK_NUMBER:+        case TOK_STRING:+        case TOK_TEMPLATE:+        case TOK_IDENT:+        case TOK_REGEXP:+        case TOK_DEC:+        case TOK_INC:+        case TOK_NULL:+        case TOK_FALSE:+        case TOK_TRUE:+        case TOK_IF:+        case TOK_RETURN:+        case TOK_VAR:+        case TOK_THIS:+        case TOK_DELETE:+        case TOK_TYPEOF:+        case TOK_NEW:+        case TOK_DO:+        case TOK_WHILE:+        case TOK_FOR:+        case TOK_SWITCH:+        case TOK_THROW:+        case TOK_TRY:+        case TOK_FUNCTION:+        case TOK_DEBUGGER:+        case TOK_WITH:+        case TOK_CLASS:+        case TOK_CONST:+        case TOK_ENUM:+        case TOK_EXPORT:+        case TOK_IMPORT:+        case TOK_SUPER:+        case TOK_INTERFACE:+        case TOK_LET:+        case TOK_PACKAGE:+        case TOK_PRIVATE:+        case TOK_PROTECTED:+        case TOK_PUBLIC:+        case TOK_STATIC:+            /* automatic insertion of ';' */+            if (s->got_lf)+                has_semi = TRUE;+            break;+        default:+            break;+        }+        if (!has_semi)+            break;+        if (!strcmp(str, "use strict")) {+            s->cur_func->has_use_strict = TRUE;+            s->cur_func->js_mode |= JS_MODE_STRICT;+        }+#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)+        else if (!strcmp(str, "use strip")) {+            s->cur_func->js_mode |= JS_MODE_STRIP;+        }+#endif+#ifdef CONFIG_BIGNUM+        else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {+            s->cur_func->js_mode |= JS_MODE_MATH;+        }+#endif+    }+    return js_parse_seek_token(s, &pos);+}++static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,+                                         JSAtom func_name)+{+    JSAtom name;+    int i, idx;++    if (fd->js_mode & JS_MODE_STRICT) {+        if (!fd->has_simple_parameter_list && fd->has_use_strict) {+            return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");+        }+        if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {+            return js_parse_error(s, "invalid function name in strict code");+        }+        for (idx = 0; idx < fd->arg_count; idx++) {+            name = fd->args[idx].var_name;++            if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {+                return js_parse_error(s, "invalid argument name in strict code");+            }+        }+    }+    /* check async_generator case */+    if ((fd->js_mode & JS_MODE_STRICT)+    ||  !fd->has_simple_parameter_list+    ||  (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)+    ||  fd->func_type == JS_PARSE_FUNC_ARROW+    ||  fd->func_type == JS_PARSE_FUNC_METHOD) {+        for (idx = 0; idx < fd->arg_count; idx++) {+            name = fd->args[idx].var_name;+            if (name != JS_ATOM_NULL) {+                for (i = 0; i < idx; i++) {+                    if (fd->args[i].var_name == name)+                        goto duplicate;+                }+                /* Check if argument name duplicates a destructuring parameter */+                /* XXX: should have a flag for such variables */+                for (i = 0; i < fd->var_count; i++) {+                    if (fd->vars[i].var_name == name)+                        goto duplicate;+                }+            }+        }+    }+    return 0;++duplicate:+    return js_parse_error(s, "duplicate argument names not allowed in this context");+}++/* create a function to initialize class fields */+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)+{+    JSFunctionDef *fd;+    +    fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,+                             s->filename, 0);+    if (!fd)+        return NULL;+    fd->func_name = JS_ATOM_NULL;+    fd->has_prototype = FALSE;+    fd->has_home_object = TRUE;+    +    fd->has_arguments_binding = FALSE;+    fd->has_this_binding = TRUE;+    fd->is_derived_class_constructor = FALSE;+    fd->new_target_allowed = TRUE;+    fd->super_call_allowed = FALSE;+    fd->super_allowed = fd->has_home_object;+    fd->arguments_allowed = FALSE;+    +    fd->func_kind = JS_FUNC_NORMAL;+    fd->func_type = JS_PARSE_FUNC_METHOD;+    return fd;+}++/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and+   JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */+static __exception int js_parse_function_decl2(JSParseState *s,+                                               JSParseFunctionEnum func_type,+                                               JSFunctionKindEnum func_kind,+                                               JSAtom func_name,+                                               const uint8_t *ptr,+                                               int function_line_num,+                                               JSParseExportEnum export_flag,+                                               JSFunctionDef **pfd)+{+    JSContext *ctx = s->ctx;+    JSFunctionDef *fd = s->cur_func;+    BOOL is_expr;+    int func_idx, lexical_func_idx = -1;+    BOOL has_opt_arg;+    BOOL create_func_var = FALSE;++    is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&+               func_type != JS_PARSE_FUNC_VAR);++    if (func_type == JS_PARSE_FUNC_STATEMENT ||+        func_type == JS_PARSE_FUNC_VAR ||+        func_type == JS_PARSE_FUNC_EXPR) {+        if (func_kind == JS_FUNC_NORMAL &&+            token_is_pseudo_keyword(s, JS_ATOM_async) &&+            peek_token(s, TRUE) != '\n') {+            if (next_token(s))+                return -1;+            func_kind = JS_FUNC_ASYNC;+        }+        if (next_token(s))+            return -1;+        if (s->token.val == '*') {+            if (next_token(s))+                return -1;+            func_kind |= JS_FUNC_GENERATOR;+        }++        if (s->token.val == TOK_IDENT) {+            if (s->token.u.ident.is_reserved ||+                (s->token.u.ident.atom == JS_ATOM_yield &&+                 func_type == JS_PARSE_FUNC_EXPR &&+                 (func_kind & JS_FUNC_GENERATOR)) ||+                (s->token.u.ident.atom == JS_ATOM_await &&+                 func_type == JS_PARSE_FUNC_EXPR &&+                 (func_kind & JS_FUNC_ASYNC))) {+                return js_parse_error_reserved_identifier(s);+            }+        }+        if (s->token.val == TOK_IDENT ||+            (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||+             (s->token.val == TOK_AWAIT && !s->is_module)) &&+             func_type == JS_PARSE_FUNC_EXPR)) {+            func_name = JS_DupAtom(ctx, s->token.u.ident.atom);+            if (next_token(s)) {+                JS_FreeAtom(ctx, func_name);+                return -1;+            }+        } else {+            if (func_type != JS_PARSE_FUNC_EXPR &&+                export_flag != JS_PARSE_EXPORT_DEFAULT) {+                return js_parse_error(s, "function name expected");+            }+        }+    } else if (func_type != JS_PARSE_FUNC_ARROW) {+        func_name = JS_DupAtom(ctx, func_name);+    }++    if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&+        (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {+        JSHoistedDef *hf;+        hf = find_hoisted_def(fd, func_name);+        /* XXX: should check scope chain */+        if (hf && hf->scope_level == fd->scope_level) {+            js_parse_error(s, "invalid redefinition of global identifier in module code");+            JS_FreeAtom(ctx, func_name);+            return -1;+        }+    }++    if (func_type == JS_PARSE_FUNC_VAR) {+        /* Create lexical name here so function closure contains it */+        if (!(fd->js_mode & JS_MODE_STRICT)+        &&  find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0+        &&  !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))+        &&  !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {+            create_func_var = TRUE;+        }+        if (fd->is_eval &&+            (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||+             fd->eval_type == JS_EVAL_TYPE_MODULE) &&+            fd->scope_level == 1) {+            /* avoid creating a lexical variable in the global+               scope. XXX: check annex B */+            JSHoistedDef *hf;+            hf = find_hoisted_def(fd, func_name);+            /* XXX: should check scope chain */+            if (hf && hf->scope_level == fd->scope_level) {+                js_parse_error(s, "invalid redefinition of global identifier");+                JS_FreeAtom(ctx, func_name);+                return -1;+            }+        } else {+            /* Always create a lexical name, fail if at the same scope as+               existing name */+            /* Lexical variable will be initialized upon entering scope */+            lexical_func_idx = define_var(s, fd, func_name,+                                          func_kind != JS_FUNC_NORMAL ?+                                          JS_VAR_DEF_NEW_FUNCTION_DECL :+                                          JS_VAR_DEF_FUNCTION_DECL);+            if (lexical_func_idx < 0) {+                JS_FreeAtom(ctx, func_name);+                return -1;+            }+        }+    }++    fd = js_new_function_def(ctx, fd, FALSE, is_expr,+                             s->filename, function_line_num);+    if (!fd) {+        JS_FreeAtom(ctx, func_name);+        return -1;+    }+    if (pfd)+        *pfd = fd;+    s->cur_func = fd;+    fd->func_name = func_name;+    /* XXX: test !fd->is_generator is always false */+    fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||+                         func_type == JS_PARSE_FUNC_VAR ||+                         func_type == JS_PARSE_FUNC_EXPR) &&+                        func_kind == JS_FUNC_NORMAL;+    fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||+                           func_type == JS_PARSE_FUNC_GETTER ||+                           func_type == JS_PARSE_FUNC_SETTER ||+                           func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||+                           func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);+    fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);+    fd->has_this_binding = fd->has_arguments_binding;+    fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);+    if (func_type == JS_PARSE_FUNC_ARROW) {+        fd->new_target_allowed = fd->parent->new_target_allowed;+        fd->super_call_allowed = fd->parent->super_call_allowed;+        fd->super_allowed = fd->parent->super_allowed;+        fd->arguments_allowed = fd->parent->arguments_allowed;+    } else {+        fd->new_target_allowed = TRUE;+        fd->super_call_allowed = fd->is_derived_class_constructor;+        fd->super_allowed = fd->has_home_object;+        fd->arguments_allowed = TRUE;+    }++    /* fd->in_function_body == FALSE prevents yield/await during the parsing+       of the arguments in generator/async functions. They are parsed as+       regular identifiers for other function kinds. */+    fd->func_kind = func_kind;+    fd->func_type = func_type;++    if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||+        func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {+        /* error if not invoked as a constructor */+        emit_op(s, OP_check_ctor);+    }++    if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {+        emit_class_field_init(s);+    }+    +    /* parse arguments */+    fd->has_simple_parameter_list = TRUE;+    has_opt_arg = FALSE;+    if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {+        JSAtom name;+        if (s->token.u.ident.is_reserved) {+            js_parse_error_reserved_identifier(s);+            goto fail;+        }+        name = s->token.u.ident.atom;+        if (add_arg(ctx, fd, name) < 0)+            goto fail;+        fd->defined_arg_count = 1;+    } else {+        if (js_parse_expect(s, '('))+            goto fail;++        while (s->token.val != ')') {+            JSAtom name;+            BOOL rest = FALSE;+            int idx;++            if (s->token.val == TOK_ELLIPSIS) {+                fd->has_simple_parameter_list = FALSE;+                rest = TRUE;+                if (next_token(s))+                    goto fail;+            }+            if (s->token.val == '[' || s->token.val == '{') {+                fd->has_simple_parameter_list = FALSE;+                if (rest) {+                    emit_op(s, OP_rest);+                    emit_u16(s, fd->arg_count);+                } else {+                    /* unnamed arg for destructuring */+                    idx = add_arg(ctx, fd, JS_ATOM_NULL);+                    emit_op(s, OP_get_arg);+                    emit_u16(s, idx);+                }+                if (js_parse_destructuring_element(s, TOK_VAR, 1, TRUE, -1, TRUE))+                    goto fail;+            } else if (s->token.val == TOK_IDENT) {+                if (s->token.u.ident.is_reserved) {+                    js_parse_error_reserved_identifier(s);+                    goto fail;+                }+                name = s->token.u.ident.atom;+                if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {+                    js_parse_error_reserved_identifier(s);+                    goto fail;+                }+                idx = add_arg(ctx, fd, name);+                if (idx < 0)+                    goto fail;+                if (next_token(s))+                    goto fail;+                if (rest) {+                    emit_op(s, OP_rest);+                    emit_u16(s, idx);+                    emit_op(s, OP_put_arg);+                    emit_u16(s, idx);+                    fd->has_simple_parameter_list = FALSE;+                    has_opt_arg = TRUE;+                } else if (s->token.val == '=') {+                    fd->has_simple_parameter_list = FALSE;+                    has_opt_arg = TRUE;++                    if (next_token(s))+                        goto fail;++                    /* optimize `x = void 0` default value: no code needed */+                    if (s->token.val == TOK_VOID) {+                        JSParsePos pos;+                        js_parse_get_pos(s, &pos);+                        if (next_token(s))+                            goto fail;+                        if (s->token.val == TOK_NUMBER) {+                            if (next_token(s))+                                goto fail;+                            if (s->token.val == ',') {+                                if (next_token(s))+                                    goto fail;+                                continue;+                            }+                            if (s->token.val == ')') {+                                continue;+                            }+                        }+                        if (js_parse_seek_token(s, &pos))+                            goto fail;+                    }+#if 0+                    /* XXX: not correct for eval code */+                    /* Check for a default value of `undefined`+                       to omit default argument processing */+                    if (s->token.val == TOK_IDENT &&+                        s->token.u.ident.atom == JS_ATOM_undefined &&+                        fd->parent == NULL &&+                        ((tok = peek_token(s, FALSE)) == ',' || tok == ')')) {+                        if (next_token(s))  /* ignore undefined token */+                            goto fail;+                    } else+#endif+                    {+                        int label = new_label(s);+                        if (idx > 0) {+                            emit_op(s, OP_set_arg_valid_upto);+                            emit_u16(s, idx);+                        }+                        emit_op(s, OP_get_arg);+                        emit_u16(s, idx);+                        emit_op(s, OP_undefined);+                        emit_op(s, OP_strict_eq);+                        emit_goto(s, OP_if_false, label);+                        if (js_parse_assign_expr(s, TRUE))+                            goto fail;+                        set_object_name(s, name);+                        emit_op(s, OP_put_arg);+                        emit_u16(s, idx);+                        emit_label(s, label);+                    }+                } else if (!has_opt_arg) {+                    fd->defined_arg_count++;+                }+            } else {+                js_parse_error(s, "missing formal parameter");+                goto fail;+            }+            if (rest && s->token.val != ')') {+                js_parse_expect(s, ')');+                goto fail;+            }+            if (s->token.val == ')')+                break;+            if (js_parse_expect(s, ','))+                goto fail;+        }+        if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||+            (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {+            js_parse_error(s, "invalid number of arguments for getter or setter");+            goto fail;+        }+    }++    if (next_token(s))+        goto fail;++    /* generator function: yield after the parameters are evaluated */+    if (func_kind == JS_FUNC_GENERATOR ||+        func_kind == JS_FUNC_ASYNC_GENERATOR)+        emit_op(s, OP_initial_yield);++    /* in generators, yield expression is forbidden during the parsing+       of the arguments */+    fd->in_function_body = TRUE;+    push_scope(s);  /* enter body scope: fd->scope_level = 1 */++    if (s->token.val == TOK_ARROW) {+        if (next_token(s))+            goto fail;++        if (s->token.val != '{') {+            if (js_parse_function_check_names(s, fd, func_name))+                goto fail;++            if (js_parse_assign_expr(s, TRUE))+                goto fail;++            if (func_kind != JS_FUNC_NORMAL)+                emit_op(s, OP_return_async);+            else+                emit_op(s, OP_return);++            if (!(fd->js_mode & JS_MODE_STRIP)) {+                /* save the function source code */+                /* the end of the function source code is after the last+                   token of the function source stored into s->last_ptr */+                fd->source_len = s->last_ptr - ptr;+                fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);+                if (!fd->source)+                    goto fail;+            }+            goto done;+        }+    }++    if (js_parse_expect(s, '{'))+        goto fail;++    if (js_parse_directives(s))+        goto fail;++    /* in strict_mode, check function and argument names */+    if (js_parse_function_check_names(s, fd, func_name))+        goto fail;++    while (s->token.val != '}') {+        if (js_parse_source_element(s))+            goto fail;+    }+    if (!(fd->js_mode & JS_MODE_STRIP)) {+        /* save the function source code */+        fd->source_len = s->buf_ptr - ptr;+        fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);+        if (!fd->source)+            goto fail;+    }++    if (next_token(s)) {+        /* consume the '}' */+        goto fail;+    }++    /* in case there is no return, add one */+    if (js_is_live_code(s)) {+        emit_return(s, FALSE);+    }+done:+    s->cur_func = fd->parent;++    /* create the function object */+    {+        int idx;+        JSAtom func_name = fd->func_name;++        /* the real object will be set at the end of the compilation */+        idx = cpool_add(s, JS_NULL);+        fd->parent_cpool_idx = idx;++        if (is_expr) {+            /* for constructors, no code needs to be generated here */+            if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&+                func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {+                /* OP_fclosure creates the function object from the bytecode+                   and adds the scope information */+                emit_op(s, OP_fclosure);+                emit_u32(s, idx);+                if (func_name == JS_ATOM_NULL) {+                    emit_op(s, OP_set_name);+                    emit_u32(s, JS_ATOM_NULL);+                }+            }+        } else if (func_type == JS_PARSE_FUNC_VAR) {+            emit_op(s, OP_fclosure);+            emit_u32(s, idx);+            if (create_func_var) {+                if (s->cur_func->is_global_var) {+                    JSHoistedDef *hf;+                    /* the global variable must be defined at the start of the+                       function */+                    hf = add_hoisted_def(ctx, s->cur_func, -1, func_name, -1, FALSE);+                    if (!hf)+                        goto fail;+                    /* it is considered as defined at the top level+                       (needed for annex B.3.3.4 and B.3.3.5+                       checks) */+                    hf->scope_level = 0; +                    hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);+                    /* store directly into global var, bypass lexical scope */+                    emit_op(s, OP_dup);+                    emit_op(s, OP_scope_put_var);+                    emit_atom(s, func_name);+                    emit_u16(s, 0);+                } else {+                    /* do not call define_var to bypass lexical scope check */+                    func_idx = find_var(ctx, s->cur_func, func_name);+                    if (func_idx < 0) {+                        func_idx = add_var(ctx, s->cur_func, func_name);+                        if (func_idx < 0)+                            goto fail;+                    }+                    /* store directly into local var, bypass lexical catch scope */+                    emit_op(s, OP_dup);+                    emit_op(s, OP_scope_put_var);+                    emit_atom(s, func_name);+                    emit_u16(s, 0);+                }+            }+            if (lexical_func_idx >= 0) {+                /* lexical variable will be initialized upon entering scope */+                s->cur_func->vars[lexical_func_idx].func_pool_or_scope_idx = idx;+                emit_op(s, OP_drop);+            } else {+                /* store function object into its lexical name */+                /* XXX: could use OP_put_loc directly */+                emit_op(s, OP_scope_put_var_init);+                emit_atom(s, func_name);+                emit_u16(s, s->cur_func->scope_level);+            }+        } else {+            if (!s->cur_func->is_global_var) {+                int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);++                if (var_idx < 0)+                    goto fail;+                /* the variable will be assigned at the top of the function */+                if (!add_hoisted_def(ctx, s->cur_func, idx, JS_ATOM_NULL, var_idx, FALSE))+                    goto fail;+            } else {+                JSAtom func_var_name;+                if (func_name == JS_ATOM_NULL)+                    func_var_name = JS_ATOM__default_; /* export default */+                else+                    func_var_name = func_name;+                /* the variable will be assigned at the top of the function */+                if (!add_hoisted_def(ctx, s->cur_func, idx, func_var_name, -1, FALSE))+                    goto fail;+                if (export_flag != JS_PARSE_EXPORT_NONE) {+                    if (!add_export_entry(s, s->cur_func->module, func_var_name,+                                          export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))+                        goto fail;+                }+            }+        }+    }+    return 0;+ fail:+    s->cur_func = fd->parent;+    js_free_function_def(ctx, fd);+    if (pfd)+        *pfd = NULL;+    return -1;+}++static __exception int js_parse_function_decl(JSParseState *s,+                                              JSParseFunctionEnum func_type,+                                              JSFunctionKindEnum func_kind,+                                              JSAtom func_name,+                                              const uint8_t *ptr,+                                              int function_line_num)+{+    return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,+                                   function_line_num, JS_PARSE_EXPORT_NONE,+                                   NULL);+}++static __exception int js_parse_program(JSParseState *s)+{+    JSFunctionDef *fd = s->cur_func;+    int idx;++    if (next_token(s))+        return -1;++    if (js_parse_directives(s))+        return -1;++    fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||+        (fd->eval_type == JS_EVAL_TYPE_MODULE) ||+        !(fd->js_mode & JS_MODE_STRICT);++    if (!s->is_module) {+        /* hidden variable for the return value */+        fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);+        if (idx < 0)+            return -1;+    }++    while (s->token.val != TOK_EOF) {+        if (js_parse_source_element(s))+            return -1;+    }++    if (!s->is_module) {+        /* return the value of the hidden variable eval_ret_idx  */+        emit_op(s, OP_get_loc);+        emit_u16(s, fd->eval_ret_idx);++        emit_op(s, OP_return);+    } else {+        emit_op(s, OP_return_undef);+    }++    return 0;+}++static void js_parse_init(JSContext *ctx, JSParseState *s,+                          const char *input, size_t input_len,+                          const char *filename)+{+    memset(s, 0, sizeof(*s));+    s->ctx = ctx;+    s->filename = filename;+    s->line_num = 1;+    s->buf_ptr = (const uint8_t *)input;+    s->buf_end = s->buf_ptr + input_len;+    s->token.val = ' ';+    s->token.line_num = 1;+}++static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,+                                       JSValueConst this_obj,+                                       JSVarRef **var_refs, JSStackFrame *sf)+{+    JSValue ret_val;+    uint32_t tag;++    tag = JS_VALUE_GET_TAG(fun_obj);+    if (tag == JS_TAG_FUNCTION_BYTECODE) {+        fun_obj = js_closure(ctx, fun_obj, var_refs, sf);+        ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);+    } else if (tag == JS_TAG_MODULE) {+        JSModuleDef *m;+        m = JS_VALUE_GET_PTR(fun_obj);+        /* the module refcount should be >= 2 */+        JS_FreeValue(ctx, fun_obj);+        if (js_create_module_function(ctx, m) < 0)+            goto fail;+        if (js_instantiate_module(ctx, m) < 0)+            goto fail;+        ret_val = js_evaluate_module(ctx, m);+        if (JS_IsException(ret_val)) {+        fail:+            js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);+            return JS_EXCEPTION;+        }+    } else {+        JS_FreeValue(ctx, fun_obj);+        ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");+    }+    return ret_val;+}++JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)+{+    return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);+}++static void skip_shebang(JSParseState *s)+{+    const uint8_t *p = s->buf_ptr;+    int c;++    if (p[0] == '#' && p[1] == '!') {+        p += 2;+        while (p < s->buf_end) {+            if (*p == '\n' || *p == '\r') {+                break;+            } else if (*p >= 0x80) {+                c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+                if (c == CP_LS || c == CP_PS) {+                    break;+                } else if (c == -1) {+                    p++; /* skip invalid UTF-8 */+                }+            } else {+                p++;+            }+        }+        s->buf_ptr = p;+    }+}++/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */+static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,+                                 const char *input, size_t input_len,+                                 const char *filename, int flags, int scope_idx)+{+    JSParseState s1, *s = &s1;+    int err, js_mode, eval_type;+    JSValue fun_obj, ret_val;+    JSStackFrame *sf;+    JSVarRef **var_refs;+    JSFunctionBytecode *b;+    JSFunctionDef *fd;+    JSModuleDef *m;++    js_parse_init(ctx, s, input, input_len, filename);+    skip_shebang(s);++    eval_type = flags & JS_EVAL_TYPE_MASK;+    m = NULL;+    if (eval_type == JS_EVAL_TYPE_DIRECT) {+        JSObject *p;+        sf = ctx->rt->current_stack_frame;+        assert(sf != NULL);+        assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);+        p = JS_VALUE_GET_OBJ(sf->cur_func);+        assert(js_class_has_bytecode(p->class_id));+        b = p->u.func.function_bytecode;+        var_refs = p->u.func.var_refs;+        js_mode = b->js_mode;+    } else {+        sf = NULL;+        b = NULL;+        var_refs = NULL;+        js_mode = 0;+        if (flags & JS_EVAL_FLAG_STRICT)+            js_mode |= JS_MODE_STRICT;+        if (flags & JS_EVAL_FLAG_STRIP)+            js_mode |= JS_MODE_STRIP;+        if (eval_type == JS_EVAL_TYPE_MODULE) {+            JSAtom module_name = JS_NewAtom(ctx, filename);+            if (module_name == JS_ATOM_NULL)+                return JS_EXCEPTION;+            m = js_new_module_def(ctx, module_name);+            if (!m)+                return JS_EXCEPTION;+            js_mode |= JS_MODE_STRICT;+        }+    }+    fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);+    if (!fd)+        goto fail1;+    s->cur_func = fd;+    fd->eval_type = eval_type;+    fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);+    fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);+    if (eval_type == JS_EVAL_TYPE_DIRECT) {+        fd->new_target_allowed = b->new_target_allowed;+        fd->super_call_allowed = b->super_call_allowed;+        fd->super_allowed = b->super_allowed;+        fd->arguments_allowed = b->arguments_allowed;+    } else {+        fd->new_target_allowed = FALSE;+        fd->super_call_allowed = FALSE;+        fd->super_allowed = FALSE;+        fd->arguments_allowed = TRUE;+    }+    fd->js_mode = js_mode;+    fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);+    if (b) {+        if (add_closure_variables(ctx, fd, b, scope_idx))+            goto fail;+    }+    fd->module = m;+    s->is_module = (m != NULL);+    s->allow_html_comments = !s->is_module;++    push_scope(s); /* body scope */++    err = js_parse_program(s);+    if (err) {+    fail:+        free_token(s, &s->token);+        js_free_function_def(ctx, fd);+        goto fail1;+    }++    /* create the function object and all the enclosed functions */+    fun_obj = js_create_function(ctx, fd);+    if (JS_IsException(fun_obj))+        goto fail1;+    /* Could add a flag to avoid resolution if necessary */+    if (m) {+        m->func_obj = fun_obj;+        if (js_resolve_module(ctx, m) < 0)+            goto fail1;+        fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+    }+    if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {+        ret_val = fun_obj;+    } else {+        ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);+    }+    return ret_val;+ fail1:+    /* XXX: should free all the unresolved dependencies */+    if (m)+        js_free_module_def(ctx, m);+    return JS_EXCEPTION;+}++/* the indirection is needed to make 'eval' optional */+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,+                               const char *input, size_t input_len,+                               const char *filename, int flags, int scope_idx)+{+    if (unlikely(!ctx->eval_internal)) {+        return JS_ThrowTypeError(ctx, "eval is not supported");+    }+    return ctx->eval_internal(ctx, this_obj, input, input_len, filename,+                              flags, scope_idx);+}++static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,+                             JSValueConst val, int flags, int scope_idx)+{+    JSValue ret;+    const char *str;+    size_t len;++    if (!JS_IsString(val))+        return JS_DupValue(ctx, val);+    str = JS_ToCStringLen(ctx, &len, val);+    if (!str)+        return JS_EXCEPTION;+    ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);+    JS_FreeCString(ctx, str);+    return ret;++}++JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,+                const char *filename, int eval_flags)+{+    int eval_type = eval_flags & JS_EVAL_TYPE_MASK;+    JSValue ret;++    assert(eval_type == JS_EVAL_TYPE_GLOBAL ||+           eval_type == JS_EVAL_TYPE_MODULE);+    ret = JS_EvalInternal(ctx, ctx->global_obj, input, input_len, filename,+                          eval_flags, -1);+    return ret;+}++int JS_ResolveModule(JSContext *ctx, JSValueConst obj)+{+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {+        JSModuleDef *m = JS_VALUE_GET_PTR(obj);+        if (js_resolve_module(ctx, m) < 0) {+            js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);+            return -1;+        }+    }+    return 0;+}++/*******************************************************************/+/* object list */++typedef struct {+    JSObject *obj;+    uint32_t hash_next; /* -1 if no next entry */+} JSObjectListEntry;++/* XXX: reuse it to optimize weak references */+typedef struct {+    JSObjectListEntry *object_tab;+    int object_count;+    int object_size;+    uint32_t *hash_table;+    uint32_t hash_size;+} JSObjectList;++static void js_object_list_init(JSObjectList *s)+{+    memset(s, 0, sizeof(*s));+}++static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)+{+    return ((uintptr_t)p * 3163) & (hash_size - 1);+}++static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,+                                 uint32_t new_hash_size)+{+    JSObjectListEntry *e;+    uint32_t i, h, *new_hash_table;++    new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);+    if (!new_hash_table)+        return -1;+    js_free(ctx, s->hash_table);+    s->hash_table = new_hash_table;+    s->hash_size = new_hash_size;+    +    for(i = 0; i < s->hash_size; i++) {+        s->hash_table[i] = -1;+    }+    for(i = 0; i < s->object_count; i++) {+        e = &s->object_tab[i];+        h = js_object_list_get_hash(e->obj, s->hash_size); +        e->hash_next = s->hash_table[h];+        s->hash_table[h] = i;+    }+    return 0;+}++/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if+   memory error */+static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)+{+    JSObjectListEntry *e;+    uint32_t h, new_hash_size;+    +    if (js_resize_array(ctx, (void *)&s->object_tab,+                        sizeof(s->object_tab[0]),+                        &s->object_size, s->object_count + 1))+        return -1;+    if (unlikely((s->object_count + 1) >= s->hash_size)) {+        new_hash_size = max_uint32(s->hash_size, 4);+        while (new_hash_size <= s->object_count)+            new_hash_size *= 2;+        if (js_object_list_resize_hash(ctx, s, new_hash_size))+            return -1;+    }+    e = &s->object_tab[s->object_count++];+    h = js_object_list_get_hash(obj, s->hash_size); +    e->obj = obj;+    e->hash_next = s->hash_table[h];+    s->hash_table[h] = s->object_count - 1;+    return 0;+}++/* return -1 if not present or the object index */+static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)+{+    JSObjectListEntry *e;+    uint32_t h, p;++    /* must test empty size because there is no hash table */+    if (s->object_count == 0)+        return -1;+    h = js_object_list_get_hash(obj, s->hash_size); +    p = s->hash_table[h];+    while (p != -1) {+        e = &s->object_tab[p];+        if (e->obj == obj)+            return p;+        p = e->hash_next;+    }+    return -1;+}++static void js_object_list_end(JSContext *ctx, JSObjectList *s)+{+    js_free(ctx, s->object_tab);+    js_free(ctx, s->hash_table);+}++/*******************************************************************/+/* binary object writer & reader */++typedef enum BCTagEnum {+    BC_TAG_NULL = 1,+    BC_TAG_UNDEFINED,+    BC_TAG_BOOL_FALSE,+    BC_TAG_BOOL_TRUE,+    BC_TAG_INT32,+    BC_TAG_FLOAT64,+    BC_TAG_STRING,+    BC_TAG_OBJECT,+    BC_TAG_ARRAY,+    BC_TAG_BIG_INT,+    BC_TAG_BIG_FLOAT,+    BC_TAG_BIG_DECIMAL,+    BC_TAG_TEMPLATE_OBJECT,+    BC_TAG_FUNCTION_BYTECODE,+    BC_TAG_MODULE,+    BC_TAG_TYPED_ARRAY,+    BC_TAG_ARRAY_BUFFER,+    BC_TAG_SHARED_ARRAY_BUFFER,+    BC_TAG_DATE,+    BC_TAG_OBJECT_VALUE,+    BC_TAG_OBJECT_REFERENCE,+} BCTagEnum;++#ifdef CONFIG_BIGNUM+#define BC_BASE_VERSION 2+#else+#define BC_BASE_VERSION 1+#endif+#define BC_BE_VERSION 0x40+#ifdef WORDS_BIGENDIAN+#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)+#else+#define BC_VERSION BC_BASE_VERSION+#endif++typedef struct BCWriterState {+    JSContext *ctx;+    DynBuf dbuf;+    BOOL byte_swap : 8;+    BOOL allow_bytecode : 8;+    BOOL allow_sab : 8;+    BOOL allow_reference : 8;+    uint32_t first_atom;+    uint32_t *atom_to_idx;+    int atom_to_idx_size;+    JSAtom *idx_to_atom;+    int idx_to_atom_count;+    int idx_to_atom_size;+    uint8_t **sab_tab;+    int sab_tab_len;+    int sab_tab_size;+    /* list of referenced objects (used if allow_reference = TRUE) */+    JSObjectList object_list;+} BCWriterState;++#ifdef DUMP_READ_OBJECT+static const char * const bc_tag_str[] = {+    "invalid",+    "null",+    "undefined",+    "false",+    "true",+    "int32",+    "float64",+    "string",+    "object",+    "array",+    "bigint",+    "bigfloat",+    "bigdecimal",+    "template",+    "function",+    "module",+    "TypedArray",+    "ArrayBuffer",+    "SharedArrayBuffer",+    "Date",+    "ObjectValue",+    "ObjectReference",+};+#endif++static void bc_put_u8(BCWriterState *s, uint8_t v)+{+    dbuf_putc(&s->dbuf, v);+}++static void bc_put_u16(BCWriterState *s, uint16_t v)+{+    if (s->byte_swap)+        v = bswap16(v);+    dbuf_put_u16(&s->dbuf, v);+}++static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)+{+    if (s->byte_swap)+        v = bswap32(v);+    dbuf_put_u32(&s->dbuf, v);+}++static void bc_put_u64(BCWriterState *s, uint64_t v)+{+    if (s->byte_swap)+        v = bswap64(v);+    dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));+}++static void bc_put_leb128(BCWriterState *s, uint32_t v)+{+    dbuf_put_leb128(&s->dbuf, v);+}++static void bc_put_sleb128(BCWriterState *s, int32_t v)+{+    dbuf_put_sleb128(&s->dbuf, v);+}++static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)+{+    *pflags = *pflags | (val << *pidx);+    *pidx += n;+}++static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)+{+    uint32_t v;++    if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {+        *pres = atom;+        return 0;+    }+    atom -= s->first_atom;+    if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {+        *pres = s->atom_to_idx[atom];+        return 0;+    }+    if (atom >= s->atom_to_idx_size) {+        int old_size, i;+        old_size = s->atom_to_idx_size;+        if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,+                            sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,+                            atom + 1))+            return -1;+        /* XXX: could add a specific js_resize_array() function to do it */+        for(i = old_size; i < s->atom_to_idx_size; i++)+            s->atom_to_idx[i] = 0;+    }+    if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,+                        sizeof(s->idx_to_atom[0]),+                        &s->idx_to_atom_size, s->idx_to_atom_count + 1))+        goto fail;++    v = s->idx_to_atom_count++;+    s->idx_to_atom[v] = atom + s->first_atom;+    v += s->first_atom;+    s->atom_to_idx[atom] = v;+    *pres = v;+    return 0;+ fail:+    *pres = 0;+    return -1;+}++static int bc_put_atom(BCWriterState *s, JSAtom atom)+{+    uint32_t v;++    if (__JS_AtomIsTaggedInt(atom)) {+        v = (__JS_AtomToUInt32(atom) << 1) | 1;+    } else {+        if (bc_atom_to_idx(s, &v, atom))+            return -1;+        v <<= 1;+    }+    bc_put_leb128(s, v);+    return 0;+}++static void bc_byte_swap(uint8_t *bc_buf, int bc_len)+{+    int pos, len, op, fmt;++    pos = 0;+    while (pos < bc_len) {+        op = bc_buf[pos];+        len = short_opcode_info(op).size;+        fmt = short_opcode_info(op).fmt;+        switch(fmt) {+        case OP_FMT_u16:+        case OP_FMT_i16:+        case OP_FMT_label16:+        case OP_FMT_npop:+        case OP_FMT_loc:+        case OP_FMT_arg:+        case OP_FMT_var_ref:+            put_u16(bc_buf + pos + 1,+                    bswap16(get_u16(bc_buf + pos + 1)));+            break;+        case OP_FMT_i32:+        case OP_FMT_u32:+        case OP_FMT_const:+        case OP_FMT_label:+        case OP_FMT_atom:+        case OP_FMT_atom_u8:+            put_u32(bc_buf + pos + 1,+                    bswap32(get_u32(bc_buf + pos + 1)));+            break;+        case OP_FMT_atom_u16:+        case OP_FMT_label_u16:+            put_u32(bc_buf + pos + 1,+                    bswap32(get_u32(bc_buf + pos + 1)));+            put_u16(bc_buf + pos + 1 + 4,+                    bswap16(get_u16(bc_buf + pos + 1 + 4)));+            break;+        case OP_FMT_atom_label_u8:+        case OP_FMT_atom_label_u16:+            put_u32(bc_buf + pos + 1,+                    bswap32(get_u32(bc_buf + pos + 1)));+            put_u32(bc_buf + pos + 1 + 4,+                    bswap32(get_u32(bc_buf + pos + 1 + 4)));+            if (fmt == OP_FMT_atom_label_u16) {+                put_u16(bc_buf + pos + 1 + 4 + 4,+                        bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));+            }+            break;+        case OP_FMT_npop_u16:+            put_u16(bc_buf + pos + 1,+                    bswap16(get_u16(bc_buf + pos + 1)));+            put_u16(bc_buf + pos + 1 + 2,+                    bswap16(get_u16(bc_buf + pos + 1 + 2)));+            break;+        default:+            break;+        }+        pos += len;+    }+}++static int JS_WriteFunctionBytecode(BCWriterState *s,+                                    const uint8_t *bc_buf1, int bc_len)+{+    int pos, len, op;+    JSAtom atom;+    uint8_t *bc_buf;+    uint32_t val;++    bc_buf = js_malloc(s->ctx, bc_len);+    if (!bc_buf)+        return -1;+    memcpy(bc_buf, bc_buf1, bc_len);++    pos = 0;+    while (pos < bc_len) {+        op = bc_buf[pos];+        len = short_opcode_info(op).size;+        switch(short_opcode_info(op).fmt) {+        case OP_FMT_atom:+        case OP_FMT_atom_u8:+        case OP_FMT_atom_u16:+        case OP_FMT_atom_label_u8:+        case OP_FMT_atom_label_u16:+            atom = get_u32(bc_buf + pos + 1);+            if (bc_atom_to_idx(s, &val, atom))+                goto fail;+            put_u32(bc_buf + pos + 1, val);+            break;+        default:+            break;+        }+        pos += len;+    }++    if (s->byte_swap)+        bc_byte_swap(bc_buf, bc_len);++    dbuf_put(&s->dbuf, bc_buf, bc_len);++    js_free(s->ctx, bc_buf);+    return 0;+ fail:+    js_free(s->ctx, bc_buf);+    return -1;+}++static void JS_WriteString(BCWriterState *s, JSString *p)+{+    int i;+    bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);+    if (p->is_wide_char) {+        for(i = 0; i < p->len; i++)+            bc_put_u16(s, p->u.str16[i]);+    } else {+        dbuf_put(&s->dbuf, p->u.str8, p->len);+    }+}++#ifdef CONFIG_BIGNUM+static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)+{+    uint32_t tag, tag1;+    int64_t e;+    JSBigFloat *bf = JS_VALUE_GET_PTR(obj);+    bf_t *a = &bf->num;+    size_t len, i, n1, j;+    limb_t v;++    tag = JS_VALUE_GET_TAG(obj);+    switch(tag) {+    case JS_TAG_BIG_INT:+        tag1 = BC_TAG_BIG_INT;+        break;+    case JS_TAG_BIG_FLOAT:+        tag1 = BC_TAG_BIG_FLOAT;+        break;+    case JS_TAG_BIG_DECIMAL:+        tag1 = BC_TAG_BIG_DECIMAL;+        break;+    default:+        abort();+    }+    bc_put_u8(s, tag1);++    /* sign + exponent */+    if (a->expn == BF_EXP_ZERO)+        e = 0;+    else if (a->expn == BF_EXP_INF)+        e = 1;+    else if (a->expn == BF_EXP_NAN)+        e = 2;+    else if (a->expn >= 0)+        e = a->expn + 3;+    else+        e = a->expn;+    e = (e << 1) | a->sign;+    if (e < INT32_MIN || e > INT32_MAX) {+        JS_ThrowInternalError(s->ctx, "bignum exponent is too large");+        return -1;+    }+    bc_put_sleb128(s, e);++    /* mantissa */+    if (a->len != 0) {+        if (tag != JS_TAG_BIG_DECIMAL) {+            i = 0;+            while (i < a->len && a->tab[i] == 0)+                i++;+            assert(i < a->len);+            v = a->tab[i];+            n1 = sizeof(limb_t);+            while ((v & 0xff) == 0) {+                n1--;+                v >>= 8;+            }+            i++;+            len = (a->len - i) * sizeof(limb_t) + n1;+            if (len > INT32_MAX) {+                JS_ThrowInternalError(s->ctx, "bignum is too large");+                return -1;+            }+            bc_put_leb128(s, len);+            /* always saved in byte based little endian representation */+            for(j = 0; j < n1; j++) {+                dbuf_putc(&s->dbuf, v >> (j * 8));+            }+            for(; i < a->len; i++) {+                limb_t v = a->tab[i];+#if LIMB_BITS == 32+#ifdef WORDS_BIGENDIAN+                v = bswap32(v);+#endif+                dbuf_put_u32(&s->dbuf, v);+#else+#ifdef WORDS_BIGENDIAN+                v = bswap64(v);+#endif+                dbuf_put_u64(&s->dbuf, v);+#endif+            }+        } else {+            int bpos, d;+            uint8_t v8;+            size_t i0;+            +            /* little endian BCD */+            i = 0;+            while (i < a->len && a->tab[i] == 0)+                i++;+            assert(i < a->len);+            len = a->len * LIMB_DIGITS;+            v = a->tab[i];+            j = 0;+            while ((v % 10) == 0) {+                j++;+                v /= 10;+            }+            len -= j;+            assert(len > 0);+            if (len > INT32_MAX) {+                JS_ThrowInternalError(s->ctx, "bignum is too large");+                return -1;+            }+            bc_put_leb128(s, len);+            +            bpos = 0;+            v8 = 0;+            i0 = i;+            for(; i < a->len; i++) {+                if (i != i0) {+                    v = a->tab[i];+                    j = 0;+                }+                for(; j < LIMB_DIGITS; j++) {+                    d = v % 10;+                    v /= 10;+                    if (bpos == 0) {+                        v8 = d;+                        bpos = 1;+                    } else {+                        dbuf_putc(&s->dbuf, v8 | (d << 4));+                        bpos = 0;+                    }+                }+            }+            /* flush the last digit */+            if (bpos) {+                dbuf_putc(&s->dbuf, v8);+            }+        }+    }+    return 0;+}+#endif /* CONFIG_BIGNUM */++static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);++static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)+{+    JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);+    uint32_t flags;+    int idx, i;+    +    bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);+    flags = idx = 0;+    bc_set_flags(&flags, &idx, b->has_prototype, 1);+    bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);+    bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);+    bc_set_flags(&flags, &idx, b->need_home_object, 1);+    bc_set_flags(&flags, &idx, b->func_kind, 2);+    bc_set_flags(&flags, &idx, b->new_target_allowed, 1);+    bc_set_flags(&flags, &idx, b->super_call_allowed, 1);+    bc_set_flags(&flags, &idx, b->super_allowed, 1);+    bc_set_flags(&flags, &idx, b->arguments_allowed, 1);+    bc_set_flags(&flags, &idx, b->has_debug, 1);+    bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);+    assert(idx <= 16);+    bc_put_u16(s, flags);+    bc_put_u8(s, b->js_mode);+    bc_put_atom(s, b->func_name);+    +    bc_put_leb128(s, b->arg_count);+    bc_put_leb128(s, b->var_count);+    bc_put_leb128(s, b->defined_arg_count);+    bc_put_leb128(s, b->stack_size);+    bc_put_leb128(s, b->closure_var_count);+    bc_put_leb128(s, b->cpool_count);+    bc_put_leb128(s, b->byte_code_len);+    if (b->vardefs) {+        bc_put_leb128(s, b->arg_count + b->var_count);+        for(i = 0; i < b->arg_count + b->var_count; i++) {+            JSVarDef *vd = &b->vardefs[i];+            bc_put_atom(s, vd->var_name);+            bc_put_leb128(s, vd->scope_level);+            bc_put_leb128(s, vd->scope_next + 1);+            flags = idx = 0;+            bc_set_flags(&flags, &idx, vd->var_kind, 4);+            bc_set_flags(&flags, &idx, vd->is_func_var, 1);+            bc_set_flags(&flags, &idx, vd->is_const, 1);+            bc_set_flags(&flags, &idx, vd->is_lexical, 1);+            bc_set_flags(&flags, &idx, vd->is_captured, 1);+            assert(idx <= 8);+            bc_put_u8(s, flags);+        }+    } else {+        bc_put_leb128(s, 0);+    }+    +    for(i = 0; i < b->closure_var_count; i++) {+        JSClosureVar *cv = &b->closure_var[i];+        bc_put_atom(s, cv->var_name);+        bc_put_leb128(s, cv->var_idx);+        flags = idx = 0;+        bc_set_flags(&flags, &idx, cv->is_local, 1);+        bc_set_flags(&flags, &idx, cv->is_arg, 1);+        bc_set_flags(&flags, &idx, cv->is_const, 1);+        bc_set_flags(&flags, &idx, cv->is_lexical, 1);+        bc_set_flags(&flags, &idx, cv->var_kind, 4);+        assert(idx <= 8);+        bc_put_u8(s, flags);+    }+    +    if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))+        goto fail;+    +    if (b->has_debug) {+        bc_put_atom(s, b->debug.filename);+        bc_put_leb128(s, b->debug.line_num);+        bc_put_leb128(s, b->debug.pc2line_len);+        dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);+    }+    +    for(i = 0; i < b->cpool_count; i++) {+        if (JS_WriteObjectRec(s, b->cpool[i]))+            goto fail;+    }+    return 0;+ fail:+    return -1;+}++static int JS_WriteModule(BCWriterState *s, JSValueConst obj)+{+    JSModuleDef *m = JS_VALUE_GET_PTR(obj);+    int i;+    +    bc_put_u8(s, BC_TAG_MODULE);+    bc_put_atom(s, m->module_name);+    +    bc_put_leb128(s, m->req_module_entries_count);+    for(i = 0; i < m->req_module_entries_count; i++) {+        JSReqModuleEntry *rme = &m->req_module_entries[i];+        bc_put_atom(s, rme->module_name);+    }+    +    bc_put_leb128(s, m->export_entries_count);+    for(i = 0; i < m->export_entries_count; i++) {+        JSExportEntry *me = &m->export_entries[i];+        bc_put_u8(s, me->export_type);+        if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+            bc_put_leb128(s, me->u.local.var_idx);+        } else {+            bc_put_leb128(s, me->u.req_module_idx);+            bc_put_atom(s, me->local_name);+        }+        bc_put_atom(s, me->export_name);+    }+    +    bc_put_leb128(s, m->star_export_entries_count);+    for(i = 0; i < m->star_export_entries_count; i++) {+        JSStarExportEntry *se = &m->star_export_entries[i];+        bc_put_leb128(s, se->req_module_idx);+    }+    +    bc_put_leb128(s, m->import_entries_count);+    for(i = 0; i < m->import_entries_count; i++) {+        JSImportEntry *mi = &m->import_entries[i];+        bc_put_leb128(s, mi->var_idx);+        bc_put_atom(s, mi->import_name);+        bc_put_leb128(s, mi->req_module_idx);+    }+    +    if (JS_WriteObjectRec(s, m->func_obj))+        goto fail;+    return 0;+ fail:+    return -1;+}++static int JS_WriteArray(BCWriterState *s, JSValueConst obj)+{+    JSObject *p = JS_VALUE_GET_OBJ(obj);+    uint32_t i, len;+    JSValue val;+    int ret;+    BOOL is_template;+    +    if (s->allow_bytecode && !p->extensible) {+        /* not extensible array: we consider it is a+           template when we are saving bytecode */+        bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);+        is_template = TRUE;+    } else {+        bc_put_u8(s, BC_TAG_ARRAY);+        is_template = FALSE;+    }+    if (js_get_length32(s->ctx, &len, obj))+        goto fail1;+    bc_put_leb128(s, len);+    for(i = 0; i < len; i++) {+        val = JS_GetPropertyUint32(s->ctx, obj, i);+        if (JS_IsException(val))+            goto fail1;+        ret = JS_WriteObjectRec(s, val);+        JS_FreeValue(s->ctx, val);+        if (ret)+            goto fail1;+    }+    if (is_template) {+        val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);+        if (JS_IsException(val))+            goto fail1;+        ret = JS_WriteObjectRec(s, val);+        JS_FreeValue(s->ctx, val);+        if (ret)+            goto fail1;+    }+    return 0;+ fail1:+    return -1;+}++static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)+{+    JSObject *p = JS_VALUE_GET_OBJ(obj);+    uint32_t i, prop_count;+    JSShape *sh;+    JSShapeProperty *pr;+    int pass;+    JSAtom atom;++    bc_put_u8(s, BC_TAG_OBJECT);+    prop_count = 0;+    sh = p->shape;+    for(pass = 0; pass < 2; pass++) {+        if (pass == 1)+            bc_put_leb128(s, prop_count);+        for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {+            atom = pr->atom;+            if (atom != JS_ATOM_NULL &&+                JS_AtomIsString(s->ctx, atom) &&+                (pr->flags & JS_PROP_ENUMERABLE)) {+                if (pr->flags & JS_PROP_TMASK) {+                    JS_ThrowTypeError(s->ctx, "only value properties are supported");+                    goto fail;+                }+                if (pass == 0) {+                    prop_count++;+                } else {+                    bc_put_atom(s, atom);+                    if (JS_WriteObjectRec(s, p->prop[i].u.value))+                        goto fail;+                }+            }+        }+    }+    return 0;+ fail:+    return -1;+}++static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)+{+    JSObject *p = JS_VALUE_GET_OBJ(obj);+    JSTypedArray *ta = p->u.typed_array;++    bc_put_u8(s, BC_TAG_TYPED_ARRAY);+    bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);+    bc_put_leb128(s, p->u.array.count);+    bc_put_leb128(s, ta->offset);+    if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))+        return -1;+    return 0;+}++static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)+{+    JSObject *p = JS_VALUE_GET_OBJ(obj);+    JSArrayBuffer *abuf = p->u.array_buffer;+    if (abuf->detached) {+        JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);+        return -1;+    }+    bc_put_u8(s, BC_TAG_ARRAY_BUFFER);+    bc_put_leb128(s, abuf->byte_length);+    dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);+    return 0;+}++static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)+{+    JSObject *p = JS_VALUE_GET_OBJ(obj);+    JSArrayBuffer *abuf = p->u.array_buffer;+    assert(!abuf->detached); /* SharedArrayBuffer are never detached */+    bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);+    bc_put_leb128(s, abuf->byte_length);+    bc_put_u64(s, (uintptr_t)abuf->data);+    if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),+                        &s->sab_tab_size, s->sab_tab_len + 1))+        return -1;+    /* keep the SAB pointer so that the user can clone it or free it */+    s->sab_tab[s->sab_tab_len++] = abuf->data;+    return 0;+}++static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)+{+    uint32_t tag;++    if (js_check_stack_overflow(s->ctx->rt, 0)) {+        JS_ThrowStackOverflow(s->ctx);+        return -1;+    }++    tag = JS_VALUE_GET_NORM_TAG(obj);+    switch(tag) {+    case JS_TAG_NULL:+        bc_put_u8(s, BC_TAG_NULL);+        break;+    case JS_TAG_UNDEFINED:+        bc_put_u8(s, BC_TAG_UNDEFINED);+        break;+    case JS_TAG_BOOL:+        bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));+        break;+    case JS_TAG_INT:+        bc_put_u8(s, BC_TAG_INT32);+        bc_put_sleb128(s, JS_VALUE_GET_INT(obj));+        break;+    case JS_TAG_FLOAT64:+        {+            JSFloat64Union u;+            bc_put_u8(s, BC_TAG_FLOAT64);+            u.d = JS_VALUE_GET_FLOAT64(obj);+            bc_put_u64(s, u.u64);+        }+        break;+    case JS_TAG_STRING:+        {+            JSString *p = JS_VALUE_GET_STRING(obj);+            bc_put_u8(s, BC_TAG_STRING);+            JS_WriteString(s, p);+        }+        break;+    case JS_TAG_FUNCTION_BYTECODE:+        if (!s->allow_bytecode)+            goto invalid_tag;+        if (JS_WriteFunctionTag(s, obj))+            goto fail;+        break;+    case JS_TAG_MODULE:+        if (!s->allow_bytecode)+            goto invalid_tag;+        if (JS_WriteModule(s, obj))+            goto fail;+        break;+    case JS_TAG_OBJECT:+        {+            JSObject *p = JS_VALUE_GET_OBJ(obj);+            int ret, idx;+            +            if (s->allow_reference) {+                idx = js_object_list_find(s->ctx, &s->object_list, p);+                if (idx >= 0) {+                    bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);+                    bc_put_leb128(s, idx);+                    break;+                } else {+                    if (js_object_list_add(s->ctx, &s->object_list, p))+                        goto fail;+                }+            } else {+                if (p->tmp_mark) {+                    JS_ThrowTypeError(s->ctx, "circular reference");+                    goto fail;+                }+                p->tmp_mark = 1;+            }+            switch(p->class_id) {+            case JS_CLASS_ARRAY:+                ret = JS_WriteArray(s, obj);+                break;+            case JS_CLASS_OBJECT:+                ret = JS_WriteObjectTag(s, obj);+                break;+            case JS_CLASS_ARRAY_BUFFER:+                ret = JS_WriteArrayBuffer(s, obj);+                break;+            case JS_CLASS_SHARED_ARRAY_BUFFER:+                if (!s->allow_sab)+                    goto invalid_tag;+                ret = JS_WriteSharedArrayBuffer(s, obj);+                break;+            case JS_CLASS_DATE:+                bc_put_u8(s, BC_TAG_DATE);+                ret = JS_WriteObjectRec(s, p->u.object_data);+                break;+            case JS_CLASS_NUMBER:+            case JS_CLASS_STRING:+            case JS_CLASS_BOOLEAN:+#ifdef CONFIG_BIGNUM+            case JS_CLASS_BIG_INT:+            case JS_CLASS_BIG_FLOAT:+            case JS_CLASS_BIG_DECIMAL:+#endif+                bc_put_u8(s, BC_TAG_OBJECT_VALUE);+                ret = JS_WriteObjectRec(s, p->u.object_data);+                break;+            default:+                if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                    ret = JS_WriteTypedArray(s, obj);+                } else {+                    JS_ThrowTypeError(s->ctx, "unsupported object class");+                    ret = -1;+                }+                break;+            }+            p->tmp_mark = 0;+            if (ret)+                goto fail;+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+    case JS_TAG_BIG_DECIMAL:+        if (JS_WriteBigNum(s, obj))+            goto fail;+        break;+#endif+    default:+    invalid_tag:+        JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);+        goto fail;+    }+    return 0;++ fail:+    return -1;+}++/* create the atom table */+static int JS_WriteObjectAtoms(BCWriterState *s)+{+    JSRuntime *rt = s->ctx->rt;+    DynBuf dbuf1;+    int i, atoms_size;+    uint8_t version;++    dbuf1 = s->dbuf;+    js_dbuf_init(s->ctx, &s->dbuf);++    version = BC_VERSION;+    if (s->byte_swap)+        version ^= BC_BE_VERSION;+    bc_put_u8(s, version);++    bc_put_leb128(s, s->idx_to_atom_count);+    for(i = 0; i < s->idx_to_atom_count; i++) {+        JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];+        JS_WriteString(s, p);+    }+    /* XXX: should check for OOM in above phase */++    /* move the atoms at the start */+    /* XXX: could just append dbuf1 data, but it uses more memory if+       dbuf1 is larger than dbuf */+    atoms_size = s->dbuf.size;+    if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))+        goto fail;+    memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);+    memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);+    dbuf1.size += atoms_size;+    dbuf_free(&s->dbuf);+    s->dbuf = dbuf1;+    return 0;+ fail:+    dbuf_free(&dbuf1);+    return -1;+}++uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,+                         int flags, uint8_t ***psab_tab, size_t *psab_tab_len)+{+    BCWriterState ss, *s = &ss;++    memset(s, 0, sizeof(*s));+    s->ctx = ctx;+    /* XXX: byte swapped output is untested */+    s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);+    s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);+    s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);+    s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);+    /* XXX: could use a different version when bytecode is included */+    if (s->allow_bytecode)+        s->first_atom = JS_ATOM_END;+    else+        s->first_atom = 1;+    js_dbuf_init(ctx, &s->dbuf);+    js_object_list_init(&s->object_list);+    +    if (JS_WriteObjectRec(s, obj))+        goto fail;+    if (JS_WriteObjectAtoms(s))+        goto fail;+    js_object_list_end(ctx, &s->object_list);+    js_free(ctx, s->atom_to_idx);+    js_free(ctx, s->idx_to_atom);+    *psize = s->dbuf.size;+    if (psab_tab)+        *psab_tab = s->sab_tab;+    if (psab_tab_len)+        *psab_tab_len = s->sab_tab_len;+    return s->dbuf.buf;+ fail:+    js_object_list_end(ctx, &s->object_list);+    js_free(ctx, s->atom_to_idx);+    js_free(ctx, s->idx_to_atom);+    dbuf_free(&s->dbuf);+    *psize = 0;+    if (psab_tab)+        *psab_tab = NULL;+    if (psab_tab_len)+        *psab_tab_len = 0;+    return NULL;+}++uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,+                        int flags)+{+    return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);+}++typedef struct BCReaderState {+    JSContext *ctx;+    const uint8_t *buf_start, *ptr, *buf_end;+    uint32_t first_atom;+    uint32_t idx_to_atom_count;+    JSAtom *idx_to_atom;+    int error_state;+    BOOL allow_sab : 8;+    BOOL allow_bytecode : 8;+    BOOL is_rom_data : 8;+    BOOL allow_reference : 8;+    /* object references */+    JSObject **objects;+    int objects_count;+    int objects_size;+    +#ifdef DUMP_READ_OBJECT+    const uint8_t *ptr_last;+    int level;+#endif+} BCReaderState;++#ifdef DUMP_READ_OBJECT+static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {+    va_list ap;+    int i, n, n0;++    if (!s->ptr_last)+        s->ptr_last = s->buf_start;++    n = n0 = 0;+    if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {+        n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));+        n += n0;+    }+    for (i = 0; s->ptr_last < s->ptr; i++) {+        if ((i & 7) == 0 && i > 0) {+            printf("\n%*s", n0, "");+            n = n0;+        }+        n += printf(" %02x", *s->ptr_last++);+    }+    if (*fmt == '}')+        s->level--;+    if (n < 32 + s->level * 2) {+        printf("%*s", 32 + s->level * 2 - n, "");+    }+    va_start(ap, fmt);+    vfprintf(stdout, fmt, ap);+    va_end(ap);+    if (strchr(fmt, '{'))+        s->level++;+}+#else+#define bc_read_trace(...)+#endif++static int bc_read_error_end(BCReaderState *s)+{+    if (!s->error_state) {+        JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");+    }+    return s->error_state = -1;+}++static int bc_get_u8(BCReaderState *s, uint8_t *pval)+{+    if (unlikely(s->buf_end - s->ptr < 1)) {+        *pval = 0; /* avoid warning */+        return bc_read_error_end(s);+    }+    *pval = *s->ptr++;+    return 0;+}++static int bc_get_u16(BCReaderState *s, uint16_t *pval)+{+    if (unlikely(s->buf_end - s->ptr < 2)) {+        *pval = 0; /* avoid warning */+        return bc_read_error_end(s);+    }+    *pval = get_u16(s->ptr);+    s->ptr += 2;+    return 0;+}++static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)+{+    if (unlikely(s->buf_end - s->ptr < 4)) {+        *pval = 0; /* avoid warning */+        return bc_read_error_end(s);+    }+    *pval = get_u32(s->ptr);+    s->ptr += 4;+    return 0;+}++static int bc_get_u64(BCReaderState *s, uint64_t *pval)+{+    if (unlikely(s->buf_end - s->ptr < 8)) {+        *pval = 0; /* avoid warning */+        return bc_read_error_end(s);+    }+    *pval = get_u64(s->ptr);+    s->ptr += 8;+    return 0;+}++static int bc_get_leb128(BCReaderState *s, uint32_t *pval)+{+    int ret;+    ret = get_leb128(pval, s->ptr, s->buf_end);+    if (unlikely(ret < 0))+        return bc_read_error_end(s);+    s->ptr += ret;+    return 0;+}++static int bc_get_sleb128(BCReaderState *s, int32_t *pval)+{+    int ret;+    ret = get_sleb128(pval, s->ptr, s->buf_end);+    if (unlikely(ret < 0))+        return bc_read_error_end(s);+    s->ptr += ret;+    return 0;+}++/* XXX: used to read an `int` with a positive value */+static int bc_get_leb128_int(BCReaderState *s, int *pval)+{+    return bc_get_leb128(s, (uint32_t *)pval);+}++static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)+{+    uint32_t val;+    if (bc_get_leb128(s, &val)) {+        *pval = 0;+        return -1;+    }+    *pval = val;+    return 0;+}++static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)+{+    if (buf_len != 0) {+        if (unlikely(!buf || s->buf_end - s->ptr < buf_len))+            return bc_read_error_end(s);+        memcpy(buf, s->ptr, buf_len);+        s->ptr += buf_len;+    }+    return 0;+}++static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)+{+    JSAtom atom;++    if (__JS_AtomIsTaggedInt(idx)) {+        atom = idx;+    } else if (idx < s->first_atom) {+        atom = JS_DupAtom(s->ctx, idx);+    } else {+        idx -= s->first_atom;+        if (idx >= s->idx_to_atom_count) {+            JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",+                                (unsigned int)(s->ptr - s->buf_start));+            *patom = JS_ATOM_NULL;+            return s->error_state = -1;+        }+        atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);+    }+    *patom = atom;+    return 0;+}++static int bc_get_atom(BCReaderState *s, JSAtom *patom)+{+    uint32_t v;+    if (bc_get_leb128(s, &v))+        return -1;+    if (v & 1) {+        *patom = __JS_AtomFromUInt32(v >> 1);+        return 0;+    } else {+        return bc_idx_to_atom(s, patom, v >> 1);+    }+}++static JSString *JS_ReadString(BCReaderState *s)+{+    uint32_t len;+    size_t size;+    BOOL is_wide_char;+    JSString *p;++    if (bc_get_leb128(s, &len))+        return NULL;+    is_wide_char = len & 1;+    len >>= 1;+    p = js_alloc_string(s->ctx, len, is_wide_char);+    if (!p) {+        s->error_state = -1;+        return NULL;+    }+    size = (size_t)len << is_wide_char;+    if ((s->buf_end - s->ptr) < size) {+        bc_read_error_end(s);+        js_free_string(s->ctx->rt, p);+        return NULL;+    }+    memcpy(p->u.str8, s->ptr, size);+    s->ptr += size;+    if (!is_wide_char) {+        p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */+    }+#ifdef DUMP_READ_OBJECT+    JS_DumpString(s->ctx->rt, p); printf("\n");+#endif+    return p;+}++static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)+{+    uint32_t val;+    /* XXX: this does not work for n == 32 */+    val = (flags >> *pidx) & ((1U << n) - 1);+    *pidx += n;+    return val;+}++static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,+                                   int byte_code_offset, uint32_t bc_len)+{+    uint8_t *bc_buf;+    int pos, len, op;+    JSAtom atom;+    uint32_t idx;++    if (s->is_rom_data) {+        /* directly use the input buffer */+        if (unlikely(s->buf_end - s->ptr < bc_len))+            return bc_read_error_end(s);+        bc_buf = (uint8_t *)s->ptr;+        s->ptr += bc_len;+    } else {+        bc_buf = (void *)((uint8_t*)b + byte_code_offset);+        if (bc_get_buf(s, bc_buf, bc_len))+            return -1;+    }+    b->byte_code_buf = bc_buf;++    pos = 0;+    while (pos < bc_len) {+        op = bc_buf[pos];+        len = short_opcode_info(op).size;+        switch(short_opcode_info(op).fmt) {+        case OP_FMT_atom:+        case OP_FMT_atom_u8:+        case OP_FMT_atom_u16:+        case OP_FMT_atom_label_u8:+        case OP_FMT_atom_label_u16:+            idx = get_u32(bc_buf + pos + 1);+            if (s->is_rom_data) {+                /* just increment the reference count of the atom */+                JS_DupAtom(s->ctx, (JSAtom)idx);+            } else {+                if (bc_idx_to_atom(s, &atom, idx)) {+                    /* Note: the atoms will be freed up to this position */+                    b->byte_code_len = pos;+                    return -1;+                }+                put_u32(bc_buf + pos + 1, atom);+#ifdef DUMP_READ_OBJECT+                bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");+#endif+            }+            break;+        default:+            break;+        }+        pos += len;+    }+    return 0;+}++#ifdef CONFIG_BIGNUM+static JSValue JS_ReadBigNum(BCReaderState *s, int tag)+{+    JSValue obj = JS_UNDEFINED;+    uint8_t v8;+    int32_t e;+    uint32_t len;+    limb_t l, i, n, j;+    JSBigFloat *p;+    limb_t v;+    bf_t *a;+    int bpos, d;+    +    p = js_new_bf(s->ctx);+    if (!p)+        goto fail;+    switch(tag) {+    case BC_TAG_BIG_INT:+        obj = JS_MKPTR(JS_TAG_BIG_INT, p);+        break;+    case BC_TAG_BIG_FLOAT:+        obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);+        break;+    case BC_TAG_BIG_DECIMAL:+        obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);+        break;+    default:+        abort();+    }++    /* sign + exponent */+    if (bc_get_sleb128(s, &e))+        goto fail;++    a = &p->num;+    a->sign = e & 1;+    e >>= 1;+    if (e == 0)+        a->expn = BF_EXP_ZERO;+    else if (e == 1)+        a->expn = BF_EXP_INF;+    else if (e == 2)+        a->expn = BF_EXP_NAN;+    else if (e >= 3)+        a->expn = e - 3;+    else+        a->expn = e;++    /* mantissa */+    if (a->expn != BF_EXP_ZERO &&+        a->expn != BF_EXP_INF &&+        a->expn != BF_EXP_NAN) {+        if (bc_get_leb128(s, &len))+            goto fail;+        bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);+        if (len == 0) {+            JS_ThrowInternalError(s->ctx, "invalid bignum length");+            goto fail;+        }+        if (tag != BC_TAG_BIG_DECIMAL)+            l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);+        else+            l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;+        if (bf_resize(a, l)) {+            JS_ThrowOutOfMemory(s->ctx);+            goto fail;+        }+        if (tag != BC_TAG_BIG_DECIMAL) {+            n = len & (sizeof(limb_t) - 1);+            if (n != 0) {+                v = 0;+                for(i = 0; i < n; i++) {+                    if (bc_get_u8(s, &v8))+                        goto fail;+                    v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);+                }+                a->tab[0] = v;+                i = 1;+            } else {+                i = 0;+            }+            for(; i < l; i++) {+#if LIMB_BITS == 32+                if (bc_get_u32(s, &v))+                    goto fail;+#ifdef WORDS_BIGENDIAN+                v = bswap32(v);+#endif+#else+                if (bc_get_u64(s, &v))+                    goto fail;+#ifdef WORDS_BIGENDIAN+                v = bswap64(v);+#endif+#endif+                a->tab[i] = v;+            }+        } else {+            bpos = 0;+            for(i = 0; i < l; i++) {+                if (i == 0 && (n = len % LIMB_DIGITS) != 0) {+                    j = LIMB_DIGITS - n;+                } else {+                    j = 0;+                }+                v = 0;+                for(; j < LIMB_DIGITS; j++) {+                    if (bpos == 0) {+                        if (bc_get_u8(s, &v8))+                            goto fail;+                        d = v8 & 0xf;+                        bpos = 1;+                    } else {+                        d = v8 >> 4;+                        bpos = 0;+                    }+                    if (d >= 10) {+                        JS_ThrowInternalError(s->ctx, "invalid digit");+                        goto fail;+                    }+                    v += mp_pow_dec[j] * d;+                }+                a->tab[i] = v;+            }+        }+    }+    bc_read_trace(s, "}\n");+    return obj;+ fail:+    JS_FreeValue(s->ctx, obj);+    return JS_EXCEPTION;+}+#endif /* CONFIG_BIGNUM */++static JSValue JS_ReadObjectRec(BCReaderState *s);++static int BC_add_object_ref1(BCReaderState *s, JSObject *p)+{+    if (s->allow_reference) {+        if (js_resize_array(s->ctx, (void *)&s->objects,+                            sizeof(s->objects[0]),+                            &s->objects_size, s->objects_count + 1))+            return -1;+        s->objects[s->objects_count++] = p;+    }+    return 0;+}++static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)+{+    return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));+}++static JSValue JS_ReadFunctionTag(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    JSFunctionBytecode bc, *b;+    JSValue obj = JS_UNDEFINED;+    uint16_t v16;+    uint8_t v8;+    int idx, i, local_count;+    int function_size, cpool_offset, byte_code_offset;+    int closure_var_offset, vardefs_offset;++    memset(&bc, 0, sizeof(bc));+    bc.header.ref_count = 1;+    //bc.gc_header.mark = 0;++    if (bc_get_u16(s, &v16))+        goto fail;+    idx = 0;+    bc.has_prototype = bc_get_flags(v16, &idx, 1);+    bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);+    bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);+    bc.need_home_object = bc_get_flags(v16, &idx, 1);+    bc.func_kind = bc_get_flags(v16, &idx, 2);+    bc.new_target_allowed = bc_get_flags(v16, &idx, 1);+    bc.super_call_allowed = bc_get_flags(v16, &idx, 1);+    bc.super_allowed = bc_get_flags(v16, &idx, 1);+    bc.arguments_allowed = bc_get_flags(v16, &idx, 1);+    bc.has_debug = bc_get_flags(v16, &idx, 1);+    bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);+    bc.read_only_bytecode = s->is_rom_data;+    if (bc_get_u8(s, &v8))+        goto fail;+    bc.js_mode = v8;+    if (bc_get_atom(s, &bc.func_name))  //@ atom leak if failure+        goto fail;+    if (bc_get_leb128_u16(s, &bc.arg_count))+        goto fail;+    if (bc_get_leb128_u16(s, &bc.var_count))+        goto fail;+    if (bc_get_leb128_u16(s, &bc.defined_arg_count))+        goto fail;+    if (bc_get_leb128_u16(s, &bc.stack_size))+        goto fail;+    if (bc_get_leb128_int(s, &bc.closure_var_count))+        goto fail;+    if (bc_get_leb128_int(s, &bc.cpool_count))+        goto fail;+    if (bc_get_leb128_int(s, &bc.byte_code_len))+        goto fail;+    if (bc_get_leb128_int(s, &local_count))+        goto fail;++    if (bc.has_debug) {+        function_size = sizeof(*b);+    } else {+        function_size = offsetof(JSFunctionBytecode, debug);+    }+    cpool_offset = function_size;+    function_size += bc.cpool_count * sizeof(*bc.cpool);+    vardefs_offset = function_size;+    function_size += local_count * sizeof(*bc.vardefs);+    closure_var_offset = function_size;+    function_size += bc.closure_var_count * sizeof(*bc.closure_var);+    byte_code_offset = function_size;+    if (!bc.read_only_bytecode) {+        function_size += bc.byte_code_len;+    }++    b = js_mallocz(ctx, function_size);+    if (!b)+        return JS_EXCEPTION;+            +    memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));+    b->header.ref_count = 1;+    add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);+            +    obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);++#ifdef DUMP_READ_OBJECT+    bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");+#endif+    bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",+                  b->arg_count, b->var_count, b->defined_arg_count,+                  b->closure_var_count, b->cpool_count);+    bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",+                  b->stack_size, b->byte_code_len, local_count);++    if (local_count != 0) {+        bc_read_trace(s, "vars {\n");+        b->vardefs = (void *)((uint8_t*)b + vardefs_offset);+        for(i = 0; i < local_count; i++) {+            JSVarDef *vd = &b->vardefs[i];+            if (bc_get_atom(s, &vd->var_name))+                goto fail;+            if (bc_get_leb128_int(s, &vd->scope_level))+                goto fail;+            if (bc_get_leb128_int(s, &vd->scope_next))+                goto fail;+            vd->scope_next--;+            if (bc_get_u8(s, &v8))+                goto fail;+            idx = 0;+            vd->var_kind = bc_get_flags(v8, &idx, 4);+            vd->is_func_var = bc_get_flags(v8, &idx, 1);+            vd->is_const = bc_get_flags(v8, &idx, 1);+            vd->is_lexical = bc_get_flags(v8, &idx, 1);+            vd->is_captured = bc_get_flags(v8, &idx, 1);+#ifdef DUMP_READ_OBJECT+            bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");+#endif+        }+        bc_read_trace(s, "}\n");+    }+    if (b->closure_var_count != 0) {+        bc_read_trace(s, "closure vars {\n");+        b->closure_var = (void *)((uint8_t*)b + closure_var_offset);+        for(i = 0; i < b->closure_var_count; i++) {+            JSClosureVar *cv = &b->closure_var[i];+            int var_idx;+            if (bc_get_atom(s, &cv->var_name))+                goto fail;+            if (bc_get_leb128_int(s, &var_idx))+                goto fail;+            cv->var_idx = var_idx;+            if (bc_get_u8(s, &v8))+                goto fail;+            idx = 0;+            cv->is_local = bc_get_flags(v8, &idx, 1);+            cv->is_arg = bc_get_flags(v8, &idx, 1);+            cv->is_const = bc_get_flags(v8, &idx, 1);+            cv->is_lexical = bc_get_flags(v8, &idx, 1);+            cv->var_kind = bc_get_flags(v8, &idx, 4);+#ifdef DUMP_READ_OBJECT+            bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");+#endif+        }+        bc_read_trace(s, "}\n");+    }+    {+        bc_read_trace(s, "bytecode {\n");+        if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))+            goto fail;+        bc_read_trace(s, "}\n");+    }+    if (b->has_debug) {+        /* read optional debug information */+        bc_read_trace(s, "debug {\n");+        if (bc_get_atom(s, &b->debug.filename))+            goto fail;+        if (bc_get_leb128_int(s, &b->debug.line_num))+            goto fail;+        if (bc_get_leb128_int(s, &b->debug.pc2line_len))+            goto fail;+        if (b->debug.pc2line_len) {+            b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);+            if (!b->debug.pc2line_buf)+                goto fail;+            if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))+                goto fail;+        }+#ifdef DUMP_READ_OBJECT+        bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");+#endif+        bc_read_trace(s, "}\n");+    }+    if (b->cpool_count != 0) {+        bc_read_trace(s, "cpool {\n");+        b->cpool = (void *)((uint8_t*)b + cpool_offset);+        for(i = 0; i < b->cpool_count; i++) {+            JSValue val;+            val = JS_ReadObjectRec(s);+            if (JS_IsException(val))+                goto fail;+            b->cpool[i] = val;+        }+        bc_read_trace(s, "}\n");+    }+    b->realm = JS_DupContext(ctx);+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadModule(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    JSValue obj;+    JSModuleDef *m = NULL;+    JSAtom module_name;+    int i;+    uint8_t v8;+    +    if (bc_get_atom(s, &module_name))+        goto fail;+#ifdef DUMP_READ_OBJECT+    bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");+#endif+    m = js_new_module_def(ctx, module_name);+    if (!m)+        goto fail;+    obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+    if (bc_get_leb128_int(s, &m->req_module_entries_count))+        goto fail;+    if (m->req_module_entries_count != 0) {+        m->req_module_entries_size = m->req_module_entries_count;+        m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);+        if (!m->req_module_entries)+            goto fail;+        for(i = 0; i < m->req_module_entries_count; i++) {+            JSReqModuleEntry *rme = &m->req_module_entries[i];+            if (bc_get_atom(s, &rme->module_name))+                goto fail;+        }+    }++    if (bc_get_leb128_int(s, &m->export_entries_count))+        goto fail;+    if (m->export_entries_count != 0) {+        m->export_entries_size = m->export_entries_count;+        m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);+        if (!m->export_entries)+            goto fail;+        for(i = 0; i < m->export_entries_count; i++) {+            JSExportEntry *me = &m->export_entries[i];+            if (bc_get_u8(s, &v8))+                goto fail;+            me->export_type = v8;+            if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+                if (bc_get_leb128_int(s, &me->u.local.var_idx))+                    goto fail;+            } else {+                if (bc_get_leb128_int(s, &me->u.req_module_idx))+                    goto fail;+                if (bc_get_atom(s, &me->local_name))+                    goto fail;+            }+            if (bc_get_atom(s, &me->export_name))+                goto fail;+        }+    }++    if (bc_get_leb128_int(s, &m->star_export_entries_count))+        goto fail;+    if (m->star_export_entries_count != 0) {+        m->star_export_entries_size = m->star_export_entries_count;+        m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);+        if (!m->star_export_entries)+            goto fail;+        for(i = 0; i < m->star_export_entries_count; i++) {+            JSStarExportEntry *se = &m->star_export_entries[i];+            if (bc_get_leb128_int(s, &se->req_module_idx))+                goto fail;+        }+    }++    if (bc_get_leb128_int(s, &m->import_entries_count))+        goto fail;+    if (m->import_entries_count != 0) {+        m->import_entries_size = m->import_entries_count;+        m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);+        if (!m->import_entries)+            goto fail;+        for(i = 0; i < m->import_entries_count; i++) {+            JSImportEntry *mi = &m->import_entries[i];+            if (bc_get_leb128_int(s, &mi->var_idx))+                goto fail;+            if (bc_get_atom(s, &mi->import_name))+                goto fail;+            if (bc_get_leb128_int(s, &mi->req_module_idx))+                goto fail;+        }+    }++    m->func_obj = JS_ReadObjectRec(s);+    if (JS_IsException(m->func_obj))+        goto fail;+    return obj;+ fail:+    if (m) {+        js_free_module_def(ctx, m);+    }+    return JS_EXCEPTION;+}++static JSValue JS_ReadObjectTag(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    JSValue obj;+    uint32_t prop_count, i;+    JSAtom atom;+    JSValue val;+    int ret;+    +    obj = JS_NewObject(ctx);+    if (BC_add_object_ref(s, obj))+        goto fail;+    if (bc_get_leb128(s, &prop_count))+        goto fail;+    for(i = 0; i < prop_count; i++) {+        if (bc_get_atom(s, &atom))+            goto fail;+#ifdef DUMP_READ_OBJECT+        bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");+#endif+        val = JS_ReadObjectRec(s);+        if (JS_IsException(val)) {+            JS_FreeAtom(ctx, atom);+            goto fail;+        }+        ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);+        JS_FreeAtom(ctx, atom);+        if (ret < 0)+            goto fail;+    }+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadArray(BCReaderState *s, int tag)+{+    JSContext *ctx = s->ctx;+    JSValue obj;+    uint32_t len, i;+    JSValue val;+    int ret, prop_flags;+    BOOL is_template;++    obj = JS_NewArray(ctx);+    if (BC_add_object_ref(s, obj))+        goto fail;+    is_template = (tag == BC_TAG_TEMPLATE_OBJECT);+    if (bc_get_leb128(s, &len))+        goto fail;+    for(i = 0; i < len; i++) {+        val = JS_ReadObjectRec(s);+        if (JS_IsException(val))+            goto fail;+        if (is_template)+            prop_flags = JS_PROP_ENUMERABLE;+        else+            prop_flags = JS_PROP_C_W_E;+        ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,+                                           prop_flags);+        if (ret < 0)+            goto fail;+    }+    if (is_template) {+        val = JS_ReadObjectRec(s);+        if (JS_IsException(val))+            goto fail;+        if (!JS_IsUndefined(val)) {+            ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);+            if (ret < 0)+                goto fail;+        }+        JS_PreventExtensions(ctx, obj);+    }+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadTypedArray(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;+    uint8_t array_tag;+    JSValueConst args[3];+    uint32_t offset, len, idx;+    +    if (bc_get_u8(s, &array_tag))+        return JS_EXCEPTION;+    if (array_tag >= JS_TYPED_ARRAY_COUNT)+        return JS_ThrowTypeError(ctx, "invalid typed array");+    if (bc_get_leb128(s, &len))+        return JS_EXCEPTION;+    if (bc_get_leb128(s, &offset))+        return JS_EXCEPTION;+    /* XXX: this hack could be avoided if the typed array could be+       created before the array buffer */+    idx = s->objects_count;+    if (BC_add_object_ref1(s, NULL))+        goto fail;+    array_buffer = JS_ReadObjectRec(s);+    if (JS_IsException(array_buffer))+        return JS_EXCEPTION;+    if (!js_get_array_buffer(ctx, array_buffer)) {+        JS_FreeValue(ctx, array_buffer);+        return JS_EXCEPTION;+    }+    args[0] = array_buffer;+    args[1] = JS_NewInt64(ctx, offset);+    args[2] = JS_NewInt64(ctx, len);+    obj = js_typed_array_constructor(ctx, JS_UNDEFINED,+                                     3, args,+                                     JS_CLASS_UINT8C_ARRAY + array_tag);+    if (JS_IsException(obj))+        goto fail;+    if (s->allow_reference) {+        s->objects[idx] = JS_VALUE_GET_OBJ(obj);+    }+    JS_FreeValue(ctx, array_buffer);+    return obj;+ fail:+    JS_FreeValue(ctx, array_buffer);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadArrayBuffer(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    uint32_t byte_length;+    JSValue obj;+    +    if (bc_get_leb128(s, &byte_length))+        return JS_EXCEPTION;+    if (unlikely(s->buf_end - s->ptr < byte_length)) {+        bc_read_error_end(s);+        return JS_EXCEPTION;+    }+    obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);+    if (JS_IsException(obj))+        goto fail;+    if (BC_add_object_ref(s, obj))+        goto fail;+    s->ptr += byte_length;+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    uint32_t byte_length;+    uint8_t *data_ptr;+    JSValue obj;+    uint64_t u64;+    +    if (bc_get_leb128(s, &byte_length))+        return JS_EXCEPTION;+    if (bc_get_u64(s, &u64))+        return JS_EXCEPTION;+    data_ptr = (uint8_t *)(uintptr_t)u64;+    /* the SharedArrayBuffer is cloned */+    obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,+                                       JS_CLASS_SHARED_ARRAY_BUFFER,+                                       data_ptr,+                                       NULL, NULL, FALSE);+    if (JS_IsException(obj))+        goto fail;+    if (BC_add_object_ref(s, obj))+        goto fail;+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadDate(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    JSValue val, obj = JS_UNDEFINED;++    val = JS_ReadObjectRec(s);+    if (JS_IsException(val))+        goto fail;+    if (!JS_IsNumber(val)) {+        JS_ThrowTypeError(ctx, "Number tag expected for date");+        goto fail;+    }+    obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],+                                 JS_CLASS_DATE);+    if (JS_IsException(obj))+        goto fail;+    if (BC_add_object_ref(s, obj))+        goto fail;+    JS_SetObjectData(ctx, obj, val);+    return obj;+ fail:+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadObjectValue(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    JSValue val, obj = JS_UNDEFINED;++    val = JS_ReadObjectRec(s);+    if (JS_IsException(val))+        goto fail;+    obj = JS_ToObject(ctx, val);+    if (JS_IsException(obj))+        goto fail;+    if (BC_add_object_ref(s, obj))+        goto fail;+    JS_FreeValue(ctx, val);+    return obj;+ fail:+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue JS_ReadObjectRec(BCReaderState *s)+{+    JSContext *ctx = s->ctx;+    uint8_t tag;+    JSValue obj = JS_UNDEFINED;++    if (js_check_stack_overflow(ctx->rt, 0))+        return JS_ThrowStackOverflow(ctx);++    if (bc_get_u8(s, &tag))+        return JS_EXCEPTION;++    bc_read_trace(s, "%s {\n", bc_tag_str[tag]);++    switch(tag) {+    case BC_TAG_NULL:+        obj = JS_NULL;+        break;+    case BC_TAG_UNDEFINED:+        obj = JS_UNDEFINED;+        break;+    case BC_TAG_BOOL_FALSE:+    case BC_TAG_BOOL_TRUE:+        obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);+        break;+    case BC_TAG_INT32:+        {+            int32_t val;+            if (bc_get_sleb128(s, &val))+                return JS_EXCEPTION;+            bc_read_trace(s, "%d\n", val);+            obj = JS_NewInt32(ctx, val);+        }+        break;+    case BC_TAG_FLOAT64:+        {+            JSFloat64Union u;+            if (bc_get_u64(s, &u.u64))+                return JS_EXCEPTION;+            bc_read_trace(s, "%g\n", u.d);+            obj = __JS_NewFloat64(ctx, u.d);+        }+        break;+    case BC_TAG_STRING:+        {+            JSString *p;+            p = JS_ReadString(s);+            if (!p)+                return JS_EXCEPTION;+            obj = JS_MKPTR(JS_TAG_STRING, p);+        }+        break;+    case BC_TAG_FUNCTION_BYTECODE:+        if (!s->allow_bytecode)+            goto invalid_tag;+        obj = JS_ReadFunctionTag(s);+        break;+    case BC_TAG_MODULE:+        if (!s->allow_bytecode)+            goto invalid_tag;+        obj = JS_ReadModule(s);+        break;+    case BC_TAG_OBJECT:+        obj = JS_ReadObjectTag(s);+        break;+    case BC_TAG_ARRAY:+    case BC_TAG_TEMPLATE_OBJECT:+        obj = JS_ReadArray(s, tag);+        break;+    case BC_TAG_TYPED_ARRAY:+        obj = JS_ReadTypedArray(s);+        break;+    case BC_TAG_ARRAY_BUFFER:+        obj = JS_ReadArrayBuffer(s);+        break;+    case BC_TAG_SHARED_ARRAY_BUFFER:+        if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)+            goto invalid_tag;+        obj = JS_ReadSharedArrayBuffer(s);+        break;+    case BC_TAG_DATE:+        obj = JS_ReadDate(s);+        break;+    case BC_TAG_OBJECT_VALUE:+        obj = JS_ReadObjectValue(s);+        break;+#ifdef CONFIG_BIGNUM+    case BC_TAG_BIG_INT:+    case BC_TAG_BIG_FLOAT:+    case BC_TAG_BIG_DECIMAL:+        obj = JS_ReadBigNum(s, tag);+        break;+#endif+    case BC_TAG_OBJECT_REFERENCE:+        {+            uint32_t val;+            if (!s->allow_reference)+                return JS_ThrowSyntaxError(ctx, "object references are not allowed");+            if (bc_get_leb128(s, &val))+                return JS_EXCEPTION;+            bc_read_trace(s, "%u\n", val);+            if (val >= s->objects_count) {+                return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",+                                           val, s->objects_count);+            }+            obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));+        }+        break;+    default:+    invalid_tag:+        return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",+                                   tag, (unsigned int)(s->ptr - s->buf_start));+    }+    bc_read_trace(s, "}\n");+    return obj;+}++static int JS_ReadObjectAtoms(BCReaderState *s)+{+    uint8_t v8;+    JSString *p;+    int i;+    JSAtom atom;++    if (bc_get_u8(s, &v8))+        return -1;+    /* XXX: could support byte swapped input */+    if (v8 != BC_VERSION) {+        JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",+                            v8, BC_VERSION);+        return -1;+    }+    if (bc_get_leb128(s, &s->idx_to_atom_count))+        return -1;++    bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);++    if (s->idx_to_atom_count != 0) {+        s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *+                                    sizeof(s->idx_to_atom[0]));+        if (!s->idx_to_atom)+            return s->error_state = -1;+    }+    for(i = 0; i < s->idx_to_atom_count; i++) {+        p = JS_ReadString(s);+        if (!p)+            return -1;+        atom = JS_NewAtomStr(s->ctx, p);+        if (atom == JS_ATOM_NULL)+            return s->error_state = -1;+        s->idx_to_atom[i] = atom;+        if (s->is_rom_data && (atom != (i + s->first_atom)))+            s->is_rom_data = FALSE; /* atoms must be relocated */+    }+    bc_read_trace(s, "}\n");+    return 0;+}++static void bc_reader_free(BCReaderState *s)+{+    int i;+    if (s->idx_to_atom) {+        for(i = 0; i < s->idx_to_atom_count; i++) {+            JS_FreeAtom(s->ctx, s->idx_to_atom[i]);+        }+        js_free(s->ctx, s->idx_to_atom);+    }+    js_free(s->ctx, s->objects);+}++JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,+                       int flags)+{+    BCReaderState ss, *s = &ss;+    JSValue obj;++    ctx->binary_object_count += 1;+    ctx->binary_object_size += buf_len;++    memset(s, 0, sizeof(*s));+    s->ctx = ctx;+    s->buf_start = buf;+    s->buf_end = buf + buf_len;+    s->ptr = buf;+    s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);+    s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);+    s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);+    s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);+    if (s->allow_bytecode)+        s->first_atom = JS_ATOM_END;+    else+        s->first_atom = 1;+    if (JS_ReadObjectAtoms(s)) {+        obj = JS_EXCEPTION;+    } else {+        obj = JS_ReadObjectRec(s);+    }+    bc_reader_free(s);+    return obj;+}++/*******************************************************************/+/* runtime functions & objects */++static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv);+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv);+static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv);++static int check_function(JSContext *ctx, JSValueConst obj)+{+    if (likely(JS_IsFunction(ctx, obj)))+        return 0;+    JS_ThrowTypeError(ctx, "not a function");+    return -1;+}++static int check_exception_free(JSContext *ctx, JSValue obj)+{+    JS_FreeValue(ctx, obj);+    return JS_IsException(obj);+}++static JSAtom find_atom(JSContext *ctx, const char *name)+{+    JSAtom atom;+    int len;++    if (*name == '[') {+        name++;+        len = strlen(name) - 1;+        /* We assume 8 bit non null strings, which is the case for these+           symbols */+        for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {+            JSAtomStruct *p = ctx->rt->atom_array[atom];+            JSString *str = p;+            if (str->len == len && !memcmp(str->u.str8, name, len))+                return JS_DupAtom(ctx, atom);+        }+        abort();+    } else {+        atom = JS_NewAtom(ctx, name);+    }+    return atom;+}++static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p,+                                          JSAtom atom, void *opaque)+{+    const JSCFunctionListEntry *e = opaque;+    JSValueConst obj = JS_MKPTR(JS_TAG_OBJECT, p);+    JSValue val;+    int prop_flags = e->prop_flags;++    switch(e->def_type) {+    case JS_DEF_ALIAS:+        {+            JSAtom atom1 = find_atom(ctx, e->u.alias.name);+            switch (e->u.alias.base) {+            case -1:+                val = JS_GetProperty(ctx, obj, atom1);+                break;+            case 0:+                val = JS_GetProperty(ctx, ctx->global_obj, atom1);+                break;+            case 1:+                val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);+                break;+            default:+                abort();+            }+            JS_FreeAtom(ctx, atom1);+            goto setval;+        }+    case JS_DEF_CFUNC:+        val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,+                               e->name, e->u.func.length, e->u.func.cproto, e->magic);+    setval:+        if (atom == JS_ATOM_Symbol_toPrimitive) {+            /* Symbol.toPrimitive functions are not writable */+            prop_flags = JS_PROP_CONFIGURABLE;+        } else if (atom == JS_ATOM_Symbol_hasInstance) {+            /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */+            prop_flags = 0;+        }+        break;+    case JS_DEF_CGETSET:+    case JS_DEF_CGETSET_MAGIC:+        {+            JSValue getter, setter;+            char buf[64];++            getter = JS_UNDEFINED;+            if (e->u.getset.get.generic) {+                snprintf(buf, sizeof(buf), "get %s", e->name);+                getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,+                                          buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,+                                          e->magic);+            }+            setter = JS_UNDEFINED;+            if (e->u.getset.set.generic) {+                snprintf(buf, sizeof(buf), "set %s", e->name);+                setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,+                                          buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,+                                          e->magic);+            }+            JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);+            return 0;+        }+        break;+    case JS_DEF_PROP_STRING:+        val = JS_NewAtomString(ctx, e->u.str);+        break;+    case JS_DEF_PROP_INT32:+        val = JS_NewInt32(ctx, e->u.i32);+        break;+    case JS_DEF_PROP_INT64:+        val = JS_NewInt64(ctx, e->u.i64);+        break;+    case JS_DEF_PROP_DOUBLE:+        val = __JS_NewFloat64(ctx, e->u.f64);+        break;+    case JS_DEF_PROP_UNDEFINED:+        val = JS_UNDEFINED;+        break;+    case JS_DEF_OBJECT:+        val = JS_NewObject(ctx);+        JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);+        break;+    default:+        abort();+    }+    JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);+    return 0;+}++void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,+                                const JSCFunctionListEntry *tab, int len)+{+    int i, prop_flags;++    for (i = 0; i < len; i++) {+        const JSCFunctionListEntry *e = &tab[i];+        JSAtom atom = find_atom(ctx, e->name);++        switch (e->def_type) {+        case JS_DEF_CFUNC:+        case JS_DEF_CGETSET:+        case JS_DEF_CGETSET_MAGIC:+        case JS_DEF_PROP_STRING:+        case JS_DEF_ALIAS:+        case JS_DEF_OBJECT:+            prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | (e->prop_flags & JS_PROP_ENUMERABLE);+            JS_DefineAutoInitProperty(ctx, obj, atom,+                                      JS_AUTOINIT_ID_PROP,+                                      (void *)e, prop_flags);+            break;+        case JS_DEF_PROP_INT32:+        case JS_DEF_PROP_INT64:+        case JS_DEF_PROP_DOUBLE:+        case JS_DEF_PROP_UNDEFINED:+            {+                JSObject *p = JS_VALUE_GET_OBJ(obj);+                JS_InstantiateFunctionListItem(ctx, p, atom, (void *)e);+            }+            break;+        default:+            abort();+        }+        JS_FreeAtom(ctx, atom);+    }+}++int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,+                           const JSCFunctionListEntry *tab, int len)+{+    int i;+    for(i = 0; i < len; i++) {+        if (JS_AddModuleExport(ctx, m, tab[i].name))+            return -1;+    }+    return 0;+}++int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,+                           const JSCFunctionListEntry *tab, int len)+{+    int i;+    JSValue val;++    for(i = 0; i < len; i++) {+        const JSCFunctionListEntry *e = &tab[i];+        switch(e->def_type) {+        case JS_DEF_CFUNC:+            val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,+                                   e->name, e->u.func.length, e->u.func.cproto, e->magic);+            break;+        case JS_DEF_PROP_STRING:+            val = JS_NewString(ctx, e->u.str);+            break;+        case JS_DEF_PROP_INT32:+            val = JS_NewInt32(ctx, e->u.i32);+            break;+        case JS_DEF_PROP_INT64:+            val = JS_NewInt64(ctx, e->u.i64);+            break;+        case JS_DEF_PROP_DOUBLE:+            val = __JS_NewFloat64(ctx, e->u.f64);+            break;+        case JS_DEF_OBJECT:+            val = JS_NewObject(ctx);+            JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);+            break;+        default:+            abort();+        }+        if (JS_SetModuleExport(ctx, m, e->name, val))+            return -1;+    }+    return 0;+}++/* Note: 'func_obj' is not necessarily a constructor */+static void JS_SetConstructor2(JSContext *ctx,+                               JSValueConst func_obj,+                               JSValueConst proto,+                               int proto_flags, int ctor_flags)+{+    JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,+                           JS_DupValue(ctx, proto), proto_flags);+    JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,+                           JS_DupValue(ctx, func_obj),+                           ctor_flags);+    set_cycle_flag(ctx, func_obj);+    set_cycle_flag(ctx, proto);+}++void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, +                       JSValueConst proto)+{+    JS_SetConstructor2(ctx, func_obj, proto,+                       0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+}++static void JS_NewGlobalCConstructor2(JSContext *ctx,+                                      JSValue func_obj,+                                      const char *name,+                                      JSValueConst proto)+{+    JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,+                           JS_DupValue(ctx, func_obj),+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    JS_SetConstructor(ctx, func_obj, proto);+    JS_FreeValue(ctx, func_obj);+}++static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,+                                             JSCFunction *func, int length,+                                             JSValueConst proto)+{+    JSValue func_obj;+    func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);+    JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);+    return func_obj;+}++static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,+                                                 JSCFunction *func, int length,+                                                 JSValueConst proto)+{+    JSValue func_obj;+    func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);+    JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);+    return func_obj;+}++static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);+}++static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    double d;++    /* XXX: does this work for bigfloat? */+    if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))+        return JS_EXCEPTION;+    return JS_NewBool(ctx, isnan(d));+}++static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    BOOL res;+    double d;+    if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))+        return JS_EXCEPTION;+    res = isfinite(d);+    return JS_NewBool(ctx, res);+}++/* Object class */++static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)+{+    int tag = JS_VALUE_GET_NORM_TAG(val);+    JSValue obj;++    switch(tag) {+    default:+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+        return JS_ThrowTypeError(ctx, "cannot convert to object");+    case JS_TAG_OBJECT:+    case JS_TAG_EXCEPTION:+        return JS_DupValue(ctx, val);+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);+        goto set_value;+    case JS_TAG_BIG_FLOAT:+        obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);+        goto set_value;+    case JS_TAG_BIG_DECIMAL:+        obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);+        goto set_value;+#endif+    case JS_TAG_INT:+    case JS_TAG_FLOAT64:+        obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);+        goto set_value;+    case JS_TAG_STRING:+        /* XXX: should call the string constructor */+        {+            JSString *p1 = JS_VALUE_GET_STRING(val);+            obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);+            JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);+        }+        goto set_value;+    case JS_TAG_BOOL:+        obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);+        goto set_value;+    case JS_TAG_SYMBOL:+        obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);+    set_value:+        if (!JS_IsException(obj))+            JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));+        return obj;+    }+}++static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)+{+    JSValue obj = JS_ToObject(ctx, val);+    JS_FreeValue(ctx, val);+    return obj;+}++static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,+                          JSValueConst desc)+{+    JSValue val, getter, setter;+    int flags;++    if (!JS_IsObject(desc)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    flags = 0;+    val = JS_UNDEFINED;+    getter = JS_UNDEFINED;+    setter = JS_UNDEFINED;+    if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {+        JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);+        if (JS_IsException(prop))+            goto fail;+        flags |= JS_PROP_HAS_CONFIGURABLE;+        if (JS_ToBoolFree(ctx, prop))+            flags |= JS_PROP_CONFIGURABLE;+    }+    if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {+        JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);+        if (JS_IsException(prop))+            goto fail;+        flags |= JS_PROP_HAS_WRITABLE;+        if (JS_ToBoolFree(ctx, prop))+            flags |= JS_PROP_WRITABLE;+    }+    if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {+        JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);+        if (JS_IsException(prop))+            goto fail;+        flags |= JS_PROP_HAS_ENUMERABLE;+        if (JS_ToBoolFree(ctx, prop))+            flags |= JS_PROP_ENUMERABLE;+    }+    if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {+        flags |= JS_PROP_HAS_VALUE;+        val = JS_GetProperty(ctx, desc, JS_ATOM_value);+        if (JS_IsException(val))+            goto fail;+    }+    if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {+        flags |= JS_PROP_HAS_GET;+        getter = JS_GetProperty(ctx, desc, JS_ATOM_get);+        if (JS_IsException(getter) ||+            !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {+            JS_ThrowTypeError(ctx, "invalid getter");+            goto fail;+        }+    }+    if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {+        flags |= JS_PROP_HAS_SET;+        setter = JS_GetProperty(ctx, desc, JS_ATOM_set);+        if (JS_IsException(setter) ||+            !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {+            JS_ThrowTypeError(ctx, "invalid setter");+            goto fail;+        }+    }+    if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&+        (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {+        JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");+        goto fail;+    }+    d->flags = flags;+    d->value = val;+    d->getter = getter;+    d->setter = setter;+    return 0;+ fail:+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, getter);+    JS_FreeValue(ctx, setter);+    return -1;+}++static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,+                                             JSAtom prop, JSValueConst desc,+                                             int flags)+{+    JSPropertyDescriptor d;+    int ret;++    if (js_obj_to_desc(ctx, &d, desc) < 0)+        return -1;++    ret = JS_DefineProperty(ctx, obj, prop,+                            d.value, d.getter, d.setter, d.flags | flags);+    js_free_desc(ctx, &d);+    return ret;+}++static __exception int JS_ObjectDefineProperties(JSContext *ctx,+                                                 JSValueConst obj,+                                                 JSValueConst properties)+{+    JSValue props, desc;+    JSObject *p;+    JSPropertyEnum *atoms;+    uint32_t len, i;+    int ret = -1;++    if (!JS_IsObject(obj)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    desc = JS_UNDEFINED;+    props = JS_ToObject(ctx, properties);+    if (JS_IsException(props))+        return -1;+    p = JS_VALUE_GET_OBJ(props);+    if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)+        goto exception;+    for(i = 0; i < len; i++) {+        JS_FreeValue(ctx, desc);+        desc = JS_GetProperty(ctx, props, atoms[i].atom);+        if (JS_IsException(desc))+            goto exception;+        if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)+            goto exception;+    }+    ret = 0;++exception:+    js_free_prop_enum(ctx, atoms, len);+    JS_FreeValue(ctx, props);+    JS_FreeValue(ctx, desc);+    return ret;+}++static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,+                                     int argc, JSValueConst *argv)+{+    JSValue ret;+    if (!JS_IsUndefined(new_target) &&+        JS_VALUE_GET_OBJ(new_target) !=+        JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {+        ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);+    } else {+        int tag = JS_VALUE_GET_NORM_TAG(argv[0]);+        switch(tag) {+        case JS_TAG_NULL:+        case JS_TAG_UNDEFINED:+            ret = JS_NewObject(ctx);+            break;+        default:+            ret = JS_ToObject(ctx, argv[0]);+            break;+        }+    }+    return ret;+}++static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValueConst proto, props;+    JSValue obj;++    proto = argv[0];+    if (!JS_IsObject(proto) && !JS_IsNull(proto))+        return JS_ThrowTypeError(ctx, "not a prototype");+    obj = JS_NewObjectProto(ctx, proto);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    props = argv[1];+    if (!JS_IsUndefined(props)) {+        if (JS_ObjectDefineProperties(ctx, obj, props)) {+            JS_FreeValue(ctx, obj);+            return JS_EXCEPTION;+        }+    }+    return obj;+}++static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv, int magic)+{+    JSValueConst val;++    val = argv[0];+    if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {+        /* ES6 feature non compatible with ES5.1: primitive types are+           accepted */+        if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||+            JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)+            return JS_ThrowTypeErrorNotAnObject(ctx);+    }+    return JS_GetPrototype(ctx, val);+}++static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    JSValueConst obj;+    obj = argv[0];+    if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)+        return JS_EXCEPTION;+    return JS_DupValue(ctx, obj);+}++/* magic = 1 if called as Reflect.defineProperty */+static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv, int magic)+{+    JSValueConst obj, prop, desc;+    int ret, flags;+    JSAtom atom;++    obj = argv[0];+    prop = argv[1];+    desc = argv[2];++    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);+    atom = JS_ValueToAtom(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL))+        return JS_EXCEPTION;+    flags = 0;+    if (!magic)+        flags |= JS_PROP_THROW;+    ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);+    JS_FreeAtom(ctx, atom);+    if (ret < 0) {+        return JS_EXCEPTION;+    } else if (magic) {+        return JS_NewBool(ctx, ret);+    } else {+        return JS_DupValue(ctx, obj);+    }+}++static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv)+{+    // defineProperties(obj, properties)+    JSValueConst obj = argv[0];++    if (JS_ObjectDefineProperties(ctx, obj, argv[1]))+        return JS_EXCEPTION;+    else+        return JS_DupValue(ctx, obj);+}++/* magic = 1 if called as __defineSetter__ */+static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv, int magic)+{+    JSValue obj;+    JSValueConst prop, value, get, set;+    int ret, flags;+    JSAtom atom;++    prop = argv[0];+    value = argv[1];++    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj))+        return JS_EXCEPTION;++    if (check_function(ctx, value)) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    atom = JS_ValueToAtom(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL)) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    flags = JS_PROP_THROW |+        JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |+        JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;+    if (magic) {+        get = JS_UNDEFINED;+        set = value;+        flags |= JS_PROP_HAS_SET;+    } else {+        get = value;+        set = JS_UNDEFINED;+        flags |= JS_PROP_HAS_GET;+    }+    ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);+    JS_FreeValue(ctx, obj);+    JS_FreeAtom(ctx, atom);+    if (ret < 0) {+        return JS_EXCEPTION;+    } else {+        return JS_UNDEFINED;+    }+}++static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,+                                                  int argc, JSValueConst *argv, int magic)+{+    JSValueConst prop;+    JSAtom atom;+    JSValue ret, obj;+    JSPropertyDescriptor desc;+    int res, flags;++    if (magic) {+        /* Reflect.getOwnPropertyDescriptor case */+        if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)+            return JS_ThrowTypeErrorNotAnObject(ctx);+        obj = JS_DupValue(ctx, argv[0]);+    } else {+        obj = JS_ToObject(ctx, argv[0]);+        if (JS_IsException(obj))+            return obj;+    }+    prop = argv[1];+    atom = JS_ValueToAtom(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL))+        goto exception;+    ret = JS_UNDEFINED;+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);+        if (res < 0)+            goto exception;+        if (res) {+            ret = JS_NewObject(ctx);+            if (JS_IsException(ret))+                goto exception1;+            flags = JS_PROP_C_W_E | JS_PROP_THROW;+            if (desc.flags & JS_PROP_GETSET) {+                if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0+                ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)+                    goto exception1;+            } else {+                if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0+                ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,+                                           JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)+                    goto exception1;+            }+            if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,+                                       JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0+            ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,+                                       JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)+                goto exception1;+            js_free_desc(ctx, &desc);+        }+    }+    JS_FreeAtom(ctx, atom);+    JS_FreeValue(ctx, obj);+    return ret;++exception1:+    js_free_desc(ctx, &desc);+    JS_FreeValue(ctx, ret);+exception:+    JS_FreeAtom(ctx, atom);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,+                                                   int argc, JSValueConst *argv)+{+    //getOwnPropertyDescriptors(obj)+    JSValue obj, r;+    JSObject *p;+    JSPropertyEnum *props;+    uint32_t len, i;++    r = JS_UNDEFINED;+    obj = JS_ToObject(ctx, argv[0]);+    if (JS_IsException(obj))+        return JS_EXCEPTION;++    p = JS_VALUE_GET_OBJ(obj);+    if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,+                               JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))+        goto exception;+    r = JS_NewObject(ctx);+    if (JS_IsException(r))+        goto exception;+    for(i = 0; i < len; i++) {+        JSValue atomValue, desc;+        JSValueConst args[2];++        atomValue = JS_AtomToValue(ctx, props[i].atom);+        if (JS_IsException(atomValue))+            goto exception;+        args[0] = obj;+        args[1] = atomValue;+        desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);+        JS_FreeValue(ctx, atomValue);+        if (JS_IsException(desc))+            goto exception;+        if (!JS_IsUndefined(desc)) {+            if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,+                                       JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                goto exception;+        }+    }+    js_free_prop_enum(ctx, props, len);+    JS_FreeValue(ctx, obj);+    return r;++exception:+    js_free_prop_enum(ctx, props, len);+    JS_FreeValue(ctx, obj);+    JS_FreeValue(ctx, r);+    return JS_EXCEPTION;+}++static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,+                                       int flags, int kind)+{+    JSValue obj, r, val, key, value;+    JSObject *p;+    JSPropertyEnum *atoms;+    uint32_t len, i, j;++    r = JS_UNDEFINED;+    val = JS_UNDEFINED;+    obj = JS_ToObject(ctx, obj1);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    p = JS_VALUE_GET_OBJ(obj);+    if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))+        goto exception;+    r = JS_NewArray(ctx);+    if (JS_IsException(r))+        goto exception;+    for(j = i = 0; i < len; i++) {+        JSAtom atom = atoms[i].atom;+        if (flags & JS_GPN_ENUM_ONLY) {+            JSPropertyDescriptor desc;+            int res;++            /* Check if property is still enumerable */+            res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);+            if (res < 0)+                goto exception;+            if (!res)+                continue;+            js_free_desc(ctx, &desc);+            if (!(desc.flags & JS_PROP_ENUMERABLE))+                continue;+        }+        switch(kind) {+        default:+        case JS_ITERATOR_KIND_KEY:+            val = JS_AtomToValue(ctx, atom);+            if (JS_IsException(val))+                goto exception;+            break;+        case JS_ITERATOR_KIND_VALUE:+            val = JS_GetProperty(ctx, obj, atom);+            if (JS_IsException(val))+                goto exception;+            break;+        case JS_ITERATOR_KIND_KEY_AND_VALUE:+            val = JS_NewArray(ctx);+            if (JS_IsException(val))+                goto exception;+            key = JS_AtomToValue(ctx, atom);+            if (JS_IsException(key))+                goto exception1;+            if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)+                goto exception1;+            value = JS_GetProperty(ctx, obj, atom);+            if (JS_IsException(value))+                goto exception1;+            if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)+                goto exception1;+            break;+        }+        if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)+            goto exception;+    }+    goto done;++exception1:+    JS_FreeValue(ctx, val);+exception:+    JS_FreeValue(ctx, r);+    r = JS_EXCEPTION;+done:+    js_free_prop_enum(ctx, atoms, len);+    JS_FreeValue(ctx, obj);+    return r;+}++static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,+                                             int argc, JSValueConst *argv)+{+    return JS_GetOwnPropertyNames2(ctx, argv[0],+                                   JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);+}++static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,+                                             int argc, JSValueConst *argv)+{+    return JS_GetOwnPropertyNames2(ctx, argv[0],+                                   JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);+}++static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int kind)+{+    return JS_GetOwnPropertyNames2(ctx, argv[0],+                                   JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);+}++static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv, int reflect)+{+    JSValueConst obj;+    int ret;++    obj = argv[0];+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+        if (reflect)+            return JS_ThrowTypeErrorNotAnObject(ctx);+        else+            return JS_FALSE;+    }+    ret = JS_IsExtensible(ctx, obj);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,+                                           int argc, JSValueConst *argv, int reflect)+{+    JSValueConst obj;+    int ret;++    obj = argv[0];+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+        if (reflect)+            return JS_ThrowTypeErrorNotAnObject(ctx);+        else+            return JS_DupValue(ctx, obj);+    }+    ret = JS_PreventExtensions(ctx, obj);+    if (ret < 0)+        return JS_EXCEPTION;+    if (reflect) {+        return JS_NewBool(ctx, ret);+    } else {+        if (!ret)+            return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");+        return JS_DupValue(ctx, obj);+    }+}++static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    JSValue obj;+    JSAtom atom;+    JSObject *p;+    BOOL ret;++    atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */+    if (unlikely(atom == JS_ATOM_NULL))+        return JS_EXCEPTION;+    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj)) {+        JS_FreeAtom(ctx, atom);+        return obj;+    }+    p = JS_VALUE_GET_OBJ(obj);+    ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);+    JS_FreeAtom(ctx, atom);+    JS_FreeValue(ctx, obj);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    return JS_ToObject(ctx, this_val);+}++static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValue obj, tag;+    int is_array;+    JSAtom atom;+    JSObject *p;++    if (JS_IsNull(this_val)) {+        tag = JS_NewString(ctx, "Null");+    } else if (JS_IsUndefined(this_val)) {+        tag = JS_NewString(ctx, "Undefined");+    } else {+        obj = JS_ToObject(ctx, this_val);+        if (JS_IsException(obj))+            return obj;+        is_array = JS_IsArray(ctx, obj);+        if (is_array < 0) {+            JS_FreeValue(ctx, obj);+            return JS_EXCEPTION;+        }+        if (is_array) {+            atom = JS_ATOM_Array;+        } else if (JS_IsFunction(ctx, obj)) {+            atom = JS_ATOM_Function;+        } else {+            p = JS_VALUE_GET_OBJ(obj);+            switch(p->class_id) {+            case JS_CLASS_STRING:+            case JS_CLASS_ARGUMENTS:+            case JS_CLASS_MAPPED_ARGUMENTS:+            case JS_CLASS_ERROR:+            case JS_CLASS_BOOLEAN:+            case JS_CLASS_NUMBER:+            case JS_CLASS_DATE:+            case JS_CLASS_REGEXP:+                atom = ctx->rt->class_array[p->class_id].class_name;+                break;+            default:+                atom = JS_ATOM_Object;+                break;+            }+        }+        tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);+        JS_FreeValue(ctx, obj);+        if (JS_IsException(tag))+            return JS_EXCEPTION;+        if (!JS_IsString(tag)) {+            JS_FreeValue(ctx, tag);+            tag = JS_AtomToString(ctx, atom);+        }+    }+    return JS_ConcatString3(ctx, "[object ", tag, "]");+}++static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);+}++static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    // Object.assign(obj, source1)+    JSValue obj, s;+    int i;++    s = JS_UNDEFINED;+    obj = JS_ToObject(ctx, argv[0]);+    if (JS_IsException(obj))+        goto exception;+    for (i = 1; i < argc; i++) {+        if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {+            s = JS_ToObject(ctx, argv[i]);+            if (JS_IsException(s))+                goto exception;+            if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))+                goto exception;+            JS_FreeValue(ctx, s);+        }+    }+    return obj;+exception:+    JS_FreeValue(ctx, obj);+    JS_FreeValue(ctx, s);+    return JS_EXCEPTION;+}++static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int freeze_flag)+{+    JSValueConst obj = argv[0];+    JSObject *p;+    JSPropertyEnum *props;+    uint32_t len, i;+    int flags, desc_flags, res;++    if (!JS_IsObject(obj))+        return JS_DupValue(ctx, obj);++    res = JS_PreventExtensions(ctx, obj);+    if (res < 0)+        return JS_EXCEPTION;+    if (!res) {+        return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");+    }+    +    p = JS_VALUE_GET_OBJ(obj);+    flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;+    if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))+        return JS_EXCEPTION;++    for(i = 0; i < len; i++) {+        JSPropertyDescriptor desc;+        JSAtom prop = props[i].atom;++        desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;+        if (freeze_flag) {+            res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+            if (res < 0)+                goto exception;+            if (res) {+                if (desc.flags & JS_PROP_WRITABLE)+                    desc_flags |= JS_PROP_HAS_WRITABLE;+                js_free_desc(ctx, &desc);+            }+        }+        if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,+                              JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)+            goto exception;+    }+    js_free_prop_enum(ctx, props, len);+    return JS_DupValue(ctx, obj);++ exception:+    js_free_prop_enum(ctx, props, len);+    return JS_EXCEPTION;+}++static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv, int is_frozen)+{+    JSValueConst obj = argv[0];+    JSObject *p;+    JSPropertyEnum *props;+    uint32_t len, i;+    int flags, res;+    +    if (!JS_IsObject(obj))+        return JS_TRUE;++    p = JS_VALUE_GET_OBJ(obj);+    flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;+    if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))+        return JS_EXCEPTION;++    for(i = 0; i < len; i++) {+        JSPropertyDescriptor desc;+        JSAtom prop = props[i].atom;++        res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+        if (res < 0)+            goto exception;+        if (res) {+            js_free_desc(ctx, &desc);+            if ((desc.flags & JS_PROP_CONFIGURABLE)+            ||  (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {+                res = FALSE;+                goto done;+            }+        }+    }+    res = JS_IsExtensible(ctx, obj);+    if (res < 0)+        return JS_EXCEPTION;+    res ^= 1;+done:        +    js_free_prop_enum(ctx, props, len);+    return JS_NewBool(ctx, res);++exception:+    js_free_prop_enum(ctx, props, len);+    return JS_EXCEPTION;+}++static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSValue obj, iter, next_method = JS_UNDEFINED;+    JSValueConst iterable;+    BOOL done;++    /*  RequireObjectCoercible() not necessary because it is tested in+        JS_GetIterator() by JS_GetProperty() */+    iterable = argv[0];++    obj = JS_NewObject(ctx);+    if (JS_IsException(obj))+        return obj;+    +    iter = JS_GetIterator(ctx, iterable, FALSE);+    if (JS_IsException(iter))+        goto fail;+    next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+    if (JS_IsException(next_method))+        goto fail;+    +    for(;;) {+        JSValue key, value, item;+        item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+        if (JS_IsException(item))+            goto fail;+        if (done) {+            JS_FreeValue(ctx, item);+            break;+        }+        +        key = JS_UNDEFINED;+        value = JS_UNDEFINED;+        if (!JS_IsObject(item)) {+            JS_ThrowTypeErrorNotAnObject(ctx);+            goto fail1;+        }+        key = JS_GetPropertyUint32(ctx, item, 0);+        if (JS_IsException(key))+            goto fail1;+        value = JS_GetPropertyUint32(ctx, item, 1);+        if (JS_IsException(value)) {+            JS_FreeValue(ctx, key);+            goto fail1;+        }+        if (JS_DefinePropertyValueValue(ctx, obj, key, value,+                                        JS_PROP_C_W_E | JS_PROP_THROW) < 0) {+        fail1:+            JS_FreeValue(ctx, item);+            goto fail;+        }+        JS_FreeValue(ctx, item);+    }+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    return obj;+ fail:+    if (JS_IsObject(iter)) {+        /* close the iterator object, preserving pending exception */+        JS_IteratorClose(ctx, iter, TRUE);+    }+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++#if 0+/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */+static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv)+{+    int ret;+    ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),+                                      JS_DupValue(ctx, argv[2]),+                                      JS_PROP_C_W_E | JS_PROP_THROW);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    return JS_ToObject(ctx, argv[0]);+}++static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    int hint = HINT_NONE;++    if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)+        hint = JS_VALUE_GET_INT(argv[1]);++    return JS_ToPrimitive(ctx, argv[0], hint);+}+#endif++/* return an empty string if not an object */+static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSAtom atom;+    JSObject *p;+    uint32_t tag;+    int class_id;++    tag = JS_VALUE_GET_NORM_TAG(argv[0]);+    if (tag == JS_TAG_OBJECT) {+        p = JS_VALUE_GET_OBJ(argv[0]);+        class_id = p->class_id;+        if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))+            class_id = JS_CLASS_BYTECODE_FUNCTION;+        atom = ctx->rt->class_array[class_id].class_name;+    } else {+        atom = JS_ATOM_empty_string;+    }+    return JS_AtomToString(ctx, atom);+}++static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));+}++#if 0+static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    return JS_GetObjectData(ctx, argv[0]);+}++static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))+        return JS_EXCEPTION;+    return JS_DupValue(ctx, argv[1]);+}++static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    return JS_ToPropertyKey(ctx, argv[0]);+}++static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    return JS_NewBool(ctx, JS_IsObject(argv[0]));+}++static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,+                                           int argc, JSValueConst *argv)+{+    return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));+}++static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));+}+#endif++static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,+                                     JSValueConst defaultConstructor)+{+    JSValue ctor, species;++    if (!JS_IsObject(obj))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);+    if (JS_IsException(ctor))+        return ctor;+    if (JS_IsUndefined(ctor))+        return JS_DupValue(ctx, defaultConstructor);+    if (!JS_IsObject(ctor)) {+        JS_FreeValue(ctx, ctor);+        return JS_ThrowTypeErrorNotAnObject(ctx);+    }+    species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);+    JS_FreeValue(ctx, ctor);+    if (JS_IsException(species))+        return species;+    if (JS_IsUndefined(species) || JS_IsNull(species))+        return JS_DupValue(ctx, defaultConstructor);+    if (!JS_IsConstructor(ctx, species)) {+        JS_FreeValue(ctx, species);+        return JS_ThrowTypeError(ctx, "not a constructor");+    }+    return species;+}++#if 0+static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,+                                              int argc, JSValueConst *argv)+{+    return JS_SpeciesConstructor(ctx, argv[0], argv[1]);+}+#endif++static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)+{+    JSValue val, ret;++    val = JS_ToObject(ctx, this_val);+    if (JS_IsException(val))+        return val;+    ret = JS_GetPrototype(ctx, val);+    JS_FreeValue(ctx, val);+    return ret;+}++static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,+                                       JSValueConst proto)+{+    if (JS_IsUndefined(this_val) || JS_IsNull(this_val))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    if (!JS_IsObject(proto) && !JS_IsNull(proto))+        return JS_UNDEFINED;+    if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)+        return JS_EXCEPTION;+    else+        return JS_UNDEFINED;+}++static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValue obj, v1;+    JSValueConst v;+    int res;++    v = argv[0];+    if (!JS_IsObject(v))+        return JS_FALSE;+    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    v1 = JS_DupValue(ctx, v);+    for(;;) {+        v1 = JS_GetPrototypeFree(ctx, v1);+        if (JS_IsException(v1))+            goto exception;+        if (JS_IsNull(v1)) {+            res = FALSE;+            break;+        }+        if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {+            res = TRUE;+            break;+        }+        /* avoid infinite loop (possible with proxies) */+        if (js_poll_interrupts(ctx))+            goto exception;+    }+    JS_FreeValue(ctx, v1);+    JS_FreeValue(ctx, obj);+    return JS_NewBool(ctx, res);++exception:+    JS_FreeValue(ctx, v1);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,+                                              int argc, JSValueConst *argv)+{+    JSValue obj, res = JS_EXCEPTION;+    JSAtom prop = JS_ATOM_NULL;+    JSPropertyDescriptor desc;+    int has_prop;++    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj))+        goto exception;+    prop = JS_ValueToAtom(ctx, argv[0]);+    if (unlikely(prop == JS_ATOM_NULL))+        goto exception;++    has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);+    if (has_prop < 0)+        goto exception;+    if (has_prop) {+        res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);+        js_free_desc(ctx, &desc);+    } else {+        res = JS_FALSE;+    }++exception:+    JS_FreeAtom(ctx, prop);+    JS_FreeValue(ctx, obj);+    return res;+}++static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv, int setter)+{+    JSValue obj, res = JS_EXCEPTION;+    JSAtom prop = JS_ATOM_NULL;+    JSPropertyDescriptor desc;+    int has_prop;++    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj))+        goto exception;+    prop = JS_ValueToAtom(ctx, argv[0]);+    if (unlikely(prop == JS_ATOM_NULL))+        goto exception;++    for (;;) {+        has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);+        if (has_prop < 0)+            goto exception;+        if (has_prop) {+            if (desc.flags & JS_PROP_GETSET)+                res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);+            else+                res = JS_UNDEFINED;+            js_free_desc(ctx, &desc);+            break;+        }+        obj = JS_GetPrototypeFree(ctx, obj);+        if (JS_IsException(obj))+            goto exception;+        if (JS_IsNull(obj)) {+            res = JS_UNDEFINED;+            break;+        }+        /* avoid infinite loop (possible with proxies) */+        if (js_poll_interrupts(ctx))+            goto exception;+    }++exception:+    JS_FreeAtom(ctx, prop);+    JS_FreeValue(ctx, obj);+    return res;+}++static const JSCFunctionListEntry js_object_funcs[] = {+    JS_CFUNC_DEF("create", 2, js_object_create ),+    JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),+    JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),+    JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),+    JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),+    JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),+    JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),+    JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),+    JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),+    JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),+    JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),+    JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),+    JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),+    JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),+    JS_CFUNC_DEF("is", 2, js_object_is ),+    JS_CFUNC_DEF("assign", 2, js_object_assign ),+    JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),+    JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),+    JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),+    JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),+    JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),+    //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),+    //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),+    //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),+    //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),+    //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),+    //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),+    //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),+    //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),+    //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),+    //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),+    JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),+};++static const JSCFunctionListEntry js_object_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_object_toString ),+    JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),+    JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),+    JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),+    JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),+    JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),+    JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),+    JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),+    JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),+    JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),+    JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),+};++/* Function class */++static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    return JS_UNDEFINED;+}++/* XXX: add a specific eval mode so that Function("}), ({") is rejected */+static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,+                                       int argc, JSValueConst *argv, int magic)+{+    JSFunctionKindEnum func_kind = magic;+    int i, n, ret;+    JSValue s, proto, obj = JS_UNDEFINED;+    StringBuffer b_s, *b = &b_s;++    string_buffer_init(ctx, b, 0);+    string_buffer_putc8(b, '(');+    +    if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {+        string_buffer_puts8(b, "async ");+    }+    string_buffer_puts8(b, "function");++    if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {+        string_buffer_putc8(b, '*');+    }+    string_buffer_puts8(b, " anonymous(");++    n = argc - 1;+    for(i = 0; i < n; i++) {+        if (i != 0) {+            string_buffer_putc8(b, ',');+        }+        if (string_buffer_concat_value(b, argv[i]))+            goto fail;+    }+    string_buffer_puts8(b, "\n) {\n");+    if (n >= 0) {+        if (string_buffer_concat_value(b, argv[n]))+            goto fail;+    }+    string_buffer_puts8(b, "\n})");+    s = string_buffer_end(b);+    if (JS_IsException(s))+        goto fail1;++    obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);+    JS_FreeValue(ctx, s);+    if (JS_IsException(obj))+        goto fail1;+    if (!JS_IsUndefined(new_target)) {+        /* set the prototype */+        proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);+        if (JS_IsException(proto))+            goto fail1;+        if (!JS_IsObject(proto)) {+            JSContext *realm;+            JS_FreeValue(ctx, proto);+            realm = JS_GetFunctionRealm(ctx, new_target);+            if (!realm)+                goto fail1;+            proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);+        }+        ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);+        JS_FreeValue(ctx, proto);+        if (ret < 0)+            goto fail1;+    }+    return obj;++ fail:+    string_buffer_free(b);+ fail1:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,+                                       JSValueConst obj)+{+    JSValue len_val;+    len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);+    if (JS_IsException(len_val)) {+        *pres = 0;+        return -1;+    }+    return JS_ToUint32Free(ctx, pres, len_val);+}++static __exception int js_get_length64(JSContext *ctx, int64_t *pres,+                                       JSValueConst obj)+{+    JSValue len_val;+    len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);+    if (JS_IsException(len_val)) {+        *pres = 0;+        return -1;+    }+    return JS_ToLengthFree(ctx, pres, len_val);+}++static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)+{+    uint32_t i;+    for(i = 0; i < len; i++) {+        JS_FreeValue(ctx, tab[i]);+    }+    js_free(ctx, tab);+}++/* XXX: should use ValueArray */+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,+                               JSValueConst array_arg)+{+    uint32_t len, i;+    JSValue *tab, ret;+    JSObject *p;++    if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {+        JS_ThrowTypeError(ctx, "not a object");+        return NULL;+    }+    if (js_get_length32(ctx, &len, array_arg))+        return NULL;+    /* avoid allocating 0 bytes */+    tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));+    if (!tab)+        return NULL;+    p = JS_VALUE_GET_OBJ(array_arg);+    if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&+        p->fast_array &&+        len == p->u.array.count) {+        for(i = 0; i < len; i++) {+            tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);+        }+    } else {+        for(i = 0; i < len; i++) {+            ret = JS_GetPropertyUint32(ctx, array_arg, i);+            if (JS_IsException(ret)) {+                free_arg_list(ctx, tab, i);+                return NULL;+            }+            tab[i] = ret;+        }+    }+    *plen = len;+    return tab;+}++static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv, int magic)+{+    JSValueConst this_arg, array_arg;+    uint32_t len;+    JSValue *tab, ret;++    if (check_function(ctx, this_val))+        return JS_EXCEPTION;+    this_arg = argv[0];+    array_arg = argv[1];+    if (JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||+        JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) {+        return JS_Call(ctx, this_val, this_arg, 0, NULL);+    }+    tab = build_arg_list(ctx, &len, array_arg);+    if (!tab)+        return JS_EXCEPTION;+    if (magic) {+        ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);+    } else {+        ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);+    }+    free_arg_list(ctx, tab, len);+    return ret;+}++static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    if (argc <= 0) {+        return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);+    } else {+        return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);+    }+}++static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSBoundFunction *bf;+    JSValue func_obj, name1;+    JSObject *p;+    int arg_count, i;+    uint32_t len1;++    if (check_function(ctx, this_val))+        return JS_EXCEPTION;++    func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+                                 JS_CLASS_BOUND_FUNCTION);+    if (JS_IsException(func_obj))+        return JS_EXCEPTION;+    p = JS_VALUE_GET_OBJ(func_obj);+    p->is_constructor = JS_IsConstructor(ctx, this_val);+    arg_count = max_int(0, argc - 1);+    bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));+    if (!bf)+        goto exception;+    bf->func_obj = JS_DupValue(ctx, this_val);+    bf->this_val = JS_DupValue(ctx, argv[0]);+    bf->argc = arg_count;+    for(i = 0; i < arg_count; i++) {+        bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);+    }+    p->u.bound_function = bf;++    name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);+    if (JS_IsException(name1))+        goto exception;+    if (!JS_IsString(name1)) {+        JS_FreeValue(ctx, name1);+        name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);+    }+    name1 = JS_ConcatString3(ctx, "bound ", name1, "");+    if (JS_IsException(name1))+        goto exception;+    JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,+                           JS_PROP_CONFIGURABLE);+    if (js_get_length32(ctx, &len1, this_val))+        goto exception;+    if (len1 <= (uint32_t)arg_count)+        len1 = 0;+    else+        len1 -= arg_count;+    JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,+                           JS_NewUint32(ctx, len1),+                           JS_PROP_CONFIGURABLE);+    return func_obj;+ exception:+    JS_FreeValue(ctx, func_obj);+    return JS_EXCEPTION;+}++static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSObject *p;+    JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;++    if (check_function(ctx, this_val))+        return JS_EXCEPTION;++    p = JS_VALUE_GET_OBJ(this_val);+    if (js_class_has_bytecode(p->class_id)) {+        JSFunctionBytecode *b = p->u.func.function_bytecode;+        if (b->has_debug && b->debug.source) {+            return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);+        }+        func_kind = b->func_kind;+    }+    {+        JSValue name;+        const char *pref, *suff;++        switch(func_kind) {+        default:+        case JS_FUNC_NORMAL:+            pref = "function ";+            break;+        case JS_FUNC_GENERATOR:+            pref = "function *";+            break;+        case JS_FUNC_ASYNC:+            pref = "async function ";+            break;+        case JS_FUNC_ASYNC_GENERATOR:+            pref = "async function *";+            break;+        }+        suff = "() {\n    [native code]\n}";+        name = JS_GetProperty(ctx, this_val, JS_ATOM_name);+        if (JS_IsUndefined(name))+            name = JS_AtomToString(ctx, JS_ATOM_empty_string);+        return JS_ConcatString3(ctx, pref, name, suff);+    }+}++static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    int ret;+    ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static const JSCFunctionListEntry js_function_proto_funcs[] = {+    JS_CFUNC_DEF("call", 1, js_function_call ),+    JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),+    JS_CFUNC_DEF("bind", 1, js_function_bind ),+    JS_CFUNC_DEF("toString", 0, js_function_toString ),+    JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),+    JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),+    JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),+};++/* Error class */++static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)+{+    JSValue iter, next_method = JS_UNDEFINED;+    JSValue v, r = JS_UNDEFINED;+    int64_t k;+    BOOL done;+    +    iter = JS_GetIterator(ctx, items, FALSE);+    if (JS_IsException(iter))+        goto exception;+    next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+    if (JS_IsException(next_method))+        goto exception;+    r = JS_NewArray(ctx);+    if (JS_IsException(r))+        goto exception;+    for (k = 0;; k++) {+        v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+        if (JS_IsException(v))+            goto exception_close;+        if (done)+            break;+        if (JS_DefinePropertyValueInt64(ctx, r, k, v,+                                        JS_PROP_C_W_E | JS_PROP_THROW) < 0)+            goto exception_close;+    }+ done:+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    return r;+ exception_close:+    JS_IteratorClose(ctx, iter, TRUE);+ exception:+    JS_FreeValue(ctx, r);+    r = JS_EXCEPTION;+    goto done;+}++static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,+                                    int argc, JSValueConst *argv, int magic)+{+    JSValue obj, msg, proto;+    JSValueConst message;++    if (JS_IsUndefined(new_target))+        new_target = JS_GetActiveFunction(ctx);+    proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);+    if (JS_IsException(proto))+        return proto;+    if (!JS_IsObject(proto)) {+        JSContext *realm;+        JSValueConst proto1;+        +        JS_FreeValue(ctx, proto);+        realm = JS_GetFunctionRealm(ctx, new_target);+        if (!realm)+            return JS_EXCEPTION;+        if (magic < 0) {+            proto1 = realm->class_proto[JS_CLASS_ERROR];+        } else {+            proto1 = realm->native_error_proto[magic];+        }+        proto = JS_DupValue(ctx, proto1);+    }+    obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);+    JS_FreeValue(ctx, proto);+    if (JS_IsException(obj))+        return obj;+    if (magic == JS_AGGREGATE_ERROR) {+        message = argv[1];+    } else {+        message = argv[0];+    }++    if (!JS_IsUndefined(message)) {+        msg = JS_ToString(ctx, message);+        if (unlikely(JS_IsException(msg)))+            goto exception;+        JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    }++    if (magic == JS_AGGREGATE_ERROR) {+        JSValue error_list = iterator_to_array(ctx, argv[0]);+        if (JS_IsException(error_list))+            goto exception;+        JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    }++    /* skip the Error() function in the backtrace */+    build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);+    return obj;+ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue name, msg;++    if (!JS_IsObject(this_val))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    name = JS_GetProperty(ctx, this_val, JS_ATOM_name);+    if (JS_IsUndefined(name))+        name = JS_AtomToString(ctx, JS_ATOM_Error);+    else+        name = JS_ToStringFree(ctx, name);+    if (JS_IsException(name))+        return JS_EXCEPTION;++    msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);+    if (JS_IsUndefined(msg))+        msg = JS_AtomToString(ctx, JS_ATOM_empty_string);+    else+        msg = JS_ToStringFree(ctx, msg);+    if (JS_IsException(msg)) {+        JS_FreeValue(ctx, name);+        return JS_EXCEPTION;+    }+    if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))+        name = JS_ConcatString3(ctx, "", name, ": ");+    return JS_ConcatString(ctx, name, msg);+}++static const JSCFunctionListEntry js_error_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_error_toString ),+    JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+    JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++/* AggregateError */++/* used by C code. */+static JSValue js_aggregate_error_constructor(JSContext *ctx,+                                              JSValueConst errors)+{+    JSValue obj;+    +    obj = JS_NewObjectProtoClass(ctx,+                                 ctx->native_error_proto[JS_AGGREGATE_ERROR],+                                 JS_CLASS_ERROR);+    if (JS_IsException(obj))+        return obj;+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    return obj;+}++/* Array */++static int JS_CopySubArray(JSContext *ctx,+                           JSValueConst obj, int64_t to_pos,+                           int64_t from_pos, int64_t count, int dir)+{+    int64_t i, from, to;+    JSValue val;+    int fromPresent;++    /* XXX: should special case fast arrays */+    for (i = 0; i < count; i++) {+        if (dir < 0) {+            from = from_pos + count - i - 1;+            to = to_pos + count - i - 1;+        } else {+            from = from_pos + i;+            to = to_pos + i;+        }+        fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);+        if (fromPresent < 0)+            goto exception;++        if (fromPresent) {+            if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)+                goto exception;+        } else {+            if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)+                goto exception;+        }+    }+    return 0;++ exception:+    return -1;+}++static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,+                                    int argc, JSValueConst *argv)+{+    JSValue obj;+    int i;++    obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);+    if (JS_IsException(obj))+        return obj;+    if (argc == 1 && JS_IsNumber(argv[0])) {+        uint32_t len;+        if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))+            goto fail;+        if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)+            goto fail;+    } else {+        for(i = 0; i < argc; i++) {+            if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)+                goto fail;+        }+    }+    return obj;+fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    // from(items, mapfn = void 0, this_arg = void 0)+    JSValueConst items = argv[0], mapfn, this_arg;+    JSValueConst args[2];+    JSValue stack[2];+    JSValue iter, r, v, v2, arrayLike;+    int64_t k, len;+    int done, mapping;++    mapping = FALSE;+    mapfn = JS_UNDEFINED;+    this_arg = JS_UNDEFINED;+    r = JS_UNDEFINED;+    arrayLike = JS_UNDEFINED;+    stack[0] = JS_UNDEFINED;+    stack[1] = JS_UNDEFINED;++    if (argc > 1) {+        mapfn = argv[1];+        if (!JS_IsUndefined(mapfn)) {+            if (check_function(ctx, mapfn))+                goto exception;+            mapping = 1;+            if (argc > 2)+                this_arg = argv[2];+        }+    }+    iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);+    if (JS_IsException(iter))+        goto exception;+    if (!JS_IsUndefined(iter)) {+        JS_FreeValue(ctx, iter);+        if (JS_IsConstructor(ctx, this_val))+            r = JS_CallConstructor(ctx, this_val, 0, NULL);+        else+            r = JS_NewArray(ctx);+        if (JS_IsException(r))+            goto exception;+        stack[0] = JS_DupValue(ctx, items);+        if (js_for_of_start(ctx, &stack[1], FALSE))+            goto exception;+        for (k = 0;; k++) {+            v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);+            if (JS_IsException(v))+                goto exception_close;+            if (done)+                break;+            if (mapping) {+                args[0] = v;+                args[1] = JS_NewInt32(ctx, k);+                v2 = JS_Call(ctx, mapfn, this_arg, 2, args);+                JS_FreeValue(ctx, v);+                v = v2;+                if (JS_IsException(v))+                    goto exception_close;+            }+            if (JS_DefinePropertyValueInt64(ctx, r, k, v,+                                            JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                goto exception_close;+        }+    } else {+        arrayLike = JS_ToObject(ctx, items);+        if (JS_IsException(arrayLike))+            goto exception;+        if (js_get_length64(ctx, &len, arrayLike) < 0)+            goto exception;+        v = JS_NewInt64(ctx, len);+        args[0] = v;+        if (JS_IsConstructor(ctx, this_val)) {+            r = JS_CallConstructor(ctx, this_val, 1, args);+        } else {+            r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);+        }+        JS_FreeValue(ctx, v);+        if (JS_IsException(r))+            goto exception;+        for(k = 0; k < len; k++) {+            v = JS_GetPropertyInt64(ctx, arrayLike, k);+            if (JS_IsException(v))+                goto exception;+            if (mapping) {+                args[0] = v;+                args[1] = JS_NewInt32(ctx, k);+                v2 = JS_Call(ctx, mapfn, this_arg, 2, args);+                JS_FreeValue(ctx, v);+                v = v2;+                if (JS_IsException(v))+                    goto exception;+            }+            if (JS_DefinePropertyValueInt64(ctx, r, k, v,+                                            JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                goto exception;+        }+    }+    if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)+        goto exception;+    goto done;++ exception_close:+    if (!JS_IsUndefined(stack[0]))+        JS_IteratorClose(ctx, stack[0], TRUE);+ exception:+    JS_FreeValue(ctx, r);+    r = JS_EXCEPTION;+ done:+    JS_FreeValue(ctx, arrayLike);+    JS_FreeValue(ctx, stack[0]);+    JS_FreeValue(ctx, stack[1]);+    return r;+}++static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    JSValue obj, args[1];+    int i;++    if (JS_IsConstructor(ctx, this_val)) {+        args[0] = JS_NewInt32(ctx, argc);+        obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);+    } else {+        obj = JS_NewArray(ctx);+    }+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    for(i = 0; i < argc; i++) {+        if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),+                                        JS_PROP_THROW) < 0) {+            goto fail;+        }+    }+    if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {+    fail:+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    return obj;+}++static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    int ret;+    ret = JS_IsArray(ctx, argv[0]);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_get_this(JSContext *ctx,+                           JSValueConst this_val)+{+    return JS_DupValue(ctx, this_val);+}++static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,+                                     JSValueConst len_val)+{+    JSValue ctor, ret, species;+    int res;+    JSContext *realm;+    +    res = JS_IsArray(ctx, obj);+    if (res < 0)+        return JS_EXCEPTION;+    if (!res)+        return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);+    ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);+    if (JS_IsException(ctor))+        return ctor;+    if (JS_IsConstructor(ctx, ctor)) {+        /* legacy web compatibility */+        realm = JS_GetFunctionRealm(ctx, ctor);+        if (!realm) {+            JS_FreeValue(ctx, ctor);+            return JS_EXCEPTION;+        }+        if (realm != ctx &&+            js_same_value(ctx, ctor, realm->array_ctor)) {+            JS_FreeValue(ctx, ctor);+            ctor = JS_UNDEFINED;+        }+    }+    if (JS_IsObject(ctor)) {+        species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);+        JS_FreeValue(ctx, ctor);+        if (JS_IsException(species))+            return species;+        ctor = species;+        if (JS_IsNull(ctor))+            ctor = JS_UNDEFINED;+    }+    if (JS_IsUndefined(ctor)) {+        return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);+    } else {+        ret = JS_CallConstructor(ctx, ctor, 1, &len_val);+        JS_FreeValue(ctx, ctor);+        return ret;+    }+}++static const JSCFunctionListEntry js_array_funcs[] = {+    JS_CFUNC_DEF("isArray", 1, js_array_isArray ),+    JS_CFUNC_DEF("from", 1, js_array_from ),+    JS_CFUNC_DEF("of", 0, js_array_of ),+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)+{+    JSValue val;++    if (!JS_IsObject(obj))+        return FALSE;+    val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);+    if (JS_IsException(val))+        return -1;+    if (!JS_IsUndefined(val))+        return JS_ToBoolFree(ctx, val);+    return JS_IsArray(ctx, obj);+}++static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    JSValue obj, arr, val;+    JSValueConst e;+    int64_t len, k, n;+    int i, res;++    arr = JS_UNDEFINED;+    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj))+        goto exception;++    arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));+    if (JS_IsException(arr))+        goto exception;+    n = 0;+    for (i = -1; i < argc; i++) {+        if (i < 0)+            e = obj;+        else+            e = argv[i];++        res = JS_isConcatSpreadable(ctx, e);+        if (res < 0)+            goto exception;+        if (res) {+            if (js_get_length64(ctx, &len, e))+                goto exception;+            if (n + len > MAX_SAFE_INTEGER) {+                JS_ThrowTypeError(ctx, "Array loo long");+                goto exception;+            }+            for (k = 0; k < len; k++, n++) {+                res = JS_TryGetPropertyInt64(ctx, e, k, &val);+                if (res < 0)+                    goto exception;+                if (res) {+                    if (JS_DefinePropertyValueInt64(ctx, arr, n, val,+                                                    JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                        goto exception;+                }+            }+        } else {+            if (n >= MAX_SAFE_INTEGER) {+                JS_ThrowTypeError(ctx, "Array loo long");+                goto exception;+            }+            if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),+                                            JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                goto exception;+            n++;+        }+    }+    if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)+        goto exception;++    JS_FreeValue(ctx, obj);+    return arr;++exception:+    JS_FreeValue(ctx, arr);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++#define special_every    0+#define special_some     1+#define special_forEach  2+#define special_map      3+#define special_filter   4+#define special_TA       8++static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);++static JSValue js_typed_array___speciesCreate(JSContext *ctx,+                                              JSValueConst this_val,+                                              int argc, JSValueConst *argv);++static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int special)+{+    JSValue obj, val, index_val, res, ret;+    JSValueConst args[3];+    JSValueConst func, this_arg;+    int64_t len, k, n;+    int present;++    ret = JS_UNDEFINED;+    val = JS_UNDEFINED;+    if (special & special_TA) {+        obj = JS_DupValue(ctx, this_val);+        len = js_typed_array_get_length_internal(ctx, obj);+        if (len < 0)+            goto exception;+    } else {+        obj = JS_ToObject(ctx, this_val);+        if (js_get_length64(ctx, &len, obj))+            goto exception;+    }+    func = argv[0];+    this_arg = JS_UNDEFINED;+    if (argc > 1)+        this_arg = argv[1];+        +    if (check_function(ctx, func))+        goto exception;++    switch (special) {+    case special_every:+    case special_every | special_TA:+        ret = JS_TRUE;+        break;+    case special_some:+    case special_some | special_TA:+        ret = JS_FALSE;+        break;+    case special_map:+        /* XXX: JS_ArraySpeciesCreate should take int64_t */+        ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));+        if (JS_IsException(ret))+            goto exception;+        break;+    case special_filter:+        ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));+        if (JS_IsException(ret))+            goto exception;+        break;+    case special_map | special_TA:+        args[0] = obj;+        args[1] = JS_NewInt32(ctx, len);+        ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);+        if (JS_IsException(ret))+            goto exception;+        break;+    case special_filter | special_TA:+        ret = JS_NewArray(ctx);+        if (JS_IsException(ret))+            goto exception;+        break;+    }+    n = 0;++    for(k = 0; k < len; k++) {+        present = JS_TryGetPropertyInt64(ctx, obj, k, &val);+        if (present < 0)+            goto exception;+        if (present) {+            index_val = JS_NewInt64(ctx, k);+            if (JS_IsException(index_val))+                goto exception;+            args[0] = val;+            args[1] = index_val;+            args[2] = obj;+            res = JS_Call(ctx, func, this_arg, 3, args);+            JS_FreeValue(ctx, index_val);+            if (JS_IsException(res))+                goto exception;+            switch (special) {+            case special_every:+            case special_every | special_TA:+                if (!JS_ToBoolFree(ctx, res)) {+                    ret = JS_FALSE;+                    goto done;+                }+                break;+            case special_some:+            case special_some | special_TA:+                if (JS_ToBoolFree(ctx, res)) {+                    ret = JS_TRUE;+                    goto done;+                }+                break;+            case special_map:+                if (JS_DefinePropertyValueInt64(ctx, ret, k, res,+                                                JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                    goto exception;+                break;+            case special_map | special_TA:+                if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)+                    goto exception;+                break;+            case special_filter:+            case special_filter | special_TA:+                if (JS_ToBoolFree(ctx, res)) {+                    if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),+                                                    JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                        goto exception;+                }+                break;+            default:+                JS_FreeValue(ctx, res);+                break;+            }+            JS_FreeValue(ctx, val);+            val = JS_UNDEFINED;+        }+    }+done:+    if (special == (special_filter | special_TA)) {+        JSValue arr;+        args[0] = obj;+        args[1] = JS_NewInt32(ctx, n);+        arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);+        if (JS_IsException(arr))+            goto exception;+        args[0] = ret;+        res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);+        if (check_exception_free(ctx, res))+            goto exception;+        JS_FreeValue(ctx, ret);+        ret = arr;+    }+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, obj);+    return ret;++exception:+    JS_FreeValue(ctx, ret);+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++#define special_reduce       0+#define special_reduceRight  1++static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv, int special)+{+    JSValue obj, val, index_val, acc, acc1;+    JSValueConst args[4];+    JSValueConst func;+    int64_t len, k, k1;+    int present;++    acc = JS_UNDEFINED;+    val = JS_UNDEFINED;+    if (special & special_TA) {+        obj = JS_DupValue(ctx, this_val);+        len = js_typed_array_get_length_internal(ctx, obj);+        if (len < 0)+            goto exception;+    } else {+        obj = JS_ToObject(ctx, this_val);+        if (js_get_length64(ctx, &len, obj))+            goto exception;+    }+    func = argv[0];++    if (check_function(ctx, func))+        goto exception;++    k = 0;+    if (argc > 1) {+        acc = JS_DupValue(ctx, argv[1]);+    } else {+        for(;;) {+            if (k >= len) {+                JS_ThrowTypeError(ctx, "empty array");+                goto exception;+            }+            k1 = (special & special_reduceRight) ? len - k - 1 : k;+            k++;+            present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);+            if (present < 0)+                goto exception;+            if (present)+                break;+        }+    }+    for (; k < len; k++) {+        k1 = (special & special_reduceRight) ? len - k - 1 : k;+        present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);+        if (present < 0)+            goto exception;+        if (present) {+            index_val = JS_NewInt64(ctx, k1);+            if (JS_IsException(index_val))+                goto exception;+            args[0] = acc;+            args[1] = val;+            args[2] = index_val;+            args[3] = obj;+            acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);+            JS_FreeValue(ctx, index_val);+            JS_FreeValue(ctx, val);+            val = JS_UNDEFINED;+            if (JS_IsException(acc1))+                goto exception;+            JS_FreeValue(ctx, acc);+            acc = acc1;+        }+    }+    JS_FreeValue(ctx, obj);+    return acc;++exception:+    JS_FreeValue(ctx, acc);+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    JSValue obj;+    int64_t len, start, end;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    start = 0;+    if (argc > 1 && !JS_IsUndefined(argv[1])) {+        if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))+            goto exception;+    }++    end = len;+    if (argc > 2 && !JS_IsUndefined(argv[2])) {+        if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))+            goto exception;+    }++    /* XXX: should special case fast arrays */+    while (start < end) {+        if (JS_SetPropertyInt64(ctx, obj, start,+                                JS_DupValue(ctx, argv[0])) < 0)+            goto exception;+        start++;+    }+    return obj;++ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue obj, val;+    int64_t len, n, res;+    JSValue *arrp;+    uint32_t count;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    res = FALSE;+    if (len > 0) {+        n = 0;+        if (argc > 1) {+            if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))+                goto exception;+        }+        if (js_get_fast_array(ctx, obj, &arrp, &count)) {+            for (; n < count; n++) {+                if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),+                                  JS_DupValue(ctx, arrp[n]),+                                  JS_EQ_SAME_VALUE_ZERO)) {+                    res = TRUE;+                    goto done;+                }+            }+        }+        for (; n < len; n++) {+            val = JS_GetPropertyInt64(ctx, obj, n);+            if (JS_IsException(val))+                goto exception;+            if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,+                              JS_EQ_SAME_VALUE_ZERO)) {+                res = TRUE;+                break;+            }+        }+    }+ done:+    JS_FreeValue(ctx, obj);+    return JS_NewBool(ctx, res);++ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue obj, val;+    int64_t len, n, res;+    JSValue *arrp;+    uint32_t count;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    res = -1;+    if (len > 0) {+        n = 0;+        if (argc > 1) {+            if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))+                goto exception;+        }+        if (js_get_fast_array(ctx, obj, &arrp, &count)) {+            for (; n < count; n++) {+                if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),+                                  JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {+                    res = n;+                    goto done;+                }+            }+        }+        for (; n < len; n++) {+            int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);+            if (present < 0)+                goto exception;+            if (present) {+                if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {+                    res = n;+                    break;+                }+            }+        }+    }+ done:+    JS_FreeValue(ctx, obj);+    return JS_NewInt64(ctx, res);++ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValue obj, val;+    int64_t len, n, res;+    int present;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    res = -1;+    if (len > 0) {+        n = len - 1;+        if (argc > 1) {+            if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))+                goto exception;+        }+        /* XXX: should special case fast arrays */+        for (; n >= 0; n--) {+            present = JS_TryGetPropertyInt64(ctx, obj, n, &val);+            if (present < 0)+                goto exception;+            if (present) {+                if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {+                    res = n;+                    break;+                }+            }+        }+    }+    JS_FreeValue(ctx, obj);+    return JS_NewInt64(ctx, res);++ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv, int findIndex)+{+    JSValueConst func, this_arg;+    JSValueConst args[3];+    JSValue obj, val, index_val, res;+    int64_t len, k;++    index_val = JS_UNDEFINED;+    val = JS_UNDEFINED;+    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    func = argv[0];+    if (check_function(ctx, func))+        goto exception;++    this_arg = JS_UNDEFINED;+    if (argc > 1)+        this_arg = argv[1];++    for(k = 0; k < len; k++) {+        index_val = JS_NewInt64(ctx, k);+        if (JS_IsException(index_val))+            goto exception;+        val = JS_GetPropertyValue(ctx, obj, index_val);+        if (JS_IsException(val))+            goto exception;+        args[0] = val;+        args[1] = index_val;+        args[2] = this_val;+        res = JS_Call(ctx, func, this_arg, 3, args);+        if (JS_IsException(res))+            goto exception;+        if (JS_ToBoolFree(ctx, res)) {+            if (findIndex) {+                JS_FreeValue(ctx, val);+                JS_FreeValue(ctx, obj);+                return index_val;+            } else {+                JS_FreeValue(ctx, index_val);+                JS_FreeValue(ctx, obj);+                return val;+            }+        }+        JS_FreeValue(ctx, val);+        JS_FreeValue(ctx, index_val);+    }+    JS_FreeValue(ctx, obj);+    if (findIndex)+        return JS_NewInt32(ctx, -1);+    else+        return JS_UNDEFINED;++exception:+    JS_FreeValue(ctx, index_val);+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue obj, method, ret;++    obj = JS_ToObject(ctx, this_val);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    method = JS_GetProperty(ctx, obj, JS_ATOM_join);+    if (JS_IsException(method)) {+        ret = JS_EXCEPTION;+    } else+    if (!JS_IsFunction(ctx, method)) {+        /* Use intrinsic Object.prototype.toString */+        JS_FreeValue(ctx, method);+        ret = js_object_toString(ctx, obj, 0, NULL);+    } else {+        ret = JS_CallFree(ctx, method, obj, 0, NULL);+    }+    JS_FreeValue(ctx, obj);+    return ret;+}++static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv, int toLocaleString)+{+    JSValue obj, sep = JS_UNDEFINED, el;+    StringBuffer b_s, *b = &b_s;+    JSString *p = NULL;+    int64_t i, n;+    int c;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &n, obj))+        goto exception;++    c = ',';    /* default separator */+    if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {+        sep = JS_ToString(ctx, argv[0]);+        if (JS_IsException(sep))+            goto exception;+        p = JS_VALUE_GET_STRING(sep);+        if (p->len == 1 && !p->is_wide_char)+            c = p->u.str8[0];+        else+            c = -1;+    }+    string_buffer_init(ctx, b, 0);++    for(i = 0; i < n; i++) {+        if (i > 0) {+            if (c >= 0) {+                string_buffer_putc8(b, c);+            } else {+                string_buffer_concat(b, p, 0, p->len);+            }+        }+        el = JS_GetPropertyUint32(ctx, obj, i);+        if (JS_IsException(el))+            goto fail;+        if (!JS_IsNull(el) && !JS_IsUndefined(el)) {+            if (toLocaleString) {+                el = JS_ToLocaleStringFree(ctx, el);+            }+            if (string_buffer_concat_value_free(b, el))+                goto fail;+        }+    }+    JS_FreeValue(ctx, sep);+    JS_FreeValue(ctx, obj);+    return string_buffer_end(b);++fail:+    string_buffer_free(b);+    JS_FreeValue(ctx, sep);+exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv, int shift)+{+    JSValue obj, res = JS_UNDEFINED;+    int64_t len, newLen;+    JSValue *arrp;+    uint32_t count32;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;+    newLen = 0;+    if (len > 0) {+        newLen = len - 1;+        /* Special case fast arrays */+        if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {+            JSObject *p = JS_VALUE_GET_OBJ(obj);+            if (shift) {+                res = arrp[0];+                memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));+                p->u.array.count--;+            } else {+                res = arrp[count32 - 1];+                p->u.array.count--;+            }+        } else {+            if (shift) {+                res = JS_GetPropertyInt64(ctx, obj, 0);+                if (JS_IsException(res))+                    goto exception;+                if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))+                    goto exception;+            } else {+                res = JS_GetPropertyInt64(ctx, obj, newLen);+                if (JS_IsException(res))+                    goto exception;+            }+            if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)+                goto exception;+        }+    }+    if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)+        goto exception;++    JS_FreeValue(ctx, obj);+    return res;++ exception:+    JS_FreeValue(ctx, res);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv, int unshift)+{+    JSValue obj;+    int i;+    int64_t len, from, newLen;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;+    newLen = len + argc;+    if (newLen > MAX_SAFE_INTEGER) {+        JS_ThrowTypeError(ctx, "Array loo long");+        goto exception;+    }+    from = len;+    if (unshift && argc > 0) {+        if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))+            goto exception;+        from = 0;+    }+    for(i = 0; i < argc; i++) {+        if (JS_SetPropertyInt64(ctx, obj, from + i,+                                JS_DupValue(ctx, argv[i])) < 0)+            goto exception;+    }+    if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)+        goto exception;++    JS_FreeValue(ctx, obj);+    return JS_NewInt64(ctx, newLen);++ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue obj, lval, hval;+    JSValue *arrp;+    int64_t len, l, h;+    int l_present, h_present;+    uint32_t count32;++    lval = JS_UNDEFINED;+    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    /* Special case fast arrays */+    if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {+        uint32_t ll, hh;++        if (count32 > 1) {+            for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {+                lval = arrp[ll];+                arrp[ll] = arrp[hh];+                arrp[hh] = lval;+            }+        }+        return obj;+    }++    for (l = 0, h = len - 1; l < h; l++, h--) {+        l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);+        if (l_present < 0)+            goto exception;+        h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);+        if (h_present < 0)+            goto exception;+        if (h_present) {+            if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)+                goto exception;++            if (l_present) {+                if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {+                    lval = JS_UNDEFINED;+                    goto exception;+                }+                lval = JS_UNDEFINED;+            } else {+                if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)+                    goto exception;+            }+        } else {+            if (l_present) {+                if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)+                    goto exception;+                if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {+                    lval = JS_UNDEFINED;+                    goto exception;+                }+                lval = JS_UNDEFINED;+            }+        }+    }+    return obj;++ exception:+    JS_FreeValue(ctx, lval);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int splice)+{+    JSValue obj, arr, val, len_val;+    int64_t len, start, k, final, n, count, del_count, new_len;+    int kPresent;+    JSValue *arrp;+    uint32_t count32, i, item_count;++    arr = JS_UNDEFINED;+    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))+        goto exception;++    if (splice) {+        if (argc == 0) {+            item_count = 0;+            del_count = 0;+        } else+        if (argc == 1) {+            item_count = 0;+            del_count = len - start;+        } else {+            item_count = argc - 2;+            if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))+                goto exception;+        }+        if (len + item_count - del_count > MAX_SAFE_INTEGER) {+            JS_ThrowTypeError(ctx, "Array loo long");+            goto exception;+        }+        count = del_count;+    } else {+        item_count = 0; /* avoid warning */+        final = len;+        if (!JS_IsUndefined(argv[1])) {+            if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))+                goto exception;+        }+        count = max_int64(final - start, 0);+    }+    len_val = JS_NewInt64(ctx, count);+    arr = JS_ArraySpeciesCreate(ctx, obj, len_val);+    JS_FreeValue(ctx, len_val);+    if (JS_IsException(arr))+        goto exception;++    k = start;+    final = start + count;+    n = 0;+    /* The fast array test on arr ensures that+       JS_CreateDataPropertyUint32() won't modify obj in case arr is+       an exotic object */+    /* Special case fast arrays */+    if (js_get_fast_array(ctx, obj, &arrp, &count32) &&+        js_is_fast_array(ctx, arr)) {+        /* XXX: should share code with fast array constructor */+        for (; k < final && k < count32; k++, n++) {+            if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)+                goto exception;+        }+    }+    /* Copy the remaining elements if any (handle case of inherited properties) */+    for (; k < final; k++, n++) {+        kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);+        if (kPresent < 0)+            goto exception;+        if (kPresent) {+            if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)+                goto exception;+        }+    }+    if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)+        goto exception;++    if (splice) {+        new_len = len + item_count - del_count;+        if (item_count != del_count) {+            if (JS_CopySubArray(ctx, obj, start + item_count,+                                start + del_count, len - (start + del_count),+                                item_count <= del_count ? +1 : -1) < 0)+                goto exception;++            for (k = len; k-- > new_len; ) {+                if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)+                    goto exception;+            }+        }+        for (i = 0; i < item_count; i++) {+            if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)+                goto exception;+        }+        if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)+            goto exception;+    }+    JS_FreeValue(ctx, obj);+    return arr;++ exception:+    JS_FreeValue(ctx, obj);+    JS_FreeValue(ctx, arr);+    return JS_EXCEPTION;+}++static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSValue obj;+    int64_t len, from, to, final, count;++    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))+        goto exception;++    if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))+        goto exception;++    final = len;+    if (argc > 2 && !JS_IsUndefined(argv[2])) {+        if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))+            goto exception;+    }++    count = min_int64(final - from, len - to);++    if (JS_CopySubArray(ctx, obj, to, from, count,+                        (from < to && to < from + count) ? -1 : +1))+        goto exception;++    return obj;++ exception:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,+                                   JSValueConst source, int64_t sourceLen,+                                   int64_t targetIndex, int depth,+                                   JSValueConst mapperFunction,+                                   JSValueConst thisArg)+{+    JSValue element;+    int64_t sourceIndex, elementLen;+    int present, is_array;++    for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {+        present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);+        if (present < 0)+            return -1;+        if (!present)+            continue;+        if (!JS_IsUndefined(mapperFunction)) {+            JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };+            element = JS_Call(ctx, mapperFunction, thisArg, 3, args);+            JS_FreeValue(ctx, (JSValue)args[0]);+            JS_FreeValue(ctx, (JSValue)args[1]);+            if (JS_IsException(element))+                return -1;+        }+        if (depth > 0) {+            is_array = JS_IsArray(ctx, element);+            if (is_array < 0)+                goto fail;+            if (is_array) {+                if (js_get_length64(ctx, &elementLen, element) < 0)+                    goto fail;+                targetIndex = JS_FlattenIntoArray(ctx, target, element,+                                                  elementLen, targetIndex,+                                                  depth - 1,+                                                  JS_UNDEFINED, JS_UNDEFINED);+                if (targetIndex < 0)+                    goto fail;+                JS_FreeValue(ctx, element);+                continue;+            }+        }+        if (targetIndex >= MAX_SAFE_INTEGER) {+            JS_ThrowTypeError(ctx, "Array too long");+            goto fail;+        }+        if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,+                                        JS_PROP_C_W_E | JS_PROP_THROW) < 0)+            return -1;+        targetIndex++;+    }+    return targetIndex;++fail:+    JS_FreeValue(ctx, element);+    return -1;+}++static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv, int map)+{+    JSValue obj, arr;+    JSValueConst mapperFunction, thisArg;+    int64_t sourceLen;+    int depthNum;++    arr = JS_UNDEFINED;+    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &sourceLen, obj))+        goto exception;++    depthNum = 1;+    mapperFunction = JS_UNDEFINED;+    thisArg = JS_UNDEFINED;+    if (map) {+        mapperFunction = argv[0];+        if (argc > 1) {+            thisArg = argv[1];+        }+        if (check_function(ctx, mapperFunction))+            goto exception;+    } else {+        if (argc > 0 && !JS_IsUndefined(argv[0])) {+            if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)+                goto exception;+        }+    }+    arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));+    if (JS_IsException(arr))+        goto exception;+    if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,+                            mapperFunction, thisArg) < 0)+        goto exception;+    JS_FreeValue(ctx, obj);+    return arr;++exception:+    JS_FreeValue(ctx, obj);+    JS_FreeValue(ctx, arr);+    return JS_EXCEPTION;+}++/* Array sort */++typedef struct ValueSlot {+    JSValue val;+    JSString *str;+    int64_t pos;+} ValueSlot;++struct array_sort_context {+    JSContext *ctx;+    int exception;+    int has_method;+    JSValueConst method;+};++static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {+    struct array_sort_context *psc = opaque;+    JSContext *ctx = psc->ctx;+    JSValueConst argv[2];+    JSValue res;+    ValueSlot *ap = (ValueSlot *)(void *)a;+    ValueSlot *bp = (ValueSlot *)(void *)b;+    int cmp;++    if (psc->exception)+        return 0;++    if (psc->has_method) {+        /* custom sort function is specified as returning 0 for identical+         * objects: avoid method call overhead.+         */+        if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))+            goto cmp_same;+        argv[0] = ap->val;+        argv[1] = bp->val;+        res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);+        if (JS_IsException(res))+            goto exception;+        if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {+            int val = JS_VALUE_GET_INT(res);+            cmp = (val > 0) - (val < 0);+        } else {+            double val;+            if (JS_ToFloat64Free(ctx, &val, res) < 0)+                goto exception;+            cmp = (val > 0) - (val < 0);+        }+    } else {+        /* Not supposed to bypass ToString even for identical objects as+         * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js+         */+        if (!ap->str) {+            JSValue str = JS_ToString(ctx, ap->val);+            if (JS_IsException(str))+                goto exception;+            ap->str = JS_VALUE_GET_STRING(str);+        }+        if (!bp->str) {+            JSValue str = JS_ToString(ctx, bp->val);+            if (JS_IsException(str))+                goto exception;+            bp->str = JS_VALUE_GET_STRING(str);+        }+        cmp = js_string_compare(ctx, ap->str, bp->str);+    }+    if (cmp != 0)+        return cmp;+cmp_same:+    /* make sort stable: compare array offsets */+    return (ap->pos > bp->pos) - (ap->pos < bp->pos);++exception:+    psc->exception = 1;+    return 0;+}++static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    struct array_sort_context asc = { ctx, 0, 0, argv[0] };+    JSValue obj = JS_UNDEFINED;+    ValueSlot *array = NULL;+    size_t array_size = 0, pos = 0, n = 0;+    int64_t i, len, undefined_count = 0;+    int present;++    if (!JS_IsUndefined(asc.method)) {+        if (check_function(ctx, asc.method))+            goto exception;+        asc.has_method = 1;+    }+    obj = JS_ToObject(ctx, this_val);+    if (js_get_length64(ctx, &len, obj))+        goto exception;++    /* XXX: should special case fast arrays */+    for (i = 0; i < len; i++) {+        if (pos >= array_size) {+            size_t new_size, slack;+            ValueSlot *new_array;+            new_size = (array_size + (array_size >> 1) + 31) & ~15;+            new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);+            if (new_array == NULL)+                goto exception;+            new_size += slack / sizeof(*new_array);+            array = new_array;+            array_size = new_size;+        }+        present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);+        if (present < 0)+            goto exception;+        if (present == 0)+            continue;+        if (JS_IsUndefined(array[pos].val)) {+            undefined_count++;+            continue;+        }+        array[pos].str = NULL;+        array[pos].pos = i;+        pos++;+    }+    rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);+    if (asc.exception)+        goto exception;++    /* XXX: should special case fast arrays */+    while (n < pos) {+        if (array[n].str)+            JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));+        if (array[n].pos == n) {+            JS_FreeValue(ctx, array[n].val);+        } else {+            if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {+                n++;+                goto exception;+            }+        }+        n++;+    }+    js_free(ctx, array);+    for (i = n; undefined_count-- > 0; i++) {+        if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)+            goto fail;+    }+    for (; i < len; i++) {+        if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)+            goto fail;+    }+    return obj;++exception:+    for (; n < pos; n++) {+        JS_FreeValue(ctx, array[n].val);+        if (array[n].str)+            JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));+    }+    js_free(ctx, array);+fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++typedef struct JSArrayIteratorData {+    JSValue obj;+    JSIteratorKindEnum kind;+    uint32_t idx;+} JSArrayIteratorData;++static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSArrayIteratorData *it = p->u.array_iterator_data;+    if (it) {+        JS_FreeValueRT(rt, it->obj);+        js_free_rt(rt, it);+    }+}++static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,+                                   JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSArrayIteratorData *it = p->u.array_iterator_data;+    if (it) {+        JS_MarkValue(rt, it->obj, mark_func);+    }+}++static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)+{+    JSValue obj;+    int i;++    obj = JS_NewArray(ctx);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    for(i = 0; i < len; i++) {+        if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {+            JS_FreeValue(ctx, obj);+            return JS_EXCEPTION;+        }+    }+    return obj;+}++static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv, int magic)+{+    JSValue enum_obj, arr;+    JSArrayIteratorData *it;+    JSIteratorKindEnum kind;+    int class_id;++    kind = magic & 3;+    if (magic & 4) {+        /* string iterator case */+        arr = JS_ToStringCheckObject(ctx, this_val);+        class_id = JS_CLASS_STRING_ITERATOR;+    } else {+        arr = JS_ToObject(ctx, this_val);+        class_id = JS_CLASS_ARRAY_ITERATOR;+    }+    if (JS_IsException(arr))+        goto fail;+    enum_obj = JS_NewObjectClass(ctx, class_id);+    if (JS_IsException(enum_obj))+        goto fail;+    it = js_malloc(ctx, sizeof(*it));+    if (!it)+        goto fail1;+    it->obj = arr;+    it->kind = kind;+    it->idx = 0;+    JS_SetOpaque(enum_obj, it);+    return enum_obj;+ fail1:+    JS_FreeValue(ctx, enum_obj);+ fail:+    JS_FreeValue(ctx, arr);+    return JS_EXCEPTION;+}++static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv,+                                      BOOL *pdone, int magic)+{+    JSArrayIteratorData *it;+    uint32_t len, idx;+    JSValue val, obj;+    JSObject *p;++    it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);+    if (!it)+        goto fail1;+    if (JS_IsUndefined(it->obj))+        goto done;+    p = JS_VALUE_GET_OBJ(it->obj);+    if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+        p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+        if (typed_array_is_detached(ctx, p)) {+            JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+            goto fail1;+        }+        len = p->u.array.count;+    } else {+        if (js_get_length32(ctx, &len, it->obj)) {+        fail1:+            *pdone = FALSE;+            return JS_EXCEPTION;+        }+    }+    idx = it->idx;+    if (idx >= len) {+        JS_FreeValue(ctx, it->obj);+        it->obj = JS_UNDEFINED;+    done:+        *pdone = TRUE;+        return JS_UNDEFINED;+    }+    it->idx = idx + 1;+    *pdone = FALSE;+    if (it->kind == JS_ITERATOR_KIND_KEY) {+        return JS_NewUint32(ctx, idx);+    } else {+        val = JS_GetPropertyUint32(ctx, it->obj, idx);+        if (JS_IsException(val))+            return JS_EXCEPTION;+        if (it->kind == JS_ITERATOR_KIND_VALUE) {+            return val;+        } else {+            JSValueConst args[2];+            JSValue num;+            num = JS_NewUint32(ctx, idx);+            args[0] = num;+            args[1] = val;+            obj = js_create_array(ctx, 2, args);+            JS_FreeValue(ctx, val);+            JS_FreeValue(ctx, num);+            return obj;+        }+    }+}++static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv)+{+    return JS_DupValue(ctx, this_val);+}++static const JSCFunctionListEntry js_iterator_proto_funcs[] = {+    JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),+};++static const JSCFunctionListEntry js_array_proto_funcs[] = {+    JS_CFUNC_DEF("concat", 1, js_array_concat ),+    JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),+    JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),+    JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),+    JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),+    JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),+    JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),+    JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),+    JS_CFUNC_DEF("fill", 1, js_array_fill ),+    JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),+    JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),+    JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),+    JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),+    JS_CFUNC_DEF("includes", 1, js_array_includes ),+    JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),+    JS_CFUNC_DEF("toString", 0, js_array_toString ),+    JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),+    JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),+    JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),+    JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),+    JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),+    JS_CFUNC_DEF("reverse", 0, js_array_reverse ),+    JS_CFUNC_DEF("sort", 1, js_array_sort ),+    JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),+    JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),+    JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),+    JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),+    JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),+    JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),+    JS_ALIAS_DEF("[Symbol.iterator]", "values" ),+    JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),+    JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),+};++static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {+    JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),+};++/* Number */++static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,+                                     int argc, JSValueConst *argv)+{+    JSValue val, obj;+    if (argc == 0) {+        val = JS_NewInt32(ctx, 0);+    } else {+        val = JS_ToNumeric(ctx, argv[0]);+        if (JS_IsException(val))+            return val;+        switch(JS_VALUE_GET_TAG(val)) {+#ifdef CONFIG_BIGNUM+        case JS_TAG_BIG_INT:+        case JS_TAG_BIG_FLOAT:+            {+                JSBigFloat *p = JS_VALUE_GET_PTR(val);+                double d;+                bf_get_float64(&p->num, &d, BF_RNDN);+                JS_FreeValue(ctx, val);+                val = __JS_NewFloat64(ctx, d);+            }+            break;+        case JS_TAG_BIG_DECIMAL:+            val = JS_ToStringFree(ctx, val);+            if (JS_IsException(val))+                return val;+            val = JS_ToNumberFree(ctx, val);+            if (JS_IsException(val))+                return val;+            break;+#endif+        default:+            break;+        }+    }+    if (!JS_IsUndefined(new_target)) {+        obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);+        if (!JS_IsException(obj))+            JS_SetObjectData(ctx, obj, val);+        return obj;+    } else {+        return val;+    }+}++#if 0+static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));+}++static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    int64_t v;+    if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))+        return JS_EXCEPTION;+    return JS_NewInt64(ctx, v);+}+#endif++static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    if (!JS_IsNumber(argv[0]))+        return JS_FALSE;+    return js_global_isNaN(ctx, this_val, argc, argv);+}++static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    if (!JS_IsNumber(argv[0]))+        return JS_FALSE;+    return js_global_isFinite(ctx, this_val, argc, argv);+}++static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    int ret;+    ret = JS_NumberIsInteger(ctx, argv[0]);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    double d;+    if (!JS_IsNumber(argv[0]))+        return JS_FALSE;+    if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))+        return JS_EXCEPTION;+    return JS_NewBool(ctx, is_safe_integer(d));+}++static const JSCFunctionListEntry js_number_funcs[] = {+    /* global ParseInt and parseFloat should be defined already or delayed */+    JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),+    JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),+    JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),+    JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),+    JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),+    JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),+    JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),+    JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),+    JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),+    JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),+    JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),+    JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */+    JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */+    JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */+    //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),+    //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),+};++static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_IsNumber(this_val))+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_NUMBER) {+            if (JS_IsNumber(p->u.object_data))+                return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a number");+}++static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    return js_thisNumberValue(ctx, this_val);+}++static int js_get_radix(JSContext *ctx, JSValueConst val)+{+    int radix;+    if (JS_ToInt32Sat(ctx, &radix, val))+        return -1;+    if (radix < 2 || radix > 36) {+        JS_ThrowRangeError(ctx, "radix must be between 2 and 36");+        return -1;+    }+    return radix;+}++static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv, int magic)+{+    JSValue val;+    int base;+    double d;++    val = js_thisNumberValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (magic || JS_IsUndefined(argv[0])) {+        base = 10;+    } else {+        base = js_get_radix(ctx, argv[0]);+        if (base < 0)+            goto fail;+    }+    if (JS_ToFloat64Free(ctx, &d, val))+        return JS_EXCEPTION;+    return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue val;+    int f;+    double d;++    val = js_thisNumberValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToFloat64Free(ctx, &d, val))+        return JS_EXCEPTION;+    if (JS_ToInt32Sat(ctx, &f, argv[0]))+        return JS_EXCEPTION;+    if (f < 0 || f > 100)+        return JS_ThrowRangeError(ctx, "invalid number of digits");+    if (fabs(d) >= 1e21) {+        return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));+    } else {+        return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);+    }+}++static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValue val;+    int f, flags;+    double d;++    val = js_thisNumberValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToFloat64Free(ctx, &d, val))+        return JS_EXCEPTION;+    if (JS_ToInt32Sat(ctx, &f, argv[0]))+        return JS_EXCEPTION;+    if (!isfinite(d)) {+        return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));+    }+    if (JS_IsUndefined(argv[0])) {+        flags = 0;+        f = 0;+    } else {+        if (f < 0 || f > 100)+            return JS_ThrowRangeError(ctx, "invalid number of digits");+        f++;+        flags = JS_DTOA_FIXED_FORMAT;+    }+    return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);+}++static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSValue val;+    int p;+    double d;++    val = js_thisNumberValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToFloat64Free(ctx, &d, val))+        return JS_EXCEPTION;+    if (JS_IsUndefined(argv[0]))+        goto to_string;+    if (JS_ToInt32Sat(ctx, &p, argv[0]))+        return JS_EXCEPTION;+    if (!isfinite(d)) {+    to_string:+        return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));+    }+    if (p < 1 || p > 100)+        return JS_ThrowRangeError(ctx, "invalid number of digits");+    return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);+}++static const JSCFunctionListEntry js_number_proto_funcs[] = {+    JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),+    JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),+    JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),+    JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),+    JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),+    JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),+};++static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    const char *str, *p;+    int radix, flags;+    JSValue ret;++    str = JS_ToCString(ctx, argv[0]);+    if (!str)+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &radix, argv[1])) {+        JS_FreeCString(ctx, str);+        return JS_EXCEPTION;+    }+    if (radix != 0 && (radix < 2 || radix > 36)) {+        ret = JS_NAN;+    } else {+        p = str;+        p += skip_spaces(p);+        flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;+        ret = js_atof(ctx, p, NULL, radix, flags);+    }+    JS_FreeCString(ctx, str);+    return ret;+}++static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    const char *str, *p;+    JSValue ret;++    str = JS_ToCString(ctx, argv[0]);+    if (!str)+        return JS_EXCEPTION;+    p = str;+    p += skip_spaces(p);+    ret = js_atof(ctx, p, NULL, 10, 0);+    JS_FreeCString(ctx, str);+    return ret;+}++/* Boolean */+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,+                                     int argc, JSValueConst *argv)+{+    JSValue val, obj;+    val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));+    if (!JS_IsUndefined(new_target)) {+        obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);+        if (!JS_IsException(obj))+            JS_SetObjectData(ctx, obj, val);+        return obj;+    } else {+        return val;+    }+}++static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_BOOLEAN) {+            if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)+                return p->u.object_data;+        }+    }+    return JS_ThrowTypeError(ctx, "not a boolean");+}++static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSValue val = js_thisBooleanValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?+                       JS_ATOM_true : JS_ATOM_false);+}++static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    return js_thisBooleanValue(ctx, this_val);+}++static const JSCFunctionListEntry js_boolean_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_boolean_toString ),+    JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),+};++/* String */++static int js_string_get_own_property(JSContext *ctx,+                                      JSPropertyDescriptor *desc,+                                      JSValueConst obj, JSAtom prop)+{+    JSObject *p;+    JSString *p1;+    uint32_t idx, ch;++    /* This is a class exotic method: obj class_id is JS_CLASS_STRING */+    if (__JS_AtomIsTaggedInt(prop)) {+        p = JS_VALUE_GET_OBJ(obj);+        if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {+            p1 = JS_VALUE_GET_STRING(p->u.object_data);+            idx = __JS_AtomToUInt32(prop);+            if (idx < p1->len) {+                if (desc) {+                    if (p1->is_wide_char)+                        ch = p1->u.str16[idx];+                    else+                        ch = p1->u.str8[idx];+                    desc->flags = JS_PROP_ENUMERABLE;+                    desc->value = js_new_string_char(ctx, ch);+                    desc->getter = JS_UNDEFINED;+                    desc->setter = JS_UNDEFINED;+                }+                return TRUE;+            }+        }+    }+    return FALSE;+}++static uint32_t js_string_obj_get_length(JSContext *ctx,+                                         JSValueConst obj)+{+    JSObject *p;+    JSString *p1;+    uint32_t len = 0;++    /* This is a class exotic method: obj class_id is JS_CLASS_STRING */+    p = JS_VALUE_GET_OBJ(obj);+    if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {+        p1 = JS_VALUE_GET_STRING(p->u.object_data);+        len = p1->len;+    }+    return len;+}++static int js_string_get_own_property_names(JSContext *ctx,+                                            JSPropertyEnum **ptab,+                                            uint32_t *plen,+                                            JSValueConst obj)+{+    JSPropertyEnum *tab;+    uint32_t len, i;++    len = js_string_obj_get_length(ctx, obj);+    tab = NULL;+    if (len > 0) {+        /* do not allocate 0 bytes */+        tab = js_malloc(ctx, sizeof(JSPropertyEnum) * len);+        if (!tab)+            return -1;+        for(i = 0; i < len; i++) {+            tab[i].atom = __JS_AtomFromUInt32(i);+        }+    }+    *ptab = tab;+    *plen = len;+    return 0;+}++static int js_string_define_own_property(JSContext *ctx,+                                         JSValueConst this_obj,+                                         JSAtom prop, JSValueConst val,+                                         JSValueConst getter,+                                         JSValueConst setter, int flags)+{+    uint32_t idx;+    JSObject *p;+    JSString *p1, *p2;+    +    if (__JS_AtomIsTaggedInt(prop)) {+        idx = __JS_AtomToUInt32(prop);+        p = JS_VALUE_GET_OBJ(this_obj);+        if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)+            goto def;+        p1 = JS_VALUE_GET_STRING(p->u.object_data);+        if (idx >= p1->len)+            goto def;+        if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))+            goto fail;+        /* check that the same value is configured */+        if (flags & JS_PROP_HAS_VALUE) {+            if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)+                goto fail;+            p2 = JS_VALUE_GET_STRING(val);+            if (p2->len != 1)+                goto fail;+            if (string_get(p1, idx) != string_get(p2, 0)) {+            fail:+                return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");+            }+        }+        return TRUE;+    } else {+    def:+        return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,+                                 flags | JS_PROP_NO_EXOTIC);+    }+}++static int js_string_delete_property(JSContext *ctx,+                                     JSValueConst obj, JSAtom prop)+{+    uint32_t idx;++    if (__JS_AtomIsTaggedInt(prop)) {+        idx = __JS_AtomToUInt32(prop);+        if (idx < js_string_obj_get_length(ctx, obj)) {+            return FALSE;+        }+    }+    return TRUE;+}++static const JSClassExoticMethods js_string_exotic_methods = {+    .get_own_property = js_string_get_own_property,+    .get_own_property_names = js_string_get_own_property_names,+    .define_own_property = js_string_define_own_property,+    .delete_property = js_string_delete_property,+};++static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,+                                     int argc, JSValueConst *argv)+{+    JSValue val, obj;+    if (argc == 0) {+        val = JS_AtomToString(ctx, JS_ATOM_empty_string);+    } else {+        if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {+            JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);+            val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");+        } else {+            val = JS_ToString(ctx, argv[0]);+        }+        if (JS_IsException(val))+            return val;+    }+    if (!JS_IsUndefined(new_target)) {+        JSString *p1 = JS_VALUE_GET_STRING(val);++        obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);+        if (!JS_IsException(obj)) {+            JS_SetObjectData(ctx, obj, val);+            JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);+        }+        return obj;+    } else {+        return val;+    }+}++static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_STRING) {+            if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)+                return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a string");+}++static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    int i;+    StringBuffer b_s, *b = &b_s;++    string_buffer_init(ctx, b, argc);++    for(i = 0; i < argc; i++) {+        int32_t c;+        if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {+            string_buffer_free(b);+            return JS_EXCEPTION;+        }+    }+    return string_buffer_end(b);+}++static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    double d;+    int i, c;+    StringBuffer b_s, *b = &b_s;++    /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */++    if (string_buffer_init(ctx, b, argc))+        goto fail;+    for(i = 0; i < argc; i++) {+        if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {+            c = JS_VALUE_GET_INT(argv[i]);+            if (c < 0 || c > 0x10ffff)+                goto range_error;+        } else {+            if (JS_ToFloat64(ctx, &d, argv[i]))+                goto fail;+            if (d < 0 || d > 0x10ffff || (c = (int)d) != d)+                goto range_error;+        }+        if (string_buffer_putc(b, c))+            goto fail;+    }+    return string_buffer_end(b);++ range_error:+    JS_ThrowRangeError(ctx, "invalid code point");+ fail:+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    // raw(temp,...a)+    JSValue cooked, val, raw;+    StringBuffer b_s, *b = &b_s;+    int64_t i, n;++    string_buffer_init(ctx, b, 0);+    raw = JS_UNDEFINED;+    cooked = JS_ToObject(ctx, argv[0]);+    if (JS_IsException(cooked))+        goto exception;+    raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));+    if (JS_IsException(raw))+        goto exception;+    if (js_get_length64(ctx, &n, raw) < 0)+        goto exception;+        +    for (i = 0; i < n; i++) {+        val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));+        if (JS_IsException(val))+            goto exception;+        string_buffer_concat_value_free(b, val);+        if (i < n - 1 && i + 1 < argc) {+            if (string_buffer_concat_value(b, argv[i + 1]))+                goto exception;+        }+    }+    JS_FreeValue(ctx, cooked);+    JS_FreeValue(ctx, raw);+    return string_buffer_end(b);++exception:+    JS_FreeValue(ctx, cooked);+    JS_FreeValue(ctx, raw);+    string_buffer_free(b);+    return JS_EXCEPTION;+}++/* only used in test262 */+JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    uint32_t start, end, i, n;+    StringBuffer b_s, *b = &b_s;++    if (JS_ToUint32(ctx, &start, argv[0]) ||+        JS_ToUint32(ctx, &end, argv[1]))+        return JS_EXCEPTION;+    end = min_uint32(end, 0x10ffff + 1);++    if (start > end) {+        start = end;+    }+    n = end - start;+    if (end > 0x10000) {+        n += end - max_uint32(start, 0x10000);+    }+    if (string_buffer_init2(ctx, b, n, end >= 0x100))+        return JS_EXCEPTION;+    for(i = start; i < end; i++) {+        string_buffer_putc(b, i);+    }+    return string_buffer_end(b);+}++#if 0+static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    int c;+    if (JS_ToInt32(ctx, &c, argv[0]))+        return JS_EXCEPTION;+    return JS_NewBool(ctx, lre_is_space(c));+}+#endif++static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSValue val, ret;+    JSString *p;+    int idx, c;++    val = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_STRING(val);+    if (JS_ToInt32Sat(ctx, &idx, argv[0])) {+        JS_FreeValue(ctx, val);+        return JS_EXCEPTION;+    }+    if (idx < 0 || idx >= p->len) {+        ret = JS_NAN;+    } else {+        if (p->is_wide_char)+            c = p->u.str16[idx];+        else+            c = p->u.str8[idx];+        ret = JS_NewInt32(ctx, c);+    }+    JS_FreeValue(ctx, val);+    return ret;+}++static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue val, ret;+    JSString *p;+    int idx, c;++    val = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_STRING(val);+    if (JS_ToInt32Sat(ctx, &idx, argv[0])) {+        JS_FreeValue(ctx, val);+        return JS_EXCEPTION;+    }+    if (idx < 0 || idx >= p->len) {+        ret = js_new_string8(ctx, NULL, 0);+    } else {+        if (p->is_wide_char)+            c = p->u.str16[idx];+        else+            c = p->u.str8[idx];+        ret = js_new_string_char(ctx, c);+    }+    JS_FreeValue(ctx, val);+    return ret;+}++static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSValue val, ret;+    JSString *p;+    int idx, c;++    val = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_STRING(val);+    if (JS_ToInt32Sat(ctx, &idx, argv[0])) {+        JS_FreeValue(ctx, val);+        return JS_EXCEPTION;+    }+    if (idx < 0 || idx >= p->len) {+        ret = JS_UNDEFINED;+    } else {+        c = string_getc(p, &idx);+        ret = JS_NewInt32(ctx, c);+    }+    JS_FreeValue(ctx, val);+    return ret;+}++static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue r;+    int i;++    /* XXX: Use more efficient method */+    /* XXX: This method is OK if r has a single refcount */+    /* XXX: should use string_buffer? */+    r = JS_ToStringCheckObject(ctx, this_val);+    for (i = 0; i < argc; i++) {+        if (JS_IsException(r))+            break;+        r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));+    }+    return r;+}++static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)+{+    int i, c1, c2;+    for (i = 0; i < len; i++) {+        if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))+            return c1 - c2;+    }+    return 0;+}++static int string_indexof_char(JSString *p, int c, int from)+{+    /* assuming 0 <= from <= p->len */+    int i, len = p->len;+    if (p->is_wide_char) {+        for (i = from; i < len; i++) {+            if (p->u.str16[i] == c)+                return i;+        }+    } else {+        if ((c & ~0xff) == 0) {+            for (i = from; i < len; i++) {+                if (p->u.str8[i] == (uint8_t)c)+                    return i;+            }+        }+    }+    return -1;+}++static int string_indexof(JSString *p1, JSString *p2, int from)+{+    /* assuming 0 <= from <= p1->len */+    int c, i, j, len1 = p1->len, len2 = p2->len;+    if (len2 == 0)+        return from;+    for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {+        j = string_indexof_char(p1, c, i);+        if (j < 0 || j + len2 > len1)+            break;+        if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))+            return j;+    }+    return -1;+}++static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)+{+    if (!unicode || index >= p->len || !p->is_wide_char) {+        index++;+    } else {+        int index32 = (int)index;+        string_getc(p, &index32);+        index = index32;+    }+    return index;+}++static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv, int lastIndexOf)+{+    JSValue str, v;+    int i, len, v_len, pos, start, stop, ret, inc;+    JSString *p;+    JSString *p1;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return str;+    v = JS_ToString(ctx, argv[0]);+    if (JS_IsException(v))+        goto fail;+    p = JS_VALUE_GET_STRING(str);+    p1 = JS_VALUE_GET_STRING(v);+    len = p->len;+    v_len = p1->len;+    if (lastIndexOf) {+        pos = len - v_len;+        if (argc > 1) {+            double d;+            if (JS_ToFloat64(ctx, &d, argv[1]))+                goto fail;+            if (!isnan(d)) {+                if (d <= 0)+                    pos = 0;+                else if (d < pos)+                    pos = d;+            }+        }+        start = pos;+        stop = 0;+        inc = -1;+    } else {+        pos = 0;+        if (argc > 1) {+            if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))+                goto fail;+        }+        start = pos;+        stop = len - v_len;+        inc = 1;+    }+    ret = -1;+    if (len >= v_len && inc * (stop - start) >= 0) {+        for (i = start;; i += inc) {+            if (!string_cmp(p, p1, i, 0, v_len)) {+                ret = i;+                break;+            }+            if (i == stop)+                break;+        }+    }+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, v);+    return JS_NewInt32(ctx, ret);++fail:+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, v);+    return JS_EXCEPTION;+}++/* return < 0 if exception or TRUE/FALSE */+static int js_is_regexp(JSContext *ctx, JSValueConst obj);++static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv, int magic)+{+    JSValue str, v = JS_UNDEFINED;+    int i, len, v_len, pos, start, stop, ret;+    JSString *p;+    JSString *p1;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return str;+    ret = js_is_regexp(ctx, argv[0]);+    if (ret) {+        if (ret > 0)+            JS_ThrowTypeError(ctx, "regex not supported");+        goto fail;+    }+    v = JS_ToString(ctx, argv[0]);+    if (JS_IsException(v))+        goto fail;+    p = JS_VALUE_GET_STRING(str);+    p1 = JS_VALUE_GET_STRING(v);+    len = p->len;+    v_len = p1->len;+    pos = (magic == 2) ? len : 0;+    if (argc > 1 && !JS_IsUndefined(argv[1])) {+        if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))+            goto fail;+    }+    len -= v_len;+    ret = 0;+    if (magic == 0) {+        start = pos;+        stop = len;+    } else {+        if (magic == 1) {+            if (pos > len)+                goto done;+        } else {+            pos -= v_len;+        }+        start = stop = pos;+    }+    if (start >= 0 && start <= stop) {+        for (i = start;; i++) {+            if (!string_cmp(p, p1, i, 0, v_len)) {+                ret = 1;+                break;+            }+            if (i == stop)+                break;+        }+    }+ done:+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, v);+    return JS_NewBool(ctx, ret);++fail:+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, v);+    return JS_EXCEPTION;+}++static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)+{+    int ret;+    JSValue flags;+    +    ret = js_is_regexp(ctx, regexp);+    if (ret < 0)+        return -1;+    if (ret) {+        flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);+        if (JS_IsException(flags))+            return -1;+        if (JS_IsUndefined(flags) || JS_IsNull(flags)) {+            JS_ThrowTypeError(ctx, "cannot convert to object");+            return -1;+        }+        flags = JS_ToStringFree(ctx, flags);+        if (JS_IsException(flags))+            return -1;+        ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);+        JS_FreeValue(ctx, flags);+        if (ret < 0) {+            JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");+            return -1;+        }+    }+    return 0;+}++static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv, int atom)+{+    // match(rx), search(rx), matchAll(rx)+    // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll+    JSValueConst O = this_val, regexp = argv[0], args[2];+    JSValue matcher, S, rx, result, str;+    int args_len;++    if (JS_IsUndefined(O) || JS_IsNull(O))+        return JS_ThrowTypeError(ctx, "cannot convert to object");++    if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {+        matcher = JS_GetProperty(ctx, regexp, atom);+        if (JS_IsException(matcher))+            return JS_EXCEPTION;+        if (atom == JS_ATOM_Symbol_matchAll) {+            if (check_regexp_g_flag(ctx, regexp) < 0) {+                JS_FreeValue(ctx, matcher);+                return JS_EXCEPTION;+            }+        }+        if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {+            return JS_CallFree(ctx, matcher, regexp, 1, &O);+        }+    }+    S = JS_ToString(ctx, O);+    if (JS_IsException(S))+        return JS_EXCEPTION;+    args_len = 1;+    args[0] = regexp;+    str = JS_UNDEFINED;+    if (atom == JS_ATOM_Symbol_matchAll) {+        str = JS_NewString(ctx, "g");+        if (JS_IsException(str))+            goto fail;+        args[args_len++] = (JSValueConst)str;+    }+    rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);+    JS_FreeValue(ctx, str);+    if (JS_IsException(rx)) {+    fail:+        JS_FreeValue(ctx, S);+        return JS_EXCEPTION;+    }+    result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);+    JS_FreeValue(ctx, S);+    return result;+}++static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,+                                           int argc, JSValueConst *argv)+{+    // GetSubstitution(matched, str, position, captures, namedCaptures, rep)+    JSValueConst matched, str, captures, namedCaptures, rep;+    JSValue capture, name, s;+    uint32_t position, len, matched_len, captures_len;+    int i, j, j0, k, k1;+    int c, c1;+    StringBuffer b_s, *b = &b_s;+    JSString *sp, *rp;++    matched = argv[0];+    str = argv[1];+    captures = argv[3];+    namedCaptures = argv[4];+    rep = argv[5];++    if (!JS_IsString(rep) || !JS_IsString(str))+        return JS_ThrowTypeError(ctx, "not a string");++    sp = JS_VALUE_GET_STRING(str);+    rp = JS_VALUE_GET_STRING(rep);++    string_buffer_init(ctx, b, 0);++    captures_len = 0;+    if (!JS_IsUndefined(captures)) {+        if (js_get_length32(ctx, &captures_len, captures))+            goto exception;+    }+    if (js_get_length32(ctx, &matched_len, matched))+        goto exception;+    if (JS_ToUint32(ctx, &position, argv[2]) < 0)+        goto exception;++    len = rp->len;+    i = 0;+    for(;;) {+        j = string_indexof_char(rp, '$', i);+        if (j < 0 || j + 1 >= len)+            break;+        string_buffer_concat(b, rp, i, j);+        j0 = j++;+        c = string_get(rp, j++);+        if (c == '$') {+            string_buffer_putc8(b, '$');+        } else if (c == '&') {+            if (string_buffer_concat_value(b, matched))+                goto exception;+        } else if (c == '`') {+            string_buffer_concat(b, sp, 0, position);+        } else if (c == '\'') {+            string_buffer_concat(b, sp, position + matched_len, sp->len);+        } else if (c >= '0' && c <= '9') {+            k = c - '0';+            c1 = string_get(rp, j);+            if (c1 >= '0' && c1 <= '9') {+                /* This behavior is specified in ES6 and refined in ECMA 2019 */+                /* ECMA 2019 does not have the extra test, but+                   Test262 S15.5.4.11_A3_T1..3 require this behavior */+                k1 = k * 10 + c1 - '0';+                if (k1 >= 1 && k1 < captures_len) {+                    k = k1;+                    j++;+                }+            }+            if (k >= 1 && k < captures_len) {+                s = JS_GetPropertyInt64(ctx, captures, k);+                if (JS_IsException(s))+                    goto exception;+                if (!JS_IsUndefined(s)) {+                    if (string_buffer_concat_value_free(b, s))+                        goto exception;+                }+            } else {+                goto norep;+            }+        } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {+            k = string_indexof_char(rp, '>', j);+            if (k < 0)+                goto norep;+            name = js_sub_string(ctx, rp, j, k);+            if (JS_IsException(name))+                goto exception;+            capture = JS_GetPropertyValue(ctx, namedCaptures, name);+            if (JS_IsException(capture))+                goto exception;+            if (!JS_IsUndefined(capture)) {+                if (string_buffer_concat_value_free(b, capture))+                    goto exception;+            }+            j = k + 1;+        } else {+        norep:+            string_buffer_concat(b, rp, j0, j);+        }+        i = j;+    }+    string_buffer_concat(b, rp, i, rp->len);+    return string_buffer_end(b);+exception:+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv,+                                 int is_replaceAll)+{+    // replace(rx, rep)+    JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];+    JSValueConst args[6];+    JSValue str, search_str, replaceValue_str, repl_str;+    JSString *sp, *searchp;+    StringBuffer b_s, *b = &b_s;+    int pos, functionalReplace, endOfLastMatch;+    BOOL is_first;++    if (JS_IsUndefined(O) || JS_IsNull(O))+        return JS_ThrowTypeError(ctx, "cannot convert to object");++    search_str = JS_UNDEFINED;+    replaceValue_str = JS_UNDEFINED;+    repl_str = JS_UNDEFINED;++    if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {+        JSValue replacer;+        if (is_replaceAll) {+            if (check_regexp_g_flag(ctx, searchValue) < 0)+                return JS_EXCEPTION;+        }+        replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);+        if (JS_IsException(replacer))+            return JS_EXCEPTION;+        if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {+            args[0] = O;+            args[1] = replaceValue;+            return JS_CallFree(ctx, replacer, searchValue, 2, args);+        }+    }+    string_buffer_init(ctx, b, 0);++    str = JS_ToString(ctx, O);+    if (JS_IsException(str))+        goto exception;+    search_str = JS_ToString(ctx, searchValue);+    if (JS_IsException(search_str))+        goto exception;+    functionalReplace = JS_IsFunction(ctx, replaceValue);+    if (!functionalReplace) {+        replaceValue_str = JS_ToString(ctx, replaceValue);+        if (JS_IsException(replaceValue_str))+            goto exception;+    }++    sp = JS_VALUE_GET_STRING(str);+    searchp = JS_VALUE_GET_STRING(search_str);+    endOfLastMatch = 0;+    is_first = TRUE;+    for(;;) {+        if (unlikely(searchp->len == 0)) {+            if (is_first)+                pos = 0;+            else if (endOfLastMatch >= sp->len)+                pos = -1;+            else+                pos = endOfLastMatch + 1;+        } else {+            pos = string_indexof(sp, searchp, endOfLastMatch);+        }+        if (pos < 0) {+            if (is_first) {+                string_buffer_free(b);+                JS_FreeValue(ctx, search_str);+                JS_FreeValue(ctx, replaceValue_str);+                return str;+            } else {+                break;+            }+        }+        if (functionalReplace) {+            args[0] = search_str;+            args[1] = JS_NewInt32(ctx, pos);+            args[2] = str;+            repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));+        } else {+            args[0] = search_str;+            args[1] = str;+            args[2] = JS_NewInt32(ctx, pos);+            args[3] = JS_UNDEFINED;+            args[4] = JS_UNDEFINED;+            args[5] = replaceValue_str;+            repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);+        }+        if (JS_IsException(repl_str))+            goto exception;+        +        string_buffer_concat(b, sp, endOfLastMatch, pos);+        string_buffer_concat_value_free(b, repl_str);+        endOfLastMatch = pos + searchp->len;+        is_first = FALSE;+        if (!is_replaceAll)+            break;+    }+    string_buffer_concat(b, sp, endOfLastMatch, sp->len);+    JS_FreeValue(ctx, search_str);+    JS_FreeValue(ctx, replaceValue_str);+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);++exception:+    string_buffer_free(b);+    JS_FreeValue(ctx, search_str);+    JS_FreeValue(ctx, replaceValue_str);+    JS_FreeValue(ctx, str);+    return JS_EXCEPTION;+}++static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    // split(sep, limit)+    JSValueConst O = this_val, separator = argv[0], limit = argv[1];+    JSValueConst args[2];+    JSValue S, A, R, T;+    uint32_t lim, lengthA;+    int64_t p, q, s, r, e;+    JSString *sp, *rp;++    if (JS_IsUndefined(O) || JS_IsNull(O))+        return JS_ThrowTypeError(ctx, "cannot convert to object");++    S = JS_UNDEFINED;+    A = JS_UNDEFINED;+    R = JS_UNDEFINED;++    if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {+        JSValue splitter;+        splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);+        if (JS_IsException(splitter))+            return JS_EXCEPTION;+        if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {+            args[0] = O;+            args[1] = limit;+            return JS_CallFree(ctx, splitter, separator, 2, args);+        }+    }+    S = JS_ToString(ctx, O);+    if (JS_IsException(S))+        goto exception;+    A = JS_NewArray(ctx);+    if (JS_IsException(A))+        goto exception;+    lengthA = 0;+    if (JS_IsUndefined(limit)) {+        lim = 0xffffffff;+    } else {+        if (JS_ToUint32(ctx, &lim, limit) < 0)+            goto exception;+    }+    sp = JS_VALUE_GET_STRING(S);+    s = sp->len;+    R = JS_ToString(ctx, separator);+    if (JS_IsException(R))+        goto exception;+    rp = JS_VALUE_GET_STRING(R);+    r = rp->len;+    p = 0;+    if (lim == 0)+        goto done;+    if (JS_IsUndefined(separator))+        goto add_tail;+    if (s == 0) {+        if (r != 0)+            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)+            break;+        T = js_sub_string(ctx, sp, p, e);+        if (JS_IsException(T))+            goto exception;+        if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)+            goto exception;+        if (lengthA == lim)+            goto done;+    }+add_tail:+    T = js_sub_string(ctx, sp, p, s);+    if (JS_IsException(T))+        goto exception;+    if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)+        goto exception;+done:+    JS_FreeValue(ctx, S);+    JS_FreeValue(ctx, R);+    return A;++exception:+    JS_FreeValue(ctx, A);+    JS_FreeValue(ctx, S);+    JS_FreeValue(ctx, R);+    return JS_EXCEPTION;+}++static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSValue str, ret;+    int a, b, start, end;+    JSString *p;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return str;+    p = JS_VALUE_GET_STRING(str);+    if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {+        JS_FreeValue(ctx, str);+        return JS_EXCEPTION;+    }+    b = p->len;+    if (!JS_IsUndefined(argv[1])) {+        if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {+            JS_FreeValue(ctx, str);+            return JS_EXCEPTION;+        }+    }+    if (a < b) {+        start = a;+        end = b;+    } else {+        start = b;+        end = a;+    }+    ret = js_sub_string(ctx, p, start, end);+    JS_FreeValue(ctx, str);+    return ret;+}++static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue str, ret;+    int a, len, n;+    JSString *p;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return str;+    p = JS_VALUE_GET_STRING(str);+    len = p->len;+    if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {+        JS_FreeValue(ctx, str);+        return JS_EXCEPTION;+    }+    n = len - a;+    if (!JS_IsUndefined(argv[1])) {+        if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {+            JS_FreeValue(ctx, str);+            return JS_EXCEPTION;+        }+    }+    ret = js_sub_string(ctx, p, a, a + n);+    JS_FreeValue(ctx, str);+    return ret;+}++static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    JSValue str, ret;+    int len, start, end;+    JSString *p;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return str;+    p = JS_VALUE_GET_STRING(str);+    len = p->len;+    if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {+        JS_FreeValue(ctx, str);+        return JS_EXCEPTION;+    }+    end = len;+    if (!JS_IsUndefined(argv[1])) {+        if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {+            JS_FreeValue(ctx, str);+            return JS_EXCEPTION;+        }+    }+    ret = js_sub_string(ctx, p, start, max_int(end, start));+    JS_FreeValue(ctx, str);+    return ret;+}++static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv, int padEnd)+{+    JSValue str, v = JS_UNDEFINED;+    StringBuffer b_s, *b = &b_s;+    JSString *p, *p1 = NULL;+    int n, len, c = ' ';++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        goto fail1;+    if (JS_ToInt32Sat(ctx, &n, argv[0]))+        goto fail2;+    p = JS_VALUE_GET_STRING(str);+    len = p->len;+    if (len >= n)+        return str;+    if (n > JS_STRING_LEN_MAX) {+        JS_ThrowInternalError(ctx, "string too long");+        goto fail2;+    }+    if (argc > 1 && !JS_IsUndefined(argv[1])) {+        v = JS_ToString(ctx, argv[1]);+        if (JS_IsException(v))+            goto fail2;+        p1 = JS_VALUE_GET_STRING(v);+        if (p1->len == 0) {+            JS_FreeValue(ctx, v);+            return str;+        }+        if (p1->len == 1) {+            c = string_get(p1, 0);+            p1 = NULL;+        }+    }+    if (string_buffer_init(ctx, b, n))+        goto fail3;+    n -= len;+    if (padEnd) {+        if (string_buffer_concat(b, p, 0, len))+            goto fail;+    }+    if (p1) {+        while (n > 0) {+            int chunk = min_int(n, p1->len);+            if (string_buffer_concat(b, p1, 0, chunk))+                goto fail;+            n -= chunk;+        }+    } else {+        if (string_buffer_fill(b, c, n))+            goto fail;+    }+    if (!padEnd) {+        if (string_buffer_concat(b, p, 0, len))+            goto fail;+    }+    JS_FreeValue(ctx, v);+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);++fail:+    string_buffer_free(b);+fail3:+    JS_FreeValue(ctx, v);+fail2:+    JS_FreeValue(ctx, str);+fail1:+    return JS_EXCEPTION;+}++static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue str;+    StringBuffer b_s, *b = &b_s;+    JSString *p;+    int64_t val;+    int n, len;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        goto fail;+    if (JS_ToInt64Sat(ctx, &val, argv[0]))+        goto fail;+    if (val < 0 || val > 2147483647) {+        JS_ThrowRangeError(ctx, "invalid repeat count");+        goto fail;+    }+    n = val;+    p = JS_VALUE_GET_STRING(str);+    len = p->len;+    if (len == 0 || n == 1)+        return str;+    if (val * len > JS_STRING_LEN_MAX) {+        JS_ThrowInternalError(ctx, "string too long");+        goto fail;+    }+    if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))+        goto fail;+    if (len == 1) {+        string_buffer_fill(b, string_get(p, 0), n);+    } else {+        while (n-- > 0) {+            string_buffer_concat(b, p, 0, len);+        }+    }+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);++fail:+    JS_FreeValue(ctx, str);+    return JS_EXCEPTION;+}++static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int magic)+{+    JSValue str, ret;+    int a, b, len;+    JSString *p;++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return str;+    p = JS_VALUE_GET_STRING(str);+    a = 0;+    b = len = p->len;+    if (magic & 1) {+        while (a < len && lre_is_space(string_get(p, a)))+            a++;+    }+    if (magic & 2) {+        while (b > a && lre_is_space(string_get(p, b - 1)))+            b--;+    }+    ret = js_sub_string(ctx, p, a, b);+    JS_FreeValue(ctx, str);+    return ret;+}++static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    return JS_ToQuotedString(ctx, this_val);+}++/* return 0 if before the first char */+static int string_prevc(JSString *p, int *pidx)+{+    int idx, c, c1;++    idx = *pidx;+    if (idx <= 0)+        return 0;+    idx--;+    if (p->is_wide_char) {+        c = p->u.str16[idx];+        if (c >= 0xdc00 && c < 0xe000 && idx > 0) {+            c1 = p->u.str16[idx - 1];+            if (c1 >= 0xd800 && c1 <= 0xdc00) {+                c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;+                idx--;+            }+        }+    } else {+        c = p->u.str8[idx];+    }+    *pidx = idx;+    return c;+}++static BOOL test_final_sigma(JSString *p, int sigma_pos)+{+    int k, c1;++    /* before C: skip case ignorable chars and check there is+       a cased letter */+    k = sigma_pos;+    for(;;) {+        c1 = string_prevc(p, &k);+        if (!lre_is_case_ignorable(c1))+            break;+    }+    if (!lre_is_cased(c1))+        return FALSE;++    /* after C: skip case ignorable chars and check there is+       no cased letter */+    k = sigma_pos + 1;+    for(;;) {+        if (k >= p->len)+            return TRUE;+        c1 = string_getc(p, &k);+        if (!lre_is_case_ignorable(c1))+            break;+    }+    return !lre_is_cased(c1);+}++static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValue a, b;+    int cmp;++    a = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(a))+        return JS_EXCEPTION;+    b = JS_ToString(ctx, argv[0]);+    if (JS_IsException(b)) {+        JS_FreeValue(ctx, a);+        return JS_EXCEPTION;+    }+    cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));+    JS_FreeValue(ctx, a);+    JS_FreeValue(ctx, b);+    return JS_NewInt32(ctx, cmp);+}++static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv, int to_lower)+{+    JSValue val;+    StringBuffer b_s, *b = &b_s;+    JSString *p;+    int i, c, j, l;+    uint32_t res[LRE_CC_RES_LEN_MAX];++    val = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_STRING(val);+    if (p->len == 0)+        return val;+    if (string_buffer_init(ctx, b, p->len))+        goto fail;+    for(i = 0; i < p->len;) {+        c = string_getc(p, &i);+        if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {+            res[0] = 0x3c2; /* final sigma */+            l = 1;+        } else {+            l = lre_case_conv(res, c, to_lower);+        }+        for(j = 0; j < l; j++) {+            if (string_buffer_putc(b, res[j]))+                goto fail;+        }+    }+    JS_FreeValue(ctx, val);+    return string_buffer_end(b);+ fail:+    JS_FreeValue(ctx, val);+    string_buffer_free(b);+    return JS_EXCEPTION;+}++#ifdef CONFIG_ALL_UNICODE++/* return (-1, NULL) if exception, otherwise (len, buf) */+static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)+{+    JSValue val;+    JSString *p;+    uint32_t *buf;+    int i, j, len;++    val = JS_ToString(ctx, val1);+    if (JS_IsException(val))+        return -1;+    p = JS_VALUE_GET_STRING(val);+    len = p->len;+    /* UTF32 buffer length is len minus the number of correct surrogates pairs */+    buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));+    if (!buf) {+        JS_FreeValue(ctx, val);+        goto fail;+    }+    for(i = j = 0; i < len;)+        buf[j++] = string_getc(p, &i);+    JS_FreeValue(ctx, val);+    *pbuf = buf;+    return j;+ fail:+    *pbuf = NULL;+    return -1;+}++static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)+{+    int i;+    StringBuffer b_s, *b = &b_s;+    if (string_buffer_init(ctx, b, len))+        return JS_EXCEPTION;+    for(i = 0; i < len; i++) {+        if (string_buffer_putc(b, buf[i]))+            goto fail;+    }+    return string_buffer_end(b);+ fail:+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    const char *form, *p;+    size_t form_len;+    int is_compat, buf_len, out_len;+    UnicodeNormalizationEnum n_type;+    JSValue val;+    uint32_t *buf, *out_buf;++    val = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(val))+        return val;+    buf_len = JS_ToUTF32String(ctx, &buf, val);+    JS_FreeValue(ctx, val);+    if (buf_len < 0)+        return JS_EXCEPTION;++    if (argc == 0 || JS_IsUndefined(argv[0])) {+        n_type = UNICODE_NFC;+    } else {+        form = JS_ToCStringLen(ctx, &form_len, argv[0]);+        if (!form)+            goto fail1;+        p = form;+        if (p[0] != 'N' || p[1] != 'F')+            goto bad_form;+        p += 2;+        is_compat = FALSE;+        if (*p == 'K') {+            is_compat = TRUE;+            p++;+        }+        if (*p == 'C' || *p == 'D') {+            n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');+            if ((p + 1 - form) != form_len)+                goto bad_form;+        } else {+        bad_form:+            JS_FreeCString(ctx, form);+            JS_ThrowRangeError(ctx, "bad normalization form");+        fail1:+            js_free(ctx, buf);+            return JS_EXCEPTION;+        }+        JS_FreeCString(ctx, form);+    }++    out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,+                                ctx->rt, (DynBufReallocFunc *)js_realloc_rt);+    js_free(ctx, buf);+    if (out_len < 0)+        return JS_EXCEPTION;+    val = JS_NewUTF32String(ctx, out_buf, out_len);+    js_free(ctx, out_buf);+    return val;+}+#endif /* CONFIG_ALL_UNICODE */++/* also used for String.prototype.valueOf */+static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    return js_thisStringValue(ctx, this_val);+}++#if 0+static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,+                                               int argc, JSValueConst *argv)+{+    return JS_ToStringCheckObject(ctx, argv[0]);+}++static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    return JS_ToString(ctx, argv[0]);+}++static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst+                                              this_val,+                                              int argc, JSValueConst *argv)+{+    JSValue str;+    int idx;+    BOOL is_unicode;+    JSString *p;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        return str;+    if (JS_ToInt32Sat(ctx, &idx, argv[1])) {+        JS_FreeValue(ctx, str);+        return JS_EXCEPTION;+    }+    is_unicode = JS_ToBool(ctx, argv[2]);+    p = JS_VALUE_GET_STRING(str);+    if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {+        idx++;+    } else {+        string_getc(p, &idx);+    }+    JS_FreeValue(ctx, str);+    return JS_NewInt32(ctx, idx);+}+#endif++/* String Iterator */++static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv,+                                       BOOL *pdone, int magic)+{+    JSArrayIteratorData *it;+    uint32_t idx, c, start;+    JSString *p;++    it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);+    if (!it) {+        *pdone = FALSE;+        return JS_EXCEPTION;+    }+    if (JS_IsUndefined(it->obj))+        goto done;+    p = JS_VALUE_GET_STRING(it->obj);+    idx = it->idx;+    if (idx >= p->len) {+        JS_FreeValue(ctx, it->obj);+        it->obj = JS_UNDEFINED;+    done:+        *pdone = TRUE;+        return JS_UNDEFINED;+    }++    start = idx;+    c = string_getc(p, (int *)&idx);+    it->idx = idx;+    *pdone = FALSE;+    if (c <= 0xffff) {+        return js_new_string_char(ctx, c);+    } else {+        return js_new_string16(ctx, p->u.str16 + start, 2);+    }+}++/* ES6 Annex B 2.3.2 etc. */+enum {+    magic_string_anchor,+    magic_string_big,+    magic_string_blink,+    magic_string_bold,+    magic_string_fixed,+    magic_string_fontcolor,+    magic_string_fontsize,+    magic_string_italics,+    magic_string_link,+    magic_string_small,+    magic_string_strike,+    magic_string_sub,+    magic_string_sup,+};++static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv, int magic)+{+    JSValue str;+    const JSString *p;+    StringBuffer b_s, *b = &b_s;+    static struct { const char *tag, *attr; } const defs[] = {+        { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },+        { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },+        { "a", "href" }, { "small", NULL }, { "strike", NULL }, +        { "sub", NULL }, { "sup", NULL },+    };++    str = JS_ToStringCheckObject(ctx, this_val);+    if (JS_IsException(str))+        return JS_EXCEPTION;+    string_buffer_init(ctx, b, 7);+    string_buffer_putc8(b, '<');+    string_buffer_puts8(b, defs[magic].tag);+    if (defs[magic].attr) {+        // r += " " + attr + "=\"" + value + "\"";+        JSValue value;+        int i;++        string_buffer_putc8(b, ' ');+        string_buffer_puts8(b, defs[magic].attr);+        string_buffer_puts8(b, "=\"");+        value = JS_ToStringCheckObject(ctx, argv[0]);+        if (JS_IsException(value)) {+            JS_FreeValue(ctx, str);+            string_buffer_free(b);+            return JS_EXCEPTION;+        }+        p = JS_VALUE_GET_STRING(value);+        for (i = 0; i < p->len; i++) {+            int c = string_get(p, i);+            if (c == '"') {+                string_buffer_puts8(b, "&quot;");+            } else {+                string_buffer_putc16(b, c);+            }+        }+        JS_FreeValue(ctx, value);+        string_buffer_putc8(b, '\"');+    }+    // return r + ">" + str + "</" + tag + ">";+    string_buffer_putc8(b, '>');+    string_buffer_concat_value_free(b, str);+    string_buffer_puts8(b, "</");+    string_buffer_puts8(b, defs[magic].tag);+    string_buffer_putc8(b, '>');+    return string_buffer_end(b);+}++static const JSCFunctionListEntry js_string_funcs[] = {+    JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),+    JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),+    JS_CFUNC_DEF("raw", 1, js_string_raw ),+    //JS_CFUNC_DEF("__toString", 1, js_string___toString ),+    //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),+    //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),+    //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),+    //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),+};++static const JSCFunctionListEntry js_string_proto_funcs[] = {+    JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),+    JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),+    JS_CFUNC_DEF("charAt", 1, js_string_charAt ),+    JS_CFUNC_DEF("concat", 1, js_string_concat ),+    JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),+    JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),+    JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),+    JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),+    JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),+    JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),+    JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),+    JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),+    JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),+    JS_CFUNC_DEF("split", 2, js_string_split ),+    JS_CFUNC_DEF("substring", 2, js_string_substring ),+    JS_CFUNC_DEF("substr", 2, js_string_substr ),+    JS_CFUNC_DEF("slice", 2, js_string_slice ),+    JS_CFUNC_DEF("repeat", 1, js_string_repeat ),+    JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),+    JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),+    JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),+    JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),+    JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),+    JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),+    JS_ALIAS_DEF("trimRight", "trimEnd" ),+    JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),+    JS_ALIAS_DEF("trimLeft", "trimStart" ),+    JS_CFUNC_DEF("toString", 0, js_string_toString ),+    JS_CFUNC_DEF("valueOf", 0, js_string_toString ),+    JS_CFUNC_DEF("__quote", 1, js_string___quote ),+    JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),+    JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),+    JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),+    JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),+    JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),+    JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),+    /* ES6 Annex B 2.3.2 etc. */+    JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),+    JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),+    JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),+    JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),+    JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),+    JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),+    JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),+    JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),+    JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),+    JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),+    JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),+    JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),+    JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),+};++static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {+    JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),+};++#ifdef CONFIG_ALL_UNICODE+static const JSCFunctionListEntry js_string_proto_normalize[] = {+    JS_CFUNC_DEF("normalize", 0, js_string_normalize ),+};+#endif++void JS_AddIntrinsicStringNormalize(JSContext *ctx)+{+#ifdef CONFIG_ALL_UNICODE+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,+                               countof(js_string_proto_normalize));+#endif+}++/* Math */++/* precondition: a and b are not NaN */+static double js_fmin(double a, double b)+{+    if (a == 0 && b == 0) {+        JSFloat64Union a1, b1;+        a1.d = a;+        b1.d = b;+        a1.u64 |= b1.u64;+        return a1.d;+    } else {+        return fmin(a, b);+    }+}++/* precondition: a and b are not NaN */+static double js_fmax(double a, double b)+{+    if (a == 0 && b == 0) {+        JSFloat64Union a1, b1;+        a1.d = a;+        b1.d = b;+        a1.u64 &= b1.u64;+        return a1.d;+    } else {+        return fmax(a, b);+    }+}++static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv, int magic)+{+    BOOL is_max = magic;+    double r, a;+    int i;+    uint32_t tag;++    if (unlikely(argc == 0)) {+        return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);+    }++    tag = JS_VALUE_GET_TAG(argv[0]);+    if (tag == JS_TAG_INT) {+        int a1, r1 = JS_VALUE_GET_INT(argv[0]);+        for(i = 1; i < argc; i++) {+            tag = JS_VALUE_GET_TAG(argv[i]);+            if (tag != JS_TAG_INT) {+                r = r1;+                goto generic_case;+            }+            a1 = JS_VALUE_GET_INT(argv[i]);+            if (is_max)+                r1 = max_int(r1, a1);+            else+                r1 = min_int(r1, a1);++        }+        return JS_NewInt32(ctx, r1);+    } else {+        if (JS_ToFloat64(ctx, &r, argv[0]))+            return JS_EXCEPTION;+        i = 1;+    generic_case:+        while (i < argc) {+            if (JS_ToFloat64(ctx, &a, argv[i]))+                return JS_EXCEPTION;+            if (!isnan(r)) {+                if (isnan(a)) {+                    r = a;+                } else {+                    if (is_max)+                        r = js_fmax(r, a);+                    else+                        r = js_fmin(r, a);+                }+            }+            i++;+        }+        return JS_NewFloat64(ctx, r);+    }+}++static double js_math_sign(double a)+{+    if (isnan(a) || a == 0.0)+        return a;+    if (a < 0)+        return -1;+    else+        return 1;+}++static double js_math_round(double a)+{+    JSFloat64Union u;+    uint64_t frac_mask, one;+    unsigned int e, s;++    u.d = a;+    e = (u.u64 >> 52) & 0x7ff;+    if (e < 1023) {+        /* abs(a) < 1 */+        if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {+            /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */+            u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);+        } else {+            /* return +/-0.0 */+            u.u64 &= (uint64_t)1 << 63;+        }+    } else if (e < (1023 + 52)) {+        s = u.u64 >> 63;+        one = (uint64_t)1 << (52 - (e - 1023));+        frac_mask = one - 1;+        u.u64 += (one >> 1) - s;+        u.u64 &= ~frac_mask; /* truncate to an integer */+    }+    /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */+    return u.d;+}++static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    double r, a;+    int i;++    r = 0;+    if (argc > 0) {+        if (JS_ToFloat64(ctx, &r, argv[0]))+            return JS_EXCEPTION;+        if (argc == 1) {+            r = fabs(r);+        } else {+            /* use the built-in function to minimize precision loss */+            for (i = 1; i < argc; i++) {+                if (JS_ToFloat64(ctx, &a, argv[i]))+                    return JS_EXCEPTION;+                r = hypot(r, a);+            }+        }+    }+    return JS_NewFloat64(ctx, r);+}++static double js_math_fround(double a)+{+    return (float)a;+}++static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv)+{+    int a, b;++    if (JS_ToInt32(ctx, &a, argv[0]))+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &b, argv[1]))+        return JS_EXCEPTION;+    /* purposely ignoring overflow */+    return JS_NewInt32(ctx, a * b);+}++static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    uint32_t a, r;++    if (JS_ToUint32(ctx, &a, argv[0]))+        return JS_EXCEPTION;+    if (a == 0)+        r = 32;+    else+        r = clz32(a);+    return JS_NewInt32(ctx, r);+}++/* xorshift* random number generator by Marsaglia */+static uint64_t xorshift64star(uint64_t *pstate)+{+    uint64_t x;+    x = *pstate;+    x ^= x >> 12;+    x ^= x << 25;+    x ^= x >> 27;+    *pstate = x;+    return x * 0x2545F4914F6CDD1D;+}++static void js_random_init(JSContext *ctx)+{+    struct timeval tv;+    gettimeofday(&tv, NULL);+    ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;+    /* the state must be non zero */+    if (ctx->random_state == 0)+        ctx->random_state = 1;+}++static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    JSFloat64Union u;+    uint64_t v;++    v = xorshift64star(&ctx->random_state);+    /* 1.0 <= u.d < 2 */+    u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);+    return __JS_NewFloat64(ctx, u.d - 1.0);+}++static const JSCFunctionListEntry js_math_funcs[] = {+    JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),+    JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),+    JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),+    JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),+    JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),+    JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),+    JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),++    JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),+    JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),+    JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),+    JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),+    JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),+    JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),+    JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),+    JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),+    JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),+    JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),+    /* ES6 */+    JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),+    JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),+    JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),+    JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),+    JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),+    JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),+    JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),+    JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),+    JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),+    JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),+    JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),+    JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),+    JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),+    JS_CFUNC_DEF("hypot", 2, js_math_hypot ),+    JS_CFUNC_DEF("random", 0, js_math_random ),+    JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),+    JS_CFUNC_DEF("imul", 2, js_math_imul ),+    JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),+    JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),+    JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),+    JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),+    JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),+    JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),+    JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),+    JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),+    JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),+};++static const JSCFunctionListEntry js_math_obj[] = {+    JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++/* Date */++#if 0+/* OS dependent: return the UTC time in ms since 1970. */+static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    int64_t d;+    struct timeval tv;+    gettimeofday(&tv, NULL);+    d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);+    return JS_NewInt64(ctx, d);+}+#endif++/* OS dependent: return the UTC time in microseconds since 1970. */+static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    int64_t d;+    struct timeval tv;+    gettimeofday(&tv, NULL);+    d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;+    return JS_NewInt64(ctx, d);+}++/* OS dependent. d = argv[0] is in ms from 1970. Return the difference+   between local time and UTC time 'd' in minutes */+static int getTimezoneOffset(int64_t time) {+#if defined(_WIN32)+    /* XXX: TODO */+    return 0;+#else+    time_t ti;+    struct tm tm;++    time /= 1000; /* convert to seconds */+    if (sizeof(time_t) == 4) {+        /* on 32-bit systems, we need to clamp the time value to the+           range of `time_t`. This is better than truncating values to+           32 bits and hopefully provides the same result as 64-bit+           implementation of localtime_r.+         */+        if ((time_t)-1 < 0) {+            if (time < INT32_MIN) {+                time = INT32_MIN;+            } else if (time > INT32_MAX) {+                time = INT32_MAX;+            }+        } else {+            if (time < 0) {+                time = 0;+            } else if (time > UINT32_MAX) {+                time = UINT32_MAX;+            }+        }+    }+    ti = time;+    localtime_r(&ti, &tm);+    return -tm.tm_gmtoff / 60;+#endif+}++#if 0+static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,+                                           int argc, JSValueConst *argv)+{+    double dd;++    if (JS_ToFloat64(ctx, &dd, argv[0]))+        return JS_EXCEPTION;+    if (isnan(dd))+        return __JS_NewFloat64(ctx, dd);+    else+        return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));+}++static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,+                                          JSValueConst def_proto)+{+    JSValue proto;+    proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);+    if (JS_IsException(proto))+        return proto;+    if (!JS_IsObject(proto)) {+        JS_FreeValue(ctx, proto);+        proto = JS_DupValue(ctx, def_proto);+    }+    return proto;+}++/* create a new date object */+static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue obj, proto;+    proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);+    if (JS_IsException(proto))+        return proto;+    obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);+    JS_FreeValue(ctx, proto);+    if (!JS_IsException(obj))+        JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));+    return obj;+}+#endif++/* RegExp */++static void js_regexp_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSRegExp *re = &p->u.regexp;+    JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));+    JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));+}++/* create a string containing the RegExp bytecode */+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,+                                 JSValueConst flags)+{+    const char *str;+    int re_flags, mask;+    uint8_t *re_bytecode_buf;+    size_t i, len;+    int re_bytecode_len;+    JSValue ret;+    char error_msg[64];++    re_flags = 0;+    if (!JS_IsUndefined(flags)) {+        str = JS_ToCStringLen(ctx, &len, flags);+        if (!str)+            return JS_EXCEPTION;+        /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */+        for (i = 0; i < len; i++) {+            switch(str[i]) {+            case 'g':+                mask = LRE_FLAG_GLOBAL;+                break;+            case 'i':+                mask = LRE_FLAG_IGNORECASE;+                break;+            case 'm':+                mask = LRE_FLAG_MULTILINE;+                break;+            case 's':+                mask = LRE_FLAG_DOTALL;+                break;+            case 'u':+                mask = LRE_FLAG_UTF16;+                break;+            case 'y':+                mask = LRE_FLAG_STICKY;+                break;+            default:+                goto bad_flags;+            }+            if ((re_flags & mask) != 0) {+            bad_flags:+                JS_FreeCString(ctx, str);+                return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");+            }+            re_flags |= mask;+        }+        JS_FreeCString(ctx, str);+    }++    str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));+    if (!str)+        return JS_EXCEPTION;+    re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,+                                  sizeof(error_msg), str, len, re_flags, ctx);+    JS_FreeCString(ctx, str);+    if (!re_bytecode_buf) {+        JS_ThrowSyntaxError(ctx, "%s", error_msg);+        return JS_EXCEPTION;+    }++    ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);+    js_free(ctx, re_bytecode_buf);+    return ret;+}++/* create a RegExp object from a string containing the RegExp bytecode+   and the source pattern */+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,+                                              JSValue pattern, JSValue bc)+{+    JSValue obj;+    JSObject *p;+    JSRegExp *re;++    /* sanity check */+    if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||+        JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {+        JS_ThrowTypeError(ctx, "string expected");+    fail:+        JS_FreeValue(ctx, bc);+        JS_FreeValue(ctx, pattern);+        return JS_EXCEPTION;+    }++    obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);+    if (JS_IsException(obj))+        goto fail;+    p = JS_VALUE_GET_OBJ(obj);+    re = &p->u.regexp;+    re->pattern = JS_VALUE_GET_STRING(pattern);+    re->bytecode = JS_VALUE_GET_STRING(bc);+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),+                           JS_PROP_WRITABLE);+    return obj;+}++static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)+{+    if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(obj);+        if (p->class_id == JS_CLASS_REGEXP)+            return &p->u.regexp;+    }+    if (throw_error) {+        JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);+    }+    return NULL;+}++/* return < 0 if exception or TRUE/FALSE */+static int js_is_regexp(JSContext *ctx, JSValueConst obj)+{+    JSValue m;++    if (!JS_IsObject(obj))+        return FALSE;+    m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);+    if (JS_IsException(m))+        return -1;+    if (!JS_IsUndefined(m))+        return JS_ToBoolFree(ctx, m);+    return js_get_regexp(ctx, obj, FALSE) != NULL;+}++static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,+                                     int argc, JSValueConst *argv)+{+    JSValue pattern, flags, bc, val;+    JSValueConst pat, flags1;+    JSRegExp *re;+    int pat_is_regexp;++    pat = argv[0];+    flags1 = argv[1];+    pat_is_regexp = js_is_regexp(ctx, pat);+    if (pat_is_regexp < 0)+        return JS_EXCEPTION;+    if (JS_IsUndefined(new_target)) {+        /* called as a function */+        new_target = JS_GetActiveFunction(ctx);+        if (pat_is_regexp && JS_IsUndefined(flags1)) {+            JSValue ctor;+            BOOL res;+            ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);+            if (JS_IsException(ctor))+                return ctor;+            res = js_same_value(ctx, ctor, new_target);+            JS_FreeValue(ctx, ctor);+            if (res)+                return JS_DupValue(ctx, pat);+        }+    }+    re = js_get_regexp(ctx, pat, FALSE);+    if (re) {+        pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));+        if (JS_IsUndefined(flags1)) {+            bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));+            goto no_compilation;+        } else {+            flags = JS_ToString(ctx, flags1);+            if (JS_IsException(flags))+                goto fail;+        }+    } else {+        flags = JS_UNDEFINED;+        if (pat_is_regexp) {+            pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);+            if (JS_IsException(pattern))+                goto fail;+            if (JS_IsUndefined(flags1)) {+                flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);+                if (JS_IsException(flags))+                    goto fail;+            } else {+                flags = JS_DupValue(ctx, flags1);+            }+        } else {+            pattern = JS_DupValue(ctx, pat);+            flags = JS_DupValue(ctx, flags1);+        }+        if (JS_IsUndefined(pattern)) {+            pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);+        } else {+            val = pattern;+            pattern = JS_ToString(ctx, val);+            JS_FreeValue(ctx, val);+            if (JS_IsException(pattern))+                goto fail;+        }+    }+    bc = js_compile_regexp(ctx, pattern, flags);+    if (JS_IsException(bc))+        goto fail;+    JS_FreeValue(ctx, flags);+ no_compilation:+    return js_regexp_constructor_internal(ctx, new_target, pattern, bc);+ fail:+    JS_FreeValue(ctx, pattern);+    JS_FreeValue(ctx, flags);+    return JS_EXCEPTION;+}++static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSRegExp *re1, *re;+    JSValueConst pattern1, flags1;+    JSValue bc, pattern;++    re = js_get_regexp(ctx, this_val, TRUE);+    if (!re)+        return JS_EXCEPTION;+    pattern1 = argv[0];+    flags1 = argv[1];+    re1 = js_get_regexp(ctx, pattern1, FALSE);+    if (re1) {+        if (!JS_IsUndefined(flags1))+            return JS_ThrowTypeError(ctx, "flags must be undefined");+        pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));+        bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));+    } else {+        bc = JS_UNDEFINED;+        if (JS_IsUndefined(pattern1))+            pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);+        else+            pattern = JS_ToString(ctx, pattern1);+        if (JS_IsException(pattern))+            goto fail;+        bc = js_compile_regexp(ctx, pattern, flags1);+        if (JS_IsException(bc))+            goto fail;+    }+    JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));+    JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));+    re->pattern = JS_VALUE_GET_STRING(pattern);+    re->bytecode = JS_VALUE_GET_STRING(bc);+    if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+                       JS_NewInt32(ctx, 0)) < 0)+        return JS_EXCEPTION;+    return JS_DupValue(ctx, this_val);+ fail:+    JS_FreeValue(ctx, pattern);+    JS_FreeValue(ctx, bc);+    return JS_EXCEPTION;+}++#if 0+static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)+{+    JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+    if (!re)+        return JS_EXCEPTION;+    return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));+}++static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)+{+    JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+    int flags;++    if (!re)+        return JS_EXCEPTION;+    flags = lre_get_flags(re->bytecode->u.str8);+    return JS_NewInt32(ctx, flags);+}+#endif++static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)+{+    JSRegExp *re;+    JSString *p;+    StringBuffer b_s, *b = &b_s;+    int i, n, c, c2, bra;++    if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);++    if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))+        goto empty_regex;++    re = js_get_regexp(ctx, this_val, TRUE);+    if (!re)+        return JS_EXCEPTION;++    p = re->pattern;++    if (p->len == 0) {+    empty_regex:+        return JS_NewString(ctx, "(?:)");+    }    +    string_buffer_init2(ctx, b, p->len, p->is_wide_char);++    /* Escape '/' and newline sequences as needed */+    bra = 0;+    for (i = 0, n = p->len; i < n;) {+        c2 = -1;+        switch (c = string_get(p, i++)) {+        case '\\':+            if (i < n)+                c2 = string_get(p, i++);+            break;+        case ']':+            bra = 0;+            break;+        case '[':+            if (!bra) {+                if (i < n && string_get(p, i) == ']')+                    c2 = string_get(p, i++);+                bra = 1;+            }+            break;+        case '\n':+            c = '\\';+            c2 = 'n';+            break;+        case '\r':+            c = '\\';+            c2 = 'r';+            break;+        case '/':+            if (!bra) {+                c = '\\';+                c2 = '/';+            }+            break;+        }+        string_buffer_putc16(b, c);+        if (c2 >= 0)+            string_buffer_putc16(b, c2);+    }+    return string_buffer_end(b);+}++static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)+{+    JSRegExp *re;+    int flags;++    if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);++    re = js_get_regexp(ctx, this_val, FALSE);+    if (!re) {+        if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))+            return JS_UNDEFINED;+        else+            return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);+    }+    +    flags = lre_get_flags(re->bytecode->u.str8);+    return JS_NewBool(ctx, (flags & mask) != 0);+}++static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)+{+    char str[8], *p = str;+    int res;++    if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);++    res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));+    if (res < 0)+        goto exception;+    if (res)+        *p++ = 'g';+    res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));+    if (res < 0)+        goto exception;+    if (res)+        *p++ = 'i';+    res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));+    if (res < 0)+        goto exception;+    if (res)+        *p++ = 'm';+    res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));+    if (res < 0)+        goto exception;+    if (res)+        *p++ = 's';+    res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));+    if (res < 0)+        goto exception;+    if (res)+        *p++ = 'u';+    res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));+    if (res < 0)+        goto exception;+    if (res)+        *p++ = 'y';+    return JS_NewStringLen(ctx, str, p - str);++exception:+    return JS_EXCEPTION;+}++static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValue pattern, flags;+    StringBuffer b_s, *b = &b_s;++    if (!JS_IsObject(this_val))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    string_buffer_init(ctx, b, 0);+    string_buffer_putc8(b, '/');+    pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);+    if (string_buffer_concat_value_free(b, pattern))+        goto fail;+    string_buffer_putc8(b, '/');+    flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);+    if (string_buffer_concat_value_free(b, flags))+        goto fail;+    return string_buffer_end(b);++fail:+    string_buffer_free(b);+    return JS_EXCEPTION;+}++BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)+{+    JSContext *ctx = opaque;+    return js_check_stack_overflow(ctx->rt, alloca_size);+}++void *lre_realloc(void *opaque, void *ptr, size_t size)+{+    JSContext *ctx = opaque;+    /* No JS exception is raised here */+    return js_realloc_rt(ctx->rt, ptr, size);+}++static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+    JSString *str;+    JSValue str_val, obj, val, groups = JS_UNDEFINED;+    uint8_t *re_bytecode;+    int ret;+    uint8_t **capture, *str_buf;+    int capture_count, shift, i, re_flags;+    int64_t last_index;+    const char *group_name_ptr;++    if (!re)+        return JS_EXCEPTION;+    str_val = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str_val))+        return str_val;+    val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);+    if (JS_IsException(val) ||+        JS_ToLengthFree(ctx, &last_index, val)) {+        JS_FreeValue(ctx, str_val);+        return JS_EXCEPTION;+    }+    re_bytecode = re->bytecode->u.str8;+    re_flags = lre_get_flags(re_bytecode);+    if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {+        last_index = 0;+    }+    str = JS_VALUE_GET_STRING(str_val);+    capture_count = lre_get_capture_count(re_bytecode);+    capture = NULL;+    if (capture_count > 0) {+        capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);+        if (!capture) {+            JS_FreeValue(ctx, str_val);+            return JS_EXCEPTION;+        }+    }+    shift = str->is_wide_char;+    str_buf = str->u.str8;+    if (last_index > str->len) {+        ret = 2;+    } else {+        ret = lre_exec(capture, re_bytecode,+                       str_buf, last_index, str->len,+                       shift, ctx);+    }+    obj = JS_NULL;+    if (ret != 1) {+        if (ret >= 0) {+            if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {+                if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+                                   JS_NewInt32(ctx, 0)) < 0)+                    goto fail;+            }+        } else {+            JS_ThrowInternalError(ctx, "out of memory in regexp execution");+            goto fail;+        }+        JS_FreeValue(ctx, str_val);+    } else {+        int prop_flags;+        if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {+            if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+                               JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)+                goto fail;+        }+        obj = JS_NewArray(ctx);+        if (JS_IsException(obj))+            goto fail;+        prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;+        group_name_ptr = NULL;+        if (re_flags & LRE_FLAG_NAMED_GROUPS) {+            uint32_t re_bytecode_len;+            groups = JS_NewObjectProto(ctx, JS_NULL);+            if (JS_IsException(groups))+                goto fail;+            re_bytecode_len = get_u32(re_bytecode + 3);+            group_name_ptr = (char *)(re_bytecode + 7 + re_bytecode_len);+        }++        for(i = 0; i < capture_count; i++) {+            int start, end;+            JSValue val;+            if (capture[2 * i] == NULL ||+                capture[2 * i + 1] == NULL) {+                val = JS_UNDEFINED;+            } else {+                start = (capture[2 * i] - str_buf) >> shift;+                end = (capture[2 * i + 1] - str_buf) >> shift;+                val = js_sub_string(ctx, str, start, end);+                if (JS_IsException(val))+                    goto fail;+            }+            if (group_name_ptr && i > 0) {+                if (*group_name_ptr) {+                    if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,+                                                  JS_DupValue(ctx, val),+                                                  prop_flags) < 0) {+                        JS_FreeValue(ctx, val);+                        goto fail;+                    }+                }+                group_name_ptr += strlen(group_name_ptr) + 1;+            }+            if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)+                goto fail;+        }+        if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,+                                   groups, prop_flags) < 0)+            goto fail;+        if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,+                                   JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)+            goto fail;+        if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)+            goto fail1;+    }+    js_free(ctx, capture);+    return obj;+fail:+    JS_FreeValue(ctx, groups);+    JS_FreeValue(ctx, str_val);+fail1:+    JS_FreeValue(ctx, obj);+    js_free(ctx, capture);+    return JS_EXCEPTION;+}++/* delete portions of a string that match a given regex */+static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)+{+    JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+    JSString *str;+    JSValue str_val, val;+    uint8_t *re_bytecode;+    int ret;+    uint8_t **capture, *str_buf;+    int capture_count, shift, re_flags;+    int next_src_pos, start, end;+    int64_t last_index;+    StringBuffer b_s, *b = &b_s;++    if (!re)+        return JS_EXCEPTION;++    string_buffer_init(ctx, b, 0);++    capture = NULL;+    str_val = JS_ToString(ctx, arg);+    if (JS_IsException(str_val))+        goto fail;+    str = JS_VALUE_GET_STRING(str_val);+    re_bytecode = re->bytecode->u.str8;+    re_flags = lre_get_flags(re_bytecode);+    if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {+        last_index = 0;+    } else {+        val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);+        if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, 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);+        if (!capture)+            goto fail;+    }+    shift = str->is_wide_char;+    str_buf = str->u.str8;+    next_src_pos = 0;+    for (;;) {+        if (last_index > str->len)+            break;++        ret = lre_exec(capture, re_bytecode,+                       str_buf, last_index, str->len, shift, ctx);+        if (ret != 1) {+            if (ret >= 0) {+                if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {+                    if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+                                       JS_NewInt32(ctx, 0)) < 0)+                        goto fail;+                }+            } else {+                JS_ThrowInternalError(ctx, "out of memory in regexp execution");+                goto fail;+            }+            break;+        }+        start = (capture[0] - str_buf) >> shift;+        end = (capture[1] - str_buf) >> shift;+        last_index = end;+        if (next_src_pos < start) {+            if (string_buffer_concat(b, str, next_src_pos, start))+                goto fail;+        }+        next_src_pos = end;+        if (!(re_flags & LRE_FLAG_GLOBAL)) {+            if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+                               JS_NewInt32(ctx, end)) < 0)+                goto fail;+            break;+        }+        if (end == start) {+            if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {+                end++;+            } else {+                string_getc(str, &end);+            }+        }+        last_index = end;+    }+    if (string_buffer_concat(b, str, next_src_pos, str->len))+        goto fail;+    JS_FreeValue(ctx, str_val);+    js_free(ctx, capture);+    return string_buffer_end(b);+fail:+    JS_FreeValue(ctx, str_val);+    js_free(ctx, capture);+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)+{+    JSValue method, ret;++    method = JS_GetProperty(ctx, r, JS_ATOM_exec);+    if (JS_IsException(method))+        return method;+    if (JS_IsFunction(ctx, method)) {+        ret = JS_CallFree(ctx, method, r, 1, &s);+        if (JS_IsException(ret))+            return ret;+        if (!JS_IsObject(ret) && !JS_IsNull(ret)) {+            JS_FreeValue(ctx, ret);+            return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");+        }+        return ret;+    }+    JS_FreeValue(ctx, method);+    return js_regexp_exec(ctx, r, 1, &s);+}++#if 0+static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    return JS_RegExpExec(ctx, argv[0], argv[1]);+}+static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    return JS_RegExpDelete(ctx, argv[0], argv[1]);+}+#endif++static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    JSValue val;+    BOOL ret;++    val = JS_RegExpExec(ctx, this_val, argv[0]);+    if (JS_IsException(val))+        return JS_EXCEPTION;+    ret = !JS_IsNull(val);+    JS_FreeValue(ctx, val);+    return JS_NewBool(ctx, ret);+}++static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    // [Symbol.match](str)+    JSValueConst rx = this_val;+    JSValue A, S, result, matchStr;+    int global, n, fullUnicode, isEmpty;+    JSString *p;++    if (!JS_IsObject(rx))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    A = JS_UNDEFINED;+    result = JS_UNDEFINED;+    matchStr = JS_UNDEFINED;+    S = JS_ToString(ctx, argv[0]);+    if (JS_IsException(S))+        goto exception;++    global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));+    if (global < 0)+        goto exception;++    if (!global) {+        A = JS_RegExpExec(ctx, rx, S);+    } else {+        fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));+        if (fullUnicode < 0)+            goto exception;++        if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)+            goto exception;+        A = JS_NewArray(ctx);+        if (JS_IsException(A))+            goto exception;+        n = 0;+        for(;;) {+            JS_FreeValue(ctx, result);+            result = JS_RegExpExec(ctx, rx, S);+            if (JS_IsException(result))+                goto exception;+            if (JS_IsNull(result))+                break;+            matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));+            if (JS_IsException(matchStr))+                goto exception;+            isEmpty = JS_IsEmptyString(matchStr);+            if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)+                goto exception;+            if (isEmpty) {+                int64_t thisIndex, nextIndex;+                if (JS_ToLengthFree(ctx, &thisIndex,+                                    JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)+                    goto exception;+                p = JS_VALUE_GET_STRING(S);+                nextIndex = string_advance_index(p, thisIndex, fullUnicode);+                if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)+                    goto exception;+            }+        }+        if (n == 0) {+            JS_FreeValue(ctx, A);+            A = JS_NULL;+        }+    }+    JS_FreeValue(ctx, result);+    JS_FreeValue(ctx, S);+    return A;++exception:+    JS_FreeValue(ctx, A);+    JS_FreeValue(ctx, result);+    JS_FreeValue(ctx, S);+    return JS_EXCEPTION;+}++typedef struct JSRegExpStringIteratorData {+    JSValue iterating_regexp;+    JSValue iterated_string;+    BOOL global;+    BOOL unicode;+    BOOL done;+} JSRegExpStringIteratorData;++static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;+    if (it) {+        JS_FreeValueRT(rt, it->iterating_regexp);+        JS_FreeValueRT(rt, it->iterated_string);+        js_free_rt(rt, it);+    }+}++static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,+                                           JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;+    if (it) {+        JS_MarkValue(rt, it->iterating_regexp, mark_func);+        JS_MarkValue(rt, it->iterated_string, mark_func);+    }+}++static JSValue js_regexp_string_iterator_next(JSContext *ctx,+                                              JSValueConst this_val,+                                              int argc, JSValueConst *argv,+                                              BOOL *pdone, int magic)+{+    JSRegExpStringIteratorData *it;+    JSValueConst R, S;+    JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;+    JSString *sp;++    it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);+    if (!it)+        goto exception;+    if (it->done) {+        *pdone = TRUE;+        return JS_UNDEFINED;+    }+    R = it->iterating_regexp;+    S = it->iterated_string;+    match = JS_RegExpExec(ctx, R, S);+    if (JS_IsException(match))+        goto exception;+    if (JS_IsNull(match)) {+        it->done = TRUE;+        *pdone = TRUE;+        return JS_UNDEFINED;+    } else if (it->global) {+        matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));+        if (JS_IsException(matchStr))+            goto exception;+        if (JS_IsEmptyString(matchStr)) {+            int64_t thisIndex, nextIndex;+            if (JS_ToLengthFree(ctx, &thisIndex,+                                JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)+                goto exception;+            sp = JS_VALUE_GET_STRING(S);+            nextIndex = string_advance_index(sp, thisIndex, it->unicode);+            if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,+                               JS_NewInt64(ctx, nextIndex)) < 0)+                goto exception;+        }+        JS_FreeValue(ctx, matchStr);+    } else {+        it->done = TRUE;+    }+    *pdone = FALSE;+    return match;+ exception:+    JS_FreeValue(ctx, match);+    JS_FreeValue(ctx, matchStr);+    *pdone = FALSE;+    return JS_EXCEPTION;+}++static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    // [Symbol.matchAll](str)+    JSValueConst R = this_val;+    JSValue S, C, flags, matcher, iter;+    JSValueConst args[2];+    JSString *strp;+    int64_t lastIndex;+    JSRegExpStringIteratorData *it;+    +    if (!JS_IsObject(R))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    C = JS_UNDEFINED;+    flags = JS_UNDEFINED;+    matcher = JS_UNDEFINED;+    iter = JS_UNDEFINED;+    +    S = JS_ToString(ctx, argv[0]);+    if (JS_IsException(S))+        goto exception;+    C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);+    if (JS_IsException(C))+        goto exception;+    flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));+    if (JS_IsException(flags))+        goto exception;+    args[0] = R;+    args[1] = flags;+    matcher = JS_CallConstructor(ctx, C, 2, args);+    if (JS_IsException(matcher))+        goto exception;+    if (JS_ToLengthFree(ctx, &lastIndex,+                        JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))+        goto exception;+    if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,+                       JS_NewInt64(ctx, lastIndex)) < 0)+        goto exception;+    +    iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);+    if (JS_IsException(iter))+        goto exception;+    it = js_malloc(ctx, sizeof(*it));+    if (!it)+        goto exception;+    it->iterating_regexp = matcher;+    it->iterated_string = S;+    strp = JS_VALUE_GET_STRING(flags);+    it->global = string_indexof_char(strp, 'g', 0) >= 0;+    it->unicode = string_indexof_char(strp, 'u', 0) >= 0;+    it->done = FALSE;+    JS_SetOpaque(iter, it);++    JS_FreeValue(ctx, C);+    JS_FreeValue(ctx, flags);+    return iter;+ exception:+    JS_FreeValue(ctx, S);+    JS_FreeValue(ctx, C);+    JS_FreeValue(ctx, flags);+    JS_FreeValue(ctx, matcher);+    JS_FreeValue(ctx, iter);+    return JS_EXCEPTION;+}++typedef struct ValueBuffer {+    JSContext *ctx;+    JSValue *arr;+    JSValue def[4];+    int len;+    int size;+    int error_status;+} ValueBuffer;++static int value_buffer_init(JSContext *ctx, ValueBuffer *b)+{+    b->ctx = ctx;+    b->len = 0;+    b->size = 4;+    b->error_status = 0;+    b->arr = b->def;+    return 0;+}++static void value_buffer_free(ValueBuffer *b)+{+    while (b->len > 0)+        JS_FreeValue(b->ctx, b->arr[--b->len]);+    if (b->arr != b->def)+        js_free(b->ctx, b->arr);+    b->arr = b->def;+    b->size = 4;+}++static int value_buffer_append(ValueBuffer *b, JSValue val)+{+    if (b->error_status)+        return -1;++    if (b->len >= b->size) {+        int new_size = (b->len + (b->len >> 1) + 31) & ~16;+        size_t slack;+        JSValue *new_arr;++        if (b->arr == b->def) {+            new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);+            if (new_arr)+                memcpy(new_arr, b->def, sizeof b->def);+        } else {+            new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);+        }+        if (!new_arr) {+            value_buffer_free(b);+            JS_FreeValue(b->ctx, val);+            b->error_status = -1;+            return -1;+        }+        new_size += slack / sizeof(*new_arr);+        b->arr = new_arr;+        b->size = new_size;+    }+    b->arr[b->len++] = val;+    return 0;+}++static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)+{+    JSValue val;+    int res;++    val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);+    if (JS_IsException(val))+        return -1;+    // rx.constructor === RegExp+    res = js_same_value(ctx, val, ctx->regexp_ctor);+    JS_FreeValue(ctx, val);+    if (res) {+        val = JS_GetProperty(ctx, rx, JS_ATOM_exec);+        if (JS_IsException(val))+            return -1;+        // rx.exec === RE_exec+        res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);+        JS_FreeValue(ctx, val);+    }+    return res;+}++static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    // [Symbol.replace](str, rep)+    JSValueConst rx = this_val, rep = argv[1];+    JSValueConst args[6];+    JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;+    JSString *sp, *rp;+    StringBuffer b_s, *b = &b_s;+    ValueBuffer v_b, *results = &v_b;+    int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;+    uint32_t nCaptures;+    int64_t position;++    if (!JS_IsObject(rx))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    string_buffer_init(ctx, b, 0);+    value_buffer_init(ctx, results);++    rep_val = JS_UNDEFINED;+    matched = JS_UNDEFINED;+    tab = JS_UNDEFINED;+    rep_str = JS_UNDEFINED;+    namedCaptures = JS_UNDEFINED;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        goto exception;+        +    sp = JS_VALUE_GET_STRING(str);+    rp = NULL;+    functionalReplace = JS_IsFunction(ctx, rep);+    if (!functionalReplace) {+        rep_val = JS_ToString(ctx, rep);+        if (JS_IsException(rep_val))+            goto exception;+        rp = JS_VALUE_GET_STRING(rep_val);+    }+    fullUnicode = 0;+    is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));+    if (is_global < 0)+        goto exception;+    if (is_global) {+        fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));+        if (fullUnicode < 0)+            goto exception;+        if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)+            goto exception;+    }++    if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {+        /* use faster version for simple cases */+        res = JS_RegExpDelete(ctx, rx, str);+        goto done;+    }+    for(;;) {+        JSValue result;+        result = JS_RegExpExec(ctx, rx, str);+        if (JS_IsException(result))+            goto exception;+        if (JS_IsNull(result))+            break;+        if (value_buffer_append(results, result) < 0)+            goto exception;+        if (!is_global)+            break;+        JS_FreeValue(ctx, matched);+        matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));+        if (JS_IsException(matched))+            goto exception;+        if (JS_IsEmptyString(matched)) {+            /* always advance of at least one char */+            int64_t thisIndex, nextIndex;+            if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)+                goto exception;+            nextIndex = string_advance_index(sp, thisIndex, fullUnicode);+            if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)+                goto exception;+        }+    }+    nextSourcePosition = 0;+    for(j = 0; j < results->len; j++) {+        JSValueConst result;+        result = results->arr[j];+        if (js_get_length32(ctx, &nCaptures, result) < 0)+            goto exception;+        JS_FreeValue(ctx, matched);+        matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));+        if (JS_IsException(matched))+            goto exception;+        if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))+            goto exception;+        if (position > sp->len)+            position = sp->len;+        else if (position < 0)+            position = 0;+        /* ignore substition if going backward (can happen+           with custom regexp object) */+        JS_FreeValue(ctx, tab);+        tab = JS_NewArray(ctx);+        if (JS_IsException(tab))+            goto exception;+        if (JS_SetPropertyInt64(ctx, tab, 0, JS_DupValue(ctx, matched)) < 0)+            goto exception;+        for(n = 1; n < nCaptures; n++) {+            JSValue capN;+            capN = JS_GetPropertyInt64(ctx, result, n);+            if (JS_IsException(capN))+                goto exception;+            if (!JS_IsUndefined(capN)) {+                capN = JS_ToStringFree(ctx, capN);+                if (JS_IsException(capN))+                    goto exception;+            }+            if (JS_SetPropertyInt64(ctx, tab, n, capN) < 0)+                goto exception;+        }+        JS_FreeValue(ctx, namedCaptures);+        namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);+        if (JS_IsException(namedCaptures))+            goto exception;+        if (functionalReplace) {+            if (JS_SetPropertyInt64(ctx, tab, n++, JS_NewInt32(ctx, position)) < 0)+                goto exception;+            if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, str)) < 0)+                goto exception;+            if (!JS_IsUndefined(namedCaptures)) {+                if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures)) < 0)+                    goto exception;+            }+            args[0] = JS_UNDEFINED;+            args[1] = tab;+            JS_FreeValue(ctx, rep_str);+            rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));+        } else {+            JSValue namedCaptures1;+            if (!JS_IsUndefined(namedCaptures)) {+                namedCaptures1 = JS_ToObject(ctx, namedCaptures);+                if (JS_IsException(namedCaptures1))+                    goto exception;+            } else {+                namedCaptures1 = JS_UNDEFINED;+            }+            args[0] = matched;+            args[1] = str;+            args[2] = JS_NewInt32(ctx, position);+            args[3] = tab;+            args[4] = namedCaptures1;+            args[5] = rep_val;+            JS_FreeValue(ctx, rep_str);+            rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);+            JS_FreeValue(ctx, namedCaptures1);+        }+        if (JS_IsException(rep_str))+            goto exception;+        if (position >= nextSourcePosition) {+            string_buffer_concat(b, sp, nextSourcePosition, position);+            string_buffer_concat_value(b, rep_str);+            nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;+        }+    }+    string_buffer_concat(b, sp, nextSourcePosition, sp->len);+    res = string_buffer_end(b);+    goto done1;++exception:+    res = JS_EXCEPTION;+done:+    string_buffer_free(b);+done1:+    value_buffer_free(results);+    JS_FreeValue(ctx, rep_val);+    JS_FreeValue(ctx, matched);+    JS_FreeValue(ctx, tab);+    JS_FreeValue(ctx, rep_str);+    JS_FreeValue(ctx, namedCaptures);+    JS_FreeValue(ctx, str);+    return res;+}++static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValueConst rx = this_val;+    JSValue str, previousLastIndex, currentLastIndex, result, index;++    if (!JS_IsObject(rx))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    result = JS_UNDEFINED;+    currentLastIndex = JS_UNDEFINED;+    previousLastIndex = JS_UNDEFINED;+    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        goto exception;++    previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);+    if (JS_IsException(previousLastIndex))+        goto exception;++    if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {+        if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {+            goto exception;+        }+    }+    result = JS_RegExpExec(ctx, rx, str);+    if (JS_IsException(result))+        goto exception;+    currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);+    if (JS_IsException(currentLastIndex))+        goto exception;+    if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {+        JS_FreeValue(ctx, previousLastIndex);+    } else {+        if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0)+            goto exception;+    }+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, currentLastIndex);++    if (JS_IsNull(result)) {+        return JS_NewInt32(ctx, -1);+    } else {+        index = JS_GetProperty(ctx, result, JS_ATOM_index);+        JS_FreeValue(ctx, result);+        return index;+    }++exception:+    JS_FreeValue(ctx, result);+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, currentLastIndex);+    JS_FreeValue(ctx, previousLastIndex);+    return JS_EXCEPTION;+}++static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    // [Symbol.split](str, limit)+    JSValueConst rx = this_val;+    JSValueConst args[2];+    JSValue str, ctor, splitter, A, flags, z, sub;+    JSString *strp;+    uint32_t lim, size, p, q;+    int unicodeMatching;+    int64_t lengthA, e, numberOfCaptures, i;++    if (!JS_IsObject(rx))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    ctor = JS_UNDEFINED;+    splitter = JS_UNDEFINED;+    A = JS_UNDEFINED;+    flags = JS_UNDEFINED;+    z = JS_UNDEFINED;+    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        goto exception;+    ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);+    if (JS_IsException(ctor))+        goto exception;+    flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));+    if (JS_IsException(flags))+        goto exception;+    strp = JS_VALUE_GET_STRING(flags);+    unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;+    if (string_indexof_char(strp, 'y', 0) < 0) {+        flags = JS_ConcatString3(ctx, "", flags, "y");+        if (JS_IsException(flags))+            goto exception;+    }+    args[0] = rx;+    args[1] = flags;+    splitter = JS_CallConstructor(ctx, ctor, 2, args);+    if (JS_IsException(splitter))+        goto exception;+    A = JS_NewArray(ctx);+    if (JS_IsException(A))+        goto exception;+    lengthA = 0;+    if (JS_IsUndefined(argv[1])) {+        lim = 0xffffffff;+    } else {+        if (JS_ToUint32(ctx, &lim, argv[1]) < 0)+            goto exception;+        if (lim == 0)+            goto done;+    }+    strp = JS_VALUE_GET_STRING(str);+    p = q = 0;+    size = strp->len;+    if (size == 0) {+        z = JS_RegExpExec(ctx, splitter, str);+        if (JS_IsException(z))+            goto exception;+        if (JS_IsNull(z))+            goto add_tail;+        goto done;+    }+    while (q < size) {+        if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)+            goto exception;+        JS_FreeValue(ctx, z);    +        z = JS_RegExpExec(ctx, splitter, str);+        if (JS_IsException(z))+            goto exception;+        if (JS_IsNull(z)) {+            q = string_advance_index(strp, q, unicodeMatching);+        } else {+            if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))+                goto exception;+            if (e > size)+                e = size;+            if (e == p) {+                q = string_advance_index(strp, q, unicodeMatching);+            } else {+                sub = js_sub_string(ctx, strp, p, q);+                if (JS_IsException(sub))+                    goto exception;+                if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0)+                    goto exception;+                if (lengthA == lim)+                    goto done;+                p = e;+                if (js_get_length64(ctx, &numberOfCaptures, z))+                    goto exception;+                for(i = 1; i < numberOfCaptures; i++) {+                    sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));+                    if (JS_IsException(sub))+                        goto exception;+                    if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0)+                        goto exception;+                    if (lengthA == lim)+                        goto done;+                }+                q = p;+            }+        }+    }+add_tail:+    if (p > size)+        p = size;+    sub = js_sub_string(ctx, strp, p, size);+    if (JS_IsException(sub))+        goto exception;+    if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0)+        goto exception;+    goto done;+exception:+    JS_FreeValue(ctx, A);+    A = JS_EXCEPTION;+done:+    JS_FreeValue(ctx, str);+    JS_FreeValue(ctx, ctor);+    JS_FreeValue(ctx, splitter);+    JS_FreeValue(ctx, flags);+    JS_FreeValue(ctx, z);    +    return A;+}++static const JSCFunctionListEntry js_regexp_funcs[] = {+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+    //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),+    //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),+};++static const JSCFunctionListEntry js_regexp_proto_funcs[] = {+    JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),+    JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),+    JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),+    JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),+    JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),+    JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),+    JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),+    JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),+    JS_CFUNC_DEF("exec", 1, js_regexp_exec ),+    JS_CFUNC_DEF("compile", 2, js_regexp_compile ),+    JS_CFUNC_DEF("test", 1, js_regexp_test ),+    JS_CFUNC_DEF("toString", 0, js_regexp_toString ),+    JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),+    JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),+    JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),+    JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),+    JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),+    //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),+    //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),+};++static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {+    JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)+{+    ctx->compile_regexp = js_compile_regexp;+}++void JS_AddIntrinsicRegExp(JSContext *ctx)+{+    JSValueConst obj;++    JS_AddIntrinsicRegExpCompiler(ctx);++    ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,+                               countof(js_regexp_proto_funcs));+    obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,+                                   ctx->class_proto[JS_CLASS_REGEXP]);+    ctx->regexp_ctor = JS_DupValue(ctx, obj);+    JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));++    ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =+        JS_NewObjectProto(ctx, ctx->iterator_proto);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],+                               js_regexp_string_iterator_proto_funcs,+                               countof(js_regexp_string_iterator_proto_funcs));+}++/* JSON */++static int json_parse_expect(JSParseState *s, int tok)+{+    if (s->token.val != tok) {+        /* XXX: dump token correctly in all cases */+        return js_parse_error(s, "expecting '%c'", tok);+    }+    return json_next_token(s);+}++static JSValue json_parse_value(JSParseState *s)+{+    JSContext *ctx = s->ctx;+    JSValue val = JS_NULL;+    int ret;++    switch(s->token.val) {+    case '{':+        {+            JSValue prop_val;+            JSAtom prop_name;+            +            if (json_next_token(s))+                goto fail;+            val = JS_NewObject(ctx);+            if (JS_IsException(val))+                goto fail;+            if (s->token.val != '}') {+                for(;;) {+                    if (s->token.val == TOK_STRING) {+                        prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);+                        if (prop_name == JS_ATOM_NULL)+                            goto fail;+                    } else if (s->ext_json && s->token.val == TOK_IDENT) {+                        prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);+                    } else {+                        js_parse_error(s, "expecting property name");+                        goto fail;+                    }+                    if (json_next_token(s))+                        goto fail1;+                    if (json_parse_expect(s, ':'))+                        goto fail1;+                    prop_val = json_parse_value(s);+                    if (JS_IsException(prop_val)) {+                    fail1:+                        JS_FreeAtom(ctx, prop_name);+                        goto fail;+                    }+                    ret = JS_DefinePropertyValue(ctx, val, prop_name,+                                                 prop_val, JS_PROP_C_W_E);+                    JS_FreeAtom(ctx, prop_name);+                    if (ret < 0)+                        goto fail;++                    if (s->token.val != ',')+                        break;+                    if (json_next_token(s))+                        goto fail;+                    if (s->ext_json && s->token.val == '}')+                        break;+                }+            }+            if (json_parse_expect(s, '}'))+                goto fail;+        }+        break;+    case '[':+        {+            JSValue el;+            uint32_t idx;++            if (json_next_token(s))+                goto fail;+            val = JS_NewArray(ctx);+            if (JS_IsException(val))+                goto fail;+            if (s->token.val != ']') {+                idx = 0;+                for(;;) {+                    el = json_parse_value(s);+                    if (JS_IsException(el))+                        goto fail;+                    ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);+                    if (ret < 0)+                        goto fail;+                    if (s->token.val != ',')+                        break;+                    if (json_next_token(s))+                        goto fail;+                    idx++;+                    if (s->ext_json && s->token.val == ']')+                        break;+                }+            }+            if (json_parse_expect(s, ']'))+                goto fail;+        }+        break;+    case TOK_STRING:+        val = JS_DupValue(ctx, s->token.u.str.str);+        if (json_next_token(s))+            goto fail;+        break;+    case TOK_NUMBER:+        val = s->token.u.num.val;+        if (json_next_token(s))+            goto fail;+        break;+    case TOK_IDENT:+        if (s->token.u.ident.atom == JS_ATOM_false ||+            s->token.u.ident.atom == JS_ATOM_true) {+            val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));+        } else if (s->token.u.ident.atom == JS_ATOM_null) {+            val = JS_NULL;+        } else {+            goto def_token;+        }+        if (json_next_token(s))+            goto fail;+        break;+    default:+    def_token:+        if (s->token.val == TOK_EOF) {+            js_parse_error(s, "unexpected end of input");+        } else {+            js_parse_error(s, "unexpected token: '%.*s'",+                           (int)(s->buf_ptr - s->token.ptr), s->token.ptr);+        }+        goto fail;+    }+    return val;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,+                      const char *filename, int flags)+{+    JSParseState s1, *s = &s1;+    JSValue val = JS_UNDEFINED;++    js_parse_init(ctx, s, buf, buf_len, filename);+    s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);+    if (json_next_token(s))+        goto fail;+    val = json_parse_value(s);+    if (JS_IsException(val))+        goto fail;+    if (s->token.val != TOK_EOF) {+        if (js_parse_error(s, "unexpected data at the end"))+            goto fail;+    }+    return val;+ fail:+    JS_FreeValue(ctx, val);+    free_token(s, &s->token);+    return JS_EXCEPTION;+}++JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,+                     const char *filename)+{+    return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); +}++static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,+                                         JSAtom name, JSValueConst reviver)+{+    JSValue val, new_el, name_val, res;+    JSValueConst args[2];+    int ret, is_array;+    uint32_t i, len = 0;+    JSAtom prop;+    JSPropertyEnum *atoms = NULL;++    if (js_check_stack_overflow(ctx->rt, 0)) {+        return JS_ThrowStackOverflow(ctx);+    }++    val = JS_GetProperty(ctx, holder, name);+    if (JS_IsException(val))+        return val;+    if (JS_IsObject(val)) {+        is_array = JS_IsArray(ctx, val);+        if (is_array < 0)+            goto fail;+        if (is_array) {+            if (js_get_length32(ctx, &len, val))+                goto fail;+        } else {+            ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);+            if (ret < 0)+                goto fail;+        }+        for(i = 0; i < len; i++) {+            if (is_array) {+                prop = JS_NewAtomUInt32(ctx, i);+                if (prop == JS_ATOM_NULL)+                    goto fail;+            } else {+                prop = JS_DupAtom(ctx, atoms[i].atom);+            }+            new_el = internalize_json_property(ctx, val, prop, reviver);+            if (JS_IsException(new_el)) {+                JS_FreeAtom(ctx, prop);+                goto fail;+            }+            if (JS_IsUndefined(new_el)) {+                ret = JS_DeleteProperty(ctx, val, prop, 0);+            } else {+                ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);+            }+            JS_FreeAtom(ctx, prop);+            if (ret < 0)+                goto fail;+        }+    }+    js_free_prop_enum(ctx, atoms, len);+    atoms = NULL;+    name_val = JS_AtomToValue(ctx, name);+    if (JS_IsException(name_val))+        goto fail;+    args[0] = name_val;+    args[1] = val;+    res = JS_Call(ctx, reviver, holder, 2, args);+    JS_FreeValue(ctx, name_val);+    JS_FreeValue(ctx, val);+    return res;+ fail:+    js_free_prop_enum(ctx, atoms, len);+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    JSValue obj, root;+    JSValueConst reviver;+    const char *str;+    size_t len;++    str = JS_ToCStringLen(ctx, &len, argv[0]);+    if (!str)+        return JS_EXCEPTION;+    obj = JS_ParseJSON(ctx, str, len, "<input>");+    JS_FreeCString(ctx, str);+    if (JS_IsException(obj))+        return obj;+    if (argc > 1 && JS_IsFunction(ctx, argv[1])) {+        reviver = argv[1];+        root = JS_NewObject(ctx);+        if (JS_IsException(root)) {+            JS_FreeValue(ctx, obj);+            return JS_EXCEPTION;+        }+        if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,+                                   JS_PROP_C_W_E) < 0) {+            JS_FreeValue(ctx, root);+            return JS_EXCEPTION;+        }+        obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,+                                        reviver);+        JS_FreeValue(ctx, root);+    }+    return obj;+}++typedef struct JSONStringifyContext {+    JSValueConst replacer_func;+    JSValue stack;+    JSValue property_list;+    JSValue gap;+    JSValue empty;+    StringBuffer *b;+} JSONStringifyContext;++static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {+    JSValue r = JS_ToQuotedString(ctx, val);+    JS_FreeValue(ctx, val);+    return r;+}++static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,+                             JSValueConst holder, JSValue val, JSValueConst key)+{+    JSValue v;+    JSValueConst args[2];++    if (JS_IsObject(val)+#ifdef CONFIG_BIGNUM+    ||  JS_IsBigInt(ctx, val)   /* XXX: probably useless */+#endif+        ) {+            JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);+            if (JS_IsException(f))+                goto exception;+            if (JS_IsFunction(ctx, f)) {+                v = JS_CallFree(ctx, f, val, 1, &key);+                JS_FreeValue(ctx, val);+                val = v;+                if (JS_IsException(val))+                    goto exception;+            } else {+                JS_FreeValue(ctx, f);+            }+        }++    if (!JS_IsUndefined(jsc->replacer_func)) {+        args[0] = key;+        args[1] = val;+        v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);+        JS_FreeValue(ctx, val);+        val = v;+        if (JS_IsException(val))+            goto exception;+    }++    switch (JS_VALUE_GET_NORM_TAG(val)) {+    case JS_TAG_OBJECT:+        if (JS_IsFunction(ctx, val))+            break;+    case JS_TAG_STRING:+    case JS_TAG_INT:+    case JS_TAG_FLOAT64:+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+#endif+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+#endif+    case JS_TAG_EXCEPTION:+        return val;+    default:+        break;+    }+    JS_FreeValue(ctx, val);+    return JS_UNDEFINED;++exception:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,+                          JSValueConst holder, JSValue val,+                          JSValueConst indent)+{+    JSValue indent1, sep, sep1, tab, v, prop;+    JSObject *p;+    int64_t i, len;+    int cl, ret;+    BOOL has_content;+    +    indent1 = JS_UNDEFINED;+    sep = JS_UNDEFINED;+    sep1 = JS_UNDEFINED;+    tab = JS_UNDEFINED;+    prop = JS_UNDEFINED;++    switch (JS_VALUE_GET_NORM_TAG(val)) {+    case JS_TAG_OBJECT:+        p = JS_VALUE_GET_OBJ(val);+        cl = p->class_id;+        if (cl == JS_CLASS_STRING) {+            val = JS_ToStringFree(ctx, val);+            if (JS_IsException(val))+                goto exception;+            val = JS_ToQuotedStringFree(ctx, val);+            if (JS_IsException(val))+                goto exception;+            return string_buffer_concat_value_free(jsc->b, val);+        } else if (cl == JS_CLASS_NUMBER) {+            val = JS_ToNumberFree(ctx, val);+            if (JS_IsException(val))+                goto exception;+            return string_buffer_concat_value_free(jsc->b, val);+        } else if (cl == JS_CLASS_BOOLEAN) {+            ret = string_buffer_concat_value(jsc->b, p->u.object_data);+            JS_FreeValue(ctx, val);+            return ret;+        }+#ifdef CONFIG_BIGNUM+        else if (cl == JS_CLASS_BIG_FLOAT) {+            return string_buffer_concat_value_free(jsc->b, val);+        } else if (cl == JS_CLASS_BIG_INT) {+            JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");+            goto exception;+        }+#endif+        v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);+        if (JS_IsException(v))+            goto exception;+        if (JS_ToBoolFree(ctx, v)) {+            JS_ThrowTypeError(ctx, "circular reference");+            goto exception;+        }+        indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));+        if (JS_IsException(indent1))+            goto exception;+        if (!JS_IsEmptyString(jsc->gap)) {+            sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");+            if (JS_IsException(sep))+                goto exception;+            sep1 = JS_NewString(ctx, " ");+            if (JS_IsException(sep1))+                goto exception;+        } else {+            sep = JS_DupValue(ctx, jsc->empty);+            sep1 = JS_DupValue(ctx, jsc->empty);+        }+        v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);+        if (check_exception_free(ctx, v))+            goto exception;+        ret = JS_IsArray(ctx, val);+        if (ret < 0)+            goto exception;+        if (ret) {+            if (js_get_length64(ctx, &len, val))+                goto exception;+            string_buffer_putc8(jsc->b, '[');+            for(i = 0; i < len; i++) {+                if (i > 0)+                    string_buffer_putc8(jsc->b, ',');+                string_buffer_concat_value(jsc->b, sep);+                v = JS_GetPropertyInt64(ctx, val, i);+                if (JS_IsException(v))+                    goto exception;+                /* XXX: could do this string conversion only when needed */+                prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));+                if (JS_IsException(prop))+                    goto exception;+                v = js_json_check(ctx, jsc, val, v, prop);+                JS_FreeValue(ctx, prop);+                prop = JS_UNDEFINED;+                if (JS_IsException(v))+                    goto exception;+                if (JS_IsUndefined(v))+                    v = JS_NULL;+                if (js_json_to_str(ctx, jsc, val, v, indent1))+                    goto exception;+            }+            if (len > 0 && !JS_IsEmptyString(jsc->gap)) {+                string_buffer_putc8(jsc->b, '\n');+                string_buffer_concat_value(jsc->b, indent);+            }+            string_buffer_putc8(jsc->b, ']');+        } else {+            if (!JS_IsUndefined(jsc->property_list))+                tab = JS_DupValue(ctx, jsc->property_list);+            else+                tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);+            if (JS_IsException(tab))+                goto exception;+            if (js_get_length64(ctx, &len, tab))+                goto exception;+            string_buffer_putc8(jsc->b, '{');+            has_content = FALSE;+            for(i = 0; i < len; i++) {+                JS_FreeValue(ctx, prop);+                prop = JS_GetPropertyInt64(ctx, tab, i);+                if (JS_IsException(prop))+                    goto exception;+                v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));+                if (JS_IsException(v))+                    goto exception;+                v = js_json_check(ctx, jsc, val, v, prop);+                if (JS_IsException(v))+                    goto exception;+                if (!JS_IsUndefined(v)) {+                    if (has_content)+                        string_buffer_putc8(jsc->b, ',');+                    prop = JS_ToQuotedStringFree(ctx, prop);+                    if (JS_IsException(prop)) {+                        JS_FreeValue(ctx, v);+                        goto exception;+                    }+                    string_buffer_concat_value(jsc->b, sep);+                    string_buffer_concat_value(jsc->b, prop);+                    string_buffer_putc8(jsc->b, ':');+                    string_buffer_concat_value(jsc->b, sep1);+                    if (js_json_to_str(ctx, jsc, val, v, indent1))+                        goto exception;+                    has_content = TRUE;+                }+            }+            if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {+                string_buffer_putc8(jsc->b, '\n');+                string_buffer_concat_value(jsc->b, indent);+            }+            string_buffer_putc8(jsc->b, '}');+        }+        if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))+            goto exception;+        JS_FreeValue(ctx, val);+        JS_FreeValue(ctx, tab);+        JS_FreeValue(ctx, sep);+        JS_FreeValue(ctx, sep1);+        JS_FreeValue(ctx, indent1);+        JS_FreeValue(ctx, prop);+        return 0;+    case JS_TAG_STRING:+        val = JS_ToQuotedStringFree(ctx, val);+        if (JS_IsException(val))+            goto exception;+        goto concat_value;+    case JS_TAG_FLOAT64:+        if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {+            val = JS_NULL;+        }+        goto concat_value;+    case JS_TAG_INT:+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_FLOAT:+#endif+    case JS_TAG_BOOL:+    case JS_TAG_NULL:+    concat_value:+        return string_buffer_concat_value_free(jsc->b, val);+#ifdef CONFIG_BIGNUM+    case JS_TAG_BIG_INT:+        JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");+        goto exception;+#endif+    default:+        JS_FreeValue(ctx, val);+        return 0;+    }+    +exception:+    JS_FreeValue(ctx, val);+    JS_FreeValue(ctx, tab);+    JS_FreeValue(ctx, sep);+    JS_FreeValue(ctx, sep1);+    JS_FreeValue(ctx, indent1);+    JS_FreeValue(ctx, prop);+    return -1;+}++JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,+                         JSValueConst replacer, JSValueConst space0)+{+    StringBuffer b_s;+    JSONStringifyContext jsc_s, *jsc = &jsc_s;+    JSValue val, v, space, ret, wrapper;+    int res;+    int64_t i, j, n;++    jsc->replacer_func = JS_UNDEFINED;+    jsc->stack = JS_UNDEFINED;+    jsc->property_list = JS_UNDEFINED;+    jsc->gap = JS_UNDEFINED;+    jsc->b = &b_s;+    jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);+    ret = JS_UNDEFINED;+    wrapper = JS_UNDEFINED;++    string_buffer_init(ctx, jsc->b, 0);+    jsc->stack = JS_NewArray(ctx);+    if (JS_IsException(jsc->stack))+        goto exception;+    if (JS_IsFunction(ctx, replacer)) {+        jsc->replacer_func = replacer;+    } else {+        res = JS_IsArray(ctx, replacer);+        if (res < 0)+            goto exception;+        if (res) {+            /* XXX: enumeration is not fully correct */+            jsc->property_list = JS_NewArray(ctx);+            if (JS_IsException(jsc->property_list))+                goto exception;+            if (js_get_length64(ctx, &n, replacer))+                goto exception;+            for (i = j = 0; i < n; i++) {+                JSValue present;+                v = JS_GetPropertyInt64(ctx, replacer, i);+                if (JS_IsException(v))+                    goto exception;+                if (JS_IsObject(v)) {+                    JSObject *p = JS_VALUE_GET_OBJ(v);+                    if (p->class_id == JS_CLASS_STRING ||+                        p->class_id == JS_CLASS_NUMBER) {+                        v = JS_ToStringFree(ctx, v);+                        if (JS_IsException(v))+                            goto exception;+                    } else {+                        JS_FreeValue(ctx, v);+                        continue;+                    }+                } else if (JS_IsNumber(v)) {+                    v = JS_ToStringFree(ctx, v);+                    if (JS_IsException(v))+                        goto exception;+                } else if (!JS_IsString(v)) {+                    JS_FreeValue(ctx, v);+                    continue;+                }+                present = js_array_includes(ctx, jsc->property_list,+                                            1, (JSValueConst *)&v);+                if (JS_IsException(present)) {+                    JS_FreeValue(ctx, v);+                    goto exception;+                }+                if (!JS_ToBoolFree(ctx, present)) {+                    JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);+                } else {+                    JS_FreeValue(ctx, v);+                }+            }+        }+    }+    space = JS_DupValue(ctx, space0);+    if (JS_IsObject(space)) {+        JSObject *p = JS_VALUE_GET_OBJ(space);+        if (p->class_id == JS_CLASS_NUMBER) {+            space = JS_ToNumberFree(ctx, space);+        } else if (p->class_id == JS_CLASS_STRING) {+            space = JS_ToStringFree(ctx, space);+        }+        if (JS_IsException(space)) {+            JS_FreeValue(ctx, space);+            goto exception;+        }+    }+    if (JS_IsNumber(space)) {+        int n;+        if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))+            goto exception;+        jsc->gap = JS_NewStringLen(ctx, "          ", n);+    } else if (JS_IsString(space)) {+        JSString *p = JS_VALUE_GET_STRING(space);+        jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));+    } else {+        jsc->gap = JS_DupValue(ctx, jsc->empty);+    }+    JS_FreeValue(ctx, space);+    if (JS_IsException(jsc->gap))+        goto exception;+    wrapper = JS_NewObject(ctx);+    if (JS_IsException(wrapper))+        goto exception;+    if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,+                               JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)+        goto exception;+    val = JS_DupValue(ctx, obj);+                           +    val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);+    if (JS_IsException(val))+        goto exception;+    if (JS_IsUndefined(val)) {+        ret = JS_UNDEFINED;+        goto done1;+    }+    if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))+        goto exception;++    ret = string_buffer_end(jsc->b);+    goto done;++exception:+    ret = JS_EXCEPTION;+done1:+    string_buffer_free(jsc->b);+done:+    JS_FreeValue(ctx, wrapper);+    JS_FreeValue(ctx, jsc->empty);+    JS_FreeValue(ctx, jsc->gap);+    JS_FreeValue(ctx, jsc->property_list);+    JS_FreeValue(ctx, jsc->stack);+    return ret;+}++static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    // stringify(val, replacer, space)+    return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);+}++static const JSCFunctionListEntry js_json_funcs[] = {+    JS_CFUNC_DEF("parse", 2, js_json_parse ),+    JS_CFUNC_DEF("stringify", 3, js_json_stringify ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_json_obj[] = {+    JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicJSON(JSContext *ctx)+{+    /* add JSON as autoinit object */+    JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));+}++/* Reflect */++static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 0);+}++static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValueConst func, array_arg, new_target;+    JSValue *tab, ret;+    uint32_t len;++    func = argv[0];+    array_arg = argv[1];+    if (argc > 2) {+        new_target = argv[2];+        if (!JS_IsConstructor(ctx, new_target))+            return JS_ThrowTypeError(ctx, "not a constructor");+    } else {+        new_target = func;+    }+    tab = build_arg_list(ctx, &len, array_arg);+    if (!tab)+        return JS_EXCEPTION;+    ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);+    free_arg_list(ctx, tab, len);+    return ret;+}++static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    JSValueConst obj;+    JSAtom atom;+    int ret;++    obj = argv[0];+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);+    atom = JS_ValueToAtom(ctx, argv[1]);+    if (unlikely(atom == JS_ATOM_NULL))+        return JS_EXCEPTION;+    ret = JS_DeleteProperty(ctx, obj, atom, 0);+    JS_FreeAtom(ctx, atom);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    JSValueConst obj, prop, receiver;+    JSAtom atom;+    JSValue ret;++    obj = argv[0];+    prop = argv[1];+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);+    if (argc > 2)+        receiver = argv[2];+    else+        receiver = obj;+    atom = JS_ValueToAtom(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL))+        return JS_EXCEPTION;+    ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);+    JS_FreeAtom(ctx, atom);+    return ret;+}++static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    JSValueConst obj, prop;+    JSAtom atom;+    int ret;++    obj = argv[0];+    prop = argv[1];+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);+    atom = JS_ValueToAtom(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL))+        return JS_EXCEPTION;+    ret = JS_HasProperty(ctx, obj, atom);+    JS_FreeAtom(ctx, atom);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    JSValueConst obj, prop, val, receiver;+    int ret;+    JSAtom atom;++    obj = argv[0];+    prop = argv[1];+    val = argv[2];+    if (argc > 3)+        receiver = argv[3];+    else+        receiver = obj;+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);+    atom = JS_ValueToAtom(ctx, prop);+    if (unlikely(atom == JS_ATOM_NULL))+        return JS_EXCEPTION;+    ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(obj), atom,+                                JS_DupValue(ctx, val), receiver, 0);+    JS_FreeAtom(ctx, atom);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    int ret;+    ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);+    if (ret < 0)+        return JS_EXCEPTION;+    else+        return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);+    return JS_GetOwnPropertyNames2(ctx, argv[0],+                                   JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,+                                   JS_ITERATOR_KIND_KEY);+}++static const JSCFunctionListEntry js_reflect_funcs[] = {+    JS_CFUNC_DEF("apply", 3, js_reflect_apply ),+    JS_CFUNC_DEF("construct", 2, js_reflect_construct ),+    JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),+    JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),+    JS_CFUNC_DEF("get", 2, js_reflect_get ),+    JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),+    JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),+    JS_CFUNC_DEF("has", 2, js_reflect_has ),+    JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),+    JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),+    JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),+    JS_CFUNC_DEF("set", 3, js_reflect_set ),+    JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),+};++static const JSCFunctionListEntry js_reflect_obj[] = {+    JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++/* Proxy */++static void js_proxy_finalizer(JSRuntime *rt, JSValue val)+{+    JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);+    if (s) {+        JS_FreeValueRT(rt, s->target);+        JS_FreeValueRT(rt, s->handler);+        js_free_rt(rt, s);+    }+}++static void js_proxy_mark(JSRuntime *rt, JSValueConst val,+                          JS_MarkFunc *mark_func)+{+    JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);+    if (s) {+        JS_MarkValue(rt, s->target, mark_func);+        JS_MarkValue(rt, s->handler, mark_func);+    }+}++static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)+{+    return JS_ThrowTypeError(ctx, "revoked proxy");+}++static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,+                                     JSValueConst obj, JSAtom name)+{+    JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);+    JSValue method;++    /* safer to test recursion in all proxy methods */+    if (js_check_stack_overflow(ctx->rt, 0)) {+        JS_ThrowStackOverflow(ctx);+        return NULL;+    }+    +    /* 's' should never be NULL */+    if (s->is_revoked) {+        JS_ThrowTypeErrorRevokedProxy(ctx);+        return NULL;+    }+    method = JS_GetProperty(ctx, s->handler, name);+    if (JS_IsException(method))+        return NULL;+    if (JS_IsNull(method))+        method = JS_UNDEFINED;+    *pmethod = method;+    return s;+}++static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)+{+    JSProxyData *s;+    JSValue method, ret, proto1;+    int res;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);+    if (!s)+        return JS_EXCEPTION;+    if (JS_IsUndefined(method))+        return JS_GetPrototype(ctx, s->target);+    ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+    if (JS_IsException(ret))+        return ret;+    if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&+        JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {+        goto fail;+    }+    res = JS_IsExtensible(ctx, s->target);+    if (res < 0) {+        JS_FreeValue(ctx, ret);+        return JS_EXCEPTION;+    }+    if (!res) {+        /* check invariant */+        proto1 = JS_GetPrototype(ctx, s->target);+        if (JS_IsException(proto1)) {+            JS_FreeValue(ctx, ret);+            return JS_EXCEPTION;+        }+        if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {+            JS_FreeValue(ctx, proto1);+        fail:+            JS_FreeValue(ctx, ret);+            return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");+        }+        JS_FreeValue(ctx, proto1);+    }+    return ret;+}++static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,+                                   JSValueConst proto_val, BOOL throw_flag)+{+    JSProxyData *s;+    JSValue method, ret, proto1;+    JSValueConst args[2];+    BOOL res;+    int res2;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);+    if (!s)+        return -1;+    if (JS_IsUndefined(method))+        return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);+    args[0] = s->target;+    args[1] = proto_val;+    ret = JS_CallFree(ctx, method, s->handler, 2, args);+    if (JS_IsException(ret))+        return -1;+    res = JS_ToBoolFree(ctx, ret);+    if (!res) {+        if (throw_flag) {+            JS_ThrowTypeError(ctx, "proxy: bad prototype");+            return -1;+        } else {+            return FALSE;+        }+    }+    res2 = JS_IsExtensible(ctx, s->target);+    if (res2 < 0)+        return -1;+    if (!res2) {+        proto1 = JS_GetPrototype(ctx, s->target);+        if (JS_IsException(proto1))+            return -1;+        if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {+            JS_FreeValue(ctx, proto1);+            JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");+            return -1;+        }+        JS_FreeValue(ctx, proto1);+    }+    return TRUE;+}++static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)+{+    JSProxyData *s;+    JSValue method, ret;+    BOOL res;+    int res2;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);+    if (!s)+        return -1;+    if (JS_IsUndefined(method))+        return JS_IsExtensible(ctx, s->target);+    ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+    if (JS_IsException(ret))+        return -1;+    res = JS_ToBoolFree(ctx, ret);+    res2 = JS_IsExtensible(ctx, s->target);+    if (res2 < 0)+        return res2;+    if (res != res2) {+        JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");+        return -1;+    }+    return res;+}++static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)+{+    JSProxyData *s;+    JSValue method, ret;+    BOOL res;+    int res2;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);+    if (!s)+        return -1;+    if (JS_IsUndefined(method))+        return JS_PreventExtensions(ctx, s->target);+    ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+    if (JS_IsException(ret))+        return -1;+    res = JS_ToBoolFree(ctx, ret);+    if (res) {+        res2 = JS_IsExtensible(ctx, s->target);+        if (res2 < 0)+            return res2;+        if (res2) {+            JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");+            return -1;+        }+    }+    return res;+}++static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)+{+    JSProxyData *s;+    JSValue method, ret1, atom_val;+    int ret, res;+    JSObject *p;+    JSValueConst args[2];+    BOOL res2;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);+    if (!s)+        return -1;+    if (JS_IsUndefined(method))+        return JS_HasProperty(ctx, s->target, atom);+    atom_val = JS_AtomToValue(ctx, atom);+    if (JS_IsException(atom_val)) {+        JS_FreeValue(ctx, method);+        return -1;+    }+    args[0] = s->target;+    args[1] = atom_val;+    ret1 = JS_CallFree(ctx, method, s->handler, 2, args);+    JS_FreeValue(ctx, atom_val);+    if (JS_IsException(ret1))+        return -1;+    ret = JS_ToBoolFree(ctx, ret1);+    if (!ret) {+        JSPropertyDescriptor desc;+        p = JS_VALUE_GET_OBJ(s->target);+        res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);+        if (res < 0)+            return -1;+        if (res) {+            res2 = !(desc.flags & JS_PROP_CONFIGURABLE);+            js_free_desc(ctx, &desc);+            if (res2 || !p->extensible) {+                JS_ThrowTypeError(ctx, "proxy: inconsistent has");+                return -1;+            }+        }+    }+    return ret;+}++static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,+                            JSValueConst receiver)+{+    JSProxyData *s;+    JSValue method, ret, atom_val;+    int res;+    JSValueConst args[3];+    JSPropertyDescriptor desc;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);+    if (!s)+        return JS_EXCEPTION;+    /* Note: recursion is possible thru the prototype of s->target */+    if (JS_IsUndefined(method))+        return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);+    atom_val = JS_AtomToValue(ctx, atom);+    if (JS_IsException(atom_val)) {+        JS_FreeValue(ctx, method);+        return JS_EXCEPTION;+    }+    args[0] = s->target;+    args[1] = atom_val;+    args[2] = receiver;+    ret = JS_CallFree(ctx, method, s->handler, 3, args);+    JS_FreeValue(ctx, atom_val);+    if (JS_IsException(ret))+        return JS_EXCEPTION;+    res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);+    if (res < 0)+        return JS_EXCEPTION;+    if (res) {+        if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {+            if (!js_same_value(ctx, desc.value, ret)) {+                goto fail;+            }+        } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {+            if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {+            fail:+                js_free_desc(ctx, &desc);+                JS_FreeValue(ctx, ret);+                return JS_ThrowTypeError(ctx, "proxy: inconsistent get");+            }+        }+        js_free_desc(ctx, &desc);+    }+    return ret;+}++static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,+                        JSValueConst value, JSValueConst receiver, int flags)+{+    JSProxyData *s;+    JSValue method, ret1, atom_val;+    int ret, res;+    JSValueConst args[4];++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);+    if (!s)+        return -1;+    if (JS_IsUndefined(method)) {+        return JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(s->target), atom,+                                     JS_DupValue(ctx, value), receiver,+                                     flags);+    }+    atom_val = JS_AtomToValue(ctx, atom);+    if (JS_IsException(atom_val)) {+        JS_FreeValue(ctx, method);+        return -1;+    }+    args[0] = s->target;+    args[1] = atom_val;+    args[2] = value;+    args[3] = receiver;+    ret1 = JS_CallFree(ctx, method, s->handler, 4, args);+    JS_FreeValue(ctx, atom_val);+    if (JS_IsException(ret1))+        return -1;+    ret = JS_ToBoolFree(ctx, ret1);+    if (ret) {+        JSPropertyDescriptor desc;+        res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);+        if (res < 0)+            return -1;+        if (res) {+            if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {+                if (!js_same_value(ctx, desc.value, value)) {+                    goto fail;+                }+            } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {+                fail:+                    js_free_desc(ctx, &desc);+                    JS_ThrowTypeError(ctx, "proxy: inconsistent set");+                    return -1;+            }+            js_free_desc(ctx, &desc);+        }+    } else {+        if ((flags & JS_PROP_THROW) ||+            ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+            JS_ThrowTypeError(ctx, "proxy: cannot set property");+            return -1;+        }+    }+    return ret;+}++static JSValue js_create_desc(JSContext *ctx, JSValueConst val,+                              JSValueConst getter, JSValueConst setter,+                              int flags)+{+    JSValue ret;+    ret = JS_NewObject(ctx);+    if (JS_IsException(ret))+        return ret;+    if (flags & JS_PROP_HAS_GET) {+        JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),+                               JS_PROP_C_W_E);+    }+    if (flags & JS_PROP_HAS_SET) {+        JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),+                               JS_PROP_C_W_E);+    }+    if (flags & JS_PROP_HAS_VALUE) {+        JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),+                               JS_PROP_C_W_E);+    }+    if (flags & JS_PROP_HAS_WRITABLE) {+        JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,+                               JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),+                               JS_PROP_C_W_E);+    }+    if (flags & JS_PROP_HAS_ENUMERABLE) {+        JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,+                               JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),+                               JS_PROP_C_W_E);+    }+    if (flags & JS_PROP_HAS_CONFIGURABLE) {+        JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,+                               JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),+                               JS_PROP_C_W_E);+    }+    return ret;+}++static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,+                                     JSValueConst obj, JSAtom prop)+{+    JSProxyData *s;+    JSValue method, trap_result_obj, prop_val;+    int res, target_desc_ret, ret;+    JSObject *p;+    JSValueConst args[2];+    JSPropertyDescriptor result_desc, target_desc;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);+    if (!s)+        return -1;+    p = JS_VALUE_GET_OBJ(s->target);+    if (JS_IsUndefined(method)) {+        return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);+    }+    prop_val = JS_AtomToValue(ctx, prop);+    if (JS_IsException(prop_val)) {+        JS_FreeValue(ctx, method);+        return -1;+    }+    args[0] = s->target;+    args[1] = prop_val;+    trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);+    JS_FreeValue(ctx, prop_val);+    if (JS_IsException(trap_result_obj))+        return -1;+    if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {+        JS_FreeValue(ctx, trap_result_obj);+        goto fail;+    }+    target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);+    if (target_desc_ret < 0) {+        JS_FreeValue(ctx, trap_result_obj);+        return -1;+    }+    if (target_desc_ret)+        js_free_desc(ctx, &target_desc);+    if (JS_IsUndefined(trap_result_obj)) {+        if (target_desc_ret) {+            if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)+                goto fail;+        }+        ret = FALSE;+    } else {+        int flags1, extensible_target;+        extensible_target = JS_IsExtensible(ctx, s->target);+        if (extensible_target < 0) {+            JS_FreeValue(ctx, trap_result_obj);+            return -1;+        }+        res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);+        JS_FreeValue(ctx, trap_result_obj);+        if (res < 0)+            return -1;+        +        if (target_desc_ret) {+            /* convert result_desc.flags to defineProperty flags */+            flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;+            if (result_desc.flags & JS_PROP_GETSET)+                flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;+            else+                flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;+            /* XXX: not complete check: need to compare value &+               getter/setter as in defineproperty */+            if (!check_define_prop_flags(target_desc.flags, flags1))+                goto fail1;+        } else {+            if (!extensible_target)+                goto fail1;+        }+        if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {+            if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))+                goto fail1;+            if ((result_desc.flags &+                 (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&+                target_desc_ret &&+                (target_desc.flags & JS_PROP_WRITABLE) != 0) {+                /* proxy-missing-checks */+            fail1:+                js_free_desc(ctx, &result_desc);+            fail:+                JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");+                return -1;+            }+        }+        ret = TRUE;+        if (pdesc) {+            *pdesc = result_desc;+        } else {+            js_free_desc(ctx, &result_desc);+        }+    }+    return ret;+}++static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,+                                        JSAtom prop, JSValueConst val,+                                        JSValueConst getter, JSValueConst setter,+                                        int flags)+{+    JSProxyData *s;+    JSValue method, ret1, prop_val, desc_val;+    int res, ret;+    JSObject *p;+    JSValueConst args[3];+    JSPropertyDescriptor desc;+    BOOL setting_not_configurable;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);+    if (!s)+        return -1;+    if (JS_IsUndefined(method)) {+        return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);+    }+    prop_val = JS_AtomToValue(ctx, prop);+    if (JS_IsException(prop_val)) {+        JS_FreeValue(ctx, method);+        return -1;+    }+    desc_val = js_create_desc(ctx, val, getter, setter, flags);+    if (JS_IsException(desc_val)) {+        JS_FreeValue(ctx, prop_val);+        JS_FreeValue(ctx, method);+        return -1;+    }+    args[0] = s->target;+    args[1] = prop_val;+    args[2] = desc_val;+    ret1 = JS_CallFree(ctx, method, s->handler, 3, args);+    JS_FreeValue(ctx, prop_val);+    JS_FreeValue(ctx, desc_val);+    if (JS_IsException(ret1))+        return -1;+    ret = JS_ToBoolFree(ctx, ret1);+    if (!ret) {+        if (flags & JS_PROP_THROW) {+            JS_ThrowTypeError(ctx, "proxy: defineProperty exception");+            return -1;+        } else {+            return 0;+        }+    }+    p = JS_VALUE_GET_OBJ(s->target);+    res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+    if (res < 0)+        return -1;+    setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |+                                          JS_PROP_CONFIGURABLE)) ==+                                JS_PROP_HAS_CONFIGURABLE);+    if (!res) {+        if (!p->extensible || setting_not_configurable)+            goto fail;+    } else {+        if (!check_define_prop_flags(desc.flags, flags) ||+            ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {+            goto fail1;+        }+        if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+            if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==+                JS_PROP_GETSET) {+                if ((flags & JS_PROP_HAS_GET) &&+                    !js_same_value(ctx, getter, desc.getter)) {+                    goto fail1;+                }+                if ((flags & JS_PROP_HAS_SET) &&+                    !js_same_value(ctx, setter, desc.setter)) {+                    goto fail1;+                }+            }+        } else if (flags & JS_PROP_HAS_VALUE) {+            if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==+                JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {+                /* missing-proxy-check feature */+                goto fail1;+            } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&+                !js_same_value(ctx, val, desc.value)) {+                goto fail1;+            }+        }+        if (flags & JS_PROP_HAS_WRITABLE) {+            if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |+                               JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {+                /* proxy-missing-checks */+            fail1:+                js_free_desc(ctx, &desc);+            fail:+                JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");+                return -1;+            }+        }+        js_free_desc(ctx, &desc);+    }+    return 1;+}++static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,+                                    JSAtom atom)+{+    JSProxyData *s;+    JSValue method, ret, atom_val;+    int res, res2, is_extensible;+    JSValueConst args[2];++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);+    if (!s)+        return -1;+    if (JS_IsUndefined(method)) {+        return JS_DeleteProperty(ctx, s->target, atom, 0);+    }+    atom_val = JS_AtomToValue(ctx, atom);;+    if (JS_IsException(atom_val)) {+        JS_FreeValue(ctx, method);+        return -1;+    }+    args[0] = s->target;+    args[1] = atom_val;+    ret = JS_CallFree(ctx, method, s->handler, 2, args);+    JS_FreeValue(ctx, atom_val);+    if (JS_IsException(ret))+        return -1;+    res = JS_ToBoolFree(ctx, ret);+    if (res) {+        JSPropertyDescriptor desc;+        res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);+        if (res2 < 0)+            return -1;+        if (res2) {+            if (!(desc.flags & JS_PROP_CONFIGURABLE))+                goto fail;+            is_extensible = JS_IsExtensible(ctx, s->target);+            if (is_extensible < 0)+                goto fail1;+            if (!is_extensible) {+                /* proxy-missing-checks */+            fail:+                JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");+            fail1:+                js_free_desc(ctx, &desc);+                return -1;+            }+            js_free_desc(ctx, &desc);+        }+    }+    return res;+}++/* return the index of the property or -1 if not found */+static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)+{+    int i;+    for(i = 0; i < n; i++) {+        if (tab[i].atom == atom)+            return i;+    }+    return -1;+}++static int js_proxy_get_own_property_names(JSContext *ctx,+                                           JSPropertyEnum **ptab,+                                           uint32_t *plen,+                                           JSValueConst obj)+{+    JSProxyData *s;+    JSValue method, prop_array, val;+    uint32_t len, i, len2;+    JSPropertyEnum *tab, *tab2;+    JSAtom atom;+    JSPropertyDescriptor desc;+    int res, is_extensible, idx;++    s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);+    if (!s)+        return -1;+    if (JS_IsUndefined(method)) {+        return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,+                                      JS_VALUE_GET_OBJ(s->target),+                                      JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);+    }+    prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+    if (JS_IsException(prop_array))+        return -1;+    tab = NULL;+    len = 0;+    tab2 = NULL;+    len2 = 0;+    if (js_get_length32(ctx, &len, prop_array))+        goto fail;+    if (len > 0) {+        tab = js_mallocz(ctx, sizeof(tab[0]) * len);+        if (!tab)+            goto fail;+    }+    for(i = 0; i < len; i++) {+        val = JS_GetPropertyUint32(ctx, prop_array, i);+        if (JS_IsException(val))+            goto fail;+        if (!JS_IsString(val) && !JS_IsSymbol(val)) {+            JS_FreeValue(ctx, val);+            JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");+            goto fail;+        }+        atom = JS_ValueToAtom(ctx, val);+        JS_FreeValue(ctx, val);+        if (atom == JS_ATOM_NULL)+            goto fail;+        tab[i].atom = atom;+        tab[i].is_enumerable = FALSE; /* XXX: redundant? */+    }++    /* check duplicate properties (XXX: inefficient, could store the+     * properties an a temporary object to use the hash) */+    for(i = 1; i < len; i++) {+        if (find_prop_key(tab, i, tab[i].atom) >= 0) {+            JS_ThrowTypeError(ctx, "proxy: duplicate property");+            goto fail;+        }+    }++    is_extensible = JS_IsExtensible(ctx, s->target);+    if (is_extensible < 0)+        goto fail;++    /* check if there are non configurable properties */+    if (s->is_revoked) {+        JS_ThrowTypeErrorRevokedProxy(ctx);+        goto fail;+    }+    if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),+                               JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))+        goto fail;+    for(i = 0; i < len2; i++) {+        if (s->is_revoked) {+            JS_ThrowTypeErrorRevokedProxy(ctx);+            goto fail;+        }+        res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),+                                tab2[i].atom);+        if (res < 0)+            goto fail;+        if (res) {  /* safety, property should be found */+            js_free_desc(ctx, &desc);+            if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {+                idx = find_prop_key(tab, len, tab2[i].atom);+                if (idx < 0) {+                    JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");+                    goto fail;+                }+                /* mark the property as found */+                if (!is_extensible)+                    tab[idx].is_enumerable = TRUE;+            }+        }+    }+    if (!is_extensible) {+        /* check that all property in 'tab' were checked */+        for(i = 0; i < len; i++) {+            if (!tab[i].is_enumerable) {+                JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");+                goto fail;+            }+        }+    }++    js_free_prop_enum(ctx, tab2, len2);+    JS_FreeValue(ctx, prop_array);+    *ptab = tab;+    *plen = len;+    return 0;+ fail:+    js_free_prop_enum(ctx, tab2, len2);+    js_free_prop_enum(ctx, tab, len);+    JS_FreeValue(ctx, prop_array);+    return -1;+}++static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,+                                         JSValueConst new_target,+                                         int argc, JSValueConst *argv)+{+    JSProxyData *s;+    JSValue method, arg_array, ret;+    JSValueConst args[3];++    s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);+    if (!s)+        return JS_EXCEPTION;+    if (!JS_IsConstructor(ctx, s->target))+        return JS_ThrowTypeError(ctx, "not a constructor");+    if (JS_IsUndefined(method))+        return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);+    arg_array = js_create_array(ctx, argc, argv);+    if (JS_IsException(arg_array)) {+        ret = JS_EXCEPTION;+        goto fail;+    }+    args[0] = s->target;+    args[1] = arg_array;+    args[2] = new_target;+    ret = JS_Call(ctx, method, s->handler, 3, args);+    if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {+        JS_FreeValue(ctx, ret);+        ret = JS_ThrowTypeErrorNotAnObject(ctx);+    }+ fail:+    JS_FreeValue(ctx, method);+    JS_FreeValue(ctx, arg_array);+    return ret;+}++static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,+                             JSValueConst this_obj,+                             int argc, JSValueConst *argv, int flags)+{+    JSProxyData *s;+    JSValue method, arg_array, ret;+    JSValueConst args[3];++    if (flags & JS_CALL_FLAG_CONSTRUCTOR)+        return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);+    +    s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);+    if (!s)+        return JS_EXCEPTION;+    if (!s->is_func) {+        JS_FreeValue(ctx, method);+        return JS_ThrowTypeError(ctx, "not a function");+    }+    if (JS_IsUndefined(method))+        return JS_Call(ctx, s->target, this_obj, argc, argv);+    arg_array = js_create_array(ctx, argc, argv);+    if (JS_IsException(arg_array)) {+        ret = JS_EXCEPTION;+        goto fail;+    }+    args[0] = s->target;+    args[1] = this_obj;+    args[2] = arg_array;+    ret = JS_Call(ctx, method, s->handler, 3, args);+ fail:+    JS_FreeValue(ctx, method);+    JS_FreeValue(ctx, arg_array);+    return ret;+}++static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)+{+    JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);+    if (!s)+        return FALSE;+    if (s->is_revoked) {+        JS_ThrowTypeErrorRevokedProxy(ctx);+        return -1;+    }+    return JS_IsArray(ctx, s->target);+}++static const JSClassExoticMethods js_proxy_exotic_methods = {+    .get_own_property = js_proxy_get_own_property,+    .define_own_property = js_proxy_define_own_property,+    .delete_property = js_proxy_delete_property,+    .get_own_property_names = js_proxy_get_own_property_names,+    .has_property = js_proxy_has,+    .get_property = js_proxy_get,+    .set_property = js_proxy_set,+};++static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValueConst target, handler;+    JSValue obj;+    JSProxyData *s;++    target = argv[0];+    handler = argv[1];+    if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||+        JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)+        return JS_ThrowTypeErrorNotAnObject(ctx);++    obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);+    if (JS_IsException(obj))+        return obj;+    s = js_malloc(ctx, sizeof(JSProxyData));+    if (!s) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    s->target = JS_DupValue(ctx, target);+    s->handler = JS_DupValue(ctx, handler);+    s->is_func = JS_IsFunction(ctx, target);+    s->is_revoked = FALSE;+    JS_SetOpaque(obj, s);+    JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));+    return obj;+}++static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv, int magic,+                               JSValue *func_data)+{+    JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);+    if (s) {+        /* We do not free the handler and target in case they are+           referenced as constants in the C call stack */+        s->is_revoked = TRUE;+        JS_FreeValue(ctx, func_data[0]);+        func_data[0] = JS_NULL;+    }+    return JS_UNDEFINED;+}++static JSValue js_proxy_revoke_constructor(JSContext *ctx,+                                           JSValueConst proxy_obj)+{+    return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);+}++static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;++    proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);+    if (JS_IsException(proxy_obj))+        goto fail;+    revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);+    if (JS_IsException(revoke_obj))+        goto fail;+    obj = JS_NewObject(ctx);+    if (JS_IsException(obj))+        goto fail;+    // XXX: exceptions?+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);+    return obj;+ fail:+    JS_FreeValue(ctx, proxy_obj);+    JS_FreeValue(ctx, revoke_obj);+    return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_proxy_funcs[] = {+    JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),+};++static const JSClassShortDef js_proxy_class_def[] = {+    { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */+};++void JS_AddIntrinsicProxy(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    JSValue obj1;++    if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {+        init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,+                         countof(js_proxy_class_def));+        rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;+        rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;+    }++    obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,+                            JS_CFUNC_constructor, 0);+    JS_SetConstructorBit(ctx, obj1, TRUE);+    JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,+                               countof(js_proxy_funcs));+    JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",+                              obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+}++/* Symbol */++static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,+                                     int argc, JSValueConst *argv)+{+    JSValue str;+    JSString *p;++    if (!JS_IsUndefined(new_target))+        return JS_ThrowTypeError(ctx, "not a constructor");+    if (argc == 0 || JS_IsUndefined(argv[0])) {+        p = NULL;+    } else {+        str = JS_ToString(ctx, argv[0]);+        if (JS_IsException(str))+            return JS_EXCEPTION;+        p = JS_VALUE_GET_STRING(str);+    }+    return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);+}++static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_SYMBOL) {+            if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)+                return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a symbol");+}++static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValue val, ret;+    val = js_thisSymbolValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    /* XXX: use JS_ToStringInternal() with a flags */+    ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);+    JS_FreeValue(ctx, val);+    return ret;+}++static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    return js_thisSymbolValue(ctx, this_val);+}++static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)+{+    JSValue val, ret;+    JSAtomStruct *p;++    val = js_thisSymbolValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    p = JS_VALUE_GET_PTR(val);+    if (p->len == 0 && p->is_wide_char != 0) {+        ret = JS_UNDEFINED;+    } else {+        ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));+    }+    JS_FreeValue(ctx, val);+    return ret;+}++static const JSCFunctionListEntry js_symbol_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_symbol_toString ),+    JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),+    // XXX: should have writable: false+    JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),+    JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),+};++static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    JSValue str;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        return JS_EXCEPTION;+    return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);+}++static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSAtomStruct *p;++    if (!JS_IsSymbol(argv[0]))+        return JS_ThrowTypeError(ctx, "not a symbol");+    p = JS_VALUE_GET_PTR(argv[0]);+    if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)+        return JS_UNDEFINED;+    return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+}++static const JSCFunctionListEntry js_symbol_funcs[] = {+    JS_CFUNC_DEF("for", 1, js_symbol_for ),+    JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),+};++/* Set/Map/WeakSet/WeakMap */++typedef struct JSMapRecord {+    int ref_count; /* used during enumeration to avoid freeing the record */+    BOOL empty; /* TRUE if the record is deleted */+    struct JSMapState *map;+    struct JSMapRecord *next_weak_ref;+    struct list_head link;+    struct list_head hash_link;+    JSValue key;+    JSValue value;+} JSMapRecord;++typedef struct JSMapState {+    BOOL is_weak; /* TRUE if WeakSet/WeakMap */+    struct list_head records; /* list of JSMapRecord.link */+    uint32_t record_count;+    struct list_head *hash_table;+    uint32_t hash_size; /* must be a power of two */+    uint32_t record_count_threshold; /* count at which a hash table+                                        resize is needed */+} JSMapState;++#define MAGIC_SET (1 << 0)+#define MAGIC_WEAK (1 << 1)++static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,+                                  int argc, JSValueConst *argv, int magic)+{+    JSMapState *s;+    JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;+    JSValueConst arr;+    BOOL is_set, is_weak;++    is_set = magic & MAGIC_SET;+    is_weak = ((magic & MAGIC_WEAK) != 0);+    obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    s = js_mallocz(ctx, sizeof(*s));+    if (!s)+        goto fail;+    init_list_head(&s->records);+    s->is_weak = is_weak;+    JS_SetOpaque(obj, s);+    s->hash_size = 1;+    s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);+    if (!s->hash_table)+        goto fail;+    init_list_head(&s->hash_table[0]);+    s->record_count_threshold = 4;++    arr = JS_UNDEFINED;+    if (argc > 0)+        arr = argv[0];+    if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {+        JSValue item, ret;+        BOOL done;++        adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);+        if (JS_IsException(adder))+            goto fail;+        if (!JS_IsFunction(ctx, adder)) {+            JS_ThrowTypeError(ctx, "set/add is not a function");+            goto fail;+        }++        iter = JS_GetIterator(ctx, arr, FALSE);+        if (JS_IsException(iter))+            goto fail;+        next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+        if (JS_IsException(next_method))+            goto fail;++        for(;;) {+            item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+            if (JS_IsException(item))+                goto fail;+            if (done) {+                JS_FreeValue(ctx, item);+                break;+            }+            if (is_set) {+                ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);+                if (JS_IsException(ret)) {+                    JS_FreeValue(ctx, item);+                    goto fail;+                }+            } else {+                JSValue key, value;+                JSValueConst args[2];+                key = JS_UNDEFINED;+                value = JS_UNDEFINED;+                if (!JS_IsObject(item)) {+                    JS_ThrowTypeErrorNotAnObject(ctx);+                    goto fail1;+                }+                key = JS_GetPropertyUint32(ctx, item, 0);+                if (JS_IsException(key))+                    goto fail1;+                value = JS_GetPropertyUint32(ctx, item, 1);+                if (JS_IsException(value))+                    goto fail1;+                args[0] = key;+                args[1] = value;+                ret = JS_Call(ctx, adder, obj, 2, args);+                if (JS_IsException(ret)) {+                fail1:+                    JS_FreeValue(ctx, item);+                    JS_FreeValue(ctx, key);+                    JS_FreeValue(ctx, value);+                    goto fail;+                }+                JS_FreeValue(ctx, key);+                JS_FreeValue(ctx, value);+            }+            JS_FreeValue(ctx, ret);+            JS_FreeValue(ctx, item);+        }+        JS_FreeValue(ctx, next_method);+        JS_FreeValue(ctx, iter);+        JS_FreeValue(ctx, adder);+    }+    return obj;+ fail:+    if (JS_IsObject(iter)) {+        /* close the iterator object, preserving pending exception */+        JS_IteratorClose(ctx, iter, TRUE);+    }+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    JS_FreeValue(ctx, adder);+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++/* XXX: could normalize strings to speed up comparison */+static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)+{+    uint32_t tag = JS_VALUE_GET_TAG(key);+    /* convert -0.0 to +0.0 */+    if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {+        key = JS_NewInt32(ctx, 0);+    }+    return key;+}++/* XXX: better hash ? */+static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)+{+    uint32_t tag = JS_VALUE_GET_NORM_TAG(key);+    uint32_t h;+    double d;+    JSFloat64Union u;++    switch(tag) {+    case JS_TAG_BOOL:+        h = JS_VALUE_GET_INT(key);+        break;+    case JS_TAG_STRING:+        h = hash_string(JS_VALUE_GET_STRING(key), 0);+        break;+    case JS_TAG_OBJECT:+    case JS_TAG_SYMBOL:+        h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;+        break;+    case JS_TAG_INT:+        d = JS_VALUE_GET_INT(key) * 3163;+        goto hash_float64;+    case JS_TAG_FLOAT64:+        d = JS_VALUE_GET_FLOAT64(key);+        /* normalize the NaN */+        if (isnan(d))+            d = JS_FLOAT64_NAN;+    hash_float64:+        u.d = d;+        h = (u.u32[0] ^ u.u32[1]) * 3163;+        break;+    default:+        h = 0; /* XXX: bignum support */+        break;+    }+    h ^= tag;+    return h;+}++static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,+                                    JSValueConst key)+{+    struct list_head *el;+    JSMapRecord *mr;+    uint32_t h;+    h = map_hash_key(ctx, key) & (s->hash_size - 1);+    list_for_each(el, &s->hash_table[h]) {+        mr = list_entry(el, JSMapRecord, hash_link);+        if (js_same_value_zero(ctx, mr->key, key))+            return mr;+    }+    return NULL;+}++static void map_hash_resize(JSContext *ctx, JSMapState *s)+{+    uint32_t new_hash_size, i, h;+    size_t slack;+    struct list_head *new_hash_table, *el;+    JSMapRecord *mr;++    /* XXX: no reporting of memory allocation failure */+    if (s->hash_size == 1)+        new_hash_size = 4;+    else+        new_hash_size = s->hash_size * 2;+    new_hash_table = js_realloc2(ctx, s->hash_table,+                                 sizeof(new_hash_table[0]) * new_hash_size, &slack);+    if (!new_hash_table)+        return;+    new_hash_size += slack / sizeof(*new_hash_table);++    for(i = 0; i < new_hash_size; i++)+        init_list_head(&new_hash_table[i]);++    list_for_each(el, &s->records) {+        mr = list_entry(el, JSMapRecord, link);+        if (!mr->empty) {+            h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);+            list_add_tail(&mr->hash_link, &new_hash_table[h]);+        }+    }+    s->hash_table = new_hash_table;+    s->hash_size = new_hash_size;+    s->record_count_threshold = new_hash_size * 2;+}++static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,+                                   JSValueConst key)+{+    uint32_t h;+    JSMapRecord *mr;++    mr = js_malloc(ctx, sizeof(*mr));+    if (!mr)+        return NULL;+    mr->ref_count = 1;+    mr->map = s;+    mr->empty = FALSE;+    if (s->is_weak) {+        JSObject *p = JS_VALUE_GET_OBJ(key);+        /* Add the weak reference */+        mr->next_weak_ref = p->first_weak_ref;+        p->first_weak_ref = mr;+    } else {+        JS_DupValue(ctx, key);+    }+    mr->key = (JSValue)key;+    h = map_hash_key(ctx, key) & (s->hash_size - 1);+    list_add_tail(&mr->hash_link, &s->hash_table[h]);+    list_add_tail(&mr->link, &s->records);+    s->record_count++;+    if (s->record_count >= s->record_count_threshold) {+        map_hash_resize(ctx, s);+    }+    return mr;+}++/* Remove the weak reference from the object weak+   reference list. we don't use a doubly linked list to+   save space, assuming a given object has few weak+       references to it */+static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)+{+    JSMapRecord **pmr, *mr1;+    JSObject *p;++    p = JS_VALUE_GET_OBJ(mr->key);+    pmr = &p->first_weak_ref;+    for(;;) {+        mr1 = *pmr;+        assert(mr1 != NULL);+        if (mr1 == mr)+            break;+        pmr = &mr1->next_weak_ref;+    }+    *pmr = mr1->next_weak_ref;+}++static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)+{+    if (mr->empty)+        return;+    list_del(&mr->hash_link);+    if (s->is_weak) {+        delete_weak_ref(rt, mr);+    } else {+        JS_FreeValueRT(rt, mr->key);+    }+    JS_FreeValueRT(rt, mr->value);+    if (--mr->ref_count == 0) {+        list_del(&mr->link);+        js_free_rt(rt, mr);+    } else {+        /* keep a zombie record for iterators */+        mr->empty = TRUE;+        mr->key = JS_UNDEFINED;+        mr->value = JS_UNDEFINED;+    }+    s->record_count--;+}++static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)+{+    if (--mr->ref_count == 0) {+        /* the record can be safely removed */+        assert(mr->empty);+        list_del(&mr->link);+        js_free_rt(rt, mr);+    }+}++static void reset_weak_ref(JSRuntime *rt, JSObject *p)+{+    JSMapRecord *mr, *mr_next;+    JSMapState *s;+    +    /* first pass to remove the records from the WeakMap/WeakSet+       lists */+    for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {+        s = mr->map;+        assert(s->is_weak);+        assert(!mr->empty); /* no iterator on WeakMap/WeakSet */+        list_del(&mr->hash_link);+        list_del(&mr->link);+    }+    +    /* second pass to free the values to avoid modifying the weak+       reference list while traversing it. */+    for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {+        mr_next = mr->next_weak_ref;+        JS_FreeValueRT(rt, mr->value);+        js_free_rt(rt, mr);+    }++    p->first_weak_ref = NULL; /* fail safe */+}++static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    JSMapRecord *mr;+    JSValueConst key, value;++    if (!s)+        return JS_EXCEPTION;+    key = map_normalize_key(ctx, argv[0]);+    if (s->is_weak && !JS_IsObject(key))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    if (magic & MAGIC_SET)+        value = JS_UNDEFINED;+    else+        value = argv[1];+    mr = map_find_record(ctx, s, key);+    if (mr) {+        JS_FreeValue(ctx, mr->value);+    } else {+        mr = map_add_record(ctx, s, key);+        if (!mr)+            return JS_EXCEPTION;+    }+    mr->value = JS_DupValue(ctx, value);+    return JS_DupValue(ctx, this_val);+}++static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    JSMapRecord *mr;+    JSValueConst key;++    if (!s)+        return JS_EXCEPTION;+    key = map_normalize_key(ctx, argv[0]);+    mr = map_find_record(ctx, s, key);+    if (!mr)+        return JS_UNDEFINED;+    else+        return JS_DupValue(ctx, mr->value);+}++static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,+                          int argc, JSValueConst *argv, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    JSMapRecord *mr;+    JSValueConst key;++    if (!s)+        return JS_EXCEPTION;+    key = map_normalize_key(ctx, argv[0]);+    mr = map_find_record(ctx, s, key);+    return JS_NewBool(ctx, (mr != NULL));+}++static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    JSMapRecord *mr;+    JSValueConst key;++    if (!s)+        return JS_EXCEPTION;+    key = map_normalize_key(ctx, argv[0]);+    mr = map_find_record(ctx, s, key);+    if (!mr)+        return JS_FALSE;+    map_delete_record(ctx->rt, s, mr);+    return JS_TRUE;+}++static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    struct list_head *el, *el1;+    JSMapRecord *mr;++    if (!s)+        return JS_EXCEPTION;+    list_for_each_safe(el, el1, &s->records) {+        mr = list_entry(el, JSMapRecord, link);+        map_delete_record(ctx->rt, s, mr);+    }+    return JS_UNDEFINED;+}++static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    if (!s)+        return JS_EXCEPTION;+    return JS_NewUint32(ctx, s->record_count);+}++static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int magic)+{+    JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    JSValueConst func, this_arg;+    JSValue ret, args[3];+    struct list_head *el;+    JSMapRecord *mr;++    if (!s)+        return JS_EXCEPTION;+    func = argv[0];+    if (argc > 1)+        this_arg = argv[1];+    else+        this_arg = JS_UNDEFINED;+    if (check_function(ctx, func))+        return JS_EXCEPTION;+    /* Note: the list can be modified while traversing it, but the+       current element is locked */+    el = s->records.next;+    while (el != &s->records) {+        mr = list_entry(el, JSMapRecord, link);+        if (!mr->empty) {+            mr->ref_count++;+            /* must duplicate in case the record is deleted */+            args[1] = JS_DupValue(ctx, mr->key);+            if (magic)+                args[0] = args[1];+            else+                args[0] = JS_DupValue(ctx, mr->value);+            args[2] = (JSValue)this_val;+            ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);+            JS_FreeValue(ctx, args[0]);+            if (!magic)+                JS_FreeValue(ctx, args[1]);+            el = el->next;+            map_decref_record(ctx->rt, mr);+            if (JS_IsException(ret))+                return ret;+            JS_FreeValue(ctx, ret);+        } else {+            el = el->next;+        }+    }+    return JS_UNDEFINED;+}++static void js_map_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p;+    JSMapState *s;+    struct list_head *el, *el1;+    JSMapRecord *mr;++    p = JS_VALUE_GET_OBJ(val);+    s = p->u.map_state;+    if (s) {+        /* if the object is deleted we are sure that no iterator is+           using it */+        list_for_each_safe(el, el1, &s->records) {+            mr = list_entry(el, JSMapRecord, link);+            if (!mr->empty) {+                if (s->is_weak)+                    delete_weak_ref(rt, mr);+                else+                    JS_FreeValueRT(rt, mr->key);+                JS_FreeValueRT(rt, mr->value);+            }+            js_free_rt(rt, mr);+        }+        js_free_rt(rt, s->hash_table);+        js_free_rt(rt, s);+    }+}++static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSMapState *s;+    struct list_head *el;+    JSMapRecord *mr;++    s = p->u.map_state;+    if (s) {+        list_for_each(el, &s->records) {+            mr = list_entry(el, JSMapRecord, link);+            if (!s->is_weak)+                JS_MarkValue(rt, mr->key, mark_func);+            JS_MarkValue(rt, mr->value, mark_func);+        }+    }+}++/* Map Iterator */++typedef struct JSMapIteratorData {+    JSValue obj;+    JSIteratorKindEnum kind;+    JSMapRecord *cur_record;+} JSMapIteratorData;++static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p;+    JSMapIteratorData *it;++    p = JS_VALUE_GET_OBJ(val);+    it = p->u.map_iterator_data;+    if (it) {+        /* During the GC sweep phase the Map finalizer may be+           called before the Map iterator finalizer */+        if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {+            map_decref_record(rt, it->cur_record);+        }+        JS_FreeValueRT(rt, it->obj);+        js_free_rt(rt, it);+    }+}++static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,+                                 JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSMapIteratorData *it;+    it = p->u.map_iterator_data;+    if (it) {+        /* the record is already marked by the object */+        JS_MarkValue(rt, it->obj, mark_func);+    }+}++static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv, int magic)+{+    JSIteratorKindEnum kind;+    JSMapState *s;+    JSMapIteratorData *it;+    JSValue enum_obj;++    kind = magic >> 2;+    magic &= 3;+    s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+    if (!s)+        return JS_EXCEPTION;+    enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);+    if (JS_IsException(enum_obj))+        goto fail;+    it = js_malloc(ctx, sizeof(*it));+    if (!it) {+        JS_FreeValue(ctx, enum_obj);+        goto fail;+    }+    it->obj = JS_DupValue(ctx, this_val);+    it->kind = kind;+    it->cur_record = NULL;+    JS_SetOpaque(enum_obj, it);+    return enum_obj;+ fail:+    return JS_EXCEPTION;+}++static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv,+                                    BOOL *pdone, int magic)+{+    JSMapIteratorData *it;+    JSMapState *s;+    JSMapRecord *mr;+    struct list_head *el;++    it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);+    if (!it) {+        *pdone = FALSE;+        return JS_EXCEPTION;+    }+    if (JS_IsUndefined(it->obj))+        goto done;+    s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);+    assert(s != NULL);+    if (!it->cur_record) {+        el = s->records.next;+    } else {+        mr = it->cur_record;+        el = mr->link.next;+        map_decref_record(ctx->rt, mr); /* the record can be freed here */+    }+    for(;;) {+        if (el == &s->records) {+            /* no more record  */+            it->cur_record = NULL;+            JS_FreeValue(ctx, it->obj);+            it->obj = JS_UNDEFINED;+        done:+            /* end of enumeration */+            *pdone = TRUE;+            return JS_UNDEFINED;+        }+        mr = list_entry(el, JSMapRecord, link);+        if (!mr->empty)+            break;+        /* get the next record */+        el = mr->link.next;+    }++    /* lock the record so that it won't be freed */+    mr->ref_count++;+    it->cur_record = mr;+    *pdone = FALSE;++    if (it->kind == JS_ITERATOR_KIND_KEY) {+        return JS_DupValue(ctx, mr->key);+    } else {+        JSValueConst args[2];+        args[0] = mr->key;+        if (magic)+            args[1] = mr->key;+        else+            args[1] = mr->value;+        if (it->kind == JS_ITERATOR_KIND_VALUE) {+            return JS_DupValue(ctx, args[1]);+        } else {+            return js_create_array(ctx, 2, args);+        }+    }+}++static const JSCFunctionListEntry js_map_funcs[] = {+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static const JSCFunctionListEntry js_map_proto_funcs[] = {+    JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),+    JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),+    JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),+    JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),+    JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),+    JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),+    JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),+    JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),+    JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),+    JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),+    JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {+    JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_set_proto_funcs[] = {+    JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),+    JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),+    JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),+    JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),+    JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),+    JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),+    JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),+    JS_ALIAS_DEF("keys", "values" ),+    JS_ALIAS_DEF("[Symbol.iterator]", "values" ),+    JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {+    JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {+    JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),+    JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),+    JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),+    JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {+    JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),+    JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),+    JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {+    js_map_proto_funcs,+    js_set_proto_funcs,+    js_weak_map_proto_funcs,+    js_weak_set_proto_funcs,+    js_map_iterator_proto_funcs,+    js_set_iterator_proto_funcs,+};++static const uint8_t js_map_proto_funcs_count[6] = {+    countof(js_map_proto_funcs),+    countof(js_set_proto_funcs),+    countof(js_weak_map_proto_funcs),+    countof(js_weak_set_proto_funcs),+    countof(js_map_iterator_proto_funcs),+    countof(js_set_iterator_proto_funcs),+};++void JS_AddIntrinsicMapSet(JSContext *ctx)+{+    int i;+    JSValue obj1;+    char buf[ATOM_GET_STR_BUF_SIZE];++    for(i = 0; i < 4; i++) {+        const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),+                                         JS_ATOM_Map + i);+        ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);+        JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],+                                   js_map_proto_funcs_ptr[i],+                                   js_map_proto_funcs_count[i]);+        obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,+                                    JS_CFUNC_constructor_magic, i);+        if (i < 2) {+            JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,+                                       countof(js_map_funcs));+        }+        JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);+    }++    for(i = 0; i < 2; i++) {+        ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =+            JS_NewObjectProto(ctx, ctx->iterator_proto);+        JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],+                                   js_map_proto_funcs_ptr[i + 4],+                                   js_map_proto_funcs_count[i + 4]);+    }+}++/* Generator */+static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),+};++static const JSCFunctionListEntry js_generator_proto_funcs[] = {+    JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),+    JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),+    JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),+};++/* Promise */++typedef enum JSPromiseStateEnum {+    JS_PROMISE_PENDING,+    JS_PROMISE_FULFILLED,+    JS_PROMISE_REJECTED,+} JSPromiseStateEnum;++typedef struct JSPromiseData {+    JSPromiseStateEnum promise_state;+    /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */+    struct list_head promise_reactions[2];+    BOOL is_handled; /* Note: only useful to debug */+    JSValue promise_result;+} JSPromiseData;++typedef struct JSPromiseFunctionDataResolved {+    int ref_count;+    BOOL already_resolved;+} JSPromiseFunctionDataResolved;++typedef struct JSPromiseFunctionData {+    JSValue promise;+    JSPromiseFunctionDataResolved *presolved;+} JSPromiseFunctionData;++typedef struct JSPromiseReactionData {+    struct list_head link; /* not used in promise_reaction_job */+    JSValue resolving_funcs[2];+    JSValue handler;+} JSPromiseReactionData;++static int js_create_resolving_functions(JSContext *ctx, JSValue *args,+                                         JSValueConst promise);++static void promise_reaction_data_free(JSRuntime *rt,+                                       JSPromiseReactionData *rd)+{+    JS_FreeValueRT(rt, rd->resolving_funcs[0]);+    JS_FreeValueRT(rt, rd->resolving_funcs[1]);+    JS_FreeValueRT(rt, rd->handler);+    js_free_rt(rt, rd);+}++static JSValue promise_reaction_job(JSContext *ctx, int argc,+                                    JSValueConst *argv)+{+    JSValueConst handler, arg, func;+    JSValue res, res2;+    BOOL is_reject;++    assert(argc == 5);+    handler = argv[2];+    is_reject = JS_ToBool(ctx, argv[3]);+    arg = argv[4];+#ifdef DUMP_PROMISE+    printf("promise_reaction_job: is_reject=%d\n", is_reject);+#endif++    if (JS_IsUndefined(handler)) {+        if (is_reject) {+            res = JS_Throw(ctx, JS_DupValue(ctx, arg));+        } else {+            res = JS_DupValue(ctx, arg);+        }+    } else {+        res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);+    }+    is_reject = JS_IsException(res);+    if (is_reject)+        res = JS_GetException(ctx);+    func = argv[is_reject];+    /* as an extension, we support undefined as value to avoid+       creating a dummy promise in the 'await' implementation of async+       functions */+    if (!JS_IsUndefined(func)) {+        res2 = JS_Call(ctx, func, JS_UNDEFINED,+                       1, (JSValueConst *)&res);+    } else {+        res2 = JS_UNDEFINED;+    }+    JS_FreeValue(ctx, res);++    return res2;+}++void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,+                                       JSHostPromiseRejectionTracker *cb,+                                       void *opaque)+{+    rt->host_promise_rejection_tracker = cb;+    rt->host_promise_rejection_tracker_opaque = opaque;+}++static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,+                                      JSValueConst value, BOOL is_reject)+{+    JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);+    struct list_head *el, *el1;+    JSPromiseReactionData *rd;+    JSValueConst args[5];++    if (!s || s->promise_state != JS_PROMISE_PENDING)+        return; /* should never happen */+    set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));+    s->promise_state = JS_PROMISE_FULFILLED + is_reject;+#ifdef DUMP_PROMISE+    printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);+#endif+    if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {+        JSRuntime *rt = ctx->rt;+        if (rt->host_promise_rejection_tracker) {+            rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,+                                               rt->host_promise_rejection_tracker_opaque);+        }+    }++    list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {+        rd = list_entry(el, JSPromiseReactionData, link);+        args[0] = rd->resolving_funcs[0];+        args[1] = rd->resolving_funcs[1];+        args[2] = rd->handler;+        args[3] = JS_NewBool(ctx, is_reject);+        args[4] = value;+        JS_EnqueueJob(ctx, promise_reaction_job, 5, args);+        list_del(&rd->link);+        promise_reaction_data_free(ctx->rt, rd);+    }++    list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {+        rd = list_entry(el, JSPromiseReactionData, link);+        list_del(&rd->link);+        promise_reaction_data_free(ctx->rt, rd);+    }+}++static void reject_promise(JSContext *ctx, JSValueConst promise,+                           JSValueConst value)+{+    fulfill_or_reject_promise(ctx, promise, value, TRUE);+}++static JSValue js_promise_resolve_thenable_job(JSContext *ctx,+                                               int argc, JSValueConst *argv)+{+    JSValueConst promise, thenable, then;+    JSValue args[2], res;++#ifdef DUMP_PROMISE+    printf("js_promise_resolve_thenable_job\n");+#endif+    assert(argc == 3);+    promise = argv[0];+    thenable = argv[1];+    then = argv[2];+    if (js_create_resolving_functions(ctx, args, promise) < 0)+        return JS_EXCEPTION;+    res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);+    if (JS_IsException(res)) {+        JSValue error = JS_GetException(ctx);+        res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);+        JS_FreeValue(ctx, error);+    }+    JS_FreeValue(ctx, args[0]);+    JS_FreeValue(ctx, args[1]);+    return res;+}++static void js_promise_resolve_function_free_resolved(JSRuntime *rt,+                                                      JSPromiseFunctionDataResolved *sr)+{+    if (--sr->ref_count == 0) {+        js_free_rt(rt, sr);+    }+}++static int js_create_resolving_functions(JSContext *ctx,+                                         JSValue *resolving_funcs,+                                         JSValueConst promise)++{+    JSValue obj;+    JSPromiseFunctionData *s;+    JSPromiseFunctionDataResolved *sr;+    int i, ret;++    sr = js_malloc(ctx, sizeof(*sr));+    if (!sr)+        return -1;+    sr->ref_count = 1;+    sr->already_resolved = FALSE; /* must be shared between the two functions */+    ret = 0;+    for(i = 0; i < 2; i++) {+        obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+                                     JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);+        if (JS_IsException(obj))+            goto fail;+        s = js_malloc(ctx, sizeof(*s));+        if (!s) {+            JS_FreeValue(ctx, obj);+        fail:++            if (i != 0)+                JS_FreeValue(ctx, resolving_funcs[0]);+            ret = -1;+            break;+        }+        sr->ref_count++;+        s->presolved = sr;+        s->promise = JS_DupValue(ctx, promise);+        JS_SetOpaque(obj, s);+        js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);+        resolving_funcs[i] = obj;+    }+    js_promise_resolve_function_free_resolved(ctx->rt, sr);+    return ret;+}++static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)+{+    JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;+    if (s) {+        js_promise_resolve_function_free_resolved(rt, s->presolved);+        JS_FreeValueRT(rt, s->promise);+        js_free_rt(rt, s);+    }+}++static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,+                                             JS_MarkFunc *mark_func)+{+    JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;+    if (s) {+        JS_MarkValue(rt, s->promise, mark_func);+    }+}++static JSValue js_promise_resolve_function_call(JSContext *ctx,+                                                JSValueConst func_obj,+                                                JSValueConst this_val,+                                                int argc, JSValueConst *argv,+                                                int flags)+{+    JSObject *p = JS_VALUE_GET_OBJ(func_obj);+    JSPromiseFunctionData *s;+    JSValueConst resolution, args[3];+    JSValue then;+    BOOL is_reject;++    s = p->u.promise_function_data;+    if (!s || s->presolved->already_resolved)+        return JS_UNDEFINED;+    s->presolved->already_resolved = TRUE;+    is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;+    if (argc > 0)+        resolution = argv[0];+    else+        resolution = JS_UNDEFINED;+#ifdef DUMP_PROMISE+    printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);+    JS_DumpValue(ctx, resolution);+    printf("\n");+#endif+    if (is_reject || !JS_IsObject(resolution)) {+        goto done;+    } else if (js_same_value(ctx, resolution, s->promise)) {+        JS_ThrowTypeError(ctx, "promise self resolution");+        goto fail_reject;+    }+    then = JS_GetProperty(ctx, resolution, JS_ATOM_then);+    if (JS_IsException(then)) {+        JSValue error;+    fail_reject:+        error = JS_GetException(ctx);+        reject_promise(ctx, s->promise, error);+        JS_FreeValue(ctx, error);+    } else if (!JS_IsFunction(ctx, then)) {+        JS_FreeValue(ctx, then);+    done:+        fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);+    } else {+        args[0] = s->promise;+        args[1] = resolution;+        args[2] = then;+        JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);+        JS_FreeValue(ctx, then);+    }+    return JS_UNDEFINED;+}++static void js_promise_finalizer(JSRuntime *rt, JSValue val)+{+    JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);+    struct list_head *el, *el1;+    int i;++    if (!s)+        return;+    for(i = 0; i < 2; i++) {+        list_for_each_safe(el, el1, &s->promise_reactions[i]) {+            JSPromiseReactionData *rd =+                list_entry(el, JSPromiseReactionData, link);+            promise_reaction_data_free(rt, rd);+        }+    }+    JS_FreeValueRT(rt, s->promise_result);+    js_free_rt(rt, s);+}++static void js_promise_mark(JSRuntime *rt, JSValueConst val,+                            JS_MarkFunc *mark_func)+{+    JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);+    struct list_head *el;+    int i;++    if (!s)+        return;+    for(i = 0; i < 2; i++) {+        list_for_each(el, &s->promise_reactions[i]) {+            JSPromiseReactionData *rd =+                list_entry(el, JSPromiseReactionData, link);+            JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);+            JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);+            JS_MarkValue(rt, rd->handler, mark_func);+        }+    }+    JS_MarkValue(rt, s->promise_result, mark_func);+}++static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,+                                      int argc, JSValueConst *argv)+{+    JSValueConst executor;+    JSValue obj;+    JSPromiseData *s;+    JSValue args[2], ret;+    int i;++    executor = argv[0];+    if (check_function(ctx, executor))+        return JS_EXCEPTION;+    obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    s = js_mallocz(ctx, sizeof(*s));+    if (!s)+        goto fail;+    s->promise_state = JS_PROMISE_PENDING;+    s->is_handled = FALSE;+    for(i = 0; i < 2; i++)+        init_list_head(&s->promise_reactions[i]);+    s->promise_result = JS_UNDEFINED;+    JS_SetOpaque(obj, s);+    if (js_create_resolving_functions(ctx, args, obj))+        goto fail;+    ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);+    if (JS_IsException(ret)) {+        JSValue ret2, error;+        error = JS_GetException(ctx);+        ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);+        JS_FreeValue(ctx, error);+        if (JS_IsException(ret2))+            goto fail1;+        JS_FreeValue(ctx, ret2);+    }+    JS_FreeValue(ctx, ret);+    JS_FreeValue(ctx, args[0]);+    JS_FreeValue(ctx, args[1]);+    return obj;+ fail1:+    JS_FreeValue(ctx, args[0]);+    JS_FreeValue(ctx, args[1]);+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_promise_executor(JSContext *ctx,+                                   JSValueConst this_val,+                                   int argc, JSValueConst *argv,+                                   int magic, JSValue *func_data)+{+    int i;++    for(i = 0; i < 2; i++) {+        if (!JS_IsUndefined(func_data[i]))+            return JS_ThrowTypeError(ctx, "resolving function already set");+        func_data[i] = JS_DupValue(ctx, argv[i]);+    }+    return JS_UNDEFINED;+}++static JSValue js_promise_executor_new(JSContext *ctx)+{+    JSValueConst func_data[2];++    func_data[0] = JS_UNDEFINED;+    func_data[1] = JS_UNDEFINED;+    return JS_NewCFunctionData(ctx, js_promise_executor, 2,+                               0, 2, func_data);+}++static JSValue js_new_promise_capability(JSContext *ctx,+                                         JSValue *resolving_funcs,+                                         JSValueConst ctor)+{+    JSValue executor, result_promise;+    JSCFunctionDataRecord *s;+    int i;++    executor = js_promise_executor_new(ctx);+    if (JS_IsException(executor))+        return executor;++    if (JS_IsUndefined(ctor)) {+        result_promise = js_promise_constructor(ctx, ctor, 1,+                                                (JSValueConst *)&executor);+    } else {+        result_promise = JS_CallConstructor(ctx, ctor, 1,+                                            (JSValueConst *)&executor);+    }+    if (JS_IsException(result_promise))+        goto fail;+    s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);+    for(i = 0; i < 2; i++) {+        if (check_function(ctx, s->data[i]))+            goto fail;+    }+    for(i = 0; i < 2; i++)+        resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);+    JS_FreeValue(ctx, executor);+    return result_promise;+ fail:+    JS_FreeValue(ctx, executor);+    JS_FreeValue(ctx, result_promise);+    return JS_EXCEPTION;+}++JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)+{+    return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);+}++static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv, int magic)+{+    JSValue result_promise, resolving_funcs[2], ret;+    BOOL is_reject = magic;++    if (!JS_IsObject(this_val))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {+        JSValue ctor;+        BOOL is_same;+        ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);+        if (JS_IsException(ctor))+            return ctor;+        is_same = js_same_value(ctx, ctor, this_val);+        JS_FreeValue(ctx, ctor);+        if (is_same)+            return JS_DupValue(ctx, argv[0]);+    }+    result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);+    if (JS_IsException(result_promise))+        return result_promise;+    ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    if (JS_IsException(ret)) {+        JS_FreeValue(ctx, result_promise);+        return ret;+    }+    JS_FreeValue(ctx, ret);+    return result_promise;+}++#if 0+static JSValue js_promise___newPromiseCapability(JSContext *ctx,+                                                 JSValueConst this_val,+                                                 int argc, JSValueConst *argv)+{+    JSValue result_promise, resolving_funcs[2], obj;+    JSValueConst ctor;+    ctor = argv[0];+    if (!JS_IsObject(ctor))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);+    if (JS_IsException(result_promise))+        return result_promise;+    obj = JS_NewObject(ctx);+    if (JS_IsException(obj)) {+        JS_FreeValue(ctx, resolving_funcs[0]);+        JS_FreeValue(ctx, resolving_funcs[1]);+        JS_FreeValue(ctx, result_promise);+        return JS_EXCEPTION;+    }+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);+    return obj;+}+#endif++static __exception int remainingElementsCount_add(JSContext *ctx,+                                                  JSValueConst resolve_element_env,+                                                  int addend)+{+    JSValue val;+    int remainingElementsCount;++    val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);+    if (JS_IsException(val))+        return -1;+    if (JS_ToInt32Free(ctx, &remainingElementsCount, val))+        return -1;+    remainingElementsCount += addend;+    if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,+                             JS_NewInt32(ctx, remainingElementsCount)) < 0)+        return -1;+    return (remainingElementsCount == 0);+}++#define PROMISE_MAGIC_all        0+#define PROMISE_MAGIC_allSettled 1+#define PROMISE_MAGIC_any        2++static JSValue js_promise_all_resolve_element(JSContext *ctx,+                                              JSValueConst this_val,+                                              int argc, JSValueConst *argv,+                                              int magic,+                                              JSValue *func_data)+{+    int resolve_type = magic & 3;+    int is_reject = magic & 4;+    BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);+    JSValueConst values = func_data[2];+    JSValueConst resolve = func_data[3];+    JSValueConst resolve_element_env = func_data[4];+    JSValue ret, obj;+    int is_zero, index;+    +    if (JS_ToInt32(ctx, &index, func_data[1]))+        return JS_EXCEPTION;+    if (alreadyCalled)+        return JS_UNDEFINED;+    func_data[0] = JS_NewBool(ctx, TRUE);++    if (resolve_type == PROMISE_MAGIC_allSettled) {+        JSValue str;+        +        obj = JS_NewObject(ctx);+        if (JS_IsException(obj))+            return JS_EXCEPTION;+        str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");+        if (JS_IsException(str))+            goto fail1;+        if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,+                                   str,+                                   JS_PROP_C_W_E) < 0)+            goto fail1;+        if (JS_DefinePropertyValue(ctx, obj,+                                   is_reject ? JS_ATOM_reason : JS_ATOM_value,+                                   JS_DupValue(ctx, argv[0]),+                                   JS_PROP_C_W_E) < 0) {+        fail1:+            JS_FreeValue(ctx, obj);+            return JS_EXCEPTION;+        }+    } else {+        obj = JS_DupValue(ctx, argv[0]);+    }+    if (JS_DefinePropertyValueUint32(ctx, values, index,+                                     obj, JS_PROP_C_W_E) < 0)+        return JS_EXCEPTION;+    +    is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);+    if (is_zero < 0)+        return JS_EXCEPTION;+    if (is_zero) {+        if (resolve_type == PROMISE_MAGIC_any) {+            JSValue error;+            error = js_aggregate_error_constructor(ctx, values);+            if (JS_IsException(error))+                return JS_EXCEPTION;+            ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);+            JS_FreeValue(ctx, error);+        } else {+            ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);+        }+        if (JS_IsException(ret))+            return ret;+        JS_FreeValue(ctx, ret);+    }+    return JS_UNDEFINED;+}++/* magic = 0: Promise.all 1: Promise.allSettled */+static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int magic)+{+    JSValue result_promise, resolving_funcs[2], item, next_promise, ret;+    JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;+    JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;+    JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;+    JSValueConst then_args[2], resolve_element_data[5];+    BOOL done;+    int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);+    +    if (!JS_IsObject(this_val))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);+    if (JS_IsException(result_promise))+        return result_promise;+    promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);+    if (JS_IsException(promise_resolve) ||+        check_function(ctx, promise_resolve))+        goto fail_reject;+    iter = JS_GetIterator(ctx, argv[0], FALSE);+    if (JS_IsException(iter)) {+        JSValue error;+    fail_reject:+        error = JS_GetException(ctx);+        ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,+                       (JSValueConst *)&error);+        JS_FreeValue(ctx, error);+        if (JS_IsException(ret))+            goto fail;+        JS_FreeValue(ctx, ret);+    } else {+        next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+        if (JS_IsException(next_method))+            goto fail_reject;+        values = JS_NewArray(ctx);+        if (JS_IsException(values))+            goto fail_reject;+        resolve_element_env = JS_NewArray(ctx);+        if (JS_IsException(resolve_element_env))+            goto fail_reject;+        /* remainingElementsCount field */+        if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,+                                         JS_NewInt32(ctx, 1),+                                         JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)+            goto fail_reject;+        +        index = 0;+        for(;;) {+            /* XXX: conformance: should close the iterator if error on 'done'+               access, but not on 'value' access */+            item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+            if (JS_IsException(item))+                goto fail_reject;+            if (done)+                break;+            next_promise = JS_Call(ctx, promise_resolve, +                                   this_val, 1, (JSValueConst *)&item);+            JS_FreeValue(ctx, item);+            if (JS_IsException(next_promise)) {+            fail_reject1:+                JS_IteratorClose(ctx, iter, TRUE);+                goto fail_reject;+            }+            resolve_element_data[0] = JS_NewBool(ctx, FALSE);+            resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);+            resolve_element_data[2] = values;+            resolve_element_data[3] = resolving_funcs[is_promise_any];+            resolve_element_data[4] = resolve_element_env;+            resolve_element =+                JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,+                                    magic, 5, resolve_element_data);+            if (JS_IsException(resolve_element)) {+                JS_FreeValue(ctx, next_promise);+                goto fail_reject1;+            }+            +            if (magic == PROMISE_MAGIC_allSettled) {+                reject_element =+                    JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,+                                        magic | 4, 5, resolve_element_data);+                if (JS_IsException(reject_element)) {+                    JS_FreeValue(ctx, next_promise);+                    goto fail_reject1;+                }+            } else if (magic == PROMISE_MAGIC_any) {+                if (JS_DefinePropertyValueUint32(ctx, values, index,+                                                 JS_UNDEFINED, JS_PROP_C_W_E) < 0)+                    goto fail_reject1;+                reject_element = resolve_element;+                resolve_element = JS_DupValue(ctx, resolving_funcs[0]);+            } else {+                reject_element = JS_DupValue(ctx, resolving_funcs[1]);+            }++            if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {+                JS_FreeValue(ctx, next_promise);+                JS_FreeValue(ctx, resolve_element);+                JS_FreeValue(ctx, reject_element);+                goto fail_reject1;+            }++            then_args[0] = resolve_element;+            then_args[1] = reject_element;+            ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);+            JS_FreeValue(ctx, resolve_element);+            JS_FreeValue(ctx, reject_element);+            if (check_exception_free(ctx, ret))+                goto fail_reject1;+            index++;+        }++        is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);+        if (is_zero < 0)+            goto fail_reject;+        if (is_zero) {+            if (magic == PROMISE_MAGIC_any) {+                JSValue error;+                error = js_aggregate_error_constructor(ctx, values);+                if (JS_IsException(error))+                    goto fail_reject;+                JS_FreeValue(ctx, values);+                values = error;+            }+            ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,+                          1, (JSValueConst *)&values);+            if (check_exception_free(ctx, ret))+                goto fail_reject;+        }+    }+ done:+    JS_FreeValue(ctx, promise_resolve);+    JS_FreeValue(ctx, resolve_element_env);+    JS_FreeValue(ctx, values);+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    return result_promise;+ fail:+    JS_FreeValue(ctx, result_promise);+    result_promise = JS_EXCEPTION;+    goto done;+}++static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    JSValue result_promise, resolving_funcs[2], item, next_promise, ret;+    JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;+    JSValue promise_resolve = JS_UNDEFINED;+    BOOL done;++    if (!JS_IsObject(this_val))+        return JS_ThrowTypeErrorNotAnObject(ctx);+    result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);+    if (JS_IsException(result_promise))+        return result_promise;+    promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);+    if (JS_IsException(promise_resolve) ||+        check_function(ctx, promise_resolve))+        goto fail_reject;+    iter = JS_GetIterator(ctx, argv[0], FALSE);+    if (JS_IsException(iter)) {+        JSValue error;+    fail_reject:+        error = JS_GetException(ctx);+        ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,+                       (JSValueConst *)&error);+        JS_FreeValue(ctx, error);+        if (JS_IsException(ret))+            goto fail;+        JS_FreeValue(ctx, ret);+    } else {+        next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+        if (JS_IsException(next_method))+            goto fail_reject;++        for(;;) {+            /* XXX: conformance: should close the iterator if error on 'done'+               access, but not on 'value' access */+            item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+            if (JS_IsException(item))+                goto fail_reject;+            if (done)+                break;+            next_promise = JS_Call(ctx, promise_resolve,+                                   this_val, 1, (JSValueConst *)&item);+            JS_FreeValue(ctx, item);+            if (JS_IsException(next_promise)) {+            fail_reject1:+                JS_IteratorClose(ctx, iter, TRUE);+                goto fail_reject;+            }+            ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,+                                (JSValueConst *)resolving_funcs);+            if (check_exception_free(ctx, ret))+                goto fail_reject1;+        }+    }+ done:+    JS_FreeValue(ctx, promise_resolve);+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    return result_promise;+ fail:+    //JS_FreeValue(ctx, next_method); // why not???+    JS_FreeValue(ctx, result_promise);+    result_promise = JS_EXCEPTION;+    goto done;+}++static __exception int perform_promise_then(JSContext *ctx,+                                            JSValueConst promise,+                                            JSValueConst *resolve_reject,+                                            JSValueConst *cap_resolving_funcs)+{+    JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);+    JSPromiseReactionData *rd_array[2], *rd;+    int i, j;++    rd_array[0] = NULL;+    rd_array[1] = NULL;+    for(i = 0; i < 2; i++) {+        JSValueConst handler;+        rd = js_mallocz(ctx, sizeof(*rd));+        if (!rd) {+            if (i == 1)+                promise_reaction_data_free(ctx->rt, rd_array[0]);+            return -1;+        }+        for(j = 0; j < 2; j++)+            rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);+        handler = resolve_reject[i];+        if (!JS_IsFunction(ctx, handler))+            handler = JS_UNDEFINED;+        rd->handler = JS_DupValue(ctx, handler);+        rd_array[i] = rd;+    }++    if (s->promise_state == JS_PROMISE_PENDING) {+        for(i = 0; i < 2; i++)+            list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);+    } else {+        JSValueConst args[5];+        if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {+            JSRuntime *rt = ctx->rt;+            if (rt->host_promise_rejection_tracker) {+                rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,+                                                   TRUE, rt->host_promise_rejection_tracker_opaque);+            }+        }+        i = s->promise_state - JS_PROMISE_FULFILLED;+        rd = rd_array[i];+        args[0] = rd->resolving_funcs[0];+        args[1] = rd->resolving_funcs[1];+        args[2] = rd->handler;+        args[3] = JS_NewBool(ctx, i);+        args[4] = s->promise_result;+        JS_EnqueueJob(ctx, promise_reaction_job, 5, args);+        for(i = 0; i < 2; i++)+            promise_reaction_data_free(ctx->rt, rd_array[i]);+    }+    s->is_handled = TRUE;+    return 0;+}++static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    JSValue ctor, result_promise, resolving_funcs[2];+    JSPromiseData *s;+    int i, ret;++    s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);+    if (!s)+        return JS_EXCEPTION;++    ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);+    if (JS_IsException(ctor))+        return ctor;+    result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);+    JS_FreeValue(ctx, ctor);+    if (JS_IsException(result_promise))+        return result_promise;+    ret = perform_promise_then(ctx, this_val, argv,+                               (JSValueConst *)resolving_funcs);+    for(i = 0; i < 2; i++)+        JS_FreeValue(ctx, resolving_funcs[i]);+    if (ret) {+        JS_FreeValue(ctx, result_promise);+        return JS_EXCEPTION;+    }+    return result_promise;+}++static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValueConst args[2];+    args[0] = JS_UNDEFINED;+    args[1] = argv[0];+    return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);+}++static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,+                                              int argc, JSValueConst *argv,+                                              int magic, JSValue *func_data)+{+    return JS_DupValue(ctx, func_data[0]);+}++static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv,+                                          int magic, JSValue *func_data)+{+    return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));+}++static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,+                                            int argc, JSValueConst *argv,+                                            int magic, JSValue *func_data)+{+    JSValueConst ctor = func_data[0];+    JSValueConst onFinally = func_data[1];+    JSValue res, promise, resolving_funcs[2], ret, then_func;++    res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);+    if (JS_IsException(res))+        return res;+    promise = js_new_promise_capability(ctx, resolving_funcs, ctor);+    if (JS_IsException(promise)) {+        JS_FreeValue(ctx, res);+        return JS_EXCEPTION;+    }+    ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,+                  1, (JSValueConst*)&res);+    JS_FreeValue(ctx, res);+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    if (JS_IsException(ret)) {+        JS_FreeValue(ctx, promise);+        return ret;+    }+    JS_FreeValue(ctx, ret);+    if (magic == 0) {+        then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,+                                        0, 1, argv);+    } else {+        then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,+                                        0, 1, argv);+    }+    if (JS_IsException(then_func)) {+        JS_FreeValue(ctx, promise);+        return then_func;+    }+    ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);+    JS_FreeValue(ctx, then_func);+    return ret;+}++static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValueConst onFinally = argv[0];+    JSValue ctor, ret;+    JSValue then_funcs[2];+    JSValueConst func_data[2];+    int i;++    ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);+    if (JS_IsException(ctor))+        return ctor;+    if (!JS_IsFunction(ctx, onFinally)) {+        then_funcs[0] = JS_DupValue(ctx, onFinally);+        then_funcs[1] = JS_DupValue(ctx, onFinally);+    } else {+        func_data[0] = ctor;+        func_data[1] = onFinally;+        for(i = 0; i < 2; i++) {+            then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);+            if (JS_IsException(then_funcs[i])) {+                if (i == 1)+                    JS_FreeValue(ctx, then_funcs[0]);+                JS_FreeValue(ctx, ctor);+                return JS_EXCEPTION;+            }+        }+    }+    JS_FreeValue(ctx, ctor);+    ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);+    JS_FreeValue(ctx, then_funcs[0]);+    JS_FreeValue(ctx, then_funcs[1]);+    return ret;+}++static const JSCFunctionListEntry js_promise_funcs[] = {+    JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),+    JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),+    JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),+    JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),+    JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),+    JS_CFUNC_DEF("race", 1, js_promise_race ),+    //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),+};++static const JSCFunctionListEntry js_promise_proto_funcs[] = {+    JS_CFUNC_DEF("then", 2, js_promise_then ),+    JS_CFUNC_DEF("catch", 1, js_promise_catch ),+    JS_CFUNC_DEF("finally", 1, js_promise_finally ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),+};++/* AsyncFunction */+static const JSCFunctionListEntry js_async_function_proto_funcs[] = {+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),+};++static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,+                                                  JSValueConst this_val,+                                                  int argc, JSValueConst *argv,+                                                  int magic, JSValue *func_data)+{+    return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),+                                     JS_ToBool(ctx, func_data[0]));+}++static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,+                                                              BOOL done)+{+    JSValueConst func_data[1];++    func_data[0] = (JSValueConst)JS_NewBool(ctx, done);+    return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,+                               1, 0, 1, func_data);+}++/* AsyncIteratorPrototype */++static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {+    JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),+};++/* AsyncFromSyncIteratorPrototype */++typedef struct JSAsyncFromSyncIteratorData {+    JSValue sync_iter;+    JSValue next_method;+} JSAsyncFromSyncIteratorData;++static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)+{+    JSAsyncFromSyncIteratorData *s =+        JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+    if (s) {+        JS_FreeValueRT(rt, s->sync_iter);+        JS_FreeValueRT(rt, s->next_method);+        js_free_rt(rt, s);+    }+}++static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,+                                             JS_MarkFunc *mark_func)+{+    JSAsyncFromSyncIteratorData *s =+        JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+    if (s) {+        JS_MarkValue(rt, s->sync_iter, mark_func);+        JS_MarkValue(rt, s->next_method, mark_func);+    }+}++static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,+                                              JSValueConst sync_iter)+{+    JSValue async_iter, next_method;+    JSAsyncFromSyncIteratorData *s;++    next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);+    if (JS_IsException(next_method))+        return JS_EXCEPTION;+    async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+    if (JS_IsException(async_iter)) {+        JS_FreeValue(ctx, next_method);+        return async_iter;+    }+    s = js_mallocz(ctx, sizeof(*s));+    if (!s) {+        JS_FreeValue(ctx, async_iter);+        JS_FreeValue(ctx, next_method);+        return JS_EXCEPTION;+    }+    s->sync_iter = JS_DupValue(ctx, sync_iter);+    s->next_method = next_method;+    JS_SetOpaque(async_iter, s);+    return async_iter;+}++static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,+                                                int argc, JSValueConst *argv,+                                                int magic)+{+    JSValue promise, resolving_funcs[2], value, err, method;+    JSAsyncFromSyncIteratorData *s;+    int done;+    int is_reject;++    promise = JS_NewPromiseCapability(ctx, resolving_funcs);+    if (JS_IsException(promise))+        return JS_EXCEPTION;+    s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+    if (!s) {+        JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");+        goto reject;+    }++    if (magic == GEN_MAGIC_NEXT) {+        method = JS_DupValue(ctx, s->next_method);+    } else {+        method = JS_GetProperty(ctx, s->sync_iter,+                                magic == GEN_MAGIC_RETURN ? JS_ATOM_return :+                                JS_ATOM_throw);+        if (JS_IsException(method))+            goto reject;+        if (JS_IsUndefined(method) || JS_IsNull(method)) {+            if (magic == GEN_MAGIC_RETURN) {+                err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);+                is_reject = 0;+            } else {+                err = JS_DupValue(ctx, argv[0]);+                is_reject = 1;+            }+            goto done_resolve;+        }+    }+    value = JS_IteratorNext2(ctx, s->sync_iter, method,+                             argc >= 1 ? 1 : 0, argv, &done);+    JS_FreeValue(ctx, method);+    if (JS_IsException(value))+        goto reject;+    if (done == 2) {+        JSValue obj = value;+        value = JS_IteratorGetCompleteValue(ctx, obj, &done);+        JS_FreeValue(ctx, obj);+        if (JS_IsException(value))+            goto reject;+    }++    if (JS_IsException(value)) {+        JSValue res2;+    reject:+        err = JS_GetException(ctx);+        is_reject = 1;+    done_resolve:+        res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,+                       1, (JSValueConst *)&err);+        JS_FreeValue(ctx, err);+        JS_FreeValue(ctx, res2);+        JS_FreeValue(ctx, resolving_funcs[0]);+        JS_FreeValue(ctx, resolving_funcs[1]);+        return promise;+    }+    {+        JSValue value_wrapper_promise, resolve_reject[2];+        int res;++        value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,+                                                   1, (JSValueConst *)&value, 0);+        if (JS_IsException(value_wrapper_promise)) {+            JS_FreeValue(ctx, value);+            goto reject;+        }++        resolve_reject[0] =+            js_async_from_sync_iterator_unwrap_func_create(ctx, done);+        if (JS_IsException(resolve_reject[0])) {+            JS_FreeValue(ctx, value_wrapper_promise);+            goto fail;+        }+        JS_FreeValue(ctx, value);+        resolve_reject[1] = JS_UNDEFINED;++        res = perform_promise_then(ctx, value_wrapper_promise,+                                   (JSValueConst *)resolve_reject,+                                   (JSValueConst *)resolving_funcs);+        JS_FreeValue(ctx, resolve_reject[0]);+        JS_FreeValue(ctx, value_wrapper_promise);+        JS_FreeValue(ctx, resolving_funcs[0]);+        JS_FreeValue(ctx, resolving_funcs[1]);+        if (res) {+            JS_FreeValue(ctx, promise);+            return JS_EXCEPTION;+        }+    }+    return promise;+ fail:+    JS_FreeValue(ctx, value);+    JS_FreeValue(ctx, resolving_funcs[0]);+    JS_FreeValue(ctx, resolving_funcs[1]);+    JS_FreeValue(ctx, promise);+    return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {+    JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),+    JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),+    JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),+};++/* AsyncGeneratorFunction */++static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),+};++/* AsyncGenerator prototype */++static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {+    JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),+    JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),+    JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),+};++static JSClassShortDef const js_async_class_def[] = {+    { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark },                      /* JS_CLASS_PROMISE */+    { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */+    { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */+    { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_FUNCTION */+    { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */+    { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */+    { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */+    { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */+    { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark },  /* JS_CLASS_ASYNC_GENERATOR */+};++void JS_AddIntrinsicPromise(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    JSValue obj1;++    if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {+        init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,+                         countof(js_async_class_def));+        rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;+        rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;+        rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;+        rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;+        rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;+        rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;+    }++    /* Promise */+    ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],+                               js_promise_proto_funcs,+                               countof(js_promise_proto_funcs));+    obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,+                            JS_CFUNC_constructor, 0);+    ctx->promise_ctor = JS_DupValue(ctx, obj1);+    JS_SetPropertyFunctionList(ctx, obj1,+                               js_promise_funcs,+                               countof(js_promise_funcs));+    JS_NewGlobalCConstructor2(ctx, obj1, "Promise",+                              ctx->class_proto[JS_CLASS_PROMISE]);++    /* AsyncFunction */+    ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);+    obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,+                            "AsyncFunction", 1,+                            JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,+                            ctx->function_ctor);+    JS_SetPropertyFunctionList(ctx,+                               ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],+                               js_async_function_proto_funcs,+                               countof(js_async_function_proto_funcs));+    JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],+                       0, JS_PROP_CONFIGURABLE);+    JS_FreeValue(ctx, obj1);++    /* AsyncIteratorPrototype */+    ctx->async_iterator_proto = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,+                               js_async_iterator_proto_funcs,+                               countof(js_async_iterator_proto_funcs));++    /* AsyncFromSyncIteratorPrototype */+    ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =+        JS_NewObjectProto(ctx, ctx->async_iterator_proto);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],+                               js_async_from_sync_iterator_proto_funcs,+                               countof(js_async_from_sync_iterator_proto_funcs));++    /* AsyncGeneratorPrototype */+    ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =+        JS_NewObjectProto(ctx, ctx->async_iterator_proto);+    JS_SetPropertyFunctionList(ctx,+                               ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],+                               js_async_generator_proto_funcs,+                               countof(js_async_generator_proto_funcs));++    /* AsyncGeneratorFunction */+    ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =+        JS_NewObjectProto(ctx, ctx->function_proto);+    obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,+                            "AsyncGeneratorFunction", 1,+                            JS_CFUNC_constructor_or_func_magic,+                            JS_FUNC_ASYNC_GENERATOR,+                            ctx->function_ctor);+    JS_SetPropertyFunctionList(ctx,+                               ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],+                               js_async_generator_function_proto_funcs,+                               countof(js_async_generator_function_proto_funcs));+    JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],+                       ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],+                       JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);+    JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],+                       0, JS_PROP_CONFIGURABLE);+    JS_FreeValue(ctx, obj1);+}++/* URI handling */++static int string_get_hex(JSString *p, int k, int n) {+    int c = 0, h;+    while (n-- > 0) {+        if ((h = from_hex(string_get(p, k++))) < 0)+            return -1;+        c = (c << 4) | h;+    }+    return c;+}++static int isURIReserved(int c) {+    return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;+}++static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)+{+    va_list ap;++    va_start(ap, fmt);+    JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);+    va_end(ap);+    return -1;+}++static int hex_decode(JSContext *ctx, JSString *p, int k) {+    int c;++    if (k >= p->len || string_get(p, k) != '%')+        return js_throw_URIError(ctx, "expecting %%");+    if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)+        return js_throw_URIError(ctx, "expecting hex digit");++    return c;+}++static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv, int isComponent)+{+    JSValue str;+    StringBuffer b_s, *b = &b_s;+    JSString *p;+    int k, c, c1, n, c_min;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        return str;++    string_buffer_init(ctx, b, 0);++    p = JS_VALUE_GET_STRING(str);+    for (k = 0; k < p->len;) {+        c = string_get(p, k);+        if (c == '%') {+            c = hex_decode(ctx, p, k);+            if (c < 0)+                goto fail;+            k += 3;+            if (c < 0x80) {+                if (!isComponent && isURIReserved(c)) {+                    c = '%';+                    k -= 2;+                }+            } else {+                /* Decode URI-encoded UTF-8 sequence */+                if (c >= 0xc0 && c <= 0xdf) {+                    n = 1;+                    c_min = 0x80;+                    c &= 0x1f;+                } else if (c >= 0xe0 && c <= 0xef) {+                    n = 2;+                    c_min = 0x800;+                    c &= 0xf;+                } else if (c >= 0xf0 && c <= 0xf7) {+                    n = 3;+                    c_min = 0x10000;+                    c &= 0x7;+                } else {+                    n = 0;+                    c_min = 1;+                    c = 0;+                }+                while (n-- > 0) {+                    c1 = hex_decode(ctx, p, k);+                    if (c1 < 0)+                        goto fail;+                    k += 3;+                    if ((c1 & 0xc0) != 0x80) {+                        c = 0;+                        break;+                    }+                    c = (c << 6) | (c1 & 0x3f);+                }+                if (c < c_min || c > 0x10FFFF ||+                    (c >= 0xd800 && c < 0xe000)) {+                    js_throw_URIError(ctx, "malformed UTF-8");+                    goto fail;+                }+            }+        } else {+            k++;+        }+        string_buffer_putc(b, c);+    }+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);++fail:+    JS_FreeValue(ctx, str);+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static int isUnescaped(int c) {+    static char const unescaped_chars[] =+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+        "abcdefghijklmnopqrstuvwxyz"+        "0123456789"+        "@*_+-./";+    return c < 0x100 &&+        memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);+}++static int isURIUnescaped(int c, int isComponent) {+    return c < 0x100 &&+        ((c >= 0x61 && c <= 0x7a) ||+         (c >= 0x41 && c <= 0x5a) ||+         (c >= 0x30 && c <= 0x39) ||+         memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||+         (!isComponent && isURIReserved(c)));+}++static int encodeURI_hex(StringBuffer *b, int c) {+    uint8_t buf[6];+    int n = 0;+    const char *hex = "0123456789ABCDEF";++    buf[n++] = '%';+    if (c >= 256) {+        buf[n++] = 'u';+        buf[n++] = hex[(c >> 12) & 15];+        buf[n++] = hex[(c >>  8) & 15];+    }+    buf[n++] = hex[(c >> 4) & 15];+    buf[n++] = hex[(c >> 0) & 15];+    return string_buffer_write8(b, buf, n);+}++static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv,+                                   int isComponent)+{+    JSValue str;+    StringBuffer b_s, *b = &b_s;+    JSString *p;+    int k, c, c1;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        return str;++    p = JS_VALUE_GET_STRING(str);+    string_buffer_init(ctx, b, p->len);+    for (k = 0; k < p->len;) {+        c = string_get(p, k);+        k++;+        if (isURIUnescaped(c, isComponent)) {+            string_buffer_putc16(b, c);+        } else {+            if (c >= 0xdc00 && c <= 0xdfff) {+                js_throw_URIError(ctx, "invalid character");+                goto fail;+            } else if (c >= 0xd800 && c <= 0xdbff) {+                if (k >= p->len) {+                    js_throw_URIError(ctx, "expecting surrogate pair");+                    goto fail;+                }+                c1 = string_get(p, k);+                k++;+                if (c1 < 0xdc00 || c1 > 0xdfff) {+                    js_throw_URIError(ctx, "expecting surrogate pair");+                    goto fail;+                }+                c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+            }+            if (c < 0x80) {+                encodeURI_hex(b, c);+            } else {+                /* XXX: use C UTF-8 conversion ? */+                if (c < 0x800) {+                    encodeURI_hex(b, (c >> 6) | 0xc0);+                } else {+                    if (c < 0x10000) {+                        encodeURI_hex(b, (c >> 12) | 0xe0);+                    } else {+                        encodeURI_hex(b, (c >> 18) | 0xf0);+                        encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);+                    }+                    encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);+                }+                encodeURI_hex(b, (c & 0x3f) | 0x80);+            }+        }+    }+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);++fail:+    JS_FreeValue(ctx, str);+    string_buffer_free(b);+    return JS_EXCEPTION;+}++static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    JSValue str;+    StringBuffer b_s, *b = &b_s;+    JSString *p;+    int i, len, c;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        return str;++    p = JS_VALUE_GET_STRING(str);+    string_buffer_init(ctx, b, p->len);+    for (i = 0, len = p->len; i < len; i++) {+        c = string_get(p, i);+        if (isUnescaped(c)) {+            string_buffer_putc16(b, c);+        } else {+            encodeURI_hex(b, c);+        }+    }+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);+}++static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValue str;+    StringBuffer b_s, *b = &b_s;+    JSString *p;+    int i, len, c, n;++    str = JS_ToString(ctx, argv[0]);+    if (JS_IsException(str))+        return str;++    string_buffer_init(ctx, b, 0);+    p = JS_VALUE_GET_STRING(str);+    for (i = 0, len = p->len; i < len; i++) {+        c = string_get(p, i);+        if (c == '%') {+            if (i + 6 <= len+            &&  string_get(p, i + 1) == 'u'+            &&  (n = string_get_hex(p, i + 2, 4)) >= 0) {+                c = n;+                i += 6 - 1;+            } else+            if (i + 3 <= len+            &&  (n = string_get_hex(p, i + 1, 2)) >= 0) {+                c = n;+                i += 3 - 1;+            }+        }+        string_buffer_putc16(b, c);+    }+    JS_FreeValue(ctx, str);+    return string_buffer_end(b);+}++/* global object */++static const JSCFunctionListEntry js_global_funcs[] = {+    JS_CFUNC_DEF("parseInt", 2, js_parseInt ),+    JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),+    JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),+    JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),++    JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),+    JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),+    JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),+    JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),+    JS_CFUNC_DEF("escape", 1, js_global_escape ),+    JS_CFUNC_DEF("unescape", 1, js_global_unescape ),+    JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),+    JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),+    JS_PROP_UNDEFINED_DEF("undefined", 0 ),++    /* for the 'Date' implementation */+    JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),+    //JS_CFUNC_DEF("__date_now", 0, js___date_now ),+    //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),+    //JS_CFUNC_DEF("__date_create", 3, js___date_create ),+};++/* Date */++static int64_t math_mod(int64_t a, int64_t b) {+    /* return positive modulo */+    int64_t m = a % b;+    return m + (m < 0) * b;+}++static int64_t floor_div(int64_t a, int64_t b) {+    /* integer division rounding toward -Infinity */+    int64_t m = a % b;+    return (a - (m + (m < 0) * b)) / b;+}++static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv);++static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)+{+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))+            return JS_ToFloat64(ctx, valp, p->u.object_data);+    }+    JS_ThrowTypeError(ctx, "not a Date object");+    return -1;+}++static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)+{+    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_DATE) {+            JS_FreeValue(ctx, p->u.object_data);+            p->u.object_data = __JS_NewFloat64(ctx, v);+            return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a Date object");+}++static int64_t days_from_year(int64_t y) {+    return 365 * (y - 1970) + floor_div(y - 1969, 4) -+        floor_div(y - 1901, 100) + floor_div(y - 1601, 400);+}++static int64_t days_in_year(int64_t y) {+    return 365 + !(y % 4) - !(y % 100) + !(y % 400);+}++/* return the year, update days */+static int64_t year_from_days(int64_t *days) {+    int64_t y, d1, nd, d = *days;+    y = floor_div(d * 10000, 3652425) + 1970;+    /* the initial approximation is very good, so only a few+       iterations are necessary */+    for(;;) {+        d1 = d - days_from_year(y);+        if (d1 < 0) {+            y--;+            d1 += days_in_year(y);+        } else {+            nd = days_in_year(y);+            if (d1 < nd)+                break;+            d1 -= nd;+            y++;+        }+    }+    *days = d1;+    return y;+}++static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };+static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";+static char const day_names[] = "SunMonTueWedThuFriSat";++static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,+                                       int64_t fields[9], int is_local, int force)+{+    double dval;+    int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;++    if (JS_ThisTimeValue(ctx, &dval, obj))+        return -1;++    if (isnan(dval)) {+        if (!force)+            return FALSE; /* NaN */+        d = 0;        /* initialize all fields to 0 */+    } else {+        d = dval;+        if (is_local) {+            tz = -getTimezoneOffset(d);+            d += tz * 60000;+        }+    }++    /* result is >= 0, we can use % */+    h = math_mod(d, 86400000);+    days = (d - h) / 86400000;+    ms = h % 1000;+    h = (h - ms) / 1000;+    s = h % 60;+    h = (h - s) / 60;+    m = h % 60;+    h = (h - m) / 60;+    wd = math_mod(days + 4, 7); /* week day */+    y = year_from_days(&days);++    for(i = 0; i < 11; i++) {+        md = month_days[i];+        if (i == 1)+            md += days_in_year(y) - 365;+        if (days < md)+            break;+        days -= md;+    }+    fields[0] = y;+    fields[1] = i;+    fields[2] = days + 1;+    fields[3] = h;+    fields[4] = m;+    fields[5] = s;+    fields[6] = ms;+    fields[7] = wd;+    fields[8] = tz;+    return TRUE;+}++static double time_clip(double t) {+    if (t >= -8.64e15 && t <= 8.64e15)+        return trunc(t) + 0.0;  /* convert -0 to +0 */+    else+        return NAN;+}++static double set_date_fields(int64_t fields[], int is_local) {+    int64_t days, y, m, md, h, d, i;++    i = fields[1];+    m = math_mod(i, 12);+    y = fields[0] + (i - m) / 12;+    days = days_from_year(y);++    for(i = 0; i < m; i++) {+        md = month_days[i];+        if (i == 1)+            md += days_in_year(y) - 365;+        days += md;+    }+    days += fields[2] - 1;+    h = ((fields[3] * 60 + fields[4]) * 60 + fields[5]) * 1000 + fields[6];+    d = days * 86400000 + h;+    if (is_local)+        d += getTimezoneOffset(d) * 60000;+    return time_clip(d);+}++static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int magic)+{+    // get_date_field(obj, n, is_local)+    int64_t fields[9];+    int res, n, is_local;++    is_local = magic & 0x0F;+    n = (magic >> 4) & 0x0F;+    res = get_date_fields(ctx, this_val, fields, is_local, 0);+    if (res < 0)+        return JS_EXCEPTION;+    if (!res)+        return JS_NAN;++    if (magic & 0x100) {    // getYear+        fields[0] -= 1900;+    }+    return JS_NewInt64(ctx, fields[n]);+}++static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv, int magic)+{+    // _field(obj, first_field, end_field, args, is_local)+    int64_t fields[9];+    int res, first_field, end_field, is_local, i, n;+    double d, a;++    d = NAN;+    first_field = (magic >> 8) & 0x0F;+    end_field = (magic >> 4) & 0x0F;+    is_local = magic & 0x0F;++    res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);+    if (res < 0)+        return JS_EXCEPTION;+    if (res && argc > 0) {+        n = end_field - first_field;+        if (argc < n)+            n = argc;+        for(i = 0; i < n; i++) {+            if (JS_ToFloat64(ctx, &a, argv[i]))+                return JS_EXCEPTION;+            if (!isfinite(a))+                goto done;+            fields[first_field + i] = trunc(a);+        }+        d = set_date_fields(fields, is_local);+    }+done:+    return JS_SetThisTimeValue(ctx, this_val, d);+}++/* fmt:+   0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"+   1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"+   2: toISOString: "2018-01-02T23:02:56.927Z"+   3: toLocaleString: "1/2/2018, 11:40:40 PM"+   part: 1=date, 2=time 3=all+   XXX: should use a variant of strftime().+ */+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv, int magic)+{+    // _string(obj, fmt, part)+    char buf[64];+    int64_t fields[9];+    int res, fmt, part, pos;+    int y, mon, d, h, m, s, ms, wd, tz;++    fmt = (magic >> 4) & 0x0F;+    part = magic & 0x0F;++    res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);+    if (res < 0)+        return JS_EXCEPTION;+    if (!res) {+        if (fmt == 2)+            return JS_ThrowRangeError(ctx, "Date value is NaN");+        else+            return JS_NewString(ctx, "Invalid Date");+    }++    y = fields[0];+    mon = fields[1];+    d = fields[2];+    h = fields[3];+    m = fields[4];+    s = fields[5];+    ms = fields[6];+    wd = fields[7];+    tz = fields[8];++    pos = 0;++    if (part & 1) { /* date part */+        switch(fmt) {+        case 0:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%.3s, %02d %.3s %0*d ",+                            day_names + wd * 3, d,+                            month_names + mon * 3, 4 + (y < 0), y);+            break;+        case 1:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%.3s %.3s %02d %0*d",+                            day_names + wd * 3,+                            month_names + mon * 3, d, 4 + (y < 0), y);+            if (part == 3) {+                buf[pos++] = ' ';+            }+            break;+        case 2:+            if (y >= 0 && y <= 9999) {+                pos += snprintf(buf + pos, sizeof(buf) - pos,+                                "%04d", y);+            } else {+                pos += snprintf(buf + pos, sizeof(buf) - pos,+                                "%+07d", y);+            }+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "-%02d-%02dT", mon + 1, d);+            break;+        case 3:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);+            if (part == 3) {+                buf[pos++] = ',';+                buf[pos++] = ' ';+            }+            break;+        }+    }+    if (part & 2) { /* time part */+        switch(fmt) {+        case 0:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%02d:%02d:%02d GMT", h, m, s);+            break;+        case 1:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%02d:%02d:%02d GMT", h, m, s);+            if (tz < 0) {+                buf[pos++] = '-';+                tz = -tz;+            } else {+                buf[pos++] = '+';+            }+            /* tz is >= 0, can use % */+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%02d%02d", tz / 60, tz % 60);+            /* XXX: tack the time zone code? */+            break;+        case 2:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%02d:%02d:%02d.%03dZ", h, m, s, ms);+            break;+        case 3:+            pos += snprintf(buf + pos, sizeof(buf) - pos,+                            "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,+                            (h < 12) ? 'A' : 'P');+            break;+        }+    }+    return JS_NewStringLen(ctx, buf, pos);+}++/* OS dependent: return the UTC time in ms since 1970. */+static int64_t date_now(void) {+    struct timeval tv;+    gettimeofday(&tv, NULL);+    return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);+}++static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,+                                   int argc, JSValueConst *argv)+{+    // Date(y, mon, d, h, m, s, ms)+    JSValue rv;+    int i, n;+    double a, val;++    if (JS_IsUndefined(new_target)) {+        /* invoked as function */+        argc = 0;+    }+    n = argc;+    if (n == 0) {+        val = date_now();+    } else if (n == 1) {+        JSValue v, dv;+        if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {+            JSObject *p = JS_VALUE_GET_OBJ(argv[0]);+            if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {+                if (JS_ToFloat64(ctx, &val, p->u.object_data))+                    return JS_EXCEPTION;+                val = time_clip(val);+                goto has_val;+            }+        }+        v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);+        if (JS_IsString(v)) {+            dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);+            JS_FreeValue(ctx, v);+            if (JS_IsException(dv))+                return JS_EXCEPTION;+            if (JS_ToFloat64Free(ctx, &val, dv))+                return JS_EXCEPTION;+        } else {+            if (JS_ToFloat64Free(ctx, &val, v))+                return JS_EXCEPTION;+        }+        val = time_clip(val);+    } else {+        int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 };+        if (n > 7)+            n = 7;+        for(i = 0; i < n; i++) {+            if (JS_ToFloat64(ctx, &a, argv[i]))+                return JS_EXCEPTION;+            if (!isfinite(a))+                break;+            fields[i] = trunc(a);+            if (i == 0 && fields[0] >= 0 && fields[0] < 100)+                fields[0] += 1900;+        }+        val = (i == n) ? set_date_fields(fields, 1) : NAN;+    }+has_val:+#if 0+    JSValueConst args[3];+    args[0] = new_target;+    args[1] = ctx->class_proto[JS_CLASS_DATE];+    args[2] = __JS_NewFloat64(ctx, val);+    rv = js___date_create(ctx, JS_UNDEFINED, 3, args);+#else+    rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);+    if (!JS_IsException(rv))+        JS_SetObjectData(ctx, rv, __JS_NewFloat64(ctx, val));+#endif+    if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {+        /* invoked as a function, return (new Date()).toString(); */+        JSValue s;+        s = get_date_string(ctx, rv, 0, NULL, 0x13);+        JS_FreeValue(ctx, rv);+        rv = s;+    }+    return rv;+}++static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    // UTC(y, mon, d, h, m, s, ms)+    int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 };+    int i, n;+    double a;++    n = argc;+    if (n == 0)+        return JS_NAN;+    if (n > 7)+        n = 7;+    for(i = 0; i < n; i++) {+        if (JS_ToFloat64(ctx, &a, argv[i]))+            return JS_EXCEPTION;+        if (!isfinite(a))+            return JS_NAN;+        fields[i] = trunc(a);+        if (i == 0 && fields[0] >= 0 && fields[0] < 100)+            fields[0] += 1900;+    }+    return __JS_NewFloat64(ctx, set_date_fields(fields, 0));+}++static void string_skip_spaces(JSString *sp, int *pp) {+    while (*pp < sp->len && string_get(sp, *pp) == ' ')+        *pp += 1;+}++static void string_skip_non_spaces(JSString *sp, int *pp) {+    while (*pp < sp->len && string_get(sp, *pp) != ' ')+        *pp += 1;+}++/* parse a numeric field */+static int string_get_field(JSString *sp, int *pp, int64_t *pval) {+    int64_t v = 0;+    int c, p = *pp;++    /* skip non digits, should only skip spaces? */+    while (p < sp->len) {+        c = string_get(sp, p);+        if (c >= '0' && c <= '9')+            break;+        p++;+    }+    if (p >= sp->len)+        return -1;+    while (p < sp->len) {+        c = string_get(sp, p);+        if (!(c >= '0' && c <= '9'))+            break;+        v = v * 10 + c - '0';+        p++;+    }+    *pval = v;+    *pp = p;+    return 0;+}++/* parse a fixed width numeric field */+static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) {+    int64_t v = 0;+    int i, c, p = *pp;++    for(i = 0; i < n; i++) {+        if (p >= sp->len)+            return -1;+        c = string_get(sp, p);+        if (!(c >= '0' && c <= '9'))+            return -1;+        v = v * 10 + c - '0';+        p++;+    }+    *pval = v;+    *pp = p;+    return 0;+}++/* parse a signed numeric field */+static int string_get_signed_field(JSString *sp, int *pp, int64_t *pval) {+    int sgn, res;++    if (*pp >= sp->len)+        return -1;++    sgn = string_get(sp, *pp);+    if (sgn == '-' || sgn == '+')+        *pp += 1;++    res = string_get_field(sp, pp, pval);+    if (res == 0 && sgn == '-')+        *pval = -*pval;+    return res;+}++static int find_abbrev(JSString *sp, int p, const char *list, int count) {+    int n, i;++    if (p + 3 <= sp->len) {+        for (n = 0; n < count; n++) {+            for (i = 0; i < 3; i++) {+                if (string_get(sp, p + i) != month_names[n * 3 + i])+                    goto next;+            }+            return n;+        next:;+        }+    }+    return -1;+}++static int string_get_month(JSString *sp, int *pp, int64_t *pval) {+    int n;++    string_skip_spaces(sp, pp);+    n = find_abbrev(sp, *pp, month_names, 12);+    if (n < 0)+        return -1;++    *pval = n;+    *pp += 3;+    return 0;+}++static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv)+{+    // parse(s)+    JSValue s, rv;+    int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };+    int64_t tz, hh, mm;+    double d;+    int p, i, c, sgn;+    JSString *sp;+    BOOL is_local;+    +    rv = JS_NAN;++    s = JS_ToString(ctx, argv[0]);+    if (JS_IsException(s))+        return JS_EXCEPTION;+    +    sp = JS_VALUE_GET_STRING(s);+    p = 0;+    if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {+        /* ISO format */+        /* year field can be negative */+        /* XXX: could be stricter */+        if (string_get_signed_field(sp, &p, &fields[0]))+            goto done;++        is_local = TRUE;+        for (i = 1; i < 6; i++) {+            if (string_get_field(sp, &p, &fields[i]))+                break;+        }+        if (i <= 3) {+            /* no time: UTC by default */+            is_local = FALSE;+        } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') {+            /* parse milliseconds as a fractional part, round to nearest */+            /* XXX: the spec does not indicate which rounding should be used */+            int mul = 1000, ms = 0;+            while (++p < sp->len) {+                int c = string_get(sp, p);+                if (!(c >= '0' && c <= '9'))+                    break;+                if (mul == 1 && c >= '5')+                    ms += 1;+                ms += (c - '0') * (mul /= 10);+            }+            fields[6] = ms;+        }+        fields[1] -= 1;++        /* parse the time zone offset if present: [+-]HH:mm */+        tz = 0;+        if (p < sp->len) {+            sgn = string_get(sp, p);+            if (sgn == '+' || sgn == '-') {+                if (string_get_field(sp, &p, &hh))+                    goto done;+                if (string_get_field(sp, &p, &mm))+                    goto done;+                tz = hh * 60 + mm;+                if (sgn == '-')+                    tz = -tz;+                is_local = FALSE;+            } else if (sgn == 'Z') {+                is_local = FALSE;+            }+        }+    } else {+        /* toString or toUTCString format */+        /* skip the day of the week */+        string_skip_non_spaces(sp, &p);+        string_skip_spaces(sp, &p);+        if (p >= sp->len)+            goto done;+        c = string_get(sp, p);+        if (c >= '0' && c <= '9') {+            /* day of month first */+            if (string_get_field(sp, &p, &fields[2]))+                goto done;+            if (string_get_month(sp, &p, &fields[1]))+                goto done;+        } else {+            /* month first */+            if (string_get_month(sp, &p, &fields[1]))+                goto done;+            if (string_get_field(sp, &p, &fields[2]))+                goto done;+        }+        string_skip_spaces(sp, &p);+        if (string_get_signed_field(sp, &p, &fields[0]))+            goto done;++        /* hour, min, seconds */+        for(i = 0; i < 3; i++) {+            if (string_get_field(sp, &p, &fields[3 + i]))+                goto done;+        }+        // XXX: parse optional milliseconds?++        /* parse the time zone offset if present: [+-]HHmm */+        is_local = FALSE;+        tz = 0;+        for (tz = 0; p < sp->len; p++) {+            sgn = string_get(sp, p);+            if (sgn == '+' || sgn == '-') {+                p++;+                if (string_get_digits(sp, &p, 2, &hh))+                    goto done;+                if (string_get_digits(sp, &p, 2, &mm))+                    goto done;+                tz = hh * 60 + mm;+                if (sgn == '-')+                    tz = -tz;+                break;+            }+        }+    }+    d = set_date_fields(fields, is_local) - tz * 60000;+    rv = __JS_NewFloat64(ctx, d);++done:+    JS_FreeValue(ctx, s);+    return rv;+}++static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv)+{+    // now()+    return JS_NewInt64(ctx, date_now());+}++static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,+                                          int argc, JSValueConst *argv)+{+    // Symbol_toPrimitive(hint)+    JSValueConst obj = this_val;+    JSAtom hint = JS_ATOM_NULL;+    int hint_num;++    if (!JS_IsObject(obj))+        return JS_ThrowTypeErrorNotAnObject(ctx);++    if (JS_IsString(argv[0])) {+        hint = JS_ValueToAtom(ctx, argv[0]);+        if (hint == JS_ATOM_NULL)+            return JS_EXCEPTION;+        JS_FreeAtom(ctx, hint);+    }+    switch (hint) {+    case JS_ATOM_number:+#ifdef CONFIG_BIGNUM+    case JS_ATOM_integer:+#endif+        hint_num = HINT_NUMBER;+        break;+    case JS_ATOM_string:+    case JS_ATOM_default:+        hint_num = HINT_STRING;+        break;+    default:+        return JS_ThrowTypeError(ctx, "invalid hint");+    }+    return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);+}++static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    // getTimezoneOffset()+    double v;++    if (JS_ThisTimeValue(ctx, &v, this_val))+        return JS_EXCEPTION;+    if (isnan(v))+        return JS_NAN;+    else+        return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));+}++static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    // getTime()+    double v;++    if (JS_ThisTimeValue(ctx, &v, this_val))+        return JS_EXCEPTION;+    return __JS_NewFloat64(ctx, v);+}++static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    // setTime(v)+    double v;++    if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))+        return JS_EXCEPTION;+    return JS_SetThisTimeValue(ctx, this_val, time_clip(v));+}++static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,+                               int argc, JSValueConst *argv)+{+    // setYear(y)+    double y;+    JSValueConst args[1];++    if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))+        return JS_EXCEPTION;+    y = +y;+    if (isfinite(y)) {+        y = trunc(y);+        if (y >= 0 && y < 100)+            y += 1900;+    }+    args[0] = __JS_NewFloat64(ctx, y);+    return set_date_field(ctx, this_val, 1, args, 0x011);+}++static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,+                              int argc, JSValueConst *argv)+{+    // toJSON(key)+    JSValue obj, tv, method, rv;+    double d;++    rv = JS_EXCEPTION;+    tv = JS_UNDEFINED;++    obj = JS_ToObject(ctx, this_val);+    tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);+    if (JS_IsException(tv))+        goto exception;+    if (JS_IsNumber(tv)) {+        if (JS_ToFloat64(ctx, &d, tv) < 0)+            goto exception;+        if (!isfinite(d)) {+            rv = JS_NULL;+            goto done;+        }+    }+    method = JS_GetPropertyStr(ctx, obj, "toISOString");+    if (JS_IsException(method))+        goto exception;+    if (!JS_IsFunction(ctx, method)) {+        JS_ThrowTypeError(ctx, "object needs toISOString method");+        JS_FreeValue(ctx, method);+        goto exception;+    }+    rv = JS_CallFree(ctx, method, obj, 0, NULL);+exception:+done:+    JS_FreeValue(ctx, obj);+    JS_FreeValue(ctx, tv);+    return rv;+}++static const JSCFunctionListEntry js_date_funcs[] = {+    JS_CFUNC_DEF("now", 0, js_Date_now ),+    JS_CFUNC_DEF("parse", 1, js_Date_parse ),+    JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),+};++static const JSCFunctionListEntry js_date_proto_funcs[] = {+    JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),+    JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),+    JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),+    JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),+    JS_ALIAS_DEF("toGMTString", "toUTCString" ),+    JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),+    JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),+    JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),+    JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),+    JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),+    JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),+    JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),+    JS_CFUNC_DEF("getTime", 0, js_date_getTime ),+    JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),+    JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),+    JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),+    JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),+    JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),+    JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),+    JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),+    JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),+    JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),+    JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),+    JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),+    JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),+    JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),+    JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),+    JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),+    JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),+    JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),+    JS_CFUNC_DEF("setTime", 1, js_date_setTime ),+    JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),+    JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),+    JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),+    JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),+    JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),+    JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),+    JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),+    JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),+    JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),+    JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),+    JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),+    JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),+    JS_CFUNC_DEF("setYear", 1, js_date_setYear ),+    JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),+    JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),+    JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),+};++void JS_AddIntrinsicDate(JSContext *ctx)+{+    JSValueConst obj;++    /* Date */+    ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,+                               countof(js_date_proto_funcs));+    obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,+                                   ctx->class_proto[JS_CLASS_DATE]);+    JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));+}++/* eval */++void JS_AddIntrinsicEval(JSContext *ctx)+{+    ctx->eval_internal = __JS_EvalInternal;+}++#ifdef CONFIG_BIGNUM++/* Operators */++static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)+{+    JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);+    int i, j;+    JSBinaryOperatorDefEntry *ent;+    +    if (opset) {+        for(i = 0; i < JS_OVOP_COUNT; i++) {+            if (opset->self_ops[i])+                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));+        }+        for(j = 0; j < opset->left.count; j++) {+            ent = &opset->left.tab[j];+            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+                if (ent->ops[i])+                    JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));+            }+        }+        js_free_rt(rt, opset->left.tab);+        for(j = 0; j < opset->right.count; j++) {+            ent = &opset->right.tab[j];+            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+                if (ent->ops[i])+                    JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));+            }+        }+        js_free_rt(rt, opset->right.tab);+        js_free_rt(rt, opset);+    }+}++static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,+                                 JS_MarkFunc *mark_func)+{+    JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);+    int i, j;+    JSBinaryOperatorDefEntry *ent;+    +    if (opset) {+        for(i = 0; i < JS_OVOP_COUNT; i++) {+            if (opset->self_ops[i])+                JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),+                             mark_func);+        }+        for(j = 0; j < opset->left.count; j++) {+            ent = &opset->left.tab[j];+            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+                if (ent->ops[i])+                    JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),+                                 mark_func);+            }+        }+        for(j = 0; j < opset->right.count; j++) {+            ent = &opset->right.tab[j];+            for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+                if (ent->ops[i])+                    JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),+                                 mark_func);+            }+        }+    }+}+++/* create an OperatorSet object */+static JSValue js_operators_create_internal(JSContext *ctx,+                                            int argc, JSValueConst *argv,+                                            BOOL is_primitive)+{+    JSValue opset_obj, prop, obj;+    JSOperatorSetData *opset, *opset1;+    JSBinaryOperatorDef *def;+    JSValueConst arg;+    int i, j;+    JSBinaryOperatorDefEntry *new_tab;+    JSBinaryOperatorDefEntry *ent;+    uint32_t op_count;++    if (ctx->rt->operator_count == UINT32_MAX) {+        return JS_ThrowTypeError(ctx, "too many operators");+    }+    opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);+    if (JS_IsException(opset_obj))+        goto fail;+    opset = js_mallocz(ctx, sizeof(*opset));+    if (!opset)+        goto fail;+    JS_SetOpaque(opset_obj, opset);+    if (argc >= 1) {+        arg = argv[0];+        /* self operators */+        for(i = 0; i < JS_OVOP_COUNT; i++) {+            prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);+            if (JS_IsException(prop))+                goto fail;+            if (!JS_IsUndefined(prop)) {+                if (check_function(ctx, prop)) {+                    JS_FreeValue(ctx, prop);+                    goto fail;+                }+                opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);+            }+        }+    }+    /* left & right operators */+    for(j = 1; j < argc; j++) {+        arg = argv[j];+        prop = JS_GetPropertyStr(ctx, arg, "left");+        if (JS_IsException(prop))+            goto fail;+        def = &opset->right;+        if (JS_IsUndefined(prop)) {+            prop = JS_GetPropertyStr(ctx, arg, "right");+            if (JS_IsException(prop))+                goto fail;+            if (JS_IsUndefined(prop)) {+                JS_ThrowTypeError(ctx, "left or right property must be present");+                goto fail;+            }+            def = &opset->left;+        }+        /* get the operator set */+        obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);+        JS_FreeValue(ctx, prop);+        if (JS_IsException(obj))+            goto fail;+        prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);+        JS_FreeValue(ctx, obj);+        if (JS_IsException(prop))+            goto fail;+        opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);+        if (!opset1) {+            JS_FreeValue(ctx, prop);+            goto fail;+        }+        op_count = opset1->operator_counter;+        JS_FreeValue(ctx, prop);+        +        /* we assume there are few entries */+        new_tab = js_realloc(ctx, def->tab,+                             (def->count + 1) * sizeof(def->tab[0]));+        if (!new_tab)+            goto fail;+        def->tab = new_tab;+        def->count++;+        ent = def->tab + def->count - 1;+        memset(ent, 0, sizeof(def->tab[0]));+        ent->operator_index = op_count;+        +        for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+            prop = JS_GetPropertyStr(ctx, arg,+                                     js_overloadable_operator_names[i]);+            if (JS_IsException(prop))+                goto fail;+            if (!JS_IsUndefined(prop)) {+                if (check_function(ctx, prop)) {+                    JS_FreeValue(ctx, prop);+                    goto fail;+                }+                ent->ops[i] = JS_VALUE_GET_OBJ(prop);+            }+        }+    }+    opset->is_primitive = is_primitive;+    opset->operator_counter = ctx->rt->operator_count++;+    return opset_obj;+ fail:+    JS_FreeValue(ctx, opset_obj);+    return JS_EXCEPTION;+}++static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,+                                int argc, JSValueConst *argv)+{+    return js_operators_create_internal(ctx, argc, argv, FALSE);+}++static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,+                                                  int argc, JSValueConst *argv)+{+    JSValue opset_obj, prop;+    JSOperatorSetData *opset;+    const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };+    JSOverloadableOperatorEnum op;+    int i;+    +    opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],+                               JS_ATOM_Symbol_operatorSet);+    if (JS_IsException(opset_obj))+        goto fail;+    opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);+    if (!opset)+        goto fail;+    for(i = 0; i < countof(ops); i++) {+        op = ops[i];+        prop = JS_GetPropertyStr(ctx, argv[0],+                                 js_overloadable_operator_names[op]);+        if (JS_IsException(prop))+            goto fail;+        if (!JS_IsUndefined(prop)) {+            if (!JS_IsNull(prop) && check_function(ctx, prop)) {+                JS_FreeValue(ctx, prop);+                goto fail;+            }+            if (opset->self_ops[op])+                JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));+            if (JS_IsNull(prop)) {+                opset->self_ops[op] = NULL;+            } else {+                opset->self_ops[op] = JS_VALUE_GET_PTR(prop);+            }+        }+    }+    JS_FreeValue(ctx, opset_obj);+    return JS_UNDEFINED;+ fail:+    JS_FreeValue(ctx, opset_obj);+    return JS_EXCEPTION;+}++static int js_operators_set_default(JSContext *ctx, JSValueConst obj)+{+    JSValue opset_obj;++    if (!JS_IsObject(obj)) /* in case the prototype is not defined */+        return 0;+    opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);+    if (JS_IsException(opset_obj))+        return -1;+    /* cannot be modified by the user */+    JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,+                           opset_obj, 0);+    return 0;+}++static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,+                                       int argc, JSValueConst *argv)+{+    return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);+}++static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSValue func_obj, proto, opset_obj;++    func_obj = JS_UNDEFINED;+    proto = JS_NewObject(ctx);+    if (JS_IsException(proto))+        return JS_EXCEPTION;+    opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);+    if (JS_IsException(opset_obj))+        goto fail;+    JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,+                           opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",+                                0, JS_CFUNC_constructor, 0);+    if (JS_IsException(func_obj))+        goto fail;+    JS_SetConstructor2(ctx, func_obj, proto,+                       0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    JS_FreeValue(ctx, proto);+    return func_obj;+ fail:+    JS_FreeValue(ctx, proto);+    JS_FreeValue(ctx, func_obj);+    return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_operators_funcs[] = {+    JS_CFUNC_DEF("create", 1, js_operators_create ),+    JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),+};++/* must be called after all overloadable base types are initialized */+void JS_AddIntrinsicOperators(JSContext *ctx)+{+    JSValue obj;++    ctx->allow_operator_overloading = TRUE;+    obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);+    JS_SetPropertyFunctionList(ctx, obj,+                               js_operators_funcs,+                               countof(js_operators_funcs));+    JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,+                           obj,+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    /* add default operatorSets */+    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);+    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);+    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);+    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);+    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);+    js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);+}++/* BigInt */++static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)+{+    uint32_t tag;++ redo:+    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_INT:+    case JS_TAG_BOOL:+        val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));+        break;+    case JS_TAG_BIG_INT:+        break;+    case JS_TAG_FLOAT64:+    case JS_TAG_BIG_FLOAT:+        {+            bf_t *a, a_s;+            +            a = JS_ToBigFloat(ctx, &a_s, val);+            if (!bf_is_finite(a)) {+                JS_FreeValue(ctx, val);+                val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");+            } else {+                JSValue val1 = JS_NewBigInt(ctx);+                bf_t *r;+                int ret;+                if (JS_IsException(val1)) {+                    JS_FreeValue(ctx, val);+                    return JS_EXCEPTION;+                }+                r = JS_GetBigInt(val1);+                ret = bf_set(r, a);+                ret |= bf_rint(r, BF_RNDZ);+                JS_FreeValue(ctx, val);+                if (ret & BF_ST_MEM_ERROR) {+                    JS_FreeValue(ctx, val1);+                    val = JS_ThrowOutOfMemory(ctx);+                } else if (ret & BF_ST_INEXACT) {+                    JS_FreeValue(ctx, val1);+                    val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");+                } else {+                    val = JS_CompactBigInt(ctx, val1);+                }+            }+            if (a == &a_s)+                bf_delete(a);+        }+        break;+    case JS_TAG_BIG_DECIMAL:+        val = JS_ToStringFree(ctx, val);+         if (JS_IsException(val))+            break;+        goto redo;+    case JS_TAG_STRING:+        val = JS_StringToBigIntErr(ctx, val);+        break;+    case JS_TAG_OBJECT:+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+        if (JS_IsException(val))+            break;+        goto redo;+    case JS_TAG_NULL:+    case JS_TAG_UNDEFINED:+    default:+        JS_FreeValue(ctx, val);+        return JS_ThrowTypeError(ctx, "cannot convert to bigint");+    }+    return val;+}++static JSValue js_bigint_constructor(JSContext *ctx,+                                     JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));+}++static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_IsBigInt(ctx, this_val))+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_BIG_INT) {+            if (JS_IsBigInt(ctx, p->u.object_data))+                return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a bigint");+}++static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValue val;+    int base;+    JSValue ret;++    val = js_thisBigIntValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (argc == 0 || JS_IsUndefined(argv[0])) {+        base = 10;+    } else {+        base = js_get_radix(ctx, argv[0]);+        if (base < 0)+            goto fail;+    }+    ret = js_bigint_to_string1(ctx, val, base);+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    return js_thisBigIntValue(ctx, this_val);+}++static JSValue js_bigint_div(JSContext *ctx,+                              JSValueConst this_val,+                              int argc, JSValueConst *argv, int magic)+{+    bf_t a_s, b_s, *a, *b, *r, *q;+    int status;+    JSValue q_val, r_val;+    +    q_val = JS_NewBigInt(ctx);+    if (JS_IsException(q_val))+        return JS_EXCEPTION;+    r_val = JS_NewBigInt(ctx);+    if (JS_IsException(r_val))+        goto fail;+    b = NULL;+    a = JS_ToBigInt(ctx, &a_s, argv[0]);+    if (!a)+        goto fail;+    b = JS_ToBigInt(ctx, &b_s, argv[1]);+    if (!b) {+        JS_FreeBigInt(ctx, a, &a_s);+        goto fail;+    }+    q = JS_GetBigInt(q_val);+    r = JS_GetBigInt(r_val);+    status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);+    JS_FreeBigInt(ctx, a, &a_s);+    JS_FreeBigInt(ctx, b, &b_s);+    if (unlikely(status)) {+        throw_bf_exception(ctx, status);+        goto fail;+    }+    q_val = JS_CompactBigInt(ctx, q_val);+    if (magic & 0x10) {+        JSValue ret;+        ret = JS_NewArray(ctx);+        if (JS_IsException(ret))+            goto fail;+        JS_SetPropertyUint32(ctx, ret, 0, q_val);+        JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));+        return ret;+    } else {+        JS_FreeValue(ctx, r_val);+        return q_val;+    }+ fail:+    JS_FreeValue(ctx, q_val);+    JS_FreeValue(ctx, r_val);+    return JS_EXCEPTION;+}++static JSValue js_bigint_sqrt(JSContext *ctx,+                               JSValueConst this_val,+                               int argc, JSValueConst *argv, int magic)+{+    bf_t a_s, *a, *r, *rem;+    int status;+    JSValue r_val, rem_val;+    +    r_val = JS_NewBigInt(ctx);+    if (JS_IsException(r_val))+        return JS_EXCEPTION;+    rem_val = JS_NewBigInt(ctx);+    if (JS_IsException(rem_val))+        return JS_EXCEPTION;+    r = JS_GetBigInt(r_val);+    rem = JS_GetBigInt(rem_val);++    a = JS_ToBigInt(ctx, &a_s, argv[0]);+    if (!a)+        goto fail;+    status = bf_sqrtrem(r, rem, a);+    JS_FreeBigInt(ctx, a, &a_s);+    if (unlikely(status & ~BF_ST_INEXACT)) {+        throw_bf_exception(ctx, status);+        goto fail;+    }+    r_val = JS_CompactBigInt(ctx, r_val);+    if (magic) {+        JSValue ret;+        ret = JS_NewArray(ctx);+        if (JS_IsException(ret))+            goto fail;+        JS_SetPropertyUint32(ctx, ret, 0, r_val);+        JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));+        return ret;+    } else {+        JS_FreeValue(ctx, rem_val);+        return r_val;+    }+ fail:+    JS_FreeValue(ctx, r_val);+    JS_FreeValue(ctx, rem_val);+    return JS_EXCEPTION;+}++static JSValue js_bigint_op1(JSContext *ctx,+                              JSValueConst this_val,+                              int argc, JSValueConst *argv,+                              int magic)+{+    bf_t a_s, *a;+    int64_t res;++    a = JS_ToBigInt(ctx, &a_s, argv[0]);+    if (!a)+        return JS_EXCEPTION;+    switch(magic) {+    case 0: /* floorLog2 */+        if (a->sign || a->expn <= 0) {+            res = -1;+        } else {+            res = a->expn - 1;+        }+        break;+    case 1: /* ctz */+        if (bf_is_zero(a)) {+            res = -1;+        } else {+            res = bf_get_exp_min(a);+        }+        break;+    default:+        abort();+    }+    JS_FreeBigInt(ctx, a, &a_s);+    return JS_NewBigInt64(ctx, res);+}++static JSValue js_bigint_asUintN(JSContext *ctx,+                                  JSValueConst this_val,+                                  int argc, JSValueConst *argv, int asIntN)+{+    uint64_t bits;+    bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;+    JSValue res;+    +    if (JS_ToIndex(ctx, &bits, argv[0]))+        return JS_EXCEPTION;+    res = JS_NewBigInt(ctx);+    if (JS_IsException(res))+        return JS_EXCEPTION;+    r = JS_GetBigInt(res);+    a = JS_ToBigInt(ctx, &a_s, argv[1]);+    if (!a) {+        JS_FreeValue(ctx, res);+        return JS_EXCEPTION;+    }+    /* XXX: optimize */+    r = JS_GetBigInt(res);+    bf_init(ctx->bf_ctx, mask);+    bf_set_ui(mask, 1);+    bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);+    bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);+    bf_logic_and(r, a, mask);+    if (asIntN && bits != 0) {+        bf_set_ui(mask, 1);+        bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);+        if (bf_cmpu(r, mask) >= 0) {+            bf_set_ui(mask, 1);+            bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);+            bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);+        }+    }+    bf_delete(mask);+    JS_FreeBigInt(ctx, a, &a_s);+    return JS_CompactBigInt(ctx, res);+}++static const JSCFunctionListEntry js_bigint_funcs[] = {+    JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),+    JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),+    /* QuickJS extensions */+    JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),+    JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),+    JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),+    JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),+    JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),+    JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),+    JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),+    JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),+    JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),+    JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),+    JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),+    JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),+};++static const JSCFunctionListEntry js_bigint_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_bigint_toString ),+    JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicBigInt(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    JSValue obj1;++    rt->bigint_ops.to_string = js_bigint_to_string;+    rt->bigint_ops.from_string = js_string_to_bigint;+    rt->bigint_ops.unary_arith = js_unary_arith_bigint;+    rt->bigint_ops.binary_arith = js_binary_arith_bigint;+    rt->bigint_ops.compare = js_compare_bigfloat;+    +    ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],+                               js_bigint_proto_funcs,+                               countof(js_bigint_proto_funcs));+    obj1 = JS_NewCFunction(ctx, js_bigint_constructor, "BigInt", 1);+    JS_NewGlobalCConstructor2(ctx, obj1, "BigInt",+                              ctx->class_proto[JS_CLASS_BIG_INT]);+    JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,+                               countof(js_bigint_funcs));+}++/* BigFloat */++static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_IsBigFloat(this_val))+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_BIG_FLOAT) {+            if (JS_IsBigFloat(p->u.object_data))+                return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a bigfloat");+}++static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValue val;+    int base;+    JSValue ret;++    val = js_thisBigFloatValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (argc == 0 || JS_IsUndefined(argv[0])) {+        base = 10;+    } else {+        base = js_get_radix(ctx, argv[0]);+        if (base < 0)+            goto fail;+    }+    ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    return js_thisBigFloatValue(ctx, this_val);+}++static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)+{+    int rnd_mode;+    if (JS_ToInt32Sat(ctx, &rnd_mode, val))+        return -1;+    if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {+        JS_ThrowRangeError(ctx, "invalid rounding mode");+        return -1;+    }+    return rnd_mode;+}++static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue val, ret;+    int64_t f;+    int rnd_mode, radix;++    val = js_thisBigFloatValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToInt64Sat(ctx, &f, argv[0]))+        goto fail;+    if (f < 0 || f > BF_PREC_MAX) {+        JS_ThrowRangeError(ctx, "invalid number of digits");+        goto fail;+    }+    rnd_mode = BF_RNDNA;+    radix = 10;+    /* XXX: swap parameter order for rounding mode and radix */+    if (argc > 1) {+        rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);+        if (rnd_mode < 0)+            goto fail;+    }+    if (argc > 2) {+        radix = js_get_radix(ctx, argv[2]);+        if (radix < 0)+            goto fail;+    }+    ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)+{+    BOOL res;+    uint32_t tag;++    tag = JS_VALUE_GET_NORM_TAG(val);+    switch(tag) {+    case JS_TAG_BIG_FLOAT:+        {+            JSBigFloat *p = JS_VALUE_GET_PTR(val);+            res = bf_is_finite(&p->num);+        }+        break;+    default:+        res = FALSE;+        break;+    }+    return res;+}++static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValue val, ret;+    int64_t f;+    int rnd_mode, radix;++    val = js_thisBigFloatValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToInt64Sat(ctx, &f, argv[0]))+        goto fail;+    if (!js_bigfloat_is_finite(ctx, val)) {+        ret = JS_ToString(ctx, val);+    } else if (JS_IsUndefined(argv[0])) {+        ret = js_ftoa(ctx, val, 10, 0,+                      BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);+    } else {+        if (f < 0 || f > BF_PREC_MAX) {+            JS_ThrowRangeError(ctx, "invalid number of digits");+            goto fail;+        }+        rnd_mode = BF_RNDNA;+        radix = 10;+        if (argc > 1) {+            rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);+            if (rnd_mode < 0)+                goto fail;+        }+        if (argc > 2) {+            radix = js_get_radix(ctx, argv[2]);+            if (radix < 0)+                goto fail;+        }+        ret = js_ftoa(ctx, val, radix, f + 1,+                      rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);+    }+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSValue val, ret;+    int64_t p;+    int rnd_mode, radix;++    val = js_thisBigFloatValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_IsUndefined(argv[0]))+        goto to_string;+    if (JS_ToInt64Sat(ctx, &p, argv[0]))+        goto fail;+    if (!js_bigfloat_is_finite(ctx, val)) {+    to_string:+        ret = JS_ToString(ctx, this_val);+    } else {+        if (p < 1 || p > BF_PREC_MAX) {+            JS_ThrowRangeError(ctx, "invalid number of digits");+            goto fail;+        }+        rnd_mode = BF_RNDNA;+        radix = 10;+        if (argc > 1) {+            rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);+            if (rnd_mode < 0)+                goto fail;+        }+        if (argc > 2) {+            radix = js_get_radix(ctx, argv[2]);+            if (radix < 0)+                goto fail;+        }+        ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);+    }+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),+    JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),+    JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),+    JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),+    JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),+};++static JSValue js_bigfloat_constructor(JSContext *ctx,+                                       JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValue val;+    if (argc == 0) {+        bf_t *r;+        val = JS_NewBigFloat(ctx);+        if (JS_IsException(val))+            return val;+        r = JS_GetBigFloat(val);+        bf_set_zero(r, 0);+    } else {+        val = JS_DupValue(ctx, argv[0]);+    redo:+        switch(JS_VALUE_GET_NORM_TAG(val)) {+        case JS_TAG_BIG_FLOAT:+            break;+        case JS_TAG_FLOAT64:+            {+                bf_t *r;+                double d = JS_VALUE_GET_FLOAT64(val);+                val = JS_NewBigFloat(ctx);+                if (JS_IsException(val))+                    break;+                r = JS_GetBigFloat(val);+                if (bf_set_float64(r, d))+                    goto fail;+            }+            break;+        case JS_TAG_INT:+            {+                bf_t *r;+                int32_t v = JS_VALUE_GET_INT(val);+                val = JS_NewBigFloat(ctx);+                if (JS_IsException(val))+                    break;+                r = JS_GetBigFloat(val);+                if (bf_set_si(r, v))+                    goto fail;+            }+            break;+        case JS_TAG_BIG_INT:+            /* We keep the full precision of the integer */+            {+                JSBigFloat *p = JS_VALUE_GET_PTR(val);+                val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);+            }+            break;+        case JS_TAG_BIG_DECIMAL:+            val = JS_ToStringFree(ctx, val);+            if (JS_IsException(val))+                break;+            goto redo;+        case JS_TAG_STRING:+            {+                const char *str, *p;+                size_t len;+                int err;++                str = JS_ToCStringLen(ctx, &len, val);+                JS_FreeValue(ctx, val);+                if (!str)+                    return JS_EXCEPTION;+                p = str;+                p += skip_spaces(p);+                if ((p - str) == len) {+                    bf_t *r;+                    val = JS_NewBigFloat(ctx);+                    if (JS_IsException(val))+                        break;+                    r = JS_GetBigFloat(val);+                    bf_set_zero(r, 0);+                    err = 0;+                } else {+                    val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |+                                  ATOD_TYPE_BIG_FLOAT |+                                  ATOD_ACCEPT_PREFIX_AFTER_SIGN);+                    if (JS_IsException(val)) {+                        JS_FreeCString(ctx, str);+                        return JS_EXCEPTION;+                    }+                    p += skip_spaces(p);+                    err = ((p - str) != len);+                }+                JS_FreeCString(ctx, str);+                if (err) {+                    JS_FreeValue(ctx, val);+                    return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");+                }+            }+            break;+        case JS_TAG_OBJECT:+            val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+            if (JS_IsException(val))+                break;+            goto redo;+        case JS_TAG_NULL:+        case JS_TAG_UNDEFINED:+        default:+            JS_FreeValue(ctx, val);+            return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");+        }+    }+    return val;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_bigfloat_get_const(JSContext *ctx,+                                     JSValueConst this_val, int magic)+{+    bf_t *r;+    JSValue val;+    val = JS_NewBigFloat(ctx);+    if (JS_IsException(val))+        return val;+    r = JS_GetBigFloat(val);+    switch(magic) {+    case 0: /* PI */+        bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case 1: /* LN2 */+        bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    case 2: /* MIN_VALUE */+    case 3: /* MAX_VALUE */+        {+            slimb_t e_range, e;+            e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);+            bf_set_ui(r, 1);+            if (magic == 2) {+                e = -e_range + 2;+                if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)+                    e -= ctx->fp_env.prec - 1;+                bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);+            } else {+                bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,+                            ctx->fp_env.flags);+                bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);+                bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,+                            ctx->fp_env.flags);+            }+        }+        break;+    case 4: /* EPSILON */+        bf_set_ui(r, 1);+        bf_mul_2exp(r, 1 - ctx->fp_env.prec,+                    ctx->fp_env.prec, ctx->fp_env.flags);+        break;+    default:+        abort();+    }+    return val;+}++static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    bf_t *a;+    const char *str;+    JSValue ret;+    int radix;+    JSFloatEnv *fe;++    str = JS_ToCString(ctx, argv[0]);+    if (!str)+        return JS_EXCEPTION;+    if (JS_ToInt32(ctx, &radix, argv[1])) {+    fail:+        JS_FreeCString(ctx, str);+        return JS_EXCEPTION;+    }+    if (radix != 0 && (radix < 2 || radix > 36)) {+        JS_ThrowRangeError(ctx, "radix must be between 2 and 36");+        goto fail;+    }+    fe = &ctx->fp_env;+    if (argc > 2) {+        fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);+        if (!fe)+            goto fail;+    }+    ret = JS_NewBigFloat(ctx);+    if (JS_IsException(ret))+        goto done;+    a = JS_GetBigFloat(ret);+    /* XXX: use js_atof() */+    bf_atof(a, str, NULL, radix, fe->prec, fe->flags);+ done:+    JS_FreeCString(ctx, str);+    return ret;+}++static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValueConst val = argv[0];+    JSBigFloat *p;+    +    if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)+        return JS_FALSE;+    p = JS_VALUE_GET_PTR(val);+    return JS_NewBool(ctx, bf_is_finite(&p->num));+}++static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValueConst val = argv[0];+    JSBigFloat *p;+    +    if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)+        return JS_FALSE;+    p = JS_VALUE_GET_PTR(val);+    return JS_NewBool(ctx, bf_is_nan(&p->num));+}++enum {+    MATH_OP_ABS,+    MATH_OP_FLOOR,+    MATH_OP_CEIL,+    MATH_OP_ROUND,+    MATH_OP_TRUNC,+    MATH_OP_SQRT,+    MATH_OP_FPROUND,+    MATH_OP_ACOS,+    MATH_OP_ASIN,+    MATH_OP_ATAN,+    MATH_OP_ATAN2,+    MATH_OP_COS,+    MATH_OP_EXP,+    MATH_OP_LOG,+    MATH_OP_POW,+    MATH_OP_SIN,+    MATH_OP_TAN,+    MATH_OP_FMOD,+    MATH_OP_REM,+    MATH_OP_SIGN,++    MATH_OP_ADD,+    MATH_OP_SUB,+    MATH_OP_MUL,+    MATH_OP_DIV,+};++static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,+                           int argc, JSValueConst *argv, int magic)+{+    bf_t a_s, *a, *r;+    JSFloatEnv *fe;+    int rnd_mode;+    JSValue op1, res;++    op1 = JS_ToNumeric(ctx, argv[0]);+    if (JS_IsException(op1))+        return op1;+    a = JS_ToBigFloat(ctx, &a_s, op1);+    fe = &ctx->fp_env;+    if (argc > 1) {+        fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);+        if (!fe)+            goto fail;+    }+    res = JS_NewBigFloat(ctx);+    if (JS_IsException(res)) {+    fail:+        if (a == &a_s)+            bf_delete(a);+        JS_FreeValue(ctx, op1);+        return JS_EXCEPTION;+    }+    r = JS_GetBigFloat(res);+    switch (magic) {+    case MATH_OP_ABS:+        bf_set(r, a);+        r->sign = 0;+        break;+    case MATH_OP_FLOOR:+        rnd_mode = BF_RNDD;+        goto rint;+    case MATH_OP_CEIL:+        rnd_mode = BF_RNDU;+        goto rint;+    case MATH_OP_ROUND:+        rnd_mode = BF_RNDNA;+        goto rint;+    case MATH_OP_TRUNC:+        rnd_mode = BF_RNDZ;+    rint:+        bf_set(r, a);+        fe->status |= bf_rint(r, rnd_mode);+        break;+    case MATH_OP_SQRT:+        fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_FPROUND:+        bf_set(r, a);+        fe->status |= bf_round(r, fe->prec, fe->flags);+        break;+    case MATH_OP_ACOS:+        fe->status |= bf_acos(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_ASIN:+        fe->status |= bf_asin(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_ATAN:+        fe->status |= bf_atan(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_COS:+        fe->status |= bf_cos(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_EXP:+        fe->status |= bf_exp(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_LOG:+        fe->status |= bf_log(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_SIN:+        fe->status |= bf_sin(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_TAN:+        fe->status |= bf_tan(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_SIGN:+        if (bf_is_nan(a) || bf_is_zero(a)) {+            bf_set(r, a);+        } else {+            bf_set_si(r, 1 - 2 * a->sign);+        }+        break;+    default:+        abort();+    }+    if (a == &a_s)+        bf_delete(a);+    JS_FreeValue(ctx, op1);+    return res;+}++static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,+                            int argc, JSValueConst *argv, int magic)+{+    bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;+    JSFloatEnv *fe;+    JSValue op1, op2, res;++    op1 = JS_ToNumeric(ctx, argv[0]);+    if (JS_IsException(op1))+        return op1;+    op2 = JS_ToNumeric(ctx, argv[1]);+    if (JS_IsException(op2)) {+        JS_FreeValue(ctx, op1);+        return op2;+    }+    a = JS_ToBigFloat(ctx, &a_s, op1);+    b = JS_ToBigFloat(ctx, &b_s, op2);+    fe = &ctx->fp_env;+    if (argc > 2) {+        fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);+        if (!fe)+            goto fail;+    }+    res = JS_NewBigFloat(ctx);+    if (JS_IsException(res)) {+    fail:+        if (a == &a_s)+            bf_delete(a);+        if (b == &b_s)+            bf_delete(b);+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        return JS_EXCEPTION;+    }+    r = JS_GetBigFloat(res);+    switch (magic) {+    case MATH_OP_ATAN2:+        fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_POW:+        fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);+        break;+    case MATH_OP_FMOD:+        fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);+        break;+    case MATH_OP_REM:+        fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);+        break;+    case MATH_OP_ADD:+        fe->status |= bf_add(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_SUB:+        fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_MUL:+        fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_DIV:+        fe->status |= bf_div(r, a, b, fe->prec, fe->flags);+        break;+    default:+        abort();+    }+    if (a == &a_s)+        bf_delete(a);+    if (b == &b_s)+        bf_delete(b);+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    return res;+}++static const JSCFunctionListEntry js_bigfloat_funcs[] = {+    JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),+    JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),+    JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),+    JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),+    JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),+    JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),+    JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),+    JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),+    JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),+    JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),+    JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),+    JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),+    JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),+    JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),+    JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),+    JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),+    JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),+    JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),+    JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),+    JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),+    JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),+    JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),+    JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),+    JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),+    JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),+    JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),+    JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),+    JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),+    JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),+    JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),+    JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),+    JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),+};++/* FloatEnv */++static JSValue js_float_env_constructor(JSContext *ctx,+                                        JSValueConst new_target,+                                        int argc, JSValueConst *argv)+{+    JSValue obj;+    JSFloatEnv *fe;+    int64_t prec;+    int flags, rndmode;++    prec = ctx->fp_env.prec;+    flags = ctx->fp_env.flags;+    if (!JS_IsUndefined(argv[0])) {+        if (JS_ToInt64Sat(ctx, &prec, argv[0]))+            return JS_EXCEPTION;+        if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)+            return JS_ThrowRangeError(ctx, "invalid precision");+        flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */+        if (argc > 1 && !JS_IsUndefined(argv[1])) {+            if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))+                return JS_EXCEPTION;+            if (rndmode < BF_RNDN || rndmode > BF_RNDF)+                return JS_ThrowRangeError(ctx, "invalid rounding mode");+            flags = rndmode;+        }+    }++    obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    fe = js_malloc(ctx, sizeof(*fe));+    if (!fe)+        return JS_EXCEPTION;+    fe->prec = prec;+    fe->flags = flags;+    fe->status = 0;+    JS_SetOpaque(obj, fe);+    return obj;+}++static void js_float_env_finalizer(JSRuntime *rt, JSValue val)+{+    JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);+    js_free_rt(rt, fe);+}++static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)+{+    return JS_NewInt64(ctx, ctx->fp_env.prec);+}++static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)+{+    return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));+}++static JSValue js_float_env_setPrec(JSContext *ctx,+                                    JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValueConst func;+    int exp_bits, flags, saved_flags;+    JSValue ret;+    limb_t saved_prec;+    int64_t prec;++    func = argv[0];+    if (JS_ToInt64Sat(ctx, &prec, argv[1]))+        return JS_EXCEPTION;+    if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)+        return JS_ThrowRangeError(ctx, "invalid precision");+    exp_bits = BF_EXP_BITS_MAX;++    if (argc > 2 && !JS_IsUndefined(argv[2])) {+        if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))+            return JS_EXCEPTION;+        if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)+            return JS_ThrowRangeError(ctx, "invalid number of exponent bits");+    }++    flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);++    saved_prec = ctx->fp_env.prec;+    saved_flags = ctx->fp_env.flags;++    ctx->fp_env.prec = prec;+    ctx->fp_env.flags = flags;++    ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);+    /* always restore the floating point precision */+    ctx->fp_env.prec = saved_prec;+    ctx->fp_env.flags = saved_flags;+    return ret;+}++#define FE_PREC      (-1)+#define FE_EXP       (-2)+#define FE_RNDMODE   (-3)+#define FE_SUBNORMAL (-4)++static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)+{+    JSFloatEnv *fe;+    fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);+    if (!fe)+        return JS_EXCEPTION;+    switch(magic) {+    case FE_PREC:+        return JS_NewInt64(ctx, fe->prec);+    case FE_EXP:+        return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));+    case FE_RNDMODE:+        return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);+    case FE_SUBNORMAL:+        return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);+    default:+        return JS_NewBool(ctx, (fe->status & magic) != 0);+    }+}++static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)+{+    JSFloatEnv *fe;+    int b;+    int64_t prec;++    fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);+    if (!fe)+        return JS_EXCEPTION;+    switch(magic) {+    case FE_PREC:+        if (JS_ToInt64Sat(ctx, &prec, val))+            return JS_EXCEPTION;+        if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)+            return JS_ThrowRangeError(ctx, "invalid precision");+        fe->prec = prec;+        break;+    case FE_EXP:+        if (JS_ToInt32Sat(ctx, &b, val))+            return JS_EXCEPTION;+        if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)+            return JS_ThrowRangeError(ctx, "invalid number of exponent bits");+        fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |+            bf_set_exp_bits(b);+        break;+    case FE_RNDMODE:+        b = bigfloat_get_rnd_mode(ctx, val);+        if (b < 0)+            return JS_EXCEPTION;+        fe->flags = (fe->flags & ~BF_RND_MASK) | b;+        break;+    case FE_SUBNORMAL:+        b = JS_ToBool(ctx, val);+        fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);+        break;+    default:+        b = JS_ToBool(ctx, val);+        fe->status = (fe->status & ~magic) & ((-b) & magic);+        break;+    }+    return JS_UNDEFINED;+}++static JSValue js_float_env_clearStatus(JSContext *ctx,+                                        JSValueConst this_val,+                                        int argc, JSValueConst *argv)+{+    JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);+    if (!fe)+        return JS_EXCEPTION;+    fe->status = 0;+    return JS_UNDEFINED;+}++static const JSCFunctionListEntry js_float_env_funcs[] = {+    JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),+    JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),+    JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),+    JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),+    JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),+    JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),+    JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),+    JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),+    JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),+    JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),+    JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),+    JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),+    JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),+    JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),+};++static const JSCFunctionListEntry js_float_env_proto_funcs[] = {+    JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, FE_PREC ),+    JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, FE_EXP ),+    JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, FE_RNDMODE ),+    JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, FE_SUBNORMAL ),+    JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, BF_ST_INVALID_OP ),+    JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),+    JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, BF_ST_OVERFLOW ),+    JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, BF_ST_UNDERFLOW ),+    JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,+                         js_float_env_proto_set_status, BF_ST_INEXACT ),+    JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),+};++void JS_AddIntrinsicBigFloat(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    JSValue obj1;+    JSValueConst obj2;+    +    rt->bigfloat_ops.to_string = js_bigfloat_to_string;+    rt->bigfloat_ops.from_string = js_string_to_bigfloat;+    rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;+    rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;+    rt->bigfloat_ops.compare = js_compare_bigfloat;+    rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;+    rt->bigfloat_ops.mul_pow10 = js_mul_pow10;+    +    ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],+                               js_bigfloat_proto_funcs,+                               countof(js_bigfloat_proto_funcs));+    obj1 = JS_NewCFunction(ctx, js_bigfloat_constructor, "BigFloat", 1);+    JS_NewGlobalCConstructor2(ctx, obj1, "BigFloat",+                              ctx->class_proto[JS_CLASS_BIG_FLOAT]);+    JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,+                               countof(js_bigfloat_funcs));++    ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],+                               js_float_env_proto_funcs,+                               countof(js_float_env_proto_funcs));+    obj2 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",+                                        js_float_env_constructor, 1,+                                        ctx->class_proto[JS_CLASS_FLOAT_ENV]);+    JS_SetPropertyFunctionList(ctx, obj2, js_float_env_funcs,+                               countof(js_float_env_funcs));+}++/* BigDecimal */++static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,+                                   BOOL allow_null_or_undefined)+{+ redo:+    switch(JS_VALUE_GET_NORM_TAG(val)) {+    case JS_TAG_BIG_DECIMAL:+        break;+    case JS_TAG_NULL:+        if (!allow_null_or_undefined)+            goto fail;+        /* fall thru */+    case JS_TAG_BOOL:+    case JS_TAG_INT:+        {+            bfdec_t *r;+            int32_t v = JS_VALUE_GET_INT(val);++            val = JS_NewBigDecimal(ctx);+            if (JS_IsException(val))+                break;+            r = JS_GetBigDecimal(val);+            if (bfdec_set_si(r, v)) {+                JS_FreeValue(ctx, val);+                val = JS_EXCEPTION;+                break;+            }+        }+        break;+    case JS_TAG_FLOAT64:+    case JS_TAG_BIG_INT:+    case JS_TAG_BIG_FLOAT:+        val = JS_ToStringFree(ctx, val);+        if (JS_IsException(val))+            break;+        goto redo;+    case JS_TAG_STRING:+        {+            const char *str, *p;+            size_t len;+            int err;++            str = JS_ToCStringLen(ctx, &len, val);+            JS_FreeValue(ctx, val);+            if (!str)+                return JS_EXCEPTION;+            p = str;+            p += skip_spaces(p);+            if ((p - str) == len) {+                bfdec_t *r;+                val = JS_NewBigDecimal(ctx);+                if (JS_IsException(val))+                    break;+                r = JS_GetBigDecimal(val);+                bfdec_set_zero(r, 0);+                err = 0;+            } else {+                val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);+                if (JS_IsException(val)) {+                    JS_FreeCString(ctx, str);+                    return JS_EXCEPTION;+                }+                p += skip_spaces(p);+                err = ((p - str) != len);+            }+            JS_FreeCString(ctx, str);+            if (err) {+                JS_FreeValue(ctx, val);+                return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");+            }+        }+        break;+    case JS_TAG_OBJECT:+        val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+        if (JS_IsException(val))+            break;+        goto redo;+    case JS_TAG_UNDEFINED:+        {+            bfdec_t *r;+            if (!allow_null_or_undefined)+                goto fail;+            val = JS_NewBigDecimal(ctx);+            if (JS_IsException(val))+                break;+            r = JS_GetBigDecimal(val);+            bfdec_set_nan(r);+        }+        break;+    default:+    fail:+        JS_FreeValue(ctx, val);+        return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");+    }+    return val;+}++static JSValue js_bigdecimal_constructor(JSContext *ctx,+                                         JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    JSValue val;+    if (argc == 0) {+        bfdec_t *r;+        val = JS_NewBigDecimal(ctx);+        if (JS_IsException(val))+            return val;+        r = JS_GetBigDecimal(val);+        bfdec_set_zero(r, 0);+    } else {+        val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);+    }+    return val;+}++static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)+{+    if (JS_IsBigDecimal(this_val))+        return JS_DupValue(ctx, this_val);++    if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+        JSObject *p = JS_VALUE_GET_OBJ(this_val);+        if (p->class_id == JS_CLASS_BIG_DECIMAL) {+            if (JS_IsBigDecimal(p->u.object_data))+                return JS_DupValue(ctx, p->u.object_data);+        }+    }+    return JS_ThrowTypeError(ctx, "not a bigdecimal");+}++static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    JSValue val;++    val = js_thisBigDecimalValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    return JS_ToStringFree(ctx, val);+}++static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    return js_thisBigDecimalValue(ctx, this_val);+}++static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)+{+    const char *str;+    size_t size;+    int rnd_mode;+    +    str = JS_ToCStringLen(ctx, &size, obj);+    if (!str)+        return -1;+    if (strlen(str) != size)+        goto invalid_rounding_mode;+    if (!strcmp(str, "floor")) {+        rnd_mode = BF_RNDD;+    } else if (!strcmp(str, "ceiling")) {+        rnd_mode = BF_RNDU;+    } else if (!strcmp(str, "down")) {+        rnd_mode = BF_RNDZ;+    } else if (!strcmp(str, "up")) {+        rnd_mode = BF_RNDA;+    } else if (!strcmp(str, "half-even")) {+        rnd_mode = BF_RNDN;+    } else if (!strcmp(str, "half-up")) {+        rnd_mode = BF_RNDNA;+    } else {+    invalid_rounding_mode:+        JS_FreeCString(ctx, str);+        JS_ThrowTypeError(ctx, "invalid rounding mode");+        return -1;+    }+    JS_FreeCString(ctx, str);+    return rnd_mode;+}++typedef struct {+    int64_t prec;+    bf_flags_t flags;+} BigDecimalEnv;++static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,+                                 JSValueConst obj)+{+    JSValue prop;+    int64_t val;+    BOOL has_prec;+    int rnd_mode;+    +    if (!JS_IsObject(obj)) {+        JS_ThrowTypeErrorNotAnObject(ctx);+        return -1;+    }+    prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);+    if (JS_IsException(prop))+        return -1;+    rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);+    JS_FreeValue(ctx, prop);+    if (rnd_mode < 0)+        return -1;+    fe->flags = rnd_mode;+    +    prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);+    if (JS_IsException(prop))+        return -1;+    has_prec = FALSE;+    if (!JS_IsUndefined(prop)) {+        if (JS_ToInt64SatFree(ctx, &val, prop))+            return -1;+        if (val < 1 || val > BF_PREC_MAX)+            goto invalid_precision;+        fe->prec = val;+        has_prec = TRUE;+    }++    prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);+    if (JS_IsException(prop))+        return -1;+    if (!JS_IsUndefined(prop)) {+        if (has_prec) {+            JS_FreeValue(ctx, prop);+            JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");+            return -1;+        }+        if (JS_ToInt64SatFree(ctx, &val, prop))+            return -1;+        if (val < 0 || val > BF_PREC_MAX) {+        invalid_precision:+            JS_ThrowTypeError(ctx, "invalid precision");+            return -1;+        }+        fe->prec = val;+        fe->flags |= BF_FLAG_RADPNT_PREC;+        has_prec = TRUE;+    }+    if (!has_prec) {+        JS_ThrowTypeError(ctx, "precision must be present");+        return -1;+    }+    return 0;+}+++static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv, int magic)+{+    bfdec_t *a, *b, r_s, *r = &r_s;+    JSValue op1, op2, res;+    BigDecimalEnv fe_s, *fe = &fe_s;+    int op_count, ret;++    if (magic == MATH_OP_SQRT ||+        magic == MATH_OP_ROUND)+        op_count = 1;+    else+        op_count = 2;+    +    op1 = JS_ToNumeric(ctx, argv[0]);+    if (JS_IsException(op1))+        return op1;+    a = JS_ToBigDecimal(ctx, op1);+    if (!a) {+        JS_FreeValue(ctx, op1);+        return JS_EXCEPTION;+    }+    if (op_count >= 2) {+        op2 = JS_ToNumeric(ctx, argv[1]);+        if (JS_IsException(op2)) {+            JS_FreeValue(ctx, op1);+            return op2;+        }+        b = JS_ToBigDecimal(ctx, op2);+        if (!b)+            goto fail;+    } else {+        op2 = JS_UNDEFINED;+        b = NULL;+    }+    fe->flags = BF_RNDZ;+    fe->prec = BF_PREC_INF;+    if (op_count < argc) {+        if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))+            goto fail;+    }++    res = JS_NewBigDecimal(ctx);+    if (JS_IsException(res)) {+    fail:+        JS_FreeValue(ctx, op1);+        JS_FreeValue(ctx, op2);+        return JS_EXCEPTION;+    }+    r = JS_GetBigDecimal(res);+    switch (magic) {+    case MATH_OP_ADD:+        ret = bfdec_add(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_SUB:+        ret = bfdec_sub(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_MUL:+        ret = bfdec_mul(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_DIV:+        ret = bfdec_div(r, a, b, fe->prec, fe->flags);+        break;+    case MATH_OP_FMOD:+        ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);+        break;+    case MATH_OP_SQRT:+        ret = bfdec_sqrt(r, a, fe->prec, fe->flags);+        break;+    case MATH_OP_ROUND:+        ret = bfdec_set(r, a);+        if (!(ret & BF_ST_MEM_ERROR))+            ret = bfdec_round(r, fe->prec, fe->flags);+        break;+    default:+        abort();+    }+    JS_FreeValue(ctx, op1);+    JS_FreeValue(ctx, op2);+    ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |+        BF_ST_OVERFLOW;+    if (ret != 0) {+        JS_FreeValue(ctx, res);+        return throw_bf_exception(ctx, ret);+    } else {+        return res;+    }+}++static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue val, ret;+    int64_t f;+    int rnd_mode;++    val = js_thisBigDecimalValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToInt64Sat(ctx, &f, argv[0]))+        goto fail;+    if (f < 0 || f > BF_PREC_MAX) {+        JS_ThrowRangeError(ctx, "invalid number of digits");+        goto fail;+    }+    rnd_mode = BF_RNDNA;+    if (argc > 1) {+        rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);+        if (rnd_mode < 0)+            goto fail;+    }+    ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValue val, ret;+    int64_t f;+    int rnd_mode;++    val = js_thisBigDecimalValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_ToInt64Sat(ctx, &f, argv[0]))+        goto fail;+    if (JS_IsUndefined(argv[0])) {+        ret = js_bigdecimal_to_string1(ctx, val, 0,+                  BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);+    } else {+        if (f < 0 || f > BF_PREC_MAX) {+            JS_ThrowRangeError(ctx, "invalid number of digits");+            goto fail;+        }+        rnd_mode = BF_RNDNA;+        if (argc > 1) {+            rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);+            if (rnd_mode < 0)+                goto fail;+        }+        ret = js_bigdecimal_to_string1(ctx, val, f + 1,+                      rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);+    }+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,+                                     int argc, JSValueConst *argv)+{+    JSValue val, ret;+    int64_t p;+    int rnd_mode;++    val = js_thisBigDecimalValue(ctx, this_val);+    if (JS_IsException(val))+        return val;+    if (JS_IsUndefined(argv[0])) {+        return JS_ToStringFree(ctx, val);+    }+    if (JS_ToInt64Sat(ctx, &p, argv[0]))+        goto fail;+    if (p < 1 || p > BF_PREC_MAX) {+        JS_ThrowRangeError(ctx, "invalid number of digits");+        goto fail;+    }+    rnd_mode = BF_RNDNA;+    if (argc > 1) {+        rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);+        if (rnd_mode < 0)+            goto fail;+    }+    ret = js_bigdecimal_to_string1(ctx, val, p,+                                   rnd_mode | BF_FTOA_FORMAT_FIXED);+    JS_FreeValue(ctx, val);+    return ret;+ fail:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {+    JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),+    JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),+    JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),+    JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),+    JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),+};++static const JSCFunctionListEntry js_bigdecimal_funcs[] = {+    JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),+    JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),+    JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),+    JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),+    JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),+    JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),+    JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),+};++void JS_AddIntrinsicBigDecimal(JSContext *ctx)+{+    JSRuntime *rt = ctx->rt;+    JSValue obj1;++    rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;+    rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;+    rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;+    rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;+    rt->bigdecimal_ops.compare = js_compare_bigdecimal;++    ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],+                               js_bigdecimal_proto_funcs,+                               countof(js_bigdecimal_proto_funcs));+    obj1 = JS_NewCFunction(ctx, js_bigdecimal_constructor, "BigDecimal", 1);+    JS_NewGlobalCConstructor2(ctx, obj1, "BigDecimal",+                              ctx->class_proto[JS_CLASS_BIG_DECIMAL]);+    JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,+                               countof(js_bigdecimal_funcs));+}++void JS_EnableBignumExt(JSContext *ctx, BOOL enable)+{+    ctx->bignum_ext = enable;+}++#endif /* CONFIG_BIGNUM */++static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {+    "EvalError", "RangeError", "ReferenceError",+    "SyntaxError", "TypeError", "URIError",+    "InternalError", "AggregateError",+};++/* Minimum amount of objects to be able to compile code and display+   error messages. No JSAtom should be allocated by this function. */+static void JS_AddIntrinsicBasicObjects(JSContext *ctx)+{+    JSValue proto;+    int i;++    ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);+    ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,+                                           JS_CFUNC_generic, 0,+                                           ctx->class_proto[JS_CLASS_OBJECT]);+    ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);+    ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);+#if 0+    /* these are auto-initialized from js_error_proto_funcs,+       but delaying might be a problem */+    JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,+                           JS_AtomToString(ctx, JS_ATOM_Error),+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+    JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,+                           JS_AtomToString(ctx, JS_ATOM_empty_string),+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+#endif+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],+                               js_error_proto_funcs,+                               countof(js_error_proto_funcs));++    for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+        proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);+        JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,+                               JS_NewAtomString(ctx, native_error_name[i]),+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+        JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,+                               JS_AtomToString(ctx, JS_ATOM_empty_string),+                               JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+        ctx->native_error_proto[i] = proto;+    }++    /* the array prototype is an array */+    ctx->class_proto[JS_CLASS_ARRAY] =+        JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                               JS_CLASS_ARRAY);++    ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),+                                     JS_PROP_INITIAL_HASH_SIZE, 1);+    add_shape_property(ctx, &ctx->array_shape, NULL,+                       JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);++    /* XXX: could test it on first context creation to ensure that no+       new atoms are created in JS_AddIntrinsicBasicObjects(). It is+       necessary to avoid useless renumbering of atoms after+       JS_EvalBinary() if it is done just after+       JS_AddIntrinsicBasicObjects(). */+    //    assert(ctx->rt->atom_count == JS_ATOM_END);+}++void JS_AddIntrinsicBaseObjects(JSContext *ctx)+{+    int i;+    JSValueConst obj, number_obj;+    JSValue obj1;++    ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);++    /* add caller and arguments properties to throw a TypeError */+    obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);+    JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,+                      obj1, ctx->throw_type_error,+                      JS_PROP_HAS_GET | JS_PROP_HAS_SET |+                      JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);+    JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,+                      obj1, ctx->throw_type_error,+                      JS_PROP_HAS_GET | JS_PROP_HAS_SET |+                      JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);+    JS_FreeValue(ctx, obj1);+    JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));++    ctx->global_obj = JS_NewObject(ctx);+    ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);++    /* Object */+    obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,+                                   ctx->class_proto[JS_CLASS_OBJECT]);+    JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                               js_object_proto_funcs, countof(js_object_proto_funcs));++    /* Function */+    JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));+    ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,+                                              "Function", 1, JS_CFUNC_constructor_or_func_magic,+                                              JS_FUNC_NORMAL);+    JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",+                              ctx->function_proto);++    /* Error */+    obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,+                                "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);+    JS_NewGlobalCConstructor2(ctx, obj1,+                              "Error", ctx->class_proto[JS_CLASS_ERROR]);++    for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+        JSValue func_obj;+        int n_args;+        n_args = 1 + (i == JS_AGGREGATE_ERROR);+        func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,+                                    native_error_name[i], n_args,+                                    JS_CFUNC_constructor_or_func_magic, i, obj1);+        JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],+                                  ctx->native_error_proto[i]);+    }++    /* Iterator prototype */+    ctx->iterator_proto = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,+                               js_iterator_proto_funcs,+                               countof(js_iterator_proto_funcs));++    /* Array */+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],+                               js_array_proto_funcs,+                               countof(js_array_proto_funcs));++    obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,+                                   ctx->class_proto[JS_CLASS_ARRAY]);+    ctx->array_ctor = JS_DupValue(ctx, obj);+    JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,+                               countof(js_array_funcs));++    /* XXX: create auto_initializer */+    {+        /* initialize Array.prototype[Symbol.unscopables] */+        char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"+            "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";+        const char *p = unscopables;+        obj1 = JS_NewObjectProto(ctx, JS_NULL);+        for(p = unscopables; *p; p += strlen(p) + 1) {+            JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);+        }+        JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],+                               JS_ATOM_Symbol_unscopables, obj1,+                               JS_PROP_CONFIGURABLE);+    }++    /* needed to initialize arguments[Symbol.iterator] */+    ctx->array_proto_values =+        JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);++    ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],+                               js_array_iterator_proto_funcs,+                               countof(js_array_iterator_proto_funcs));++    /* parseFloat and parseInteger must be defined before Number+       because of the Number.parseFloat and Number.parseInteger+       aliases */+    JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,+                               countof(js_global_funcs));++    /* Number */+    ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                                                               JS_CLASS_NUMBER);+    JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],+                               js_number_proto_funcs,+                               countof(js_number_proto_funcs));+    number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,+                                          ctx->class_proto[JS_CLASS_NUMBER]);+    JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));++    /* Boolean */+    ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                                                                JS_CLASS_BOOLEAN);+    JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,+                               countof(js_boolean_proto_funcs));+    JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,+                             ctx->class_proto[JS_CLASS_BOOLEAN]);++    /* String */+    ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+                                                               JS_CLASS_STRING);+    JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));+    obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,+                                   ctx->class_proto[JS_CLASS_STRING]);+    JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,+                               countof(js_string_funcs));+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,+                               countof(js_string_proto_funcs));++    ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],+                               js_string_iterator_proto_funcs,+                               countof(js_string_iterator_proto_funcs));++    /* Math: create as autoinit object */+    js_random_init(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));++    /* ES6 Reflect: create as autoinit object */+    JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));++    /* ES6 Symbol */+    ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,+                               countof(js_symbol_proto_funcs));+    obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,+                                   ctx->class_proto[JS_CLASS_SYMBOL]);+    JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,+                               countof(js_symbol_funcs));+    for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {+        char buf[ATOM_GET_STR_BUF_SIZE];+        const char *str, *p;+        str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);+        /* skip "Symbol." */+        p = strchr(str, '.');+        if (p)+            str = p + 1;+        JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);+    }++    /* ES6 Generator */+    ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],+                               js_generator_proto_funcs,+                               countof(js_generator_proto_funcs));++    ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);+    obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,+                                "GeneratorFunction", 1,+                                JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);+    JS_SetPropertyFunctionList(ctx,+                               ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],+                               js_generator_function_proto_funcs,+                               countof(js_generator_function_proto_funcs));+    JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],+                       ctx->class_proto[JS_CLASS_GENERATOR],+                       JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);+    JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],+                       0, JS_PROP_CONFIGURABLE);+    JS_FreeValue(ctx, obj1);++    /* global properties */+    ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);+    JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,+                           JS_DupValue(ctx, ctx->eval_obj),+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);++    JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,+                           JS_DupValue(ctx, ctx->global_obj),+                           JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+}++/* Typed Arrays */++static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {+    0, 0, 0, 1, 1, 2, 2,+#ifdef CONFIG_BIGNUM+    3, 3, /* BigInt64Array, BigUint64Array */+#endif+    2, 3+};++static JSValue js_array_buffer_constructor3(JSContext *ctx,+                                            JSValueConst new_target,+                                            uint64_t len, JSClassID class_id,+                                            uint8_t *buf,+                                            JSFreeArrayBufferDataFunc *free_func,+                                            void *opaque, BOOL alloc_flag)+{+    JSRuntime *rt = ctx->rt;+    JSValue obj;+    JSArrayBuffer *abuf = NULL;++    obj = js_create_from_ctor(ctx, new_target, class_id);+    if (JS_IsException(obj))+        return obj;+    /* XXX: we are currently limited to 2 GB */+    if (len > INT32_MAX) {+        JS_ThrowRangeError(ctx, "invalid array buffer length");+        goto fail;+    }+    abuf = js_malloc(ctx, sizeof(*abuf));+    if (!abuf)+        goto fail;+    abuf->byte_length = len;+    if (alloc_flag) {+        if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&+            rt->sab_funcs.sab_alloc) {+            abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,+                                                 max_int(len, 1));+            if (!abuf->data)+                goto fail;+            memset(abuf->data, 0, len);+        } else {+            /* the allocation must be done after the object creation */+            abuf->data = js_mallocz(ctx, max_int(len, 1));+            if (!abuf->data)+                goto fail;+        }+    } else {+        if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&+            rt->sab_funcs.sab_dup) {+            rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);+        }+        abuf->data = buf;+    }+    init_list_head(&abuf->array_list);+    abuf->detached = FALSE;+    abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);+    abuf->opaque = opaque;+    abuf->free_func = free_func;+    if (alloc_flag && buf)+        memcpy(abuf->data, buf, len);+    JS_SetOpaque(obj, abuf);+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    js_free(ctx, abuf);+    return JS_EXCEPTION;+}++static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)+{+    js_free_rt(rt, ptr);+}++static JSValue js_array_buffer_constructor2(JSContext *ctx,+                                            JSValueConst new_target,+                                            uint64_t len, JSClassID class_id)+{+    return js_array_buffer_constructor3(ctx, new_target, len, class_id,+                                        NULL, js_array_buffer_free, NULL,+                                        TRUE);+}++static JSValue js_array_buffer_constructor1(JSContext *ctx,+                                            JSValueConst new_target,+                                            uint64_t len)+{+    return js_array_buffer_constructor2(ctx, new_target, len,+                                        JS_CLASS_ARRAY_BUFFER);+}++JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,+                          JSFreeArrayBufferDataFunc *free_func, void *opaque,+                          BOOL is_shared)+{+    return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,+                                        is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,+                                        buf, free_func, opaque, FALSE);+}++/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */+JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)+{+    return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,+                                        JS_CLASS_ARRAY_BUFFER,+                                        (uint8_t *)buf,+                                        js_array_buffer_free, NULL,+                                        TRUE);+}++static JSValue js_array_buffer_constructor(JSContext *ctx,+                                           JSValueConst new_target,+                                           int argc, JSValueConst *argv)+{+    uint64_t len;+    if (JS_ToIndex(ctx, &len, argv[0]))+        return JS_EXCEPTION;+    return js_array_buffer_constructor1(ctx, new_target, len);+}++static JSValue js_shared_array_buffer_constructor(JSContext *ctx,+                                                  JSValueConst new_target,+                                                  int argc, JSValueConst *argv)+{+    uint64_t len;+    if (JS_ToIndex(ctx, &len, argv[0]))+        return JS_EXCEPTION;+    return js_array_buffer_constructor2(ctx, new_target, len,+                                        JS_CLASS_SHARED_ARRAY_BUFFER);+}++/* also used for SharedArrayBuffer */+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSArrayBuffer *abuf = p->u.array_buffer;+    if (abuf) {+        /* The ArrayBuffer finalizer may be called before the typed+           array finalizers using it, so abuf->array_list is not+           necessarily empty. */+        // assert(list_empty(&abuf->array_list));+        if (abuf->shared && rt->sab_funcs.sab_free) {+            rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);+        } else {+            if (abuf->free_func)+                abuf->free_func(rt, abuf->opaque, abuf->data);+        }+        js_free_rt(rt, abuf);+    }+}++static JSValue js_array_buffer_isView(JSContext *ctx,+                                      JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    JSObject *p;+    BOOL res;+    res = FALSE;+    if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {+        p = JS_VALUE_GET_OBJ(argv[0]);+        if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+            p->class_id <= JS_CLASS_DATAVIEW) {+            res = TRUE;+        }+    }+    return JS_NewBool(ctx, res);+}++static const JSCFunctionListEntry js_array_buffer_funcs[] = {+    JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)+{+    return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");+}++static JSValue js_array_buffer_get_byteLength(JSContext *ctx,+                                              JSValueConst this_val,+                                              int class_id)+{+    JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);+    if (!abuf)+        return JS_EXCEPTION;+    if (abuf->detached)+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    return JS_NewUint32(ctx, abuf->byte_length);+}++void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)+{+    JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);+    struct list_head *el;++    if (!abuf || abuf->detached)+        return;+    if (abuf->free_func)+        abuf->free_func(ctx->rt, abuf->opaque, abuf->data);+    abuf->data = NULL;+    abuf->byte_length = 0;+    abuf->detached = TRUE;++    list_for_each(el, &abuf->array_list) {+        JSTypedArray *ta;+        JSObject *p;++        ta = list_entry(el, JSTypedArray, link);+        p = ta->obj;+        /* Note: the typed array length and offset fields are not modified */+        if (p->class_id != JS_CLASS_DATAVIEW) {+            p->u.array.count = 0;+            p->u.array.u.ptr = NULL;+        }+    }+}++/* get an ArrayBuffer or SharedArrayBuffer */+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        goto fail;+    p = JS_VALUE_GET_OBJ(obj);+    if (p->class_id != JS_CLASS_ARRAY_BUFFER &&+        p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {+    fail:+        JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);+        return NULL;+    }+    return p->u.array_buffer;+}++/* return NULL if exception. WARNING: any JS call can detach the+   buffer and render the returned pointer invalid */+uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)+{+    JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);+    if (!abuf)+        goto fail;+    if (abuf->detached) {+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    *psize = abuf->byte_length;+    return abuf->data;+ fail:+    *psize = 0;+    return NULL;+}++static JSValue js_array_buffer_slice(JSContext *ctx,+                                     JSValueConst this_val,+                                     int argc, JSValueConst *argv, int class_id)+{+    JSArrayBuffer *abuf, *new_abuf;+    int64_t len, start, end, new_len;+    JSValue ctor, new_obj;++    abuf = JS_GetOpaque2(ctx, this_val, class_id);+    if (!abuf)+        return JS_EXCEPTION;+    if (abuf->detached)+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    len = abuf->byte_length;++    if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))+        return JS_EXCEPTION;++    end = len;+    if (!JS_IsUndefined(argv[1])) {+        if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))+            return JS_EXCEPTION;+    }+    new_len = max_int64(end - start, 0);+    ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);+    if (JS_IsException(ctor))+        return ctor;+    if (JS_IsUndefined(ctor)) {+        new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,+                                               class_id);+    } else {+        JSValue args[1];+        args[0] = JS_NewInt64(ctx, new_len);+        new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);+        JS_FreeValue(ctx, ctor);+        JS_FreeValue(ctx, args[0]);+    }+    if (JS_IsException(new_obj))+        return new_obj;+    new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);+    if (!new_abuf)+        goto fail;+    if (js_same_value(ctx, new_obj, this_val)) {+        JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");+        goto fail;+    }+    if (new_abuf->detached) {+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    if (new_abuf->byte_length < new_len) {+        JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");+        goto fail;+    }+    /* must test again because of side effects */+    if (abuf->detached) {+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    memcpy(new_abuf->data, abuf->data + start, new_len);+    return new_obj;+ fail:+    JS_FreeValue(ctx, new_obj);+    return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {+    JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),+    JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),+};++/* SharedArrayBuffer */++static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {+    JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),+    JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),+};++static JSObject *get_typed_array(JSContext *ctx,+                                 JSValueConst this_val,+                                 int is_dataview)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+        goto fail;+    p = JS_VALUE_GET_OBJ(this_val);+    if (is_dataview) {+        if (p->class_id != JS_CLASS_DATAVIEW)+            goto fail;+    } else {+        if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&+              p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {+        fail:+            JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");+            return NULL;+        }+    }+    return p;+}++/* WARNING: 'p' must be a typed array */+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)+{+    JSTypedArray *ta = p->u.typed_array;+    JSArrayBuffer *abuf = ta->buffer->u.array_buffer;+    /* XXX: could simplify test by ensuring that+       p->u.array.u.ptr is NULL iff it is detached */+    return abuf->detached;+}++/* WARNING: 'p' must be a typed array. Works even if the array buffer+   is detached */+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)+{+    JSTypedArray *ta = p->u.typed_array;+    int size_log2 = typed_array_size_log2(p->class_id);+    return ta->length >> size_log2;+}++static int validate_typed_array(JSContext *ctx, JSValueConst this_val)+{+    JSObject *p;+    p = get_typed_array(ctx, this_val, 0);+    if (!p)+        return -1;+    if (typed_array_is_detached(ctx, p)) {+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        return -1;+    }+    return 0;+}++static JSValue js_typed_array_get_length(JSContext *ctx,+                                         JSValueConst this_val)+{+    JSObject *p;+    p = get_typed_array(ctx, this_val, 0);+    if (!p)+        return JS_EXCEPTION;+    return JS_NewInt32(ctx, p->u.array.count);+}++static JSValue js_typed_array_get_buffer(JSContext *ctx,+                                         JSValueConst this_val, int is_dataview)+{+    JSObject *p;+    JSTypedArray *ta;+    p = get_typed_array(ctx, this_val, is_dataview);+    if (!p)+        return JS_EXCEPTION;+    ta = p->u.typed_array;+    return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));+}++static JSValue js_typed_array_get_byteLength(JSContext *ctx,+                                             JSValueConst this_val,+                                             int is_dataview)+{+    JSObject *p;+    JSTypedArray *ta;+    p = get_typed_array(ctx, this_val, is_dataview);+    if (!p)+        return JS_EXCEPTION;+    if (typed_array_is_detached(ctx, p)) {+        if (is_dataview) {+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        } else {+            return JS_NewInt32(ctx, 0);+        }+    }+    ta = p->u.typed_array;+    return JS_NewInt32(ctx, ta->length);+}++static JSValue js_typed_array_get_byteOffset(JSContext *ctx,+                                             JSValueConst this_val,+                                             int is_dataview)+{+    JSObject *p;+    JSTypedArray *ta;+    p = get_typed_array(ctx, this_val, is_dataview);+    if (!p)+        return JS_EXCEPTION;+    if (typed_array_is_detached(ctx, p)) {+        if (is_dataview) {+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        } else {+            return JS_NewInt32(ctx, 0);+        }+    }+    ta = p->u.typed_array;+    return JS_NewInt32(ctx, ta->offset);+}++/* Return the buffer associated to the typed array or an exception if+   it is not a typed array or if the buffer is detached. pbyte_offset,+   pbyte_length or pbytes_per_element can be NULL. */+JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,+                               size_t *pbyte_offset,+                               size_t *pbyte_length,+                               size_t *pbytes_per_element)+{+    JSObject *p;+    JSTypedArray *ta;+    p = get_typed_array(ctx, obj, FALSE);+    if (!p)+        return JS_EXCEPTION;+    if (typed_array_is_detached(ctx, p))+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    ta = p->u.typed_array;+    if (pbyte_offset)+        *pbyte_offset = ta->offset;+    if (pbyte_length)+        *pbyte_length = ta->length;+    if (pbytes_per_element) {+        *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);+    }+    return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));+}+                               +static JSValue js_typed_array_get_toStringTag(JSContext *ctx,+                                              JSValueConst this_val)+{+    JSObject *p;+    if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+        return JS_UNDEFINED;+    p = JS_VALUE_GET_OBJ(this_val);+    if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&+          p->class_id <= JS_CLASS_FLOAT64_ARRAY))+        return JS_UNDEFINED;+    return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);+}++static JSValue js_typed_array_set_internal(JSContext *ctx,+                                           JSValueConst dst,+                                           JSValueConst src,+                                           JSValueConst off)+{+    JSObject *p;+    JSObject *src_p;+    uint32_t i;+    int64_t src_len, offset;+    JSValue val, src_obj = JS_UNDEFINED;++    p = get_typed_array(ctx, dst, 0);+    if (!p)+        goto fail;+    if (JS_ToInt64Sat(ctx, &offset, off))+        goto fail;+    if (offset < 0)+        goto range_error;+    if (typed_array_is_detached(ctx, p)) {+    detached:+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    src_obj = JS_ToObject(ctx, src);+    if (JS_IsException(src_obj))+        goto fail;+    src_p = JS_VALUE_GET_OBJ(src_obj);+    if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&+        src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+        JSTypedArray *dest_ta = p->u.typed_array;+        JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;+        JSTypedArray *src_ta = src_p->u.typed_array;+        JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;+        int shift = typed_array_size_log2(p->class_id);++        if (src_abuf->detached)+            goto detached;++        src_len = src_p->u.array.count;+        if (offset > (int64_t)(p->u.array.count - src_len))+            goto range_error;++        /* copying between typed objects */+        if (src_p->class_id == p->class_id) {+            /* same type, use memmove */+            memmove(dest_abuf->data + dest_ta->offset + (offset << shift),+                    src_abuf->data + src_ta->offset, src_len << shift);+            goto done;+        }+        if (dest_abuf->data == src_abuf->data) {+            /* copying between the same buffer using different types of mappings+               would require a temporary buffer */+        }+        /* otherwise, default behavior is slow but correct */+    } else {+        if (js_get_length64(ctx, &src_len, src_obj))+            goto fail;+        if (offset > (int64_t)(p->u.array.count - src_len)) {+        range_error:+            JS_ThrowRangeError(ctx, "invalid array length");+            goto fail;+        }+    }+    for(i = 0; i < src_len; i++) {+        val = JS_GetPropertyUint32(ctx, src_obj, i);+        if (JS_IsException(val))+            goto fail;+        if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)+            goto fail;+    }+done:+    JS_FreeValue(ctx, src_obj);+    return JS_UNDEFINED;+fail:+    JS_FreeValue(ctx, src_obj);+    return JS_EXCEPTION;+}++static JSValue js_typed_array_set(JSContext *ctx,+                                  JSValueConst this_val,+                                  int argc, JSValueConst *argv)+{+    JSValueConst offset = JS_UNDEFINED;+    if (argc > 1) {+        offset = argv[1];+    }+    return js_typed_array_set_internal(ctx, this_val, argv[0], offset);+}++static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,+                                              int argc, JSValueConst *argv, int magic)+{+    if (validate_typed_array(ctx, this_val))+        return JS_EXCEPTION;+    return js_create_array_iterator(ctx, this_val, argc, argv, magic);+}++/* return < 0 if exception */+static int js_typed_array_get_length_internal(JSContext *ctx,+                                              JSValueConst obj)+{+    JSObject *p;+    p = get_typed_array(ctx, obj, 0);+    if (!p)+        return -1;+    if (typed_array_is_detached(ctx, p)) {+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        return -1;+    }+    return p->u.array.count;+}++#if 0+/* validate a typed array and return its length */+static JSValue js_typed_array___getLength(JSContext *ctx,+                                          JSValueConst this_val,+                                          int argc, JSValueConst *argv)+{+    BOOL ignore_detached = JS_ToBool(ctx, argv[1]);++    if (ignore_detached) {+        return js_typed_array_get_length(ctx, argv[0]);+    } else {+        int len;+        len = js_typed_array_get_length_internal(ctx, argv[0]);+        if (len < 0)+            return JS_EXCEPTION;+        return JS_NewInt32(ctx, len);+    }+}+#endif++static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,+                                     int argc, JSValueConst *argv)+{+    JSValue ret;+    int new_len;+    int64_t len;++    ret = JS_CallConstructor(ctx, ctor, argc, argv);+    if (JS_IsException(ret))+        return ret;+    /* validate the typed array */+    new_len = js_typed_array_get_length_internal(ctx, ret);+    if (new_len < 0)+        goto fail;+    if (argc == 1) {+        /* ensure that it is large enough */+        if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))+            goto fail;+        if (new_len < len) {+            JS_ThrowTypeError(ctx, "TypedArray length is too small");+        fail:+            JS_FreeValue(ctx, ret);+            return JS_EXCEPTION;+        }+    }+    return ret;+}++#if 0+static JSValue js_typed_array___create(JSContext *ctx,+                                       JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);+}+#endif++static JSValue js_typed_array___speciesCreate(JSContext *ctx,+                                              JSValueConst this_val,+                                              int argc, JSValueConst *argv)+{+    JSValueConst obj;+    JSObject *p;+    JSValue ctor, ret;+    int argc1;++    obj = argv[0];+    p = get_typed_array(ctx, obj, 0);+    if (!p)+        return JS_EXCEPTION;+    ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);+    if (JS_IsException(ctor))+        return ctor;+    argc1 = max_int(argc - 1, 0);+    if (JS_IsUndefined(ctor)) {+        ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,+                                         p->class_id);+    } else {+        ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);+        JS_FreeValue(ctx, ctor);+    }+    return ret;+}++static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    // from(items, mapfn = void 0, this_arg = void 0)+    JSValueConst items = argv[0], mapfn, this_arg;+    JSValueConst args[2];+    JSValue stack[2];+    JSValue iter, arr, r, v, v2;+    int64_t k, len;+    int done, mapping;++    mapping = FALSE;+    mapfn = JS_UNDEFINED;+    this_arg = JS_UNDEFINED;+    r = JS_UNDEFINED;+    arr = JS_UNDEFINED;+    stack[0] = JS_UNDEFINED;+    stack[1] = JS_UNDEFINED;++    if (argc > 1) {+        mapfn = argv[1];+        if (!JS_IsUndefined(mapfn)) {+            if (check_function(ctx, mapfn))+                goto exception;+            mapping = 1;+            if (argc > 2)+                this_arg = argv[2];+        }+    }+    iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);+    if (JS_IsException(iter))+        goto exception;+    if (!JS_IsUndefined(iter)) {+        JS_FreeValue(ctx, iter);+        arr = JS_NewArray(ctx);+        if (JS_IsException(arr))+            goto exception;+        stack[0] = JS_DupValue(ctx, items);+        if (js_for_of_start(ctx, &stack[1], FALSE))+            goto exception;+        for (k = 0;; k++) {+            v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);+            if (JS_IsException(v))+                goto exception_close;+            if (done)+                break;+            if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0)+                goto exception_close;+        }+    } else {+        arr = JS_ToObject(ctx, items);+        if (JS_IsException(arr))+            goto exception;+    }+    if (js_get_length64(ctx, &len, arr) < 0)+        goto exception;+    v = JS_NewInt64(ctx, len);+    args[0] = v;+    r = js_typed_array_create(ctx, this_val, 1, args);+    JS_FreeValue(ctx, v);+    if (JS_IsException(r))+        goto exception;+    for(k = 0; k < len; k++) {+        v = JS_GetPropertyInt64(ctx, arr, k);+        if (JS_IsException(v))+            goto exception;+        if (mapping) {+            args[0] = v;+            args[1] = JS_NewInt32(ctx, k);+            v2 = JS_Call(ctx, mapfn, this_arg, 2, args);+            JS_FreeValue(ctx, v);+            v = v2;+            if (JS_IsException(v))+                goto exception;+        }+        if (JS_SetPropertyInt64(ctx, r, k, v) < 0)+            goto exception;+    }+    goto done;++ exception_close:+    if (!JS_IsUndefined(stack[0]))+        JS_IteratorClose(ctx, stack[0], TRUE);+ exception:+    JS_FreeValue(ctx, r);+    r = JS_EXCEPTION;+ done:+    JS_FreeValue(ctx, arr);+    JS_FreeValue(ctx, stack[0]);+    JS_FreeValue(ctx, stack[1]);+    return r;+}++static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv)+{+    JSValue obj;+    JSValueConst args[1];+    int i;++    args[0] = JS_NewInt32(ctx, argc);+    obj = js_typed_array_create(ctx, this_val, 1, args);+    if (JS_IsException(obj))+        return obj;++    for(i = 0; i < argc; i++) {+        if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {+            JS_FreeValue(ctx, obj);+            return JS_EXCEPTION;+        }+    }+    return obj;+}++static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,+                                         int argc, JSValueConst *argv)+{+    JSObject *p;+    int len, to, from, final, count, shift;++    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        return JS_EXCEPTION;++    if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))+        return JS_EXCEPTION;++    if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))+        return JS_EXCEPTION;++    final = len;+    if (argc > 2 && !JS_IsUndefined(argv[2])) {+        if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))+            return JS_EXCEPTION;+    }++    count = min_int(final - from, len - to);+    if (count > 0) {+        p = JS_VALUE_GET_OBJ(this_val);+        if (typed_array_is_detached(ctx, p))+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        shift = typed_array_size_log2(p->class_id);+        memmove(p->u.array.u.uint8_ptr + (to << shift),+                p->u.array.u.uint8_ptr + (from << shift),+                count << shift);+    }+    return JS_DupValue(ctx, this_val);+}++static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSObject *p;+    int len, k, final, shift;+    uint64_t v64;++    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        return JS_EXCEPTION;+    p = JS_VALUE_GET_OBJ(this_val);++    if (p->class_id == JS_CLASS_UINT8C_ARRAY) {+        int32_t v;+        if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))+            return JS_EXCEPTION;+        v64 = v;+    } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {+        uint32_t v;+        if (JS_ToUint32(ctx, &v, argv[0]))+            return JS_EXCEPTION;+        v64 = v;+    } else+#ifdef CONFIG_BIGNUM+    if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {+        if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))+            return JS_EXCEPTION;+    } else+#endif+    {+        double d;+        if (JS_ToFloat64(ctx, &d, argv[0]))+            return JS_EXCEPTION;+        if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {+            union {+                float f;+                uint32_t u32;+            } u;+            u.f = d;+            v64 = u.u32;+        } else {+            JSFloat64Union u;+            u.d = d;+            v64 = u.u64;+        }+    }++    k = 0;+    if (argc > 1) {+        if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))+            return JS_EXCEPTION;+    }++    final = len;+    if (argc > 2 && !JS_IsUndefined(argv[2])) {+        if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))+            return JS_EXCEPTION;+    }++    if (typed_array_is_detached(ctx, p))+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    +    shift = typed_array_size_log2(p->class_id);+    switch(shift) {+    case 0:+        if (k < final) {+            memset(p->u.array.u.uint8_ptr + k, v64, final - k);+        }+        break;+    case 1:+        for(; k < final; k++) {+            p->u.array.u.uint16_ptr[k] = v64;+        }+        break;+    case 2:+        for(; k < final; k++) {+            p->u.array.u.uint32_ptr[k] = v64;+        }+        break;+    case 3:+        for(; k < final; k++) {+            p->u.array.u.uint64_ptr[k] = v64;+        }+        break;+    default:+        abort();+    }+    return JS_DupValue(ctx, this_val);+}++static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv, int findIndex)+{+    JSValueConst func, this_arg;+    JSValueConst args[3];+    JSValue val, index_val, res;+    int len, k;++    val = JS_UNDEFINED;+    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        goto exception;++    func = argv[0];+    if (check_function(ctx, func))+        goto exception;++    this_arg = JS_UNDEFINED;+    if (argc > 1)+        this_arg = argv[1];++    for(k = 0; k < len; k++) {+        index_val = JS_NewInt32(ctx, k);+        val = JS_GetPropertyValue(ctx, this_val, index_val);+        if (JS_IsException(val))+            goto exception;+        args[0] = val;+        args[1] = index_val;+        args[2] = this_val;+        res = JS_Call(ctx, func, this_arg, 3, args);+        if (JS_IsException(res))+            goto exception;+        if (JS_ToBoolFree(ctx, res)) {+            if (findIndex) {+                JS_FreeValue(ctx, val);+                return index_val;+            } else {+                return val;+            }+        }+        JS_FreeValue(ctx, val);+    }+    if (findIndex)+        return JS_NewInt32(ctx, -1);+    else+        return JS_UNDEFINED;++exception:+    JS_FreeValue(ctx, val);+    return JS_EXCEPTION;+}++#define special_indexOf 0+#define special_lastIndexOf 1+#define special_includes -1++static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv, int special)+{+    JSObject *p;+    int len, tag, is_int, is_bigint, k, stop, inc, res = -1;+    int64_t v64;+    double d;+    float f;++    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        goto exception;+    if (len == 0)+        goto done;++    if (special == special_lastIndexOf) {+        k = len - 1;+        if (argc > 1) {+            if (JS_ToFloat64(ctx, &d, argv[1]))+                goto exception;+            if (isnan(d)) {+                k = 0;+            } else {+                if (d >= 0) {+                    if (d < k) {+                        k = d;+                    }+                } else {+                    d += len;+                    if (d < 0)+                        goto done;+                    k = d;+                }+            }+        }+        stop = -1;+        inc = -1;+    } else {+        k = 0;+        if (argc > 1) {+            if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))+                goto exception;+        }+        stop = len;+        inc = 1;+    }++    if (validate_typed_array(ctx, this_val))+        goto exception;+    p = JS_VALUE_GET_OBJ(this_val);++    is_bigint = 0;+    is_int = 0; /* avoid warning */+    v64 = 0; /* avoid warning */+    tag = JS_VALUE_GET_NORM_TAG(argv[0]);+    if (tag == JS_TAG_INT) {+        is_int = 1;+        v64 = JS_VALUE_GET_INT(argv[0]);+        d = v64;+    } else+    if (tag == JS_TAG_FLOAT64) {+        d = JS_VALUE_GET_FLOAT64(argv[0]);+        v64 = d;+        is_int = (v64 == d);+    } else+#ifdef CONFIG_BIGNUM+    if (tag == JS_TAG_BIG_INT) {+        JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);+        +        if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {+            if (bf_get_int64(&v64, &p1->num, 0) != 0)+                goto done;+        } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {+            if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)+                goto done;+        } else {+            goto done;+        }+        d = 0;+        is_bigint = 1;+    } else+#endif+    {+        goto done;+    }++    p = JS_VALUE_GET_OBJ(this_val);+    switch (p->class_id) {+    case JS_CLASS_INT8_ARRAY:+        if (is_int && (int8_t)v64 == v64)+            goto scan8;+        break;+    case JS_CLASS_UINT8C_ARRAY:+    case JS_CLASS_UINT8_ARRAY:+        if (is_int && (uint8_t)v64 == v64) {+            const uint8_t *pv, *pp;+            uint16_t v;+        scan8:+            pv = p->u.array.u.uint8_ptr;+            v = v64;+            if (inc > 0) {+                pp = memchr(pv + k, v, len - k);+                if (pp)+                    res = pp - pv;+            } else {+                for (; k != stop; k += inc) {+                    if (pv[k] == v) {+                        res = k;+                        break;+                    }+                }+            }+        }+        break;+    case JS_CLASS_INT16_ARRAY:+        if (is_int && (int16_t)v64 == v64)+            goto scan16;+        break;+    case JS_CLASS_UINT16_ARRAY:+        if (is_int && (uint16_t)v64 == v64) {+            const uint16_t *pv;+            uint16_t v;+        scan16:+            pv = p->u.array.u.uint16_ptr;+            v = v64;+            for (; k != stop; k += inc) {+                if (pv[k] == v) {+                    res = k;+                    break;+                }+            }+        }+        break;+    case JS_CLASS_INT32_ARRAY:+        if (is_int && (int32_t)v64 == v64)+            goto scan32;+        break;+    case JS_CLASS_UINT32_ARRAY:+        if (is_int && (uint32_t)v64 == v64) {+            const uint32_t *pv;+            uint32_t v;+        scan32:+            pv = p->u.array.u.uint32_ptr;+            v = v64;+            for (; k != stop; k += inc) {+                if (pv[k] == v) {+                    res = k;+                    break;+                }+            }+        }+        break;+    case JS_CLASS_FLOAT32_ARRAY:+        if (is_bigint)+            break;+        if (isnan(d)) {+            const float *pv = p->u.array.u.float_ptr;+            /* special case: indexOf returns -1, includes finds NaN */+            if (special != special_includes)+                goto done;+            for (; k != stop; k += inc) {+                if (isnan(pv[k])) {+                    res = k;+                    break;+                }+            }+        } else if ((f = (float)d) == d) {+            const float *pv = p->u.array.u.float_ptr;+            for (; k != stop; k += inc) {+                if (pv[k] == f) {+                    res = k;+                    break;+                }+            }+        }+        break;+    case JS_CLASS_FLOAT64_ARRAY:+        if (is_bigint)+            break;+        if (isnan(d)) {+            const double *pv = p->u.array.u.double_ptr;+            /* special case: indexOf returns -1, includes finds NaN */+            if (special != special_includes)+                goto done;+            for (; k != stop; k += inc) {+                if (isnan(pv[k])) {+                    res = k;+                    break;+                }+            }+        } else {+            const double *pv = p->u.array.u.double_ptr;+            for (; k != stop; k += inc) {+                if (pv[k] == d) {+                    res = k;+                    break;+                }+            }+        }+        break;+#ifdef CONFIG_BIGNUM+    case JS_CLASS_BIG_INT64_ARRAY:+        if (is_bigint || (is_math_mode(ctx) && is_int &&+                          v64 >= -MAX_SAFE_INTEGER &&+                          v64 <= MAX_SAFE_INTEGER)) {+            goto scan64;+        }+        break;+    case JS_CLASS_BIG_UINT64_ARRAY:+        if (is_bigint || (is_math_mode(ctx) && is_int &&+                          v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {+            const uint64_t *pv;+            uint64_t v;+        scan64:+            pv = p->u.array.u.uint64_ptr;+            v = v64;+            for (; k != stop; k += inc) {+                if (pv[k] == v) {+                    res = k;+                    break;+                }+            }+        }+        break;+#endif+    }++done:+    if (special == special_includes)+        return JS_NewBool(ctx, res >= 0);+    else+        return JS_NewInt32(ctx, res);++exception:+    return JS_EXCEPTION;+}++static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv, int toLocaleString)+{+    JSValue sep = JS_UNDEFINED, el;+    StringBuffer b_s, *b = &b_s;+    JSString *p = NULL;+    int i, n;+    int c;++    n = js_typed_array_get_length_internal(ctx, this_val);+    if (n < 0)+        goto exception;++    c = ',';    /* default separator */+    if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {+        sep = JS_ToString(ctx, argv[0]);+        if (JS_IsException(sep))+            goto exception;+        p = JS_VALUE_GET_STRING(sep);+        if (p->len == 1 && !p->is_wide_char)+            c = p->u.str8[0];+        else+            c = -1;+    }+    string_buffer_init(ctx, b, 0);++    /* XXX: optimize with direct access */+    for(i = 0; i < n; i++) {+        if (i > 0) {+            if (c >= 0) {+                if (string_buffer_putc8(b, c))+                    goto fail;+            } else {+                if (string_buffer_concat(b, p, 0, p->len))+                    goto fail;+            }+        }+        el = JS_GetPropertyUint32(ctx, this_val, i);+        if (JS_IsException(el))+            goto fail;+        if (toLocaleString) {+            el = JS_ToLocaleStringFree(ctx, el);+        }+        if (string_buffer_concat_value_free(b, el))+            goto fail;+    }+    JS_FreeValue(ctx, sep);+    return string_buffer_end(b);++fail:+    string_buffer_free(b);+    JS_FreeValue(ctx, sep);+exception:+    return JS_EXCEPTION;+}++static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,+                                      int argc, JSValueConst *argv)+{+    JSObject *p;+    int len;++    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        return JS_EXCEPTION;+    if (len > 0) {+        p = JS_VALUE_GET_OBJ(this_val);+        switch (typed_array_size_log2(p->class_id)) {+        case 0:+            {+                uint8_t *p1 = p->u.array.u.uint8_ptr;+                uint8_t *p2 = p1 + len - 1;+                while (p1 < p2) {+                    uint8_t v = *p1;+                    *p1++ = *p2;+                    *p2-- = v;+                }+            }+            break;+        case 1:+            {+                uint16_t *p1 = p->u.array.u.uint16_ptr;+                uint16_t *p2 = p1 + len - 1;+                while (p1 < p2) {+                    uint16_t v = *p1;+                    *p1++ = *p2;+                    *p2-- = v;+                }+            }+            break;+        case 2:+            {+                uint32_t *p1 = p->u.array.u.uint32_ptr;+                uint32_t *p2 = p1 + len - 1;+                while (p1 < p2) {+                    uint32_t v = *p1;+                    *p1++ = *p2;+                    *p2-- = v;+                }+            }+            break;+        case 3:+            {+                uint64_t *p1 = p->u.array.u.uint64_ptr;+                uint64_t *p2 = p1 + len - 1;+                while (p1 < p2) {+                    uint64_t v = *p1;+                    *p1++ = *p2;+                    *p2-- = v;+                }+            }+            break;+        default:+            abort();+        }+    }+    return JS_DupValue(ctx, this_val);+}++static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,+                                    int argc, JSValueConst *argv)+{+    JSValueConst args[2];+    JSValue arr, val;+    JSObject *p, *p1;+    int n, len, start, final, count, shift;++    arr = JS_UNDEFINED;+    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        goto exception;++    if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))+        goto exception;+    final = len;+    if (!JS_IsUndefined(argv[1])) {+        if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))+            goto exception;+    }+    count = max_int(final - start, 0);++    p = get_typed_array(ctx, this_val, 0);+    if (p == NULL)+        goto exception;+    shift = typed_array_size_log2(p->class_id);++    args[0] = this_val;+    args[1] = JS_NewInt32(ctx, count);+    arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);+    if (JS_IsException(arr))+        goto exception;++    if (count > 0) {+        if (validate_typed_array(ctx, this_val)+        ||  validate_typed_array(ctx, arr))+            goto exception;++        p1 = get_typed_array(ctx, arr, 0);+        if (p1 != NULL && p->class_id == p1->class_id &&+            typed_array_get_length(ctx, p1) >= count &&+            typed_array_get_length(ctx, p) >= start + count) {+            memcpy(p1->u.array.u.uint8_ptr,+                   p->u.array.u.uint8_ptr + (start << shift),+                   count << shift);+        } else {+            for (n = 0; n < count; n++) {+                val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));+                if (JS_IsException(val))+                    goto exception;+                if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,+                                        JS_PROP_THROW) < 0)+                    goto exception;+            }+        }+    }+    return arr;++ exception:+    JS_FreeValue(ctx, arr);+    return JS_EXCEPTION;+}++static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,+                                       int argc, JSValueConst *argv)+{+    JSValueConst args[4];+    JSValue arr, byteOffset, ta_buffer;+    JSObject *p;+    int len, start, final, count, shift, offset;++    p = get_typed_array(ctx, this_val, 0);+    if (!p)+        goto exception;+    len = p->u.array.count;+    if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))+        goto exception;++    final = len;+    if (!JS_IsUndefined(argv[1])) {+        if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))+            goto exception;+    }+    count = max_int(final - start, 0);+    byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);+    if (JS_IsException(byteOffset))+        goto exception;+    shift = typed_array_size_log2(p->class_id);+    offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);+    JS_FreeValue(ctx, byteOffset);+    ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);+    if (JS_IsException(ta_buffer))+        goto exception;+    args[0] = this_val;+    args[1] = ta_buffer;+    args[2] = JS_NewInt32(ctx, offset);+    args[3] = JS_NewInt32(ctx, count);+    arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);+    JS_FreeValue(ctx, ta_buffer);+    return arr;++ exception:+    return JS_EXCEPTION;+}++/* TypedArray.prototype.sort */++static int js_cmp_doubles(double x, double y)+{+    if (isnan(x))    return isnan(y) ? 0 : +1;+    if (isnan(y))    return -1;+    if (x < y)       return -1;+    if (x > y)       return 1;+    if (x != 0)      return 0;+    if (signbit(x))  return signbit(y) ? 0 : -1;+    else             return signbit(y) ? 1 : 0;+}++static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {+    return *(const int8_t *)a - *(const int8_t *)b;+}++static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {+    return *(const uint8_t *)a - *(const uint8_t *)b;+}++static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {+    return *(const int16_t *)a - *(const int16_t *)b;+}++static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {+    return *(const uint16_t *)a - *(const uint16_t *)b;+}++static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {+    int32_t x = *(const int32_t *)a;+    int32_t y = *(const int32_t *)b;+    return (y < x) - (y > x);+}++static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {+    uint32_t x = *(const uint32_t *)a;+    uint32_t y = *(const uint32_t *)b;+    return (y < x) - (y > x);+}++#ifdef CONFIG_BIGNUM+static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {+    int64_t x = *(const int64_t *)a;+    int64_t y = *(const int64_t *)b;+    return (y < x) - (y > x);+}++static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {+    uint64_t x = *(const uint64_t *)a;+    uint64_t y = *(const uint64_t *)b;+    return (y < x) - (y > x);+}+#endif++static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {+    return js_cmp_doubles(*(const float *)a, *(const float *)b);+}++static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {+    return js_cmp_doubles(*(const double *)a, *(const double *)b);+}++static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {+    return JS_NewInt32(ctx, *(const int8_t *)a);+}++static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {+    return JS_NewInt32(ctx, *(const uint8_t *)a);+}++static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {+    return JS_NewInt32(ctx, *(const int16_t *)a);+}++static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {+    return JS_NewInt32(ctx, *(const uint16_t *)a);+}++static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {+    return JS_NewInt32(ctx, *(const int32_t *)a);+}++static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {+    return JS_NewUint32(ctx, *(const uint32_t *)a);+}++#ifdef CONFIG_BIGNUM+static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {+    return JS_NewBigInt64(ctx, *(int64_t *)a);+}++static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {+    return JS_NewBigUint64(ctx, *(uint64_t *)a);+}+#endif++static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {+    return __JS_NewFloat64(ctx, *(const float *)a);+}++static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {+    return __JS_NewFloat64(ctx, *(const double *)a);+}++struct TA_sort_context {+    JSContext *ctx;+    int exception;+    JSValueConst arr;+    JSValueConst cmp;+    JSValue (*getfun)(JSContext *ctx, const void *a);+    uint8_t *array_ptr; /* cannot change unless the array is detached */+    int elt_size;+};++static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {+    struct TA_sort_context *psc = opaque;+    JSContext *ctx = psc->ctx;+    uint32_t a_idx, b_idx;+    JSValueConst argv[2];+    JSValue res;+    int cmp;++    cmp = 0;+    if (!psc->exception) {+        a_idx = *(uint32_t *)a;+        b_idx = *(uint32_t *)b;+        argv[0] = psc->getfun(ctx, psc->array_ptr ++                              a_idx * (size_t)psc->elt_size);+        argv[1] = psc->getfun(ctx, psc->array_ptr ++                              b_idx * (size_t)(psc->elt_size));+        res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);+        if (JS_IsException(res)) {+            psc->exception = 1;+            goto done;+        }+        if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {+            int val = JS_VALUE_GET_INT(res);+            cmp = (val > 0) - (val < 0);+        } else {+            double val;+            if (JS_ToFloat64Free(ctx, &val, res) < 0) {+                psc->exception = 1;+                goto done;+            } else {+                cmp = (val > 0) - (val < 0);+            }+        }+        if (cmp == 0) {+            /* make sort stable: compare array offsets */+            cmp = (a_idx > b_idx) - (a_idx < b_idx);+        }+        if (validate_typed_array(ctx, psc->arr) < 0) {+            psc->exception = 1;+        }+    done:+        JS_FreeValue(ctx, (JSValue)argv[0]);+        JS_FreeValue(ctx, (JSValue)argv[1]);+    }+    return cmp;+}++static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,+                                   int argc, JSValueConst *argv)+{+    JSObject *p;+    int len;+    size_t elt_size;+    struct TA_sort_context tsc;+    void *array_ptr;+    int (*cmpfun)(const void *a, const void *b, void *opaque);++    tsc.ctx = ctx;+    tsc.exception = 0;+    tsc.arr = this_val;+    tsc.cmp = argv[0];++    len = js_typed_array_get_length_internal(ctx, this_val);+    if (len < 0)+        return JS_EXCEPTION;+    if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))+        return JS_EXCEPTION;++    if (len > 1) {+        p = JS_VALUE_GET_OBJ(this_val);+        switch (p->class_id) {+        case JS_CLASS_INT8_ARRAY:+            tsc.getfun = js_TA_get_int8;+            cmpfun = js_TA_cmp_int8;+            break;+        case JS_CLASS_UINT8C_ARRAY:+        case JS_CLASS_UINT8_ARRAY:+            tsc.getfun = js_TA_get_uint8;+            cmpfun = js_TA_cmp_uint8;+            break;+        case JS_CLASS_INT16_ARRAY:+            tsc.getfun = js_TA_get_int16;+            cmpfun = js_TA_cmp_int16;+            break;+        case JS_CLASS_UINT16_ARRAY:+            tsc.getfun = js_TA_get_uint16;+            cmpfun = js_TA_cmp_uint16;+            break;+        case JS_CLASS_INT32_ARRAY:+            tsc.getfun = js_TA_get_int32;+            cmpfun = js_TA_cmp_int32;+            break;+        case JS_CLASS_UINT32_ARRAY:+            tsc.getfun = js_TA_get_uint32;+            cmpfun = js_TA_cmp_uint32;+            break;+#ifdef CONFIG_BIGNUM+        case JS_CLASS_BIG_INT64_ARRAY:+            tsc.getfun = js_TA_get_int64;+            cmpfun = js_TA_cmp_int64;+            break;+        case JS_CLASS_BIG_UINT64_ARRAY:+            tsc.getfun = js_TA_get_uint64;+            cmpfun = js_TA_cmp_uint64;+            break;+#endif+        case JS_CLASS_FLOAT32_ARRAY:+            tsc.getfun = js_TA_get_float32;+            cmpfun = js_TA_cmp_float32;+            break;+        case JS_CLASS_FLOAT64_ARRAY:+            tsc.getfun = js_TA_get_float64;+            cmpfun = js_TA_cmp_float64;+            break;+        default:+            abort();+        }+        array_ptr = p->u.array.u.ptr;+        elt_size = 1 << typed_array_size_log2(p->class_id);+        if (!JS_IsUndefined(tsc.cmp)) {+            uint32_t *array_idx;+            void *array_tmp;+            size_t i, j;+            +            /* XXX: a stable sort would use less memory */+            array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));+            if (!array_idx)+                return JS_EXCEPTION;+            for(i = 0; i < len; i++)+                array_idx[i] = i;+            tsc.array_ptr = array_ptr;+            tsc.elt_size = elt_size;+            rqsort(array_idx, len, sizeof(array_idx[0]),+                   js_TA_cmp_generic, &tsc);+            if (tsc.exception)+                goto fail;+            array_tmp = js_malloc(ctx, len * elt_size);+            if (!array_tmp) {+            fail:+                js_free(ctx, array_idx);+                return JS_EXCEPTION;+            }+            memcpy(array_tmp, array_ptr, len * elt_size);+            switch(elt_size) {+            case 1:+                for(i = 0; i < len; i++) {+                    j = array_idx[i];+                    ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];+                }+                break;+            case 2:+                for(i = 0; i < len; i++) {+                    j = array_idx[i];+                    ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];+                }+                break;+            case 4:+                for(i = 0; i < len; i++) {+                    j = array_idx[i];+                    ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];+                }+                break;+            case 8:+                for(i = 0; i < len; i++) {+                    j = array_idx[i];+                    ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];+                }+                break;+            default:+                abort();+            }+            js_free(ctx, array_tmp);+            js_free(ctx, array_idx);+        } else {+            rqsort(array_ptr, len, elt_size, cmpfun, &tsc);+            if (tsc.exception)+                return JS_EXCEPTION;+        }+    }+    return JS_DupValue(ctx, this_val);+}++static const JSCFunctionListEntry js_typed_array_base_funcs[] = {+    JS_CFUNC_DEF("from", 1, js_typed_array_from ),+    JS_CFUNC_DEF("of", 0, js_typed_array_of ),+    JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+    //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),+    //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),+    //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),+};++static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {+    JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),+    JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),+    JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),+    JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),+    JS_CFUNC_DEF("set", 1, js_typed_array_set ),+    JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),+    JS_ALIAS_DEF("[Symbol.iterator]", "values" ),+    JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),+    JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),+    JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),+    JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),+    JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),+    JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),+    JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),+    JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),+    JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),+    JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),+    JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),+    JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),+    JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ),+    JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ),+    JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),+    JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),+    JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),+    JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),+    JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),+    JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),+    JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),+    JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),+    JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),+    //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@+};++static JSValue js_typed_array_base_constructor(JSContext *ctx,+                                               JSValueConst this_val,+                                               int argc, JSValueConst *argv)+{+    return JS_ThrowTypeError(ctx, "cannot be called");+}++/* 'obj' must be an allocated typed array object */+static int typed_array_init(JSContext *ctx, JSValueConst obj,+                            JSValue buffer, uint64_t offset, uint64_t len)+{+    JSTypedArray *ta;+    JSObject *p, *pbuffer;+    JSArrayBuffer *abuf;+    int size_log2;++    p = JS_VALUE_GET_OBJ(obj);+    size_log2 = typed_array_size_log2(p->class_id);+    ta = js_malloc(ctx, sizeof(*ta));+    if (!ta) {+        JS_FreeValue(ctx, buffer);+        return -1;+    }+    pbuffer = JS_VALUE_GET_OBJ(buffer);+    abuf = pbuffer->u.array_buffer;+    ta->obj = p;+    ta->buffer = pbuffer;+    ta->offset = offset;+    ta->length = len << size_log2;+    list_add_tail(&ta->link, &abuf->array_list);+    p->u.typed_array = ta;+    p->u.array.count = len;+    p->u.array.u.ptr = abuf->data + offset;+    return 0;+}+++static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,+                                      JSValueConst obj, JSValueConst method)+{+    JSValue arr, iter, next_method = JS_UNDEFINED, val;+    BOOL done;+    uint32_t k;++    *plen = 0;+    arr = JS_NewArray(ctx);+    if (JS_IsException(arr))+        return arr;+    iter = JS_GetIterator2(ctx, obj, method);+    if (JS_IsException(iter))+        goto fail;+    next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+    if (JS_IsException(next_method))+        goto fail;+    k = 0;+    for(;;) {+        val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+        if (JS_IsException(val))+            goto fail;+        if (done) {+            JS_FreeValue(ctx, val);+            break;+        }+        if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)+            goto fail;+        k++;+    }+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    *plen = k;+    return arr;+ fail:+    JS_FreeValue(ctx, next_method);+    JS_FreeValue(ctx, iter);+    JS_FreeValue(ctx, arr);+    return JS_EXCEPTION;+}++static JSValue js_typed_array_constructor_obj(JSContext *ctx,+                                              JSValueConst new_target,+                                              JSValueConst obj,+                                              int classid)+{+    JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;+    uint32_t i;+    int size_log2;+    int64_t len;++    size_log2 = typed_array_size_log2(classid);+    ret = js_create_from_ctor(ctx, new_target, classid);+    if (JS_IsException(ret))+        return JS_EXCEPTION;++    iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);+    if (JS_IsException(iter))+        goto fail;+    if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {+        uint32_t len1;+        arr = js_array_from_iterator(ctx, &len1, obj, iter);+        JS_FreeValue(ctx, iter);+        if (JS_IsException(arr))+            goto fail;+        len = len1;+    } else {+        if (js_get_length64(ctx, &len, obj))+            goto fail;+        arr = JS_DupValue(ctx, obj);+    }++    buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,+                                          len << size_log2);+    if (JS_IsException(buffer))+        goto fail;+    if (typed_array_init(ctx, ret, buffer, 0, len))+        goto fail;++    for(i = 0; i < len; i++) {+        val = JS_GetPropertyUint32(ctx, arr, i);+        if (JS_IsException(val))+            goto fail;+        if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)+            goto fail;+    }+    JS_FreeValue(ctx, arr);+    return ret;+ fail:+    JS_FreeValue(ctx, arr);+    JS_FreeValue(ctx, ret);+    return JS_EXCEPTION;+}++static JSValue js_typed_array_constructor_ta(JSContext *ctx,+                                             JSValueConst new_target,+                                             JSValueConst src_obj,+                                             int classid)+{+    JSObject *p, *src_buffer;+    JSTypedArray *ta;+    JSValue ctor, obj, buffer;+    uint32_t len, i;+    int size_log2;+    JSArrayBuffer *src_abuf, *abuf;++    obj = js_create_from_ctor(ctx, new_target, classid);+    if (JS_IsException(obj))+        return obj;+    p = JS_VALUE_GET_OBJ(src_obj);+    if (typed_array_is_detached(ctx, p)) {+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    ta = p->u.typed_array;+    len = p->u.array.count;+    src_buffer = ta->buffer;+    src_abuf = src_buffer->u.array_buffer;+    if (!src_abuf->shared) {+        ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer),+                                     JS_UNDEFINED);+        if (JS_IsException(ctor))+            goto fail;+    } else {+        /* force ArrayBuffer default constructor */+        ctor = JS_UNDEFINED;+    }+    size_log2 = typed_array_size_log2(classid);+    buffer = js_array_buffer_constructor1(ctx, ctor,+                                          (uint64_t)len << size_log2);+    JS_FreeValue(ctx, ctor);+    if (JS_IsException(buffer))+        goto fail;+    /* necessary because it could have been detached */+    if (typed_array_is_detached(ctx, p)) {+        JS_FreeValue(ctx, buffer);+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);+    if (typed_array_init(ctx, obj, buffer, 0, len))+        goto fail;+    if (p->class_id == classid) {+        /* same type: copy the content */+        memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);+    } else {+        for(i = 0; i < len; i++) {+            JSValue val;+            val = JS_GetPropertyUint32(ctx, src_obj, i);+            if (JS_IsException(val))+                goto fail;+            if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)+                goto fail;+        }+    }+    return obj;+ fail:+    JS_FreeValue(ctx, obj);+    return JS_EXCEPTION;+}++static JSValue js_typed_array_constructor(JSContext *ctx,+                                          JSValueConst new_target,+                                          int argc, JSValueConst *argv,+                                          int classid)+{+    JSValue buffer, obj;+    JSArrayBuffer *abuf;+    int size_log2;+    uint64_t len, offset;++    size_log2 = typed_array_size_log2(classid);+    if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {+        if (JS_ToIndex(ctx, &len, argv[0]))+            return JS_EXCEPTION;+        buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,+                                              len << size_log2);+        if (JS_IsException(buffer))+            return JS_EXCEPTION;+        offset = 0;+    } else {+        JSObject *p = JS_VALUE_GET_OBJ(argv[0]);+        if (p->class_id == JS_CLASS_ARRAY_BUFFER ||+            p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {+            abuf = p->u.array_buffer;+            if (JS_ToIndex(ctx, &offset, argv[1]))+                return JS_EXCEPTION;+            if (abuf->detached)+                return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+            if ((offset & ((1 << size_log2) - 1)) != 0 ||+                offset > abuf->byte_length)+                return JS_ThrowRangeError(ctx, "invalid offset");+            if (JS_IsUndefined(argv[2])) {+                if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)+                    goto invalid_length;+                len = (abuf->byte_length - offset) >> size_log2;+            } else {+                if (JS_ToIndex(ctx, &len, argv[2]))+                    return JS_EXCEPTION;+                if (abuf->detached)+                    return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+                if ((offset + (len << size_log2)) > abuf->byte_length) {+                invalid_length:+                    return JS_ThrowRangeError(ctx, "invalid length");+                }+            }+            buffer = JS_DupValue(ctx, argv[0]);+        } else {+            if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+                p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+                return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);+            } else {+                return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);+            }+        }+    }++    obj = js_create_from_ctor(ctx, new_target, classid);+    if (JS_IsException(obj)) {+        JS_FreeValue(ctx, buffer);+        return JS_EXCEPTION;+    }+    if (typed_array_init(ctx, obj, buffer, offset, len)) {+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    return obj;+}++static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSTypedArray *ta = p->u.typed_array;+    if (ta) {+        /* during the GC the finalizers are called in an arbitrary+           order so the ArrayBuffer finalizer may have been called */+        if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) {+            list_del(&ta->link);+        }+        JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));+        js_free_rt(rt, ta);+    }+}++static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,+                                JS_MarkFunc *mark_func)+{+    JSObject *p = JS_VALUE_GET_OBJ(val);+    JSTypedArray *ta = p->u.typed_array;+    if (ta) {+        JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);+    }+}++static JSValue js_dataview_constructor(JSContext *ctx,+                                       JSValueConst new_target,+                                       int argc, JSValueConst *argv)+{+    JSArrayBuffer *abuf;+    uint64_t offset;+    uint32_t len;+    JSValueConst buffer;+    JSValue obj;+    JSTypedArray *ta;+    JSObject *p;++    buffer = argv[0];+    abuf = js_get_array_buffer(ctx, buffer);+    if (!abuf)+        return JS_EXCEPTION;+    offset = 0;+    if (argc > 1) {+        if (JS_ToIndex(ctx, &offset, argv[1]))+            return JS_EXCEPTION;+    }+    if (abuf->detached)+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    if (offset > abuf->byte_length)+        return JS_ThrowRangeError(ctx, "invalid byteOffset");+    len = abuf->byte_length - offset;+    if (argc > 2 && !JS_IsUndefined(argv[2])) {+        uint64_t l;+        if (JS_ToIndex(ctx, &l, argv[2]))+            return JS_EXCEPTION;+        if (l > len)+            return JS_ThrowRangeError(ctx, "invalid byteLength");+        len = l;+    }++    obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);+    if (JS_IsException(obj))+        return JS_EXCEPTION;+    if (abuf->detached) {+        /* could have been detached in js_create_from_ctor() */+        JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        goto fail;+    }+    ta = js_malloc(ctx, sizeof(*ta));+    if (!ta) {+    fail:+        JS_FreeValue(ctx, obj);+        return JS_EXCEPTION;+    }+    p = JS_VALUE_GET_OBJ(obj);+    ta->obj = p;+    ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));+    ta->offset = offset;+    ta->length = len;+    list_add_tail(&ta->link, &abuf->array_list);+    p->u.typed_array = ta;+    return obj;+}++static JSValue js_dataview_getValue(JSContext *ctx,+                                    JSValueConst this_obj,+                                    int argc, JSValueConst *argv, int class_id)+{+    JSTypedArray *ta;+    JSArrayBuffer *abuf;+    int is_swap, size;+    uint8_t *ptr;+    uint32_t v;+    uint64_t pos;++    ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);+    if (!ta)+        return JS_EXCEPTION;+    size = 1 << typed_array_size_log2(class_id);+    if (JS_ToIndex(ctx, &pos, argv[0]))+        return JS_EXCEPTION;+    is_swap = FALSE;+    if (argc > 1)+        is_swap = JS_ToBool(ctx, argv[1]);+#ifndef WORDS_BIGENDIAN+    is_swap ^= 1;+#endif+    abuf = ta->buffer->u.array_buffer;+    if (abuf->detached)+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    if ((pos + size) > ta->length)+        return JS_ThrowRangeError(ctx, "out of bound");+    ptr = abuf->data + ta->offset + pos;++    switch(class_id) {+    case JS_CLASS_INT8_ARRAY:+        return JS_NewInt32(ctx, *(int8_t *)ptr);+    case JS_CLASS_UINT8_ARRAY:+        return JS_NewInt32(ctx, *(uint8_t *)ptr);+    case JS_CLASS_INT16_ARRAY:+        v = get_u16(ptr);+        if (is_swap)+            v = bswap16(v);+        return JS_NewInt32(ctx, (int16_t)v);+    case JS_CLASS_UINT16_ARRAY:+        v = get_u16(ptr);+        if (is_swap)+            v = bswap16(v);+        return JS_NewInt32(ctx, v);+    case JS_CLASS_INT32_ARRAY:+        v = get_u32(ptr);+        if (is_swap)+            v = bswap32(v);+        return JS_NewInt32(ctx, v);+    case JS_CLASS_UINT32_ARRAY:+        v = get_u32(ptr);+        if (is_swap)+            v = bswap32(v);+        return JS_NewUint32(ctx, v);+#ifdef CONFIG_BIGNUM+    case JS_CLASS_BIG_INT64_ARRAY:+        {+            uint64_t v;+            v = get_u64(ptr);+            if (is_swap)+                v = bswap64(v);+            return JS_NewBigInt64(ctx, v);+        }+        break;+    case JS_CLASS_BIG_UINT64_ARRAY:+        {+            uint64_t v;+            v = get_u64(ptr);+            if (is_swap)+                v = bswap64(v);+            return JS_NewBigUint64(ctx, v);+        }+        break;+#endif+    case JS_CLASS_FLOAT32_ARRAY:+        {+            union {+                float f;+                uint32_t i;+            } u;+            v = get_u32(ptr);+            if (is_swap)+                v = bswap32(v);+            u.i = v;+            return __JS_NewFloat64(ctx, u.f);+        }+    case JS_CLASS_FLOAT64_ARRAY:+        {+            union {+                double f;+                uint64_t i;+            } u;+            u.i = get_u64(ptr);+            if (is_swap)+                u.i = bswap64(u.i);+            return __JS_NewFloat64(ctx, u.f);+        }+    default:+        abort();+    }+}++static JSValue js_dataview_setValue(JSContext *ctx,+                                    JSValueConst this_obj,+                                    int argc, JSValueConst *argv, int class_id)+{+    JSTypedArray *ta;+    JSArrayBuffer *abuf;+    int is_swap, size;+    uint8_t *ptr;+    uint64_t v64;+    uint32_t v;+    uint64_t pos;+    JSValueConst val;++    ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);+    if (!ta)+        return JS_EXCEPTION;+    size = 1 << typed_array_size_log2(class_id);+    if (JS_ToIndex(ctx, &pos, argv[0]))+        return JS_EXCEPTION;+    val = argv[1];+    v = 0; /* avoid warning */+    v64 = 0; /* avoid warning */+    if (class_id <= JS_CLASS_UINT32_ARRAY) {+        if (JS_ToUint32(ctx, &v, val))+            return JS_EXCEPTION;+    } else+#ifdef CONFIG_BIGNUM+    if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {+        if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))+            return JS_EXCEPTION;+    } else+#endif+    {+        double d;+        if (JS_ToFloat64(ctx, &d, val))+            return JS_EXCEPTION;+        if (class_id == JS_CLASS_FLOAT32_ARRAY) {+            union {+                float f;+                uint32_t i;+            } u;+            u.f = d;+            v = u.i;+        } else {+            JSFloat64Union u;+            u.d = d;+            v64 = u.u64;+        }+    }+    is_swap = FALSE;+    if (argc > 2)+        is_swap = JS_ToBool(ctx, argv[2]);+#ifndef WORDS_BIGENDIAN+    is_swap ^= 1;+#endif+    abuf = ta->buffer->u.array_buffer;+    if (abuf->detached)+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+    if ((pos + size) > ta->length)+        return JS_ThrowRangeError(ctx, "out of bound");+    ptr = abuf->data + ta->offset + pos;++    switch(class_id) {+    case JS_CLASS_INT8_ARRAY:+    case JS_CLASS_UINT8_ARRAY:+        *ptr = v;+        break;+    case JS_CLASS_INT16_ARRAY:+    case JS_CLASS_UINT16_ARRAY:+        if (is_swap)+            v = bswap16(v);+        put_u16(ptr, v);+        break;+    case JS_CLASS_INT32_ARRAY:+    case JS_CLASS_UINT32_ARRAY:+    case JS_CLASS_FLOAT32_ARRAY:+        if (is_swap)+            v = bswap32(v);+        put_u32(ptr, v);+        break;+#ifdef CONFIG_BIGNUM+    case JS_CLASS_BIG_INT64_ARRAY:+    case JS_CLASS_BIG_UINT64_ARRAY:+#endif+    case JS_CLASS_FLOAT64_ARRAY:+        if (is_swap)+            v64 = bswap64(v64);+        put_u64(ptr, v64);+        break;+    default:+        abort();+    }+    return JS_UNDEFINED;+}++static const JSCFunctionListEntry js_dataview_proto_funcs[] = {+    JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),+    JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),+    JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),+    JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),+#ifdef CONFIG_BIGNUM+    JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),+#endif+    JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),+    JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),+#ifdef CONFIG_BIGNUM+    JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),+#endif+    JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),+    JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),+};++/* Atomics */+#ifdef CONFIG_ATOMICS++typedef enum AtomicsOpEnum {+    ATOMICS_OP_ADD,+    ATOMICS_OP_AND,+    ATOMICS_OP_OR,+    ATOMICS_OP_SUB,+    ATOMICS_OP_XOR,+    ATOMICS_OP_EXCHANGE,+    ATOMICS_OP_COMPARE_EXCHANGE,+    ATOMICS_OP_LOAD,+} AtomicsOpEnum;++static void *js_atomics_get_ptr(JSContext *ctx,+                                JSArrayBuffer **pabuf,+                                int *psize_log2, JSClassID *pclass_id,+                                JSValueConst obj, JSValueConst idx_val,+                                int is_waitable)+{+    JSObject *p;+    JSTypedArray *ta;+    JSArrayBuffer *abuf;+    void *ptr;+    uint64_t idx;+    BOOL err;+    int size_log2;++    if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+        goto fail;+    p = JS_VALUE_GET_OBJ(obj);+#ifdef CONFIG_BIGNUM+    if (is_waitable)+        err = (p->class_id != JS_CLASS_INT32_ARRAY &&+               p->class_id != JS_CLASS_BIG_INT64_ARRAY);+    else+        err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&+                p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);+#else+    if (is_waitable)+        err = (p->class_id != JS_CLASS_INT32_ARRAY);+    else+        err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&+                p->class_id <= JS_CLASS_UINT32_ARRAY);+#endif+    if (err) {+    fail:+        JS_ThrowTypeError(ctx, "integer TypedArray expected");+        return NULL;+    }+    ta = p->u.typed_array;+    abuf = ta->buffer->u.array_buffer;+    if (!abuf->shared) {+        if (is_waitable == 2) {+            JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");+            return NULL;+        }+        if (abuf->detached) {+            JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+            return NULL;+        }+    }+    if (JS_ToIndex(ctx, &idx, idx_val)) {+        return NULL;+    }+    /* if the array buffer is detached, p->u.array.count = 0 */+    if (idx >= p->u.array.count) {+        JS_ThrowRangeError(ctx, "out-of-bound access");+        return NULL;+    }+    size_log2 = typed_array_size_log2(p->class_id);+    ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);+    if (pabuf)+        *pabuf = abuf;+    if (psize_log2)+        *psize_log2 = size_log2;+    if (pclass_id)+        *pclass_id = p->class_id;+    return ptr;+}++static JSValue js_atomics_op(JSContext *ctx,+                             JSValueConst this_obj,+                             int argc, JSValueConst *argv, int op)+{+    int size_log2;+#ifdef CONFIG_BIGNUM+    uint64_t v, a, rep_val;+#else+    uint32_t v, a, rep_val;+#endif+    void *ptr;+    JSValue ret;+    JSClassID class_id;+    JSArrayBuffer *abuf;++    ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,+                             argv[0], argv[1], 0);+    if (!ptr)+        return JS_EXCEPTION;+    rep_val = 0;+    if (op == ATOMICS_OP_LOAD) {+        v = 0;+    } else {+#ifdef CONFIG_BIGNUM+        if (size_log2 == 3) {+            int64_t v64;+            if (JS_ToBigInt64(ctx, &v64, argv[2]))+                return JS_EXCEPTION;+            v = v64;+            if (op == ATOMICS_OP_COMPARE_EXCHANGE) {+                if (JS_ToBigInt64(ctx, &v64, argv[3]))+                    return JS_EXCEPTION;+                rep_val = v64;+            }+        } else+#endif+        {+                uint32_t v32;+                if (JS_ToUint32(ctx, &v32, argv[2]))+                    return JS_EXCEPTION;+                v = v32;+                if (op == ATOMICS_OP_COMPARE_EXCHANGE) {+                    if (JS_ToUint32(ctx, &v32, argv[3]))+                        return JS_EXCEPTION;+                    rep_val = v32;+                }+        }+        if (abuf->detached)+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+   }++   switch(op | (size_log2 << 3)) {+            +#ifdef CONFIG_BIGNUM+#define OP(op_name, func_name)                          \+    case ATOMICS_OP_ ## op_name | (0 << 3):             \+       a = func_name((_Atomic(uint8_t) *)ptr, v);       \+       break;                                           \+    case ATOMICS_OP_ ## op_name | (1 << 3):             \+        a = func_name((_Atomic(uint16_t) *)ptr, v);     \+        break;                                          \+    case ATOMICS_OP_ ## op_name | (2 << 3):             \+        a = func_name((_Atomic(uint32_t) *)ptr, v);     \+        break;                                          \+    case ATOMICS_OP_ ## op_name | (3 << 3):             \+        a = func_name((_Atomic(uint64_t) *)ptr, v);     \+        break;+#else+#define OP(op_name, func_name)                          \+    case ATOMICS_OP_ ## op_name | (0 << 3):             \+       a = func_name((_Atomic(uint8_t) *)ptr, v);       \+       break;                                           \+    case ATOMICS_OP_ ## op_name | (1 << 3):             \+        a = func_name((_Atomic(uint16_t) *)ptr, v);     \+        break;                                          \+    case ATOMICS_OP_ ## op_name | (2 << 3):             \+        a = func_name((_Atomic(uint32_t) *)ptr, v);     \+        break;+#endif+        OP(ADD, atomic_fetch_add)+        OP(AND, atomic_fetch_and)+        OP(OR, atomic_fetch_or)+        OP(SUB, atomic_fetch_sub)+        OP(XOR, atomic_fetch_xor)+        OP(EXCHANGE, atomic_exchange)+#undef OP++    case ATOMICS_OP_LOAD | (0 << 3):+        a = atomic_load((_Atomic(uint8_t) *)ptr);+        break;+    case ATOMICS_OP_LOAD | (1 << 3):+        a = atomic_load((_Atomic(uint16_t) *)ptr);+        break;+    case ATOMICS_OP_LOAD | (2 << 3):+        a = atomic_load((_Atomic(uint32_t) *)ptr);+        break;+#ifdef CONFIG_BIGNUM+    case ATOMICS_OP_LOAD | (3 << 3):+        a = atomic_load((_Atomic(uint64_t) *)ptr);+        break;+#endif+        +    case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):+        {+            uint8_t v1 = v;+            atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);+            a = v1;+        }+        break;+    case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):+        {+            uint16_t v1 = v;+            atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);+            a = v1;+        }+        break;+    case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):+        {+            uint32_t v1 = v;+            atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);+            a = v1;+        }+        break;+#ifdef CONFIG_BIGNUM+    case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):+        {+            uint64_t v1 = v;+            atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);+            a = v1;+        }+        break;+#endif+    default:+        abort();+    }++    switch(class_id) {+    case JS_CLASS_INT8_ARRAY:+        a = (int8_t)a;+        goto done;+    case JS_CLASS_UINT8_ARRAY:+        a = (uint8_t)a;+        goto done;+    case JS_CLASS_INT16_ARRAY:+        a = (int16_t)a;+        goto done;+    case JS_CLASS_UINT16_ARRAY:+        a = (uint16_t)a;+        goto done;+    case JS_CLASS_INT32_ARRAY:+    done:+        ret = JS_NewInt32(ctx, a);+        break;+    case JS_CLASS_UINT32_ARRAY:+        ret = JS_NewUint32(ctx, a);+        break;+#ifdef CONFIG_BIGNUM+    case JS_CLASS_BIG_INT64_ARRAY:+        ret = JS_NewBigInt64(ctx, a);+        break;+    case JS_CLASS_BIG_UINT64_ARRAY:+        ret = JS_NewBigUint64(ctx, a);+        break;+#endif+    default:+        abort();+    }+    return ret;+}++static JSValue js_atomics_store(JSContext *ctx,+                                JSValueConst this_obj,+                                int argc, JSValueConst *argv)+{+    int size_log2;+    void *ptr;+    JSValue ret;+    JSArrayBuffer *abuf;++    ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,+                             argv[0], argv[1], 0);+    if (!ptr)+        return JS_EXCEPTION;+#ifdef CONFIG_BIGNUM+    if (size_log2 == 3) {+        int64_t v64;+        ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));+        if (JS_IsException(ret))+            return ret;+        if (JS_ToBigInt64(ctx, &v64, ret)) {+            JS_FreeValue(ctx, ret);+            return JS_EXCEPTION;+        }+        if (abuf->detached)+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        atomic_store((_Atomic(uint64_t) *)ptr, v64);+    } else+#endif+    {+        uint32_t v;+        /* XXX: spec, would be simpler to return the written value */+        ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));+        if (JS_IsException(ret))+            return ret;+        if (JS_ToUint32(ctx, &v, ret)) {+            JS_FreeValue(ctx, ret);+            return JS_EXCEPTION;+        }+        if (abuf->detached)+            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+        switch(size_log2) {+        case 0:+            atomic_store((_Atomic(uint8_t) *)ptr, v);+            break;+        case 1:+            atomic_store((_Atomic(uint16_t) *)ptr, v);+            break;+        case 2:+            atomic_store((_Atomic(uint32_t) *)ptr, v);+            break;+        default:+            abort();+        }+    }+    return ret;+}++static JSValue js_atomics_isLockFree(JSContext *ctx,+                                     JSValueConst this_obj,+                                     int argc, JSValueConst *argv)+{+    int v, ret;+    if (JS_ToInt32Sat(ctx, &v, argv[0]))+        return JS_EXCEPTION;+    ret = (v == 1 || v == 2 || v == 4+#ifdef CONFIG_BIGNUM+           || v == 8+#endif+           );+    return JS_NewBool(ctx, ret);+}++typedef struct JSAtomicsWaiter {+    struct list_head link;+    BOOL linked;+    pthread_cond_t cond;+    int32_t *ptr;+} JSAtomicsWaiter;++static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;+static struct list_head js_atomics_waiter_list =+    LIST_HEAD_INIT(js_atomics_waiter_list);++static JSValue js_atomics_wait(JSContext *ctx,+                               JSValueConst this_obj,+                               int argc, JSValueConst *argv)+{+    int64_t v;+    int32_t v32;+    void *ptr;+    int64_t timeout;+    struct timespec ts;+    JSAtomicsWaiter waiter_s, *waiter;+    int ret, size_log2, res;+    double d;++    ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,+                             argv[0], argv[1], 2);+    if (!ptr)+        return JS_EXCEPTION;+#ifdef CONFIG_BIGNUM+    if (size_log2 == 3) {+        if (JS_ToBigInt64(ctx, &v, argv[2]))+            return JS_EXCEPTION;+    } else+#endif+    {        +        if (JS_ToInt32(ctx, &v32, argv[2]))+            return JS_EXCEPTION;+        v = v32;+    }+    if (JS_ToFloat64(ctx, &d, argv[3]))+        return JS_EXCEPTION;+    if (isnan(d) || d > INT64_MAX)+        timeout = INT64_MAX;+    else if (d < 0)+        timeout = 0;+    else+        timeout = (int64_t)d;+    if (!ctx->rt->can_block)+        return JS_ThrowTypeError(ctx, "cannot block in this thread");++    /* XXX: inefficient if large number of waiters, should hash on+       'ptr' value */+    /* XXX: use Linux futexes when available ? */+    pthread_mutex_lock(&js_atomics_mutex);+    if (size_log2 == 3) {+        res = *(int64_t *)ptr != v;+    } else {+        res = *(int32_t *)ptr != v;+    }+    if (res) {+        pthread_mutex_unlock(&js_atomics_mutex);+        return JS_AtomToString(ctx, JS_ATOM_not_equal);+    }++    waiter = &waiter_s;+    waiter->ptr = ptr;+    pthread_cond_init(&waiter->cond, NULL);+    waiter->linked = TRUE;+    list_add_tail(&waiter->link, &js_atomics_waiter_list);++    if (timeout == INT64_MAX) {+        pthread_cond_wait(&waiter->cond, &js_atomics_mutex);+        ret = 0;+    } else {+        /* XXX: use clock monotonic */+        clock_gettime(CLOCK_REALTIME, &ts);+        ts.tv_sec += timeout / 1000;+        ts.tv_nsec += (timeout % 1000) * 1000000;+        if (ts.tv_nsec >= 1000000000) {+            ts.tv_nsec -= 1000000000;+            ts.tv_sec++;+        }+        ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,+                                     &ts);+    }+    if (waiter->linked)+        list_del(&waiter->link);+    pthread_mutex_unlock(&js_atomics_mutex);+    pthread_cond_destroy(&waiter->cond);+    if (ret == ETIMEDOUT) {+        return JS_AtomToString(ctx, JS_ATOM_timed_out);+    } else {+        return JS_AtomToString(ctx, JS_ATOM_ok);+    }+}++static JSValue js_atomics_notify(JSContext *ctx,+                                 JSValueConst this_obj,+                                 int argc, JSValueConst *argv)+{+    struct list_head *el, *el1, waiter_list;+    int32_t count, n;+    void *ptr;+    JSAtomicsWaiter *waiter;+    JSArrayBuffer *abuf;++    ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);+    if (!ptr)+        return JS_EXCEPTION;++    if (JS_IsUndefined(argv[2])) {+        count = INT32_MAX;+    } else {+        if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))+            return JS_EXCEPTION;+    }+    if (abuf->detached)+        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);++    n = 0;+    if (abuf->shared && count > 0) {+        pthread_mutex_lock(&js_atomics_mutex);+        init_list_head(&waiter_list);+        list_for_each_safe(el, el1, &js_atomics_waiter_list) {+            waiter = list_entry(el, JSAtomicsWaiter, link);+            if (waiter->ptr == ptr) {+                list_del(&waiter->link);+                waiter->linked = FALSE;+                list_add_tail(&waiter->link, &waiter_list);+                n++;+                if (n >= count)+                    break;+            }+        }+        list_for_each(el, &waiter_list) {+            waiter = list_entry(el, JSAtomicsWaiter, link);+            pthread_cond_signal(&waiter->cond);+        }+        pthread_mutex_unlock(&js_atomics_mutex);+    }+    return JS_NewInt32(ctx, n);+}++static const JSCFunctionListEntry js_atomics_funcs[] = {+    JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),+    JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),+    JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),+    JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),+    JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),+    JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),+    JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),+    JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),+    JS_CFUNC_DEF("store", 3, js_atomics_store ),+    JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),+    JS_CFUNC_DEF("wait", 4, js_atomics_wait ),+    JS_CFUNC_DEF("notify", 3, js_atomics_notify ),+    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_atomics_obj[] = {+    JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicAtomics(JSContext *ctx)+{+    /* add Atomics as autoinit object */+    JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));+}++#endif /* CONFIG_ATOMICS */++void JS_AddIntrinsicTypedArrays(JSContext *ctx)+{+    JSValue typed_array_base_proto, typed_array_base_func;+    JSValueConst array_buffer_func, shared_array_buffer_func;+    int i;++    ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],+                               js_array_buffer_proto_funcs,+                               countof(js_array_buffer_proto_funcs));++    array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",+                                                 js_array_buffer_constructor, 1,+                                                 ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);+    JS_SetPropertyFunctionList(ctx, array_buffer_func,+                               js_array_buffer_funcs,+                               countof(js_array_buffer_funcs));++    ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],+                               js_shared_array_buffer_proto_funcs,+                               countof(js_shared_array_buffer_proto_funcs));++    shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",+                                                 js_shared_array_buffer_constructor, 1,+                                                 ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);+    JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,+                               js_shared_array_buffer_funcs,+                               countof(js_shared_array_buffer_funcs));++    typed_array_base_proto = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, typed_array_base_proto,+                               js_typed_array_base_proto_funcs,+                               countof(js_typed_array_base_proto_funcs));++    /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */+    JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);+    /* XXX: should use alias method in JSCFunctionListEntry */ //@@@+    JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,+                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);++    typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,+                                            "TypedArray", 0);+    JS_SetPropertyFunctionList(ctx, typed_array_base_func,+                               js_typed_array_base_funcs,+                               countof(js_typed_array_base_funcs));+    JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);++    for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {+        JSValue func_obj;+        char buf[ATOM_GET_STR_BUF_SIZE];+        const char *name;++        ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);+        JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],+                                  "BYTES_PER_ELEMENT",+                                  JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),+                                  0);+        name = JS_AtomGetStr(ctx, buf, sizeof(buf),+                             JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);+        func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor,+                                    name, 3, JS_CFUNC_constructor_magic, i,+                                    typed_array_base_func);+        JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);+        JS_DefinePropertyValueStr(ctx, func_obj,+                                  "BYTES_PER_ELEMENT",+                                  JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),+                                  0);+    }+    JS_FreeValue(ctx, typed_array_base_proto);+    JS_FreeValue(ctx, typed_array_base_func);++    /* DataView */+    ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);+    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],+                               js_dataview_proto_funcs,+                               countof(js_dataview_proto_funcs));+    JS_NewGlobalCConstructorOnly(ctx, "DataView",+                                 js_dataview_constructor, 1,+                                 ctx->class_proto[JS_CLASS_DATAVIEW]);+    /* Atomics */+#ifdef CONFIG_ATOMICS+    JS_AddIntrinsicAtomics(ctx);+#endif+}
+ quickjs/quickjs.h view
@@ -0,0 +1,1030 @@+/*+ * QuickJS Javascript Engine+ *+ * Copyright (c) 2017-2020 Fabrice Bellard+ * Copyright (c) 2017-2020 Charlie Gordon+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to deal+ * in the Software without restriction, including without limitation the rights+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ * copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ * THE SOFTWARE.+ */+#ifndef QUICKJS_H+#define QUICKJS_H++#include <stdio.h>+#include <stdint.h>++#ifdef __cplusplus+extern "C" {+#endif++#if defined(__GNUC__) || defined(__clang__)+#define js_likely(x)          __builtin_expect(!!(x), 1)+#define js_unlikely(x)        __builtin_expect(!!(x), 0)+#define js_force_inline       inline __attribute__((always_inline))+#define __js_printf_like(f, a)   __attribute__((format(printf, f, a)))+#else+#define js_likely(x)     (x)+#define js_unlikely(x)   (x)+#define js_force_inline  inline+#define __js_printf_like(a, b)+#endif++#define JS_BOOL int++typedef struct JSRuntime JSRuntime;+typedef struct JSContext JSContext;+typedef struct JSObject JSObject;+typedef struct JSClass JSClass;+typedef uint32_t JSClassID;+typedef uint32_t JSAtom;++#if INTPTR_MAX >= INT64_MAX+#define JS_PTR64+#define JS_PTR64_DEF(a) a+#else+#define JS_PTR64_DEF(a)+#endif++#ifndef JS_PTR64+#define JS_NAN_BOXING+#endif++enum {+    /* all tags with a reference count are negative */+    JS_TAG_FIRST       = -11, /* first negative tag */+    JS_TAG_BIG_DECIMAL = -11,+    JS_TAG_BIG_INT     = -10,+    JS_TAG_BIG_FLOAT   = -9,+    JS_TAG_SYMBOL      = -8,+    JS_TAG_STRING      = -7,+    JS_TAG_MODULE      = -3, /* used internally */+    JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */+    JS_TAG_OBJECT      = -1,++    JS_TAG_INT         = 0,+    JS_TAG_BOOL        = 1,+    JS_TAG_NULL        = 2,+    JS_TAG_UNDEFINED   = 3,+    JS_TAG_UNINITIALIZED = 4,+    JS_TAG_CATCH_OFFSET = 5,+    JS_TAG_EXCEPTION   = 6,+    JS_TAG_FLOAT64     = 7,+    /* any larger tag is FLOAT64 if JS_NAN_BOXING */+};++typedef struct JSRefCountHeader {+    int ref_count;+} JSRefCountHeader;++#define JS_FLOAT64_NAN NAN++#ifdef CONFIG_CHECK_JSVALUE+/* JSValue consistency : it is not possible to run the code in this+   mode, but it is useful to detect simple reference counting+   errors. It would be interesting to modify a static C analyzer to+   handle specific annotations (clang has such annotations but only+   for objective C) */+typedef struct __JSValue *JSValue;+typedef const struct __JSValue *JSValueConst;++#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf)+/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */+#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)+#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)+#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)+#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)+#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)++#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))+#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag))++#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)++#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1)++static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)+{+    return JS_MKVAL(JS_TAG_FLOAT64, (int)d);+}++static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)+{+    return 0;+}+    +#elif defined(JS_NAN_BOXING)++typedef uint64_t JSValue;++#define JSValueConst JSValue++#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)+#define JS_VALUE_GET_INT(v) (int)(v)+#define JS_VALUE_GET_BOOL(v) (int)(v)+#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)++#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))+#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr))++#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */++static inline double JS_VALUE_GET_FLOAT64(JSValue v)+{+    union {+        JSValue v;+        double d;+    } u;+    u.v = v;+    u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32;+    return u.d;+}++#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32))++static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)+{+    union {+        double d;+        uint64_t u64;+    } u;+    JSValue v;+    u.d = d;+    /* normalize NaN */+    if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))+        v = JS_NAN;+    else+        v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32);+    return v;+}++#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST))++/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */+static inline int JS_VALUE_GET_NORM_TAG(JSValue v)+{+    uint32_t tag;+    tag = JS_VALUE_GET_TAG(v);+    if (JS_TAG_IS_FLOAT64(tag))+        return JS_TAG_FLOAT64;+    else+        return tag;+}++static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)+{+    uint32_t tag;+    tag = JS_VALUE_GET_TAG(v);+    return tag == (JS_NAN >> 32);+}+    +#else /* !JS_NAN_BOXING */++typedef union JSValueUnion {+    int32_t int32;+    double float64;+    void *ptr;+} JSValueUnion;++typedef struct JSValue {+    JSValueUnion u;+    int64_t tag;+} JSValue;++#define JSValueConst JSValue++#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag)+/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */+#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)+#define JS_VALUE_GET_INT(v) ((v).u.int32)+#define JS_VALUE_GET_BOOL(v) ((v).u.int32)+#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)+#define JS_VALUE_GET_PTR(v) ((v).u.ptr)++#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }+#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }++#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)++#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }++static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)+{+    JSValue v;+    v.tag = JS_TAG_FLOAT64;+    v.u.float64 = d;+    return v;+}++static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)+{+    union {+        double d;+        uint64_t u64;+    } u;+    if (v.tag != JS_TAG_FLOAT64)+        return 0;+    u.d = v.u.float64;+    return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;+}++#endif /* !JS_NAN_BOXING */++#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)+#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))++#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))+#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))+#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)++/* special values */+#define JS_NULL      JS_MKVAL(JS_TAG_NULL, 0)+#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0)+#define JS_FALSE     JS_MKVAL(JS_TAG_BOOL, 0)+#define JS_TRUE      JS_MKVAL(JS_TAG_BOOL, 1)+#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0)+#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0)++/* flags for object properties */+#define JS_PROP_CONFIGURABLE  (1 << 0)+#define JS_PROP_WRITABLE      (1 << 1)+#define JS_PROP_ENUMERABLE    (1 << 2)+#define JS_PROP_C_W_E         (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)+#define JS_PROP_LENGTH        (1 << 3) /* used internally in Arrays */+#define JS_PROP_TMASK         (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */+#define JS_PROP_NORMAL         (0 << 4)+#define JS_PROP_GETSET         (1 << 4)+#define JS_PROP_VARREF         (2 << 4) /* used internally */+#define JS_PROP_AUTOINIT       (3 << 4) /* used internally */++/* flags for JS_DefineProperty */+#define JS_PROP_HAS_SHIFT        8+#define JS_PROP_HAS_CONFIGURABLE (1 << 8)+#define JS_PROP_HAS_WRITABLE     (1 << 9)+#define JS_PROP_HAS_ENUMERABLE   (1 << 10)+#define JS_PROP_HAS_GET          (1 << 11)+#define JS_PROP_HAS_SET          (1 << 12)+#define JS_PROP_HAS_VALUE        (1 << 13)++/* throw an exception if false would be returned+   (JS_DefineProperty/JS_SetProperty) */+#define JS_PROP_THROW            (1 << 14)+/* throw an exception if false would be returned in strict mode+   (JS_SetProperty) */+#define JS_PROP_THROW_STRICT     (1 << 15)++#define JS_PROP_NO_ADD           (1 << 16) /* internal use */+#define JS_PROP_NO_EXOTIC        (1 << 17) /* internal use */++#define JS_DEFAULT_STACK_SIZE (256 * 1024)++/* JS_Eval() flags */+#define JS_EVAL_TYPE_GLOBAL   (0 << 0) /* global code (default) */+#define JS_EVAL_TYPE_MODULE   (1 << 0) /* module code */+#define JS_EVAL_TYPE_DIRECT   (2 << 0) /* direct call (internal use) */+#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */+#define JS_EVAL_TYPE_MASK     (3 << 0)++#define JS_EVAL_FLAG_STRICT   (1 << 3) /* force 'strict' mode */+#define JS_EVAL_FLAG_STRIP    (1 << 4) /* force 'strip' mode */+/* compile but do not run. The result is an object with a+   JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed+   with JS_EvalFunction(). */+#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)+/* don't include the stack frames before this eval in the Error() backtraces */+#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)++typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);+typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);+typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data);++typedef struct JSMallocState {+    size_t malloc_count;+    size_t malloc_size;+    size_t malloc_limit;+    void *opaque; /* user opaque */+} JSMallocState;++typedef struct JSMallocFunctions {+    void *(*js_malloc)(JSMallocState *s, size_t size);+    void (*js_free)(JSMallocState *s, void *ptr);+    void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);+    size_t (*js_malloc_usable_size)(const void *ptr);+} JSMallocFunctions;++typedef struct JSGCObjectHeader JSGCObjectHeader;++JSRuntime *JS_NewRuntime(void);+/* info lifetime must exceed that of rt */+void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);+void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);+void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);+void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size);+JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque);+void JS_FreeRuntime(JSRuntime *rt);+void *JS_GetRuntimeOpaque(JSRuntime *rt);+void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque);+typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp);+void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);+void JS_RunGC(JSRuntime *rt);+JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);++JSContext *JS_NewContext(JSRuntime *rt);+void JS_FreeContext(JSContext *s);+JSContext *JS_DupContext(JSContext *ctx);+void *JS_GetContextOpaque(JSContext *ctx);+void JS_SetContextOpaque(JSContext *ctx, void *opaque);+JSRuntime *JS_GetRuntime(JSContext *ctx);+void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj);+JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id);++/* the following functions are used to select the intrinsic object to+   save memory */+JSContext *JS_NewContextRaw(JSRuntime *rt);+void JS_AddIntrinsicBaseObjects(JSContext *ctx);+void JS_AddIntrinsicDate(JSContext *ctx);+void JS_AddIntrinsicEval(JSContext *ctx);+void JS_AddIntrinsicStringNormalize(JSContext *ctx);+void JS_AddIntrinsicRegExpCompiler(JSContext *ctx);+void JS_AddIntrinsicRegExp(JSContext *ctx);+void JS_AddIntrinsicJSON(JSContext *ctx);+void JS_AddIntrinsicProxy(JSContext *ctx);+void JS_AddIntrinsicMapSet(JSContext *ctx);+void JS_AddIntrinsicTypedArrays(JSContext *ctx);+void JS_AddIntrinsicPromise(JSContext *ctx);+void JS_AddIntrinsicBigInt(JSContext *ctx);+void JS_AddIntrinsicBigFloat(JSContext *ctx);+void JS_AddIntrinsicBigDecimal(JSContext *ctx);+/* enable operator overloading */+void JS_AddIntrinsicOperators(JSContext *ctx);+/* enable "use math" */+void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);++JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,+                                 int argc, JSValueConst *argv);++void *js_malloc_rt(JSRuntime *rt, size_t size);+void js_free_rt(JSRuntime *rt, void *ptr);+void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size);+size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr);+void *js_mallocz_rt(JSRuntime *rt, size_t size);++void *js_malloc(JSContext *ctx, size_t size);+void js_free(JSContext *ctx, void *ptr);+void *js_realloc(JSContext *ctx, void *ptr, size_t size);+size_t js_malloc_usable_size(JSContext *ctx, const void *ptr);+void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack);+void *js_mallocz(JSContext *ctx, size_t size);+char *js_strdup(JSContext *ctx, const char *str);+char *js_strndup(JSContext *ctx, const char *s, size_t n);++typedef struct JSMemoryUsage {+    int64_t malloc_size, malloc_limit, memory_used_size;+    int64_t malloc_count;+    int64_t memory_used_count;+    int64_t atom_count, atom_size;+    int64_t str_count, str_size;+    int64_t obj_count, obj_size;+    int64_t prop_count, prop_size;+    int64_t shape_count, shape_size;+    int64_t js_func_count, js_func_size, js_func_code_size;+    int64_t js_func_pc2line_count, js_func_pc2line_size;+    int64_t c_func_count, array_count;+    int64_t fast_array_count, fast_array_elements;+    int64_t binary_object_count, binary_object_size;+} JSMemoryUsage;++void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s);+void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);++/* atom support */+JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len);+JSAtom JS_NewAtom(JSContext *ctx, const char *str);+JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n);+JSAtom JS_DupAtom(JSContext *ctx, JSAtom v);+void JS_FreeAtom(JSContext *ctx, JSAtom v);+void JS_FreeAtomRT(JSRuntime *rt, JSAtom v);+JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom);+JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);+const char *JS_AtomToCString(JSContext *ctx, JSAtom atom);+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val);++/* object class support */++typedef struct JSPropertyEnum {+    JS_BOOL is_enumerable;+    JSAtom atom;+} JSPropertyEnum;++typedef struct JSPropertyDescriptor {+    int flags;+    JSValue value;+    JSValue getter;+    JSValue setter;+} JSPropertyDescriptor;++typedef struct JSClassExoticMethods {+    /* Return -1 if exception (can only happen in case of Proxy object),+       FALSE if the property does not exists, TRUE if it exists. If 1 is+       returned, the property descriptor 'desc' is filled if != NULL. */+    int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc,+                             JSValueConst obj, JSAtom prop);+    /* '*ptab' should hold the '*plen' property keys. Return 0 if OK,+       -1 if exception. The 'is_enumerable' field is ignored.+    */+    int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab,+                                  uint32_t *plen,+                                  JSValueConst obj);+    /* return < 0 if exception, or TRUE/FALSE */+    int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop);+    /* return < 0 if exception or TRUE/FALSE */+    int (*define_own_property)(JSContext *ctx, JSValueConst this_obj,+                               JSAtom prop, JSValueConst val,+                               JSValueConst getter, JSValueConst setter,+                               int flags);+    /* The following methods can be emulated with the previous ones,+       so they are usually not needed */+    /* return < 0 if exception or TRUE/FALSE */+    int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom);+    JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,+                            JSValueConst receiver);+    /* return < 0 if exception or TRUE/FALSE */+    int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,+                        JSValueConst value, JSValueConst receiver, int flags);+} JSClassExoticMethods;++typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);+typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val,+                           JS_MarkFunc *mark_func);+#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0)+typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj,+                            JSValueConst this_val, int argc, JSValueConst *argv,+                            int flags);++typedef struct JSClassDef {+    const char *class_name;+    JSClassFinalizer *finalizer;+    JSClassGCMark *gc_mark;+    /* if call != NULL, the object is a function. If (flags &+       JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a+       constructor. In this case, 'this_val' is new.target. A+       constructor call only happens if the object constructor bit is+       set (see JS_SetConstructorBit()). */+    JSClassCall *call;+    /* XXX: suppress this indirection ? It is here only to save memory+       because only a few classes need these methods */+    JSClassExoticMethods *exotic;+} JSClassDef;++JSClassID JS_NewClassID(JSClassID *pclass_id);+int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);+int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);++/* value handling */++static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)+{+    return JS_MKVAL(JS_TAG_BOOL, (val != 0));+}++static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val)+{+    return JS_MKVAL(JS_TAG_INT, val);+}++static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val)+{+    return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);+}++static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val)+{+    JSValue v;+    if (val == (int32_t)val) {+        v = JS_NewInt32(ctx, val);+    } else {+        v = __JS_NewFloat64(ctx, val);+    }+    return v;+}++static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)+{+    JSValue v;+    if (val <= 0x7fffffff) {+        v = JS_NewInt32(ctx, val);+    } else {+        v = __JS_NewFloat64(ctx, val);+    }+    return v;+}++JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);++static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)+{+    JSValue v;+    int32_t val;+    union {+        double d;+        uint64_t u;+    } u, t;+    u.d = d;+    val = (int32_t)d;+    t.d = val;+    /* -0 cannot be represented as integer, so we compare the bit+        representation */+    if (u.u == t.u) {+        v = JS_MKVAL(JS_TAG_INT, val);+    } else {+        v = __JS_NewFloat64(ctx, d);+    }+    return v;+}++static inline JS_BOOL JS_IsNumber(JSValueConst v)+{+    int tag = JS_VALUE_GET_TAG(v);+    return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag);+}++static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)+{+    int tag = JS_VALUE_GET_TAG(v);+    return tag == JS_TAG_BIG_INT;+}++static inline JS_BOOL JS_IsBigFloat(JSValueConst v)+{+    int tag = JS_VALUE_GET_TAG(v);+    return tag == JS_TAG_BIG_FLOAT;+}++static inline JS_BOOL JS_IsBigDecimal(JSValueConst v)+{+    int tag = JS_VALUE_GET_TAG(v);+    return tag == JS_TAG_BIG_DECIMAL;+}++static inline JS_BOOL JS_IsBool(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL;+}++static inline JS_BOOL JS_IsNull(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_NULL;+}++static inline JS_BOOL JS_IsUndefined(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED;+}++static inline JS_BOOL JS_IsException(JSValueConst v)+{+    return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION);+}++static inline JS_BOOL JS_IsUninitialized(JSValueConst v)+{+    return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED);+}++static inline JS_BOOL JS_IsString(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_STRING;+}++static inline JS_BOOL JS_IsSymbol(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL;+}++static inline JS_BOOL JS_IsObject(JSValueConst v)+{+    return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT;+}++JSValue JS_Throw(JSContext *ctx, JSValue obj);+JSValue JS_GetException(JSContext *ctx);+JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);+void JS_ResetUncatchableError(JSContext *ctx);+JSValue JS_NewError(JSContext *ctx);+JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);+JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...);+JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...);+JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...);+JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);+JSValue JS_ThrowOutOfMemory(JSContext *ctx);++void __JS_FreeValue(JSContext *ctx, JSValue v);+static inline void JS_FreeValue(JSContext *ctx, JSValue v)+{+    if (JS_VALUE_HAS_REF_COUNT(v)) {+        JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);+        if (--p->ref_count <= 0) {+            __JS_FreeValue(ctx, v);+        }+    }+}+void __JS_FreeValueRT(JSRuntime *rt, JSValue v);+static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)+{+    if (JS_VALUE_HAS_REF_COUNT(v)) {+        JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);+        if (--p->ref_count <= 0) {+            __JS_FreeValueRT(rt, v);+        }+    }+}++static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)+{+    if (JS_VALUE_HAS_REF_COUNT(v)) {+        JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);+        p->ref_count++;+    }+    return (JSValue)v;+}++static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)+{+    if (JS_VALUE_HAS_REF_COUNT(v)) {+        JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);+        p->ref_count++;+    }+    return (JSValue)v;+}++int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);+static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)+{+    return JS_ToInt32(ctx, (int32_t*)pres, val);+}+int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val);+int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val);+int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val);+/* return an exception if 'val' is a Number */+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val);+/* same as JS_ToInt64() but allow BigInt */+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val);++JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1);+JSValue JS_NewString(JSContext *ctx, const char *str);+JSValue JS_NewAtomString(JSContext *ctx, const char *str);+JSValue JS_ToString(JSContext *ctx, JSValueConst val);+JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);+const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8);+static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1)+{+    return JS_ToCStringLen2(ctx, plen, val1, 0);+}+static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1)+{+    return JS_ToCStringLen2(ctx, NULL, val1, 0);+}+void JS_FreeCString(JSContext *ctx, const char *ptr);++JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id);+JSValue JS_NewObjectClass(JSContext *ctx, int class_id);+JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);+JSValue JS_NewObject(JSContext *ctx);++JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);+JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);+JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val);++JSValue JS_NewArray(JSContext *ctx);+int JS_IsArray(JSContext *ctx, JSValueConst val);++JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,+                               JSAtom prop, JSValueConst receiver,+                               JS_BOOL throw_ref_error);+static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj,+                                              JSAtom prop)+{+    return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0);+}+JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,+                          const char *prop);+JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,+                             uint32_t idx);++int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,+                           JSAtom prop, JSValue val,+                           int flags);+static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,+                                 JSAtom prop, JSValue val)+{+    return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);+}+int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,+                         uint32_t idx, JSValue val);+int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,+                        int64_t idx, JSValue val);+int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,+                      const char *prop, JSValue val);+int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop);+int JS_IsExtensible(JSContext *ctx, JSValueConst obj);+int JS_PreventExtensions(JSContext *ctx, JSValueConst obj);+int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags);+int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val);++#define JS_GPN_STRING_MASK  (1 << 0)+#define JS_GPN_SYMBOL_MASK  (1 << 1)+#define JS_GPN_PRIVATE_MASK (1 << 2)+/* only include the enumerable properties */+#define JS_GPN_ENUM_ONLY    (1 << 4)+/* set theJSPropertyEnum.is_enumerable field */+#define JS_GPN_SET_ENUM     (1 << 5)++int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,+                           uint32_t *plen, JSValueConst obj, int flags);+int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,+                      JSValueConst obj, JSAtom prop);++JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,+                int argc, JSValueConst *argv);+JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,+                  int argc, JSValueConst *argv);+JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,+                           int argc, JSValueConst *argv);+JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,+                            JSValueConst new_target,+                            int argc, JSValueConst *argv);+JS_BOOL JS_DetectModule(const char *input, size_t input_len);+/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,+                const char *filename, int eval_flags);+JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj);+JSValue JS_GetGlobalObject(JSContext *ctx);+int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj);+int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,+                      JSAtom prop, JSValueConst val,+                      JSValueConst getter, JSValueConst setter, int flags);+int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,+                           JSAtom prop, JSValue val, int flags);+int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,+                                 uint32_t idx, JSValue val, int flags);+int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,+                              const char *prop, JSValue val, int flags);+int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,+                            JSAtom prop, JSValue getter, JSValue setter,+                            int flags);+void JS_SetOpaque(JSValue obj, void *opaque);+void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);+void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);++/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */+JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,+                     const char *filename);+#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,+                      const char *filename, int flags);+JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,+                         JSValueConst replacer, JSValueConst space0);++typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr);+JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,+                          JSFreeArrayBufferDataFunc *free_func, void *opaque,+                          JS_BOOL is_shared);+JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);+void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);+uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);+JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,+                               size_t *pbyte_offset,+                               size_t *pbyte_length,+                               size_t *pbytes_per_element);+typedef struct {+    void *(*sab_alloc)(void *opaque, size_t size);+    void (*sab_free)(void *opaque, void *ptr);+    void (*sab_dup)(void *opaque, void *ptr);+    void *sab_opaque;+} JSSharedArrayBufferFunctions;+void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,+                                      const JSSharedArrayBufferFunctions *sf);++JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);++/* is_handled = TRUE means that the rejection is handled */+typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,+                                           JSValueConst reason,+                                           JS_BOOL is_handled, void *opaque);+void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque);++/* return != 0 if the JS code needs to be interrupted */+typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);+void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);+/* if can_block is TRUE, Atomics.wait() can be used */+void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);++typedef struct JSModuleDef JSModuleDef;++/* return the module specifier (allocated with js_malloc()) or NULL if+   exception */+typedef char *JSModuleNormalizeFunc(JSContext *ctx,+                                    const char *module_base_name,+                                    const char *module_name, void *opaque);+typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx,+                                        const char *module_name, void *opaque);++/* module_normalize = NULL is allowed and invokes the default module+   filename normalizer */+void JS_SetModuleLoaderFunc(JSRuntime *rt,+                            JSModuleNormalizeFunc *module_normalize,+                            JSModuleLoaderFunc *module_loader, void *opaque);+/* return the import.meta object of a module */+JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);+JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);++/* JS Job support */++typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv);+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv);++JS_BOOL JS_IsJobPending(JSRuntime *rt);+int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx);++/* Object Writer/Reader (currently only used to handle precompiled code) */+#define JS_WRITE_OBJ_BYTECODE  (1 << 0) /* allow function/module */+#define JS_WRITE_OBJ_BSWAP     (1 << 1) /* byte swapped output */+#define JS_WRITE_OBJ_SAB       (1 << 2) /* allow SharedArrayBuffer */+#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to+                                           encode arbitrary object+                                           graph */+uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,+                        int flags);+uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,+                         int flags, uint8_t ***psab_tab, size_t *psab_tab_len);++#define JS_READ_OBJ_BYTECODE  (1 << 0) /* allow function/module */+#define JS_READ_OBJ_ROM_DATA  (1 << 1) /* avoid duplicating 'buf' data */+#define JS_READ_OBJ_SAB       (1 << 2) /* allow SharedArrayBuffer */+#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */+JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,+                      int flags);++/* load the dependencies of the module 'obj'. Useful when JS_ReadObject()+   returns a module. */+int JS_ResolveModule(JSContext *ctx, JSValueConst obj);++/* C function definition */+typedef enum JSCFunctionEnum {  /* XXX: should rename for namespace isolation */+    JS_CFUNC_generic,+    JS_CFUNC_generic_magic,+    JS_CFUNC_constructor,+    JS_CFUNC_constructor_magic,+    JS_CFUNC_constructor_or_func,+    JS_CFUNC_constructor_or_func_magic,+    JS_CFUNC_f_f,+    JS_CFUNC_f_f_f,+    JS_CFUNC_getter,+    JS_CFUNC_setter,+    JS_CFUNC_getter_magic,+    JS_CFUNC_setter_magic,+    JS_CFUNC_iterator_next,+} JSCFunctionEnum;++typedef union JSCFunctionType {+    JSCFunction *generic;+    JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);+    JSCFunction *constructor;+    JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic);+    JSCFunction *constructor_or_func;+    double (*f_f)(double);+    double (*f_f_f)(double, double);+    JSValue (*getter)(JSContext *ctx, JSValueConst this_val);+    JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val);+    JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic);+    JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic);+    JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val,+                             int argc, JSValueConst *argv, int *pdone, int magic);+} JSCFunctionType;++JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,+                         const char *name,+                         int length, JSCFunctionEnum cproto, int magic);+JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,+                            int length, int magic, int data_len,+                            JSValueConst *data);++static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,+                                      int length)+{+    return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);+}++static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,+                                           const char *name,+                                           int length, JSCFunctionEnum cproto, int magic)+{+    return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic);+}+void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, +                       JSValueConst proto);++/* C property definition */++typedef struct JSCFunctionListEntry {+    const char *name;+    uint8_t prop_flags;+    uint8_t def_type;+    int16_t magic;+    union {+        struct {+            uint8_t length; /* XXX: should move outside union */+            uint8_t cproto; /* XXX: should move outside union */+            JSCFunctionType cfunc;+        } func;+        struct {+            JSCFunctionType get;+            JSCFunctionType set;+        } getset;+        struct {+            const char *name;+            int base;+        } alias;+        struct {+            const struct JSCFunctionListEntry *tab;+            int len;+        } prop_list;+        const char *str;+        int32_t i32;+        int64_t i64;+        double f64;+    } u;+} JSCFunctionListEntry;++#define JS_DEF_CFUNC          0+#define JS_DEF_CGETSET        1+#define JS_DEF_CGETSET_MAGIC  2+#define JS_DEF_PROP_STRING    3+#define JS_DEF_PROP_INT32     4+#define JS_DEF_PROP_INT64     5+#define JS_DEF_PROP_DOUBLE    6+#define JS_DEF_PROP_UNDEFINED 7+#define JS_DEF_OBJECT         8+#define JS_DEF_ALIAS          9++/* Note: c++ does not like nested designators */+#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }+#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } }+#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } }+#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } }+#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }+#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } }+#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } }+#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } }+#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } }+#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } }+#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }+#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } }+#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } }+#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } }++void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,+                                const JSCFunctionListEntry *tab,+                                int len);++/* C module definition */++typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m);++JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,+                           JSModuleInitFunc *func);+/* can only be called before the module is instantiated */+int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str);+int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,+                           const JSCFunctionListEntry *tab, int len);+/* can only be called after the module is instantiated */+int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,+                       JSValue val);+int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,+                           const JSCFunctionListEntry *tab, int len);++#undef js_unlikely+#undef js_force_inline++#ifdef __cplusplus+} /* extern "C" { */+#endif++#endif /* QUICKJS_H */
+ src/Quickjs.hs view
@@ -0,0 +1,618 @@+{-# LANGUAGE BangPatterns, QuasiQuotes, TemplateHaskell, OverloadedStrings, ScopedTypeVariables, FlexibleContexts #-}++{-|+Module      : Quickjs+Description : Haskell bindings to the [QuickJS](https://bellard.org/quickjs/) library+Copyright   : (c) Samuel Balco, 2020+License     : MIT+Maintainer  : goodlyrottenapple@gmail.com++This is a very basic wrapper for the [QuickJS](https://bellard.org/quickjs/) .++The current functionality includes evaluating JS code, calling a JS function in the global scope+and marshalling 'Value's to and from 'JSValue's.+-}+module Quickjs (JSValue, JSContextPtr, quickjs, quickjsMultithreaded, call, eval, eval_, withJSValue, fromJSValue_) where++import           Foreign+import           Foreign.C                   (CString, CInt, CDouble, CSize)+import           Data.ByteString             (ByteString, useAsCString, useAsCStringLen, packCString)+import           Data.Text.Encoding          (encodeUtf8)+import qualified Language.C.Inline           as C+import           Control.Monad.Catch         (MonadThrow(..), MonadCatch(..), MonadMask(..), finally)+import           Control.Monad               (when, forM_)+import           Control.Monad.Reader        (MonadReader, runReaderT, ask)+import           Control.Monad.Trans.Reader  (ReaderT)+import           Control.Monad.IO.Class      (MonadIO, liftIO)+import           Control.Monad.IO.Unlift     (MonadUnliftIO(..), UnliftIO(..), askUnliftIO)+import           Data.Aeson                  (Value(..), encode, toJSON)+import qualified Data.Aeson                  as Aeson+import           Data.Scientific             (fromFloatDigits, toRealFloat, toBoundedInteger, isInteger)+import           Data.Text                   (Text)+import           Data.Vector                 (fromList, imapM_)+import           Data.HashMap.Strict         (HashMap, empty, insert, toList)+import           Data.String.Conv            (toS)+import           Data.Time.Clock.POSIX       (posixSecondsToUTCTime)+import           Control.Concurrent          (rtsSupportsBoundThreads, runInBoundThread)++import           Quickjs.Types+import           Quickjs.Error+++C.context quickjsCtx+C.include "quickjs.h"+C.include "quickjs-libc.h"+++foreign import ccall "JS_NewRuntime"+  jsNewRuntime :: IO (Ptr JSRuntime)++foreign import ccall "JS_FreeRuntime"+  jsFreeRuntime :: Ptr JSRuntime -> IO ()++++foreign import ccall "JS_NewContext"+  jsNewContext :: Ptr JSRuntime -> IO (Ptr JSContext)++foreign import ccall "JS_FreeContext"+  jsFreeContext :: Ptr JSContext -> IO ()++++jsFreeValue :: JSContextPtr -> JSValue -> IO ()+jsFreeValue ctx val = with val $ \v -> [C.block| void {+    if (JS_VALUE_HAS_REF_COUNT(*$(JSValue *v))) {+      JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(*$(JSValue *v));+      if (--p->ref_count <= 0) {+        __JS_FreeValue($(JSContext *ctx), *$(JSValue *v));+      }+    }+  } |]++++type JSContextPtr = Ptr JSContext+type JSValueConstPtr = Ptr JSValueConst++jsIs_ :: (MonadIO m, Storable p, Eq n, Num n) => p -> (Ptr p -> IO n) -> m Bool+jsIs_ val fun = do+  b <- liftIO $ with val fun+  return $ b == 1++-- jsIsNumber :: MonadIO m => JSValue -> m Bool+-- jsIsNumber val = jsIs_ val $ \valPtr -> [C.block| int { return JS_IsNumber(*$(JSValueConst *valPtr)); } |]++jsIsArray :: MonadIO m => JSContextPtr -> JSValue -> m Bool+jsIsArray ctxPtr val = jsIs_ val $ \valPtr -> [C.block| int { return JS_IsArray($(JSContext *ctxPtr), *$(JSValueConst *valPtr)); } |]++jsIsDate :: MonadIO m => JSContextPtr -> JSValue -> m Bool+jsIsDate ctxPtr val = do+  globalObject <- liftIO $ C.withPtr_ $ \globalObjectPtr ->+      [C.block| void { *$(JSValue *globalObjectPtr) = JS_GetGlobalObject($(JSContext *ctxPtr)); } |]+  dateConstructor <- jsGetPropertyStr ctxPtr globalObject "Date"+  liftIO $ do+    jsFreeValue ctxPtr globalObject+    res <- with val $ \valPtr -> with dateConstructor $ \dateCPtr -> +      [C.block| int { return JS_IsInstanceOf($(JSContext *ctxPtr), *$(JSValueConst *valPtr), *$(JSValueConst *dateCPtr)); } |]+    jsFreeValue ctxPtr dateConstructor+    return $ res > 0+++jsIsTryAll :: MonadThrow m =>+  JSValue -> [JSValue -> m Bool] -> [JSTypeEnum] -> JSTypeEnum -> m JSTypeEnum+jsIsTryAll _ [] _ def = return def+jsIsTryAll jsval (f:funs)(l:lbls) def = do+  b <- f jsval+  if b then return l else jsIsTryAll jsval funs lbls def+jsIsTryAll _ _ _ _ = throwM $ InternalError $ "jsIsTryAll_ unreachable case"+++jsIs :: (MonadIO m, MonadThrow m) => JSContextPtr -> JSValue -> m JSTypeEnum+jsIs ctx jsval = case fromCType $ tag jsval of+  Just JSTagObject -> +    jsIsTryAll jsval [jsIsArray ctx, jsIsDate ctx] [JSIsArray, JSIsDate] (JSTypeFromTag JSTagObject)+  Just t | t == JSTagBigDecimal || +           t == JSTagBigInt ||+           t == JSTagBigFloat ||+           t == JSTagInt || +           t == JSTagFloat64 -> return JSIsNumber+         | otherwise -> return $ JSTypeFromTag t+  Nothing -> throwM $ UnknownJSTag (tag jsval)+ +++jsNullValue :: JSValue+jsNullValue = JSValue { u = 0, tag = toCType JSTagNull }++jsNewBool :: JSContextPtr -> Bool -> IO JSValue+jsNewBool ctxPtr bool = do+  let b = if bool then 1 else 0+  C.withPtr_ $ \ptr -> [C.block| void { *$(JSValue *ptr) = JS_NewBool($(JSContext *ctxPtr), $(int b)); } |]++jsNewFloat64 :: JSContextPtr -> CDouble -> IO JSValue+jsNewFloat64 ctxPtr num =+  C.withPtr_ $ \ptr -> [C.block| void { *$(JSValue *ptr) = JS_NewFloat64($(JSContext *ctxPtr), $(double num)); } |]++jsNewInt64 :: JSContextPtr -> Int64 -> IO JSValue+jsNewInt64 ctxPtr num = do+  C.withPtr_ $ \ptr -> [C.block| void { *$(JSValue *ptr) = JS_NewInt64($(JSContext *ctxPtr), $(int64_t num)); } |]++jsNewString :: JSContextPtr -> ByteString -> IO JSValue+jsNewString ctxPtr s = C.withPtr_ $ \ptr -> useAsCStringLen s $ \(cstringPtr, cstringLen) -> do+  let len = fromIntegral cstringLen+  [C.block| void { *$(JSValue *ptr) = JS_NewStringLen($(JSContext *ctxPtr), $(const char *cstringPtr), $(size_t len)); } |]++++checkIsException :: (MonadThrow m, MonadIO m) => Text -> JSContextPtr -> JSValue -> m ()+checkIsException loc ctxPtr val =+  case fromCType $ tag val of+    Just JSTagException -> do+      err <- getErrorMessage ctxPtr +      liftIO $ jsFreeValue ctxPtr val+      throwM $ JSException loc err+    _ -> pure ()++++jsonToJSValue :: (MonadThrow m, MonadIO m) => JSContextPtr -> Value -> m JSValue+jsonToJSValue _ Null = pure jsNullValue+jsonToJSValue ctx (Bool b) = liftIO $ jsNewBool ctx b+jsonToJSValue ctx (Number n) = +  if not (isInteger n) then liftIO $ jsNewFloat64 ctx (toRealFloat n)+  else case toBoundedInteger n of+    Just i -> liftIO $ jsNewInt64 ctx i+    Nothing -> throwM $ InternalError "Value does not fit in Int64"+jsonToJSValue ctx (String s) = liftIO $ jsNewString ctx $ toS s+jsonToJSValue ctxPtr (Array xs) = do+  arrVal <- liftIO (C.withPtr_ $ \arrValPtr -> [C.block| void { *$(JSValueConst *arrValPtr) = JS_NewArray($(JSContext *ctxPtr)); } |])+  +  checkIsException "jsonToJSValue/Array/1" ctxPtr arrVal++  flip imapM_ xs $ \index value -> do +    val <- jsonToJSValue ctxPtr value+    checkIsException "jsonToJSValue/Array/2" ctxPtr val++    let idx = fromIntegral index+    code <- liftIO (with arrVal $ \arrValPtr -> with val $ \valPtr -> +      [C.block| int { return JS_DefinePropertyValueUint32(+        $(JSContext *ctxPtr), +        *$(JSValueConst *arrValPtr),+        $(uint32_t idx),+        *$(JSValueConst *valPtr),+        JS_PROP_C_W_E+      ); } |])+    return ()++    if (code < 0) then do+      liftIO $ jsFreeValue ctxPtr arrVal+      throwM $ InternalError "Could not append element to array"+    else return ()++  return arrVal+jsonToJSValue ctxPtr (Object o) = do+  objVal <- liftIO (C.withPtr_ $ \objValPtr -> +    [C.block| void { *$(JSValueConst *objValPtr) = JS_NewObject($(JSContext *ctxPtr)); } |])++  checkIsException "jsonToJSValue/Object/1" ctxPtr objVal+  +  forM_ (toList o) $ \(key,value) -> do+    val <- jsonToJSValue ctxPtr value+    checkIsException "jsonToJSValue/Object/2" ctxPtr val++    code <- liftIO (with objVal $ \objValPtr -> with val $ \valPtr -> +      useAsCString (encodeUtf8 key) $ \cstringPtr -> do+        [C.block| int { +          return JS_DefinePropertyValueStr(+            $(JSContext *ctxPtr), +            *$(JSValueConst *objValPtr),+            $(const char *cstringPtr),+            *$(JSValueConst *valPtr),+            JS_PROP_C_W_E+          ); +        } |])++    when (code < 0) $ do+      liftIO $ jsFreeValue ctxPtr objVal+      throwM $ InternalError "Could not add add property to object"++  return objVal+++jsToBool :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> m Bool+jsToBool ctxPtr val = do+    code <- liftIO $ with val $ \valPtr -> [C.block| int { return JS_ToBool($(JSContext *ctxPtr), *$(JSValueConst *valPtr)); } |]+    case code of+        -1 -> getErrorMessage ctxPtr >>= throwM . JSException "jsToBool"+        0 -> return False+        _ -> return True++jsToInt64 :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> m Int64+jsToInt64 ctxPtr val = do+  (res, code) <- liftIO $ C.withPtr $ \intPtr -> with val $ \valPtr -> [C.block| int { return JS_ToInt64($(JSContext *ctxPtr), $(int64_t *intPtr), *$(JSValueConst *valPtr)); } |]+  if code == 0 then return res+  else getErrorMessage ctxPtr >>= throwM . JSException "jsToInt64"+++jsToFloat64 :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> m CDouble+jsToFloat64 ctxPtr val = do+  (res, code) <- liftIO $ C.withPtr $ \doublePtr -> with val $ \valPtr -> [C.block| int { return JS_ToFloat64($(JSContext *ctxPtr), $(double *doublePtr), *$(JSValueConst *valPtr)); } |]+  if code == 0 then return res+  else getErrorMessage ctxPtr >>= throwM . JSException "jsToFloat64"++++jsToString :: MonadIO m => JSContextPtr -> JSValue -> m ByteString+jsToString ctxPtr val = liftIO $ do+    cstring <- with val $ \valPtr -> [C.block| const char * { return JS_ToCString($(JSContext *ctxPtr), *$(JSValueConst *valPtr)); } |]+    if cstring == nullPtr then return ""+    else do+      string <- packCString cstring+      jsFreeCString ctxPtr cstring+      return string+++foreign import ccall "JS_FreeCString"+  jsFreeCString :: JSContextPtr -> CString -> IO ()+++jsToJSON :: (MonadCatch m, MonadIO m) => JSContextPtr -> JSValue -> m Value+jsToJSON ctx jsval = do+  ty <- jsIs ctx jsval+  case ty of+    JSTypeFromTag JSTagException -> do+      err <- getErrorMessage ctx +      liftIO $ jsFreeValue ctx jsval+      throwM $ JSException "jsToJSON/JSTagException" err+    JSTypeFromTag JSTagNull -> return Null+    JSTypeFromTag JSTagUndefined -> return Null+    JSTypeFromTag JSTagBool -> do+      b <- jsToBool ctx jsval+      return $ Bool b+    JSIsNumber -> do+      n <- jsToFloat64 ctx jsval+      return $ Number $ fromFloatDigits n+    JSTypeFromTag JSTagString -> do+      s <- jsToString ctx jsval+      return $ String $ toS s+    JSIsArray -> do+      len <- do+        lenVal <- jsGetPropertyStr ctx jsval "length" +        len' <- jsToInt64 ctx lenVal+        liftIO $ jsFreeValue ctx lenVal+        return len'+      vs <- jsArrayToJSON ctx jsval 0 (fromIntegral len)+      return $ Array $ fromList vs+    JSIsDate -> do+      getter <- jsGetPropertyStr ctx jsval "getTime" ++      timestampRaw <- liftIO $ C.withPtr_ $ \res -> with getter $ \getterPtr -> with jsval $ \jsvalPtr -> +        [C.block| void { *$(JSValue *res) = JS_Call($(JSContext *ctx), *$(JSValueConst *getterPtr), *$(JSValueConst *jsvalPtr), 0, NULL); } |]++      timestamp <- jsToFloat64 ctx timestampRaw+      liftIO $ do+        jsFreeValue ctx getter+        jsFreeValue ctx timestampRaw+      return $ toJSON $ posixSecondsToUTCTime $ realToFrac $ timestamp / 1000+    JSTypeFromTag JSTagObject -> do+      o <- jsObjectToJSON ctx jsval+      return $ Object o +    JSTypeFromTag f -> throwM $ UnsupportedTypeTag f+    JSIsError -> throwM $ InternalError "JSIsError unreachable"+++jsArrayToJSON :: (MonadCatch m, MonadIO m) => JSContextPtr -> JSValue -> Int -> Int -> m [Value]+jsArrayToJSON ctxPtr jsval index len = +  if index < len then do+    v <- do+      let idx = fromIntegral index+      val <- liftIO $ C.withPtr_ $ \ptr -> with jsval $ \jsvalPtr -> +        [C.block| void { *$(JSValue *ptr) = JS_GetPropertyUint32($(JSContext *ctxPtr), *$(JSValueConst *jsvalPtr), $(uint32_t idx)); } |]++      checkIsException "jsArrayToJSON" ctxPtr val+      res <- jsToJSON ctxPtr val+      liftIO $ jsFreeValue ctxPtr val+      return res++    vs <- jsArrayToJSON ctxPtr jsval (index+1) len+    return $ v:vs+  else return []+++++++forLoop :: (Num a, Ord a, Monad m) => a -> (a -> m ()) -> m ()+forLoop end f = go 0+  where+    go !x | x < end   = f x >> go (x+1)+          | otherwise = return ()+++++jsObjectToJSON :: (MonadCatch m, MonadIO m) => JSContextPtr -> JSValue -> m (HashMap Text Value)+jsObjectToJSON ctxPtr obj = do+    let flags = unJSGPNMask $ jsGPNStringMask .|. jsGPNSymbolMask .|. jsGPNEnumOnly+    properties <- liftIO $ malloc+    plen <- jsGetOwnPropertyNames ctxPtr obj properties flags +      `catch` (\(e::SomeJSRuntimeException) -> do+        liftIO $ free properties+        throwM e+      )+    objPtr <- liftIO $ malloc+    liftIO $ poke objPtr obj++    res <- collectVals properties objPtr 0 plen `catch` (\(e::SomeJSRuntimeException) -> do+        liftIO $ free objPtr+        throwM e+      )+    cleanup properties plen+    return res+  where+    collectVals :: (MonadCatch m, MonadIO m) => Ptr (Ptr JSPropertyEnum) -> JSValueConstPtr -> Int -> Int -> m (HashMap Text Value)+    collectVals properties objPtr !index end +      | index < end = do+        let i = fromIntegral index++        key <- do+          key' <- liftIO $ C.withPtr_ $ \ptr -> [C.block| void { *$(JSValue *ptr) = JS_AtomToString($(JSContext *ctxPtr), (*$(JSPropertyEnum **properties))[$(uint32_t i)].atom); } |]+          checkIsException "jsObjectToJSON/collectVals/1" ctxPtr key'+          res <- jsToJSON ctxPtr key'+          liftIO $ jsFreeValue ctxPtr key'+          return res++        case key of +          String k -> do+            val <-  do+              val' <- liftIO $ C.withPtr_ $ \ptr ->+                [C.block| void { *$(JSValue *ptr) = JS_GetProperty($(JSContext *ctxPtr), *$(JSValueConst *objPtr), (*$(JSPropertyEnum **properties))[$(uint32_t i)].atom); } |]+              checkIsException "jsObjectToJSON/collectVals/2" ctxPtr val'+              res <- jsToJSON ctxPtr val'+              liftIO $ jsFreeValue ctxPtr val'+              return res++            xs <- collectVals properties objPtr (index+1) end+            return $ insert k val xs+          x -> throwM $ InternalError $ "Could not get property name" <> toS (encode x)++      | otherwise = return empty++    cleanup :: MonadIO m => Ptr (Ptr JSPropertyEnum) -> Int -> m ()+    cleanup properties plen = liftIO $ do+      forLoop plen $ \index -> do+        let i = fromIntegral index+        [C.block| void { JS_FreeAtom($(JSContext *ctxPtr), (*$(JSPropertyEnum **properties))[$(uint32_t i)].atom); }|]++      let void_ptr = castPtr properties+      [C.block| void { js_free($(JSContext *ctxPtr), *$(void **void_ptr)); }|]++      free properties++++getErrorMessage :: MonadIO m => JSContextPtr -> m Text+getErrorMessage ctxPtr = liftIO $ do+  ex <- C.withPtr_ $ \ptr -> [C.block| void { *$(JSValue *ptr) = JS_GetException($(JSContext *ctxPtr)); } |]+  res <- jsToString ctxPtr ex+  jsFreeValue ctxPtr ex+  return $ toS res++++jsGetPropertyStr :: MonadIO m => JSContextPtr -> JSValue -> ByteString -> m JSValue+jsGetPropertyStr ctxPtr val str = liftIO $+  C.withPtr_ $ \ptr -> useAsCString str $ \prop -> with val $ \valPtr ->+    [C.block| void { *$(JSValue *ptr) = JS_GetPropertyStr($(JSContext *ctxPtr), *$(JSValueConst *valPtr), $(const char *prop)); } |]+++jsGetOwnPropertyNames :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> Ptr (Ptr JSPropertyEnum) -> CInt -> m Int+jsGetOwnPropertyNames ctxPtr val properties flags = do+  (len,code) <- liftIO $ C.withPtr $ \plen -> with val $ \valPtr -> +    [C.block| int { return JS_GetOwnPropertyNames($(JSContext *ctxPtr), $(JSPropertyEnum **properties), $(uint32_t *plen), *$(JSValueConst *valPtr), $(int flags)); } |]+  if code == 0 then return (fromIntegral len)+  else throwM $ InternalError "Could not get object properties"+++jsCall :: JSContextPtr -> JSValue -> CInt -> (Ptr JSValue) -> IO JSValue+jsCall ctxt fun_obj argc argv = C.withPtr_ $ \res -> with fun_obj $ \funPtr -> +  [C.block| void { *$(JSValue *res) = JS_Call($(JSContext *ctxt), *$(JSValueConst *funPtr), JS_NULL, $(int argc), $(JSValueConst *argv)); } |]+++jsEval :: JSContextPtr -> CString -> CSize -> CString -> CInt -> IO JSValue+jsEval ctxPtr input input_len filename eval_flags = C.withPtr_ $ \ptr -> +  [C.block| void { *$(JSValue *ptr) = JS_Eval($(JSContext *ctxPtr), $(const char *input), $(size_t input_len), $(const char *filename), $(int eval_flags)); } |]+++evalRaw :: JSContextPtr -> JSEvalType -> ByteString -> IO JSValue+evalRaw ctx eTyp code = +    useAsCString "script.js" $ \cfilename ->+        useAsCStringLen code $ \(ccode, ccode_len) -> +            jsEval ctx ccode (fromIntegral ccode_len) cfilename (toCType eTyp)+++++evalAs :: (MonadMask m, MonadReader JSContextPtr m, MonadIO m) => JSEvalType -> ByteString -> m Value+evalAs eTyp code = do+  ctx <- ask+  val <- liftIO $ evalRaw ctx eTyp code+  -- checkIsException "evalAs" ctx val+  jsToJSON ctx val `finally` freeJSValue val++++{-|+Evaluates the given string and returns a 'Value' (if the result can be converted).+-}+eval :: (MonadMask m, MonadReader JSContextPtr m, MonadIO m) => ByteString -> m Value+eval = evalAs Global++evalAs_ :: (MonadThrow m, MonadReader JSContextPtr m, MonadIO m) => JSEvalType -> ByteString -> m ()+evalAs_ eTyp code = do+  ctx <- ask+  val <- liftIO $ evalRaw ctx eTyp code+  checkIsException "evalAs_" ctx val+  freeJSValue val++++{-|+More efficient than 'eval' if we don't care about the value of the expression, +e.g. if we are evaluating a function definition or performing other side-effects such as+printing to console/modifying state.+-}+eval_ :: (MonadThrow m, MonadReader JSContextPtr m, MonadIO m) => ByteString -> m ()+eval_ = evalAs_ Global+++fromJSValue_ :: (MonadCatch m, MonadReader JSContextPtr m, MonadIO m) => JSValue -> m Value+fromJSValue_ val = do+  ctx <- ask+  jsToJSON ctx val++++-- fromJSValue :: (Aeson.FromJSON a, MonadCatch m, MonadReader JSContextPtr m, MonadIO m) => JSValue -> m a+-- fromJSValue val = do+--   jsonval <- fromJSValue_ val++--   case Aeson.fromJSON jsonval of+--     Aeson.Success a -> return a+--     Aeson.Error err -> throwM $ InternalError err++++{-|+Takes a value with a defined 'ToJSON' instance. This value is marshalled to a 'JSValue'+and passed as an argument to the callback function, provided as the second argument to 'withJSValue'+-}+withJSValue :: (MonadMask m, MonadReader JSContextPtr m, MonadIO m, Aeson.ToJSON a) => a -> (JSValue -> m b) -> m b+withJSValue v f = do++  ctx <- ask+  val <- jsonToJSValue ctx (Aeson.toJSON v)+  f val `finally` freeJSValue val+++++callRaw :: (MonadThrow m, MonadIO m) => JSContextPtr -> ByteString -> [JSValue] -> m JSValue+callRaw ctxPtr funName args = do+    globalObject <- liftIO $ C.withPtr_ $ \globalObjectPtr ->+      [C.block| void { *$(JSValue *globalObjectPtr) = JS_GetGlobalObject($(JSContext *ctxPtr)); } |]++    fun <- jsGetPropertyStr ctxPtr globalObject funName++    liftIO $ jsFreeValue ctxPtr globalObject++    ty <- jsIs ctxPtr fun+    case ty of+      JSTypeFromTag JSTagException -> do+        err <- getErrorMessage ctxPtr +        liftIO $ jsFreeValue ctxPtr fun+        throwM $ JSException "callRaw" err+      JSTypeFromTag JSTagUndefined -> throwM $ JSValueUndefined $ toS funName+      JSTypeFromTag JSTagObject -> do+        res <- liftIO $ withArrayLen args $ \len argv -> jsCall ctxPtr fun (fromIntegral $ len) argv+        liftIO $ jsFreeValue ctxPtr fun+        return res+      _ -> throwM $ JSValueIncorrectType {name = toS funName, expected = JSTypeFromTag JSTagObject, found = ty }+++-- call :: (MonadThrow m, MonadReader JSContextPtr m, MonadIO m) => String -> [JSValue] -> m JSValue+-- call funName args = do+--   ctx <- ask+--   val <- callRaw ctx funName args+--   checkIsException ctx val+--   return val++++call :: (MonadMask m, MonadReader JSContextPtr m, MonadIO m) => ByteString -> [JSValue] -> m Value+call funName args = do+  ctx <- ask+  val <- callRaw ctx funName args+  jsToJSON ctx val `finally` freeJSValue val+++freeJSValue :: (MonadThrow m, MonadReader JSContextPtr m, MonadIO m) => JSValue -> m ()+freeJSValue val = do+  ctx <- ask+  liftIO $ jsFreeValue ctx val++{-|+This function initialises a new JS runtime and performs the given computation within this context.++For example, we can evaluate an expression:++>quickjs $ do+>  res <- eval "1+2"+>  liftIO $ print res++Declare a function and call it on an argument:++>quickjs $ do+>  _ <- eval_ "f = (x) => x+1"+>  res <- eval "f(2)"+>  liftIO $ print res++Pass a Haskell value to the JS runtime:++>quickjs $ do+>  _ <- eval_ "f = (x) => x+1"+>  res <- withJSValue (3::Int) $ \x -> call "f" [x]+>  liftIO $ print res++-}+quickjs :: MonadIO m => ReaderT (Ptr JSContext) m b -> m b+quickjs f = do+  (rt, ctx) <- liftIO $ do+    _rt <- jsNewRuntime+    _ctx <- jsNewContext _rt++    [C.block| void { +      js_std_add_helpers($(JSContext *_ctx), -1, NULL);+    } |]+    return (_rt, _ctx)++  res <- runReaderT f ctx+  cleanup ctx rt+  return res+  where+    cleanup ctx rt = liftIO $ do+      jsFreeContext ctx+      jsFreeRuntime rt++{-|+This env differs from regular 'quickjs', in that it wraps the computation in the 'runInBoundThread' function.+This is needed when running the Haskell program mutithreaded (e.g. when using the testing framework Tasty),+since  quickjs does not like being called from an OS thread other than the one it was started in.+Because Haskell uses lightweight threads, this might happen if threaded mode is enabled, as is the case in Tasty.+This problem does not occur when running via Main.hs, if compiled as single threaded...+For more info see the paper [Extending the Haskell Foreign Function Interface with Concurrency](https://simonmar.github.io/bib/papers/conc-ffi.pdf)+-}+quickjsMultithreaded :: MonadUnliftIO m => ReaderT (Ptr JSContext) m b -> m b+quickjsMultithreaded f +  | rtsSupportsBoundThreads = do+    (u :: UnliftIO m) <- askUnliftIO+    +    liftIO $ runInBoundThread $ do+      rt <- jsNewRuntime+      ctx <- jsNewContext rt++      [C.block| void { +        js_std_add_helpers($(JSContext *ctx), -1, NULL);+      } |]++      res <-  unliftIO u $ runReaderT f ctx+      cleanup ctx rt+      return res+  | otherwise = quickjs f+  where+    cleanup ctx rt = do+      jsFreeContext ctx+      jsFreeRuntime rt+
+ src/Quickjs/Error.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE ExistentialQuantification, DuplicateRecordFields, GeneralizedNewtypeDeriving, DerivingStrategies, DeriveGeneric, DerivingVia, RecordWildCards #-}++module Quickjs.Error where+import           Control.Exception   (Exception(..), SomeException)+import           Data.Typeable       (cast)+import           Data.Text           (Text)+import           Data.String.Conv    (toS)+import           Type.Reflection     (Typeable)+import           GHC.Generics+import           Foreign.C.Types+import           Data.Aeson          (ToJSON(..))++import           Quickjs.Types+++data SomeJSRuntimeException = forall e . Exception e => SomeJSRuntimeException e deriving Typeable++instance Show SomeJSRuntimeException where+    show (SomeJSRuntimeException e) = show e++instance Exception SomeJSRuntimeException where+    toException = jsRuntimeExceptionToException+    fromException = jsRuntimeExceptionFromException+++jsRuntimeExceptionToException :: Exception e => e -> SomeException+jsRuntimeExceptionToException = toException . SomeJSRuntimeException++jsRuntimeExceptionFromException :: Exception e => SomeException -> Maybe e+jsRuntimeExceptionFromException x = do+    SomeJSRuntimeException a <- fromException x+    cast a+++newtype JSRuntimeException e = JSRuntimeException e +  deriving Generic+  deriving newtype Show++instance (Show e, Typeable e) => Exception (JSRuntimeException e)+  where+    toException = jsRuntimeExceptionToException+    fromException = jsRuntimeExceptionFromException++++instance ToJSON CLong where+  toJSON cl = toJSON (fromIntegral cl :: Integer)++data UnknownJSTag = UnknownJSTag {raw_tag :: !CLong} +  deriving (Generic, Typeable)+  deriving Exception via (JSRuntimeException UnknownJSTag)++instance Show UnknownJSTag where+  show UnknownJSTag{..} = "Uknown JS tag: " ++ show raw_tag+++data UnsupportedTypeTag = UnsupportedTypeTag {_tag :: JSTagEnum} +  deriving (Generic, Typeable)+  deriving Exception via (JSRuntimeException UnsupportedTypeTag)+++instance Show UnsupportedTypeTag where+  show UnsupportedTypeTag{..} = "Unsupported type tag: " ++ show _tag+++data JSException = JSException {location :: Text, message :: Text} +  deriving (Generic, Typeable)+  deriving Exception via (JSRuntimeException JSException)++instance Show JSException where+    show JSException{..} = "JS runtime threw an exception in " ++ toS location ++ ":\n=================\n" ++ toS message ++ "\n=================\n"++++data JSValueUndefined = JSValueUndefined {value :: Text} +  deriving (Generic, Typeable)+  deriving Exception via (JSRuntimeException JSValueUndefined)++instance Show JSValueUndefined where+  show JSValueUndefined{..} =  "The JS value '" ++ toS value ++ "' is undefined."+++data JSValueIncorrectType = +  JSValueIncorrectType {+    name :: Text+  , expected :: JSTypeEnum+  , found :: JSTypeEnum+  } +  deriving (Generic, Typeable)+  deriving Exception via (JSRuntimeException JSValueIncorrectType)++instance Show JSValueIncorrectType where+  show JSValueIncorrectType{..} = "Type mismatch of the JS value '" ++ toS name ++ "'. Expected: " ++ show expected ++ ", found: " ++ show found+++data InternalError = InternalError { message :: Text } +  deriving (Generic, Typeable)+  deriving Exception via (JSRuntimeException InternalError)++instance Show InternalError where+  show InternalError{..} = "Internal error occured:\n" ++ toS message
+ src/Quickjs/Types.hsc view
@@ -0,0 +1,189 @@+{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, MultiParamTypeClasses, FlexibleInstances, GeneralizedNewtypeDeriving #-}++module Quickjs.Types where++import qualified Data.Map                  as Map+import           Data.Bits                 (Bits)+import           Foreign.C.Types+import           Foreign.Ptr               (plusPtr)+import           Foreign.Storable          (Storable(..))+import qualified Language.Haskell.TH       as TH+import           Language.C.Inline+import           Language.C.Inline.Context+import qualified Language.C.Types          as C++#include "quickjs.h"++data JSValue = JSValue+  { u :: {-# UNPACK #-} !CDouble+  , tag :: {-# UNPACK #-} !CLong+  } deriving (Show, Eq)++instance Storable JSValue where+  sizeOf _ = #{size JSValue}+  alignment _ = #{alignment JSValue}+  peek ptr = do+    u <- peek (#{ptr JSValue, u} ptr)+    tag <- peek (#{ptr JSValue, tag} ptr)+    Prelude.pure (JSValue u tag)+  poke ptr (JSValue u tag) = do+    poke (#{ptr JSValue, u} ptr) u+    poke (#{ptr JSValue, tag} ptr) tag++type JSValueConst = JSValue++newtype JSRuntime = JSRuntime { _unusedRuntime :: CUChar }++newtype JSContext = JSContext { _unusedContext :: CUChar }+++type JSBool = CInt++type JSAtom = CUInt+++data JSPropertyEnum = JSPropertyEnum+  { is_enumerable :: {-# UNPACK #-} !JSBool+  , atom :: {-# UNPACK #-} !JSAtom+  } deriving (Show, Eq)++instance Storable JSPropertyEnum where+  sizeOf _ = #{size JSPropertyEnum}+  alignment _ = #{alignment JSPropertyEnum}+  peek ptr = do+    is_enumerable <- peek (#{ptr JSPropertyEnum, is_enumerable} ptr)+    atom <- peek (#{ptr JSPropertyEnum, atom} ptr)+    Prelude.pure (JSPropertyEnum is_enumerable atom)+  poke ptr (JSPropertyEnum is_enumerable atom) = do+    poke (#{ptr JSPropertyEnum, is_enumerable} ptr) is_enumerable+    poke (#{ptr JSPropertyEnum, atom} ptr) atom++++data JSRefCountHeader = JSRefCountHeader+  { ref_count :: {-# UNPACK #-} !CInt+  } deriving (Show, Eq)++instance Storable JSRefCountHeader where+  sizeOf _ = #{size JSRefCountHeader}+  alignment _ = #{alignment JSRefCountHeader}+  peek ptr = do+    ref_count <- peek (#{ptr JSRefCountHeader, ref_count} ptr)+    Prelude.pure (JSRefCountHeader ref_count)+  poke ptr (JSRefCountHeader ref_count) = do+    poke (#{ptr JSRefCountHeader, ref_count} ptr) ref_count++++class ToCType ty cty where+  toCType :: ty -> cty+++class FromCType ty cty where+  fromCType :: cty -> Maybe ty+++data JSTagEnum = JSTagFirst+               | JSTagBigDecimal+               | JSTagBigInt+               | JSTagBigFloat+               | JSTagSymbol+               | JSTagString+               | JSTagModule+               | JSTagFunctionBytecode+               | JSTagObject+               | JSTagInt+               | JSTagBool+               | JSTagNull+               | JSTagUndefined+               | JSTagUninitialized+               | JSTagCatchOffset+               | JSTagException+               | JSTagFloat64+  deriving (Show, Eq)+  +instance Num a => ToCType JSTagEnum a where+  toCType JSTagFirst            = #{const JS_TAG_FIRST}+  toCType JSTagBigDecimal       = #{const JS_TAG_BIG_DECIMAL}+  toCType JSTagBigInt           = #{const JS_TAG_BIG_INT}+  toCType JSTagBigFloat         = #{const JS_TAG_BIG_FLOAT}+  toCType JSTagSymbol           = #{const JS_TAG_SYMBOL}+  toCType JSTagString           = #{const JS_TAG_STRING}+  toCType JSTagModule           = #{const JS_TAG_MODULE}+  toCType JSTagFunctionBytecode = #{const JS_TAG_FUNCTION_BYTECODE}+  toCType JSTagObject           = #{const JS_TAG_OBJECT}+  toCType JSTagInt              = #{const JS_TAG_INT}+  toCType JSTagBool             = #{const JS_TAG_BOOL}+  toCType JSTagNull             = #{const JS_TAG_NULL}+  toCType JSTagUndefined        = #{const JS_TAG_UNDEFINED}+  toCType JSTagUninitialized    = #{const JS_TAG_UNINITIALIZED}+  toCType JSTagCatchOffset      = #{const JS_TAG_CATCH_OFFSET}+  toCType JSTagException        = #{const JS_TAG_EXCEPTION}+  toCType JSTagFloat64          = #{const JS_TAG_FLOAT64}+++instance (Eq a, Num a) => FromCType JSTagEnum a where+  fromCType (#{const JS_TAG_BIG_DECIMAL}) = Just JSTagBigDecimal+  fromCType (#{const JS_TAG_BIG_INT}) = Just JSTagBigInt+  fromCType (#{const JS_TAG_BIG_FLOAT}) = Just JSTagBigFloat+  fromCType (#{const JS_TAG_SYMBOL}) = Just JSTagSymbol+  fromCType (#{const JS_TAG_STRING}) = Just JSTagString+  fromCType (#{const JS_TAG_MODULE}) = Just JSTagModule+  fromCType (#{const JS_TAG_FUNCTION_BYTECODE}) = Just JSTagFunctionBytecode+  fromCType (#{const JS_TAG_OBJECT}) = Just JSTagObject+  fromCType (#{const JS_TAG_INT}) = Just JSTagInt+  fromCType (#{const JS_TAG_BOOL}) = Just JSTagBool+  fromCType (#{const JS_TAG_NULL}) = Just JSTagNull+  fromCType (#{const JS_TAG_UNDEFINED}) = Just JSTagUndefined+  fromCType (#{const JS_TAG_UNINITIALIZED}) = Just JSTagUninitialized+  fromCType (#{const JS_TAG_CATCH_OFFSET}) = Just JSTagCatchOffset+  fromCType (#{const JS_TAG_EXCEPTION}) = Just JSTagException +  fromCType (#{const JS_TAG_FLOAT64}) = Just JSTagFloat64+  fromCType _ = Nothing++data JSTypeEnum = JSTypeFromTag JSTagEnum+                | JSIsNumber+                | JSIsArray+                | JSIsDate+                | JSIsError+  deriving Show++data JSEvalType = Global | Module++  +instance Num a => ToCType JSEvalType a where+  toCType Global = #{const JS_EVAL_TYPE_GLOBAL}+  toCType Module = #{const JS_EVAL_TYPE_MODULE}+++newtype JSGPNMask = JSGPNMask { unJSGPNMask :: CInt }+  deriving (Eq, Bits)++#{enum JSGPNMask, JSGPNMask+, jsGPNStringMask  = JS_GPN_STRING_MASK+, jsGPNSymbolMask  = JS_GPN_SYMBOL_MASK+, jsGPNPrivateMask = JS_GPN_PRIVATE_MASK+, jsGPNEnumOnly    = JS_GPN_ENUM_ONLY+, jsGPNSetEnum     = JS_GPN_SET_ENUM+}+++quickjsCtx :: Context+quickjsCtx = baseCtx <> fptrCtx <> ctx+  where+    ctx = mempty+      { ctxTypesTable = quickjsTypesTable+      }++quickjsTypesTable :: Map.Map C.TypeSpecifier TH.TypeQ+quickjsTypesTable = Map.fromList+  [ +    (C.TypeName "JSValue", [t| JSValue |])+  , (C.TypeName "JSValueConst", [t| JSValueConst |])+  , (C.TypeName "JSRuntime", [t| JSRuntime |])+  , (C.TypeName "JSContext", [t| JSContext |])+  , (C.TypeName "JSBool", [t| JSBool |])+  , (C.TypeName "JSAtom", [t| JSAtom |])+  , (C.TypeName "JSPropertyEnum", [t| JSPropertyEnum |])+  ]+
+ test/Spec.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import           Test.Tasty              (TestTree, testGroup, defaultMain, after, DependencyType(..))+import           Test.Tasty.HUnit        (testCase)+import           Test.Tasty.QuickCheck   (testProperty, QuickCheckTests(..), QuickCheckVerbose(..))+import           Test.HUnit              (Assertion, (@?=))+import qualified Test.QuickCheck         as QC+import qualified Test.QuickCheck.Monadic as QC+import           Data.Aeson              (Value(..))+import           Control.Monad.IO.Class  (liftIO)+import           Control.Monad.Catch     (SomeException, MonadCatch(..))+import           Data.Text               (pack)+import qualified Data.HashMap.Strict            as HM+import qualified Data.Vector             as V+import           Quickjs+++eval_1_plus_2 :: Assertion+eval_1_plus_2 = quickjsMultithreaded $ do+  v <- eval "1 + 2;"+  liftIO $ v @?= Number 3++genText = do +  k <- QC.choose (0,200) +  pack <$> QC.vectorOf k (QC.oneof $ map pure $ ['0'..'~'])++genVal 0 = QC.oneof+  [ +    String <$> genText+  , Number . fromInteger <$> QC.arbitrary+  , Bool <$> QC.arbitrary+  , pure Null+  ]+genVal n | n > 0 = QC.oneof+  [ +    do { k <- QC.choose (0,n) ; Object . HM.fromList <$> (zip <$> QC.vectorOf k genText <*> QC.vectorOf k genVal') }      +  , do { k <- QC.choose (0,n) ; Array . V.fromList <$> QC.vectorOf k genVal' }+  , String <$> genText+  , Number . fromInteger <$> QC.arbitrary+  , Bool <$> QC.arbitrary+  , pure Null+  ]+  where genVal' = genVal (n `div` 2)++instance QC.Arbitrary Value where+  arbitrary = QC.sized genVal+++++marshall_to_from_JSValue :: Value -> QC.Property    +marshall_to_from_JSValue val = QC.monadicIO $ do+  val' <- QC.run $ quickjsMultithreaded $ withJSValue val $ \jsval ->+    fromJSValue_ jsval+  QC.assert $ val == val'++tests :: TestTree+tests = +  -- adjustOption (\_ -> QuickCheckTests 10) $+  -- adjustOption (\_ -> QuickCheckVerbose True) $  +  testGroup "Quickjs"+    [ testCase "empty quickjs call" (quickjsMultithreaded $ pure ())+    , testCase "eval '1 + 2;'" eval_1_plus_2+    , testProperty "marshalling Value to JSValue and back" marshall_to_from_JSValue+    ]++main = defaultMain tests