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 +5/−0
- LICENSE +21/−0
- README.md +47/−0
- Setup.hs +2/−0
- quickjs-hs.cabal +90/−0
- quickjs/cutils.c +620/−0
- quickjs/libbf.c +8466/−0
- quickjs/libregexp.c +2588/−0
- quickjs/libunicode.c +1538/−0
- quickjs/quickjs-libc.c +3773/−0
- quickjs/quickjs-libc.h +58/−0
- quickjs/quickjs.c +53313/−0
- quickjs/quickjs.h +1030/−0
- src/Quickjs.hs +618/−0
- src/Quickjs/Error.hs +101/−0
- src/Quickjs/Types.hsc +189/−0
- test/Spec.hs +69/−0
+ 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+++++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, ×));+ }+#else+ {+ struct timeval times[2];+ ms_to_timeval(×[0], atime);+ ms_to_timeval(×[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, """);+ } 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