mquickjs-hs (empty) → 0.1.2.4
raw patch · 26 files changed
+29839/−0 lines, 26 filesdep +HUnitdep +QuickCheckdep +aesonsetup-changed
Dependencies added: HUnit, QuickCheck, aeson, base, bytestring, containers, exceptions, inline-c, mquickjs-hs, mtl, scientific, string-conv, tasty, tasty-hunit, tasty-quickcheck, text, time, transformers, unliftio-core, unordered-containers, vector
Files
- LICENSE +21/−0
- README.md +55/−0
- Setup.hs +2/−0
- changelog.md +6/−0
- mquickjs-hs.cabal +88/−0
- mquickjs/cutils.c +178/−0
- mquickjs/cutils.h +355/−0
- mquickjs/dtoa.c +1620/−0
- mquickjs/dtoa.h +83/−0
- mquickjs/libm.c +2260/−0
- mquickjs/libm.h +46/−0
- mquickjs/list.h +99/−0
- mquickjs/mqjs_stdlib.h +2692/−0
- mquickjs/mquickjs.c +18320/−0
- mquickjs/mquickjs.h +382/−0
- mquickjs/mquickjs_atom.h +76/−0
- mquickjs/mquickjs_build.h +97/−0
- mquickjs/mquickjs_opcode.h +264/−0
- mquickjs/mquickjs_priv.h +268/−0
- mquickjs/softfp_template.h +970/−0
- mquickjs/softfp_template_icvt.h +172/−0
- mquickjs/stdlib_wrapper.c +44/−0
- src/MQuickJS.hs +615/−0
- src/MQuickJS/Error.hs +109/−0
- src/MQuickJS/Types.hsc +113/−0
- test/Spec.hs +904/−0
+ 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,55 @@+# mquickjs-hs++This package provides a Haskell wrapper for the [Micro QuickJS](https://github.com/bellard/mquickjs) JavaScript Engine.+It has been inspired by the [quickjs-rs](https://github.com/theduke/quickjs-rs)+and [ocaml-quickjs](https://github.com/dhcmrlchtdj/ocaml-quickjs) libraries.+++## 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:++```hs+import MQuickJS++one_plus_two = mquickjs $ do+ res <- eval "1+2"+ liftIO $ print res+```++Declare a function and call it on an argument:++```hs+call_f = mquickjs $ do+ _ <- eval_ "function f(x) { return 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:++```hs+aeson_marshall = mquickjs $ do+ _ <- eval_ "function f(x) { return 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/ad-si/mquickjs-hs/issues)+and submit any pull requests to the+[git repository](https://github.com/ad-si/mquickjs-hs/).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ changelog.md view
@@ -0,0 +1,6 @@+# Changelog for mquickjs-hs++## 2026-01-14 - 0.1++* Initial implementation based on+ [quickjs-hs](https://github.com/haskell-github-trust/quickjs-hs)
+ mquickjs-hs.cabal view
@@ -0,0 +1,88 @@+cabal-version: 1.12+name: mquickjs-hs+version: 0.1.2.4+homepage: https://github.com/ad-si/mquickjs-hs#readme+bug-reports: https://github.com/ad-si/mquickjs-hs/issues+author: Sam Balco, Adrian Sieber+maintainer: haskell@ad-si.com+copyright: 2020 Sam Balco, 2026 Adrian Sieber+license: MIT+license-file: LICENSE+build-type: Simple+extra-source-files:+ changelog.md+ README.md++category: JavaScript+synopsis: Wrapper for the Micro QuickJS JavaScript Engine+description:+ This library is a wrapper for+ [Micro QuickJS](https://github.com/bellard/mquickjs),+ a compact JavaScript Engine.+ .+ To get started, see the Readme below.++extra-source-files: mquickjs/*.h++source-repository head+ type: git+ location: https://github.com/ad-si/mquickjs-hs++library+ exposed-modules:+ MQuickJS+ MQuickJS.Error+ MQuickJS.Types++ other-modules: Paths_mquickjs_hs+ hs-source-dirs: src+ build-depends:+ aeson >=2.0 && <2.3+ , base >=4.11 && <5+ , bytestring >=0.10 && <0.13+ , containers >=0.5 && <0.8+ , exceptions >=0.8 && <0.11+ , inline-c >=0.5 && <0.10+ , mtl >=2.2.2 && <2.4+ , scientific >=0.3.5 && <0.4+ , string-conv >=0.1.2 && <0.3+ , text >=1.2.0 && <2.2+ , time >=1.8 && <1.14+ , transformers >=0.5 && <0.7+ , unliftio-core >=0.1 && <0.3+ , unordered-containers >=0.2.8 && <0.3+ , vector >=0.12 && <0.14++ default-language: Haskell2010+ include-dirs: mquickjs+ c-sources:+ mquickjs/cutils.c+ mquickjs/dtoa.c+ mquickjs/libm.c+ mquickjs/mquickjs.c+ mquickjs/stdlib_wrapper.c++ includes: mquickjs/mquickjs.h+ install-includes: mquickjs/mquickjs.h+ cc-options: -static -D_GNU_SOURCE -include stddef.h++test-suite mquickjs-hs-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test+ other-modules: Paths_mquickjs_hs+ default-language: Haskell2010+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ aeson+ , base+ , exceptions+ , HUnit >=1.6.0.0+ , mquickjs-hs+ , QuickCheck >=2.9+ , tasty >=1.0+ , tasty-hunit >=0.10+ , tasty-quickcheck >=0.9+ , text+ , unordered-containers+ , vector
+ mquickjs/cutils.c view
@@ -0,0 +1,178 @@+/*+ * 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));+}++size_t __unicode_to_utf8(uint8_t *buf, unsigned int c)+{+ uint8_t *q = buf;++ if (c < 0x800) {+ *q++ = (c >> 6) | 0xc0;+ } else {+ if (c < 0x10000) {+ *q++ = (c >> 12) | 0xe0;+ } else {+ if (c < 0x00200000) {+ *q++ = (c >> 18) | 0xf0;+ } else {+ return 0;+ }+ *q++ = ((c >> 12) & 0x3f) | 0x80;+ }+ *q++ = ((c >> 6) & 0x3f) | 0x80;+ }+ *q++ = (c & 0x3f) | 0x80;+ return q - buf;+}++int __unicode_from_utf8(const uint8_t *p, size_t max_len, size_t *plen)+{+ size_t len = 1;+ int c;+ + c = p[0];+ if (c < 0xc0) {+ goto fail;+ } else if (c < 0xe0) {+ if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80))+ goto fail;+ c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);+ len = 2;+ if (unlikely(c < 0x80))+ goto fail;+ } else if (c < 0xf0) {+ if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80))+ goto fail;+ if (unlikely(max_len < 3 || (p[2] & 0xc0) != 0x80)) {+ len = 2;+ goto fail;+ }+ c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);+ len = 3;+ if (unlikely(c < 0x800))+ goto fail;+ } else if (c < 0xf8) {+ if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80))+ goto fail;+ if (unlikely(max_len < 3 || (p[2] & 0xc0) != 0x80)) {+ len = 2;+ goto fail;+ }+ if (unlikely(max_len < 4 || (p[3] & 0xc0) != 0x80)) {+ len = 3;+ goto fail;+ }+ c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f);+ len = 4;+ /* We explicitly accept surrogate pairs */+ if (unlikely(c < 0x10000 || c > 0x10ffff))+ goto fail;+ } else {+ fail:+ *plen = len;+ return -1;+ }+ *plen = len;+ return c;+}++int __utf8_get(const uint8_t *p, size_t *plen)+{+ size_t len;+ int c;+ + c = p[0];+ if (c < 0xc0) {+ len = 1;+ } else if (c < 0xe0) {+ c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);+ len = 2;+ } else if (c < 0xf0) {+ c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);+ len = 3;+ } else if (c < 0xf8) {+ c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f);+ len = 4;+ } else {+ len = 1;+ }+ *plen = len;+ return c;+}
+ mquickjs/cutils.h view
@@ -0,0 +1,355 @@+/*+ * 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.+ */+#ifndef CUTILS_H+#define CUTILS_H++#include <stdlib.h>+#include <inttypes.h>++/* set if CPU is big endian */+#undef WORDS_BIGENDIAN++#define likely(x) __builtin_expect(!!(x), 1)+#define unlikely(x) __builtin_expect(!!(x), 0)+#define force_inline inline __attribute__((always_inline))+#define no_inline __attribute__((noinline))+#define __maybe_unused __attribute__((unused))++#define xglue(x, y) x ## y+#define glue(x, y) xglue(x, y)+#define stringify(s) tostring(s)+#define tostring(s) #s++#ifndef offsetof+#define offsetof(type, field) ((size_t) &((type *)0)->field)+#endif+#ifndef countof+#define countof(x) (sizeof(x) / sizeof((x)[0]))+#endif++/* return the pointer of type 'type *' containing 'ptr' as field 'member' */+#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))++typedef int BOOL;++#ifndef FALSE+enum {+ FALSE = 0,+ TRUE = 1,+};+#endif++void pstrcpy(char *buf, int buf_size, const char *str);+char *pstrcat(char *buf, int buf_size, const char *s);+int strstart(const char *str, const char *val, const char **ptr);+int has_suffix(const char *str, const char *suffix);++static inline int max_int(int a, int b)+{+ if (a > b)+ return a;+ else+ return b;+}++static inline int min_int(int a, int b)+{+ if (a < b)+ return a;+ else+ return b;+}++static inline uint32_t max_uint32(uint32_t a, uint32_t b)+{+ if (a > b)+ return a;+ else+ return b;+}++static inline uint32_t min_uint32(uint32_t a, uint32_t b)+{+ if (a < b)+ return a;+ else+ return b;+}++static inline int64_t max_int64(int64_t a, int64_t b)+{+ if (a > b)+ return a;+ else+ return b;+}++static inline int64_t min_int64(int64_t a, int64_t b)+{+ if (a < b)+ return a;+ else+ return b;+}++static inline size_t max_size_t(size_t a, size_t b)+{+ if (a > b)+ return a;+ else+ return b;+}++static inline size_t min_size_t(size_t a, size_t b)+{+ if (a < b)+ return a;+ else+ return b;+}++/* WARNING: undefined if a = 0 */+static inline int clz32(unsigned int a)+{+ return __builtin_clz(a);+}++/* WARNING: undefined if a = 0 */+static inline int clz64(uint64_t a)+{+ return __builtin_clzll(a);+}++/* WARNING: undefined if a = 0 */+static inline int ctz32(unsigned int a)+{+ return __builtin_ctz(a);+}++/* WARNING: undefined if a = 0 */+static inline int ctz64(uint64_t a)+{+ return __builtin_ctzll(a);+}++struct __attribute__((packed)) packed_u64 {+ uint64_t v;+};++struct __attribute__((packed)) packed_u32 {+ uint32_t v;+};++struct __attribute__((packed)) packed_u16 {+ uint16_t v;+};++static inline uint64_t get_u64(const uint8_t *tab)+{+ return ((const struct packed_u64 *)tab)->v;+}++static inline int64_t get_i64(const uint8_t *tab)+{+ return (int64_t)((const struct packed_u64 *)tab)->v;+}++static inline void put_u64(uint8_t *tab, uint64_t val)+{+ ((struct packed_u64 *)tab)->v = val;+}++static inline uint32_t get_u32(const uint8_t *tab)+{+ return ((const struct packed_u32 *)tab)->v;+}++static inline int32_t get_i32(const uint8_t *tab)+{+ return (int32_t)((const struct packed_u32 *)tab)->v;+}++static inline void put_u32(uint8_t *tab, uint32_t val)+{+ ((struct packed_u32 *)tab)->v = val;+}++static inline uint32_t get_u16(const uint8_t *tab)+{+ return ((const struct packed_u16 *)tab)->v;+}++static inline int32_t get_i16(const uint8_t *tab)+{+ return (int16_t)((const struct packed_u16 *)tab)->v;+}++static inline void put_u16(uint8_t *tab, uint16_t val)+{+ ((struct packed_u16 *)tab)->v = val;+}++static inline uint32_t get_u8(const uint8_t *tab)+{+ return *tab;+}++static inline int32_t get_i8(const uint8_t *tab)+{+ return (int8_t)*tab;+}++static inline void put_u8(uint8_t *tab, uint8_t val)+{+ *tab = val;+}++static inline uint16_t bswap16(uint16_t x)+{+ return (x >> 8) | (x << 8);+}++static inline uint32_t bswap32(uint32_t v)+{+ return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |+ ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);+}++static inline uint64_t bswap64(uint64_t v)+{+ return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));+}++static inline uint32_t get_be32(const uint8_t *d)+{+ return bswap32(get_u32(d));+}++static inline void put_be32(uint8_t *d, uint32_t v)+{+ put_u32(d, bswap32(v));+}++#define UTF8_CHAR_LEN_MAX 4++size_t __unicode_to_utf8(uint8_t *buf, unsigned int c);+int __unicode_from_utf8(const uint8_t *p, size_t max_len, size_t *plen);+int __utf8_get(const uint8_t *p, size_t *plen);++/* Note: at most 21 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes+ are output. */+static inline size_t unicode_to_utf8(uint8_t *buf, unsigned int c)+{+ if (c < 0x80) {+ buf[0] = c;+ return 1;+ } else {+ return __unicode_to_utf8(buf, c);+ }+}++/* return -1 in case of error. Surrogates are accepted. max_len must+ be >= 1. *plen is set in case of error and always >= 1. */+static inline int unicode_from_utf8(const uint8_t *buf, size_t max_len, size_t *plen)+{+ if (buf[0] < 0x80) {+ *plen = 1;+ return buf[0];+ } else {+ return __unicode_from_utf8(buf, max_len, plen);+ }+}++/* Warning: no error checking is done so the UTF-8 encoding must be+ validated before. */+static force_inline int utf8_get(const uint8_t *buf, size_t *plen)+{+ if (likely(buf[0] < 0x80)) {+ *plen = 1;+ return buf[0];+ } else {+ return __utf8_get(buf, plen);+ }+}++static inline int from_hex(int c)+{+ if (c >= '0' && c <= '9')+ return c - '0';+ else if (c >= 'A' && c <= 'F')+ return c - 'A' + 10;+ else if (c >= 'a' && c <= 'f')+ return c - 'a' + 10;+ else+ return -1;+}++static inline uint64_t float64_as_uint64(double d)+{+ union {+ double d;+ uint64_t u64;+ } u;+ u.d = d;+ return u.u64;+}++static inline double uint64_as_float64(uint64_t u64)+{+ union {+ double d;+ uint64_t u64;+ } u;+ u.u64 = u64;+ return u.d;+}++typedef union {+ uint32_t u32;+ float f;+} f32_union;++static inline uint32_t float_as_uint(float f)+{+ f32_union u;+ u.f = f;+ return u.u32;+}++static inline float uint_as_float(uint32_t v)+{+ f32_union u;+ u.u32 = v;+ return u.f;+}++#endif /* CUTILS_H */
+ mquickjs/dtoa.c view
@@ -0,0 +1,1620 @@+/*+ * Tiny float64 printing and parsing library+ *+ * Copyright (c) 2024-2025 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 <ctype.h>+#include <sys/time.h>+#include <math.h>+#include <setjmp.h>++#include "cutils.h"+#include "dtoa.h"++/* + TODO:+ - test n_digits=101 instead of 100+ - simplify subnormal handling+ - reduce max memory usage+ - free format: could add shortcut if exact result+ - use 64 bit limb_t when possible+ - use another algorithm for free format dtoa in base 10 (ryu ?)+*/++#define USE_POW5_TABLE+/* use fast path to print small integers in free format */+#define USE_FAST_INT++#define LIMB_LOG2_BITS 5++#define LIMB_BITS (1 << LIMB_LOG2_BITS)++typedef int32_t slimb_t;+typedef uint32_t limb_t;+typedef uint64_t dlimb_t;++#define LIMB_DIGITS 9++#define JS_RADIX_MAX 36++#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */+#define MANT_LEN_MAX 18 /* < 36^100 */++typedef intptr_t mp_size_t;++/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */+typedef struct {+ int len; /* >= 1 */+ limb_t tab[];+} mpb_t;++static 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;+}++/* 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;+}++/* 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;+}++static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n,+ limb_t b, limb_t r)+{+ slimb_t i;+ 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;+}++/* 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);+}++/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low <+ 2^shift. */+static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t low)+{+ mp_size_t i;+ limb_t l, a;++ assert(shift >= 1 && shift < LIMB_BITS);+ l = low;+ for(i = 0; i < n; i++) {+ a = tab[i];+ tab_r[i] = (a << shift) | l;+ l = (a >> (LIMB_BITS - shift)); + }+ return l;+}++static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n,+ limb_t b, limb_t r, limb_t b_inv, int shift)+{+ slimb_t i;++ if (shift != 0) {+ r = (r << shift) | mp_shl(tabr, taba, n, shift, 0);+ }+ for(i = n - 1; i >= 0; i--) {+ tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);+ }+ r >>= shift;+ return r;+}++static __maybe_unused void mpb_dump(const char *str, const mpb_t *a)+{+ int i;+ + printf("%s= 0x", str);+ for(i = a->len - 1; i >= 0; i--) {+ printf("%08x", a->tab[i]);+ if (i != 0)+ printf("_");+ }+ printf("\n");+}++static void mpb_renorm(mpb_t *r)+{+ while (r->len > 1 && r->tab[r->len - 1] == 0)+ r->len--;+}++#ifdef USE_POW5_TABLE+static const uint32_t pow5_table[17] = {+ 0x00000005, 0x00000019, 0x0000007d, 0x00000271, + 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1, + 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51, + 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1, + 0xa2bc2ec5, +};++static const uint8_t pow5h_table[4] = {+ 0x00000001, 0x00000007, 0x00000023, 0x000000b1, +};++static const uint32_t pow5_inv_table[13] = {+ 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1,+ 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23,+ 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812,+ 0xc25c2684,+};+#endif++/* return a^b */+static uint64_t pow_ui(uint32_t a, uint32_t b)+{+ int i, n_bits;+ uint64_t r;+ if (b == 0)+ return 1;+ if (b == 1)+ return a;+#ifdef USE_POW5_TABLE+ if ((a == 5 || a == 10) && b <= 17) {+ r = pow5_table[b - 1];+ if (b >= 14) {+ r |= (uint64_t)pow5h_table[b - 14] << 32;+ }+ if (a == 10)+ r <<= b;+ return r;+ }+#endif+ r = a;+ n_bits = 32 - clz32(b);+ for(i = n_bits - 2; i >= 0; i--) {+ r *= r;+ if ((b >> i) & 1)+ r *= a;+ }+ return r;+}++static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b)+{+ uint32_t r_inv, r;+ int shift;+#ifdef USE_POW5_TABLE+ if (a == 5 && b >= 1 && b <= 13) {+ r = pow5_table[b - 1];+ shift = clz32(r);+ r <<= shift;+ r_inv = pow5_inv_table[b - 1];+ } else+#endif+ {+ r = pow_ui(a, b);+ shift = clz32(r);+ r <<= shift;+ r_inv = udiv1norm_init(r);+ }+ *pshift = shift;+ *pr_inv = r_inv;+ return r;+}++enum {+ JS_RNDN, /* round to nearest, ties to even */+ JS_RNDNA, /* round to nearest, ties away from zero */+ JS_RNDZ,+};++static int mpb_get_bit(const mpb_t *r, int k)+{+ int l;+ + l = (unsigned)k / LIMB_BITS;+ k = k & (LIMB_BITS - 1);+ if (l >= r->len)+ return 0;+ else+ return (r->tab[l] >> k) & 1;+}++/* compute round(r / 2^shift). 'shift' can be negative */+static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode)+{+ int l, i;++ if (shift == 0)+ return;+ if (shift < 0) {+ shift = -shift;+ l = (unsigned)shift / LIMB_BITS;+ shift = shift & (LIMB_BITS - 1);+ if (shift != 0) {+ r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0);+ r->len++;+ mpb_renorm(r);+ }+ if (l > 0) {+ for(i = r->len - 1; i >= 0; i--)+ r->tab[i + l] = r->tab[i];+ for(i = 0; i < l; i++)+ r->tab[i] = 0;+ r->len += l;+ }+ } else {+ limb_t bit1, bit2;+ int k, add_one;+ + switch(rnd_mode) {+ default:+ case JS_RNDZ:+ add_one = 0;+ break;+ case JS_RNDN:+ case JS_RNDNA:+ bit1 = mpb_get_bit(r, shift - 1);+ if (bit1) {+ if (rnd_mode == JS_RNDNA) {+ bit2 = 1;+ } else {+ /* bit2 = oring of all the bits after bit1 */+ bit2 = 0;+ if (shift >= 2) {+ k = shift - 1;+ l = (unsigned)k / LIMB_BITS;+ k = k & (LIMB_BITS - 1);+ for(i = 0; i < min_int(l, r->len); i++)+ bit2 |= r->tab[i];+ if (l < r->len)+ bit2 |= r->tab[l] & (((limb_t)1 << k) - 1);+ }+ }+ if (bit2) {+ add_one = 1;+ } else {+ /* round to even */+ add_one = mpb_get_bit(r, shift);+ }+ } else {+ add_one = 0;+ }+ break;+ }++ l = (unsigned)shift / LIMB_BITS;+ shift = shift & (LIMB_BITS - 1);+ if (l >= r->len) {+ r->len = 1;+ r->tab[0] = add_one;+ } else {+ if (l > 0) {+ r->len -= l;+ for(i = 0; i < r->len; i++)+ r->tab[i] = r->tab[i + l];+ }+ if (shift != 0) {+ mp_shr(r->tab, r->tab, r->len, shift, 0);+ mpb_renorm(r);+ }+ if (add_one) {+ limb_t a;+ a = mp_add_ui(r->tab, 1, r->len);+ if (a)+ r->tab[r->len++] = a;+ }+ }+ }+}++/* return -1, 0 or 1 */+static int mpb_cmp(const mpb_t *a, const mpb_t *b)+{+ mp_size_t i;+ if (a->len < b->len)+ return -1;+ else if (a->len > b->len)+ return 1;+ for(i = a->len - 1; i >= 0; i--) {+ if (a->tab[i] != b->tab[i]) {+ if (a->tab[i] < b->tab[i])+ return -1;+ else+ return 1;+ }+ }+ return 0;+}++static void mpb_set_u64(mpb_t *r, uint64_t m)+{+#if LIMB_BITS == 64+ r->tab[0] = m;+ r->len = 1;+#else+ r->tab[0] = m;+ r->tab[1] = m >> LIMB_BITS;+ if (r->tab[1] == 0)+ r->len = 1;+ else+ r->len = 2;+#endif+}++static uint64_t mpb_get_u64(mpb_t *r)+{+#if LIMB_BITS == 64+ return r->tab[0];+#else+ if (r->len == 1) {+ return r->tab[0];+ } else {+ return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS);+ }+#endif+}++/* floor_log2() = position of the first non zero bit or -1 if zero. */+static int mpb_floor_log2(mpb_t *a)+{+ limb_t v;+ v = a->tab[a->len - 1];+ if (v == 0)+ return -1;+ else+ return a->len * LIMB_BITS - 1 - clz32(v);+}++#define MUL_LOG2_RADIX_BASE_LOG2 24++/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */+static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = {+ 0x000000, 0xa1849d, 0x000000, 0x6e40d2, + 0x6308c9, 0x5b3065, 0x000000, 0x50c24e, + 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54, + 0x433d00, 0x418677, 0x000000, 0x3ea16b, + 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899, + 0x39680b, 0x3897b3, 0x37d5af, 0x372069, + 0x367686, 0x35d6df, 0x354072, 0x34b261, + 0x342bea, 0x33ac62, 0x000000, 0x32bfd9, + 0x3251dd, 0x31e8d6, 0x318465,+};++/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */+static int mul_log2_radix(int a, int radix)+{+ int radix_bits, mult;++ if ((radix & (radix - 1)) == 0) {+ /* if the radix is a power of two better to do it exactly */+ radix_bits = 31 - clz32(radix);+ if (a < 0)+ a -= radix_bits - 1;+ return a / radix_bits;+ } else {+ mult = mul_log2_radix_table[radix - 2];+ return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2;+ }+}++#if 0+static void build_mul_log2_radix_table(void)+{+ int base, radix, mult, col, base_log2;++ base_log2 = 24;+ base = 1 << base_log2;+ col = 0;+ for(radix = 2; radix <= 36; radix++) {+ if ((radix & (radix - 1)) == 0)+ mult = 0;+ else+ mult = lrint((double)base / log2(radix));+ printf("0x%06x, ", mult);+ if (++col == 4) {+ printf("\n");+ col = 0;+ }+ }+ printf("\n");+}++static void mul_log2_radix_test(void)+{+ int radix, i, ref, r;+ + for(radix = 2; radix <= 36; radix++) {+ for(i = -2048; i <= 2047; i++) {+ ref = (int)floor((double)i / log2(radix));+ r = mul_log2_radix(i, radix);+ if (ref != r) {+ printf("ERROR: radix=%d i=%d r=%d ref=%d\n",+ radix, i, r, ref);+ exit(1);+ }+ }+ }+ if (0)+ build_mul_log2_radix_table();+}+#endif++static void u32toa_len(char *buf, uint32_t n, size_t len)+{+ int digit, i;+ for(i = len - 1; i >= 0; i--) {+ digit = n % 10;+ n = n / 10;+ buf[i] = digit + '0';+ }+}++/* for power of 2 radixes. len >= 1 */+static void u64toa_bin_len(char *buf, uint64_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;+ }+}++/* len >= 1. 2 <= radix <= 36 */+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 */+#if LIMB_BITS == 32+ u32toa_len(buf, n, len);+#else+ /* XXX: optimize */+ for(i = len - 1; i >= 0; i--) {+ digit = (limb_t)n % 10;+ n = (limb_t)n / 10;+ buf[i] = digit + '0';+ }+#endif+ } 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;+ }+ }+}++size_t u32toa(char *buf, uint32_t n)+{+ char buf1[10], *q;+ size_t len;+ + q = buf1 + sizeof(buf1);+ do {+ *--q = n % 10 + '0';+ n /= 10;+ } while (n != 0);+ len = buf1 + sizeof(buf1) - q;+ memcpy(buf, q, len);+ return len;+}++size_t i32toa(char *buf, int32_t n)+{+ if (n >= 0) {+ return u32toa(buf, n);+ } else {+ buf[0] = '-';+ return u32toa(buf + 1, -(uint32_t)n) + 1;+ }+}++#ifdef USE_FAST_INT+size_t u64toa(char *buf, uint64_t n)+{+ if (n < 0x100000000) {+ return u32toa(buf, n);+ } else {+ uint64_t n1;+ char *q = buf;+ uint32_t n2;+ + n1 = n / 1000000000;+ n %= 1000000000;+ if (n1 >= 0x100000000) {+ n2 = n1 / 1000000000;+ n1 = n1 % 1000000000;+ /* at most two digits */+ if (n2 >= 10) {+ *q++ = n2 / 10 + '0';+ n2 %= 10;+ }+ *q++ = n2 + '0';+ u32toa_len(q, n1, 9);+ q += 9;+ } else {+ q += u32toa(q, n1);+ }+ u32toa_len(q, n, 9);+ q += 9;+ return q - buf;+ }+}++size_t i64toa(char *buf, int64_t n)+{+ if (n >= 0) {+ return u64toa(buf, n);+ } else {+ buf[0] = '-';+ return u64toa(buf + 1, -(uint64_t)n) + 1;+ }+}++/* XXX: only tested for 1 <= n < 2^53 */+size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix)+{+ int radix_bits, l;+ if (likely(radix == 10))+ return u64toa(buf, n);+ if ((radix & (radix - 1)) == 0) {+ radix_bits = 31 - clz32(radix);+ if (n == 0)+ l = 1;+ else+ l = (64 - clz64(n) + radix_bits - 1) / radix_bits;+ u64toa_bin_len(buf, n, radix_bits, l);+ return l;+ } else {+ char buf1[41], *q; /* maximum length for radix = 3 */+ size_t len;+ int digit;+ q = buf1 + sizeof(buf1);+ do {+ digit = n % radix;+ n /= radix;+ if (digit < 10)+ digit += '0';+ else+ digit += 'a' - 10;+ *--q = digit;+ } while (n != 0);+ len = buf1 + sizeof(buf1) - q;+ memcpy(buf, q, len);+ return len;+ }+}++size_t i64toa_radix(char *buf, int64_t n, unsigned int radix)+{+ if (n >= 0) {+ return u64toa_radix(buf, n, radix);+ } else {+ buf[0] = '-';+ return u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1;+ }+}+#endif /* USE_FAST_INT */++static const uint8_t digits_per_limb_table[JS_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 const uint32_t radix_base_table[JS_RADIX_MAX - 1] = {+ 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,+ 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,+ 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,+ 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,+ 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,+ 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,+ 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,+ 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,+ 0x5c13d840, 0x6d91b519, 0x81bf1000,+};++/* XXX: remove the table ? */+static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = {+ 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12,+};++/* we limit the maximum number of significant digits for atod to about+ 128 bits of precision for non power of two bases. The only+ requirement for Javascript is at least 20 digits in base 10. For+ power of two bases, we do an exact rounding in all the cases. */+static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = {+ 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24,+};++/* if abs(d) >= B^max_exponent, it is an overflow */+static const int16_t max_exponent[JS_RADIX_MAX - 1] = {+ 1024, 647, 512, 442, 397, 365, 342, 324, + 309, 297, 286, 277, 269, 263, 256, 251, + 246, 242, 237, 234, 230, 227, 224, 221, + 218, 216, 214, 211, 209, 207, 205, 203, + 202, 200, 199, +};++/* if abs(d) <= B^min_exponent, it is an underflow */+static const int16_t min_exponent[JS_RADIX_MAX - 1] = {+-1075, -679, -538, -463, -416, -383, -359, -340, + -324, -311, -300, -291, -283, -276, -269, -263, + -258, -254, -249, -245, -242, -238, -235, -232, + -229, -227, -224, -222, -220, -217, -215, -214, + -212, -210, -208, +};++#if 0+void build_tables(void)+{+ int r, j, radix, n, col, i;+ + /* radix_base_table */+ for(radix = 2; radix <= 36; radix++) {+ r = 1;+ for(j = 0; j < digits_per_limb_table[radix - 2]; j++) {+ r *= radix;+ }+ printf(" 0x%08x,", r);+ if ((radix % 4) == 1)+ printf("\n");+ }+ printf("\n");++ /* dtoa_max_digits_table */+ for(radix = 2; radix <= 36; radix++) {+ /* Note: over estimated when the radix is a power of two */+ printf(" %d,", 1 + (int)ceil(53.0 / log2(radix)));+ }+ printf("\n");++ /* atod_max_digits_table */+ for(radix = 2; radix <= 36; radix++) {+ if ((radix & (radix - 1)) == 0) {+ /* 64 bits is more than enough */+ n = (int)floor(64.0 / log2(radix));+ } else {+ n = (int)floor(128.0 / log2(radix));+ }+ printf(" %d,", n);+ }+ printf("\n");++ printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n");+ col = 0;+ for(radix = 2; radix <= 36; radix++) {+ printf("%5d, ", (int)ceil(1024 / log2(radix)));+ if (++col == 8) {+ col = 0;+ printf("\n");+ }+ }+ printf("\n};\n\n");++ printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n");+ col = 0; + for(radix = 2; radix <= 36; radix++) {+ printf("%5d, ", (int)floor(-1075 / log2(radix)));+ if (++col == 8) {+ col = 0;+ printf("\n");+ }+ }+ printf("\n};\n\n");++ printf("static const uint32_t pow5_table[16] = {\n");+ col = 0; + for(i = 2; i <= 17; i++) {+ r = 1;+ for(j = 0; j < i; j++) {+ r *= 5;+ }+ printf("0x%08x, ", r);+ if (++col == 4) {+ col = 0;+ printf("\n");+ }+ }+ printf("\n};\n\n");++ /* high part */+ printf("static const uint8_t pow5h_table[4] = {\n");+ col = 0; + for(i = 14; i <= 17; i++) {+ uint64_t r1;+ r1 = 1;+ for(j = 0; j < i; j++) {+ r1 *= 5;+ }+ printf("0x%08x, ", (uint32_t)(r1 >> 32));+ if (++col == 4) {+ col = 0;+ printf("\n");+ }+ }+ printf("\n};\n\n");+}+#endif++/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits,+ the dot is not displayed. 'a' is modified. */+static int output_digits(char *buf,+ mpb_t *a, int radix, int n_digits1,+ int dot_pos)+{+ int n_digits, digits_per_limb, radix_bits, n, len;++ n_digits = n_digits1;+ if ((radix & (radix - 1)) == 0) {+ /* radix = 2^radix_bits */+ radix_bits = 31 - clz32(radix);+ } else {+ radix_bits = 0;+ }+ digits_per_limb = digits_per_limb_table[radix - 2];+ if (radix_bits != 0) {+ for(;;) {+ n = min_int(n_digits, digits_per_limb);+ n_digits -= n;+ u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n);+ if (n_digits == 0)+ break;+ mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ);+ }+ } else {+ limb_t r;+ while (n_digits != 0) {+ n = min_int(n_digits, digits_per_limb);+ n_digits -= n;+ r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0);+ mpb_renorm(a);+ limb_to_a(buf + n_digits, r, radix, n);+ }+ }++ /* add the dot */+ len = n_digits1;+ if (dot_pos != n_digits1) {+ memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos);+ buf[dot_pos] = '.';+ len++;+ }+ return len;+}++/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f *+ 2^-e_offset. 'f' can be negative. */+static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, BOOL is_int, int e)+{+ int e_offset, d, n, n0;++ e_offset = -f * radix_shift;+ if (radix1 != 1) {+ d = digits_per_limb_table[radix1 - 2];+ if (f >= 0) {+ limb_t h, b;+ + b = 0;+ n0 = 0;+ while (f != 0) {+ n = min_int(f, d);+ if (n != n0) {+ b = pow_ui(radix1, n);+ n0 = n;+ }+ h = mp_mul1(a->tab, a->tab, a->len, b, 0);+ if (h != 0) {+ a->tab[a->len++] = h;+ }+ f -= n;+ }+ } else {+ int extra_bits, l, shift;+ limb_t r, rem, b, b_inv;+ + f = -f;+ l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */+ e_offset += l * LIMB_BITS;+ if (!is_int) {+ /* at least 'e' bits are needed in the final result for rounding */+ extra_bits = max_int(e - mpb_floor_log2(a), 0);+ } else {+ /* at least two extra bits are needed in the final result+ for rounding */+ extra_bits = max_int(2 + e - e_offset, 0);+ }+ e_offset += extra_bits;+ mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ);+ + b = 0;+ b_inv = 0;+ shift = 0;+ n0 = 0;+ rem = 0;+ while (f != 0) {+ n = min_int(f, d);+ if (n != n0) {+ b = pow_ui_inv(&b_inv, &shift, radix1, n);+ n0 = n;+ }+ r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift);+ rem |= r;+ mpb_renorm(a);+ f -= n;+ }+ /* if the remainder is non zero, use it for rounding */+ a->tab[0] |= (rem != 0);+ }+ }+ return e_offset;+}++/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */+static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f,+ int rnd_mode)+{+ int e_offset;++ mpb_set_u64(tmp1, m);+ e_offset = mul_pow(tmp1, radix1, radix_shift, f, TRUE, e);+ mpb_shr_round(tmp1, -e + e_offset, rnd_mode);+}++/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */+static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode)+{+ int e;+ uint64_t m;++ if (a->tab[0] == 0 && a->len == 1) {+ /* zero result */+ m = 0;+ e = 0; /* don't care */+ } else {+ int prec, prec1, e_min;+ e = mpb_floor_log2(a) + 1 - e_offset;+ prec1 = 53;+ e_min = -1021;+ if (e < e_min) {+ /* subnormal result or zero */+ prec = prec1 - (e_min - e);+ } else {+ prec = prec1;+ }+ mpb_shr_round(a, e + e_offset - prec, rnd_mode);+ m = mpb_get_u64(a);+ m <<= (53 - prec);+ /* mantissa overflow due to rounding */+ if (m >= (uint64_t)1 << 53) {+ m >>= 1;+ e++;+ }+ }+ *pe = e;+ return m;+}++/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52+ <= m < 2^53 or m = 0.+ 'a' is modified. */+static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a,+ int radix1, int radix_shift, int f, int rnd_mode)+{+ int e_offset;++ e_offset = mul_pow(a, radix1, radix_shift, f, FALSE, 55);+ return round_to_d(pe, a, e_offset, rnd_mode);+}++#ifdef JS_DTOA_DUMP_STATS+static int out_len_count[17];++void js_dtoa_dump_stats(void)+{+ int i, sum;+ sum = 0;+ for(i = 0; i < 17; i++)+ sum += out_len_count[i];+ for(i = 0; i < 17; i++) {+ printf("%2d %8d %5.2f%%\n",+ i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100);+ }+}+#endif++/* return a maximum bound of the string length. The bound depends on+ 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED+ is enabled. */+int js_dtoa_max_len(double d, int radix, int n_digits, int flags)+{+ int fmt = flags & JS_DTOA_FORMAT_MASK;+ int n, e;+ uint64_t a;++ if (fmt != JS_DTOA_FORMAT_FRAC) {+ if (fmt == JS_DTOA_FORMAT_FREE) {+ n = dtoa_max_digits_table[radix - 2];+ } else {+ n = n_digits;+ }+ if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) {+ /* no exponential */+ a = float64_as_uint64(d);+ e = (a >> 52) & 0x7ff;+ if (e == 0x7ff) {+ /* NaN, Infinity */+ n = 0;+ } else {+ e -= 1023;+ /* XXX: adjust */+ n += 10 + abs(mul_log2_radix(e - 1, radix));+ }+ } else {+ /* extra: sign, 1 dot and exponent "e-1000" */+ n += 1 + 1 + 6;+ }+ } else {+ a = float64_as_uint64(d);+ e = (a >> 52) & 0x7ff;+ if (e == 0x7ff) {+ /* NaN, Infinity */+ n = 0;+ } else {+ /* high bound for the integer part */+ e -= 1023;+ /* x < 2^(e + 1) */+ if (e < 0) {+ n = 1;+ } else {+ n = 2 + mul_log2_radix(e - 1, radix);+ }+ /* sign, extra digit, 1 dot */+ n += 1 + 1 + 1 + n_digits;+ }+ }+ return max_int(n, 9); /* also include NaN and [-]Infinity */+}++#if defined(__SANITIZE_ADDRESS__) && 0+static void *dtoa_malloc(uint64_t **pptr, size_t size)+{+ return malloc(size);+}+static void dtoa_free(void *ptr)+{+ free(ptr);+}+#else+static void *dtoa_malloc(uint64_t **pptr, size_t size)+{+ void *ret;+ ret = *pptr;+ *pptr += (size + 7) / 8;+ return ret;+}++static void dtoa_free(void *ptr)+{+}+#endif++/* return the length */+int js_dtoa(char *buf, double d, int radix, int n_digits, int flags,+ JSDTOATempMem *tmp_mem)+{+ uint64_t a, m, *mptr = tmp_mem->mem;+ int e, sgn, l, E, P, i, E_max, radix1, radix_shift;+ char *q;+ mpb_t *tmp1, *mant_max;+ int fmt = flags & JS_DTOA_FORMAT_MASK;++ tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX);+ mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX);+ assert((mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0]));++ radix_shift = ctz32(radix);+ radix1 = radix >> radix_shift;+ a = float64_as_uint64(d);+ sgn = a >> 63;+ e = (a >> 52) & 0x7ff;+ m = a & (((uint64_t)1 << 52) - 1);+ q = buf;+ if (e == 0x7ff) {+ if (m == 0) {+ if (sgn)+ *q++ = '-';+ memcpy(q, "Infinity", 8);+ q += 8;+ } else {+ memcpy(q, "NaN", 3);+ q += 3;+ }+ goto done;+ } else if (e == 0) {+ if (m == 0) {+ tmp1->len = 1;+ tmp1->tab[0] = 0;+ E = 1;+ if (fmt == JS_DTOA_FORMAT_FREE)+ P = 1;+ else if (fmt == JS_DTOA_FORMAT_FRAC)+ P = n_digits + 1;+ else+ P = n_digits;+ /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */+ if (sgn && (flags & JS_DTOA_MINUS_ZERO))+ *q++ = '-';+ goto output;+ }+ /* denormal number: convert to a normal number */+ l = clz64(m) - 11;+ e -= l - 1;+ m <<= l;+ } else {+ m |= (uint64_t)1 << 52;+ }+ if (sgn)+ *q++ = '-';+ /* remove the bias */+ e -= 1022;+ /* d = 2^(e-53)*m */+ // printf("m=0x%016" PRIx64 " e=%d\n", m, e);+#ifdef USE_FAST_INT+ if (fmt == JS_DTOA_FORMAT_FREE &&+ e >= 1 && e <= 53 &&+ (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 &&+ (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) {+ m >>= 53 - e;+ /* 'm' is never zero */+ q += u64toa_radix(q, m, radix);+ goto done;+ }+#endif+ + /* this choice of E implies F=round(x*B^(P-E) is such as: + B^(P-1) <= F < 2.B^P. */+ E = 1 + mul_log2_radix(e - 1, radix);+ + if (fmt == JS_DTOA_FORMAT_FREE) {+ int P_max, E0, e1, E_found, P_found;+ uint64_t m1, mant_found, mant, mant_max1;+ /* P_max is guaranteed to work by construction */+ P_max = dtoa_max_digits_table[radix - 2];+ E0 = E;+ E_found = 0;+ P_found = 0;+ mant_found = 0;+ /* find the minimum number of digits by successive tries */+ P = P_max; /* P_max is guaranteed to work */+ for(;;) {+ /* mant_max always fits on 64 bits */+ mant_max1 = pow_ui(radix, P);+ /* compute the mantissa in base B */+ E = E0;+ for(;;) {+ /* XXX: add inexact flag */+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN);+ mant = mpb_get_u64(tmp1);+ if (mant < mant_max1)+ break;+ E++; /* at most one iteration is possible */+ }+ /* remove useless trailing zero digits */+ while ((mant % radix) == 0) {+ mant /= radix;+ P--;+ }+ /* guaranteed to work for P = P_max */+ if (P_found == 0)+ goto prec_found;+ /* convert back to base 2 */+ mpb_set_u64(tmp1, mant);+ m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN);+ // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1);+ /* Note: (m, e) is never zero here, so the exponent for m1+ = 0 does not matter */+ if (m1 == m && e1 == e) {+ prec_found:+ P_found = P;+ E_found = E;+ mant_found = mant;+ if (P == 1)+ break;+ P--; /* try lower exponent */+ } else {+ break;+ }+ }+ P = P_found;+ E = E_found;+ mpb_set_u64(tmp1, mant_found);+#ifdef JS_DTOA_DUMP_STATS+ if (radix == 10) {+ out_len_count[P - 1]++;+ }+#endif + } else if (fmt == JS_DTOA_FORMAT_FRAC) {+ int len;++ assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS);+ /* P = max_int(E, 1) + n_digits; */+ /* frac is rounded using RNDNA */+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA);++ /* we add one extra digit on the left and remove it if needed+ to avoid testing if the result is < radix^P */+ len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits,+ max_int(E + 1, 1));+ if (q[0] == '0' && len >= 2 && q[1] != '.') {+ len--;+ memmove(q, q + 1, len);+ }+ q += len;+ goto done;+ } else {+ int pow_shift;+ assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS);+ P = n_digits;+ /* mant_max = radix^P */+ mant_max->len = 1;+ mant_max->tab[0] = 1;+ pow_shift = mul_pow(mant_max, radix1, radix_shift, P, FALSE, 0);+ mpb_shr_round(mant_max, pow_shift, JS_RNDZ);+ + for(;;) {+ /* fixed and frac are rounded using RNDNA */+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA);+ if (mpb_cmp(tmp1, mant_max) < 0)+ break;+ E++; /* at most one iteration is possible */+ }+ }+ output:+ if (fmt == JS_DTOA_FORMAT_FIXED)+ E_max = n_digits;+ else+ E_max = dtoa_max_digits_table[radix - 2] + 4;+ if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED ||+ ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) {+ q += output_digits(q, tmp1, radix, P, 1);+ E--;+ if (radix == 10) {+ *q++ = 'e';+ } else if (radix1 == 1 && radix_shift <= 4) {+ E *= radix_shift;+ *q++ = 'p';+ } else {+ *q++ = '@';+ }+ if (E < 0) {+ *q++ = '-';+ E = -E;+ } else {+ *q++ = '+';+ }+ q += u32toa(q, E);+ } else if (E <= 0) {+ *q++ = '0';+ *q++ = '.';+ for(i = 0; i < -E; i++)+ *q++ = '0';+ q += output_digits(q, tmp1, radix, P, P);+ } else {+ q += output_digits(q, tmp1, radix, P, min_int(P, E));+ for(i = 0; i < E - P; i++)+ *q++ = '0';+ }+ done:+ *q = '\0';+ dtoa_free(mant_max);+ dtoa_free(tmp1);+ return q - buf;+}++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;+}++/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */+static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a)+{+ int i;+ if (r->tab[0] == 0 && r->len == 1) {+ r->tab[0] = a;+ } else {+ if (radix_base == 0) {+ for(i = r->len; i >= 0; i--) {+ r->tab[i + 1] = r->tab[i];+ }+ r->tab[0] = a;+ } else {+ r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len,+ radix_base, a);+ }+ r->len++;+ mpb_renorm(r);+ }+}++/* XXX: add fast path for small integers */+double js_atod(const char *str, const char **pnext, int radix, int flags,+ JSATODTempMem *tmp_mem)+{+ uint64_t *mptr = tmp_mem->mem;+ const char *p, *p_start;+ limb_t cur_limb, radix_base, extra_digits;+ int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift;+ int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos;+ mpb_t *tmp0;+ double dval;+ BOOL is_bin_exp, is_zero, expn_overflow;+ uint64_t m, a;++ tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX);+ assert((mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0]));+ /* optional separator between digits */+ sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;++ p = str;+ 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)) {+ p += 2;+ radix = 16;+ } else if ((p[1] == 'o' || p[1] == 'O') &&+ radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) {+ p += 2;+ radix = 8;+ } else if ((p[1] == 'b' || p[1] == 'B') &&+ radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) {+ p += 2;+ radix = 2;+ } else if ((p[1] >= '0' && p[1] <= '9') &&+ radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) {+ int i;+ 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 {+ if (!(flags & JS_ATOD_INT_ONLY) && strstart(p, "Infinity", &p))+ goto overflow;+ }+ if (radix == 0)+ radix = 10;++ cur_limb = 0;+ expn_offset = 0;+ digit_count = 0;+ limb_digit_count = 0;+ max_digits = atod_max_digits_table[radix - 2];+ digits_per_limb = digits_per_limb_table[radix - 2];+ radix_base = radix_base_table[radix - 2];+ radix_shift = ctz32(radix);+ radix1 = radix >> radix_shift;+ if (radix1 == 1) {+ /* radix = 2^radix_bits */+ radix_bits = radix_shift;+ } else {+ radix_bits = 0;+ }+ tmp0->len = 1;+ tmp0->tab[0] = 0;+ extra_digits = 0;+ pos = 0;+ dot_pos = -1;+ /* skip leading zeros */+ for(;;) {+ if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) &&+ !(flags & JS_ATOD_INT_ONLY)) {+ if (*p == sep)+ goto fail;+ if (dot_pos >= 0)+ break;+ dot_pos = pos;+ p++;+ }+ if (*p == sep && p > p_start && p[1] == '0')+ p++;+ if (*p != '0')+ break;+ p++;+ pos++;+ }+ + sig_pos = pos;+ for(;;) {+ limb_t c;+ if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) &&+ !(flags & JS_ATOD_INT_ONLY)) {+ if (*p == sep)+ goto fail;+ if (dot_pos >= 0)+ break;+ dot_pos = pos;+ p++;+ }+ if (*p == sep && p > p_start && to_digit(p[1]) < radix)+ p++;+ c = to_digit(*p);+ if (c >= radix)+ break;+ p++;+ pos++;+ if (digit_count < max_digits) {+ /* XXX: could be faster when radix_bits != 0 */+ cur_limb = cur_limb * radix + c;+ limb_digit_count++;+ if (limb_digit_count == digits_per_limb) {+ mpb_mul1_base(tmp0, radix_base, cur_limb);+ cur_limb = 0;+ limb_digit_count = 0;+ }+ digit_count++;+ } else {+ extra_digits |= c;+ }+ }+ if (limb_digit_count != 0) {+ mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb);+ }+ if (digit_count == 0) {+ is_zero = TRUE;+ expn_offset = 0;+ } else {+ is_zero = FALSE;+ if (dot_pos < 0)+ dot_pos = pos;+ expn_offset = sig_pos + digit_count - dot_pos;+ }+ + /* Use the extra digits for rounding if the base is a power of+ two. Otherwise they are just truncated. */+ if (radix_bits != 0 && extra_digits != 0) {+ tmp0->tab[0] |= 1;+ }+ + /* parse the exponent, if any */+ expn = 0;+ expn_overflow = FALSE;+ is_bin_exp = FALSE;+ if (!(flags & JS_ATOD_INT_ONLY) &&+ ((radix == 10 && (*p == 'e' || *p == 'E')) ||+ (radix != 10 && (*p == '@' ||+ (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) &&+ p > p_start) {+ BOOL exp_is_neg;+ int c;+ is_bin_exp = (*p == 'p' || *p == 'P');+ p++;+ exp_is_neg = 0;+ if (*p == '+') {+ p++;+ } else if (*p == '-') {+ exp_is_neg = 1;+ p++;+ }+ c = to_digit(*p);+ if (c >= 10)+ goto fail; /* XXX: could stop before the exponent part */+ expn = c;+ p++;+ for(;;) {+ if (*p == sep && to_digit(p[1]) < 10)+ p++;+ c = to_digit(*p);+ if (c >= 10)+ break;+ if (!expn_overflow) {+ if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) {+ expn_overflow = TRUE;+ } else {+ expn = expn * 10 + c;+ }+ }+ p++;+ }+ if (exp_is_neg)+ expn = -expn;+ /* if zero result, the exponent can be arbitrarily large */+ if (!is_zero && expn_overflow) {+ if (exp_is_neg)+ a = 0;+ else+ a = (uint64_t)0x7ff << 52; /* infinity */+ goto done;+ }+ }++ if (p == p_start)+ goto fail;++ if (is_zero) {+ a = 0;+ } else {+ int expn1;+ if (radix_bits != 0) {+ if (!is_bin_exp)+ expn *= radix_bits;+ expn -= expn_offset * radix_bits;+ expn1 = expn + digit_count * radix_bits;+ if (expn1 >= 1024 + radix_bits)+ goto overflow;+ else if (expn1 <= -1075)+ goto underflow;+ m = round_to_d(&e, tmp0, -expn, JS_RNDN);+ } else {+ expn -= expn_offset;+ expn1 = expn + digit_count;+ if (expn1 >= max_exponent[radix - 2] + 1)+ goto overflow;+ else if (expn1 <= min_exponent[radix - 2])+ goto underflow;+ m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN);+ }+ if (m == 0) {+ underflow:+ a = 0;+ } else if (e > 1024) {+ overflow:+ /* overflow */+ a = (uint64_t)0x7ff << 52;+ } else if (e < -1073) {+ /* underflow */+ /* XXX: check rounding */+ a = 0;+ } else if (e < -1021) {+ /* subnormal */+ a = m >> (-e - 1021);+ } else {+ a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1));+ }+ }+ done:+ a |= (uint64_t)is_neg << 63;+ dval = uint64_as_float64(a);+ done1:+ if (pnext)+ *pnext = p;+ dtoa_free(tmp0);+ return dval;+ fail:+ dval = NAN;+ goto done1;+}
+ mquickjs/dtoa.h view
@@ -0,0 +1,83 @@+/*+ * Tiny float64 printing and parsing library+ *+ * Copyright (c) 2024-2025 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.+ */++//#define JS_DTOA_DUMP_STATS++/* maximum number of digits for fixed and frac formats */+#define JS_DTOA_MAX_DIGITS 101++/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */+/* use as many digits as necessary */+#define JS_DTOA_FORMAT_FREE (0 << 0)+/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */+#define JS_DTOA_FORMAT_FIXED (1 << 0)+/* force fractional format: [-]dd.dd with n_digits fractional digits.+ 0 <= n_digits <= JS_DTOA_MAX_DIGITS */+#define JS_DTOA_FORMAT_FRAC (2 << 0)+#define JS_DTOA_FORMAT_MASK (3 << 0)++/* select exponential notation either in fixed or free format */+#define JS_DTOA_EXP_AUTO (0 << 2)+#define JS_DTOA_EXP_ENABLED (1 << 2)+#define JS_DTOA_EXP_DISABLED (2 << 2)+#define JS_DTOA_EXP_MASK (3 << 2)++#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */++/* only accepts integers (no dot, no exponent) */+#define JS_ATOD_INT_ONLY (1 << 0)+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */+#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1)+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */+#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2)+/* accept _ between digits as a digit separator */+#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3)++typedef struct {+ uint64_t mem[37];+} JSDTOATempMem;++typedef struct {+ uint64_t mem[27];+} JSATODTempMem;++/* return a maximum bound of the string length */+int js_dtoa_max_len(double d, int radix, int n_digits, int flags);+/* return the string length */+int js_dtoa(char *buf, double d, int radix, int n_digits, int flags,+ JSDTOATempMem *tmp_mem);+double js_atod(const char *str, const char **pnext, int radix, int flags,+ JSATODTempMem *tmp_mem);++#ifdef JS_DTOA_DUMP_STATS+void js_dtoa_dump_stats(void);+#endif++/* additional exported functions */+size_t u32toa(char *buf, uint32_t n);+size_t i32toa(char *buf, int32_t n);+size_t u64toa(char *buf, uint64_t n);+size_t i64toa(char *buf, int64_t n);+size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix);+size_t i64toa_radix(char *buf, int64_t n, unsigned int radix);
+ mquickjs/libm.c view
@@ -0,0 +1,2260 @@+/*+ * Tiny Math Library+ *+ * Copyright (c) 2024 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.+ */+/*+ * ====================================================+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.+ *+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */+#include <stdio.h>+#include <inttypes.h>+#include <math.h>+#define NDEBUG+#include <assert.h>++#include "cutils.h"+#include "libm.h"++/* define to enable softfloat support */+//#define USE_SOFTFLOAT+/* use less code for tan() but currently less precise */+#define USE_TAN_SHORTCUT ++/* + TODO:+ - smaller scalbn implementation ?+ - add all ES6 math functions+*/+/*+ tc32: + - base: size libm+libgcc: 21368+ - size libm+libgcc: 11832++ x86:+ - size libm softfp: 18510+ - size libm hardfp: 10051++ TODO:+ - unify i32 bit and i64 bit conversions+ - unify comparisons operations+*/++typedef enum {+ RM_RNE, /* Round to Nearest, ties to Even */+ RM_RTZ, /* Round towards Zero */+ RM_RDN, /* Round Down (must be even) */+ RM_RUP, /* Round Up (must be odd) */+ RM_RMM, /* Round to Nearest, ties to Max Magnitude */+ RM_RMMUP, /* only for rint_sf64(): round to nearest, ties to +inf (must be odd) */+} RoundingModeEnum;++#define FFLAG_INVALID_OP (1 << 4)+#define FFLAG_DIVIDE_ZERO (1 << 3)+#define FFLAG_OVERFLOW (1 << 2)+#define FFLAG_UNDERFLOW (1 << 1)+#define FFLAG_INEXACT (1 << 0)++typedef enum {+ FMINMAX_PROP, /* min(1, qNaN/sNaN) -> qNaN */+ FMINMAX_IEEE754_2008, /* min(1, qNaN) -> 1, min(1, sNaN) -> qNaN */+ FMINMAX_IEEE754_201X, /* min(1, qNaN/sNaN) -> 1 */+} SoftFPMinMaxTypeEnum;++typedef uint32_t sfloat32;+typedef uint64_t sfloat64;++#define F_STATIC static __maybe_unused+#define F_USE_FFLAGS 0++#define F_SIZE 32+#define F_NORMALIZE_ONLY+#include "softfp_template.h"++#define F_SIZE 64+#include "softfp_template.h"++#ifdef USE_SOFTFLOAT++/* wrappers */+double __adddf3(double a, double b)+{+ return uint64_as_float64(add_sf64(float64_as_uint64(a),+ float64_as_uint64(b), RM_RNE));+}++double __subdf3(double a, double b)+{+ return uint64_as_float64(sub_sf64(float64_as_uint64(a),+ float64_as_uint64(b), RM_RNE));+}++double __muldf3(double a, double b)+{+ return uint64_as_float64(mul_sf64(float64_as_uint64(a),+ float64_as_uint64(b), RM_RNE));+}++double __divdf3(double a, double b)+{+ return uint64_as_float64(div_sf64(float64_as_uint64(a),+ float64_as_uint64(b), RM_RNE));+}++/* comparisons */++int __eqdf2(double a, double b)+{+ int ret = cmp_sf64(float64_as_uint64(a),+ float64_as_uint64(b));+ return ret;+}++/* NaN: return 0 */+int __nedf2(double a, double b)+{+ int ret = cmp_sf64(float64_as_uint64(a),+ float64_as_uint64(b));+ if (unlikely(ret == 2))+ return 0;+ else+ return ret;+}++int __ledf2(double a, double b)+{+ int ret = cmp_sf64(float64_as_uint64(a),+ float64_as_uint64(b));+ return ret;+}++int __ltdf2(double a, double b)+{+ int ret = cmp_sf64(float64_as_uint64(a),+ float64_as_uint64(b));+ return ret;+}++int __gedf2(double a, double b)+{+ int ret = cmp_sf64(float64_as_uint64(a),+ float64_as_uint64(b));+ if (unlikely(ret == 2))+ return -1;+ else+ return ret;+}++int __gtdf2(double a, double b)+{+ int ret = cmp_sf64(float64_as_uint64(a),+ float64_as_uint64(b));+ if (unlikely(ret == 2))+ return -1;+ else+ return ret;+}++int __unorddf2(double a, double b)+{+ return isnan_sf64(float64_as_uint64(a)) ||+ isnan_sf64(float64_as_uint64(b));+}++/* conversions */+double __floatsidf(int32_t a)+{+ return uint64_as_float64(cvt_i32_sf64(a, RM_RNE));+}++double __floatdidf(int64_t a)+{+ return uint64_as_float64(cvt_i64_sf64(a, RM_RNE));+}++double __floatunsidf(unsigned int a)+{+ return uint64_as_float64(cvt_u32_sf64(a, RM_RNE));+}++int32_t __fixdfsi(double a)+{+ return cvt_sf64_i32(float64_as_uint64(a), RM_RTZ);+}++double __extendsfdf2(float a)+{+ return uint64_as_float64(cvt_sf32_sf64(float_as_uint(a)));+}++float __truncdfsf2(double a)+{+ return uint_as_float(cvt_sf64_sf32(float64_as_uint64(a), RM_RNE));+}++double js_sqrt(double a)+{+ return uint64_as_float64(sqrt_sf64(float64_as_uint64(a), RM_RNE));+}++#if defined(__tc32__)+/* XXX: check */+int __fpclassifyd(double a)+{+ uint64_t u = float64_as_uint64(a);+ uint32_t h = u >> 32;+ uint32_t l = u;++ h &= 0x7fffffff;+ if (h >= 0x7ff00000) {+ if (h == 0x7ff00000 && l == 0)+ return FP_INFINITE;+ else+ return FP_NAN;+ } else if (h < 0x00100000) {+ if (h == 0 && l == 0)+ return FP_ZERO;+ else+ return FP_SUBNORMAL;+ } else {+ return FP_NORMAL;+ }+}+#endif++#endif /* USE_SOFTFLOAT */++int32_t js_lrint(double a)+{+ return cvt_sf64_i32(float64_as_uint64(a), RM_RNE);+}++double js_fmod(double a, double b)+{+ return uint64_as_float64(fmod_sf64(float64_as_uint64(a), float64_as_uint64(b)));+}++/* supported rounding modes: RM_UP, RM_DN, RM_RTZ, RM_RMMUP, RM_RMM */+static double rint_sf64(double a, RoundingModeEnum rm)+{+ uint64_t u = float64_as_uint64(a);+ uint64_t frac_mask, one, m, addend;+ int e;+ unsigned int s;++ e = ((u >> 52) & 0x7ff) - 0x3ff;+ s = u >> 63;+ if (e < 0) {+ m = u & (((uint64_t)1 << 52) - 1);+ if (e == -0x3ff && m == 0) {+ /* zero: nothing to do */+ } else {+ /* abs(a) < 1 */+ s = u >> 63;+ one = (uint64_t)0x3ff << 52;+ u = 0;+ switch(rm) {+ case RM_RUP:+ case RM_RDN:+ if (s ^ (rm & 1))+ u = one;+ break;+ default:+ case RM_RMM:+ case RM_RMMUP:+ if (e == -1 && (m != 0 || (m == 0 && (!s || rm == RM_RMM))))+ u = one;+ break;+ case RM_RTZ:+ break;+ }+ u |= (uint64_t)s << 63;+ }+ } else if (e < 52) {+ one = (uint64_t)1 << (52 - e);+ frac_mask = one - 1;+ addend = 0;+ switch(rm) {+ case RM_RMMUP:+ addend = (one >> 1) - s;+ break;+ default:+ case RM_RMM:+ addend = (one >> 1);+ break;+ case RM_RTZ:+ break;+ case RM_RUP:+ case RM_RDN:+ if (s ^ (rm & 1))+ addend = one - 1;+ break;+ }+ u += addend;+ u &= ~frac_mask; /* truncate to an integer */+ }+ /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */+ return uint64_as_float64(u);+}++double js_floor(double x)+{+ return rint_sf64(x, RM_RDN);+}++double js_ceil(double x)+{+ return rint_sf64(x, RM_RUP);+}++double js_trunc(double x)+{+ return rint_sf64(x, RM_RTZ);+}++double js_round_inf(double x)+{+ return rint_sf64(x, RM_RMMUP);+}++double js_fabs(double x)+{+ uint64_t a = float64_as_uint64(x);+ return uint64_as_float64(a & 0x7fffffffffffffff);+}++/************************************************************/+/* libm */++#define EXTRACT_WORDS(ix0,ix1,d) \+ do { \+ uint64_t __u = float64_as_uint64(d); \+ (ix0) = (uint32_t)(__u >> 32); \+ (ix1) = (uint32_t)__u; \+ } while (0)++static uint32_t get_high_word(double d)+{+ return float64_as_uint64(d) >> 32;+}++static double set_high_word(double d, uint32_t h)+{+ uint64_t u = float64_as_uint64(d);+ u = (u & 0xffffffff) | ((uint64_t)h << 32);+ return uint64_as_float64(u);+}++static uint32_t get_low_word(double d)+{+ return float64_as_uint64(d);+}++/* set the low 32 bits to zero */+static double zero_low(double x)+{+ uint64_t u = float64_as_uint64(x);+ u &= 0xffffffff00000000;+ return uint64_as_float64(u);+}++static double float64_from_u32(uint32_t h, uint32_t l)+{+ return uint64_as_float64(((uint64_t)h << 32) | l);+}++static const double zero = 0.0;+static const double one = 1.0;+static const double half = 5.00000000000000000000e-01;+static const double tiny = 1.0e-300;+static const double huge = 1.0e300;++/* @(#)s_scalbn.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* + * scalbn (double x, int n)+ * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication.+ */++static const double+ two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */+ twom54 = 5.55111512312578270212e-17; /* 0x3C900000, 0x00000000 */++double js_scalbn(double x, int n)+{+ int k,hx,lx;+ EXTRACT_WORDS(hx, lx, x);+ k = (hx&0x7ff00000)>>20; /* extract exponent */+ if (k==0) { /* 0 or subnormal x */+ if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */+ x *= two54; + hx = get_high_word(x);+ k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/+ }+ if (k==0x7ff) return x+x; /* NaN or Inf */+ k = k+n; + if (k > 0x7fe) return huge*copysign(huge,x); /* overflow */+ if (k > 0) /* normal result */+ {x = set_high_word(x, (hx&0x800fffff)|(k<<20)); return x;}+ if (k <= -54) {+ if (n > 50000) /* in case integer overflow in n+k */+ return huge*copysign(huge,x); /*overflow*/+ else+ return tiny*copysign(tiny,x); /*underflow*/+ }+ k += 54; /* subnormal result */+ x = set_high_word(x, (hx&0x800fffff)|(k<<20));+ return x*twom54;+}++#ifndef USE_SOFTFLOAT+/* @(#)e_sqrt.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* __ieee754_sqrt(x)+ * Return correctly rounded sqrt.+ * ------------------------------------------+ * | Use the hardware sqrt if you have one |+ * ------------------------------------------+ * Method: + * Bit by bit method using integer arithmetic. (Slow, but portable) + * 1. Normalization+ * Scale x to y in [1,4) with even powers of 2: + * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then+ * sqrt(x) = 2^k * sqrt(y)+ * 2. Bit by bit computation+ * Let q = sqrt(y) truncated to i bit after binary point (q = 1),+ * i 0+ * i+1 2+ * s = 2*q , and y = 2 * ( y - q ). (1)+ * i i i i+ * + * To compute q from q , one checks whether + * i+1 i + *+ * -(i+1) 2+ * (q + 2 ) <= y. (2)+ * i+ * -(i+1)+ * If (2) is false, then q = q ; otherwise q = q + 2 .+ * i+1 i i+1 i+ *+ * With some algebraic manipulation, it is not difficult to see+ * that (2) is equivalent to + * -(i+1)+ * s + 2 <= y (3)+ * i i+ *+ * The advantage of (3) is that s and y can be computed by + * i i+ * the following recurrence formula:+ * if (3) is false+ *+ * s = s , y = y ; (4)+ * i+1 i i+1 i+ *+ * otherwise,+ * -i -(i+1)+ * s = s + 2 , y = y - s - 2 (5)+ * i+1 i i+1 i i+ * + * One may easily use induction to prove (4) and (5). + * Note. Since the left hand side of (3) contain only i+2 bits,+ * it does not necessary to do a full (53-bit) comparison + * in (3).+ * 3. Final rounding+ * After generating the 53 bits result, we compute one more bit.+ * Together with the remainder, we can decide whether the+ * result is exact, bigger than 1/2ulp, or less than 1/2ulp+ * (it will never equal to 1/2ulp).+ * The rounding mode can be detected by checking whether+ * huge + tiny is equal to huge, and whether huge - tiny is+ * equal to huge for some floating point number "huge" and "tiny".+ * + * Special cases:+ * sqrt(+-0) = +-0 ... exact+ * sqrt(inf) = inf+ * sqrt(-ve) = NaN ... with invalid signal+ * sqrt(NaN) = NaN ... with invalid signal for signaling NaN+ *+ * Other methods : see the appended file at the end of the program below.+ *---------------+ */++#if defined(__aarch64__) || defined(__x86_64__) || defined(__i386__)+/* hardware sqrt is available */+double js_sqrt(double x)+{+ return sqrt(x);+}+#else+double js_sqrt(double x)+{+ double z;+ int sign = (int)0x80000000; + unsigned r,t1,s1,ix1,q1;+ int ix0,s0,q,m,t,i;++ EXTRACT_WORDS(ix0, ix1, x);++ /* take care of Inf and NaN */+ if((ix0&0x7ff00000)==0x7ff00000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf+ sqrt(-inf)=sNaN */+ } + /* take care of zero */+ if(ix0<=0) {+ if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */+ else if(ix0<0)+ return (x-x)/(x-x); /* sqrt(-ve) = sNaN */+ }+ /* normalize x */+ m = (ix0>>20);+ if(m==0) { /* subnormal x */+ while(ix0==0) {+ m -= 21;+ ix0 |= (ix1>>11); ix1 <<= 21;+ }+ for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1;+ m -= i-1;+ ix0 |= (ix1>>(32-i));+ ix1 <<= i;+ }+ m -= 1023; /* unbias exponent */+ ix0 = (ix0&0x000fffff)|0x00100000;+ if(m&1){ /* odd m, double x to make it even */+ ix0 += ix0 + ((ix1&sign)>>31);+ ix1 += ix1;+ }+ m >>= 1; /* m = [m/2] */++ /* generate sqrt(x) bit by bit */+ ix0 += ix0 + ((ix1&sign)>>31);+ ix1 += ix1;+ q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */+ r = 0x00200000; /* r = moving bit from right to left */++ while(r!=0) {+ t = s0+r; + if(t<=ix0) { + s0 = t+r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1&sign)>>31);+ ix1 += ix1;+ r>>=1;+ }++ r = sign;+ while(r!=0) {+ t1 = s1+r; + t = s0;+ if((t<ix0)||((t==ix0)&&(t1<=ix1))) { + s1 = t1+r;+ if(((t1&sign)==sign)&&(s1&sign)==0) s0 += 1;+ ix0 -= t;+ if (ix1 < t1) ix0 -= 1;+ ix1 -= t1;+ q1 += r;+ }+ ix0 += ix0 + ((ix1&sign)>>31);+ ix1 += ix1;+ r>>=1;+ }++ /* use floating add to find out rounding direction */+ if((ix0|ix1)!=0) {+ z = one-tiny; /* trigger inexact flag */+ if (z>=one) {+ z = one+tiny;+ if (q1==(unsigned)0xffffffff) { q1=0; q += 1;}+ else if (z>one) {+ if (q1==(unsigned)0xfffffffe) q+=1;+ q1+=2; + } else+ q1 += (q1&1);+ }+ }+ ix0 = (q>>1)+0x3fe00000;+ ix1 = q1>>1;+ if ((q&1)==1) ix1 |= sign;+ ix0 += (m <<20);+ return float64_from_u32(ix0, ix1);+}+#endif /* !hardware sqrt */+#endif /* USE_SOFTFLOAT */++/* to have smaller code */+/* n >= 1 */+/* return sum(x^i*coefs[i] with i = 0 ... n - 1 and n >= 1 using+ Horner algorithm. */+static double eval_poly(double x, const double *coefs, int n)+{+ double r;+ int i;+ r = coefs[n - 1];+ for(i = n - 2; i >= 0; i--) {+ r = r * x + coefs[i];+ }+ return r;+}++/* @(#)k_sin.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* __kernel_sin( x, y, iy)+ * kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854+ * Input x is assumed to be bounded by ~pi/4 in magnitude.+ * Input y is the tail of x.+ * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). + *+ * Algorithm+ * 1. Since sin(-x) = -sin(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0.+ * 3. sin(x) is approximated by a polynomial of degree 13 on+ * [0,pi/4]+ * 3 13+ * sin(x) ~ x + S1*x + ... + S6*x+ * where+ * + * |sin(x) 2 4 6 8 10 12 | -58+ * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2+ * | x | + * + * 4. sin(x+y) = sin(x) + sin'(x')*y+ * ~ sin(x) + (1-x*x/2)*y+ * For better accuracy, let + * 3 2 2 2 2+ * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6))))+ * then 3 2+ * sin(x) = x + (S1*x + (x *(r-y/2)+y))+ */++static const double +S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */+static const double S_tab[] = {+ /* S2 */ 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */+ /* S3 */ -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */+ /* S4 */ 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */+ /* S5 */ -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */+ /* S6 */ 1.58969099521155010221e-10, /* 0x3DE5D93A, 0x5ACFD57C */+};++/* iy=0 if y is zero */+static double __kernel_sin(double x, double y, int iy)+{+ double z,r,v;+ int ix;+ ix = get_high_word(x)&0x7fffffff; /* high word of x */+ if(ix<0x3e400000) /* |x| < 2**-27 */+ {if((int)x==0) return x;} /* generate inexact */+ z = x*x;+ v = z*x;+ r = eval_poly(z, S_tab, 5);+ if(iy==0) return x+v*(S1+z*r);+ else return x-((z*(half*y-v*r)-y)-v*S1);+}+++/* @(#)k_cos.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/*+ * __kernel_cos( x, y )+ * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164+ * Input x is assumed to be bounded by ~pi/4 in magnitude.+ * Input y is the tail of x. + *+ * Algorithm+ * 1. Since cos(-x) = cos(x), we need only to consider positive x.+ * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0.+ * 3. cos(x) is approximated by a polynomial of degree 14 on+ * [0,pi/4]+ * 4 14+ * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x+ * where the remez error is+ * + * | 2 4 6 8 10 12 14 | -58+ * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2+ * | | + * + * 4 6 8 10 12 14 + * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then+ * cos(x) = 1 - x*x/2 + r+ * since cos(x+y) ~ cos(x) - sin(x)*y + * ~ cos(x) - x*y,+ * a correction term is necessary in cos(x) and hence+ * cos(x+y) = 1 - (x*x/2 - (r - x*y))+ * For better accuracy when x > 0.3, let qx = |x|/4 with+ * the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125.+ * Then+ * cos(x+y) = (1-qx) - ((x*x/2-qx) - (r-x*y)).+ * Note that 1-qx and (x*x/2-qx) is EXACT here, and the+ * magnitude of the latter is at least a quarter of x*x/2,+ * thus, reducing the rounding error in the subtraction.+ */++static const double C_tab[] = {+ /* C1 */ 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */+ /* C2 */ -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */+ /* C3 */ 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */+ /* C4 */ -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */+ /* C5 */ 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */+ /* C6 */ -1.13596475577881948265e-11, /* 0xBDA8FAE9, 0xBE8838D4 */+};++static double __kernel_cos(double x, double y)+{+ double a,hz,z,r,qx;+ int ix;+ ix = get_high_word(x)&0x7fffffff; /* ix = |x|'s high word*/+ if(ix<0x3e400000) { /* if x < 2**27 */+ if(((int)x)==0) return one; /* generate inexact */+ }+ z = x*x;+ r = z * eval_poly(z, C_tab, 6);+ if(ix < 0x3FD33333) /* if |x| < 0.3 */ + return one - (0.5*z - (z*r - x*y));+ else {+ if(ix > 0x3fe90000) { /* x > 0.78125 */+ qx = 0.28125;+ } else {+ qx = float64_from_u32(ix-0x00200000, 0); /* x/4 */+ }+ hz = 0.5*z-qx;+ a = one-qx;+ return a - (hz - (z*r-x*y));+ }+}++/* rem_pio2 */++#define T_LEN 19++/* T[i] = floor(2^(64*(T_LEN - i))/2pi) mod 2^64 */+static const uint64_t T[T_LEN] = {+ 0x1580cc11bf1edaea,+ 0x9afed7ec47e35742,+ 0xcf41ce7de294a4ba,+ 0x5d49eeb1faf97c5e,+ 0xd3d18fd9a797fa8b,+ 0xdb4d9fb3c9f2c26d,+ 0xfbcbc462d6829b47,+ 0xc7fe25fff7816603,+ 0x272117e2ef7e4a0e,+ 0x4e64758e60d4ce7d,+ 0x3a671c09ad17df90,+ 0xba208d7d4baed121,+ 0x3f877ac72c4a69cf,+ 0x01924bba82746487,+ 0x6dc91b8e909374b8,+ 0x7f9458eaf7aef158,+ 0x36d8a5664f10e410,+ 0x7f09d5f47d4d3770,+ 0x28be60db9391054a, /* high part */+};++/* PIO2[i] = floor(2^(64*(2 - i))*PI/4) mod 2^64 */+static const uint64_t PIO4[2] = {+ 0xc4c6628b80dc1cd1,+ 0xc90fdaa22168c234,+};++static uint64_t get_u64_at_bit(const uint64_t *tab, uint32_t tab_len,+ uint32_t pos)+{+ uint64_t v;+ uint32_t p = pos / 64;+ int shift = pos % 64;+ v = tab[p] >> shift;+ if (shift != 0 && (p + 1) < tab_len)+ v |= tab[p + 1] << (64 - shift);+ return v;+}++/* return n = round(x/(pi/2)) (only low 2 bits are valid) and+ (y[0], y[1]) = x - (pi/2) * n. + 'x' must be finite and such as abs(x) >= PI/4.+ The initial algorithm comes from the CORE-MATH project.+*/+static int rem_pio2_large(double x, double *y)+{+ uint64_t m;+ int e, sgn, n, rnd, j, i, y_sgn;+ uint64_t c[2], d[3];+ uint64_t r0, r1;+ uint32_t carry, carry1;++ m = float64_as_uint64(x);+ sgn = m >> 63;+ e = (m >> 52) & 0x7ff;+ /* 1022 <= e <= 2047 */+ m = (m & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);++ /* multiply m by T[j:j+192] */+ j = T_LEN * 64 - (e - 1075) - 192;+ /* 53 <= j <= 1077 */+ // printf("m=0x%016" PRIx64 " e=%d j=%d\n", m, e, j);+ for(i = 0; i < 3; i++) {+ d[i] = get_u64_at_bit(T, T_LEN, j + i * 64);+ }+ r1 = mul_u64(&r0, m, d[0]);+ c[0] = r1;+ r1 = mul_u64(&r0, m, d[1]);+ c[0] += r0;+ carry = c[0] < r0;+ c[1] = r1 + carry;+ mul_u64(&r0, m, d[2]);+ c[1] += r0;++ // printf("c0=%016" PRIx64 " %016" PRIx64 "\n", c[1], c[0]);++ /* n = round(c[1]/2^62) */+ n = c[1] >> 62;+ rnd = (c[1] >> 61) & 1;+ n += rnd;+ /* c = c * 4 - n */+ c[1] = (c[1] << 2) | (c[0] >> 62);+ c[0] = (c[0] << 2);+ y_sgn = sgn;+ if (rnd) {+ /* 'y' sign change */+ y_sgn ^= 1;+ c[0] = ~c[0];+ c[1] = ~c[1];+ if (++c[0] == 0)+ c[1]++;+ }+ // printf("c1=%016" PRIx64 " %016" PRIx64 " n=%d sgn=%d\n", c[1], c[0], n, sgn);++ /* c = c * (PI/2) (high 128 bits of the product) */+ r1 = mul_u64(&r0, c[0], PIO4[1]);+ d[0] = r0;+ d[1] = r1;++ r1 = mul_u64(&r0, c[1], PIO4[0]);+ d[0] += r0;+ carry = d[0] < r0;+ d[1] += r1;+ carry1 = d[1] < r1;+ d[1] += carry;+ carry1 |= (d[1] < carry);+ d[2] = carry1;++ r1 = mul_u64(&r0, c[1], PIO4[1]);+ d[1] += r0;+ carry = d[1] < r0;+ d[2] += r1 + carry;+ + /* convert d to two float64 */+ // printf("d=%016" PRIx64 " %016" PRIx64 "\n", d[2], d[1]);+ if (d[2] == 0) {+ /* should never happen (see ARGUMENT REDUCTION FOR HUGE+ ARGUMENTS: Good to the Last Bit, K. C. Ng and the members+ of the FP group of SunPro */+ y[0] = y[1] = 0;+ } else {+ uint64_t m0, m1;+ int e1;+ + e = clz64(d[2]);+ d[2] = (d[2] << e) | (d[1] >> (64 - e));+ d[1] = (d[1] << e);+ // printf("d=%016" PRIx64 " %016" PRIx64 " e=%d\n", d[2], d[1], e);+ m0 = (d[2] >> 11) & (((uint64_t)1 << 52) - 1);+ m1 = ((d[2] & 0x7ff) << 42) | (d[1] >> (64 - 42));+ y[0] = uint64_as_float64(((uint64_t)y_sgn << 63) |+ ((uint64_t)(1023 - e) << 52) |+ m0);+ if (m1 == 0) {+ y[1] = 0;+ } else {+ e1 = clz64(m1) - 11;+ m1 = (m1 << e1) & (((uint64_t)1 << 52) - 1);+ y[1] = uint64_as_float64(((uint64_t)y_sgn << 63) |+ ((uint64_t)(1023 - e - 53 - e1) << 52) |+ m1);+ }+ }+ if (sgn)+ n = -n;+ return n;+}++#ifdef USE_SOFTFLOAT+/* when using softfloat, the FP reduction should be not much faster+ than the generic one */+int js_rem_pio2(double x, double *y)+{+ int ix,hx;++ hx = get_high_word(x); /* high word of x */+ ix = hx&0x7fffffff;+ if(ix<=0x3fe921fb) {+ /* |x| ~<= pi/4 , no need for reduction */+ y[0] = x;+ y[1] = 0;+ return 0;+ }+ /* + * all other (large) arguments+ */+ if(ix>=0x7ff00000) { /* x is inf or NaN */+ y[0]=y[1]=x-x;+ return 0;+ }++ return rem_pio2_large(x, y);+}+#else+/*+ * invpio2: 53 bits of 2/pi+ * pio2_1: first 33 bit of pi/2+ * pio2_1t: pi/2 - pio2_1+ * pio2_2: second 33 bit of pi/2+ * pio2_2t: pi/2 - (pio2_1+pio2_2)+ * pio2_3: third 33 bit of pi/2+ * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3)+ */++static const double +invpio2 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */+static const double pio2_tab[3] = {+ /* pio2_1 */ 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */+ /* pio2_2 */ 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */+ /* pio2_3 */ 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */+};+static const double pio2_t_tab[3] = {+ /* pio2_1t */ 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */+ /* pio2_2t */ 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */+ /* pio2_3t */ 8.47842766036889956997e-32, /* 0x397B839A, 0x252049C1 */+};+static uint8_t rem_pio2_emax[2] = { 16, 49 };++int js_rem_pio2(double x, double *y)+{+ double w,t,r,fn;+ int i,j,n,ix,hx,it;++ hx = get_high_word(x); /* high word of x */+ ix = hx&0x7fffffff;+ if(ix<=0x3fe921fb) {+ /* |x| ~<= pi/4 , no need for reduction */+ y[0] = x;+ y[1] = 0;+ return 0;+ }+ if(ix<=0x413921fb) { /* |x| ~<= 2^19*(pi/2), medium size */+ t = fabs(x);+ if (ix<0x4002d97c) {+ /* |x| < 3pi/4, special case with n=+-1 */+ n = 1;+ fn = 1;+ } else {+ n = (int) (t*invpio2+half);+ fn = (double)n;+ }++ it = 0;+ for(;;) {+ /* 1st round good to 85 bit */+ /* 2nd iteration needed, good to 118 */+ /* 3rd iteration need, 151 bits acc */+ r = t-fn*pio2_tab[it];+ w = fn*pio2_t_tab[it]; + y[0] = r-w;+ j = ix>>20;+ i = j-(((get_high_word(y[0]))>>20)&0x7ff);+ if (it == 2 || i <= rem_pio2_emax[it])+ break;+ t = r;+ it++;+ }+ y[1] = (r-y[0])-w;+ if (hx<0) {+ y[0] = -y[0];+ y[1] = -y[1];+ return -n;+ } else {+ return n;+ }+ }+ /* + * all other (large) arguments+ */+ if(ix>=0x7ff00000) { /* x is inf or NaN */+ y[0]=y[1]=x-x;+ return 0;+ }++ return rem_pio2_large(x, y);+}+#endif /* !USE_SOFTFLOAT */++/* @(#)s_sin.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* sin(x)+ * Return sine function of x.+ *+ * kernel function:+ * __kernel_sin ... sine function on [-pi/4,pi/4]+ * __kernel_cos ... cose function on [-pi/4,pi/4]+ * __ieee754_rem_pio2 ... argument reduction routine+ *+ * Method.+ * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4.+ * We have+ *+ * n sin(x) cos(x) tan(x)+ * ----------------------------------------------------------+ * 0 S C T+ * 1 C -S -1/T+ * 2 -S -C T+ * 3 -C S -1/T+ * ----------------------------------------------------------+ *+ * Special cases:+ * Let trig be any of sin, cos, or tan.+ * trig(+-INF) is NaN, with signals;+ * trig(NaN) is that NaN;+ *+ * Accuracy:+ * TRIG(x) returns trig(x) nearly rounded + */++/* flag = 0: sin()+ flag = 1: cos()+ flag = 3: tan()+*/+static double js_sin_cos(double x, int flag)+{+ double y[2], z, s, c;+ int ix;+ uint32_t n;+ + /* High word of x. */+ ix = get_high_word(x);++ /* sin(Inf or NaN) is NaN */+ if (ix>=0x7ff00000)+ return x-x;+ + n = js_rem_pio2(x,y);+ s = c = 0; /* avoid warning */+ if (flag == 3 || (n & 1) == flag) {+ s = __kernel_sin(y[0],y[1],1);+ if (flag != 3)+ goto done;+ }+ if (flag == 3 || (n & 1) != flag) {+ c = __kernel_cos(y[0],y[1]);+ if (flag != 3) {+ s = c;+ goto done;+ }+ }+ if (n & 1)+ z = -c / s;+ else+ z = s / c;+ return z;+done:+ if ((n + flag) & 2)+ s = -s;+ return s;+}++double js_sin(double x)+{+ return js_sin_cos(x, 0);+}++double js_cos(double x)+{+ return js_sin_cos(x, 1);+}++#ifdef USE_TAN_SHORTCUT+double js_tan(double x)+{+ return js_sin_cos(x, 3);+}+#endif++#ifndef USE_TAN_SHORTCUT+/*+ * ====================================================+ * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.+ *+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice+ * is preserved.+ * ====================================================+ */++/* INDENT OFF */+/* __kernel_tan( x, y, k )+ * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854+ * Input x is assumed to be bounded by ~pi/4 in magnitude.+ * Input y is the tail of x.+ * Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned.+ *+ * Algorithm+ * 1. Since tan(-x) = -tan(x), we need only to consider positive x.+ * 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0.+ * 3. tan(x) is approximated by a odd polynomial of degree 27 on+ * [0,0.67434]+ * 3 27+ * tan(x) ~ x + T1*x + ... + T13*x+ * where+ *+ * |tan(x) 2 4 26 | -59.2+ * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2+ * | x |+ *+ * Note: tan(x+y) = tan(x) + tan'(x)*y+ * ~ tan(x) + (1+x*x)*y+ * Therefore, for better accuracy in computing tan(x+y), let+ * 3 2 2 2 2+ * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13))))+ * then+ * 3 2+ * tan(x+y) = x + (T1*x + (x *(r+y)+y))+ *+ * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then+ * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y))+ * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y)))+ */++static const double T0 = 3.33333333333334091986e-01; /* 3FD55555, 55555563 */+static const double T_even[] = {+ 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */+ 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */+ 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */+ 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */+ 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */+ 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */+};+static const double T_odd[] = {+ 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */+ 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */+ 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */+ 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */+ 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */+ -1.85586374855275456654e-05, /* BEF375CB, DB605373 */+};+static const double pio4 = 7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */+ pio4lo = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */++/* compute -1 / (x+y) carefully */+static double minus_inv(double x, double y)+{+ double a, t, z, v, s, w;+ + w = x + y;+ z = zero_low(w);+ v = y - (z - x);+ a = -one / w;+ t = zero_low(a);+ s = one + t * z;+ return t + a * (s + t * v);+}++static double+__kernel_tan(double x, double y, int iy) {+ double z, r, v, w, s;+ int ix, hx;++ hx = get_high_word(x); /* high word of x */+ ix = hx & 0x7fffffff; /* high word of |x| */+ if (ix < 0x3e300000) { /* x < 2**-28 */+ if ((int) x == 0) { /* generate inexact */+ if (((ix | get_low_word(x)) | (iy + 1)) == 0)+ return one / fabs(x);+ else {+ if (iy == 1)+ return x;+ else+ return minus_inv(x, y);+ }+ }+ }+ if (ix >= 0x3FE59428) { /* |x| >= 0.6744 */+ if (hx < 0) {+ x = -x;+ y = -y;+ }+ z = pio4 - x;+ w = pio4lo - y;+ x = z + w;+ y = 0.0;+ }+ z = x * x;+ w = z * z;+ /*+ * Break x^5*(T[1]+x^2*T[2]+...) into+ * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) ++ * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))+ */+ r = eval_poly(w, T_odd, 6);+ v = z * eval_poly(w, T_even, 6);+ s = z * x;+ r = y + z * (s * (r + v) + y);+ r += T0 * s;+ w = x + r;+ if (ix >= 0x3FE59428) {+ v = (double) iy;+ return (double) (1 - ((hx >> 30) & 2)) *+ (v - 2.0 * (x - (w * w / (w + v) - r)));+ }+ if (iy == 1) {+ return w;+ } else {+ /*+ * if allow error up to 2 ulp, simply return+ * -1.0 / (x+r) here+ */+ return minus_inv(x, r);+ }+}++/* @(#)s_tan.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* tan(x)+ * Return tangent function of x.+ *+ * kernel function:+ * __kernel_tan ... tangent function on [-pi/4,pi/4]+ * __ieee754_rem_pio2 ... argument reduction routine+ *+ * Method.+ * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4.+ * We have+ *+ * n sin(x) cos(x) tan(x)+ * ----------------------------------------------------------+ * 0 S C T+ * 1 C -S -1/T+ * 2 -S -C T+ * 3 -C S -1/T+ * ----------------------------------------------------------+ *+ * Special cases:+ * Let trig be any of sin, cos, or tan.+ * trig(+-INF) is NaN, with signals;+ * trig(NaN) is that NaN;+ *+ * Accuracy:+ * TRIG(x) returns trig(x) nearly rounded + */++double js_tan(double x) +{+ double y[2],z=0.0;+ int n, ix;++ /* High word of x. */+ ix = get_high_word(x);++ /* |x| ~< pi/4 */+ ix &= 0x7fffffff;+ if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1);++ /* tan(Inf or NaN) is NaN */+ else if (ix>=0x7ff00000) return x-x; /* NaN */++ /* argument reduction needed */+ else {+ n = js_rem_pio2(x,y);+ return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even+ -1 -- n odd */+ }+}+#endif++/* @(#)e_asin.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* __ieee754_asin(x)+ * Method : + * Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ...+ * we approximate asin(x) on [0,0.5] by+ * asin(x) = x + x*x^2*R(x^2)+ * where+ * R(x^2) is a rational approximation of (asin(x)-x)/x^3 + * and its remez error is bounded by+ * |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75)+ *+ * For x in [0.5,1]+ * asin(x) = pi/2-2*asin(sqrt((1-x)/2))+ * Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2;+ * then for x>0.98+ * asin(x) = pi/2 - 2*(s+s*z*R(z))+ * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo)+ * For x<=0.98, let pio4_hi = pio2_hi/2, then+ * f = hi part of s;+ * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z)+ * and+ * asin(x) = pi/2 - 2*(s+s*z*R(z))+ * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo)+ * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c))+ *+ * Special cases:+ * if x is NaN, return x itself;+ * if |x|>1, return NaN with invalid signal.+ *+ */+++static const double +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */+ pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */+ pio4_hi = 7.85398163397448278999e-01; /* 0x3FE921FB, 0x54442D18 */+/* coefficient for R(x^2) */+static const double pS[] = { + /* pS0 */ 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */+ /* pS1 */-3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */+ /* pS2 */ 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */+ /* pS3 */ -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */+ /* pS4 */ 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */+ /* pS5 */ 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */+};++static const double qS[] = { + /* qS1 */ -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */+ /* qS2 */ 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */+ /* qS3 */ -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */+ /* qS4 */ 7.70381505559019352791e-02, /* 0x3FB3B8C5, 0xB12E9282 */+};++static double R(double t)+{+ double p, q, w;+ p = t * eval_poly(t, pS, 6);+ q = one + t * eval_poly(t, qS, 4);+ w = p/q;+ return w;+}++double js_asin(double x)+{+ double t,w,p,q,c,r,s;+ int hx,ix;+ hx = get_high_word(x);+ ix = hx&0x7fffffff;+ if(ix>= 0x3ff00000) { /* |x|>= 1 */+ if(((ix-0x3ff00000)|get_low_word(x))==0)+ /* asin(1)=+-pi/2 with inexact */+ return x*pio2_hi+x*pio2_lo; + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3fe00000) { /* |x|<0.5 */+ if(ix<0x3e400000) { /* if |x| < 2**-27 */+ if(huge+x>one) return x;/* return x with inexact if x!=0*/+ } else {+ t = x*x;+ w = R(t);+ return x+x*w;+ }+ }+ /* 1> |x|>= 0.5 */+ w = one-fabs(x);+ t = w*0.5;+ r = R(t);+ s = js_sqrt(t);+ if(ix>=0x3FEF3333) { /* if |x| > 0.975 */+ w = r;+ t = pio2_hi-(2.0*(s+s*w)-pio2_lo);+ } else {+ w = zero_low(s);+ c = (t-w*w)/(s+w);+ p = 2.0*s*r-(pio2_lo-2.0*c);+ q = pio4_hi-2.0*w;+ t = pio4_hi-(p-q);+ } + if(hx>0) return t; else return -t; +}+++/* @(#)e_acos.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* __ieee754_acos(x)+ * Method : + * acos(x) = pi/2 - asin(x)+ * acos(-x) = pi/2 + asin(x)+ * For |x|<=0.5+ * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c)+ * For x>0.5+ * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2)))+ * = 2asin(sqrt((1-x)/2)) + * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z)+ * = 2f + (2c + 2s*z*R(z))+ * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term+ * for f so that f+c ~ sqrt(z).+ * For x<-0.5+ * acos(x) = pi - 2asin(sqrt((1-|x|)/2))+ * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z)+ *+ * Special cases:+ * if x is NaN, return x itself;+ * if |x|>1, return NaN with invalid signal.+ *+ * Function needed: sqrt+ */++static const double +pi = 3.14159265358979311600e+00; /* 0x400921FB, 0x54442D18 */++double js_acos(double x)+{+ double z,r,w,s,c,df;+ int hx,ix;+ hx = get_high_word(x);+ ix = hx&0x7fffffff;+ if(ix>=0x3ff00000) { /* |x| >= 1 */+ if(((ix-0x3ff00000)|get_low_word(x))==0) { /* |x|==1 */+ if(hx>0) return 0.0; /* acos(1) = 0 */+ else return pi+2.0*pio2_lo; /* acos(-1)= pi */+ }+ return (x-x)/(x-x); /* acos(|x|>1) is NaN */+ }+ if(ix<0x3fe00000) { /* |x| < 0.5 */+ if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/+ z = x*x;+ r = R(z);+ return pio2_hi - (x - (pio2_lo-x*r));+ } else {+ z = (one-fabs(x))*0.5;+ r = R(z);+ s = js_sqrt(z);+ if (hx<0) { /* x < -0.5 */+ w = r*s-pio2_lo;+ return pi - 2.0*(s+w);+ } else { /* x > 0.5 */+ df = zero_low(s);+ c = (z-df*df)/(s+df);+ w = r*s+c;+ return 2.0*(df+w);+ }+ }+}+++/* @(#)s_atan.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ *+ */++/* atan(x)+ * Method+ * 1. Reduce x to positive by atan(x) = -atan(-x).+ * 2. According to the integer k=4t+0.25 chopped, t=x, the argument+ * is further reduced to one of the following intervals and the+ * arctangent of t is evaluated by the corresponding formula:+ *+ * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)+ * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )+ * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )+ * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )+ * [39/16,INF] atan(x) = atan(INF) + atan( -1/t )+ *+ * Constants:+ * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown.+ */++static const double atanhi[] = {+ 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */+ 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */+ 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */+ 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */+};++static const double atanlo[] = {+ 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */+ 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */+ 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */+ 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */+};++static const double aT_even[] = {+ 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */+ 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */+ 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */+ 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */+ 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */+ 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */+};+static const double aT_odd[] = {+ -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */+ -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */+ -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */+ -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */+ -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */+};++double js_atan(double x)+{+ double w,s1,s2,z;+ int ix,hx,id;++ hx = get_high_word(x);+ ix = hx&0x7fffffff;+ if(ix>=0x44100000) { /* if |x| >= 2^66 */+ if(ix>0x7ff00000||+ (ix==0x7ff00000&&(get_low_word(x)!=0)))+ return x+x; /* NaN */+ if(hx>0) return atanhi[3]+atanlo[3];+ else return -atanhi[3]-atanlo[3];+ } if (ix < 0x3fdc0000) { /* |x| < 0.4375 */+ if (ix < 0x3e200000) { /* |x| < 2^-29 */+ if(huge+x>one) return x; /* raise inexact */+ }+ id = -1;+ } else {+ x = fabs(x);+ if (ix < 0x3ff30000) { /* |x| < 1.1875 */+ if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */+ id = 0; x = (2.0*x-one)/(2.0+x); + } else { /* 11/16<=|x|< 19/16 */+ id = 1; x = (x-one)/(x+one); + }+ } else {+ if (ix < 0x40038000) { /* |x| < 2.4375 */+ id = 2; x = (x-1.5)/(one+1.5*x);+ } else { /* 2.4375 <= |x| < 2^66 */+ id = 3; x = -1.0/x;+ }+ }}+ /* end of argument reduction */+ z = x*x;+ w = z*z;+ /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */+ s1 = z*eval_poly(w, aT_even, 6);+ s2 = w*eval_poly(w, aT_odd, 5);+ if (id<0) return x - x*(s1+s2);+ else {+ z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x);+ return (hx<0)? -z:z;+ }+}++/* @(#)e_atan2.c 1.3 95/01/18 */+/*+ * ====================================================+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.+ *+ * Developed at SunSoft, a Sun Microsystems, Inc. business.+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ *+ */++/* __ieee754_atan2(y,x)+ * Method :+ * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x).+ * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0,+ * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0,+ *+ * Special cases:+ *+ * ATAN2((anything), NaN ) is NaN;+ * ATAN2(NAN , (anything) ) is NaN;+ * ATAN2(+-0, +(anything but NaN)) is +-0 ;+ * ATAN2(+-0, -(anything but NaN)) is +-pi ;+ * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2;+ * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ;+ * ATAN2(+-(anything but INF and NaN), -INF) is +-pi;+ * ATAN2(+-INF,+INF ) is +-pi/4 ;+ * ATAN2(+-INF,-INF ) is +-3pi/4;+ * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2;+ *+ * Constants:+ * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown.+ */++static const double +pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */+pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */+pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */++double js_atan2(double y, double x)+{ + double z;+ int k,m,hx,hy,ix,iy;+ unsigned lx,ly;++ EXTRACT_WORDS(hx, lx, x);+ EXTRACT_WORDS(hy, ly, y);+ ix = hx&0x7fffffff;+ iy = hy&0x7fffffff;+ if(((ix|((lx|-lx)>>31))>0x7ff00000)||+ ((iy|((ly|-ly)>>31))>0x7ff00000)) /* x or y is NaN */+ return x+y;+ if(((hx-0x3ff00000)|lx)==0)+ return js_atan(y); /* x=1.0 */+ m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */++ /* when y = 0 */+ if((iy|ly)==0) {+ z = 0;+ goto done;+ }+ /* when x = 0 */+ if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;+ + /* when x is INF */+ if(ix==0x7ff00000) {+ if(iy==0x7ff00000) {+ z = pi_o_4;+ } else {+ z = 0;+ }+ goto done;+ }+ /* when y is INF */+ if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;++ /* compute y/x */+ k = (iy-ix)>>20;+ if(k > 60) {+ z=pi_o_2+0.5*pi_lo; /* |y/x| > 2**60 */+ } else if(hx<0&&k<-60) {+ z=0.0; /* |y|/x < -2**60 */+ } else {+ z=js_atan(fabs(y/x)); /* safe to do y/x */+ }+ done:+ switch (m) {+ case 0:+ return z ; /* atan(+,+) */+ case 1:+ z = set_high_word(z, get_high_word(z) ^ 0x80000000);+ return z ; /* atan(-,+) */+ case 2:+ return pi-(z-pi_lo);/* atan(+,-) */+ default: /* case 3 */+ return (z-pi_lo)-pi;/* atan(-,-) */+ }+}++/* @(#)e_exp.c 1.6 04/04/22 */+/*+ * ====================================================+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.+ *+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* __ieee754_exp(x)+ * Returns the exponential of x.+ *+ * Method+ * 1. Argument reduction:+ * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658.+ * Given x, find r and integer k such that+ *+ * x = k*ln2 + r, |r| <= 0.5*ln2. + *+ * Here r will be represented as r = hi-lo for better + * accuracy.+ *+ * 2. Approximation of exp(r) by a special rational function on+ * the interval [0,0.34658]:+ * Write+ * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ...+ * We use a special Remes algorithm on [0,0.34658] to generate + * a polynomial of degree 5 to approximate R. The maximum error + * of this polynomial approximation is bounded by 2**-59. In+ * other words,+ * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5+ * (where z=r*r, and the values of P1 to P5 are listed below)+ * and+ * | 5 | -59+ * | 2.0+P1*z+...+P5*z - R(z) | <= 2 + * | |+ * The computation of exp(r) thus becomes+ * 2*r+ * exp(r) = 1 + -------+ * R - r+ * r*R1(r) + * = 1 + r + ----------- (for better accuracy)+ * 2 - R1(r)+ * where+ * 2 4 10+ * R1(r) = r - (P1*r + P2*r + ... + P5*r ).+ * + * 3. Scale back to obtain exp(x):+ * From step 1, we have+ * exp(x) = 2^k * exp(r)+ *+ * Special cases:+ * exp(INF) is INF, exp(NaN) is NaN;+ * exp(-INF) is 0, and+ * for finite argument, only exp(0)=1 is exact.+ *+ * Accuracy:+ * according to an error analysis, the error is always less than+ * 1 ulp (unit in the last place).+ *+ * Misc. info.+ * For IEEE double + * if x > 7.09782712893383973096e+02 then exp(x) overflow+ * if x < -7.45133219101941108420e+02 then exp(x) underflow+ *+ * Constants:+ * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough+ * to produce the hexadecimal values shown.+ */++static const double+two = 2.0,+halF[2] = {0.5,-0.5,},+twom1000= 9.33263618503218878990e-302, /* 2**-1000=0x01700000,0*/+o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */+u_threshold= -7.45133219101941108420e+02, /* 0xc0874910, 0xD52D3051 */+ln2HI[2] ={ 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */+ -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */+ln2LO[2] ={ 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */+ -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */+ invln2 = 1.44269504088896338700e+00; /* 0x3ff71547, 0x652b82fe */+static const double P[] = {+ /* P1 */ 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */+ /* P2 */ -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */+ /* P3 */ 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */+ /* P4 */ -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */+ /* P5 */ 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */+};++/* compute exp(z+w)*2^n */+static double kernel_exp(double z, double w, double lo, double hi, int n)+{+ int j;+ double t, t1, r;+ t = z*z;+ t1 = z - t*eval_poly(t, P, 5);+ r = (z*t1)/(t1-two) - (w+z*w);+ z = one-((lo + r)-hi);+ j = get_high_word(z);+ j += (n<<20);+ if((j>>20)<=0)+ z = js_scalbn(z,n); /* subnormal output */+ else+ z = set_high_word(z, get_high_word(z) + (n<<20));+ return z;+}++double js_exp(double x)+{+ double hi,lo,t;+ int k,xsb;+ unsigned hx;++ hx = get_high_word(x); /* high word of x */+ xsb = (hx>>31)&1; /* sign bit of x */+ hx &= 0x7fffffff; /* high word of |x| */++ /* filter out non-finite argument */+ if(hx >= 0x40862E42) { /* if |x|>=709.78... */+ if(hx>=0x7ff00000) {+ if(((hx&0xfffff)|get_low_word(x))!=0) + return x+x; /* NaN */+ else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */+ }+ if(x > o_threshold) return huge*huge; /* overflow */+ if(x < u_threshold) return twom1000*twom1000; /* underflow */+ }++ /* argument reduction */+ if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */+ hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb;+ } else {+ k = (int)(invln2*x+halF[xsb]);+ t = k;+ hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */+ lo = t*ln2LO[0];+ }+ x = hi - lo;+ } + else if(hx < 0x3e300000) { /* when |x|<2**-28 */+ if(huge+x>one) return one+x;/* trigger inexact */+ k = 0; /* avoid warning */+ }+ else k = 0;++ /* x is now in primary range */+ if (k == 0) {+ lo = 0;+ hi = x;+ }+ return kernel_exp(x, 0, lo, hi, k);+}+++/* @(#)e_pow.c 1.5 04/04/22 SMI */+/*+ * ====================================================+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.+ *+ * Permission to use, copy, modify, and distribute this+ * software is freely granted, provided that this notice + * is preserved.+ * ====================================================+ */++/* __ieee754_pow(x,y) return x**y+ *+ * n+ * Method: Let x = 2 * (1+f)+ * 1. Compute and return log2(x) in two pieces:+ * log2(x) = w1 + w2,+ * where w1 has 53-24 = 29 bit trailing zeros.+ * 2. Perform y*log2(x) = n+y' by simulating multi-precision + * arithmetic, where |y'|<=0.5.+ * 3. Return x**y = 2**n*exp(y'*log2)+ *+ * Special cases:+ * 1. (anything) ** 0 is 1+ * 2. (anything) ** 1 is itself+ * 3. (anything) ** NAN is NAN+ * 4. NAN ** (anything except 0) is NAN+ * 5. +-(|x| > 1) ** +INF is +INF+ * 6. +-(|x| > 1) ** -INF is +0+ * 7. +-(|x| < 1) ** +INF is +0+ * 8. +-(|x| < 1) ** -INF is +INF+ * 9. +-1 ** +-INF is NAN+ * 10. +0 ** (+anything except 0, NAN) is +0+ * 11. -0 ** (+anything except 0, NAN, odd integer) is +0+ * 12. +0 ** (-anything except 0, NAN) is +INF+ * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF+ * 14. -0 ** (odd integer) = -( +0 ** (odd integer) )+ * 15. +INF ** (+anything except 0,NAN) is +INF+ * 16. +INF ** (-anything except 0,NAN) is +0+ * 17. -INF ** (anything) = -0 ** (-anything)+ * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)+ * 19. (-anything except 0 and inf) ** (non-integer) is NAN+ *+ * Accuracy:+ * pow(x,y) returns x**y nearly rounded. In particular+ * pow(integer,integer)+ * always returns the correct integer provided it is + * representable.+ *+ * Constants :+ * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown.+ */++static const double +bp[] = {1.0, 1.5,},+dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */+dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */+two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */+ /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */+lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */+lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */+lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */+ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */+cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */+cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */+cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/+ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */+ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/+ivln2_l = 1.92596299112661746887e-08, /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/+ivlg10b2 = 0.3010299956639812, /* 0x3fd34413509f79ff 1/log2(10) */+ivlg10b2_h = 0.30102992057800293, /* 0x3fd3441300000000 1/log2(10) high */+ivlg10b2_l = 7.508597826552624e-8; /* 0x3e7427de7fbcc47c 1/log2(10) low */++static const double L_tab[] = {+ /* L1 */ 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */+ /* L2 */ 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */+ /* L3 */ 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */+ /* L4 */ 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */+ /* L5 */ 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */+ /* L6 */ 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */+};++/* compute (t1, t2) = log2(ax). is_small_ax is true if abs(ax)<= 2**-20 */+static void kernel_log2(double *pt1, double *pt2, double ax)+{+ double t, u, v, t1, t2, r;+ int n, j, ix, k;+ double ss, s2, s_h, s_l, t_h, t_l, p_l, p_h, z_h, z_l;++ n = 0;+ ix = get_high_word(ax);+ /* take care subnormal number */+ if(ix<0x00100000)+ {ax *= two53; n -= 53; ix = get_high_word(ax); }+ n += ((ix)>>20)-0x3ff;+ j = ix&0x000fffff;+ /* determine interval */+ ix = j|0x3ff00000; /* normalize ix */+ if(j<=0x3988E) k=0; /* |x|<sqrt(3/2) */+ else if(j<0xBB67A) k=1; /* |x|<sqrt(3) */+ else {k=0;n+=1;ix -= 0x00100000;}+ ax = set_high_word(ax, ix);+ + /* compute ss = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */+ u = ax-bp[k]; /* bp[0]=1.0, bp[1]=1.5 */+ v = one/(ax+bp[k]);+ ss = u*v;+ s_h = zero_low(ss);+ /* t_h=ax+bp[k] High */+ t_h = zero;+ t_h = set_high_word(t_h, ((ix>>1)|0x20000000)+0x00080000+(k<<18));+ t_l = ax - (t_h-bp[k]);+ s_l = v*((u-s_h*t_h)-s_h*t_l);+ /* compute log(ax) */+ s2 = ss*ss;+ r = s2*s2*eval_poly(s2, L_tab, 6);+ r += s_l*(s_h+ss);+ s2 = s_h*s_h;+ t_h = zero_low(3.0+s2+r);+ t_l = r-((t_h-3.0)-s2);+ /* u+v = ss*(1+...) */+ u = s_h*t_h;+ v = s_l*t_h+t_l*ss;+ /* 2/(3log2)*(ss+...) */+ p_h = zero_low(u+v);+ p_l = v-(p_h-u);+ z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */+ z_l = cp_l*p_h+p_l*cp+dp_l[k];+ /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */+ t = (double)n;+ t1 = zero_low(((z_h+z_l)+dp_h[k])+t);+ t2 = z_l-(((t1-t)-dp_h[k])-z_h);++ *pt1 = t1;+ *pt2 = t2;+}++/* flag = 0: log2()+ flag = 1: log()+ flag = 2: log10()+*/+static double js_log_internal(double x, int flag)+{+ double p_h, p_l, t, u, v;+ int hx, lx;++ EXTRACT_WORDS(hx, lx, x);+ if (hx <= 0) {+ if (((hx&0x7fffffff)|lx)==0) + return -INFINITY; /* log(+-0)=-inf */+ if (hx<0)+ return NAN; /* log(-#) = NaN */+ } else if (hx >= 0x7ff00000) {+ /* log(inf) = inf, log(nan) = nan */+ return x+x;+ }+ kernel_log2(&p_h, &p_l, x);++ t = p_h + p_l;+ if (flag == 0) {+ return t;+ } else {+ t = zero_low(t);+ if (flag == 1) {+ /* multiply (p_l+p_h) by lg2 */+ u = t*lg2_h;+ v = (p_l-(t-p_h))*lg2+t*lg2_l;+ } else {+ /* mutiply (p_l+p_h) by 1/log2(10) */+ u = t*ivlg10b2_h;+ v = (p_l-(t-p_h))*ivlg10b2+t*ivlg10b2_l;+ }+ return u+v;+ }+}++double js_log2(double x)+{+ return js_log_internal(x, 0);+}++double js_log(double x)+{+ return js_log_internal(x, 1);+}++double js_log10(double x)+{+ return js_log_internal(x, 2);+}++double js_pow(double x, double y)+{+ double z,ax,p_h,p_l;+ double y1,t1,t2,s,t,u,v,w;+ int i,j,k,yisint,n;+ int hx,hy,ix,iy;+ unsigned lx,ly;++ EXTRACT_WORDS(hx, lx, x);+ EXTRACT_WORDS(hy, ly, y);+ ix = hx&0x7fffffff; iy = hy&0x7fffffff;++ /* y==zero: x**0 = 1 */+ if((iy|ly)==0) return one; ++ /* +-NaN return x+y */+ if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) ||+ iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return x+y; ++ /* determine if y is an odd int when x < 0+ * yisint = 0 ... y is not an integer+ * yisint = 1 ... y is an odd int+ * yisint = 2 ... y is an even int+ */+ yisint = 0;+ if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */+ else if(iy>=0x3ff00000) {+ k = (iy>>20)-0x3ff; /* exponent */+ if(k>20) {+ j = ly>>(52-k);+ if((j<<(52-k))==ly) yisint = 2-(j&1);+ } else if(ly==0) {+ j = iy>>(20-k);+ if((j<<(20-k))==iy) yisint = 2-(j&1);+ }+ } + } ++ /* special value of y */+ if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */+ if(((ix-0x3ff00000)|lx)==0)+ return y - y; /* inf**+-1 is NaN */+ else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */+ return (hy>=0)? y: zero;+ else /* (|x|<1)**-,+inf = inf,0 */+ return (hy<0)?-y: zero;+ } + if(iy==0x3ff00000) { /* y is +-1 */+ if(hy<0) return one/x; else return x;+ }+ if(hy==0x40000000) return x*x; /* y is 2 */+ if(hy==0x3fe00000) { /* y is 0.5 */+ if(hx>=0) /* x >= +0 */+ return js_sqrt(x); + }+ }++ ax = fabs(x);+ /* special value of x */+ if(lx==0) {+ if(ix==0x7ff00000||ix==0||ix==0x3ff00000){+ z = ax; /*x is +-0,+-inf,+-1*/+ if(hy<0) z = one/z; /* z = (1/|x|) */+ if(hx<0) {+ if(((ix-0x3ff00000)|yisint)==0) {+ z = (z-z)/(z-z); /* (-1)**non-int is NaN */+ } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */+ }+ return z;+ }+ }+ + n = (hx>>31)+1;++ /* (x<0)**(non-int) is NaN */+ if((n|yisint)==0) return (x-x)/(x-x);++ s = one; /* s (sign of result -ve**odd) = -1 else = 1 */+ if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */++ /* |y| is huge */+ if(iy>0x41e00000) { /* if |y| > 2**31 */+ if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */+ if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny;+ if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny;+ }+ /* over/underflow if x is not close to one */+ if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny;+ if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny;+ t = ax-one; /* t has 20 trailing zeros */+ w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25));+ u = ivln2_h*t; /* ivln2_h has 21 sig. bits */+ v = t*ivln2_l-w*ivln2;+ t1 = zero_low(u+v);+ t2 = v-(t1-u);+ } else {+ kernel_log2(&t1, &t2, ax);+ }+ /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */+ y1 = zero_low(y);+ p_l = (y-y1)*t1+y*t2;+ p_h = y1*t1;+ z = p_l+p_h;+ EXTRACT_WORDS(j, i, z);+ if (j>=0x40900000) { /* z >= 1024 */+ if(((j-0x40900000)|i)!=0) /* if z > 1024 */+ return s*huge*huge; /* overflow */+ else {+ if(p_l+ovt>z-p_h) return s*huge*huge; /* overflow */+ }+ } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */+ if(((j-0xc090cc00)|i)!=0) /* z < -1075 */+ return s*tiny*tiny; /* underflow */+ else {+ if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */+ }+ }+ /*+ * compute 2**(p_h+p_l)+ */+ i = j&0x7fffffff;+ k = (i>>20)-0x3ff;+ n = 0;+ if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */+ n = j+(0x00100000>>(k+1));+ k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */+ t = zero;+ t = set_high_word(t, n&~(0x000fffff>>k));+ n = ((n&0x000fffff)|0x00100000)>>(20-k);+ if(j<0) n = -n;+ p_h -= t;+ }+ /* multiply (p_l+p_h) by lg2 */+ t = zero_low(p_l+p_h);+ u = t*lg2_h;+ v = (p_l-(t-p_h))*lg2+t*lg2_l;+ z = u+v;+ w = v-(z-u);+ return s * kernel_exp(z, w, 0, z, n);+}+
+ mquickjs/libm.h view
@@ -0,0 +1,46 @@+/*+ * Tiny Math Library+ *+ * Copyright (c) 2024 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.+ */+double js_scalbn(double x, int n);+double js_floor(double x);+double js_ceil(double x);+double js_trunc(double x);+double js_round_inf(double a);+double js_fabs(double x);+double js_sqrt(double x);+int32_t js_lrint(double a);+double js_fmod(double x, double y);+double js_sin(double x);+double js_cos(double x);+double js_tan(double x);+double js_acos(double x);+double js_asin(double x);+double js_atan(double x);+double js_atan2(double y, double x);+double js_exp(double x);+double js_log(double x);+double js_log2(double x);+double js_log10(double x);+double js_pow(double x, double y);+/* exported only for tests */+int js_rem_pio2(double x, double *y);
+ mquickjs/list.h view
@@ -0,0 +1,99 @@+/*+ * Linux klist like system+ * + * Copyright (c) 2016-2017 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 LIST_H+#define LIST_H++#ifndef NULL+#include <stddef.h>+#endif++struct list_head {+ struct list_head *prev;+ struct list_head *next;+};++#define LIST_HEAD_INIT(el) { &(el), &(el) }++/* return the pointer of type 'type *' containing 'el' as field 'member' */+#define list_entry(el, type, member) container_of(el, type, member)++static inline void init_list_head(struct list_head *head)+{+ head->prev = head;+ head->next = head;+}++/* insert 'el' between 'prev' and 'next' */+static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next)+{+ prev->next = el;+ el->prev = prev;+ el->next = next;+ next->prev = el;+}++/* add 'el' at the head of the list 'head' (= after element head) */+static inline void list_add(struct list_head *el, struct list_head *head)+{+ __list_add(el, head, head->next);+}++/* add 'el' at the end of the list 'head' (= before element head) */+static inline void list_add_tail(struct list_head *el, struct list_head *head)+{+ __list_add(el, head->prev, head);+}++static inline void list_del(struct list_head *el)+{+ struct list_head *prev, *next;+ prev = el->prev;+ next = el->next;+ prev->next = next;+ next->prev = prev;+ el->prev = NULL; /* fail safe */+ el->next = NULL; /* fail safe */+}++static inline int list_empty(struct list_head *el)+{+ return el->next == el;+}++#define list_for_each(el, head) \+ for(el = (head)->next; el != (head); el = el->next)++#define list_for_each_safe(el, el1, head) \+ for(el = (head)->next, el1 = el->next; el != (head); \+ el = el1, el1 = el->next)++#define list_for_each_prev(el, head) \+ for(el = (head)->prev; el != (head); el = el->prev)++#define list_for_each_prev_safe(el, el1, head) \+ for(el = (head)->prev, el1 = el->prev; el != (head); \+ el = el1, el1 = el->prev)++#endif /* LIST_H */
+ mquickjs/mqjs_stdlib.h view
@@ -0,0 +1,2692 @@+/* this file is automatically generated - do not edit */++#include "mquickjs_priv.h"++static const uint64_t __attribute((aligned(64))) js_stdlib_table[] = {+ /* atom_table */+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "null" (offset=0) */+ 0x000000006c6c756e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "false" (offset=2) */+ 0x00000065736c6166,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "true" (offset=4) */+ 0x0000000065757274,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "if" (offset=6) */+ 0x0000000000006669,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "else" (offset=8) */+ 0x0000000065736c65,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "return" (offset=10) */+ 0x00006e7275746572,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "var" (offset=12) */+ 0x0000000000726176,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "this" (offset=14) */+ 0x0000000073696874,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "delete" (offset=16) */+ 0x00006574656c6564,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "void" (offset=18) */+ 0x0000000064696f76,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "typeof" (offset=20) */+ 0x0000666f65707974,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "new" (offset=22) */+ 0x000000000077656e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "in" (offset=24) */+ 0x0000000000006e69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "instanceof" (offset=26) */+ 0x65636e6174736e69,+ 0x000000000000666f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "do" (offset=29) */+ 0x0000000000006f64,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "while" (offset=31) */+ 0x000000656c696877,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "for" (offset=33) */+ 0x0000000000726f66,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "break" (offset=35) */+ 0x0000006b61657262,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "continue" (offset=37) */+ 0x65756e69746e6f63,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "switch" (offset=40) */+ 0x0000686374697773,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "case" (offset=42) */+ 0x0000000065736163,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "default" (offset=44) */+ 0x00746c7561666564,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "throw" (offset=46) */+ 0x000000776f726874,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "try" (offset=48) */+ 0x0000000000797274,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "catch" (offset=50) */+ 0x0000006863746163,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "finally" (offset=52) */+ 0x00796c6c616e6966,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "function" (offset=54) */+ 0x6e6f6974636e7566,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "debugger" (offset=57) */+ 0x7265676775626564,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "with" (offset=60) */+ 0x0000000068746977,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "class" (offset=62) */+ 0x0000007373616c63,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "const" (offset=64) */+ 0x00000074736e6f63,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "enum" (offset=66) */+ 0x000000006d756e65,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "export" (offset=68) */+ 0x000074726f707865,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "extends" (offset=70) */+ 0x0073646e65747865,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "import" (offset=72) */+ 0x000074726f706d69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "super" (offset=74) */+ 0x0000007265707573,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "implements" (offset=76) */+ 0x6e656d656c706d69,+ 0x0000000000007374,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "interface" (offset=79) */+ 0x6361667265746e69,+ 0x0000000000000065,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "let" (offset=82) */+ 0x000000000074656c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "package" (offset=84) */+ 0x006567616b636170,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "private" (offset=86) */+ 0x0065746176697270,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "protected" (offset=88) */+ 0x65746365746f7270,+ 0x0000000000000064,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "public" (offset=91) */+ 0x000063696c627570,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "static" (offset=93) */+ 0x0000636974617473,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "yield" (offset=95) */+ 0x000000646c656979,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (0 << (JS_MTAG_BITS + 3)), /* "" (offset=97) */+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "toString" (offset=99) */+ 0x676e697274536f74,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "valueOf" (offset=102) */+ 0x00664f65756c6176,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "number" (offset=104) */+ 0x00007265626d756e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "object" (offset=106) */+ 0x00007463656a626f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "undefined" (offset=108) */+ 0x656e696665646e75,+ 0x0000000000000064,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "string" (offset=111) */+ 0x0000676e69727473,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "boolean" (offset=113) */+ 0x006e61656c6f6f62,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "<ret>" (offset=115) */+ 0x0000003e7465723c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "<eval>" (offset=117) */+ 0x00003e6c6176653c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "eval" (offset=119) */+ 0x000000006c617665,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "arguments" (offset=121) */+ 0x746e656d75677261,+ 0x0000000000000073,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "value" (offset=124) */+ 0x00000065756c6176,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "get" (offset=126) */+ 0x0000000000746567,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "set" (offset=128) */+ 0x0000000000746573,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "prototype" (offset=130) */+ 0x7079746f746f7270,+ 0x0000000000000065,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "constructor" (offset=133) */+ 0x63757274736e6f63,+ 0x0000000000726f74,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "length" (offset=136) */+ 0x00006874676e656c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "target" (offset=138) */+ 0x0000746567726174,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "of" (offset=140) */+ 0x000000000000666f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (1 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "NaN" (offset=142) */+ 0x00000000004e614e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (1 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "Infinity" (offset=144) */+ 0x7974696e69666e49,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (1 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "-Infinity" (offset=147) */+ 0x74696e69666e492d,+ 0x0000000000000079,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "name" (offset=150) */+ 0x00000000656d616e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "Error" (offset=152) */+ 0x000000726f727245,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "__proto__" (offset=154) */+ 0x5f6f746f72705f5f,+ 0x000000000000005f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "index" (offset=157) */+ 0x0000007865646e69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "input" (offset=159) */+ 0x0000007475706e69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "bound" (offset=161) */+ 0x000000646e756f62,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "Object" (offset=163) */+ 0x00007463656a624f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "defineProperty" (offset=165) */+ 0x7250656e69666564,+ 0x000079747265706f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "getPrototypeOf" (offset=168) */+ 0x6f746f7250746567,+ 0x0000664f65707974,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "setPrototypeOf" (offset=171) */+ 0x6f746f7250746573,+ 0x0000664f65707974,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "create" (offset=174) */+ 0x0000657461657263,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "keys" (offset=176) */+ 0x000000007379656b,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "hasOwnProperty" (offset=178) */+ 0x72506e774f736168,+ 0x000079747265706f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "Function" (offset=181) */+ 0x6e6f6974636e7546,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "get prototype" (offset=184) */+ 0x746f727020746567,+ 0x000000657079746f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "set prototype" (offset=187) */+ 0x746f727020746573,+ 0x000000657079746f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "call" (offset=190) */+ 0x000000006c6c6163,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "apply" (offset=192) */+ 0x000000796c707061,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "bind" (offset=194) */+ 0x00000000646e6962,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "get length" (offset=196) */+ 0x676e656c20746567,+ 0x0000000000006874,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "get name" (offset=199) */+ 0x656d616e20746567,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "Number" (offset=202) */+ 0x00007265626d754e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "parseInt" (offset=204) */+ 0x746e496573726170,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "parseFloat" (offset=207) */+ 0x6f6c466573726170,+ 0x0000000000007461,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "MAX_VALUE" (offset=210) */+ 0x554c41565f58414d,+ 0x0000000000000045,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "MIN_VALUE" (offset=213) */+ 0x554c41565f4e494d,+ 0x0000000000000045,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "NEGATIVE_INFINITY" (offset=216) */+ 0x455649544147454e,+ 0x54494e49464e495f,+ 0x0000000000000059,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "POSITIVE_INFINITY" (offset=220) */+ 0x4556495449534f50,+ 0x54494e49464e495f,+ 0x0000000000000059,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "EPSILON" (offset=224) */+ 0x004e4f4c49535045,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (16 << (JS_MTAG_BITS + 3)), /* "MAX_SAFE_INTEGER" (offset=226) */+ 0x454641535f58414d,+ 0x52454745544e495f,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (16 << (JS_MTAG_BITS + 3)), /* "MIN_SAFE_INTEGER" (offset=230) */+ 0x454641535f4e494d,+ 0x52454745544e495f,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "toExponential" (offset=234) */+ 0x656e6f7078456f74,+ 0x0000006c6169746e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "toFixed" (offset=237) */+ 0x0064657869466f74,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "toPrecision" (offset=239) */+ 0x7369636572506f74,+ 0x00000000006e6f69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "Boolean" (offset=242) */+ 0x006e61656c6f6f42,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "String" (offset=244) */+ 0x0000676e69727453,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "fromCharCode" (offset=246) */+ 0x726168436d6f7266,+ 0x0000000065646f43,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "fromCodePoint" (offset=249) */+ 0x65646f436d6f7266,+ 0x000000746e696f50,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "set length" (offset=252) */+ 0x676e656c20746573,+ 0x0000000000006874,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "charAt" (offset=255) */+ 0x0000744172616863,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "charCodeAt" (offset=257) */+ 0x65646f4372616863,+ 0x0000000000007441,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "codePointAt" (offset=260) */+ 0x6e696f5065646f63,+ 0x0000000000744174,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "slice" (offset=263) */+ 0x0000006563696c73,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "substring" (offset=265) */+ 0x6e69727473627573,+ 0x0000000000000067,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "concat" (offset=268) */+ 0x00007461636e6f63,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "indexOf" (offset=270) */+ 0x00664f7865646e69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "lastIndexOf" (offset=272) */+ 0x65646e497473616c,+ 0x0000000000664f78,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "match" (offset=275) */+ 0x000000686374616d,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "replace" (offset=277) */+ 0x006563616c706572,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "replaceAll" (offset=279) */+ 0x416563616c706572,+ 0x0000000000006c6c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "search" (offset=282) */+ 0x0000686372616573,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "split" (offset=284) */+ 0x00000074696c7073,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "toLowerCase" (offset=286) */+ 0x437265776f4c6f74,+ 0x0000000000657361,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "toUpperCase" (offset=289) */+ 0x4372657070556f74,+ 0x0000000000657361,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "trim" (offset=292) */+ 0x000000006d697274,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "trimEnd" (offset=294) */+ 0x00646e456d697274,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "trimStart" (offset=296) */+ 0x726174536d697274,+ 0x0000000000000074,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "repeat" (offset=299) */+ 0x0000746165706572,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "Array" (offset=301) */+ 0x0000007961727241,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "isArray" (offset=303) */+ 0x0079617272417369,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "push" (offset=305) */+ 0x0000000068737570,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "pop" (offset=307) */+ 0x0000000000706f70,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "join" (offset=309) */+ 0x000000006e696f6a,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "reverse" (offset=311) */+ 0x0065737265766572,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "shift" (offset=313) */+ 0x0000007466696873,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "splice" (offset=315) */+ 0x00006563696c7073,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "unshift" (offset=317) */+ 0x0074666968736e75,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "every" (offset=319) */+ 0x0000007972657665,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "some" (offset=321) */+ 0x00000000656d6f73,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "forEach" (offset=323) */+ 0x0068636145726f66,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "map" (offset=325) */+ 0x000000000070616d,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "filter" (offset=327) */+ 0x00007265746c6966,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "reduce" (offset=329) */+ 0x0000656375646572,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "reduceRight" (offset=331) */+ 0x6952656375646572,+ 0x0000000000746867,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "sort" (offset=334) */+ 0x0000000074726f73,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "Math" (offset=336) */+ 0x000000006874614d,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "min" (offset=338) */+ 0x00000000006e696d,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "max" (offset=340) */+ 0x000000000078616d,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "sign" (offset=342) */+ 0x000000006e676973,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "abs" (offset=344) */+ 0x0000000000736261,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "floor" (offset=346) */+ 0x000000726f6f6c66,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "ceil" (offset=348) */+ 0x000000006c696563,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "round" (offset=350) */+ 0x000000646e756f72,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "sqrt" (offset=352) */+ 0x0000000074727173,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "E" (offset=354) */+ 0x0000000000000045,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "LN10" (offset=356) */+ 0x0000000030314e4c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "LN2" (offset=358) */+ 0x0000000000324e4c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "LOG2E" (offset=360) */+ 0x0000004532474f4c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "LOG10E" (offset=362) */+ 0x0000453031474f4c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "PI" (offset=364) */+ 0x0000000000004950,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "SQRT1_2" (offset=366) */+ 0x00325f3154525153,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "SQRT2" (offset=368) */+ 0x0000003254525153,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "sin" (offset=370) */+ 0x00000000006e6973,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "cos" (offset=372) */+ 0x0000000000736f63,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "tan" (offset=374) */+ 0x00000000006e6174,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "asin" (offset=376) */+ 0x000000006e697361,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "acos" (offset=378) */+ 0x00000000736f6361,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "atan" (offset=380) */+ 0x000000006e617461,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "atan2" (offset=382) */+ 0x000000326e617461,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "exp" (offset=384) */+ 0x0000000000707865,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "log" (offset=386) */+ 0x0000000000676f6c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "pow" (offset=388) */+ 0x0000000000776f70,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "random" (offset=390) */+ 0x00006d6f646e6172,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "imul" (offset=392) */+ 0x000000006c756d69,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "clz32" (offset=394) */+ 0x00000032337a6c63,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "fround" (offset=396) */+ 0x0000646e756f7266,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "trunc" (offset=398) */+ 0x000000636e757274,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "log2" (offset=400) */+ 0x0000000032676f6c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "log10" (offset=402) */+ 0x0000003031676f6c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "Date" (offset=404) */+ 0x0000000065746144,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "now" (offset=406) */+ 0x0000000000776f6e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "JSON" (offset=408) */+ 0x000000004e4f534a,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "parse" (offset=410) */+ 0x0000006573726170,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "stringify" (offset=412) */+ 0x6669676e69727473,+ 0x0000000000000079,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "RegExp" (offset=415) */+ 0x0000707845676552,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "lastIndex" (offset=417) */+ 0x65646e497473616c,+ 0x0000000000000078,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "get lastIndex" (offset=420) */+ 0x7473616c20746567,+ 0x0000007865646e49,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "set lastIndex" (offset=423) */+ 0x7473616c20746573,+ 0x0000007865646e49,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "source" (offset=426) */+ 0x0000656372756f73,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "get source" (offset=428) */+ 0x72756f7320746567,+ 0x0000000000006563,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "flags" (offset=431) */+ 0x0000007367616c66,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "get flags" (offset=433) */+ 0x67616c6620746567,+ 0x0000000000000073,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "exec" (offset=436) */+ 0x0000000063657865,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "test" (offset=438) */+ 0x0000000074736574,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "message" (offset=440) */+ 0x006567617373656d,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "get message" (offset=442) */+ 0x7373656d20746567,+ 0x0000000000656761,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "stack" (offset=445) */+ 0x0000006b63617473,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "get stack" (offset=447) */+ 0x6361747320746567,+ 0x000000000000006b,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "EvalError" (offset=450) */+ 0x6f7272456c617645,+ 0x0000000000000072,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "RangeError" (offset=453) */+ 0x72724565676e6152,+ 0x000000000000726f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "ReferenceError" (offset=456) */+ 0x636e657265666552,+ 0x0000726f72724565,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "SyntaxError" (offset=459) */+ 0x72457861746e7953,+ 0x0000000000726f72,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "TypeError" (offset=462) */+ 0x6f72724565707954,+ 0x0000000000000072,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "URIError" (offset=465) */+ 0x726f727245495255,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "InternalError" (offset=468) */+ 0x6c616e7265746e49,+ 0x000000726f727245,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "ArrayBuffer" (offset=471) */+ 0x6675427961727241,+ 0x0000000000726566,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "byteLength" (offset=474) */+ 0x676e654c65747962,+ 0x0000000000006874,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "get byteLength" (offset=477) */+ 0x6574796220746567,+ 0x00006874676e654c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "Uint8ClampedArray" (offset=480) */+ 0x616c4338746e6955,+ 0x617272416465706d,+ 0x0000000000000079,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "TypedArray" (offset=484) */+ 0x7272416465707954,+ 0x0000000000007961,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "byteOffset" (offset=487) */+ 0x7366664f65747962,+ 0x0000000000007465,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "get byteOffset" (offset=490) */+ 0x6574796220746567,+ 0x000074657366664f,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "buffer" (offset=493) */+ 0x0000726566667562,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "get buffer" (offset=495) */+ 0x6666756220746567,+ 0x0000000000007265,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "subarray" (offset=498) */+ 0x7961727261627573,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "BYTES_PER_ELEMENT" (offset=501) */+ 0x45505f5345545942,+ 0x4e454d454c455f52,+ 0x0000000000000054,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "Int8Array" (offset=505) */+ 0x6172724138746e49,+ 0x0000000000000079,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "Uint8Array" (offset=508) */+ 0x72724138746e6955,+ 0x0000000000007961,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "Int16Array" (offset=511) */+ 0x7272413631746e49,+ 0x0000000000007961,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "Uint16Array" (offset=514) */+ 0x72413631746e6955,+ 0x0000000000796172,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "Int32Array" (offset=517) */+ 0x7272413233746e49,+ 0x0000000000007961,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "Uint32Array" (offset=520) */+ 0x72413233746e6955,+ 0x0000000000796172,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "Float32Array" (offset=523) */+ 0x41323374616f6c46,+ 0x0000000079617272,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "Float64Array" (offset=526) */+ 0x41343674616f6c46,+ 0x0000000079617272,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "isNaN" (offset=529) */+ 0x0000004e614e7369,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "isFinite" (offset=531) */+ 0x6574696e69467369,+ 0x0000000000000000,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "globalThis" (offset=534) */+ 0x68546c61626f6c67,+ 0x0000000000007369,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "console" (offset=537) */+ 0x00656c6f736e6f63,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "performance" (offset=539) */+ 0x616d726f66726570,+ 0x000000000065636e,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "print" (offset=542) */+ 0x000000746e697270,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "gc" (offset=544) */+ 0x0000000000006367,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "load" (offset=546) */+ 0x0000000064616f6c,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "setTimeout" (offset=548) */+ 0x6f656d6954746573,+ 0x0000000000007475,+ (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "clearTimeout" (offset=551) */+ 0x6d69547261656c63,+ 0x0000000074756f65,++ /* sorted atom table (offset=554) */+ JS_VALUE_ARRAY_HEADER(232),+ JS_ROM_VALUE(97), /* empty */+ JS_ROM_VALUE(147), /* _Infinity */+ JS_ROM_VALUE(117), /* _eval_ */+ JS_ROM_VALUE(115), /* _ret_ */+ JS_ROM_VALUE(301), /* Array */+ JS_ROM_VALUE(471), /* ArrayBuffer */+ JS_ROM_VALUE(501), /* BYTES_PER_ELEMENT */+ JS_ROM_VALUE(242), /* Boolean */+ JS_ROM_VALUE(404), /* Date */+ JS_ROM_VALUE(354), /* E */+ JS_ROM_VALUE(224), /* EPSILON */+ JS_ROM_VALUE(152), /* Error */+ JS_ROM_VALUE(450), /* EvalError */+ JS_ROM_VALUE(523), /* Float32Array */+ JS_ROM_VALUE(526), /* Float64Array */+ JS_ROM_VALUE(181), /* Function */+ JS_ROM_VALUE(144), /* Infinity */+ JS_ROM_VALUE(511), /* Int16Array */+ JS_ROM_VALUE(517), /* Int32Array */+ JS_ROM_VALUE(505), /* Int8Array */+ JS_ROM_VALUE(468), /* InternalError */+ JS_ROM_VALUE(408), /* JSON */+ JS_ROM_VALUE(356), /* LN10 */+ JS_ROM_VALUE(358), /* LN2 */+ JS_ROM_VALUE(362), /* LOG10E */+ JS_ROM_VALUE(360), /* LOG2E */+ JS_ROM_VALUE(226), /* MAX_SAFE_INTEGER */+ JS_ROM_VALUE(210), /* MAX_VALUE */+ JS_ROM_VALUE(230), /* MIN_SAFE_INTEGER */+ JS_ROM_VALUE(213), /* MIN_VALUE */+ JS_ROM_VALUE(336), /* Math */+ JS_ROM_VALUE(216), /* NEGATIVE_INFINITY */+ JS_ROM_VALUE(142), /* NaN */+ JS_ROM_VALUE(202), /* Number */+ JS_ROM_VALUE(163), /* Object */+ JS_ROM_VALUE(364), /* PI */+ JS_ROM_VALUE(220), /* POSITIVE_INFINITY */+ JS_ROM_VALUE(453), /* RangeError */+ JS_ROM_VALUE(456), /* ReferenceError */+ JS_ROM_VALUE(415), /* RegExp */+ JS_ROM_VALUE(366), /* SQRT1_2 */+ JS_ROM_VALUE(368), /* SQRT2 */+ JS_ROM_VALUE(244), /* String */+ JS_ROM_VALUE(459), /* SyntaxError */+ JS_ROM_VALUE(462), /* TypeError */+ JS_ROM_VALUE(484), /* TypedArray */+ JS_ROM_VALUE(465), /* URIError */+ JS_ROM_VALUE(514), /* Uint16Array */+ JS_ROM_VALUE(520), /* Uint32Array */+ JS_ROM_VALUE(508), /* Uint8Array */+ JS_ROM_VALUE(480), /* Uint8ClampedArray */+ JS_ROM_VALUE(154), /* __proto__ */+ JS_ROM_VALUE(344), /* abs */+ JS_ROM_VALUE(378), /* acos */+ JS_ROM_VALUE(192), /* apply */+ JS_ROM_VALUE(121), /* arguments */+ JS_ROM_VALUE(376), /* asin */+ JS_ROM_VALUE(380), /* atan */+ JS_ROM_VALUE(382), /* atan2 */+ JS_ROM_VALUE(194), /* bind */+ JS_ROM_VALUE(113), /* boolean */+ JS_ROM_VALUE(161), /* bound */+ JS_ROM_VALUE(35), /* break */+ JS_ROM_VALUE(493), /* buffer */+ JS_ROM_VALUE(474), /* byteLength */+ JS_ROM_VALUE(487), /* byteOffset */+ JS_ROM_VALUE(190), /* call */+ JS_ROM_VALUE(42), /* case */+ JS_ROM_VALUE(50), /* catch */+ JS_ROM_VALUE(348), /* ceil */+ JS_ROM_VALUE(255), /* charAt */+ JS_ROM_VALUE(257), /* charCodeAt */+ JS_ROM_VALUE(62), /* class */+ JS_ROM_VALUE(551), /* clearTimeout */+ JS_ROM_VALUE(394), /* clz32 */+ JS_ROM_VALUE(260), /* codePointAt */+ JS_ROM_VALUE(268), /* concat */+ JS_ROM_VALUE(537), /* console */+ JS_ROM_VALUE(64), /* const */+ JS_ROM_VALUE(133), /* constructor */+ JS_ROM_VALUE(37), /* continue */+ JS_ROM_VALUE(372), /* cos */+ JS_ROM_VALUE(174), /* create */+ JS_ROM_VALUE(57), /* debugger */+ JS_ROM_VALUE(44), /* default */+ JS_ROM_VALUE(165), /* defineProperty */+ JS_ROM_VALUE(16), /* delete */+ JS_ROM_VALUE(29), /* do */+ JS_ROM_VALUE(8), /* else */+ JS_ROM_VALUE(66), /* enum */+ JS_ROM_VALUE(119), /* eval */+ JS_ROM_VALUE(319), /* every */+ JS_ROM_VALUE(436), /* exec */+ JS_ROM_VALUE(384), /* exp */+ JS_ROM_VALUE(68), /* export */+ JS_ROM_VALUE(70), /* extends */+ JS_ROM_VALUE(2), /* false */+ JS_ROM_VALUE(327), /* filter */+ JS_ROM_VALUE(52), /* finally */+ JS_ROM_VALUE(431), /* flags */+ JS_ROM_VALUE(346), /* floor */+ JS_ROM_VALUE(33), /* for */+ JS_ROM_VALUE(323), /* forEach */+ JS_ROM_VALUE(246), /* fromCharCode */+ JS_ROM_VALUE(249), /* fromCodePoint */+ JS_ROM_VALUE(396), /* fround */+ JS_ROM_VALUE(54), /* function */+ JS_ROM_VALUE(544), /* gc */+ JS_ROM_VALUE(126), /* get */+ JS_ROM_VALUE(495), /* get buffer */+ JS_ROM_VALUE(477), /* get byteLength */+ JS_ROM_VALUE(490), /* get byteOffset */+ JS_ROM_VALUE(433), /* get flags */+ JS_ROM_VALUE(420), /* get lastIndex */+ JS_ROM_VALUE(196), /* get length */+ JS_ROM_VALUE(442), /* get message */+ JS_ROM_VALUE(199), /* get name */+ JS_ROM_VALUE(184), /* get prototype */+ JS_ROM_VALUE(428), /* get source */+ JS_ROM_VALUE(447), /* get stack */+ JS_ROM_VALUE(168), /* getPrototypeOf */+ JS_ROM_VALUE(534), /* globalThis */+ JS_ROM_VALUE(178), /* hasOwnProperty */+ JS_ROM_VALUE(6), /* if */+ JS_ROM_VALUE(76), /* implements */+ JS_ROM_VALUE(72), /* import */+ JS_ROM_VALUE(392), /* imul */+ JS_ROM_VALUE(24), /* in */+ JS_ROM_VALUE(157), /* index */+ JS_ROM_VALUE(270), /* indexOf */+ JS_ROM_VALUE(159), /* input */+ JS_ROM_VALUE(26), /* instanceof */+ JS_ROM_VALUE(79), /* interface */+ JS_ROM_VALUE(303), /* isArray */+ JS_ROM_VALUE(531), /* isFinite */+ JS_ROM_VALUE(529), /* isNaN */+ JS_ROM_VALUE(309), /* join */+ JS_ROM_VALUE(176), /* keys */+ JS_ROM_VALUE(417), /* lastIndex */+ JS_ROM_VALUE(272), /* lastIndexOf */+ JS_ROM_VALUE(136), /* length */+ JS_ROM_VALUE(82), /* let */+ JS_ROM_VALUE(546), /* load */+ JS_ROM_VALUE(386), /* log */+ JS_ROM_VALUE(402), /* log10 */+ JS_ROM_VALUE(400), /* log2 */+ JS_ROM_VALUE(325), /* map */+ JS_ROM_VALUE(275), /* match */+ JS_ROM_VALUE(340), /* max */+ JS_ROM_VALUE(440), /* message */+ JS_ROM_VALUE(338), /* min */+ JS_ROM_VALUE(150), /* name */+ JS_ROM_VALUE(22), /* new */+ JS_ROM_VALUE(406), /* now */+ JS_ROM_VALUE(0), /* null */+ JS_ROM_VALUE(104), /* number */+ JS_ROM_VALUE(106), /* object */+ JS_ROM_VALUE(140), /* of */+ JS_ROM_VALUE(84), /* package */+ JS_ROM_VALUE(410), /* parse */+ JS_ROM_VALUE(207), /* parseFloat */+ JS_ROM_VALUE(204), /* parseInt */+ JS_ROM_VALUE(539), /* performance */+ JS_ROM_VALUE(307), /* pop */+ JS_ROM_VALUE(388), /* pow */+ JS_ROM_VALUE(542), /* print */+ JS_ROM_VALUE(86), /* private */+ JS_ROM_VALUE(88), /* protected */+ JS_ROM_VALUE(130), /* prototype */+ JS_ROM_VALUE(91), /* public */+ JS_ROM_VALUE(305), /* push */+ JS_ROM_VALUE(390), /* random */+ JS_ROM_VALUE(329), /* reduce */+ JS_ROM_VALUE(331), /* reduceRight */+ JS_ROM_VALUE(299), /* repeat */+ JS_ROM_VALUE(277), /* replace */+ JS_ROM_VALUE(279), /* replaceAll */+ JS_ROM_VALUE(10), /* return */+ JS_ROM_VALUE(311), /* reverse */+ JS_ROM_VALUE(350), /* round */+ JS_ROM_VALUE(282), /* search */+ JS_ROM_VALUE(128), /* set */+ JS_ROM_VALUE(423), /* set lastIndex */+ JS_ROM_VALUE(252), /* set length */+ JS_ROM_VALUE(187), /* set prototype */+ JS_ROM_VALUE(171), /* setPrototypeOf */+ JS_ROM_VALUE(548), /* setTimeout */+ JS_ROM_VALUE(313), /* shift */+ JS_ROM_VALUE(342), /* sign */+ JS_ROM_VALUE(370), /* sin */+ JS_ROM_VALUE(263), /* slice */+ JS_ROM_VALUE(321), /* some */+ JS_ROM_VALUE(334), /* sort */+ JS_ROM_VALUE(426), /* source */+ JS_ROM_VALUE(315), /* splice */+ JS_ROM_VALUE(284), /* split */+ JS_ROM_VALUE(352), /* sqrt */+ JS_ROM_VALUE(445), /* stack */+ JS_ROM_VALUE(93), /* static */+ JS_ROM_VALUE(111), /* string */+ JS_ROM_VALUE(412), /* stringify */+ JS_ROM_VALUE(498), /* subarray */+ JS_ROM_VALUE(265), /* substring */+ JS_ROM_VALUE(74), /* super */+ JS_ROM_VALUE(40), /* switch */+ JS_ROM_VALUE(374), /* tan */+ JS_ROM_VALUE(138), /* target */+ JS_ROM_VALUE(438), /* test */+ JS_ROM_VALUE(14), /* this */+ JS_ROM_VALUE(46), /* throw */+ JS_ROM_VALUE(234), /* toExponential */+ JS_ROM_VALUE(237), /* toFixed */+ JS_ROM_VALUE(286), /* toLowerCase */+ JS_ROM_VALUE(239), /* toPrecision */+ JS_ROM_VALUE(99), /* toString */+ JS_ROM_VALUE(289), /* toUpperCase */+ JS_ROM_VALUE(292), /* trim */+ JS_ROM_VALUE(294), /* trimEnd */+ JS_ROM_VALUE(296), /* trimStart */+ JS_ROM_VALUE(4), /* true */+ JS_ROM_VALUE(398), /* trunc */+ JS_ROM_VALUE(48), /* try */+ JS_ROM_VALUE(20), /* typeof */+ JS_ROM_VALUE(108), /* undefined */+ JS_ROM_VALUE(317), /* unshift */+ JS_ROM_VALUE(124), /* value */+ JS_ROM_VALUE(102), /* valueOf */+ JS_ROM_VALUE(12), /* var */+ JS_ROM_VALUE(18), /* void */+ JS_ROM_VALUE(31), /* while */+ JS_ROM_VALUE(60), /* with */+ JS_ROM_VALUE(95), /* yield */++ /* properties (offset=787) */+ JS_VALUE_ARRAY_HEADER(24),+ 6 << 1, /* n_props */+ 3 << 1, /* hash_mask */+ 6 << 1,+ 18 << 1,+ 12 << 1,+ 21 << 1,+ JS_ROM_VALUE(165) /* defineProperty */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 2),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(168) /* getPrototypeOf */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 3),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(171) /* setPrototypeOf */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 4),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(174) /* create */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 5),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(176) /* keys */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 6),+ (9 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_OBJECT << 1,+ (15 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=812) */+ JS_VALUE_ARRAY_HEADER(13),+ 3 << 1, /* n_props */+ 1 << 1, /* hash_mask */+ 10 << 1,+ 4 << 1,+ JS_ROM_VALUE(178) /* hasOwnProperty */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 7),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 8),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_OBJECT - 1) << 1,+ (7 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=826) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(787),+ 1,+ JS_ROM_VALUE(812),+ JS_NULL,++ /* properties (offset=831) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_CLOSURE << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=838) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 10),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 11),++ /* getset (offset=841) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 12),+ JS_UNDEFINED,++ /* getset (offset=844) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 13),+ JS_UNDEFINED,++ /* properties (offset=847) */+ JS_VALUE_ARRAY_HEADER(30),+ 8 << 1, /* n_props */+ 3 << 1, /* hash_mask */+ 27 << 1,+ 21 << 1,+ 18 << 1,+ 24 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_ROM_VALUE(838),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(190) /* call */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 14),+ (6 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(192) /* apply */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 15),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(194) /* bind */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 16),+ (9 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 17),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(136) /* length */,+ JS_ROM_VALUE(841),+ (12 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(844),+ (15 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_CLOSURE - 1) << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=878) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(831),+ 9,+ JS_ROM_VALUE(847),+ JS_NULL,++ /* float64 (offset=883) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x7fefffffffffffff,++ /* float64 (offset=885) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x0000000000000001,++ /* float64 (offset=887) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x7ff8000000000000,++ /* float64 (offset=889) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0xfff0000000000000,++ /* float64 (offset=891) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x7ff0000000000000,++ /* float64 (offset=893) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x3cb0000000000000,++ /* float64 (offset=895) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x433fffffffffffff,++ /* float64 (offset=897) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0xc33fffffffffffff,++ /* properties (offset=899) */+ JS_VALUE_ARRAY_HEADER(43),+ 11 << 1, /* n_props */+ 7 << 1, /* hash_mask */+ 0 << 1,+ 31 << 1,+ 0 << 1,+ 40 << 1,+ 19 << 1,+ 28 << 1,+ 13 << 1,+ 37 << 1,+ JS_ROM_VALUE(204) /* parseInt */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 19),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(207) /* parseFloat */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 20),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(210) /* MAX_VALUE */,+ JS_ROM_VALUE(883),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(213) /* MIN_VALUE */,+ JS_ROM_VALUE(885),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(142) /* NaN */,+ JS_ROM_VALUE(887),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(216) /* NEGATIVE_INFINITY */,+ JS_ROM_VALUE(889),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(220) /* POSITIVE_INFINITY */,+ JS_ROM_VALUE(891),+ (10 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(224) /* EPSILON */,+ JS_ROM_VALUE(893),+ (25 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(226) /* MAX_SAFE_INTEGER */,+ JS_ROM_VALUE(895),+ (16 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(230) /* MIN_SAFE_INTEGER */,+ JS_ROM_VALUE(897),+ (22 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_NUMBER << 1,+ (34 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=943) */+ JS_VALUE_ARRAY_HEADER(21),+ 5 << 1, /* n_props */+ 3 << 1, /* hash_mask */+ 18 << 1,+ 0 << 1,+ 15 << 1,+ 6 << 1,+ JS_ROM_VALUE(234) /* toExponential */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 21),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(237) /* toFixed */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 22),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(239) /* toPrecision */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 23),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 24),+ (12 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_NUMBER - 1) << 1,+ (9 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=965) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(899),+ 18,+ JS_ROM_VALUE(943),+ JS_NULL,++ /* properties (offset=970) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_BOOLEAN << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=977) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_BOOLEAN - 1) << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=984) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(970),+ 25,+ JS_ROM_VALUE(977),+ JS_NULL,++ /* properties (offset=989) */+ JS_VALUE_ARRAY_HEADER(13),+ 3 << 1, /* n_props */+ 1 << 1, /* hash_mask */+ 7 << 1,+ 10 << 1,+ JS_ROM_VALUE(246) /* fromCharCode */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 27),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(249) /* fromCodePoint */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 28),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_STRING << 1,+ (4 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=1003) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 29),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 30),++ /* properties (offset=1006) */+ JS_VALUE_ARRAY_HEADER(76),+ 22 << 1, /* n_props */+ 7 << 1, /* hash_mask */+ 55 << 1,+ 64 << 1,+ 70 << 1,+ 46 << 1,+ 73 << 1,+ 58 << 1,+ 43 << 1,+ 61 << 1,+ JS_ROM_VALUE(136) /* length */,+ JS_ROM_VALUE(1003),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(255) /* charAt */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 31),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(257) /* charCodeAt */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 32),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(260) /* codePointAt */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 33),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(263) /* slice */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 34),+ (13 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(265) /* substring */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 35),+ (16 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(268) /* concat */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 36),+ (19 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(270) /* indexOf */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 37),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(272) /* lastIndexOf */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 38),+ (10 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(275) /* match */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 39),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(277) /* replace */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 40),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(279) /* replaceAll */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 41),+ (22 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(282) /* search */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 42),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(284) /* split */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 43),+ (28 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(286) /* toLowerCase */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 44),+ (31 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(289) /* toUpperCase */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 45),+ (25 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(292) /* trim */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 46),+ (49 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(294) /* trimEnd */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 47),+ (52 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(296) /* trimStart */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 48),+ (34 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 49),+ (37 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(299) /* repeat */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 50),+ (67 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_STRING - 1) << 1,+ (40 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1083) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(989),+ 26,+ JS_ROM_VALUE(1006),+ JS_NULL,++ /* properties (offset=1088) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(303) /* isArray */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 52),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=1098) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 53),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 54),++ /* properties (offset=1101) */+ JS_VALUE_ARRAY_HEADER(79),+ 23 << 1, /* n_props */+ 7 << 1, /* hash_mask */+ 70 << 1,+ 46 << 1,+ 67 << 1,+ 0 << 1,+ 76 << 1,+ 10 << 1,+ 61 << 1,+ 73 << 1,+ JS_ROM_VALUE(268) /* concat */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 55),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(136) /* length */,+ JS_ROM_VALUE(1098),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(305) /* push */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 56),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(307) /* pop */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 57),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(309) /* join */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 58),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 59),+ (19 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(311) /* reverse */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 60),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(313) /* shift */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 61),+ (16 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(263) /* slice */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 62),+ (28 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(315) /* splice */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 63),+ (25 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(317) /* unshift */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 64),+ (22 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(270) /* indexOf */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 65),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(272) /* lastIndexOf */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 66),+ (13 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(319) /* every */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 67),+ (34 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(321) /* some */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 68),+ (31 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(323) /* forEach */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 69),+ (37 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(325) /* map */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 70),+ (40 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(327) /* filter */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 71),+ (49 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(329) /* reduce */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 72),+ (52 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(331) /* reduceRight */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 73),+ (55 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(329) /* reduce */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 72),+ (64 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(334) /* sort */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 74),+ (43 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_ARRAY - 1) << 1,+ (58 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1181) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1088),+ 51,+ JS_ROM_VALUE(1101),+ JS_NULL,++ /* float64 (offset=1186) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x4005bf0a8b145769,++ /* float64 (offset=1188) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x40026bb1bbb55516,++ /* float64 (offset=1190) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x3fe62e42fefa39ef,++ /* float64 (offset=1192) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x3ff71547652b82fe,++ /* float64 (offset=1194) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x3fdbcb7b1526e50e,++ /* float64 (offset=1196) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x400921fb54442d18,++ /* float64 (offset=1198) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x3fe6a09e667f3bcd,++ /* float64 (offset=1200) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x3ff6a09e667f3bcd,++ /* properties (offset=1202) */+ JS_VALUE_ARRAY_HEADER(109),+ 33 << 1, /* n_props */+ 7 << 1, /* hash_mask */+ 0 << 1,+ 103 << 1,+ 0 << 1,+ 106 << 1,+ 34 << 1,+ 97 << 1,+ 0 << 1,+ 100 << 1,+ JS_ROM_VALUE(338) /* min */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 75),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(340) /* max */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 76),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(342) /* sign */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 77),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(344) /* abs */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 78),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(346) /* floor */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 79),+ (10 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(348) /* ceil */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 80),+ (13 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(350) /* round */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 81),+ (16 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(352) /* sqrt */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 82),+ (19 << 1) | (JS_PROP_NORMAL << 30),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 69) /* E */,+ JS_ROM_VALUE(1186),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(356) /* LN10 */,+ JS_ROM_VALUE(1188),+ (25 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(358) /* LN2 */,+ JS_ROM_VALUE(1190),+ (28 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(360) /* LOG2E */,+ JS_ROM_VALUE(1192),+ (31 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(362) /* LOG10E */,+ JS_ROM_VALUE(1194),+ (22 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(364) /* PI */,+ JS_ROM_VALUE(1196),+ (37 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(366) /* SQRT1_2 */,+ JS_ROM_VALUE(1198),+ (40 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(368) /* SQRT2 */,+ JS_ROM_VALUE(1200),+ (43 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(370) /* sin */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 83),+ (46 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(372) /* cos */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 84),+ (49 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(374) /* tan */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 85),+ (52 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(376) /* asin */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 86),+ (55 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(378) /* acos */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 87),+ (58 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(380) /* atan */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 88),+ (61 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(382) /* atan2 */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 89),+ (64 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(384) /* exp */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 90),+ (67 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(386) /* log */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 91),+ (70 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(388) /* pow */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 92),+ (73 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(390) /* random */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 93),+ (76 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(392) /* imul */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 94),+ (79 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(394) /* clz32 */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 95),+ (82 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(396) /* fround */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 96),+ (85 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(398) /* trunc */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 97),+ (88 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(400) /* log2 */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 98),+ (91 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(402) /* log10 */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 99),+ (94 << 1) | (JS_PROP_NORMAL << 30),+ /* class (offset=1312) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1202),+ -1,+ JS_NULL,+ JS_NULL,++ /* properties (offset=1317) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(406) /* now */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 101),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_DATE << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1327) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_DATE - 1) << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1334) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1317),+ 100,+ JS_ROM_VALUE(1327),+ JS_NULL,++ /* properties (offset=1339) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(410) /* parse */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 102),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(412) /* stringify */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 103),+ (3 << 1) | (JS_PROP_NORMAL << 30),+ /* class (offset=1349) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1339),+ -1,+ JS_NULL,+ JS_NULL,++ /* properties (offset=1354) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_REGEXP << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=1361) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 105),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 106),++ /* getset (offset=1364) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 107),+ JS_UNDEFINED,++ /* getset (offset=1367) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 108),+ JS_UNDEFINED,++ /* properties (offset=1370) */+ JS_VALUE_ARRAY_HEADER(24),+ 6 << 1, /* n_props */+ 3 << 1, /* hash_mask */+ 21 << 1,+ 15 << 1,+ 12 << 1,+ 18 << 1,+ JS_ROM_VALUE(417) /* lastIndex */,+ JS_ROM_VALUE(1361),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(426) /* source */,+ JS_ROM_VALUE(1364),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(431) /* flags */,+ JS_ROM_VALUE(1367),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(436) /* exec */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 109),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(438) /* test */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 110),+ (9 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_REGEXP - 1) << 1,+ (6 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1395) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1354),+ 104,+ JS_ROM_VALUE(1370),+ JS_NULL,++ /* properties (offset=1400) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=1407) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 112),+ JS_UNDEFINED,++ /* getset (offset=1410) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 113),+ JS_UNDEFINED,++ /* properties (offset=1413) */+ JS_VALUE_ARRAY_HEADER(21),+ 5 << 1, /* n_props */+ 3 << 1, /* hash_mask */+ 18 << 1,+ 12 << 1,+ 6 << 1,+ 9 << 1,+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 114),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(152) /* Error */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(440) /* message */,+ JS_ROM_VALUE(1407),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(445) /* stack */,+ JS_ROM_VALUE(1410),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_ERROR - 1) << 1,+ (15 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1435) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1400),+ 111,+ JS_ROM_VALUE(1413),+ JS_NULL,++ /* properties (offset=1440) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_EVAL_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1447) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(450) /* EvalError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_EVAL_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1457) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1440),+ 115,+ JS_ROM_VALUE(1447),+ JS_ROM_VALUE(1435),++ /* properties (offset=1462) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_RANGE_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1469) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(453) /* RangeError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_RANGE_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1479) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1462),+ 116,+ JS_ROM_VALUE(1469),+ JS_ROM_VALUE(1435),++ /* properties (offset=1484) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_REFERENCE_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1491) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(456) /* ReferenceError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_REFERENCE_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1501) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1484),+ 117,+ JS_ROM_VALUE(1491),+ JS_ROM_VALUE(1435),++ /* properties (offset=1506) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_SYNTAX_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1513) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(459) /* SyntaxError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_SYNTAX_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1523) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1506),+ 118,+ JS_ROM_VALUE(1513),+ JS_ROM_VALUE(1435),++ /* properties (offset=1528) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_TYPE_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1535) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(462) /* TypeError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_TYPE_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1545) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1528),+ 119,+ JS_ROM_VALUE(1535),+ JS_ROM_VALUE(1435),++ /* properties (offset=1550) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_URI_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1557) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(465) /* URIError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_URI_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1567) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1550),+ 120,+ JS_ROM_VALUE(1557),+ JS_ROM_VALUE(1435),++ /* properties (offset=1572) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_INTERNAL_ERROR << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1579) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(150) /* name */,+ JS_ROM_VALUE(468) /* InternalError */,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_INTERNAL_ERROR - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1589) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1572),+ 121,+ JS_ROM_VALUE(1579),+ JS_ROM_VALUE(1435),++ /* properties (offset=1594) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_ARRAY_BUFFER << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=1601) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 123),+ JS_UNDEFINED,++ /* properties (offset=1604) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(474) /* byteLength */,+ JS_ROM_VALUE(1601),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_ARRAY_BUFFER - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1614) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1594),+ 122,+ JS_ROM_VALUE(1604),+ JS_NULL,++ /* properties (offset=1619) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_TYPED_ARRAY << 1,+ (0 << 1) | (JS_PROP_SPECIAL << 30),+ /* getset (offset=1626) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 125),+ JS_UNDEFINED,++ /* getset (offset=1629) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 126),+ JS_UNDEFINED,++ /* getset (offset=1632) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 127),+ JS_UNDEFINED,++ /* getset (offset=1635) */+ JS_VALUE_ARRAY_HEADER(2),+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 128),+ JS_UNDEFINED,++ /* properties (offset=1638) */+ JS_VALUE_ARRAY_HEADER(37),+ 9 << 1, /* n_props */+ 7 << 1, /* hash_mask */+ 0 << 1,+ 31 << 1,+ 25 << 1,+ 28 << 1,+ 34 << 1,+ 0 << 1,+ 16 << 1,+ 0 << 1,+ JS_ROM_VALUE(136) /* length */,+ JS_ROM_VALUE(1626),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(474) /* byteLength */,+ JS_ROM_VALUE(1629),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(487) /* byteOffset */,+ JS_ROM_VALUE(1632),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(493) /* buffer */,+ JS_ROM_VALUE(1635),+ (0 << 1) | (JS_PROP_GETSET << 30),+ JS_ROM_VALUE(309) /* join */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 58),+ (19 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(99) /* toString */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 59),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(498) /* subarray */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 129),+ (13 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(128) /* set */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 130),+ (10 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_TYPED_ARRAY - 1) << 1,+ (22 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1676) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1619),+ 124,+ JS_ROM_VALUE(1638),+ JS_NULL,++ /* properties (offset=1681) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 1 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_UINT8C_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1691) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 1 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_UINT8C_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1701) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1681),+ 131,+ JS_ROM_VALUE(1691),+ JS_ROM_VALUE(1676),++ /* properties (offset=1706) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 1 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_INT8_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1716) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 1 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_INT8_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1726) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1706),+ 132,+ JS_ROM_VALUE(1716),+ JS_ROM_VALUE(1676),++ /* properties (offset=1731) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 1 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_UINT8_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1741) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 1 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_UINT8_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1751) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1731),+ 133,+ JS_ROM_VALUE(1741),+ JS_ROM_VALUE(1676),++ /* properties (offset=1756) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 2 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_INT16_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1766) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 2 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_INT16_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1776) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1756),+ 134,+ JS_ROM_VALUE(1766),+ JS_ROM_VALUE(1676),++ /* properties (offset=1781) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 2 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_UINT16_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1791) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 2 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_UINT16_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1801) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1781),+ 135,+ JS_ROM_VALUE(1791),+ JS_ROM_VALUE(1676),++ /* properties (offset=1806) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 4 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_INT32_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1816) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 4 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_INT32_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1826) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1806),+ 136,+ JS_ROM_VALUE(1816),+ JS_ROM_VALUE(1676),++ /* properties (offset=1831) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 4 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_UINT32_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1841) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 4 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_UINT32_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1851) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1831),+ 137,+ JS_ROM_VALUE(1841),+ JS_ROM_VALUE(1676),++ /* properties (offset=1856) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 4 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_FLOAT32_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1866) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 4 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_FLOAT32_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1876) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1856),+ 138,+ JS_ROM_VALUE(1866),+ JS_ROM_VALUE(1676),++ /* properties (offset=1881) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 8 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(130) /* prototype */,+ JS_CLASS_FLOAT64_ARRAY << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* properties (offset=1891) */+ JS_VALUE_ARRAY_HEADER(9),+ 2 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 6 << 1,+ JS_ROM_VALUE(501) /* BYTES_PER_ELEMENT */,+ 8 << 1,+ (0 << 1) | (JS_PROP_NORMAL << 30),+ JS_ROM_VALUE(133) /* constructor */,+ (uint32_t)(-JS_CLASS_FLOAT64_ARRAY - 1) << 1,+ (3 << 1) | (JS_PROP_SPECIAL << 30),+ /* class (offset=1901) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1881),+ 139,+ JS_ROM_VALUE(1891),+ JS_ROM_VALUE(1676),++ /* float64 (offset=1906) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x7ff0000000000000,++ /* float64 (offset=1908) */+ JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),+ 0x7ff8000000000000,++ /* properties (offset=1910) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(386) /* log */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 140),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ /* class (offset=1917) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1910),+ -1,+ JS_NULL,+ JS_NULL,++ /* properties (offset=1922) */+ JS_VALUE_ARRAY_HEADER(6),+ 1 << 1, /* n_props */+ 0 << 1, /* hash_mask */+ 3 << 1,+ JS_ROM_VALUE(406) /* now */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 141),+ (0 << 1) | (JS_PROP_NORMAL << 30),+ /* class (offset=1929) */+ JS_MB_HEADER_DEF(JS_MTAG_OBJECT),+ JS_ROM_VALUE(1922),+ -1,+ JS_NULL,+ JS_NULL,++ /* global object properties (offset=1934) */+ JS_VALUE_ARRAY_HEADER(88),+ JS_ROM_VALUE(163) /* Object */,+ JS_ROM_VALUE(826),+ JS_ROM_VALUE(181) /* Function */,+ JS_ROM_VALUE(878),+ JS_ROM_VALUE(202) /* Number */,+ JS_ROM_VALUE(965),+ JS_ROM_VALUE(242) /* Boolean */,+ JS_ROM_VALUE(984),+ JS_ROM_VALUE(244) /* String */,+ JS_ROM_VALUE(1083),+ JS_ROM_VALUE(301) /* Array */,+ JS_ROM_VALUE(1181),+ JS_ROM_VALUE(336) /* Math */,+ JS_ROM_VALUE(1312),+ JS_ROM_VALUE(404) /* Date */,+ JS_ROM_VALUE(1334),+ JS_ROM_VALUE(408) /* JSON */,+ JS_ROM_VALUE(1349),+ JS_ROM_VALUE(415) /* RegExp */,+ JS_ROM_VALUE(1395),+ JS_ROM_VALUE(152) /* Error */,+ JS_ROM_VALUE(1435),+ JS_ROM_VALUE(450) /* EvalError */,+ JS_ROM_VALUE(1457),+ JS_ROM_VALUE(453) /* RangeError */,+ JS_ROM_VALUE(1479),+ JS_ROM_VALUE(456) /* ReferenceError */,+ JS_ROM_VALUE(1501),+ JS_ROM_VALUE(459) /* SyntaxError */,+ JS_ROM_VALUE(1523),+ JS_ROM_VALUE(462) /* TypeError */,+ JS_ROM_VALUE(1545),+ JS_ROM_VALUE(465) /* URIError */,+ JS_ROM_VALUE(1567),+ JS_ROM_VALUE(468) /* InternalError */,+ JS_ROM_VALUE(1589),+ JS_ROM_VALUE(471) /* ArrayBuffer */,+ JS_ROM_VALUE(1614),+ JS_ROM_VALUE(480) /* Uint8ClampedArray */,+ JS_ROM_VALUE(1701),+ JS_ROM_VALUE(505) /* Int8Array */,+ JS_ROM_VALUE(1726),+ JS_ROM_VALUE(508) /* Uint8Array */,+ JS_ROM_VALUE(1751),+ JS_ROM_VALUE(511) /* Int16Array */,+ JS_ROM_VALUE(1776),+ JS_ROM_VALUE(514) /* Uint16Array */,+ JS_ROM_VALUE(1801),+ JS_ROM_VALUE(517) /* Int32Array */,+ JS_ROM_VALUE(1826),+ JS_ROM_VALUE(520) /* Uint32Array */,+ JS_ROM_VALUE(1851),+ JS_ROM_VALUE(523) /* Float32Array */,+ JS_ROM_VALUE(1876),+ JS_ROM_VALUE(526) /* Float64Array */,+ JS_ROM_VALUE(1901),+ JS_ROM_VALUE(204) /* parseInt */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 19),+ JS_ROM_VALUE(207) /* parseFloat */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 20),+ JS_ROM_VALUE(119) /* eval */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 142),+ JS_ROM_VALUE(529) /* isNaN */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 143),+ JS_ROM_VALUE(531) /* isFinite */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 144),+ JS_ROM_VALUE(144) /* Infinity */,+ JS_ROM_VALUE(1906),+ JS_ROM_VALUE(142) /* NaN */,+ JS_ROM_VALUE(1908),+ JS_ROM_VALUE(108) /* undefined */,+ JS_UNDEFINED,+ JS_ROM_VALUE(534) /* globalThis */,+ JS_NULL,+ JS_ROM_VALUE(537) /* console */,+ JS_ROM_VALUE(1917),+ JS_ROM_VALUE(539) /* performance */,+ JS_ROM_VALUE(1929),+ JS_ROM_VALUE(542) /* print */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 145),+ JS_ROM_VALUE(544) /* gc */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 146),+ JS_ROM_VALUE(546) /* load */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 147),+ JS_ROM_VALUE(548) /* setTimeout */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 148),+ JS_ROM_VALUE(551) /* clearTimeout */,+ JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 149),+};++static const JSCFunctionDef js_c_function_table[] = {+ { { .generic_params = js_function_bound },+ JS_ROM_VALUE(161) /* bound */,+ JS_CFUNC_generic_params, 0, 0 },+ { { .constructor = js_object_constructor },+ JS_ROM_VALUE(163) /* Object */,+ JS_CFUNC_constructor, 1, JS_CLASS_OBJECT },+ { { .generic = js_object_defineProperty },+ JS_ROM_VALUE(165) /* defineProperty */,+ JS_CFUNC_generic, 3, 0 },+ { { .generic = js_object_getPrototypeOf },+ JS_ROM_VALUE(168) /* getPrototypeOf */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_object_setPrototypeOf },+ JS_ROM_VALUE(171) /* setPrototypeOf */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_object_create },+ JS_ROM_VALUE(174) /* create */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_object_keys },+ JS_ROM_VALUE(176) /* keys */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_object_hasOwnProperty },+ JS_ROM_VALUE(178) /* hasOwnProperty */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_object_toString },+ JS_ROM_VALUE(99) /* toString */,+ JS_CFUNC_generic, 0, 0 },+ { { .constructor = js_function_constructor },+ JS_ROM_VALUE(181) /* Function */,+ JS_CFUNC_constructor, 1, JS_CLASS_CLOSURE },+ { { .generic = js_function_get_prototype },+ JS_ROM_VALUE(184) /* get prototype */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_function_set_prototype },+ JS_ROM_VALUE(187) /* set prototype */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic_magic = js_function_get_length_name },+ JS_ROM_VALUE(196) /* get length */,+ JS_CFUNC_generic_magic, 0, 0 },+ { { .generic_magic = js_function_get_length_name },+ JS_ROM_VALUE(199) /* get name */,+ JS_CFUNC_generic_magic, 0, 1 },+ { { .generic = js_function_call },+ JS_ROM_VALUE(190) /* call */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_function_apply },+ JS_ROM_VALUE(192) /* apply */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_function_bind },+ JS_ROM_VALUE(194) /* bind */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_function_toString },+ JS_ROM_VALUE(99) /* toString */,+ JS_CFUNC_generic, 0, 0 },+ { { .constructor = js_number_constructor },+ JS_ROM_VALUE(202) /* Number */,+ JS_CFUNC_constructor, 1, JS_CLASS_NUMBER },+ { { .generic = js_number_parseInt },+ JS_ROM_VALUE(204) /* parseInt */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_number_parseFloat },+ JS_ROM_VALUE(207) /* parseFloat */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_number_toExponential },+ JS_ROM_VALUE(234) /* toExponential */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_number_toFixed },+ JS_ROM_VALUE(237) /* toFixed */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_number_toPrecision },+ JS_ROM_VALUE(239) /* toPrecision */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_number_toString },+ JS_ROM_VALUE(99) /* toString */,+ JS_CFUNC_generic, 1, 0 },+ { { .constructor = js_boolean_constructor },+ JS_ROM_VALUE(242) /* Boolean */,+ JS_CFUNC_constructor, 1, JS_CLASS_BOOLEAN },+ { { .constructor = js_string_constructor },+ JS_ROM_VALUE(244) /* String */,+ JS_CFUNC_constructor, 1, JS_CLASS_STRING },+ { { .generic_magic = js_string_fromCharCode },+ JS_ROM_VALUE(246) /* fromCharCode */,+ JS_CFUNC_generic_magic, 1, 0 },+ { { .generic_magic = js_string_fromCharCode },+ JS_ROM_VALUE(249) /* fromCodePoint */,+ JS_CFUNC_generic_magic, 1, 1 },+ { { .generic = js_string_get_length },+ JS_ROM_VALUE(196) /* get length */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_string_set_length },+ JS_ROM_VALUE(252) /* set length */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic_magic = js_string_charAt },+ JS_ROM_VALUE(255) /* charAt */,+ JS_CFUNC_generic_magic, 1, magic_charAt },+ { { .generic_magic = js_string_charAt },+ JS_ROM_VALUE(257) /* charCodeAt */,+ JS_CFUNC_generic_magic, 1, magic_charCodeAt },+ { { .generic_magic = js_string_charAt },+ JS_ROM_VALUE(260) /* codePointAt */,+ JS_CFUNC_generic_magic, 1, magic_codePointAt },+ { { .generic = js_string_slice },+ JS_ROM_VALUE(263) /* slice */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_string_substring },+ JS_ROM_VALUE(265) /* substring */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_string_concat },+ JS_ROM_VALUE(268) /* concat */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic_magic = js_string_indexOf },+ JS_ROM_VALUE(270) /* indexOf */,+ JS_CFUNC_generic_magic, 1, 0 },+ { { .generic_magic = js_string_indexOf },+ JS_ROM_VALUE(272) /* lastIndexOf */,+ JS_CFUNC_generic_magic, 1, 1 },+ { { .generic = js_string_match },+ JS_ROM_VALUE(275) /* match */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic_magic = js_string_replace },+ JS_ROM_VALUE(277) /* replace */,+ JS_CFUNC_generic_magic, 2, 0 },+ { { .generic_magic = js_string_replace },+ JS_ROM_VALUE(279) /* replaceAll */,+ JS_CFUNC_generic_magic, 2, 1 },+ { { .generic = js_string_search },+ JS_ROM_VALUE(282) /* search */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_string_split },+ JS_ROM_VALUE(284) /* split */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic_magic = js_string_toLowerCase },+ JS_ROM_VALUE(286) /* toLowerCase */,+ JS_CFUNC_generic_magic, 0, 1 },+ { { .generic_magic = js_string_toLowerCase },+ JS_ROM_VALUE(289) /* toUpperCase */,+ JS_CFUNC_generic_magic, 0, 0 },+ { { .generic_magic = js_string_trim },+ JS_ROM_VALUE(292) /* trim */,+ JS_CFUNC_generic_magic, 0, 3 },+ { { .generic_magic = js_string_trim },+ JS_ROM_VALUE(294) /* trimEnd */,+ JS_CFUNC_generic_magic, 0, 2 },+ { { .generic_magic = js_string_trim },+ JS_ROM_VALUE(296) /* trimStart */,+ JS_CFUNC_generic_magic, 0, 1 },+ { { .generic = js_string_toString },+ JS_ROM_VALUE(99) /* toString */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_string_repeat },+ JS_ROM_VALUE(299) /* repeat */,+ JS_CFUNC_generic, 1, 0 },+ { { .constructor = js_array_constructor },+ JS_ROM_VALUE(301) /* Array */,+ JS_CFUNC_constructor, 1, JS_CLASS_ARRAY },+ { { .generic = js_array_isArray },+ JS_ROM_VALUE(303) /* isArray */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_array_get_length },+ JS_ROM_VALUE(196) /* get length */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_array_set_length },+ JS_ROM_VALUE(252) /* set length */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_array_concat },+ JS_ROM_VALUE(268) /* concat */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic_magic = js_array_push },+ JS_ROM_VALUE(305) /* push */,+ JS_CFUNC_generic_magic, 1, 0 },+ { { .generic = js_array_pop },+ JS_ROM_VALUE(307) /* pop */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_array_join },+ JS_ROM_VALUE(309) /* join */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_array_toString },+ JS_ROM_VALUE(99) /* toString */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_array_reverse },+ JS_ROM_VALUE(311) /* reverse */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_array_shift },+ JS_ROM_VALUE(313) /* shift */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_array_slice },+ JS_ROM_VALUE(263) /* slice */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_array_splice },+ JS_ROM_VALUE(315) /* splice */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic_magic = js_array_push },+ JS_ROM_VALUE(317) /* unshift */,+ JS_CFUNC_generic_magic, 1, 1 },+ { { .generic_magic = js_array_indexOf },+ JS_ROM_VALUE(270) /* indexOf */,+ JS_CFUNC_generic_magic, 1, 0 },+ { { .generic_magic = js_array_indexOf },+ JS_ROM_VALUE(272) /* lastIndexOf */,+ JS_CFUNC_generic_magic, 1, 1 },+ { { .generic_magic = js_array_every },+ JS_ROM_VALUE(319) /* every */,+ JS_CFUNC_generic_magic, 1, js_special_every },+ { { .generic_magic = js_array_every },+ JS_ROM_VALUE(321) /* some */,+ JS_CFUNC_generic_magic, 1, js_special_some },+ { { .generic_magic = js_array_every },+ JS_ROM_VALUE(323) /* forEach */,+ JS_CFUNC_generic_magic, 1, js_special_forEach },+ { { .generic_magic = js_array_every },+ JS_ROM_VALUE(325) /* map */,+ JS_CFUNC_generic_magic, 1, js_special_map },+ { { .generic_magic = js_array_every },+ JS_ROM_VALUE(327) /* filter */,+ JS_CFUNC_generic_magic, 1, js_special_filter },+ { { .generic_magic = js_array_reduce },+ JS_ROM_VALUE(329) /* reduce */,+ JS_CFUNC_generic_magic, 1, js_special_reduce },+ { { .generic_magic = js_array_reduce },+ JS_ROM_VALUE(331) /* reduceRight */,+ JS_CFUNC_generic_magic, 1, js_special_reduceRight },+ { { .generic = js_array_sort },+ JS_ROM_VALUE(334) /* sort */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic_magic = js_math_min_max },+ JS_ROM_VALUE(338) /* min */,+ JS_CFUNC_generic_magic, 2, 0 },+ { { .generic_magic = js_math_min_max },+ JS_ROM_VALUE(340) /* max */,+ JS_CFUNC_generic_magic, 2, 1 },+ { { .f_f = js_math_sign },+ JS_ROM_VALUE(342) /* sign */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_fabs },+ JS_ROM_VALUE(344) /* abs */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_floor },+ JS_ROM_VALUE(346) /* floor */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_ceil },+ JS_ROM_VALUE(348) /* ceil */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_round_inf },+ JS_ROM_VALUE(350) /* round */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_sqrt },+ JS_ROM_VALUE(352) /* sqrt */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_sin },+ JS_ROM_VALUE(370) /* sin */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_cos },+ JS_ROM_VALUE(372) /* cos */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_tan },+ JS_ROM_VALUE(374) /* tan */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_asin },+ JS_ROM_VALUE(376) /* asin */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_acos },+ JS_ROM_VALUE(378) /* acos */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_atan },+ JS_ROM_VALUE(380) /* atan */,+ JS_CFUNC_f_f, 1, 0 },+ { { .generic = js_math_atan2 },+ JS_ROM_VALUE(382) /* atan2 */,+ JS_CFUNC_generic, 2, 0 },+ { { .f_f = js_exp },+ JS_ROM_VALUE(384) /* exp */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_log },+ JS_ROM_VALUE(386) /* log */,+ JS_CFUNC_f_f, 1, 0 },+ { { .generic = js_math_pow },+ JS_ROM_VALUE(388) /* pow */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_math_random },+ JS_ROM_VALUE(390) /* random */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_math_imul },+ JS_ROM_VALUE(392) /* imul */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_math_clz32 },+ JS_ROM_VALUE(394) /* clz32 */,+ JS_CFUNC_generic, 1, 0 },+ { { .f_f = js_math_fround },+ JS_ROM_VALUE(396) /* fround */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_trunc },+ JS_ROM_VALUE(398) /* trunc */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_log2 },+ JS_ROM_VALUE(400) /* log2 */,+ JS_CFUNC_f_f, 1, 0 },+ { { .f_f = js_log10 },+ JS_ROM_VALUE(402) /* log10 */,+ JS_CFUNC_f_f, 1, 0 },+ { { .constructor = js_date_constructor },+ JS_ROM_VALUE(404) /* Date */,+ JS_CFUNC_constructor, 7, JS_CLASS_DATE },+ { { .generic = js_date_now },+ JS_ROM_VALUE(406) /* now */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_json_parse },+ JS_ROM_VALUE(410) /* parse */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_json_stringify },+ JS_ROM_VALUE(412) /* stringify */,+ JS_CFUNC_generic, 3, 0 },+ { { .constructor = js_regexp_constructor },+ JS_ROM_VALUE(415) /* RegExp */,+ JS_CFUNC_constructor, 2, JS_CLASS_REGEXP },+ { { .generic = js_regexp_get_lastIndex },+ JS_ROM_VALUE(420) /* get lastIndex */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_regexp_set_lastIndex },+ JS_ROM_VALUE(423) /* set lastIndex */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_regexp_get_source },+ JS_ROM_VALUE(428) /* get source */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_regexp_get_flags },+ JS_ROM_VALUE(433) /* get flags */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic_magic = js_regexp_exec },+ JS_ROM_VALUE(436) /* exec */,+ JS_CFUNC_generic_magic, 1, 0 },+ { { .generic_magic = js_regexp_exec },+ JS_ROM_VALUE(438) /* test */,+ JS_CFUNC_generic_magic, 1, 1 },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(152) /* Error */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_ERROR },+ { { .generic_magic = js_error_get_message },+ JS_ROM_VALUE(442) /* get message */,+ JS_CFUNC_generic_magic, 0, 0 },+ { { .generic_magic = js_error_get_message },+ JS_ROM_VALUE(447) /* get stack */,+ JS_CFUNC_generic_magic, 0, 1 },+ { { .generic = js_error_toString },+ JS_ROM_VALUE(99) /* toString */,+ JS_CFUNC_generic, 0, 0 },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(450) /* EvalError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_EVAL_ERROR },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(453) /* RangeError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_RANGE_ERROR },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(456) /* ReferenceError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_REFERENCE_ERROR },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(459) /* SyntaxError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_SYNTAX_ERROR },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(462) /* TypeError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_TYPE_ERROR },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(465) /* URIError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_URI_ERROR },+ { { .constructor_magic = js_error_constructor },+ JS_ROM_VALUE(468) /* InternalError */,+ JS_CFUNC_constructor_magic, 1, JS_CLASS_INTERNAL_ERROR },+ { { .constructor = js_array_buffer_constructor },+ JS_ROM_VALUE(471) /* ArrayBuffer */,+ JS_CFUNC_constructor, 1, JS_CLASS_ARRAY_BUFFER },+ { { .generic = js_array_buffer_get_byteLength },+ JS_ROM_VALUE(477) /* get byteLength */,+ JS_CFUNC_generic, 0, 0 },+ { { .constructor = js_typed_array_base_constructor },+ JS_ROM_VALUE(484) /* TypedArray */,+ JS_CFUNC_constructor, 0, JS_CLASS_TYPED_ARRAY },+ { { .generic_magic = js_typed_array_get_length },+ JS_ROM_VALUE(196) /* get length */,+ JS_CFUNC_generic_magic, 0, 0 },+ { { .generic_magic = js_typed_array_get_length },+ JS_ROM_VALUE(477) /* get byteLength */,+ JS_CFUNC_generic_magic, 0, 1 },+ { { .generic_magic = js_typed_array_get_length },+ JS_ROM_VALUE(490) /* get byteOffset */,+ JS_CFUNC_generic_magic, 0, 2 },+ { { .generic_magic = js_typed_array_get_length },+ JS_ROM_VALUE(495) /* get buffer */,+ JS_CFUNC_generic_magic, 0, 3 },+ { { .generic = js_typed_array_subarray },+ JS_ROM_VALUE(498) /* subarray */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_typed_array_set },+ JS_ROM_VALUE(128) /* set */,+ JS_CFUNC_generic, 1, 0 },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(480) /* Uint8ClampedArray */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT8C_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(505) /* Int8Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_INT8_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(508) /* Uint8Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT8_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(511) /* Int16Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_INT16_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(514) /* Uint16Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT16_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(517) /* Int32Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_INT32_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(520) /* Uint32Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT32_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(523) /* Float32Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_FLOAT32_ARRAY },+ { { .constructor_magic = js_typed_array_constructor },+ JS_ROM_VALUE(526) /* Float64Array */,+ JS_CFUNC_constructor_magic, 3, JS_CLASS_FLOAT64_ARRAY },+ { { .generic = js_print },+ JS_ROM_VALUE(386) /* log */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_performance_now },+ JS_ROM_VALUE(406) /* now */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_global_eval },+ JS_ROM_VALUE(119) /* eval */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_global_isNaN },+ JS_ROM_VALUE(529) /* isNaN */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_global_isFinite },+ JS_ROM_VALUE(531) /* isFinite */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_print },+ JS_ROM_VALUE(542) /* print */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_gc },+ JS_ROM_VALUE(544) /* gc */,+ JS_CFUNC_generic, 0, 0 },+ { { .generic = js_load },+ JS_ROM_VALUE(546) /* load */,+ JS_CFUNC_generic, 1, 0 },+ { { .generic = js_setTimeout },+ JS_ROM_VALUE(548) /* setTimeout */,+ JS_CFUNC_generic, 2, 0 },+ { { .generic = js_clearTimeout },+ JS_ROM_VALUE(551) /* clearTimeout */,+ JS_CFUNC_generic, 1, 0 },+};++#ifndef JS_CLASS_COUNT+#define JS_CLASS_COUNT JS_CLASS_USER /* total number of classes */+#endif++static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = {+};++const JSSTDLibraryDef js_stdlib = {+ js_stdlib_table,+ js_c_function_table,+ js_c_finalizer_table,+ 2023,+ 64,+ 554,+ 1934,+ JS_CLASS_COUNT,+};+
+ mquickjs/mquickjs.c view
@@ -0,0 +1,18320 @@+/*+ * Micro QuickJS Javascript Engine+ *+ * Copyright (c) 2017-2025 Fabrice Bellard+ * Copyright (c) 2017-2025 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 <math.h>+#include <setjmp.h>++#include "cutils.h"+#include "dtoa.h"+#include "mquickjs_priv.h"++/*+ TODO:+ - regexp: better error position info+ - use a specific MTAG for short functions instead of an immediate value+ - use hash table for atoms+ - set the length accessors as non configurable so that the+ 'get_length' instruction optimizations are always safe.+ - memory:+ - fix stack_bottom logic+ - launch gc at regular intervals+ - only launch compaction when needed (handle free blocks in malloc())+ - avoid pass to rehash the properties+ - ensure no undefined bytes (e.g. at end of JSString) in+ saved bytecode ?+ - reduced memory usage:+ - reduce JSFunctionBytecode size (remove source_pos)+ - do not explicitly store function names for get/set/bound+ - use JSSTDLibraryDef fields instead of copying them to JSContext ?+*/++#define __exception __attribute__((warn_unused_result))++#define JS_STACK_SLACK 16 /* additional free space on the stack */+/* min free size in bytes between heap_free and the bottom of the stack */+#define JS_MIN_FREE_SIZE 512+/* minimum free size in bytes to create the out of memory object */+#define JS_MIN_CRITICAL_FREE_SIZE (JS_MIN_FREE_SIZE - 256)+#define JS_MAX_LOCAL_VARS 65535+#define JS_MAX_FUNC_STACK_SIZE 65535+#define JS_MAX_ARGC 65535+/* maximum number of recursing JS_Call() */+#define JS_MAX_CALL_RECURSE 8+++#define JS_VALUE_IS_BOTH_INT(a, b) ((((a) | (b)) & 1) == 0)+#define JS_VALUE_IS_BOTH_SHORT_FLOAT(a, b) (((((a) - JS_TAG_SHORT_FLOAT) | ((b) - JS_TAG_SHORT_FLOAT)) & 7) == 0)++static __maybe_unused const char *js_mtag_name[JS_MTAG_COUNT] = {+ "free",+ "object",+ "float64",+ "string",+ "func_bytecode",+ "value_array",+ "byte_array",+ "varref",+};++/* function call flags (max 31 bits) */+#define FRAME_CF_ARGC_MASK 0xffff+/* FRAME_CF_CTOR */+#define FRAME_CF_POP_RET (1 << 17) /* pop the return value */+#define FRAME_CF_PC_ADD1 (1 << 18) /* increment the PC by 1 instead of 3 */++#define JS_MB_PAD(n) (JSW * 8 - (n))++typedef struct {+ JS_MB_HEADER;+ JSWord dummy: JS_MB_PAD(JS_MTAG_BITS);+} JSMemBlockHeader;++typedef struct {+ JS_MB_HEADER;+ /* in JSWords excluding the header. Free blocks of JSW bytes+ are only generated by js_shrink() and may not be always+ compacted */+ JSWord size: JS_MB_PAD(JS_MTAG_BITS);+} JSFreeBlock;++#if JSW == 8+#define JS_STRING_LEN_MAX 0x7ffffffe+#else+#define JS_STRING_LEN_MAX ((1 << (32 - JS_MTAG_BITS - 3)) - 1)+#endif++typedef struct {+ JS_MB_HEADER;+ JSWord is_unique: 1;+ JSWord is_ascii: 1;+ /* true if the string content represents a number, only meaningful+ is is_unique = true */+ JSWord is_numeric: 1;+ JSWord len: JS_MB_PAD(JS_MTAG_BITS + 3);+ uint8_t buf[];+} JSString;++typedef struct {+ JSWord string_buf[sizeof(JSString) / sizeof(JSWord)]; /* for JSString */+ uint8_t buf[5];+} JSStringCharBuf;++#define JS_BYTE_ARRAY_SIZE_MAX ((1 << (32 - JS_MTAG_BITS)) - 1)++typedef struct {+ JS_MB_HEADER;+ JSWord size: JS_MB_PAD(JS_MTAG_BITS);+ uint8_t buf[];+} JSByteArray;++#define JS_VALUE_ARRAY_SIZE_MAX ((1 << (32 - JS_MTAG_BITS)) - 1)++typedef struct {+ JS_MB_HEADER;+ JSWord size: JS_MB_PAD(JS_MTAG_BITS);+ JSValue arr[];+} JSValueArray;++typedef struct JSVarRef {+ JS_MB_HEADER;+ JSWord is_detached : 1;+ JSWord dummy: JS_MB_PAD(JS_MTAG_BITS + 1);+ union {+ JSValue value; /* is_detached = true */+ struct {+ JSValue next; /* is_detached = false: JS_NULL or JSVarRef,+ must be at the same address as 'value' */+ JSValue *pvalue;+ };+ } u;+} JSVarRef;++typedef struct {+ JS_MB_HEADER;+ JSWord dummy: JS_MB_PAD(JS_MTAG_BITS);+#ifdef JS_PTR64+ struct {+ double dval;+ } u;+#else+ /* unaligned 64 bit access in 32-bit mode */+ struct __attribute__((packed)) {+ double dval;+ } u;+#endif+} JSFloat64;++typedef struct JSROMClass {+ JS_MB_HEADER;+ JSWord dummy: JS_MB_PAD(JS_MTAG_BITS);+ JSValue props;+ int32_t ctor_idx; /* -1 if defining a normal object */+ JSValue proto_props;+ JSValue parent_class; /* JSROMClass or JS_NULL */+} JSROMClass;++#define N_ROM_ATOM_TABLES_MAX 2++/* 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++#define JS_STRING_POS_CACHE_SIZE 2+#define JS_STRING_POS_CACHE_MIN_LEN 16 ++typedef enum {+ POS_TYPE_UTF8,+ POS_TYPE_UTF16,+} StringPosTypeEnum;++typedef struct {+ JSValue str; /* JS_NULL or weak reference to a JSString. It+ contains at least JS_STRING_POS_CACHE_MIN_LEN+ bytes and is a non ascii string */+ uint32_t str_pos[2]; /* 0 = UTF-8 pos (in bytes), 1 = UTF-16 pos */+} JSStringPosCacheEntry;++struct JSContext {+ /* memory map:+ Stack+ Free area+ Heap+ JSContext+ */+ uint8_t *heap_base;+ uint8_t *heap_free; /* first free area */+ uint8_t *stack_top;+ JSValue *stack_bottom; /* sp must always be higher than stack_bottom */+ JSValue *sp; /* current stack pointer */+ JSValue *fp; /* current frame pointer, stack_top if none */+ uint32_t min_free_size; /* min free size between heap_free and the+ bottom of the stack */+ BOOL in_out_of_memory : 8; /* != 0 if generating the out of memory object */+ uint8_t n_rom_atom_tables;+ uint8_t string_pos_cache_counter; /* used for string_pos_cache[] update */+ uint16_t class_count; /* number of classes including user classes */+ int16_t interrupt_counter;+ BOOL current_exception_is_uncatchable : 8;+ struct JSParseState *parse_state; /* != NULL during JS_Eval() */+ int unique_strings_len;+ int js_call_rec_count; /* number of recursing JS_Call() */+ JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */+ JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */+ const JSWord *atom_table; /* constant atom table */+ /* 'n_rom_atom_tables' atom tables from code loaded from rom */+ const JSValueArray *rom_atom_tables[N_ROM_ATOM_TABLES_MAX];+ const JSCFunctionDef *c_function_table;+ const JSCFinalizer *c_finalizer_table;+ uint64_t random_state;+ JSInterruptHandler *interrupt_handler;+ JSWriteFunc *write_func; /* for the various dump functions */+ void *opaque;+ JSValue *class_obj; /* same as class_proto + class_count */+ JSStringPosCacheEntry string_pos_cache[JS_STRING_POS_CACHE_SIZE];+ + /* must only contain JSValue from this point (see JS_GC()) */+ JSValue unique_strings; /* JSValueArray of sorted strings or JS_NULL */+ + JSValue current_exception; /* currently pending exception, must+ come after unique_strings */+#ifdef DEBUG_GC+ JSValue dummy_block; /* dummy memory block near the start of the memory */+#endif+ JSValue empty_props; /* empty prop list, for objects with no properties */+ JSValue global_obj;+ JSValue minus_zero; /* minus zero float64 value */+ JSValue class_proto[]; /* prototype for each class (class_count+ element, then class_count elements for+ class_obj */+};++typedef enum {+ JS_VARREF_KIND_ARG, /* var_idx is an argument of the parent function */+ JS_VARREF_KIND_VAR, /* var_idx is a local variable of the parent function */+ JS_VARREF_KIND_VAR_REF, /* var_idx is a var ref of the parent function */+ JS_VARREF_KIND_GLOBAL, /* to debug */+} JSVarRefKindEnum;++typedef struct JSObject JSObject;++typedef struct {+ /* string, short integer or JS_UNINITIALIZED if no property. If+ the last property is uninitialized, hash_next = 2 *+ first_free. */+ JSValue key;+ /* JS_PROP_GETSET: JSValueArray of two elements+ JS_PROP_VARREF: JSVarRef */+ JSValue value;+ /* XXX: when JSW = 8, could use 32 bits for hash_next (faster) */+ uint32_t hash_next : 30; /* low bit at zero */+ uint32_t prop_type : 2;+} JSProperty;++typedef struct {+ JSValue func_bytecode; /* JSFunctionBytecode */+ JSValue var_refs[]; /* JSValueArray */+} JSClosureData;++typedef struct {+ uint32_t idx;+ JSValue params; /* optional associated parameters */+} JSCFunctionData;+ +typedef struct {+ JSValue tab; /* JS_NULL or JSValueArray */+ uint32_t len; /* maximum value: 2^30-1 */+} JSArrayData;++typedef struct {+ JSValue message; /* string or JS_NULL */+ JSValue stack; /* string or JS_NULL */+} JSErrorData;+ +typedef struct {+ JSValue byte_buffer; /* JSByteBuffer */+} JSArrayBuffer;++typedef struct {+ JSValue buffer; /* corresponding array buffer */+ uint32_t len; /* in elements */+ uint32_t offset; /* in elements */+} JSTypedArray;++typedef struct {+ JSValue source;+ JSValue byte_code;+ int last_index;+} JSRegExp;++typedef struct {+ void *opaque;+} JSObjectUserData;++struct JSObject {+ JS_MB_HEADER;+ JSWord class_id: 8;+ JSWord extra_size: JS_MB_PAD(JS_MTAG_BITS + 8); /* object additional size, in JSValue */++ JSValue proto; /* JSObject or JS_NULL */+ /* JSValueArray. structure:+ prop_count (number of properties excluding deleted ones)+ hash_mask (= hash_size - 1)+ hash_table[hash_size] (0 = end of list or offset in array)+ JSProperty props[]+ */+ JSValue props;+ /* number of additional fields depends on the object */+ union {+ JSClosureData closure;+ JSCFunctionData cfunc;+ JSArrayData array;+ JSErrorData error;+ JSArrayBuffer array_buffer;+ JSTypedArray typed_array;+ JSRegExp regexp;+ JSObjectUserData user;+ } u;+};++typedef struct JSFunctionBytecode {+ JS_MB_HEADER;+ JSWord has_arguments : 1; /* only used during parsing */+ JSWord has_local_func_name : 1; /* only used during parsing */+ JSWord has_column : 1; /* column debug info is present */+ /* during parse: variable index + 1 of hoisted function, 0 otherwise */+ JSWord arg_count : 16; + JSWord dummy: JS_MB_PAD(JS_MTAG_BITS + 3 + 16);++ JSValue func_name; /* JS_NULL if anonymous function */+ JSValue byte_code; /* JS_NULL if the function is not parsed yet */+ JSValue cpool; /* constant pool */+ JSValue vars; /* only for debug */+ JSValue ext_vars; /* records of (var_name, var_kind (2 bits) var_idx (16 bits)) */+ uint16_t stack_size; /* maximum stack size */+ uint16_t ext_vars_len; /* XXX: only used during parsing */+ JSValue filename; /* filename in which the function is defined */+ JSValue pc2line; /* JSByteArray or JS_NULL if not initialized */+ uint32_t source_pos; /* only used during parsing (XXX: shrink) */+} JSFunctionBytecode;++static JSValue js_resize_value_array(JSContext *ctx, JSValue val, int new_size);+static int get_mblock_size(const void *ptr);+static JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, int class_id, int extra_size);+static void js_shrink_byte_array(JSContext *ctx, JSValue *pval, int new_size);+static void build_backtrace(JSContext *ctx, JSValue error_obj,+ const char *filename, int line_num, int col_num, int skip_level);+static JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val);+static JSByteArray *js_alloc_byte_array(JSContext *ctx, int size);+static JSValue js_new_c_function_proto(JSContext *ctx, int func_idx, JSValue proto, BOOL has_params,+ JSValue params);+static int JS_ToUint8Clamp(JSContext *ctx, int *pres, JSValue val);+static JSValue js_set_prototype_internal(JSContext *ctx, JSValue obj, JSValue proto);+static JSValue js_resize_byte_array(JSContext *ctx, JSValue val, int new_size);+static JSValueArray *js_alloc_props(JSContext *ctx, int n);++typedef enum OPCodeFormat {+#define FMT(f) OP_FMT_ ## f,+#define DEF(id, size, n_pop, n_push, f)+#include "mquickjs_opcode.h"+#undef DEF+#undef FMT+} OPCodeFormat;++typedef 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 "mquickjs_opcode.h"+#undef def+#undef DEF+#undef FMT+ OP_COUNT,+} OPCodeEnum;++typedef struct {+#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_pusch items */+ uint8_t n_pop;+ uint8_t n_push;+ uint8_t fmt;+} JSOpCode;++static __maybe_unused const JSOpCode opcode_info[OP_COUNT] = {+#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 "mquickjs_opcode.h"+#undef DEF+#undef FMT+};++#include "mquickjs_atom.h"++JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref)+{+ ref->prev = ctx->top_gc_ref;+ ctx->top_gc_ref = ref;+ ref->val = JS_UNDEFINED;+ return &ref->val;+}++JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref)+{+ ctx->top_gc_ref = ref->prev;+ return ref->val;+}++JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref)+{+ ref->prev = ctx->last_gc_ref;+ ctx->last_gc_ref = ref;+ ref->val = JS_UNDEFINED;+ return &ref->val;+}++void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref)+{+ JSGCRef **pref, *ref1;+ pref = &ctx->last_gc_ref;+ for(;;) {+ ref1 = *pref;+ if (ref1 == NULL)+ abort();+ if (ref1 == ref) {+ *pref = ref1->prev;+ break;+ }+ pref = &ref1->prev;+ }+}++#undef JS_PUSH_VALUE+#undef JS_POP_VALUE++#define JS_PUSH_VALUE(ctx, v) do { \+ v ## _ref.prev = ctx->top_gc_ref; \+ ctx->top_gc_ref = &v ## _ref; \+ v ## _ref.val = v; \+ } while (0)+ +#define JS_POP_VALUE(ctx, v) do { \+ v = v ## _ref.val; \+ ctx->top_gc_ref = v ## _ref.prev; \+ } while (0)++static JSValue js_get_atom(JSContext *ctx, int a)+{+ return JS_VALUE_FROM_PTR(&ctx->atom_table[a]);+}++static force_inline JSValue JS_NewTailCall(int val)+{+ return JS_VALUE_MAKE_SPECIAL(JS_TAG_EXCEPTION, JS_EX_CALL + val);+}++static inline JS_BOOL JS_IsExceptionOrTailCall(JSValue v)+{+ return JS_VALUE_GET_SPECIAL_TAG(v) == JS_TAG_EXCEPTION;+}++static int js_get_mtag(void *ptr)+{+ return ((JSMemBlockHeader *)ptr)->mtag;+}++static int check_free_mem(JSContext *ctx, JSValue *stack_bottom, uint32_t size)+{+#ifdef DEBUG_GC+ assert(ctx->sp >= stack_bottom);+ /* don't start the GC before dummy_block is allocated */+ if (JS_IsPtr(ctx->dummy_block)) {+ JS_GC(ctx);+ }+#endif+ if (((uint8_t *)stack_bottom - ctx->heap_free) < size + ctx->min_free_size) {+ JS_GC(ctx);+ if (((uint8_t *)stack_bottom - ctx->heap_free) < size + ctx->min_free_size) {+ JS_ThrowOutOfMemory(ctx);+ return -1;+ }+ }+ return 0;+}++/* check that 'len' values can be pushed on the stack. Return 0 if OK,+ -1 if not enough space. May trigger a GC(). */+int JS_StackCheck(JSContext *ctx, uint32_t len)+{+ JSValue *new_stack_bottom;++ len += JS_STACK_SLACK;+ new_stack_bottom = ctx->sp - len;+ if (check_free_mem(ctx, new_stack_bottom, len * sizeof(JSValue)))+ return -1;+ ctx->stack_bottom = new_stack_bottom;+ return 0;+}++static void *js_malloc(JSContext *ctx, uint32_t size, int mtag)+{+ JSMemBlockHeader *p;++ if (size == 0)+ return NULL;+ size = (size + JSW - 1) & ~(JSW - 1);++ if (check_free_mem(ctx, ctx->stack_bottom, size))+ return NULL;+ + p = (JSMemBlockHeader *)ctx->heap_free;+ ctx->heap_free += size;++ p->mtag = mtag;+ p->gc_mark = 0;+ p->dummy = 0;+ return p;+}++static void *js_mallocz(JSContext *ctx, uint32_t size, int mtag)+{+ uint8_t *ptr;+ ptr = js_malloc(ctx, size, mtag);+ if (!ptr)+ return NULL;+ if (size > sizeof(uint32_t)) {+ memset(ptr + sizeof(uint32_t), 0, size - sizeof(uint32_t));+ }+ return ptr;+}++/* currently only free the last element */+static void js_free(JSContext *ctx, void *ptr)+{+ uint8_t *ptr1;+ if (!ptr)+ return;+ ptr1 = ptr;+ ptr1 += get_mblock_size(ptr1);+ if (ptr1 == ctx->heap_free)+ ctx->heap_free = ptr;+}++/* 'size' is in bytes and must be multiple of JSW and > 0 */+static void set_free_block(void *ptr, uint32_t size)+{+ JSFreeBlock *p;+ p = (JSFreeBlock *)ptr;+ p->mtag = JS_MTAG_FREE;+ p->gc_mark = 0;+ p->size = (size - sizeof(JSFreeBlock)) / sizeof(JSWord);+}++/* 'ptr' must be != NULL. new_size must be less or equal to the+ current block size. */+static void *js_shrink(JSContext *ctx, void *ptr, uint32_t new_size)+{+ uint32_t old_size;+ uint32_t diff;+ + new_size = (new_size + (JSW - 1)) & ~(JSW - 1);++ if (new_size == 0) {+ js_free(ctx, ptr);+ return NULL;+ }+ old_size = get_mblock_size(ptr);+ assert(new_size <= old_size);+ diff = old_size - new_size;+ if (diff == 0)+ return ptr;+ set_free_block((uint8_t *)ptr + new_size, diff);+ /* add a new free block after 'ptr' */+ return ptr;+}++JSValue JS_Throw(JSContext *ctx, JSValue obj)+{+ ctx->current_exception = obj;+ ctx->current_exception_is_uncatchable = FALSE;+ return JS_EXCEPTION;+}++/* return the byte length. 'buf' must contain UTF8_CHAR_LEN_MAX + 1 bytes */+static int get_short_string(uint8_t *buf, JSValue val)+{+ int len;+ len = unicode_to_utf8(buf, JS_VALUE_GET_SPECIAL_VALUE(val));+ buf[len] = '\0';+ return len;+}++/* printf utility */++#define PF_ZERO_PAD (1 << 0) /* 0 */+#define PF_ALT_FORM (1 << 1) /* # */+#define PF_MARK_POS (1 << 2) /* + */+#define PF_LEFT_ADJ (1 << 3) /* - */+#define PF_PAD_POS (1 << 4) /* ' ' */+#define PF_INT64 (1 << 5) /* l/ll */++static BOOL is_digit(int c)+{+ return (c >= '0' && c <= '9');+}++/* pad with chars 'c' */+static void pad(JSWriteFunc *write_func, void *opaque, char c,+ int width, int len)+{+ char buf[16];+ int l;+ if (len >= width)+ return;+ width -= len;+ memset(buf, c, min_int(sizeof(buf), width));+ while (width != 0) {+ l = min_int(width, sizeof(buf));+ write_func(opaque, buf, l);+ width -= l;+ }+}++/* The 'o' format can be used to print a JSValue. Only short int,+ bool, null, undefined and string types are supported. */+static void js_vprintf(JSWriteFunc *write_func, void *opaque, const char *fmt, va_list ap)+{+ const char *p;+ int width, prec, flags, c;+ char tmp_buf[32], *buf;+ size_t len;+ + while (*fmt != '\0') {+ p = fmt;+ while (*fmt != '%' && *fmt != '\0')+ fmt++;+ if (fmt > p)+ write_func(opaque, p, fmt - p);+ if (*fmt == '\0')+ break;+ fmt++;+ /* get the flags */+ flags = 0;+ for(;;) {+ c = *fmt;+ if (c == '0') {+ flags |= PF_ZERO_PAD;+ } else if (c == '#') {+ flags |= PF_ALT_FORM;+ } else if (c == '+') {+ flags |= PF_MARK_POS;+ } else if (c == '-') {+ flags |= PF_LEFT_ADJ;+ } else if (c == ' ') {+ flags |= PF_MARK_POS;+ } else {+ break;+ }+ fmt++;+ }+ width = 0;+ if (*fmt == '*') {+ width = va_arg(ap, int);+ } else {+ while (is_digit(*fmt)) {+ width = width * 10 + *fmt - '0';+ fmt++;+ }+ }+ prec = 0;+ if (*fmt == '.') {+ fmt++;+ if (*fmt == '*') {+ prec = va_arg(ap, int);+ } else {+ while (is_digit(*fmt)) {+ prec = prec * 10 + *fmt - '0';+ fmt++;+ }+ }+ }+ /* modifiers */+ for(;;) {+ c = *fmt;+ if (c == 'l') {+ if (sizeof(long) == sizeof(int64_t) || fmt[-1] == 'l')+ flags |= PF_INT64;+ } else+ if (c == 'z' || c == 't') {+ if (sizeof(size_t) == sizeof(uint64_t))+ flags |= PF_INT64;+ } else {+ break;+ }+ fmt++;+ }++ c = *fmt++;+ /* XXX: not complete, just enough for our needs */+ buf = tmp_buf;+ len = 0;+ switch(c) {+ case '%':+ write_func(opaque, fmt - 1, 1);+ break;+ case 'c':+ buf[0] = va_arg(ap, int);+ len = 1;+ flags &= ~PF_ZERO_PAD;+ break;+ case 's':+ buf = va_arg(ap, char *);+ if (!buf)+ buf = "null";+ len = strlen(buf);+ flags &= ~PF_ZERO_PAD;+ break;+ case 'd':+ if (flags & PF_INT64)+ len = i64toa(buf, va_arg(ap, int64_t));+ else+ len = i32toa(buf, va_arg(ap, int32_t));+ break;+ case 'u':+ if (flags & PF_INT64)+ len = u64toa(buf, va_arg(ap, uint64_t));+ else+ len = u32toa(buf, va_arg(ap, uint32_t));+ break;+ case 'x':+ if (flags & PF_INT64)+ len = u64toa_radix(buf, va_arg(ap, uint64_t), 16);+ else+ len = u64toa_radix(buf, va_arg(ap, uint32_t), 16);+ break;+ case 'p':+ buf[0] = '0';+ buf[1] = 'x';+ len = u64toa_radix(buf + 2, (uintptr_t)va_arg(ap, void *), 16);+ len += 2;+ break;+ case 'o':+ {+ JSValue val = (flags & PF_INT64) ? va_arg(ap, uint64_t) : va_arg(ap, uint32_t);+ if (JS_IsInt(val)) {+ len = i32toa(buf, JS_VALUE_GET_INT(val));+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ /* XXX: print it */+ buf = "[short_float]";+ goto do_strlen;+ } else+#endif+ if (!JS_IsPtr(val)) {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_NULL:+ buf = "null";+ goto do_strlen;+ case JS_TAG_UNDEFINED:+ buf = "undefined";+ goto do_strlen;+ case JS_TAG_UNINITIALIZED:+ buf = "uninitialized";+ goto do_strlen;+ case JS_TAG_BOOL:+ buf = JS_VALUE_GET_SPECIAL_VALUE(val) ? "true" : "false";+ goto do_strlen;+ case JS_TAG_STRING_CHAR:+ len = get_short_string((uint8_t *)buf, val);+ break;+ default:+ buf = "[tag]";+ goto do_strlen;+ }+ } else {+ void *ptr = JS_VALUE_TO_PTR(val);+ int mtag = ((JSMemBlockHeader *)ptr)->mtag;+ switch(mtag) {+ case JS_MTAG_STRING:+ {+ JSString *p = ptr;+ buf = (char *)p->buf;+ len = p->len;+ }+ break;+ default:+ buf = "[mtag]";+ do_strlen:+ len = strlen(buf);+ break;+ }+ }+ /* remove the trailing '\n' if any (used in error output) */+ if ((flags & PF_ALT_FORM) && len > 0 && buf[len - 1] == '\n')+ len--;+ flags &= ~PF_ZERO_PAD;+ }+ break;+ default:+ goto error;+ }+ if (flags & PF_ZERO_PAD) {+ /* XXX: incorrect with prefix */+ pad(write_func, opaque, '0', width, len);+ } else {+ if (!(flags & PF_LEFT_ADJ))+ pad(write_func, opaque, ' ', width, len);+ }+ write_func(opaque, buf, len);+ if (flags & PF_LEFT_ADJ)+ pad(write_func, opaque, ' ', width, len);+ }+ return;+ error:+ return;+}++/* used for the debug output */+static void __js_printf_like(2, 3) js_printf(JSContext *ctx,+ const char *fmt, ...)+{+ va_list ap;+ va_start(ap, fmt);+ js_vprintf(ctx->write_func, ctx->opaque, fmt, ap);+ va_end(ap);+}++static __maybe_unused void js_putchar(JSContext *ctx, uint8_t c)+{+ ctx->write_func(ctx->opaque, &c, 1);+}++typedef struct {+ char *ptr;+ char *buf_end;+ int len;+} SNPrintfState;++static void snprintf_write_func(void *opaque, const void *buf, size_t buf_len)+{+ SNPrintfState *s = opaque;+ size_t l;+ s->len += buf_len;+ l = min_size_t(buf_len, s->buf_end - s->ptr);+ if (l != 0) {+ memcpy(s->ptr, buf, l);+ s->ptr += l;+ }+}++static int js_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap)+{+ SNPrintfState ss, *s = &ss;+ s->ptr = buf;+ s->buf_end = buf + max_size_t(buf_size, 1) - 1;+ s->len = 0;+ js_vprintf(snprintf_write_func, s, fmt, ap);+ if (buf_size > 0)+ *s->ptr = '\0';+ return s->len;+}++static int __maybe_unused __js_printf_like(3, 4) js_snprintf(char *buf, size_t buf_size, const char *fmt, ...)+{+ va_list ap;+ int ret;+ va_start(ap, fmt);+ ret = js_vsnprintf(buf, buf_size, fmt, ap);+ va_end(ap);+ return ret;+}++JSValue __js_printf_like(3, 4) JS_ThrowError(JSContext *ctx, JSObjectClassEnum error_num,+ const char *fmt, ...)+{+ JSObject *p;+ va_list ap;+ char buf[128];+ JSValue msg, error_obj;+ JSGCRef msg_ref, error_obj_ref;+ + va_start(ap, fmt);+ js_vsnprintf(buf, sizeof(buf), fmt, ap);+ va_end(ap);+ msg = JS_NewString(ctx, buf);++ JS_PUSH_VALUE(ctx, msg);+ error_obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[error_num], JS_CLASS_ERROR,+ sizeof(JSErrorData));+ JS_POP_VALUE(ctx, msg);+ if (JS_IsException(error_obj))+ return error_obj;++ p = JS_VALUE_TO_PTR(error_obj);+ p->u.error.message = msg;+ p->u.error.stack = JS_NULL;++ /* in case of syntax error, the backtrace is added later */+ if (error_num != JS_CLASS_SYNTAX_ERROR) {+ JS_PUSH_VALUE(ctx, error_obj);+ build_backtrace(ctx, error_obj, NULL, 0, 0, 0);+ JS_POP_VALUE(ctx, error_obj);+ }++ return JS_Throw(ctx, error_obj);+}++JSValue JS_ThrowOutOfMemory(JSContext *ctx)+{+ JSValue val;+ if (ctx->in_out_of_memory)+ return JS_Throw(ctx, JS_NULL);+ ctx->in_out_of_memory = TRUE;+ ctx->min_free_size = JS_MIN_CRITICAL_FREE_SIZE;+ val = JS_ThrowInternalError(ctx, "out of memory");+ ctx->in_out_of_memory = FALSE;+ ctx->min_free_size = JS_MIN_FREE_SIZE;+ return val;+}++#define JS_SHORTINT_MIN (-(1 << 30))+#define JS_SHORTINT_MAX ((1 << 30) - 1)++#ifdef JS_USE_SHORT_FLOAT++#define JS_FLOAT64_VALUE_EXP_MIN (1023 - 127)+#define JS_FLOAT64_VALUE_ADDEND ((uint64_t)(JS_FLOAT64_VALUE_EXP_MIN - (JS_TAG_SHORT_FLOAT << 8)) << 52)++/* 1 <= n <= 63 */+static inline uint64_t rotl64(uint64_t a, int n)+{+ return (a << n) | (a >> (64 - n));+}++static double js_get_short_float(JSValue v)+{+ return uint64_as_float64(rotl64(v, 60) + JS_FLOAT64_VALUE_ADDEND);+}++static JSValue js_to_short_float(double d)+{+ return rotl64(float64_as_uint64(d) - JS_FLOAT64_VALUE_ADDEND, 4);+}++#endif /* JS_USE_SHORT_FLOAT */++static JSValue js_alloc_float64(JSContext *ctx, double d)+{+ JSFloat64 *f;+ f = js_malloc(ctx, sizeof(JSFloat64), JS_MTAG_FLOAT64);+ if (!f)+ return JS_EXCEPTION;+ f->u.dval = d;+ return JS_VALUE_FROM_PTR(f);+}++/* create a new float64 value which is known not to be a short integer */+static JSValue __JS_NewFloat64(JSContext *ctx, double d)+{+ if (float64_as_uint64(d) == 0x8000000000000000) {+ /* minus zero often happens, so it is worth having a constant+ value */+ return ctx->minus_zero;+ } else+#ifdef JS_USE_SHORT_FLOAT+ /* Note: this test is false for NaN */+ if (fabs(d) >= 0x1p-127 && fabs(d) <= 0x1p+128) {+ return js_to_short_float(d);+ } else+#endif+ {+ return js_alloc_float64(ctx, d);+ }+}++static inline JSValue JS_NewShortInt(int32_t val)+{+ return JS_TAG_INT + (val << 1);+}++#if defined(USE_SOFTFLOAT)+JSValue JS_NewFloat64(JSContext *ctx, double d)+{+ uint64_t a, m;+ int e, b, shift;+ JSValue v;++ a = float64_as_uint64(d);+ if (a == 0) {+ v = JS_NewShortInt(0);+ } else {+ e = (a >> 52) & 0x7ff;+ if (e >= 1023 && e <= 1023 + 30 - 1) {+ m = (a & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);+ shift = 52 - (e - 1023);+ /* test if exact integer */+ if ((m & (((uint64_t)1 << shift) - 1)) != 0)+ goto not_int;+ b = m >> shift;+ if (a >> 63)+ b = -b;+ v = JS_NewShortInt(b);+ } else if (a == 0xc1d0000000000000) {+ v = JS_NewShortInt(-(1 << 30));+ } else {+ not_int:+ v = __JS_NewFloat64(ctx, d);+ }+ }+ return v;+}+#else+JSValue JS_NewFloat64(JSContext *ctx, double d)+{+ int32_t val;+ if (d >= JS_SHORTINT_MIN && d <= JS_SHORTINT_MAX) {+ val = (int32_t)d;+ /* -0 cannot be represented as integer, so we compare the bit+ representation */+ if (float64_as_uint64(d) == float64_as_uint64((double)val))+ return JS_NewShortInt(val);+ }+ return __JS_NewFloat64(ctx, d);+}+#endif++static inline BOOL int64_is_short_int(int64_t val)+{+ return val >= JS_SHORTINT_MIN && val <= JS_SHORTINT_MAX;+}++JSValue JS_NewInt64(JSContext *ctx, int64_t val)+{+ JSValue v;+ if (likely(int64_is_short_int(val))) {+ v = JS_NewShortInt(val);+ } else {+ v = __JS_NewFloat64(ctx, val);+ }+ return v;+}++JSValue JS_NewInt32(JSContext *ctx, int32_t val)+{+ return JS_NewInt64(ctx, val);+}++JSValue JS_NewUint32(JSContext *ctx, uint32_t val)+{+ return JS_NewInt64(ctx, val);+}++static BOOL JS_IsPrimitive(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return JS_VALUE_GET_SPECIAL_TAG(val) != JS_TAG_SHORT_FUNC;+ } else {+ return (js_get_mtag(JS_VALUE_TO_PTR(val)) != JS_MTAG_OBJECT);+ }+}++/* Note: short functions are not considered as objects by this function */+static BOOL JS_IsObject(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return FALSE;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(val);+ return (p->mtag == JS_MTAG_OBJECT);+ }+}++/* return -1 if not an object */+int JS_GetClassID(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return -1;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(val);+ if (p->mtag != JS_MTAG_OBJECT)+ return -1;+ else+ return p->class_id;+ }+}++void JS_SetOpaque(JSContext *ctx, JSValue val, void *opaque)+{+ JSObject *p;+ assert(JS_IsPtr(val));+ p = JS_VALUE_TO_PTR(val);+ assert(p->mtag == JS_MTAG_OBJECT);+ assert(p->class_id >= JS_CLASS_USER);+ p->u.user.opaque = opaque;+}++void *JS_GetOpaque(JSContext *ctx, JSValue val)+{+ JSObject *p;+ assert(JS_IsPtr(val));+ p = JS_VALUE_TO_PTR(val);+ assert(p->mtag == JS_MTAG_OBJECT);+ assert(p->class_id >= JS_CLASS_USER);+ return p->u.user.opaque;+}++static JSObject *js_get_object_class(JSContext *ctx, JSValue val, int class_id)+{+ if (!JS_IsPtr(val)) {+ return NULL;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(val);+ if (p->mtag != JS_MTAG_OBJECT || p->class_id != class_id)+ return NULL;+ else+ return p;+ }+}++BOOL JS_IsFunction(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_SHORT_FUNC;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(val);+ return (p->mtag == JS_MTAG_OBJECT &&+ (p->class_id == JS_CLASS_CLOSURE ||+ p->class_id == JS_CLASS_C_FUNCTION));+ }+}++static BOOL JS_IsFunctionObject(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return FALSE;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(val);+ return (p->mtag == JS_MTAG_OBJECT &&+ (p->class_id == JS_CLASS_CLOSURE ||+ p->class_id == JS_CLASS_C_FUNCTION));+ }+}++BOOL JS_IsError(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return FALSE;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(val);+ return (p->mtag == JS_MTAG_OBJECT && p->class_id == JS_CLASS_ERROR);+ }+}++static force_inline BOOL JS_IsIntOrShortFloat(JSValue val)+{+#ifdef JS_USE_SHORT_FLOAT+ return JS_IsInt(val) || JS_IsShortFloat(val);+#else+ return JS_IsInt(val);+#endif+}++BOOL JS_IsNumber(JSContext *ctx, JSValue val)+{+ if (JS_IsIntOrShortFloat(val)) {+ return TRUE;+ } else if (JS_IsPtr(val)) {+ void *ptr = JS_VALUE_TO_PTR(val);+ return (js_get_mtag(ptr) == JS_MTAG_FLOAT64);+ } else {+ return FALSE;+ }+}++BOOL JS_IsString(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val)) {+ return JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR;+ } else {+ void *ptr = JS_VALUE_TO_PTR(val);+ return (js_get_mtag(ptr) == JS_MTAG_STRING);+ }+}++static JSString *js_alloc_string(JSContext *ctx, uint32_t buf_len)+{+ JSString *p;++ if (buf_len > JS_STRING_LEN_MAX) {+ JS_ThrowInternalError(ctx, "string too long");+ return NULL;+ }+ p = js_malloc(ctx, sizeof(JSString) + buf_len + 1, JS_MTAG_STRING);+ if (!p)+ return NULL;+ p->is_unique = FALSE;+ p->is_ascii = FALSE;+ p->is_numeric = FALSE;+ p->len = buf_len;+ p->buf[buf_len] = '\0';+ return p;+}++/* 0 <= c <= 0x10ffff */+static inline JSValue JS_NewStringChar(uint32_t c)+{+ return JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, c);+}++static force_inline int utf8_char_len(int c)+{+ int l;+ if (c < 0x80) {+ l = 1;+ } else if (c < 0xc0) {+ l = 1;+ } else if (c < 0xe0) {+ l = 2;+ } else if (c < 0xf0) {+ l = 3;+ } else if (c < 0xf8) {+ l = 4;+ } else {+ l = 1;+ }+ return l;+}++static BOOL is_ascii_string(const char *buf, size_t len)+{+ size_t i;+ for(i = 0; i < len; i++) {+ if ((uint8_t)buf[i] > 0x7f)+ return FALSE;+ }+ return TRUE;+}++static JSString *get_string_ptr(JSContext *ctx, JSStringCharBuf *buf,+ JSValue val)+{+ if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) {+ JSString *p = (JSString *)buf;+ p->is_unique = FALSE;+ p->is_ascii = JS_VALUE_GET_SPECIAL_VALUE(val) <= 0x7f;+ p->len = get_short_string(p->buf, val);+ return p;+ } else {+ return JS_VALUE_TO_PTR(val);+ }+}++static JSValue js_sub_string_utf8(JSContext *ctx, JSValue val,+ uint32_t start0, uint32_t end0)+{+ JSString *p, *p1;+ int len, start, end, c;+ BOOL start_surrogate, end_surrogate;+ JSStringCharBuf buf;+ JSGCRef val_ref;+ const uint8_t *ptr;+ size_t clen;+ + if (end0 - start0 == 0) {+ return js_get_atom(ctx, JS_ATOM_empty);+ }+ start_surrogate = start0 & 1;+ end_surrogate = end0 & 1;+ start = start0 >> 1;+ end = end0 >> 1;+ len = end - start;+ p1 = get_string_ptr(ctx, &buf, val);+ ptr = p1->buf;+ if (!start_surrogate && !end_surrogate && utf8_char_len(ptr[start]) == len) {+ c = utf8_get(ptr + start, &clen);+ return JS_NewStringChar(c);+ }++ JS_PUSH_VALUE(ctx, val);+ p = js_alloc_string(ctx, len - start_surrogate + (end_surrogate ? 3 : 0));+ JS_POP_VALUE(ctx, val);+ if (!p)+ return JS_EXCEPTION;+ p1 = get_string_ptr(ctx, &buf, val);+ ptr = p1->buf;+ if (unlikely(start_surrogate || end_surrogate)) {+ uint8_t *q = p->buf;+ p->is_ascii = FALSE;+ if (start_surrogate) {+ c = utf8_get(ptr + start, &clen);+ c = 0xdc00 + ((c - 0x10000) & 0x3ff); /* right surrogate */+ q += unicode_to_utf8(q, c);+ start += 4;+ }+ memcpy(q, ptr + start, end - start);+ q += end - start;+ if (end_surrogate) {+ c = utf8_get(ptr + end, &clen);+ c = 0xd800 + ((c - 0x10000) >> 10); /* left surrogate */+ q += unicode_to_utf8(q, c);+ }+ assert((q - p->buf) == p->len);+ } else {+ p->is_ascii = p1->is_ascii ? TRUE : is_ascii_string((const char *)(ptr + start), len);+ memcpy(p->buf, ptr + start, len);+ }+ return JS_VALUE_FROM_PTR(p);+}++/* Warning: the string must be a valid WTF-8 string (= UTF-8 ++ unpaired surrogates). */+JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t len)+{+ JSString *p;+ + if (len == 0) {+ return js_get_atom(ctx, JS_ATOM_empty);+ } else {+ if (utf8_char_len(buf[0]) == len) {+ size_t clen;+ int c;+ c = utf8_get((const uint8_t *)buf, &clen);+ return JS_NewStringChar(c);+ }+ }+ p = js_alloc_string(ctx, len);+ if (!p)+ return JS_EXCEPTION;+ p->is_ascii = is_ascii_string((const char *)buf, len);+ memcpy(p->buf, buf, len);+ return JS_VALUE_FROM_PTR(p);+}++/* Warning: the string must be a valid UTF-8 string. */+JSValue JS_NewString(JSContext *ctx, const char *buf)+{+ return JS_NewStringLen(ctx, buf, strlen(buf));+}++/* the byte array must be zero terminated. */+static JSValue js_byte_array_to_string(JSContext *ctx, JSValue val, int len, BOOL is_ascii)+{+ JSByteArray *arr = JS_VALUE_TO_PTR(val);+ JSString *p;++ assert(len + 1 <= arr->size);+ if (len == 0) {+ return js_get_atom(ctx, JS_ATOM_empty);+ } else if (utf8_char_len(arr->buf[0]) == len) {+ size_t clen;+ return JS_NewStringChar(utf8_get(arr->buf, &clen));+ } else {+ js_shrink_byte_array(ctx, &val, len + 1);+ p = (JSString *)arr;+ p->mtag = JS_MTAG_STRING;+ p->is_ascii = is_ascii;+ p->is_unique = FALSE;+ p->is_numeric = FALSE;+ p->len = len;+ return val;+ }+}++/* in bytes */+static __maybe_unused int js_string_byte_len(JSContext *ctx, JSValue val)+{+ if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) {+ int c = JS_VALUE_GET_SPECIAL_VALUE(val);+ if (c < 0x80)+ return 1;+ else if (c < 0x800)+ return 2;+ else if (c < 0x10000)+ return 3;+ else+ return 4;+ } else {+ JSString *p = JS_VALUE_TO_PTR(val);+ return p->len;+ }+}+ +/* assuming that utf8_next() returns 4, validate the corresponding UTF-8 sequence */+static BOOL is_valid_len4_utf8(const uint8_t *buf)+{+ return (((buf[0] & 0xf) << 6) | (buf[1] & 0x3f)) >= 0x10;+}++static __maybe_unused void dump_string_pos_cache(JSContext *ctx)+{+ int i;+ JSStringPosCacheEntry *ce;+ for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) {+ ce = &ctx->string_pos_cache[i];+ printf("%d: ", i);+ if (ce->str == JS_NULL) {+ printf("<empty>\n");+ } else {+ JSString *p = JS_VALUE_TO_PTR(ce->str);+ printf(" utf8_pos=%u/%u utf16_pos=%u\n",+ ce->str_pos[POS_TYPE_UTF8], (int)p->len, ce->str_pos[POS_TYPE_UTF16]);+ }+ }+}++/* an UTF-8 position is the byte position multiplied by 2. One is+ added when the corresponding UTF-16 character represents the right+ surrogate if the code is >= 0x10000.+*/+static uint32_t js_string_convert_pos(JSContext *ctx, JSValue val, uint32_t pos,+ StringPosTypeEnum pos_type)+{+ JSStringCharBuf buf;+ JSString *p;+ size_t i, clen, len, start;+ uint32_t d_min, d, j;+ JSStringPosCacheEntry *ce, *ce1;+ uint32_t surrogate_flag, has_surrogate, limit;+ int ce_idx;++ p = get_string_ptr(ctx, &buf, val);+ len = p->len;+ if (p->is_ascii) {+ if (pos_type == POS_TYPE_UTF8)+ return min_int(len, pos / 2);+ else+ return min_int(len, pos) * 2;+ }++ if (pos_type == POS_TYPE_UTF8) {+ has_surrogate = pos & 1;+ pos >>= 1;+ } else {+ has_surrogate = 0;+ }+ + ce = NULL;+ if (len < JS_STRING_POS_CACHE_MIN_LEN) {+ j = 0;+ i = 0;+ goto uncached;+ }++ d_min = pos;+ for(ce_idx = 0; ce_idx < JS_STRING_POS_CACHE_SIZE; ce_idx++) {+ ce1 = &ctx->string_pos_cache[ce_idx];+ if (ce1->str == val) {+ d = ce1->str_pos[pos_type];+ d = d >= pos ? d - pos : pos - d;+ if (d < d_min) {+ d_min = d;+ ce = ce1;+ }+ }+ }+ if (!ce) {+ /* "random" replacement */+ ce = &ctx->string_pos_cache[ctx->string_pos_cache_counter];+ if (++ctx->string_pos_cache_counter == JS_STRING_POS_CACHE_SIZE)+ ctx->string_pos_cache_counter = 0;+ ce->str = val;+ ce->str_pos[POS_TYPE_UTF8] = 0;+ ce->str_pos[POS_TYPE_UTF16] = 0;+ }+ + i = ce->str_pos[POS_TYPE_UTF8];+ j = ce->str_pos[POS_TYPE_UTF16];+ if (ce->str_pos[pos_type] <= pos) {+ uncached:+ surrogate_flag = 0;+ if (pos_type == POS_TYPE_UTF8) {+ limit = INT32_MAX;+ len = pos;+ } else {+ limit = pos;+ }+ for(; i < len; i += clen) {+ if (j == limit)+ break;+ clen = utf8_char_len(p->buf[i]);+ if (clen == 4 && is_valid_len4_utf8(p->buf + i)) {+ if ((j + 1) == limit) {+ surrogate_flag = 1;+ break;+ }+ j += 2;+ } else {+ j++;+ }+ }+ } else {+ surrogate_flag = 0;+ if (pos_type == POS_TYPE_UTF8) {+ start = pos;+ limit = INT32_MAX;+ } else {+ limit = pos;+ start = 0;+ }+ while (i > start) {+ size_t i0 = i;+ i--;+ while ((p->buf[i] & 0xc0) == 0x80)+ i--;+ clen = i0 - i;+ if (clen == 4 && is_valid_len4_utf8(p->buf + i)) {+ j -= 2;+ if ((j + 1) == limit) {+ surrogate_flag = 1;+ break;+ }+ } else {+ j--;+ }+ if (j == limit)+ break;+ }+ }+ if (ce) {+ ce->str_pos[POS_TYPE_UTF8] = i;+ ce->str_pos[POS_TYPE_UTF16] = j;+ }+ if (pos_type == POS_TYPE_UTF8)+ return j + has_surrogate;+ else+ return i * 2 + surrogate_flag;+}++static uint32_t js_string_utf16_to_utf8_pos(JSContext *ctx, JSValue val, uint32_t utf16_pos)+{+ return js_string_convert_pos(ctx, val, utf16_pos, POS_TYPE_UTF16);+}++static uint32_t js_string_utf8_to_utf16_pos(JSContext *ctx, JSValue val, uint32_t utf8_pos)+{+ return js_string_convert_pos(ctx, val, utf8_pos, POS_TYPE_UTF8);+}++/* Testing the third byte is not needed as the UTF-8 encoding must be+ correct */+static BOOL is_utf8_left_surrogate(const uint8_t *p)+{+ return p[0] == 0xed && (p[1] >= 0xa0 && p[1] <= 0xaf);+}++static BOOL is_utf8_right_surrogate(const uint8_t *p)+{+ return p[0] == 0xed && (p[1] >= 0xb0 && p[1] <= 0xbf);+}++typedef struct {+ JSGCRef buffer_ref; /* string, JSByteBuffer or JS_EXCEPTION */+ int len; /* current string length (in bytes) */+ BOOL is_ascii;+} StringBuffer;++/* return 0 if OK, -1 in case of exception (exception possible if len > 0) */+static int string_buffer_push(JSContext *ctx, StringBuffer *s, int len)+{+ s->len = 0;+ s->is_ascii = TRUE;+ if (len > 0) {+ JSByteArray *arr;+ arr = js_alloc_byte_array(ctx, len);+ if (!arr)+ return -1;+ s->buffer_ref.val = JS_VALUE_FROM_PTR(arr);+ } else {+ s->buffer_ref.val = js_get_atom(ctx, JS_ATOM_empty);+ }+ s->buffer_ref.prev = ctx->top_gc_ref;+ ctx->top_gc_ref = &s->buffer_ref;+ return 0;+}++/* val2 must be a string. Return 0 if OK, -1 in case of exception */+static int string_buffer_concat_str(JSContext *ctx, StringBuffer *s, JSValue val2)+{+ JSStringCharBuf buf1, buf2;+ JSByteArray *arr;+ JSString *p1, *p2;+ int len, len1, len2;+ JSValue val1;+ uint8_t *q;+ + if (JS_IsException(s->buffer_ref.val))+ return -1;+ p2 = get_string_ptr(ctx, &buf2, val2);+ len2 = p2->len;+ if (len2 == 0)+ return 0;+ if (JS_IsString(ctx, s->buffer_ref.val)) {+ p1 = get_string_ptr(ctx, &buf1, s->buffer_ref.val);+ len1 = p1->len;+ if (len1 == 0) {+ /* empty string in buffer: just keep 'val2' */+ s->buffer_ref.val = val2;+ return 0;+ }+ arr = NULL;+ val1 = s->buffer_ref.val;+ s->buffer_ref.val = JS_NULL;+ } else {+ arr = JS_VALUE_TO_PTR(s->buffer_ref.val);+ len1 = s->len;+ val1 = JS_NULL;+ }++ len = len1 + len2;+ if (len > JS_STRING_LEN_MAX) {+ s->buffer_ref.val = JS_ThrowInternalError(ctx, "string too long");+ return -1;+ }++ if (!arr || (len + 1) > arr->size) {+ JSGCRef val1_ref, val2_ref;++ JS_PUSH_VALUE(ctx, val1);+ JS_PUSH_VALUE(ctx, val2);+ s->buffer_ref.val = js_resize_byte_array(ctx, s->buffer_ref.val, len + 1);+ JS_POP_VALUE(ctx, val2);+ JS_POP_VALUE(ctx, val1);+ if (JS_IsException(s->buffer_ref.val))+ return -1;+ arr = JS_VALUE_TO_PTR(s->buffer_ref.val);+ if (val1 != JS_NULL) {+ p1 = get_string_ptr(ctx, &buf1, val1);+ s->is_ascii = p1->is_ascii;+ memcpy(arr->buf, p1->buf, len1);+ }+ p2 = get_string_ptr(ctx, &buf2, val2);+ }+ + q = arr->buf + len1;+ if (len2 >= 3 && unlikely(is_utf8_right_surrogate(p2->buf)) &&+ len1 >= 3 && is_utf8_left_surrogate(q - 3)) {+ size_t clen;+ int c;+ /* contract the two surrogates to 4 bytes */+ c = (utf8_get(q - 3, &clen) & 0x3ff) << 10;+ c |= (utf8_get(p2->buf, &clen) & 0x3ff);+ c += 0x10000;+ len -= 2;+ len2 -= 3;+ q -= 3;+ q += unicode_to_utf8(q, c);+ s->is_ascii = FALSE;+ }+ memcpy(q, p2->buf + p2->len - len2, len2);+ s->len = len;+ s->is_ascii &= p2->is_ascii;+ return 0;+}++/* 'str' must be a string */+static int string_buffer_concat_utf8(JSContext *ctx, StringBuffer *s, JSValue str,+ uint32_t start, uint32_t end)+{+ JSValue val2;+ + if (end <= start)+ return 0;+ /* XXX: avoid explicitly constructing the substring */+ val2 = js_sub_string_utf8(ctx, str, start, end);+ if (JS_IsException(val2)) {+ s->buffer_ref.val = JS_EXCEPTION;+ return -1;+ }+ return string_buffer_concat_str(ctx, s, val2);+}++static int string_buffer_concat_utf16(JSContext *ctx, StringBuffer *s, JSValue str,+ uint32_t start, uint32_t end)+{+ uint32_t start_utf8, end_utf8;+ if (end <= start)+ return 0;+ start_utf8 = js_string_utf16_to_utf8_pos(ctx, str, start);+ end_utf8 = js_string_utf16_to_utf8_pos(ctx, str, end);+ return string_buffer_concat_utf8(ctx, s, str, start_utf8, end_utf8);+}++static int string_buffer_concat(JSContext *ctx, StringBuffer *s, JSValue val2)+{+ val2 = JS_ToString(ctx, val2);+ if (JS_IsException(val2)) {+ s->buffer_ref.val = JS_EXCEPTION;+ return -1;+ }+ return string_buffer_concat_str(ctx, s, val2);+}++/* XXX: could optimize */+static int string_buffer_putc(JSContext *ctx, StringBuffer *s, int c)+{+ return string_buffer_concat_str(ctx, s, JS_NewStringChar(c));+}++static int string_buffer_puts(JSContext *ctx, StringBuffer *s, const char *str)+{+ JSValue val;++ /* XXX: avoid this allocation */+ val = JS_NewString(ctx, str);+ if (JS_IsException(val))+ return -1;+ return string_buffer_concat_str(ctx, s, val);+}++static JSValue string_buffer_pop(JSContext *ctx, StringBuffer *s)+{+ JSValue res;+ if (JS_IsException(s->buffer_ref.val) ||+ JS_IsString(ctx, s->buffer_ref.val)) {+ res = s->buffer_ref.val;+ } else {+ if (s->len != 0) {+ /* add the trailing '\0' */+ JSByteArray *arr = JS_VALUE_TO_PTR(s->buffer_ref.val);+ arr->buf[s->len] = '\0';+ }+ res = js_byte_array_to_string(ctx, s->buffer_ref.val, s->len, s->is_ascii);+ }+ ctx->top_gc_ref = s->buffer_ref.prev;+ return res;+}++/* val1 and val2 must be strings or exception */+static JSValue JS_ConcatString(JSContext *ctx, JSValue val1, JSValue val2)+{+ StringBuffer b_s, *b = &b_s;++ if (JS_IsException(val1) ||+ JS_IsException(val2))+ return JS_EXCEPTION;++ string_buffer_push(ctx, b, 0);+ string_buffer_concat_str(ctx, b, val1); /* no memory allocation */+ string_buffer_concat_str(ctx, b, val2);+ return string_buffer_pop(ctx, b);+}++static BOOL js_string_eq(JSContext *ctx, JSValue val1, JSValue val2)+{+ JSStringCharBuf buf1, buf2;+ JSString *p1, *p2;++ p1 = get_string_ptr(ctx, &buf1, val1);+ p2 = get_string_ptr(ctx, &buf2, val2);+ if (p1->len != p2->len)+ return FALSE;+ return !memcmp(p1->buf, p2->buf, p1->len);+}++/* Return the unicode character containing the byte at position+ 'i'. Return -1 in case of error. */+static int string_get_cp(const uint8_t *p)+{+ size_t clen;+ while ((*p & 0xc0) == 0x80)+ p--;+ return utf8_get(p, &clen);+}++static int js_string_compare(JSContext *ctx, JSValue val1, JSValue val2)+{+ JSStringCharBuf buf1, buf2;+ int len, i, res;+ JSString *p1, *p2;+ + p1 = get_string_ptr(ctx, &buf1, val1);+ p2 = get_string_ptr(ctx, &buf2, val2);+ len = min_int(p1->len, p2->len);+ for(i = 0; i < len; i++) {+ if (p1->buf[i] != p2->buf[i])+ break;+ }+ if (i != len) {+ int c1, c2;+ /* if valid UTF-8, the strings cannot be equal at this point */+ /* Note: UTF-16 does not preserve unicode order like UTF-8 */+ c1 = string_get_cp(p1->buf + i);+ c2 = string_get_cp(p2->buf + i);+ if ((c1 < 0x10000 && c2 < 0x10000) ||+ (c1 >= 0x10000 && c2 >= 0x10000)) {+ if (c1 < c2)+ res = -1;+ else+ res = 1;+ } else if (c1 < 0x10000) {+ /* p1 < p2 if same first UTF-16 char */+ c2 = 0xd800 + ((c2 - 0x10000) >> 10);+ if (c1 <= c2)+ res = -1;+ else+ res = 1;+ } else {+ /* p1 > p2 if same first UTF-16 char */+ c1 = 0xd800 + ((c1 - 0x10000) >> 10);+ if (c1 < c2)+ res = -1;+ else+ res = 1;+ }+ } else {+ if (p1->len == p2->len)+ res = 0;+ else if (p1->len < p2->len)+ res = -1;+ else+ res = 1;+ }+ return res;+}++/* return the string length in UTF16 characters. 'val' must be a+ string char or a string */+static int js_string_len(JSContext *ctx, JSValue val)+{+ if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) {+ return JS_VALUE_GET_SPECIAL_VALUE(val) >= 0x10000 ? 2 : 1;+ } else {+ JSString *p;+ p = JS_VALUE_TO_PTR(val);+ if (p->is_ascii)+ return p->len;+ else+ return js_string_utf8_to_utf16_pos(ctx, val, p->len * 2);+ }+}++/* return the UTF-16 code or the unicode character at a given UTF-8+ position or -1 if outside the string */+static int string_getcp(JSContext *ctx, JSValue str, uint32_t utf16_pos, BOOL is_codepoint)+{+ JSString *p;+ JSStringCharBuf buf;+ uint32_t surrogate_flag, c, utf8_pos;+ size_t clen;++ utf8_pos = js_string_utf16_to_utf8_pos(ctx, str, utf16_pos);+ surrogate_flag = utf8_pos & 1;+ utf8_pos >>= 1;+ p = get_string_ptr(ctx, &buf, str);+ if (utf8_pos >= p->len)+ return -1;+ c = utf8_get(p->buf + utf8_pos, &clen);+ if (c < 0x10000 || (!surrogate_flag && is_codepoint)) {+ return c;+ } else {+ c -= 0x10000;+ if (!surrogate_flag)+ return 0xd800 + (c >> 10); /* left surrogate */+ else+ return 0xdc00 + (c & 0x3ff); /* right surrogate */+ }+}++static int string_getc(JSContext *ctx, JSValue str, uint32_t utf16_pos)+{+ return string_getcp(ctx, str, utf16_pos, FALSE);+}++/* precondition: 0 <= start <= end <= string length */+static JSValue js_sub_string(JSContext *ctx, JSValue val, int start, int end)+{+ uint32_t start_utf8, end_utf8;+ + if (end <= start)+ return js_get_atom(ctx, JS_ATOM_empty);+ start_utf8 = js_string_utf16_to_utf8_pos(ctx, val, start);+ end_utf8 = js_string_utf16_to_utf8_pos(ctx, val, end);+ return js_sub_string_utf8(ctx, val, start_utf8, end_utf8);+}++static inline int is_num(int c)+{+ return c >= '0' && c <= '9';+}++/* return TRUE if the property 'val' represents a numeric property. -1+ is returned in case of exception. 'val' must be a string. It is+ assumed that NaN and infinities have already been handled. */+static int js_is_numeric_string(JSContext *ctx, JSValue val)+{+ int c, len;+ double d;+ const char *r, *q;+ JSString *p;+ JSByteArray *tmp_arr;+ JSGCRef val_ref;+ char buf[32]; /* enough for js_dtoa() */+ + p = JS_VALUE_TO_PTR(val);+ /* the fast case is when the string is not a number */+ if (p->len == 0 || !p->is_ascii)+ return FALSE;+ q = (const char *)p->buf;+ c = *q;+ if (c == '-') {+ if (p->len == 1)+ return FALSE;+ q++;+ c = *q;+ }+ if (!is_num(c))+ return FALSE;++ JS_PUSH_VALUE(ctx, val);+ tmp_arr = js_alloc_byte_array(ctx, max_int(sizeof(JSATODTempMem),+ sizeof(JSDTOATempMem)));+ JS_POP_VALUE(ctx, val);+ if (!tmp_arr)+ return -1;+ p = JS_VALUE_TO_PTR(val);+ d = js_atod((char *)p->buf, &r, 10, 0, (JSATODTempMem *)tmp_arr->buf);+ if ((r - (char *)p->buf) != p->len) {+ js_free(ctx, tmp_arr);+ return FALSE;+ }+ len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE, (JSDTOATempMem *)tmp_arr->buf);+ js_free(ctx, tmp_arr);+ return (p->len == len && !memcmp(buf, p->buf, len));+}++/* return JS_NULL if not found */+static JSValue find_atom(JSContext *ctx, int *pidx, const JSValueArray *arr, int len, JSValue val)+{+ int a, b, m, r;+ JSValue val1;++ a = 0;+ b = len - 1;+ while (a <= b) {+ m = (a + b) >> 1;+ val1 = arr->arr[m];+ r = js_string_compare(ctx, val, val1);+ if (r == 0) {+ /* found */+ *pidx = m;+ return val1;+ } else if (r < 0) {+ b = m - 1;+ } else {+ a = m + 1;+ }+ }+ *pidx = a;+ return JS_NULL;+}++/* if 'val' is not a string, it is returned */+/* XXX: use hash table */+static JSValue JS_MakeUniqueString(JSContext *ctx, JSValue val)+{+ JSString *p;+ int a, is_numeric, i;+ JSValueArray *arr;+ const JSValueArray *arr1;+ JSValue val1, new_tab;+ JSGCRef val_ref;+ + if (!JS_IsPtr(val))+ return val;+ p = JS_VALUE_TO_PTR(val);+ if (p->mtag != JS_MTAG_STRING || p->is_unique)+ return val;++ /* not unique: find it in the ROM or RAM sorted unique string table */+ for(i = 0; i < ctx->n_rom_atom_tables; i++) {+ arr1 = ctx->rom_atom_tables[i];+ if (arr1) {+ val1 = find_atom(ctx, &a, arr1, arr1->size, val); + if (!JS_IsNull(val1))+ return val1;+ }+ }+ + arr = JS_VALUE_TO_PTR( ctx->unique_strings);+ val1 = find_atom(ctx, &a, arr, ctx->unique_strings_len, val); + if (!JS_IsNull(val1))+ return val1;+ + JS_PUSH_VALUE(ctx, val);+ is_numeric = js_is_numeric_string(ctx, val);+ JS_POP_VALUE(ctx, val);+ if (is_numeric < 0)+ return JS_EXCEPTION;+ + /* not found: add it in the table */+ JS_PUSH_VALUE(ctx, val);+ new_tab = js_resize_value_array(ctx, ctx->unique_strings,+ ctx->unique_strings_len + 1);+ JS_POP_VALUE(ctx, val);+ if (JS_IsException(new_tab))+ return JS_EXCEPTION;+ ctx->unique_strings = new_tab;+ arr = JS_VALUE_TO_PTR( ctx->unique_strings);+ memmove(&arr->arr[a + 1], &arr->arr[a],+ sizeof(arr->arr[0]) * (ctx->unique_strings_len - a));+ arr->arr[a] = val;+ p = JS_VALUE_TO_PTR(val);+ p->is_unique = TRUE;+ p->is_numeric = is_numeric;+ ctx->unique_strings_len++;+ return val;+}++static int JS_ToBool(JSContext *ctx, JSValue val)+{+ if (JS_IsInt(val)) {+ return JS_VALUE_GET_INT(val) != 0;+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ double d;+ d = js_get_short_float(val);+ return !isnan(d) && d != 0;+ } else+#endif+ if (!JS_IsPtr(val)) {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ return JS_VALUE_GET_SPECIAL_VALUE(val);+ case JS_TAG_SHORT_FUNC:+ case JS_TAG_STRING_CHAR:+ return TRUE;+ default:+ return FALSE;+ }+ } else {+ JSMemBlockHeader *h = JS_VALUE_TO_PTR(val);+ switch(h->mtag) {+ case JS_MTAG_STRING:+ {+ JSString *p = (JSString *)h;+ return p->len != 0;+ }+ case JS_MTAG_FLOAT64:+ {+ JSFloat64 *p = (JSFloat64 *)h;+ return !isnan(p->u.dval) && p->u.dval != 0;+ }+ default:+ case JS_MTAG_OBJECT:+ return TRUE;+ }+ }+}++/* plen can be NULL. No memory allocation is done if 'val' already is+ a string. */+const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val,+ JSCStringBuf *buf)+{+ const char *p;+ int len;++ val = JS_ToString(ctx, val);+ if (JS_IsException(val))+ return NULL;+ if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) {+ len = get_short_string(buf->buf, val);+ p = (const char *)buf->buf;+ } else {+ JSString *r;+ r = JS_VALUE_TO_PTR(val);+ p = (const char *)r->buf;+ len = r->len;+ }+ if (plen)+ *plen = len;+ return p;+}++const char *JS_ToCString(JSContext *ctx, JSValue val, JSCStringBuf *buf)+{+ return JS_ToCStringLen(ctx, NULL, val, buf);+}++JSValue JS_GetException(JSContext *ctx)+{+ JSValue obj;+ obj = ctx->current_exception;+ ctx->current_exception = JS_UNDEFINED;+ return obj;+}++static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValue val)+{+ if (val == JS_NULL || val == JS_UNDEFINED)+ return JS_ThrowTypeError(ctx, "null or undefined are forbidden");+ return JS_ToString(ctx, val);+}++static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "not an object");+}++/* 'val' must be a string. return TRUE if the string represents a+ short integer */+static inline BOOL is_num_string(JSContext *ctx, int32_t *pval, JSValue val)+{+ JSStringCharBuf buf;+ uint32_t n;+ uint64_t n64;+ JSString *p1;+ int c, is_neg;+ const uint8_t *p, *p_end;+ + p1 = get_string_ptr(ctx, &buf, val);+ if (p1->len == 0 || p1->len > 11 || !p1->is_ascii)+ return FALSE;+ p = p1->buf;+ p_end = p + p1->len;+ c = *p++;+ is_neg = 0;+ if (c == '-') {+ if (p >= p_end)+ return FALSE;+ is_neg = 1;+ c = *p++;+ }+ if (!is_num(c))+ return FALSE;+ if (c == '0') {+ if (p != p_end || is_neg)+ return FALSE;+ n = 0;+ } else {+ n = c - '0';+ while (p < p_end) {+ c = *p++;+ if (!is_num(c))+ return FALSE;+ /* XXX: simplify ? */+ n64 = (uint64_t)n * 10 + (c - '0');+ if (n64 > (JS_SHORTINT_MAX + is_neg))+ return FALSE;+ n = n64;+ }+ if (is_neg)+ n = -n;+ }+ *pval = n;+ return TRUE;+}++/* return TRUE if the property 'val' represent a numeric property. It+ is assumed that the shortint case has been tested before */+static BOOL JS_IsNumericProperty(JSContext *ctx, JSValue val)+{+ JSString *p;+ if (!JS_IsPtr(val))+ return FALSE; /* JS_TAG_STRING_CHAR */+ p = JS_VALUE_TO_PTR(val);+ return p->is_numeric;+}++static JSValueArray *js_alloc_value_array(JSContext *ctx, int init_base, int new_size)+{+ JSValueArray *arr;+ int i;+ + if (new_size > JS_VALUE_ARRAY_SIZE_MAX) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ arr = js_malloc(ctx, sizeof(JSValueArray) + new_size * sizeof(JSValue), JS_MTAG_VALUE_ARRAY);+ if (!arr)+ return NULL;+ arr->size = new_size;+ for(i = init_base; i < new_size; i++)+ arr->arr[i] = JS_UNDEFINED;+ return arr;+}++/* val can be JS_NULL (zero size). 'prop_base' is non zero only when+ * resizing the property arrays so that the property array has a size+ * which is a multiple of 3 */+static JSValue js_resize_value_array2(JSContext *ctx, JSValue val, int new_size, int prop_base)+{+ JSValueArray *slots, *new_slots;+ int old_size, new_size1;+ JSGCRef val_ref;+ + if (val == JS_NULL) {+ slots = NULL;+ old_size = 0;+ } else {+ slots = JS_VALUE_TO_PTR(val);+ old_size = slots->size;+ }+ if (unlikely(new_size > old_size)) {+ new_size1 = old_size + old_size / 2;+ if (new_size1 > new_size) {+ new_size = new_size1;+ /* ensure that the property array has a size which is a+ * multiple of 3 */+ if (prop_base != 0) {+ int align = (new_size - prop_base) % 3;+ if (align != 0)+ new_size += 3 - align;+ }+ }+ new_size = max_int(new_size, old_size + old_size / 2);+ JS_PUSH_VALUE(ctx, val);+ new_slots = js_alloc_value_array(ctx, old_size, new_size);+ JS_POP_VALUE(ctx, val);+ if (!new_slots)+ return JS_EXCEPTION;+ if (old_size > 0) {+ slots = JS_VALUE_TO_PTR(val);+ memcpy(new_slots->arr, slots->arr, old_size * sizeof(JSValue));+ }+ val = JS_VALUE_FROM_PTR(new_slots);+ }+ return val;+}++static JSValue js_resize_value_array(JSContext *ctx, JSValue val, int new_size)+{+ return js_resize_value_array2(ctx, val, new_size, 0);+}++/* no allocation is done */+static void js_shrink_value_array(JSContext *ctx, JSValue *pval, int new_size)+{+ JSValueArray *arr;+ if (*pval == JS_NULL)+ return;+ arr = JS_VALUE_TO_PTR(*pval);+ assert(new_size <= arr->size);+ if (new_size == 0) {+ js_free(ctx, arr);+ *pval = JS_NULL;+ } else {+ arr = js_shrink(ctx, arr, sizeof(JSValueArray) + new_size * sizeof(JSValue));+ arr->size = new_size;+ }+}++static JSByteArray *js_alloc_byte_array(JSContext *ctx, int size)+{+ JSByteArray *arr;+ + if (size > JS_BYTE_ARRAY_SIZE_MAX) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ arr = js_malloc(ctx, sizeof(JSByteArray) + size, JS_MTAG_BYTE_ARRAY);+ if (!arr)+ return NULL;+ arr->size = size;+ return arr;+}++static JSValue js_resize_byte_array(JSContext *ctx, JSValue val, int new_size)+{+ JSByteArray *arr, *new_arr;+ int old_size;+ JSGCRef val_ref;+ + if (val == JS_NULL) {+ arr = NULL;+ old_size = 0;+ } else {+ arr = JS_VALUE_TO_PTR(val);+ old_size = arr->size;+ }+ if (unlikely(new_size > old_size)) {+ new_size = max_int(new_size, old_size + old_size / 2);+ JS_PUSH_VALUE(ctx, val);+ new_arr = js_alloc_byte_array(ctx, new_size);+ JS_POP_VALUE(ctx, val);+ if (!new_arr)+ return JS_EXCEPTION;+ if (old_size > 0) {+ arr = JS_VALUE_TO_PTR(val);+ memcpy(new_arr->buf, arr->buf, old_size);+ }+ val = JS_VALUE_FROM_PTR(new_arr);+ }+ return val;+}++static void js_shrink_byte_array(JSContext *ctx, JSValue *pval, int new_size)+{+ JSByteArray *arr;+ if (*pval == JS_NULL)+ return;+ arr = JS_VALUE_TO_PTR(*pval);+ assert(new_size <= arr->size);+ if (new_size == 0) {+ js_free(ctx, arr);+ *pval = JS_NULL;+ } else {+ arr = js_shrink(ctx, arr, sizeof(JSByteArray) + new_size);+ arr->size = new_size;+ }+}++/* extra_size is in bytes */+static JSObject *JS_NewObjectProtoClass1(JSContext *ctx, JSValue proto, + int class_id, int extra_size)+{+ JSObject *p;+ JSGCRef proto_ref;+ extra_size = (unsigned)(extra_size + JSW - 1) / JSW;+ JS_PUSH_VALUE(ctx, proto);+ p = js_malloc(ctx, offsetof(JSObject, u) + extra_size * JSW, JS_MTAG_OBJECT);+ JS_POP_VALUE(ctx, proto);+ if (!p)+ return NULL;+ p->class_id = class_id;+ p->extra_size = extra_size;+ p->proto = proto;+ p->props = ctx->empty_props;+ return p;+}++static JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, int class_id, int extra_size)+{+ JSObject *p;+ p = JS_NewObjectProtoClass1(ctx, proto, class_id, extra_size);+ if (!p)+ return JS_EXCEPTION;+ else+ return JS_VALUE_FROM_PTR(p);+}++static JSValue JS_NewObjectClass(JSContext *ctx, int class_id, int extra_size)+{+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id, extra_size);+}++JSValue JS_NewObjectClassUser(JSContext *ctx, int class_id)+{+ JSObject *p;+ assert(class_id >= JS_CLASS_USER);+ p = JS_NewObjectProtoClass1(ctx, ctx->class_proto[class_id], class_id,+ sizeof(JSObjectUserData));+ if (!p)+ return JS_EXCEPTION;+ p->u.user.opaque = NULL;+ return JS_VALUE_FROM_PTR(p);+}++JSValue JS_NewObject(JSContext *ctx)+{+ return JS_NewObjectClass(ctx, JS_CLASS_OBJECT, 0);+}++/* same as JS_NewObject() but preallocate for 'n' properties */+JSValue JS_NewObjectPrealloc(JSContext *ctx, int n)+{+ JSValue obj;+ JSValueArray *arr;+ JSObject *p;+ JSGCRef obj_ref;+ + obj = JS_NewObjectClass(ctx, JS_CLASS_OBJECT, 0);+ if (JS_IsException(obj) || n <= 0)+ return obj;+ JS_PUSH_VALUE(ctx, obj);+ arr = js_alloc_props(ctx, n);+ JS_POP_VALUE(ctx, obj);+ if (!arr)+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(obj);+ p->props = JS_VALUE_FROM_PTR(arr);+ return obj;+}++JSValue JS_NewArray(JSContext *ctx, int initial_len)+{+ JSObject *p;+ JSValue val;+ JSGCRef val_ref;+ + val = JS_NewObjectClass(ctx, JS_CLASS_ARRAY, sizeof(JSArrayData));+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_TO_PTR(val);+ p->u.array.tab = JS_NULL;+ p->u.array.len = 0;+ if (initial_len > 0) {+ JSValueArray *arr;+ JS_PUSH_VALUE(ctx, val);+ arr = js_alloc_value_array(ctx, 0, initial_len);+ JS_POP_VALUE(ctx, val);+ if (!arr)+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(val);+ p->u.array.tab = JS_VALUE_FROM_PTR(arr);+ p->u.array.len = initial_len;+ }+ return val;+}++static inline uint32_t hash_prop(JSValue prop)+{+ return (prop / JSW) ^ (prop % JSW); /* XXX: improve */+}++/* return NULL if not found */+static force_inline JSProperty *find_own_property_inlined(JSContext *ctx,+ JSObject *p, JSValue prop)+{+ JSValueArray *arr;+ JSProperty *pr;+ uint32_t hash_mask, h, idx;+ + arr = JS_VALUE_TO_PTR(p->props);+ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);+ h = hash_prop(prop) & hash_mask;+ idx = arr->arr[2 + h]; /* JSValue, hence idx * 2 */+ while (idx != 0) {+ pr = (JSProperty *)((uint8_t *)arr->arr + idx * (sizeof(JSValue) / 2));+ if (pr->key == prop)+ return pr;+ idx = pr->hash_next; /* JSValue, hence idx * 2 */+ }+ return NULL;+}++static inline JSProperty *find_own_property(JSContext *ctx,+ JSObject *p, JSValue prop)+{+ return find_own_property_inlined(ctx, p, prop);+}++static JSValue get_special_prop(JSContext *ctx, JSValue val)+{+ int idx;+ /* 'prototype' or 'constructor' property in ROM */+ idx = JS_VALUE_GET_INT(val);+ if (idx >= 0)+ return ctx->class_proto[idx];+ else+ return ctx->class_obj[-idx - 1];+}++/* return the value or:+ - exception + - tail call : returned in case of getter and handle_getset =+ true. The function is put on the stack+*/+static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValue obj, JSValue prop,+ BOOL allow_tail_call)+{+ JSObject *p;+ JSValue proto;+ JSProperty *pr;++ if (unlikely(!JS_IsPtr(obj))) {+ if (JS_IsIntOrShortFloat(obj)) {+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]);+ } else {+ switch(JS_VALUE_GET_SPECIAL_TAG(obj)) {+ case JS_TAG_BOOL:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_BOOLEAN]);+ break;+ case JS_TAG_SHORT_FUNC:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_CLOSURE]);+ break;+ case JS_TAG_STRING_CHAR:+ goto string_proto;+ case JS_TAG_NULL:+ return JS_ThrowTypeError(ctx, "cannot read property '%"JSValue_PRI"' of null", prop);+ case JS_TAG_UNDEFINED:+ return JS_ThrowTypeError(ctx, "cannot read property '%"JSValue_PRI"' of undefined", prop);+ default:+ goto no_prop;+ }+ }+ } else {+ p = JS_VALUE_TO_PTR(obj);+ }+ if (unlikely(p->mtag != JS_MTAG_OBJECT)) {+ switch(p->mtag) {+ case JS_MTAG_FLOAT64:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]);+ break;+ case JS_MTAG_STRING:+ string_proto:+ {+ if (JS_IsInt(prop)) {+ JSValue ret;+ ret = js_string_charAt(ctx, &obj, 1, &prop, magic_internalAt);+ if (!JS_IsUndefined(ret))+ return ret;+ }+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_STRING]);+ }+ break;+ default:+ no_prop:+ return JS_ThrowTypeError(ctx, "cannot read property '%"JSValue_PRI"' of value", prop);+ }+ }++ for(;;) {+ if (p->class_id == JS_CLASS_ARRAY) {+ if (JS_IsInt(prop)) {+ uint32_t idx = JS_VALUE_GET_INT(prop);+ if (idx < p->u.array.len) {+ JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab);+ return arr->arr[idx];+ }+ } else if (JS_IsNumericProperty(ctx, prop)) {+ return JS_UNDEFINED;+ }+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ if (JS_IsInt(prop)) {+ uint32_t idx = JS_VALUE_GET_INT(prop);+ JSObject *pbuffer;+ JSByteArray *arr;+ if (idx < p->u.typed_array.len) {+ idx += p->u.typed_array.offset;+ pbuffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer);+ arr = JS_VALUE_TO_PTR(pbuffer->u.array_buffer.byte_buffer);+ switch(p->class_id) {+ default:+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ return JS_NewShortInt(*((uint8_t *)arr->buf + idx));+ case JS_CLASS_INT8_ARRAY:+ return JS_NewShortInt(*((int8_t *)arr->buf + idx));+ case JS_CLASS_INT16_ARRAY:+ return JS_NewShortInt(*((int16_t *)arr->buf + idx));+ case JS_CLASS_UINT16_ARRAY:+ return JS_NewShortInt(*((uint16_t *)arr->buf + idx));+ case JS_CLASS_INT32_ARRAY:+ return JS_NewInt32(ctx, *((int32_t *)arr->buf + idx));+ case JS_CLASS_UINT32_ARRAY:+ return JS_NewUint32(ctx, *((uint32_t *)arr->buf + idx));+ case JS_CLASS_FLOAT32_ARRAY:+ return JS_NewFloat64(ctx, *((float *)arr->buf + idx));+ case JS_CLASS_FLOAT64_ARRAY:+ return JS_NewFloat64(ctx, *((double *)arr->buf + idx));+ }+ }+ } else if (JS_IsNumericProperty(ctx, prop)) {+ return JS_UNDEFINED;+ }+ }++ pr = find_own_property(ctx, p, prop);+ if (pr) {+ if (likely(pr->prop_type == JS_PROP_NORMAL)) {+ return pr->value;+ } else if (pr->prop_type == JS_PROP_VARREF) {+ JSVarRef *pv = JS_VALUE_TO_PTR(pr->value);+ /* always detached */+ return pv->u.value;+ } else if (pr->prop_type == JS_PROP_SPECIAL) {+ return get_special_prop(ctx, pr->value);+ } else {+ JSValueArray *arr = JS_VALUE_TO_PTR(pr->value);+ JSValue getter = arr->arr[0];+ if (getter == JS_UNDEFINED)+ return JS_UNDEFINED;+ if (allow_tail_call) {+ /* It is assumed 'this_obj' is on the stack and+ that the stack has some slack to add one element. */+ ctx->sp[-1] = ctx->sp[0];+ ctx->sp[0] = getter;+ ctx->sp--;+ return JS_NewTailCall(0);+ } else {+ JSGCRef getter_ref, obj_ref;+ int err;+ JS_PUSH_VALUE(ctx, getter);+ JS_PUSH_VALUE(ctx, obj);+ err = JS_StackCheck(ctx, 2);+ JS_POP_VALUE(ctx, obj);+ JS_POP_VALUE(ctx, getter);+ if (err)+ return JS_EXCEPTION;+ JS_PushArg(ctx, getter);+ JS_PushArg(ctx, obj);+ return JS_Call(ctx, 0);+ }+ }+ }+ /* look in the prototype */+ proto = p->proto;+ if (proto == JS_NULL)+ break;+ p = JS_VALUE_TO_PTR(proto);+ }+ return JS_UNDEFINED;+}++static JSValue JS_GetProperty(JSContext *ctx, JSValue obj, JSValue prop)+{+ return JS_GetPropertyInternal(ctx, obj, prop, FALSE);+}++JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, const char *str)+{+ JSValue prop;+ JSGCRef this_obj_ref;+ + JS_PUSH_VALUE(ctx, this_obj);+ prop = JS_NewString(ctx, str);+ if (!JS_IsException(prop)) {+ prop = JS_ToPropertyKey(ctx, prop);+ }+ JS_POP_VALUE(ctx, this_obj);+ if (JS_IsException(prop))+ return prop;+ return JS_GetProperty(ctx, this_obj, prop);+}++JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue obj, uint32_t idx)+{+ if (idx > JS_SHORTINT_MAX)+ return JS_ThrowRangeError(ctx, "invalid array index");+ return JS_GetProperty(ctx, obj, JS_NewInt32(ctx, idx));+}++static BOOL JS_HasProperty(JSContext *ctx, JSValue obj, JSValue prop)+{+ JSObject *p;+ JSProperty *pr;+ + if (!JS_IsPtr(obj))+ return FALSE;+ p = JS_VALUE_TO_PTR(obj);+ if (p->mtag != JS_MTAG_OBJECT)+ return FALSE;+ for(;;) {+ pr = find_own_property(ctx, p, prop);+ if (pr)+ return TRUE;+ obj = p->proto;+ if (obj == JS_NULL)+ break;+ p = JS_VALUE_TO_PTR(obj);+ }+ return FALSE;+}++static int get_prop_hash_size_log2(int prop_count)+{+ /* XXX: adjust ? */+ if (prop_count <= 1)+ return 0;+ else+ return (32 - clz32(prop_count - 1)) - 1;+}++/* allocate 'n' properties, assuming n >= 1 */+static JSValueArray *js_alloc_props(JSContext *ctx, int n)+{+ int hash_size_log2, hash_mask, size, i, first_free;+ JSValueArray *arr;+ JSProperty *pr;+ + hash_size_log2 = get_prop_hash_size_log2(n);+ hash_mask = (1 << hash_size_log2) - 1;+ first_free = 2 + hash_mask + 1;+ size = first_free + 3 * n;+ arr = js_alloc_value_array(ctx, 0, size);+ if (!arr)+ return NULL;+ arr->arr[0] = JS_NewShortInt(0); /* no property is allocated yet */+ arr->arr[1] = JS_NewShortInt(hash_mask);+ for(i = 0; i <= hash_mask; i++)+ arr->arr[2 + i] = 0;+ pr = NULL; /* avoid warning */+ for(i = 0; i < n; i++) {+ pr = (JSProperty *)&arr->arr[2 + hash_mask + 1 + 3 * i];+ pr->key = JS_UNINITIALIZED;+ }+ /* last property */+ pr->hash_next = first_free << 1;+ return arr;+}+ +static void js_rehash_props(JSContext *ctx, JSObject *p, BOOL gc_rehash)+{+ JSValueArray *arr;+ int prop_count, hash_mask, h, idx, i, j;+ JSProperty *pr;++ arr = JS_VALUE_TO_PTR(p->props);+ if (JS_IS_ROM_PTR(ctx, arr))+ return;+ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);+ if (hash_mask == 0 && gc_rehash)+ return; /* no need to rehash if single hash entry */+ prop_count = JS_VALUE_GET_INT(arr->arr[0]);+ for(i = 0; i <= hash_mask; i++) {+ arr->arr[2 + i] = JS_NewShortInt(0);+ }+ for(i = 0, j = 0; j < prop_count; i++) {+ idx = 2 + (hash_mask + 1) + 3 * i;+ pr = (JSProperty *)&arr->arr[idx];+ if (pr->key != JS_UNINITIALIZED) {+ h = hash_prop(pr->key) & hash_mask;+ pr->hash_next = arr->arr[2 + h];+ arr->arr[2 + h] = JS_NewShortInt(idx);+ j++;+ }+ }+}++/* Compact the properties. No memory allocation is done */+static void js_compact_props(JSContext *ctx, JSObject *p)+{+ JSValueArray *arr;+ int prop_count, hash_mask, i, j, hash_size_log2;+ int new_size, new_hash_mask;+ JSProperty *pr, *pr1;+ + arr = JS_VALUE_TO_PTR(p->props);+ prop_count = JS_VALUE_GET_INT(arr->arr[0]);++ /* no property */+ if (prop_count == 0) {+ if (p->props != ctx->empty_props) {+ //js_free(ctx, p->props);+ p->props = ctx->empty_props;+ }+ return;+ }++ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);+ hash_size_log2 = get_prop_hash_size_log2(prop_count);+ new_hash_mask = min_int(hash_mask, (1 << hash_size_log2) - 1);+ new_size = 2 + new_hash_mask + 1 + 3 * prop_count;+ if (new_size >= arr->size)+ return; /* nothing to do */+ // printf("compact_props: new_size=%d size=%d hash=%d\n", new_size, arr->size, new_hash_mask);+ + arr->arr[1] = JS_NewShortInt(new_hash_mask);++ /* move the properties, skipping the deleted ones */+ for(i = 0, j = 0; j < prop_count; i++) {+ pr = (JSProperty *)&arr->arr[2 + (hash_mask + 1) + 3 * i];+ if (pr->key != JS_UNINITIALIZED) {+ pr1 = (JSProperty *)&arr->arr[2 + (new_hash_mask + 1) + 3 * j];+ *pr1 = *pr;+ j++;+ }+ }+ + js_shrink_value_array(ctx, &p->props, new_size);++ js_rehash_props(ctx, p, FALSE);+}++/* if the existing properties are in ROM, copy them to RAM. Return non zero if error */+static int js_update_props(JSContext *ctx, JSValue obj)+{+ JSObject *p;+ JSValueArray *arr, *arr1;+ JSGCRef obj_ref;+ int i, idx, prop_count, hash_mask;+ JSProperty *pr;+ + p = JS_VALUE_TO_PTR(obj);+ arr = JS_VALUE_TO_PTR(p->props);+ if (!JS_IS_ROM_PTR(ctx, arr))+ return 0;+ JS_PUSH_VALUE(ctx, obj);+ arr1 = js_alloc_value_array(ctx, 0, arr->size);+ JS_POP_VALUE(ctx, obj);+ if (!arr1)+ return -1;+ /* no rehashing is needed because all the atoms are in ROM */+ memcpy(arr1->arr, arr->arr, arr->size * sizeof(JSValue));+ prop_count = JS_VALUE_GET_INT(arr1->arr[0]);+ hash_mask = JS_VALUE_GET_INT(arr1->arr[1]);+ /* no deleted properties in ROM */+ assert(arr1->size == 2 + (hash_mask + 1) + 3 * prop_count);+ /* convert JS_PROP_SPECIAL properties ("prototype" and "constructor") */+ for(i = 0; i < prop_count; i++) {+ idx = 2 + (hash_mask + 1) + 3 * i;+ pr = (JSProperty *)&arr1->arr[idx];+ if (pr->prop_type == JS_PROP_SPECIAL) {+ pr->value = get_special_prop(ctx, pr->value);+ pr->prop_type = JS_PROP_NORMAL;+ }+ }+ + p = JS_VALUE_TO_PTR(obj);+ p->props = JS_VALUE_FROM_PTR(arr1);+ return 0;+}++/* compute 'first_free' in a property list */+static int get_first_free(JSValueArray *arr)+{+ JSProperty *pr1;+ int first_free;+ + pr1 = (JSProperty *)&arr->arr[arr->size - 3];+ if (pr1->key == JS_UNINITIALIZED)+ first_free = pr1->hash_next >> 1;+ else+ first_free = arr->size;+ return first_free;+}++/* It is assumed that the property does not already exists. */+static JSProperty *js_create_property(JSContext *ctx, JSValue obj,+ JSValue prop)+{+ JSObject *p;+ JSValueArray *arr;+ int prop_count, hash_mask, new_size, h, first_free, new_hash_mask;+ JSProperty *pr, *pr1;+ JSValue new_props;+ JSGCRef obj_ref, prop_ref;++ p = JS_VALUE_TO_PTR(obj);+ arr = JS_VALUE_TO_PTR(p->props);++ // JS_DumpValue(ctx, "create", prop);+ prop_count = JS_VALUE_GET_INT(arr->arr[0]);+ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);+ /* extend the array if no space left (this single test is valid+ even if the property list is empty) */+ pr1 = (JSProperty *)&arr->arr[arr->size - 3];+ if (pr1->key != JS_UNINITIALIZED) {+ if (p->props == ctx->empty_props) {+ /* XXX: remove and move empty_props to ROM */+ JS_PUSH_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, prop);+ arr = js_alloc_props(ctx, 1);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, obj);+ if (!arr)+ return NULL;+ p = JS_VALUE_TO_PTR(obj);+ p->props = JS_VALUE_FROM_PTR(arr);+ first_free = 3;+ } else {+ first_free = arr->size;+ new_size = first_free + 3;+ new_hash_mask = hash_mask;+ if ((prop_count + 1) > 2 * (hash_mask + 1)) {+ /* resize the hash table if too many properties */+ new_hash_mask = 2 * (hash_mask + 1) - 1;+ new_size += new_hash_mask - hash_mask;+ }+ JS_PUSH_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, prop);+ // printf("resize_props: new_size=%d hash=%d %d\n", new_size, new_hash_mask, hash_mask);+ new_props = js_resize_value_array2(ctx, p->props, new_size, 2 + new_hash_mask + 1);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(new_props))+ return NULL;+ p = JS_VALUE_TO_PTR(obj);+ p->props = new_props;+ arr = JS_VALUE_TO_PTR(p->props);+ if (new_hash_mask != hash_mask) {+ /* rebuild the hash table */+ memmove(&arr->arr[2 + (new_hash_mask + 1)],+ &arr->arr[2 + (hash_mask + 1)],+ (first_free - (2 + hash_mask + 1)) * sizeof(JSValue));+ first_free += new_hash_mask - hash_mask;+ hash_mask = new_hash_mask;+ arr->arr[1] = JS_NewShortInt(hash_mask);+ js_rehash_props(ctx, p, FALSE);+ }+ }+ /* ensure the last element is marked as uninitialized to store 'first_free' */+ pr1 = (JSProperty *)&arr->arr[arr->size - 3];+ pr1->key = JS_UNINITIALIZED;+ } else {+ first_free = pr1->hash_next >> 1;+ }++ pr = (JSProperty *)&arr->arr[first_free];+ pr->key = prop;+ pr->value = JS_UNDEFINED;+ pr->prop_type = JS_PROP_NORMAL;+ h = hash_prop(prop) & hash_mask;+ pr->hash_next = arr->arr[2 + h];+ arr->arr[2 + h] = JS_NewShortInt(first_free);+ arr->arr[0] = JS_NewShortInt(prop_count + 1);+ /* update first_free */+ first_free += 3;+ if (first_free < arr->size) {+ pr1 = (JSProperty *)&arr->arr[arr->size - 3];+ pr1->hash_next = first_free << 1;+ }++ return pr;+}++/* don't do property lookup if not present */+#define JS_DEF_PROP_LOOKUP (1 << 0)+/* return the raw property value */+#define JS_DEF_PROP_RET_VAL (1 << 1)+#define JS_DEF_PROP_HAS_VALUE (1 << 2)+#define JS_DEF_PROP_HAS_GET (1 << 3)+#define JS_DEF_PROP_HAS_SET (1 << 4)++/* XXX: handle arrays and typed arrays */+static JSValue JS_DefinePropertyInternal(JSContext *ctx, JSValue obj,+ JSValue prop, JSValue val,+ JSValue setter, int flags)+{+ JSProperty *pr;+ JSValueArray *arr;+ JSGCRef obj_ref, prop_ref, val_ref, setter_ref;+ int ret, prop_type;+ + /* move to RAM if needed */+ JS_PUSH_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, prop);+ JS_PUSH_VALUE(ctx, val);+ JS_PUSH_VALUE(ctx, setter);+ ret = js_update_props(ctx, obj);+ JS_POP_VALUE(ctx, setter);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, obj);+ if (ret)+ return JS_EXCEPTION;+ + if (flags & JS_DEF_PROP_LOOKUP) {+ pr = find_own_property(ctx, JS_VALUE_TO_PTR(obj), prop);+ if (pr) {+ if (flags & JS_DEF_PROP_HAS_VALUE) {+ if (pr->prop_type == JS_PROP_NORMAL) {+ pr->value = val;+ } else if (pr->prop_type == JS_PROP_VARREF) {+ JSVarRef *pv = JS_VALUE_TO_PTR(pr->value);+ pv->u.value = val;+ } else {+ goto error_modify;+ }+ } else if (flags & (JS_DEF_PROP_HAS_GET | JS_DEF_PROP_HAS_SET)) {+ if (pr->prop_type != JS_PROP_GETSET) {+ error_modify:+ return JS_ThrowTypeError(ctx, "cannot modify getter/setter/value kind");+ }+ arr = JS_VALUE_TO_PTR(pr->value);+ if (unlikely(JS_IS_ROM_PTR(ctx, arr))) {+ /* move to RAM */+ JSValueArray *arr2;+ JS_PUSH_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, prop);+ JS_PUSH_VALUE(ctx, val);+ JS_PUSH_VALUE(ctx, setter);+ arr2 = js_alloc_value_array(ctx, 0, 2);+ JS_POP_VALUE(ctx, setter);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, obj);+ if (!arr2)+ return JS_EXCEPTION;+ pr = find_own_property(ctx, JS_VALUE_TO_PTR(obj), prop);+ arr = JS_VALUE_TO_PTR(pr->value);+ arr2->arr[0] = arr->arr[0];+ arr2->arr[1] = arr->arr[1];+ pr->value = JS_VALUE_FROM_PTR(arr2);+ arr = arr2;+ }+ if (flags & JS_DEF_PROP_HAS_GET)+ arr->arr[0] = val;+ if (flags & JS_DEF_PROP_HAS_SET)+ arr->arr[1] = setter;+ }+ goto done;+ }+ }++ if (flags & (JS_DEF_PROP_HAS_GET | JS_DEF_PROP_HAS_SET)) {+ prop_type = JS_PROP_GETSET;+ JS_PUSH_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, prop);+ JS_PUSH_VALUE(ctx, val);+ JS_PUSH_VALUE(ctx, setter);+ arr = js_alloc_value_array(ctx, 0, 2);+ JS_POP_VALUE(ctx, setter);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, obj);+ if (!arr)+ return JS_EXCEPTION;+ arr->arr[0] = val;+ arr->arr[1] = setter;+ val = JS_VALUE_FROM_PTR(arr);+ } else if (obj == ctx->global_obj) {+ JSVarRef *pv;+ + prop_type = JS_PROP_VARREF;+ JS_PUSH_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, prop);+ JS_PUSH_VALUE(ctx, val);+ pv = js_malloc(ctx, sizeof(JSVarRef) - sizeof(JSValue), JS_MTAG_VARREF);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, obj);+ if (!pv)+ return JS_EXCEPTION;+ pv->is_detached = TRUE;+ pv->u.value = val;+ val = JS_VALUE_FROM_PTR(pv);+ } else {+ prop_type = JS_PROP_NORMAL;+ }+ JS_PUSH_VALUE(ctx, val);+ pr = js_create_property(ctx, obj, prop);+ JS_POP_VALUE(ctx, val);+ if (!pr)+ return JS_EXCEPTION;+ pr->prop_type = prop_type;+ pr->value = val;+ done:+ if (flags & JS_DEF_PROP_RET_VAL) {+ return pr->value;+ } else {+ return JS_UNDEFINED;+ }+}++static JSValue JS_DefinePropertyValue(JSContext *ctx, JSValue obj,+ JSValue prop, JSValue val)+{+ return JS_DefinePropertyInternal(ctx, obj, prop, val, JS_NULL,+ JS_DEF_PROP_LOOKUP | JS_DEF_PROP_HAS_VALUE);+}++static JSValue JS_DefinePropertyGetSet(JSContext *ctx, JSValue obj,+ JSValue prop, JSValue getter,+ JSValue setter, int flags)+{+ return JS_DefinePropertyInternal(ctx, obj, prop, getter, setter,+ JS_DEF_PROP_LOOKUP | flags);+}++/* return a JSVarRef or an exception. */+static JSValue add_global_var(JSContext *ctx, JSValue prop, BOOL define_flag)+{+ JSObject *p;+ JSProperty *pr;+ + p = JS_VALUE_TO_PTR(ctx->global_obj);+ pr = find_own_property(ctx, p, prop);+ if (pr) {+ if (pr->prop_type != JS_PROP_VARREF)+ return JS_ThrowReferenceError(ctx, "global variable '%"JSValue_PRI"' must be a reference", prop);+ if (define_flag) {+ JSVarRef *pv = JS_VALUE_TO_PTR(pr->value);+ /* define the variable if needed */+ if (pv->u.value == JS_UNINITIALIZED)+ pv->u.value = JS_UNDEFINED;+ }+ return pr->value;+ }+ return JS_DefinePropertyInternal(ctx, ctx->global_obj, prop,+ define_flag ? JS_UNDEFINED : JS_UNINITIALIZED, JS_NULL,+ JS_DEF_PROP_RET_VAL | JS_DEF_PROP_HAS_VALUE);+}++/* return JS_UNDEFINED in the normal case. Otherwise:+ - exception + - tail call : returned in case of getter and handle_getset =+ true. The function is put on the stack+*/+static JSValue JS_SetPropertyInternal(JSContext *ctx, JSValue this_obj,+ JSValue prop, JSValue val,+ BOOL allow_tail_call)+{+ JSValue proto;+ JSObject *p;+ JSProperty *pr;+ BOOL is_obj;+ + if (unlikely(!JS_IsPtr(this_obj))) {+ is_obj = FALSE;+ if (JS_IsIntOrShortFloat(this_obj)) {+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]);+ goto prototype_lookup;+ } else {+ switch(JS_VALUE_GET_SPECIAL_TAG(this_obj)) {+ case JS_TAG_BOOL:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_BOOLEAN]);+ goto prototype_lookup;+ case JS_TAG_SHORT_FUNC:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_CLOSURE]);+ goto prototype_lookup;+ case JS_TAG_STRING_CHAR:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_STRING]);+ goto prototype_lookup;+ case JS_TAG_NULL:+ return JS_ThrowTypeError(ctx, "cannot set property '%"JSValue_PRI"' of null", prop);+ case JS_TAG_UNDEFINED:+ return JS_ThrowTypeError(ctx, "cannot set property '%"JSValue_PRI"' of undefined", prop);+ default:+ goto no_prop;+ }+ }+ } else {+ is_obj = TRUE;+ p = JS_VALUE_TO_PTR(this_obj);+ }+ if (unlikely(p->mtag != JS_MTAG_OBJECT)) {+ is_obj = FALSE;+ switch(p->mtag) {+ case JS_MTAG_FLOAT64:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]);+ goto prototype_lookup;+ case JS_MTAG_STRING:+ p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_STRING]);+ goto prototype_lookup;+ default:+ no_prop:+ return JS_ThrowTypeError(ctx, "cannot set property '%"JSValue_PRI"' of value", prop);+ }+ }++ /* search if the property is already present */+ if (p->class_id == JS_CLASS_ARRAY) {+ if (JS_IsInt(prop)) {+ JSValueArray *arr;+ uint32_t idx = JS_VALUE_GET_INT(prop);+ /* not standard: we refuse to add properties to object+ except at the last position */+ if (idx < p->u.array.len) {+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ arr->arr[idx] = val;+ return JS_UNDEFINED;+ } else if (idx == p->u.array.len) {+ JSValue new_tab;+ JSGCRef this_obj_ref, val_ref;++ JS_PUSH_VALUE(ctx, this_obj);+ JS_PUSH_VALUE(ctx, val);+ new_tab = js_resize_value_array(ctx, p->u.array.tab, idx + 1);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, this_obj);+ if (JS_IsException(new_tab))+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(this_obj);+ p->u.array.tab = new_tab;+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ arr->arr[idx] = val;+ p->u.array.len++;+ return JS_UNDEFINED;+ } else {+ goto invalid_array_subscript;+ }+ } else if (JS_IsNumericProperty(ctx, prop)) {+ goto invalid_array_subscript;+ }+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ if (JS_IsInt(prop)) {+ uint32_t idx = JS_VALUE_GET_INT(prop);+ int v, conv_ret;+ double d;+ JSObject *pbuffer;+ JSByteArray *arr;+ JSGCRef val_ref, this_obj_ref;++ JS_PUSH_VALUE(ctx, this_obj);+ JS_PUSH_VALUE(ctx, val);+ switch(p->class_id) {+ case JS_CLASS_UINT8C_ARRAY:+ conv_ret = JS_ToUint8Clamp(ctx, &v, val);+ break;+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY:+ conv_ret = JS_ToNumber(ctx, &d, val);+ break;+ default:+ conv_ret = JS_ToInt32(ctx, &v, val);+ break;+ }+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, this_obj);+ if (conv_ret)+ return JS_EXCEPTION;+ + p = JS_VALUE_TO_PTR(this_obj);+ if (idx >= p->u.typed_array.len)+ goto invalid_array_subscript;+ idx += p->u.typed_array.offset;+ pbuffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer);+ arr = JS_VALUE_TO_PTR(pbuffer->u.array_buffer.byte_buffer);+ switch(p->class_id) {+ default:+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ *((uint8_t *)arr->buf + idx) = v;+ break;+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ *((uint16_t *)arr->buf + idx) = v;+ break;+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+ *((uint32_t *)arr->buf + idx) = v;+ break;+ case JS_CLASS_FLOAT32_ARRAY:+ *((float *)arr->buf + idx) = d;+ break;+ case JS_CLASS_FLOAT64_ARRAY:+ *((double *)arr->buf + idx) = d;+ break;+ }+ return JS_UNDEFINED;+ } else if (JS_IsNumericProperty(ctx, prop)) {+ invalid_array_subscript:+ return JS_ThrowTypeError(ctx, "invalid array subscript");+ }+ }++ redo:+ pr = find_own_property(ctx, p, prop);+ if (pr) {+ if (likely(pr->prop_type == JS_PROP_NORMAL)) {+ if (unlikely(JS_IS_ROM_PTR(ctx, pr)))+ goto convert_to_ram;+ pr->value = val;+ return JS_UNDEFINED;+ } else if (pr->prop_type == JS_PROP_VARREF) {+ JSVarRef *pv = JS_VALUE_TO_PTR(pr->value);+ /* always detached */+ pv->u.value = val;+ return JS_UNDEFINED;+ } else if (pr->prop_type == JS_PROP_SPECIAL) {+ JSGCRef val_ref, prop_ref, this_obj_ref;+ int err;+ convert_to_ram:+ JS_PUSH_VALUE(ctx, this_obj);+ JS_PUSH_VALUE(ctx, prop);+ JS_PUSH_VALUE(ctx, val);+ err = js_update_props(ctx, this_obj);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, prop);+ JS_POP_VALUE(ctx, this_obj);+ if (err)+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(this_obj);+ goto redo;+ } else {+ goto getset;+ }+ }++ /* search in the prototype chain (getter/setters) */+ for(;;) {+ proto = p->proto;+ if (proto == JS_NULL)+ break;+ p = JS_VALUE_TO_PTR(proto);+ prototype_lookup:+ pr = find_own_property(ctx, p, prop);+ if (pr) {+ if (unlikely(pr->prop_type == JS_PROP_GETSET)) {+ JSValueArray *arr;+ JSValue setter;+ getset:+ arr = JS_VALUE_TO_PTR(pr->value);+ setter = arr->arr[1];+ if (allow_tail_call) {+ /* It is assumed "this_obj" already is on the stack+ and that the stack has some slack to add one+ element. */+ ctx->sp[-2] = ctx->sp[0];+ ctx->sp[-1] = setter;+ ctx->sp[0] = val;+ ctx->sp -= 2;+ return JS_NewTailCall(1 | FRAME_CF_POP_RET);+ } else {+ JSGCRef val_ref, setter_ref, this_obj_ref;+ int err;+ JS_PUSH_VALUE(ctx, val);+ JS_PUSH_VALUE(ctx, setter);+ JS_PUSH_VALUE(ctx, this_obj);+ err = JS_StackCheck(ctx, 3);+ JS_POP_VALUE(ctx, this_obj);+ JS_POP_VALUE(ctx, setter);+ JS_POP_VALUE(ctx, val);+ if (err)+ return JS_EXCEPTION;+ JS_PushArg(ctx, val);+ JS_PushArg(ctx, setter);+ JS_PushArg(ctx, this_obj);+ return JS_Call(ctx, 1);+ }+ } else {+ /* stop prototype chain lookup */+ break;+ }+ }+ }+ + /* add the property in the object */+ if (!is_obj)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ return JS_DefinePropertyInternal(ctx, this_obj, prop, val, JS_UNDEFINED,+ JS_DEF_PROP_HAS_VALUE);+}++JSValue JS_SetPropertyStr(JSContext *ctx, JSValue this_obj,+ const char *str, JSValue val)+{+ JSValue prop;+ JSGCRef this_obj_ref, val_ref;+ + JS_PUSH_VALUE(ctx, this_obj);+ JS_PUSH_VALUE(ctx, val);+ prop = JS_NewString(ctx, str);+ if (!JS_IsException(prop)) {+ prop = JS_ToPropertyKey(ctx, prop);+ }+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, this_obj);+ if (JS_IsException(prop))+ return prop;+ return JS_SetPropertyInternal(ctx, this_obj, prop, val, FALSE);+}++JSValue JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj,+ uint32_t idx, JSValue val)+{+ if (idx > JS_SHORTINT_MAX)+ return JS_ThrowRangeError(ctx, "invalid array index");+ return JS_SetPropertyInternal(ctx, this_obj, JS_NewShortInt(idx), val, FALSE);+}++/* return JS_FALSE, JS_TRUE or JS_EXCEPTION. Return false only if the+ property is not configurable which is never the case here. */+static JSValue JS_DeleteProperty(JSContext *ctx, JSValue this_obj,+ JSValue prop)+{+ JSObject *p;+ JSProperty *pr, *pr1;+ JSValueArray *arr;+ int h, idx, hash_mask, last_idx, prop_count, first_free;+ JSGCRef this_obj_ref;+ + JS_PUSH_VALUE(ctx, this_obj);+ prop = JS_ToPropertyKey(ctx, prop);+ JS_POP_VALUE(ctx, this_obj);+ if (JS_IsException(prop))+ return prop;+ + /* XXX: check return value */+ if (!JS_IsPtr(this_obj))+ return JS_TRUE;+ p = JS_VALUE_TO_PTR(this_obj);+ if (p->mtag != JS_MTAG_OBJECT)+ return JS_TRUE;++ arr = JS_VALUE_TO_PTR(p->props);+ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);+ h = hash_prop(prop) & hash_mask;+ idx = JS_VALUE_GET_INT(arr->arr[2 + h]);+ last_idx = -1;+ while (idx != 0) {+ pr = (JSProperty *)(arr->arr + idx);+ if (pr->key == prop) {+ if (JS_IS_ROM_PTR(ctx, arr)) {+ JSGCRef this_obj_ref;+ int ret;+ JS_PUSH_VALUE(ctx, this_obj);+ ret = js_update_props(ctx, this_obj);+ JS_POP_VALUE(ctx, this_obj);+ if (ret)+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(this_obj);+ arr = JS_VALUE_TO_PTR(p->props);+ pr = (JSProperty *)(arr->arr + idx);+ }+ /* found: remove it */+ if (last_idx >= 0) {+ JSProperty *lpr = (JSProperty *)(arr->arr + last_idx);+ lpr->hash_next = pr->hash_next;+ } else {+ arr->arr[2 + h] = pr->hash_next;+ }+ first_free = get_first_free(arr);++ prop_count = JS_VALUE_GET_INT(arr->arr[0]);+ arr->arr[0] = JS_NewShortInt(prop_count - 1);+ pr->prop_type = JS_PROP_NORMAL;+ pr->key = JS_UNINITIALIZED;+ pr->value = JS_UNDEFINED;++ /* update first_free if needed */+ while (first_free > 2 + hash_mask + 1) {+ pr1 = (JSProperty *)&arr->arr[first_free - 3];+ if (pr1->key != JS_UNINITIALIZED)+ break;+ first_free -= 3;+ }++ /* update first_free */+ if (first_free < arr->size) {+ pr1 = (JSProperty *)&arr->arr[arr->size - 3];+ pr1->hash_next = first_free << 1;+ }++ /* compact the properties if needed */+ if ((2 + hash_mask + 1 + 3 * prop_count) < arr->size / 2)+ js_compact_props(ctx, p);+ return JS_TRUE;+ }+ last_idx = idx;+ idx = pr->hash_next >> 1;+ }+ /* not found */+ return JS_TRUE;+}++static JSValue stdlib_init_class(JSContext *ctx, const JSROMClass *class_def)+{+ JSValue obj, proto, parent_class, parent_proto;+ JSGCRef parent_class_ref;+ JSObject *p;+ int ctor_idx = class_def->ctor_idx;++ if (ctor_idx >= 0) {+ int class_id = ctx->c_function_table[ctor_idx].magic;+ obj = ctx->class_obj[class_id];+ if (!JS_IsNull(obj))+ return obj; /* already defined */++ /* initialize the parent class if necessary */+ if (!JS_IsNull(class_def->parent_class)) {+ JSROMClass *parent_class_def = JS_VALUE_TO_PTR(class_def->parent_class);+ int parent_class_id;+ parent_class = stdlib_init_class(ctx, parent_class_def);+ parent_class_id = ctx->c_function_table[parent_class_def->ctor_idx].magic;+ parent_proto = ctx->class_proto[parent_class_id];+ } else {+ parent_class = JS_NULL;+ parent_proto = ctx->class_proto[JS_CLASS_OBJECT];+ }+ /* initialize the prototype before. It is already defined only+ for Object and Function */+ proto = ctx->class_proto[class_id];+ if (JS_IsNull(proto)) {+ JS_PUSH_VALUE(ctx, parent_class);+ proto = JS_NewObjectProtoClass(ctx, parent_proto, JS_CLASS_OBJECT, 0);+ JS_POP_VALUE(ctx, parent_class);+ ctx->class_proto[class_id] = proto;+ }+ p = JS_VALUE_TO_PTR(proto);+ if (!JS_IsNull(class_def->proto_props))+ p->props = class_def->proto_props;++ if (JS_IsNull(parent_class))+ parent_class = ctx->class_proto[JS_CLASS_CLOSURE];+ obj = js_new_c_function_proto(ctx, ctor_idx, parent_class, FALSE, JS_NULL);+ ctx->class_obj[class_id] = obj;+ } else {+ /* normal object */+ obj = JS_NewObject(ctx);+ }+ p = JS_VALUE_TO_PTR(obj);+ if (!JS_IsNull(class_def->props)) {+ /* set the properties from the ROM. They are copied to RAM+ when modified */+ p->props = class_def->props;+ } + return obj;+}++static void stdlib_init(JSContext *ctx, const JSValueArray *arr)+{+ JSValue name, val;+ int i;++ for(i = 0; i < arr->size; i += 2) {+ name = arr->arr[i];+ val = arr->arr[i + 1];+ if (JS_IsObject(ctx, val)) {+ val = stdlib_init_class(ctx, JS_VALUE_TO_PTR(val));+ } else if (val == JS_NULL) {+ val = ctx->global_obj;+ }+ JS_DefinePropertyInternal(ctx, ctx->global_obj, name,+ val, JS_NULL,+ JS_DEF_PROP_HAS_VALUE);+ }+}++static void dummy_write_func(void *opaque, const void *buf, size_t buf_len)+{+ // fwrite(buf, 1, buf_len, stdout);+}++/* if prepare_compilation is true, the context will be used to compile+ to a binary file. It is not expected to be used in the embedded+ version */+JSContext *JS_NewContext2(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def, BOOL prepare_compilation)+{+ JSContext *ctx;+ JSValueArray *arr;+ int i, mem_align;++#ifdef JS_PTR64+ mem_align = 8;+#else+ mem_align = 4;+#endif+ mem_size = mem_size & ~(mem_align - 1);+ assert(mem_size >= 1024);+ assert(((uintptr_t)mem_start & (mem_align - 1)) == 0);++ ctx = mem_start;+ memset(ctx, 0, sizeof(*ctx));+ ctx->class_count = stdlib_def->class_count;+ ctx->class_obj = ctx->class_proto + ctx->class_count;+ ctx->heap_base = (void *)(ctx->class_proto + 2 * ctx->class_count);+ ctx->heap_free = ctx->heap_base;+ ctx->stack_top = mem_start + mem_size;+ ctx->sp = (JSValue *)ctx->stack_top;+ ctx->stack_bottom = ctx->sp;+ ctx->fp = ctx->sp;+ ctx->min_free_size = JS_MIN_FREE_SIZE;+#ifdef DEBUG_GC+ ctx->dummy_block = JS_NULL;+ ctx->unique_strings = JS_NULL;+#endif + ctx->random_state = 1;+ ctx->write_func = dummy_write_func;+ for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++)+ ctx->string_pos_cache[i].str = JS_NULL;++ if (prepare_compilation) {+ int atom_table_len;+ JSValueArray *arr, *arr1;+ uint8_t *ptr;+ + /* for compilation, no stdlib is needed. Only the atoms+ corresponding to JS_ATOM_x are needed and they are stored+ in RAM. */+ /* copy the atoms to a fixed location at the beginning of the+ heap */+ ctx->atom_table = (JSWord *)ctx->heap_free;+ atom_table_len = stdlib_def->sorted_atoms_offset;+ memcpy(ctx->heap_free, stdlib_def->stdlib_table,+ atom_table_len * sizeof(JSWord));+ ctx->heap_free += atom_table_len * sizeof(JSWord);++ /* allocate the sorted atom table and populate it */+ arr1 = (JSValueArray *)(stdlib_def->stdlib_table + atom_table_len);+ arr = js_alloc_value_array(ctx, 0, arr1->size);+ ctx->unique_strings = JS_VALUE_FROM_PTR(arr);+ for(i = 0; i < arr1->size; i++) {+ ptr = JS_VALUE_TO_PTR(arr1->arr[i]);+ ptr = ptr - (uint8_t *)stdlib_def->stdlib_table ++ (uint8_t *)ctx->atom_table;+ arr->arr[i] = JS_VALUE_FROM_PTR(ptr);+ }+ ctx->unique_strings_len = arr1->size;+ } else {+ ctx->atom_table = stdlib_def->stdlib_table;+ ctx->rom_atom_tables[0] = (JSValueArray *)(stdlib_def->stdlib_table ++ stdlib_def->sorted_atoms_offset);+ ctx->n_rom_atom_tables = 1;+ ctx->c_function_table = stdlib_def->c_function_table;+ ctx->c_finalizer_table = stdlib_def->c_finalizer_table;+ ctx->unique_strings = JS_NULL;+ ctx->unique_strings_len = 0;+ }+ + + ctx->current_exception = JS_UNDEFINED;+#ifdef DEBUG_GC+ /* set the dummy block at the start of the memory */+ {+ JSByteArray *barr;+ barr = js_alloc_byte_array(ctx, (min_int(mem_size / 2, 1 << 17)) & ~(JSW - 1));+ ctx->dummy_block = JS_VALUE_FROM_PTR(barr);+ }+#endif++ arr = js_alloc_value_array(ctx, 0, 3);+ arr->arr[0] = JS_NewShortInt(0); /* prop_count */+ arr->arr[1] = JS_NewShortInt(0); /* hash_mark */+ arr->arr[2] = JS_NewShortInt(0); /* hash_table[1] */+ ctx->empty_props = JS_VALUE_FROM_PTR(arr);+ for(i = 0; i < ctx->class_count; i++)+ ctx->class_proto[i] = JS_NULL;+ for(i = 0; i < ctx->class_count; i++)+ ctx->class_obj[i] = JS_NULL;+ /* must be done first so that the prototype of Object.prototype is+ JS_NULL */+ ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObject(ctx); + /* must be done for proper function init */+ ctx->class_proto[JS_CLASS_CLOSURE] = JS_NewObject(ctx); ++ ctx->global_obj = JS_NewObject(ctx);+ ctx->minus_zero = js_alloc_float64(ctx, -0.0); /* XXX: use a ROM value instead */+ + if (!prepare_compilation) {+ stdlib_init(ctx, (JSValueArray *)(stdlib_def->stdlib_table + stdlib_def->global_object_offset));+ }+ + return ctx;+}++JSContext *JS_NewContext(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def)+{+ return JS_NewContext2(mem_start, mem_size, stdlib_def, FALSE);+}++void JS_FreeContext(JSContext *ctx)+{+ /* call the user C finalizers */+ uint8_t *ptr;+ int size;+ JSObject *p;+ + ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ size = get_mblock_size(ptr);+ p = (JSObject *)ptr;+ if (p->mtag == JS_MTAG_OBJECT && p->class_id >= JS_CLASS_USER &&+ ctx->c_finalizer_table[p->class_id - JS_CLASS_USER] != NULL) {+ ctx->c_finalizer_table[p->class_id - JS_CLASS_USER](ctx, p->u.user.opaque);+ }+ ptr += size;+ }+}++void JS_SetContextOpaque(JSContext *ctx, void *opaque)+{+ ctx->opaque = opaque;+}++void JS_SetInterruptHandler(JSContext *ctx, JSInterruptHandler *interrupt_handler)+{+ ctx->interrupt_handler = interrupt_handler;+}++void JS_SetLogFunc(JSContext *ctx, JSWriteFunc *write_func)+{+ ctx->write_func = write_func;+}++void JS_SetRandomSeed(JSContext *ctx, uint64_t seed)+{+ ctx->random_state = seed;+}++JSValue JS_GetGlobalObject(JSContext *ctx)+{+ return ctx->global_obj;+}++static JSValue get_var_ref(JSContext *ctx, JSValue *pfirst_var_ref, JSValue *pval)+{+ JSValue val;+ JSVarRef *p;+ + val = *pfirst_var_ref;+ for(;;) {+ if (val == JS_NULL)+ break;+ p = JS_VALUE_TO_PTR(val);+ assert(!p->is_detached);+ if (p->u.pvalue == pval)+ return val;+ val = p->u.next;+ }++ p = js_malloc(ctx, sizeof(JSVarRef), JS_MTAG_VARREF);+ if (!p)+ return JS_EXCEPTION;+ p->is_detached = FALSE;+ p->u.pvalue = pval;+ p->u.next = *pfirst_var_ref;+ val = JS_VALUE_FROM_PTR(p);+ *pfirst_var_ref = val;+ return val;+}++#define FRAME_OFFSET_ARG0 4+#define FRAME_OFFSET_FUNC_OBJ 3+#define FRAME_OFFSET_THIS_OBJ 2+#define FRAME_OFFSET_CALL_FLAGS 1+#define FRAME_OFFSET_SAVED_FP 0+#define FRAME_OFFSET_CUR_PC (-1) /* current pc_offset */+#define FRAME_OFFSET_FIRST_VARREF (-2)+#define FRAME_OFFSET_VAR0 (-3)++/* stack layout:+ + padded_args (padded_argc - argc)+ args (argc)+ func_obj fp[3]+ this_obj fp[2]+ call_flags (int) fp[1]+ saved_fp (int) fp[0]+ cur_pc (int) fp[-1]+ first_var_ref (val) fp[-2]+ vars (var_count)+ temp stack (pointed by sp)+*/++#define SP_TO_VALUE(ctx, fp) JS_NewShortInt((uint8_t *)(fp) - (uint8_t *)ctx)+#define VALUE_TO_SP(ctx, val) (void *)((uint8_t *)ctx + JS_VALUE_GET_INT(val))++/* buf_end points to the end of the buffer (after the final '\0') */+static __js_printf_like(3, 4) void cprintf(char **pp, char *buf_end, const char *fmt, ...)+{+ char *p;+ va_list ap;+ + p = *pp;+ if ((p + 1) >= buf_end)+ return;+ va_start(ap, fmt);+ js_vsnprintf(p, buf_end - p, fmt, ap);+ va_end(ap);+ p += strlen(p);+ *pp = p;+}++static JSValue reloc_c_func_name(JSContext *ctx, JSValue val)+{+ return val;+}++/* no memory allocation is done */+/* XXX: handle bound functions */+static JSValue js_function_get_length_name1(JSContext *ctx, JSValue *this_val,+ int is_name, JSFunctionBytecode **pb)+{+ int short_func_idx;+ const JSCFunctionDef *fd;+ JSValue ret;++ if (!JS_IsPtr(*this_val)) {+ if (JS_VALUE_GET_SPECIAL_TAG(*this_val) != JS_TAG_SHORT_FUNC)+ goto fail;+ short_func_idx = JS_VALUE_GET_SPECIAL_VALUE(*this_val);+ goto short_func;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(*this_val);+ JSFunctionBytecode *b;+ if (p->mtag != JS_MTAG_OBJECT)+ goto fail;+ if (p->class_id == JS_CLASS_CLOSURE) {+ b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode);+ if (is_name) {+ /* XXX: directly set func_name to the empty string ? */+ if (b->func_name == JS_NULL)+ ret = js_get_atom(ctx, JS_ATOM_empty);+ else+ ret = b->func_name;+ } else {+ ret = JS_NewShortInt(b->arg_count);+ }+ *pb = b;+ return ret;+ } else if (p->class_id == JS_CLASS_C_FUNCTION) {+ short_func_idx = p->u.cfunc.idx;+ short_func:+ fd = &ctx->c_function_table[short_func_idx];+ if (is_name) {+ ret = reloc_c_func_name(ctx, fd->name);+ } else {+ ret = JS_NewShortInt(fd->arg_count);+ }+ *pb = NULL;+ return ret;+ } else {+ fail:+ *pb = NULL;+ return JS_NULL;+ }+ }+}++static uint32_t get_bit(const uint8_t *buf, uint32_t index)+{+ return (buf[index >> 3] >> (7 - (index & 7))) & 1;+}++static uint32_t get_bits_slow(const uint8_t *buf, uint32_t index, int n)+{+ int i;+ uint32_t val;+ val = 0;+ for(i = 0; i < n; i++)+ val |= get_bit(buf, index + i) << (n - 1 - i);+ return val;+}++static uint32_t get_bits(const uint8_t *buf, uint32_t buf_len,+ uint32_t index, int n)+{+ uint32_t val, pos;++ pos = index >> 3;+ if (unlikely(n > 25 || (pos + 3) >= buf_len)) {+ /* slow case */+ return get_bits_slow(buf, index, n);+ } else {+ /* fast case */+ val = get_be32(buf + pos);+ return (val >> (32 - (index & 7) - n)) & ((1 << n) - 1);+ }+}++static uint32_t get_ugolomb(const uint8_t *buf, uint32_t buf_len,+ uint32_t *pindex)+{+ uint32_t index = *pindex;+ int i;+ uint32_t v;+ + i = 0;+ for(;;) {+ if (get_bit(buf, index++))+ break;+ i++;+ if (i == 32) {+ /* error */+ *pindex = index;+ return 0xffffffff;+ }+ }+ if (i == 0) {+ v = 0;+ } else {+ v = ((1 << i) | get_bits(buf, buf_len, index, i)) - 1;+ index += i;+ }+ *pindex = index;+ // printf("get_ugolomb: v=%d\n", v);+ return v;+}++static int32_t get_sgolomb(const uint8_t *buf, uint32_t buf_len,+ uint32_t *pindex)+{+ uint32_t val;+ val = get_ugolomb(buf, buf_len, pindex);+ return (val >> 1) ^ -(val & 1);+}++static int get_pc2line_hoisted_code_len(const uint8_t *buf, size_t buf_len)+{+ size_t i = buf_len;+ int v = 0;+ while (i > 0) {+ i--;+ v = (v << 7) | (buf[i] & 0x7f);+ if ((buf[i] & 0x80) == 0)+ break;+ }+ return v;+}++/* line_num, col_num and index are updated */+static void get_pc2line(int *pline_num, int *pcol_num, const uint8_t *buf,+ uint32_t buf_len, uint32_t *pindex, BOOL has_column)+{+ int line_delta, line_num, col_num, col_delta;++ line_num = *pline_num;+ col_num = *pcol_num;+ + line_delta = get_sgolomb(buf, buf_len, pindex);+ line_num += line_delta;+ if (has_column) {+ if (line_delta == 0) {+ col_delta = get_sgolomb(buf, buf_len, pindex);+ col_num += col_delta;+ } else {+ col_num = get_ugolomb(buf, buf_len, pindex) + 1;+ }+ } else {+ col_num = 0;+ }+ *pline_num = line_num;+ *pcol_num = col_num;+}++/* return 0 if line/col number info */+static int find_line_col(int *pcol_num, JSFunctionBytecode *b, uint32_t pc)+{+ JSByteArray *arr, *pc2line;+ int pos, op, line_num, col_num;+ uint32_t pc2line_pos;+ + if (b->pc2line == JS_NULL)+ goto fail;+ arr = JS_VALUE_TO_PTR(b->byte_code);+ pc2line = JS_VALUE_TO_PTR(b->pc2line);++ /* skip the hoisted code */+ pos = get_pc2line_hoisted_code_len(pc2line->buf, pc2line->size);+ if (pc < pos)+ pc = pos;+ pc2line_pos = 0;+ line_num = 1;+ col_num = 1;+ while (pos < arr->size) {+ get_pc2line(&line_num, &col_num, pc2line->buf, pc2line->size,+ &pc2line_pos, b->has_column);+ if (pos == pc) {+ *pcol_num = col_num;+ return line_num;+ }+ op = arr->buf[pos];+ pos += opcode_info[op].size;+ }+ fail:+ *pcol_num = 0;+ return 0;+}++static const char *get_func_name(JSContext *ctx, JSValue func_obj,+ JSCStringBuf *str_buf, JSFunctionBytecode **pb)+{+ JSValue val;+ val = js_function_get_length_name1(ctx, &func_obj, 1, pb);+ if (JS_IsNull(val))+ return NULL;+ return JS_ToCString(ctx, val, str_buf);+}++static void build_backtrace(JSContext *ctx, JSValue error_obj,+ const char *filename, int line_num, int col_num, int skip_level)+{+ JSObject *p1;+ char buf[128], *p, *buf_end, *line_start;+ const char *str;+ JSValue *fp, stack_str;+ JSCStringBuf str_buf;+ JSFunctionBytecode *b;+ int level;+ JSGCRef error_obj_ref;+ + if (!JS_IsError(ctx, error_obj))+ return;+ p = buf;+ buf_end = buf + sizeof(buf);+ p[0] = '\0';+ if (filename) {+ cprintf(&p, buf_end, " at %s:%d:%d\n", filename, line_num, col_num);+ }+ fp = ctx->fp;+ level = 0;+ while (fp != (JSValue *)ctx->stack_top && level < 10) {+ if (skip_level != 0) {+ skip_level--;+ } else {+ line_start = p;+ str = get_func_name(ctx, fp[FRAME_OFFSET_FUNC_OBJ], &str_buf, &b);+ if (!str)+ str = "<anonymous>";+ cprintf(&p, buf_end, " at %s", str);+ if (b) {+ int pc, line_num, col_num;+ const char *filename;+ filename = JS_ToCString(ctx, b->filename, &str_buf);+ pc = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CUR_PC]) - 1;+ line_num = find_line_col(&col_num, b, pc);+ cprintf(&p, buf_end, " (%s", filename);+ if (line_num != 0) {+ cprintf(&p, buf_end, ":%d", line_num);+ if (col_num != 0)+ cprintf(&p, buf_end, ":%d", col_num);+ }+ cprintf(&p, buf_end, ")");+ } else {+ cprintf(&p, buf_end, " (native)");+ }+ cprintf(&p, buf_end, "\n");+ /* if truncated line, remove it and stop */+ if ((p + 1) >= buf_end) {+ *line_start = '\0';+ break;+ }+ level++;+ }+ fp = VALUE_TO_SP(ctx, fp[FRAME_OFFSET_SAVED_FP]);+ }++ JS_PUSH_VALUE(ctx, error_obj);+ stack_str = JS_NewString(ctx, buf);+ JS_POP_VALUE(ctx, error_obj);+ p1 = JS_VALUE_TO_PTR(error_obj);+ p1->u.error.stack = stack_str;+}++#define HINT_STRING 0+#define HINT_NUMBER 1+#define HINT_NONE HINT_NUMBER++static JSValue JS_ToPrimitive(JSContext *ctx, JSValue val, int hint)+{+ int i, atom;+ JSValue method, ret;+ JSGCRef val_ref, method_ref;+ + if (JS_IsPrimitive(ctx, val))+ return val;+ for(i = 0; i < 2; i++) {+ if ((i ^ hint) == 0) {+ atom = JS_ATOM_toString;+ } else {+ atom = JS_ATOM_valueOf;+ }+ JS_PUSH_VALUE(ctx, val);+ method = JS_GetProperty(ctx, val, js_get_atom(ctx, atom));+ JS_POP_VALUE(ctx, val);+ if (JS_IsException(method))+ return method;+ if (JS_IsFunction(ctx, method)) {+ int err;+ JS_PUSH_VALUE(ctx, method);+ JS_PUSH_VALUE(ctx, val);+ err = JS_StackCheck(ctx, 2);+ JS_POP_VALUE(ctx, val);+ JS_POP_VALUE(ctx, method);+ if (err)+ return JS_EXCEPTION;++ JS_PushArg(ctx, method);+ JS_PushArg(ctx, val);+ JS_PUSH_VALUE(ctx, val);+ ret = JS_Call(ctx, 0);+ JS_POP_VALUE(ctx, val);+ if (JS_IsException(ret))+ return ret;+ if (!JS_IsObject(ctx, ret))+ return ret;+ }+ }+ return JS_ThrowTypeError(ctx, "toPrimitive");+}++/* return a string or an exception */+static JSValue js_dtoa2(JSContext *ctx, double d, int radix, int n_digits, int flags)+{+ int len_max, len;+ JSValue str;+ JSGCRef str_ref;+ JSByteArray *tmp_arr, *p;++ len_max = js_dtoa_max_len(d, radix, n_digits, flags);+ p = js_alloc_byte_array(ctx, len_max + 1);+ if (!p)+ return JS_EXCEPTION;+ /* allocate the temporary buffer */+ str = JS_VALUE_FROM_PTR(p);+ JS_PUSH_VALUE(ctx, str);+ tmp_arr = js_alloc_byte_array(ctx, sizeof(JSDTOATempMem));+ JS_POP_VALUE(ctx, str);+ if (!tmp_arr)+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(str);+ /* Note: tmp_arr->buf is always 32 bit aligned */+ len = js_dtoa((char *)p->buf, d, radix, n_digits, flags, (JSDTOATempMem *)tmp_arr->buf);+ js_free(ctx, tmp_arr);+ return js_byte_array_to_string(ctx, str, len, TRUE);+}+ +JSValue JS_ToString(JSContext *ctx, JSValue val)+{+ char buf[128];+ int atom;+ const char *str;+ + redo:+ if (JS_IsInt(val)) {+ int len;+ len = i32toa(buf, JS_VALUE_GET_INT(val));+ buf[len] = '\0';+ goto ret_buf;+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ return js_dtoa2(ctx, js_get_short_float(val), 10, 0, JS_DTOA_FORMAT_FREE);+ } else+#endif+ if (JS_IsPtr(val)) {+ void *ptr = JS_VALUE_TO_PTR(val);+ int mtag = js_get_mtag(ptr);+ switch(mtag) {+ case JS_MTAG_OBJECT:+ to_primitive:+ val = JS_ToPrimitive(ctx, val, HINT_STRING);+ if (JS_IsException(val))+ return val;+ goto redo;+ case JS_MTAG_STRING:+ return val;+ case JS_MTAG_FLOAT64:+ {+ JSFloat64 *p = ptr;+ return js_dtoa2(ctx, p->u.dval, 10, 0, JS_DTOA_FORMAT_FREE);+ }+ default:+ js_snprintf(buf, sizeof(buf), "[mtag %d]", mtag);+ goto ret_buf;+ }+ } else {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_NULL:+ atom = JS_ATOM_null;+ goto ret_atom;+ case JS_TAG_UNDEFINED:+ atom = JS_ATOM_undefined;+ goto ret_atom;+ case JS_TAG_BOOL:+ if (JS_VALUE_GET_SPECIAL_VALUE(val))+ atom = JS_ATOM_true;+ else+ atom = JS_ATOM_false;+ ret_atom:+ return js_get_atom(ctx, atom);+ case JS_TAG_STRING_CHAR:+ return val;+ case JS_TAG_SHORT_FUNC:+ goto to_primitive;+ default:+ str = "?";+ goto ret_str;+ ret_buf:+ str = buf;+ ret_str:+ return JS_NewString(ctx, str);+ }+ }+}++/* return either a unique string or an integer. Strings representing+ a short integer are converted to short integer */+static JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val)+{+ int32_t n;+ if (JS_IsInt(val))+ return val;+ val = JS_ToString(ctx, val);+ if (JS_IsException(val))+ return val;+ if (is_num_string(ctx, &n, val))+ return JS_NewShortInt(n);+ else+ return JS_MakeUniqueString(ctx, val);+}++static int skip_spaces(const char *p1)+{+ const char *p = p1;+ int c;+ for(;;) {+ c = *p;+ if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))+ break;+ p++;+ }+ return p - p1;+}++/* JS_ToString() specific behaviors */+#define JS_ATOD_TOSTRING (1 << 8)++/* 'val' must be a string */+static int js_atod1(JSContext *ctx, double *pres, JSValue val,+ int radix, int flags)+{+ JSString *p;+ JSByteArray *tmp_arr;+ double d;+ JSGCRef val_ref;+ const char *p1;++ if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) {+ int c = JS_VALUE_GET_SPECIAL_VALUE(val);+ if (c >= '0' && c <= '9') {+ *pres = c - '0';+ } else {+ *pres = NAN;+ }+ return 0;+ }+ + JS_PUSH_VALUE(ctx, val);+ tmp_arr = js_alloc_byte_array(ctx, sizeof(JSATODTempMem));+ JS_POP_VALUE(ctx, val);+ if (!tmp_arr) {+ *pres = NAN;+ return -1;+ }+ p = JS_VALUE_TO_PTR(val);+ p1 = (char *)p->buf;+ p1 += skip_spaces(p1);+ if ((p1 - (char *)p->buf) == p->len) {+ if (flags & JS_ATOD_TOSTRING)+ d = 0;+ else+ d = NAN;+ goto done;+ }+ d = js_atod(p1, &p1, radix, flags, (JSATODTempMem *)tmp_arr->buf);+ js_free(ctx, tmp_arr);+ if (flags & JS_ATOD_TOSTRING) {+ p1 += skip_spaces(p1);+ if ((p1 - (char *)p->buf) < p->len)+ d = NAN;+ }+ done:+ *pres = d;+ return 0;+}++/* Note: can fail due to memory allocation even if primitive type */+int JS_ToNumber(JSContext *ctx, double *pres, JSValue val)+{+ redo:+ if (JS_IsInt(val)) {+ *pres = (double)JS_VALUE_GET_INT(val);+ return 0;+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ *pres = js_get_short_float(val);+ return 0;+ } else+#endif+ if (JS_IsPtr(val)) {+ void *ptr = JS_VALUE_TO_PTR(val);+ switch(js_get_mtag(ptr)) {+ case JS_MTAG_STRING:+ goto atod;+ case JS_MTAG_FLOAT64:+ {+ JSFloat64 *p = ptr;+ *pres = p->u.dval;+ return 0;+ }+ case JS_MTAG_OBJECT:+ val = JS_ToPrimitive(ctx, val, HINT_NUMBER);+ if (JS_IsException(val)) {+ *pres = NAN;+ return -1;+ }+ goto redo;+ default:+ *pres = NAN;+ return 0;+ }+ } else {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_NULL:+ case JS_TAG_BOOL:+ *pres = (double)JS_VALUE_GET_SPECIAL_VALUE(val);+ return 0;+ case JS_TAG_UNDEFINED:+ *pres = NAN;+ return 0;+ case JS_TAG_STRING_CHAR:+ atod:+ return js_atod1(ctx, pres, val, 0,+ JS_ATOD_ACCEPT_BIN_OCT | JS_ATOD_TOSTRING);+ default:+ *pres = NAN;+ return 0;+ }+ }+}++static int JS_ToInt32Internal(JSContext *ctx, int *pres, JSValue val, BOOL sat_flag)+{+ int32_t ret;+ double d;++ if (JS_IsInt(val)) {+ ret = JS_VALUE_GET_INT(val);+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ d = js_get_short_float(val);+ goto handle_float64;+ } else+#endif+ if (JS_IsPtr(val)) {+ uint64_t u;+ int e;++ handle_number:+ if (JS_ToNumber(ctx, &d, val)) {+ *pres = 0;+ return -1;+ }+#ifdef JS_USE_SHORT_FLOAT+ handle_float64:+#endif + u = float64_as_uint64(d);+ e = (u >> 52) & 0x7ff;+ if (likely(e <= (1023 + 30))) {+ /* fast case */+ ret = (int32_t)d;+ } else if (!sat_flag) {+ if (e <= (1023 + 30 + 53)) {+ uint64_t v;+ /* remainder modulo 2^32 */+ v = (u & (((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 >> 63)+ ret = -ret;+ } else {+ ret = 0; /* also handles NaN and +inf */+ }+ } else {+ if (e == 2047 && (u & (((uint64_t)1 << 52) - 1)) != 0) {+ /* nan */+ ret = 0;+ } else {+ /* take the sign into account */+ if (u >> 63)+ ret = 0x80000000;+ else+ ret = 0x7fffffff;+ }+ }+ } else {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_VALUE_GET_SPECIAL_VALUE(val);+ break;+ default:+ goto handle_number;+ }+ }+ *pres = ret;+ return 0;+}++int JS_ToInt32(JSContext *ctx, int *pres, JSValue val)+{+ return JS_ToInt32Internal(ctx, pres, val, FALSE);+}++int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val)+{+ return JS_ToInt32Internal(ctx, (int *)pres, val, FALSE);+}++int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val)+{+ return JS_ToInt32Internal(ctx, pres, val, TRUE);+}++static int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValue val,+ int min, int max, int min_offset)+{+ int res = JS_ToInt32Sat(ctx, pres, 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_ToUint8Clamp(JSContext *ctx, int *pres, JSValue val)+{+ int32_t ret;+ double d;++ if (JS_IsInt(val)) {+ ret = JS_VALUE_GET_INT(val);+ if (ret < 0)+ ret = 0;+ else if (ret > 255)+ ret = 255;+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ d = js_get_short_float(val);+ goto handle_float64;+ } else+#endif+ if (JS_IsPtr(val)) {+ handle_number:+ if (JS_ToNumber(ctx, &d, val)) {+ *pres = 0;+ return -1;+ }+#ifdef JS_USE_SHORT_FLOAT+ handle_float64:+#endif + if (d < 0 || isnan(d))+ ret = 0;+ else if (d > 255)+ ret = 255;+ else+ ret = js_lrint(d);+ } else {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_VALUE_GET_SPECIAL_VALUE(val);+ break;+ default:+ goto handle_number;+ }+ }+ *pres = ret;+ return 0;+}++static int js_get_length32(JSContext *ctx, uint32_t *pres, JSValue obj)+{+ JSValue len_val;+ len_val = JS_GetProperty(ctx, obj, js_get_atom(ctx, JS_ATOM_length));+ if (JS_IsException(len_val)) {+ *pres = 0;+ return -1;+ }+ return JS_ToUint32(ctx, pres, len_val);+}++static no_inline JSValue js_add_slow(JSContext *ctx)+{+ JSValue *op1, *op2;+ + op1 = &ctx->sp[1];+ op2 = &ctx->sp[0];+ *op1 = JS_ToPrimitive(ctx, *op1, HINT_NONE);+ if (JS_IsException(*op1))+ return JS_EXCEPTION;+ *op2 = JS_ToPrimitive(ctx, *op2, HINT_NONE);+ if (JS_IsException(*op2))+ return JS_EXCEPTION;+ if (JS_IsString(ctx, *op1) || JS_IsString(ctx, *op2)) {+ *op1 = JS_ToString(ctx, *op1);+ if (JS_IsException(*op1))+ return JS_EXCEPTION;+ *op2 = JS_ToString(ctx, *op2);+ if (JS_IsException(*op2))+ return JS_EXCEPTION;+ return JS_ConcatString(ctx, *op1, *op2);+ } else {+ double d1, d2, r;+ /* cannot fail */+ if (JS_ToNumber(ctx, &d1, *op1))+ return JS_EXCEPTION;+ if (JS_ToNumber(ctx, &d2, *op2))+ return JS_EXCEPTION;+ r = d1 + d2;+ return JS_NewFloat64(ctx, r);+ }+}++static no_inline JSValue js_binary_arith_slow(JSContext *ctx, OPCodeEnum op)+{+ double d1, d2, r;++ if (JS_ToNumber(ctx, &d1, ctx->sp[1]))+ return JS_EXCEPTION;++ if (JS_ToNumber(ctx, &d2, ctx->sp[0]))+ return JS_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 = js_fmod(d1, d2);+ break;+ case OP_pow:+ r = js_pow(d1, d2);+ break;+ default:+ abort();+ }+ return JS_NewFloat64(ctx, r);+}++static no_inline JSValue js_unary_arith_slow(JSContext *ctx, OPCodeEnum op)+{+ double d;+ + if (JS_ToNumber(ctx, &d, ctx->sp[0]))+ return JS_EXCEPTION;++ switch(op) {+ case OP_inc:+ d++;+ break;+ case OP_dec:+ d--;+ break;+ case OP_plus:+ break;+ case OP_neg:+ d = -d;+ break;+ default:+ abort();+ }+ return JS_NewFloat64(ctx, d);+}++/* specific case necessary for correct return value semantics */+static no_inline JSValue js_post_inc_slow(JSContext *ctx, OPCodeEnum op)+{+ JSValue val;+ double d, r;+ + if (JS_ToNumber(ctx, &d, ctx->sp[0]))+ return JS_EXCEPTION;+ r = d + 2 * (op - OP_post_dec) - 1;+ val = JS_NewFloat64(ctx, d);+ if (JS_IsException(val))+ return val;+ ctx->sp[0] = val;+ return JS_NewFloat64(ctx, r);+}++static no_inline JSValue js_binary_logic_slow(JSContext *ctx, OPCodeEnum op)+{+ uint32_t v1, v2, r;++ if (JS_ToUint32(ctx, &v1, ctx->sp[1]))+ return JS_EXCEPTION;+ if (JS_ToUint32(ctx, &v2, ctx->sp[0]))+ return JS_EXCEPTION;+ switch(op) {+ case OP_shl:+ r = v1 << (v2 & 0x1f);+ break;+ case OP_sar:+ r = (int)v1 >> (v2 & 0x1f);+ break;+ case OP_shr:+ r = v1 >> (v2 & 0x1f);+ return JS_NewUint32(ctx, r);+ case OP_and:+ r = v1 & v2;+ break;+ case OP_or:+ r = v1 | v2;+ break;+ case OP_xor:+ r = v1 ^ v2;+ break;+ default:+ abort();+ }+ return JS_NewInt32(ctx, r);+}++static no_inline JSValue js_not_slow(JSContext *ctx)+{+ uint32_t r;+ + if (JS_ToUint32(ctx, &r, ctx->sp[0]))+ return JS_EXCEPTION;+ return JS_NewInt32(ctx, ~r);+}++static no_inline JSValue js_relational_slow(JSContext *ctx, OPCodeEnum op)+{+ JSValue *op1, *op2;+ int res;+ double d1, d2;+ + op1 = &ctx->sp[1];+ op2 = &ctx->sp[0];+ *op1 = JS_ToPrimitive(ctx, *op1, HINT_NUMBER);+ if (JS_IsException(*op1))+ return JS_EXCEPTION;+ *op2 = JS_ToPrimitive(ctx, *op2, HINT_NUMBER);+ if (JS_IsException(*op2))+ return JS_EXCEPTION;+ if (JS_IsString(ctx, *op1) && JS_IsString(ctx, *op2)) {+ res = js_string_compare(ctx, *op1, *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 {+ if (JS_ToNumber(ctx, &d1, *op1))+ return JS_EXCEPTION;+ if (JS_ToNumber(ctx, &d2, *op2))+ return JS_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;+ }+ }+ return JS_NewBool(res);+}++static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)+{+ BOOL res;+ + if (JS_IsNumber(ctx, op1)) {+ if (!JS_IsNumber(ctx, op2)) {+ res = FALSE;+ } else {+ double d1, d2;+ /* cannot fail */+ JS_ToNumber(ctx, &d1, op1);+ JS_ToNumber(ctx, &d2, op2);+ res = (d1 == d2); /* if NaN return false */+ }+ } else if (JS_IsString(ctx, op1)) {+ if (!JS_IsString(ctx, op2)) {+ res = FALSE;+ } else {+ res = js_string_eq(ctx, op1, op2);+ }+ } else {+ /* special value or object */+ res = (op1 == op2);+ }+ return res;+}++static JSValue js_strict_eq_slow(JSContext *ctx, BOOL is_neq)+{+ BOOL res;+ res = js_strict_eq(ctx, ctx->sp[1], ctx->sp[0]);+ return JS_NewBool(res ^ is_neq);+}++enum {+ /* special tags to simplify the comparison */+ JS_ETAG_NUMBER = JS_TAG_SPECIAL | (8 << 2),+ JS_ETAG_STRING = JS_TAG_SPECIAL | (9 << 2),+ JS_ETAG_OBJECT = JS_TAG_SPECIAL | (10 << 2),+};++static int js_eq_get_type(JSContext *ctx, JSValue val)+{+ if (JS_IsIntOrShortFloat(val)) {+ return JS_ETAG_NUMBER;+ } else if (JS_IsPtr(val)) {+ void *ptr = JS_VALUE_TO_PTR(val);+ switch(js_get_mtag(ptr)) {+ case JS_MTAG_FLOAT64:+ return JS_ETAG_NUMBER;+ case JS_MTAG_STRING:+ return JS_ETAG_STRING;+ default:+ case JS_MTAG_OBJECT:+ return JS_ETAG_OBJECT;+ }+ } else {+ int tag = JS_VALUE_GET_SPECIAL_TAG(val);+ switch(tag) {+ case JS_TAG_STRING_CHAR:+ return JS_ETAG_STRING;+ case JS_TAG_SHORT_FUNC:+ return JS_ETAG_OBJECT;+ default:+ return tag;+ }+ }+}++static no_inline JSValue js_eq_slow(JSContext *ctx, BOOL is_neq)+{+ JSValue op1, op2;+ int tag1, tag2;+ BOOL res;+ + redo:+ op1 = ctx->sp[1];+ op2 = ctx->sp[0];+ tag1 = js_eq_get_type(ctx, op1);+ tag2 = js_eq_get_type(ctx, op2);+ if (tag1 == tag2) {+ 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_ETAG_STRING && tag2 == JS_ETAG_NUMBER) ||+ (tag2 == JS_ETAG_STRING && tag1 == JS_ETAG_NUMBER)) {+ double d1;+ double d2;+ if (JS_ToNumber(ctx, &d1, ctx->sp[1]))+ return JS_EXCEPTION;+ if (JS_ToNumber(ctx, &d2, ctx->sp[0]))+ return JS_EXCEPTION;+ res = (d1 == d2);+ } else if (tag1 == JS_TAG_BOOL) {+ ctx->sp[1] = JS_NewShortInt(JS_VALUE_GET_SPECIAL_VALUE(op1));+ goto redo;+ } else if (tag2 == JS_TAG_BOOL) {+ ctx->sp[0] = JS_NewShortInt(JS_VALUE_GET_SPECIAL_VALUE(op2));+ goto redo;+ } else if (tag1 == JS_ETAG_OBJECT &&+ (tag2 == JS_ETAG_NUMBER || tag2 == JS_ETAG_STRING)) {+ ctx->sp[1] = JS_ToPrimitive(ctx, op1, HINT_NONE);+ if (JS_IsException(ctx->sp[1]))+ return JS_EXCEPTION;+ goto redo;+ } else if (tag2 == JS_ETAG_OBJECT &&+ (tag1 == JS_ETAG_NUMBER || tag1 == JS_ETAG_STRING)) {+ ctx->sp[0] = JS_ToPrimitive(ctx, op2, HINT_NONE);+ if (JS_IsException(ctx->sp[0]))+ return JS_EXCEPTION;+ goto redo;+ } else {+ res = FALSE;+ }+ return JS_NewBool(res ^ is_neq);+}++static JSValue js_operator_in(JSContext *ctx)+{+ JSValue prop;+ int res;++ if (js_eq_get_type(ctx, ctx->sp[0]) != JS_ETAG_OBJECT)+ return JS_ThrowTypeError(ctx, "invalid 'in' operand");+ prop = JS_ToPropertyKey(ctx, ctx->sp[1]);+ if (JS_IsException(prop))+ return prop;+ res = JS_HasProperty(ctx, ctx->sp[0], prop);+ return JS_NewBool(res);+}++static JSValue js_operator_instanceof(JSContext *ctx)+{+ JSValue op1, op2, proto;+ JSObject *p;++ op1 = ctx->sp[1];+ op2 = ctx->sp[0];+ if (!JS_IsFunctionObject(ctx, op2))+ return JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");+ proto = JS_GetProperty(ctx, op2, js_get_atom(ctx, JS_ATOM_prototype));+ if (JS_IsException(proto))+ return proto;+ if (!JS_IsObject(ctx, op1))+ return JS_NewBool(FALSE);+ p = JS_VALUE_TO_PTR(op1);+ for(;;) {+ if (p->proto == JS_NULL)+ return JS_NewBool(FALSE);+ if (p->proto == proto)+ return JS_NewBool(TRUE);+ p = JS_VALUE_TO_PTR(p->proto);+ }+ return JS_NewBool(FALSE);+}++static JSValue js_operator_typeof(JSContext *ctx, JSValue val)+{+ int tag, atom;+ tag = js_eq_get_type(ctx, val);+ switch(tag) {+ case JS_ETAG_NUMBER:+ atom = JS_ATOM_number;+ break;+ case JS_ETAG_STRING:+ atom = JS_ATOM_string;+ break;+ case JS_TAG_BOOL:+ atom = JS_ATOM_boolean;+ break;+ case JS_ETAG_OBJECT:+ if (JS_IsFunction(ctx, val))+ atom = JS_ATOM_function;+ else+ atom = JS_ATOM_object;+ break;+ case JS_TAG_NULL:+ atom = JS_ATOM_object;+ break;+ default:+ case JS_TAG_UNDEFINED:+ atom = JS_ATOM_undefined;+ break;+ }+ return js_get_atom(ctx, atom);+}++static void js_reverse_val(JSValue *tab, int n)+{+ int i;+ JSValue tmp;+ + for(i = 0; i < n / 2; i++) {+ tmp = tab[i];+ tab[i] = tab[n - 1 - i];+ tab[n - 1 - i] = tmp;+ }+}+ +static JSValue js_closure(JSContext *ctx, JSValue bfunc, JSValue *fp)+{+ JSFunctionBytecode *b;+ JSObject *p;+ JSGCRef bfunc_ref, closure_ref;+ JSValueArray *ext_vars;+ JSValue closure;+ int ext_vars_len;+ + b = JS_VALUE_TO_PTR(bfunc);+ if (b->ext_vars != JS_NULL) {+ ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ ext_vars_len = ext_vars->size / 2;+ } else {+ ext_vars_len = 0;+ }+ + JS_PUSH_VALUE(ctx, bfunc);+ closure = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_CLOSURE], JS_CLASS_CLOSURE,+ sizeof(JSClosureData) + ext_vars_len * sizeof(JSValue));+ JS_POP_VALUE(ctx, bfunc);+ if (JS_IsException(closure))+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(closure);+ p->u.closure.func_bytecode = bfunc;+ + if (ext_vars_len > 0) {+ JSValue *pfirst_var_ref, val;+ int i, var_idx, var_kind, decl;+ + /* initialize the var_refs in case of exception */+ memset(p->u.closure.var_refs, 0, sizeof(JSValue) * ext_vars_len);+ if (fp) {+ pfirst_var_ref = &fp[FRAME_OFFSET_FIRST_VARREF];+ } else {+ pfirst_var_ref = NULL; /* not used */+ }+ for(i = 0; i < ext_vars_len; i++) {+ b = JS_VALUE_TO_PTR(bfunc);+ ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ decl = JS_VALUE_GET_INT(ext_vars->arr[2 * i + 1]);+ var_kind = decl >> 16;+ var_idx = decl & 0xffff;+ JS_PUSH_VALUE(ctx, bfunc);+ JS_PUSH_VALUE(ctx, closure);+ switch(var_kind) {+ case JS_VARREF_KIND_ARG:+ val = get_var_ref(ctx, pfirst_var_ref, + &fp[FRAME_OFFSET_ARG0 + var_idx]);+ break;+ case JS_VARREF_KIND_VAR:+ val = get_var_ref(ctx, pfirst_var_ref,+ &fp[FRAME_OFFSET_VAR0 - var_idx]);+ break;+ case JS_VARREF_KIND_VAR_REF:+ {+ JSObject *p;+ p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]);+ val = p->u.closure.var_refs[var_idx];+ }+ break;+ case JS_VARREF_KIND_GLOBAL:+ /* only for eval code */+ val = add_global_var(ctx, ext_vars->arr[2 * i], (var_idx != 0));+ break;+ default:+ abort();+ }+ JS_POP_VALUE(ctx, closure);+ JS_POP_VALUE(ctx, bfunc);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_TO_PTR(closure);+ p->u.closure.var_refs[i] = val;+ }+ }+ return closure;+}++static JSValue js_for_of_start(JSContext *ctx, BOOL is_for_in)+{+ JSValueArray *arr;++ if (is_for_in) {+ /* XXX: not spec compliant and slow. We return only the own+ object keys. */+ ctx->sp[0] = js_object_keys(ctx, NULL, 1, &ctx->sp[0]);+ if (JS_IsException(ctx->sp[0]))+ return JS_EXCEPTION;+ }+ + if (!js_get_object_class(ctx, ctx->sp[0], JS_CLASS_ARRAY))+ return JS_ThrowTypeError(ctx, "unsupported type in for...of");+ + arr = js_alloc_value_array(ctx, 0, 2);+ if (!arr)+ return JS_EXCEPTION;+ arr->arr[0] = ctx->sp[0];+ arr->arr[1] = JS_NewShortInt(0);+ return JS_VALUE_FROM_PTR(arr);+}++static JSValue js_for_of_next(JSContext *ctx)+{+ JSValueArray *arr, *arr1;+ JSObject *p;+ int pos;+ + arr = JS_VALUE_TO_PTR(ctx->sp[0]);+ pos = JS_VALUE_GET_INT(arr->arr[1]);+ p = JS_VALUE_TO_PTR(arr->arr[0]);+ if (pos >= p->u.array.len) {+ ctx->sp[-2] = JS_TRUE;+ ctx->sp[-1] = JS_UNDEFINED;+ } else {+ ctx->sp[-2] = JS_FALSE;+ arr1 = JS_VALUE_TO_PTR(p->u.array.tab);+ ctx->sp[-1] = arr1->arr[pos];+ arr->arr[1] = JS_NewShortInt(pos + 1);+ }+ return JS_UNDEFINED;+}++static JSValue js_new_c_function_proto(JSContext *ctx, int func_idx, JSValue proto, BOOL has_params,+ JSValue params)+{+ JSObject *p;+ JSGCRef params_ref;+ + JS_PUSH_VALUE(ctx, params);+ p = JS_NewObjectProtoClass1(ctx, proto, JS_CLASS_C_FUNCTION,+ sizeof(JSCFunctionData) - (!has_params ? sizeof(JSValue) : 0));+ JS_POP_VALUE(ctx, params);+ if (!p)+ return JS_EXCEPTION;+ p->u.cfunc.idx = func_idx;+ if (has_params)+ p->u.cfunc.params = params;+ return JS_VALUE_FROM_PTR(p);+}++JSValue JS_NewCFunctionParams(JSContext *ctx, int func_idx, JSValue params)+{+ return js_new_c_function_proto(ctx, func_idx, ctx->class_proto[JS_CLASS_CLOSURE], TRUE, params);+}++static JSValue js_call_constructor_start(JSContext *ctx, JSValue func)+{+ JSValue proto;+ proto = JS_GetProperty(ctx, func, js_get_atom(ctx, JS_ATOM_prototype));+ if (JS_IsException(proto))+ return proto;+ if (!JS_IsObject(ctx, proto))+ proto = ctx->class_proto[JS_CLASS_OBJECT];+ return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT, 0);+}++#define SAVE() do { \+ fp[FRAME_OFFSET_CUR_PC] = JS_NewShortInt(pc - ((JSByteArray *)JS_VALUE_TO_PTR(b->byte_code))->buf); \+ ctx->sp = sp; \+ ctx->fp = fp; \+ } while (0)++/* only need to restore PC */+#define RESTORE() do { \+ b = JS_VALUE_TO_PTR(((JSObject *)JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]))->u.closure.func_bytecode); \+ pc = ((JSByteArray *)JS_VALUE_TO_PTR(b->byte_code))->buf + JS_VALUE_GET_INT(fp[FRAME_OFFSET_CUR_PC]); \+ } while (0)++static JSValue __js_poll_interrupt(JSContext *ctx)+{+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;+ if (ctx->interrupt_handler && ctx->interrupt_handler(ctx, ctx->opaque)) {+ JS_ThrowInternalError(ctx, "interrupted");+ ctx->current_exception_is_uncatchable = TRUE;+ return JS_EXCEPTION;+ }+ return JS_UNDEFINED;+}++/* handle user interruption */+#define POLL_INTERRUPT() do { \+ if (unlikely(--ctx->interrupt_counter <= 0)) { \+ SAVE(); \+ val = __js_poll_interrupt(ctx); \+ RESTORE(); \+ if (JS_IsException(val)) \+ goto exception; \+ } \+ } while(0)++/* must use JS_StackCheck() before using it */+void JS_PushArg(JSContext *ctx, JSValue val)+{+#ifdef DEBUG_GC+ assert((ctx->sp - 1) >= ctx->stack_bottom);+#endif+ *--ctx->sp = val;+}++/* Usage:+ if (JS_StackCheck(ctx, n + 2)) ...+ JS_PushArg(ctx, arg[n - 1]);+ ...+ JS_PushArg(ctx, arg[0]);+ JS_PushArg(ctx, func);+ JS_PushArg(ctx, this_obj);+ res = JS_Call(ctx, n);+*/+JSValue JS_Call(JSContext *ctx, int call_flags)+{+ JSValue *fp, *sp, val = JS_UNDEFINED, *initial_fp;+ uint8_t *pc;+ /* temporary variables */+ int opcode = OP_invalid, i;+ JSFunctionBytecode *b;+#ifdef JS_USE_SHORT_FLOAT+ double dr;+#endif+ + if (ctx->js_call_rec_count >= JS_MAX_CALL_RECURSE)+ return JS_ThrowInternalError(ctx, "C stack overflow");+ ctx->js_call_rec_count++;++ sp = ctx->sp;+ fp = ctx->fp;+ initial_fp = fp;+ b = NULL;+ pc = NULL;+ goto function_call;++#define CASE(op) case op+#define DEFAULT default+#define BREAK break+ + for(;;) {+ opcode = *pc++;+#ifdef DUMP_EXEC+ {+ JSByteArray *arr;+ arr = JS_VALUE_TO_PTR(b->byte_code);+ js_printf(ctx, " sp=%d\n", (int)(sp - fp));+ js_printf(ctx, "%4d: %s\n", (int)(pc - arr->buf - 1),+ opcode_info[opcode].name);+ }+#endif+ switch(opcode) {+ 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_NewShortInt(opcode - OP_push_0);+ BREAK;+ CASE(OP_push_i8):+ *--sp = JS_NewShortInt(get_i8(pc));+ pc += 1;+ BREAK;+ CASE(OP_push_i16):+ *--sp = JS_NewShortInt(get_i16(pc));+ pc += 2;+ BREAK;+ CASE(OP_push_value):+ *--sp = get_u32(pc);+ pc += 4;+ BREAK;+ CASE(OP_push_const):+ {+ JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool);+ *--sp = cpool->arr[get_u16(pc)];+ pc += 2;+ }+ BREAK;+ CASE(OP_undefined):+ *--sp = JS_UNDEFINED;+ BREAK;+ CASE(OP_null):+ *--sp = JS_NULL;+ BREAK;+ CASE(OP_push_this):+ *--sp = fp[FRAME_OFFSET_THIS_OBJ];+ BREAK;+ CASE(OP_push_false):+ *--sp = JS_FALSE;+ BREAK;+ CASE(OP_push_true):+ *--sp = JS_TRUE;+ BREAK;+ CASE(OP_object):+ {+ int n = get_u16(pc);+ SAVE();+ val = JS_NewObjectPrealloc(ctx, n);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ *--sp = val;+ pc += 2;+ }+ BREAK;+ CASE(OP_regexp):+ {+ JSObject *p;+ SAVE();+ val = JS_NewObjectClass(ctx, JS_CLASS_REGEXP, sizeof(JSRegExp));+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ p = JS_VALUE_TO_PTR(val);+ p->u.regexp.source = sp[1];+ p->u.regexp.byte_code = sp[0];+ p->u.regexp.last_index = 0;+ sp[1] = val;+ sp++;+ }+ BREAK;+ CASE(OP_array_from):+ {+ JSObject *p;+ JSValueArray *arr;+ int i, argc;++ argc = get_u16(pc);+ SAVE();+ val = JS_NewArray(ctx, argc);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ pc += 2;+ p = JS_VALUE_TO_PTR(val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ for(i = 0; i < argc; i++) {+ arr->arr[i] = sp[argc - 1 - i];+ }+ sp += argc;+ *--sp = val;+ }+ BREAK;+ CASE(OP_this_func):+ *--sp = fp[FRAME_OFFSET_FUNC_OBJ];+ BREAK;+ CASE(OP_arguments):+ {+ JSObject *p;+ JSValueArray *arr;+ int i, argc;+ + argc = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]) & FRAME_CF_ARGC_MASK;+ SAVE();+ val = JS_NewArray(ctx, argc);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ p = JS_VALUE_TO_PTR(val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ for(i = 0; i < argc; i++) {+ arr->arr[i] = fp[FRAME_OFFSET_ARG0 + i];+ }+ *--sp = val;+ }+ BREAK;+ CASE(OP_new_target):+ call_flags = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]);+ if (call_flags & FRAME_CF_CTOR) {+ *--sp = fp[FRAME_OFFSET_FUNC_OBJ];+ } else {+ *--sp = JS_UNDEFINED;+ }+ BREAK;+ CASE(OP_drop):+ sp++;+ BREAK;+ CASE(OP_nip):+ sp[1] = sp[0];+ sp++;+ BREAK;+ CASE(OP_dup):+ sp--;+ sp[0] = sp[1];+ BREAK;+ CASE(OP_dup2):+ sp -= 2;+ sp[0] = sp[2];+ sp[1] = sp[3];+ BREAK;+ CASE(OP_insert2):+ sp[-1] = sp[0];+ sp[0] = sp[1];+ sp[1] = sp[-1];+ sp--;+ BREAK;+ CASE(OP_insert3):+ sp[-1] = sp[0];+ sp[0] = sp[1];+ sp[1] = sp[2];+ sp[2] = sp[-1];+ sp--;+ BREAK;+ CASE(OP_perm3): /* obj a b -> a obj b (213) */+ {+ JSValue tmp;+ tmp = sp[1];+ sp[1] = sp[2];+ sp[2] = tmp;+ }+ BREAK;+ CASE(OP_rot3l): /* x a b -> a b x (231) */+ {+ JSValue tmp;+ tmp = sp[2];+ sp[2] = sp[1];+ sp[1] = sp[0];+ sp[0] = tmp;+ }+ BREAK;+ CASE(OP_perm4): /* obj prop a b -> a obj prop b */+ {+ JSValue tmp;+ tmp = sp[1];+ sp[1] = sp[2];+ sp[2] = sp[3];+ sp[3] = tmp;+ }+ BREAK;+ CASE(OP_swap): /* a b -> b a */+ {+ JSValue tmp;+ tmp = sp[1];+ sp[1] = sp[0];+ sp[0] = tmp;+ }+ BREAK;++ CASE(OP_fclosure):+ {+ int idx;+ JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool);+ idx = get_u16(pc);+ SAVE();+ val = js_closure(ctx, cpool->arr[idx], fp);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ pc += 2;+ *--sp = val;+ }+ BREAK;++ CASE(OP_call_constructor):+ call_flags = get_u16(pc) | FRAME_CF_CTOR;+ goto global_function_call;+ CASE(OP_call):+ call_flags = get_u16(pc);+ global_function_call:+ js_reverse_val(sp, (call_flags & FRAME_CF_ARGC_MASK) + 1);+ *--sp = JS_UNDEFINED;+ goto generic_function_call;+ CASE(OP_call_method):+ {+ int n, argc, short_func_idx;+ JSValue func_obj;+ JSObject *p;+ JSByteArray *byte_code;+ + call_flags = get_u16(pc);++ n = (call_flags & FRAME_CF_ARGC_MASK) + 2;+ js_reverse_val(sp, n);+ + generic_function_call:+ POLL_INTERRUPT();+ byte_code = JS_VALUE_TO_PTR(b->byte_code);+ /* save pc + 1 of the current call */+ fp[FRAME_OFFSET_CUR_PC] = JS_NewShortInt(pc - byte_code->buf);+ function_call:+ *--sp = JS_NewShortInt(call_flags);+ *--sp = SP_TO_VALUE(ctx, fp);+ + func_obj = sp[FRAME_OFFSET_FUNC_OBJ];+#if defined(DUMP_EXEC)+ JS_DumpValue(ctx, "calling", func_obj);+#endif+ if (!JS_IsPtr(func_obj)) {+ if (JS_VALUE_GET_SPECIAL_TAG(func_obj) != JS_TAG_SHORT_FUNC)+ goto not_a_function;+ short_func_idx = JS_VALUE_GET_SPECIAL_VALUE(func_obj);+ p = NULL;+ goto c_function;+ } else {+ p = JS_VALUE_TO_PTR(func_obj);+ if (p->mtag != JS_MTAG_OBJECT)+ goto not_a_function;+ if (p->class_id == JS_CLASS_C_FUNCTION) {+ const JSCFunctionDef *fd;+ int pushed_argc;+ short_func_idx = p->u.cfunc.idx;+ c_function:+ fd = &ctx->c_function_table[short_func_idx];+ /* add undefined arguments if the caller did not+ provide enough arguments */+ call_flags = JS_VALUE_GET_INT(sp[FRAME_OFFSET_CALL_FLAGS]);+ if ((call_flags & FRAME_CF_CTOR) &&+ (fd->def_type != JS_CFUNC_constructor &&+ fd->def_type != JS_CFUNC_constructor_magic)) {+ sp += 2; /* go back to the caller frame */+ ctx->sp = sp;+ ctx->fp = fp;+ val = JS_ThrowTypeError(ctx, "not a constructor");+ goto call_exception;+ }++ argc = call_flags & FRAME_CF_ARGC_MASK;+ /* JS_StackCheck may trigger a gc */+ ctx->sp = sp;+ ctx->fp = fp;+ n = JS_StackCheck(ctx, max_int(fd->arg_count - argc, 0));+ if (n) {+ sp += 2; /* go back to the caller frame */+ val = JS_EXCEPTION;+ goto call_exception;+ }+ pushed_argc = argc;+ if (fd->arg_count > argc) {+ n = fd->arg_count - argc;+ sp -= n;+ for(i = 0; i < FRAME_OFFSET_ARG0 + argc; i++)+ sp[i] = sp[i + n];+ for(i = 0; i < n; i++)+ sp[FRAME_OFFSET_ARG0 + argc + i] = JS_UNDEFINED;+ pushed_argc = fd->arg_count;+ }+ fp = sp;+ ctx->sp = sp;+ ctx->fp = fp;+ switch(fd->def_type) {+ case JS_CFUNC_generic:+ case JS_CFUNC_constructor:+ val = fd->func.generic(ctx, &fp[FRAME_OFFSET_THIS_OBJ],+ call_flags & (FRAME_CF_CTOR | FRAME_CF_ARGC_MASK),+ fp + FRAME_OFFSET_ARG0);+ break;+ case JS_CFUNC_generic_magic:+ case JS_CFUNC_constructor_magic:+ val = fd->func.generic_magic(ctx, &fp[FRAME_OFFSET_THIS_OBJ],+ call_flags & (FRAME_CF_CTOR | FRAME_CF_ARGC_MASK),+ fp + FRAME_OFFSET_ARG0, fd->magic);+ break;+ case JS_CFUNC_generic_params:+ p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]);+ val = fd->func.generic_params(ctx, &fp[FRAME_OFFSET_THIS_OBJ],+ call_flags & (FRAME_CF_CTOR | FRAME_CF_ARGC_MASK),+ fp + FRAME_OFFSET_ARG0, p->u.cfunc.params);+ break;+ case JS_CFUNC_f_f:+ {+ double d;+ if (JS_ToNumber(ctx, &d, fp[FRAME_OFFSET_ARG0])) {+ val = JS_EXCEPTION;+ } else {+ d = fd->func.f_f(d);+ }+ val = JS_NewFloat64(ctx, d);+ }+ break;+ default:+ assert(0);+ }+ if (JS_IsExceptionOrTailCall(val) &&+ JS_VALUE_GET_SPECIAL_VALUE(val) >= JS_EX_CALL) {+ JSValue *fp1, *sp1;+ /* tail call: equivalent to calling the+ function after the C function */+ /* XXX: handle the call flags of the caller ? */+ call_flags = JS_VALUE_GET_SPECIAL_VALUE(val) - JS_EX_CALL;+ sp = ctx->sp;+ /* pop the frame */+ fp1 = VALUE_TO_SP(ctx, fp[FRAME_OFFSET_SAVED_FP]);+ /* move the new arguments at the correct stack position */+ argc = (call_flags & FRAME_CF_ARGC_MASK) + 2;+ sp1 = fp + FRAME_OFFSET_ARG0 + pushed_argc - argc;+ memmove(sp1, sp, sizeof(*sp) * (argc));+ sp = sp1;+ fp = fp1;+ goto function_call;+ } else {+ sp = fp + FRAME_OFFSET_ARG0 + pushed_argc;+ goto return_call;+ }+ } else if (p->class_id == JS_CLASS_CLOSURE) {+ int n_vars;+ call_flags = JS_VALUE_GET_INT(sp[FRAME_OFFSET_CALL_FLAGS]);+ if (call_flags & FRAME_CF_CTOR) {+ ctx->sp = sp;+ ctx->fp = fp;+ /* Note: can recurse at this point */+ val = js_call_constructor_start(ctx, func_obj);+ if (JS_IsException(val))+ goto call_exception;+ sp[FRAME_OFFSET_THIS_OBJ] = val;+ func_obj = sp[FRAME_OFFSET_FUNC_OBJ];+ p = JS_VALUE_TO_PTR(func_obj);+ }+ b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode);+ if (b->vars != JS_NULL) {+ JSValueArray *vars = JS_VALUE_TO_PTR(b->vars);+ n_vars = vars->size - b->arg_count;+ } else {+ n_vars = 0;+ }+ argc = call_flags & FRAME_CF_ARGC_MASK;+ /* JS_StackCheck may trigger a gc */+ ctx->sp = sp;+ ctx->fp = fp;+ n = JS_StackCheck(ctx, max_int(b->arg_count - argc, 0) + 2 + n_vars ++ b->stack_size);+ if (n) {+ val = JS_EXCEPTION;+ goto call_exception;+ }+ func_obj = sp[FRAME_OFFSET_FUNC_OBJ];+ p = JS_VALUE_TO_PTR(func_obj);+ b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode);+ /* add undefined arguments if the caller did not+ provide enough arguments */+ if (unlikely(b->arg_count > argc)) {+ n = b->arg_count - argc;+ sp -= n;+ for(i = 0; i < FRAME_OFFSET_ARG0 + argc; i++)+ sp[i] = sp[i + n];+ for(i = 0; i < n; i++)+ sp[FRAME_OFFSET_ARG0 + argc + i] = JS_UNDEFINED;+ }+ fp = sp;+ *--sp = JS_NewShortInt(0); /* FRAME_OFFSET_CUR_PC */+ *--sp = JS_NULL; /* FRAME_OFFSET_FIRST_VARREF */+ sp -= n_vars;+ for(i = 0; i < n_vars; i++)+ sp[i] = JS_UNDEFINED;+ byte_code = JS_VALUE_TO_PTR(b->byte_code);+ pc = byte_code->buf;+ } else {+ not_a_function:+ sp += 2; /* go back to the caller frame */+ ctx->sp = sp;+ ctx->fp = fp;+ val = JS_ThrowTypeError(ctx, "not a function");+ call_exception:+ if (!pc) {+ goto done;+ } else {+ RESTORE();+ goto exception;+ }+ }+ }+ }+ BREAK;++ exception:+ /* 'val' must contain the exception */+ {+ JSValue *stack_top, val2;+ JSValueArray *vars;+ int v;+ /* exception before entering in the first function ?+ (XXX: remove this test) */+ if (!pc) + goto done;+ v = JS_VALUE_GET_SPECIAL_VALUE(val);+ if (v >= JS_EX_CALL) {+ /* tail call */+ call_flags = JS_VALUE_GET_SPECIAL_VALUE(val) - JS_EX_CALL;+ /* the opcode has only one byte, hence the PC must+ be updated accordingly after the function+ returns */+ if (opcode == OP_get_length ||+ opcode == OP_get_length2 ||+ opcode == OP_get_array_el ||+ opcode == OP_get_array_el2 ||+ opcode == OP_put_array_el) {+ call_flags |= FRAME_CF_PC_ADD1;+ }+ // js_printf(ctx, "tail call: 0x%x\n", call_flags);+ goto generic_function_call;+ }+ /* XXX: start gc in case of JS_EXCEPTION_MEM */+ stack_top = fp + FRAME_OFFSET_VAR0 + 1;+ if (b->vars != JS_NULL) {+ vars = JS_VALUE_TO_PTR(b->vars);+ stack_top -= (vars->size - b->arg_count);+ }+ if (ctx->current_exception_is_uncatchable) {+ sp = stack_top;+ } else {+ while (sp < stack_top) {+ val2 = *sp++;+ if (JS_VALUE_GET_SPECIAL_TAG(val2) == JS_TAG_CATCH_OFFSET) {+ JSByteArray *byte_code;+ /* exception caught by a 'catch' in the+ current function */+ *--sp = ctx->current_exception;+ ctx->current_exception = JS_NULL;+ byte_code = JS_VALUE_TO_PTR(b->byte_code);+ pc = byte_code->buf + JS_VALUE_GET_SPECIAL_VALUE(val2);+ goto restart;+ }+ }+ }+ }+ goto generic_return;++ CASE(OP_return_undef):+ val = JS_UNDEFINED;+ goto generic_return;+ + CASE(OP_return):+ val = sp[0];+ generic_return:+ {+ JSObject *p;+ int argc, pc_offset;+ JSValue val2;+ JSVarRef *pv;+ JSByteArray *byte_code;+ + /* detach the variable references */+ val2 = fp[FRAME_OFFSET_FIRST_VARREF];+ while (val2 != JS_NULL) {+ pv = JS_VALUE_TO_PTR(val2);+ val2 = pv->u.next;+ assert(!pv->is_detached);+ pv->u.value = *pv->u.pvalue;+ pv->is_detached = TRUE;+ /* shrink 'pv' */+ set_free_block((uint8_t *)pv + sizeof(JSVarRef) - sizeof(JSValue), sizeof(JSValue));+ }++ call_flags = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]);+ if (unlikely(call_flags & FRAME_CF_CTOR)) {+ if (!JS_IsException(val) && !JS_IsObject(ctx, val)) {+ val = fp[FRAME_OFFSET_THIS_OBJ];+ }+ }+ argc = call_flags & FRAME_CF_ARGC_MASK;+ argc = max_int(argc, b->arg_count);+ sp = fp + FRAME_OFFSET_ARG0 + argc;+ return_call:+ call_flags = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]);+ /* XXX: restore stack_bottom to reduce memory usage */+ fp = VALUE_TO_SP(ctx, fp[FRAME_OFFSET_SAVED_FP]);+ if (fp == initial_fp)+ goto done;+ pc_offset = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CUR_PC]);+ p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]);+ b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode);+ byte_code = JS_VALUE_TO_PTR(b->byte_code);+ pc = byte_code->buf + pc_offset;+ /* now we are in the calling function */+ if (JS_IsException(val))+ goto exception;+ if (!(call_flags & FRAME_CF_POP_RET))+ *--sp = val;+ /* Note: if variable size call, can add a flag in call_flags */+ if (!(call_flags & FRAME_CF_PC_ADD1))+ pc += 2; /* skip the call arg or get_field/put_field arg */+ }+ BREAK;+ + CASE(OP_catch):+ {+ int32_t diff;+ JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code);+ diff = get_u32(pc);+ *--sp = JS_VALUE_MAKE_SPECIAL(JS_TAG_CATCH_OFFSET, pc + diff - byte_code->buf);+ pc += 4;+ }+ BREAK;+ CASE(OP_throw):+ val = *sp++;+ SAVE();+ val = JS_Throw(ctx, val);+ RESTORE();+ goto exception;+ CASE(OP_gosub):+ {+ int32_t diff;+ JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code);+ diff = get_u32(pc);+ *--sp = JS_NewShortInt(pc + 4 - byte_code->buf);+ pc += diff;+ }+ BREAK;+ CASE(OP_ret):+ {+ JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code);+ uint32_t pos;+ if (unlikely(!JS_IsInt(sp[0])))+ goto ret_fail;+ pos = JS_VALUE_GET_INT(sp[0]);+ if (unlikely(pos >= byte_code->size)) {+ ret_fail:+ SAVE();+ val = JS_ThrowInternalError(ctx, "invalid ret value");+ RESTORE();+ goto exception;+ }+ sp++;+ pc = byte_code->buf + pos;+ }+ BREAK;++ CASE(OP_get_loc):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ *--sp = fp[FRAME_OFFSET_VAR0 - idx];+ }+ BREAK;+ CASE(OP_put_loc):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ fp[FRAME_OFFSET_VAR0 - idx] = sp[0];+ sp++;+ }+ BREAK;+ CASE(OP_get_arg):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ *--sp = fp[FRAME_OFFSET_ARG0 + idx];+ }+ BREAK;+ CASE(OP_put_arg):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ fp[FRAME_OFFSET_ARG0 + idx] = sp[0];+ sp++;+ }+ BREAK;+ + CASE(OP_get_loc0): *--sp = fp[FRAME_OFFSET_VAR0 - 0]; BREAK;+ CASE(OP_get_loc1): *--sp = fp[FRAME_OFFSET_VAR0 - 1]; BREAK;+ CASE(OP_get_loc2): *--sp = fp[FRAME_OFFSET_VAR0 - 2]; BREAK;+ CASE(OP_get_loc3): *--sp = fp[FRAME_OFFSET_VAR0 - 3]; BREAK;+ CASE(OP_get_loc8): *--sp = fp[FRAME_OFFSET_VAR0 - *pc++]; BREAK;+ + CASE(OP_put_loc0): fp[FRAME_OFFSET_VAR0 - 0] = *sp++; BREAK;+ CASE(OP_put_loc1): fp[FRAME_OFFSET_VAR0 - 1] = *sp++; BREAK;+ CASE(OP_put_loc2): fp[FRAME_OFFSET_VAR0 - 2] = *sp++; BREAK;+ CASE(OP_put_loc3): fp[FRAME_OFFSET_VAR0 - 3] = *sp++; BREAK;+ CASE(OP_put_loc8): fp[FRAME_OFFSET_VAR0 - *pc++] = *sp++; BREAK;++ CASE(OP_get_arg0): *--sp = fp[FRAME_OFFSET_ARG0 + 0]; BREAK;+ CASE(OP_get_arg1): *--sp = fp[FRAME_OFFSET_ARG0 + 1]; BREAK;+ CASE(OP_get_arg2): *--sp = fp[FRAME_OFFSET_ARG0 + 2]; BREAK;+ CASE(OP_get_arg3): *--sp = fp[FRAME_OFFSET_ARG0 + 3]; BREAK;++ CASE(OP_put_arg0): fp[FRAME_OFFSET_ARG0 + 0] = *sp++; BREAK;+ CASE(OP_put_arg1): fp[FRAME_OFFSET_ARG0 + 1] = *sp++; BREAK;+ CASE(OP_put_arg2): fp[FRAME_OFFSET_ARG0 + 2] = *sp++; BREAK;+ CASE(OP_put_arg3): fp[FRAME_OFFSET_ARG0 + 3] = *sp++; BREAK;+ + CASE(OP_get_var_ref):+ CASE(OP_get_var_ref_nocheck):+ {+ int idx;+ JSObject *p;+ JSVarRef *pv;+ idx = get_u16(pc);+ p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]);+ pv = JS_VALUE_TO_PTR(p->u.closure.var_refs[idx]);+ if (pv->is_detached)+ val = pv->u.value;+ else+ val = *pv->u.pvalue;+ if (unlikely(val == JS_TAG_UNINITIALIZED) &&+ opcode == OP_get_var_ref) {+ JSValueArray *ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ SAVE();+ val = JS_ThrowReferenceError(ctx, "variable '%"JSValue_PRI"' is not defined", ext_vars->arr[2 * idx]);+ RESTORE();+ goto exception;+ }+ pc += 2;+ *--sp = val;+ }+ BREAK;+ CASE(OP_put_var_ref):+ CASE(OP_put_var_ref_nocheck):+ {+ int idx;+ JSObject *p;+ JSVarRef *pv;+ JSValue *pval;+ idx = get_u16(pc);+ p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]);+ pv = JS_VALUE_TO_PTR(p->u.closure.var_refs[idx]);+ if (pv->is_detached)+ pval = &pv->u.value;+ else+ pval = pv->u.pvalue;+ if (unlikely(*pval == JS_TAG_UNINITIALIZED) &&+ opcode == OP_put_var_ref) {+ JSValueArray *ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ SAVE();+ val = JS_ThrowReferenceError(ctx, "variable '%"JSValue_PRI"' is not defined", ext_vars->arr[2 * idx]);+ RESTORE();+ goto exception;+ }+ *pval = *sp++;+ pc += 2;+ }+ BREAK;++ CASE(OP_goto):+ pc += (int32_t)get_u32(pc);+ POLL_INTERRUPT();+ BREAK;+ CASE(OP_if_false):+ CASE(OP_if_true):+ {+ int res;++ pc += 4;++ res = JS_ToBool(ctx, *sp++);+ if (res ^ (OP_if_true - opcode)) {+ pc += (int32_t)get_u32(pc - 4) - 4;+ }+ POLL_INTERRUPT();+ }+ BREAK;++ CASE(OP_lnot):+ {+ int res;+ res = JS_ToBool(ctx, sp[0]);+ sp[0] = JS_NewBool(!res);+ }+ BREAK;+ + CASE(OP_get_field2):+ sp--;+ sp[0] = sp[1];+ goto get_field_common;+ CASE(OP_get_field):+ get_field_common:+ {+ int idx;+ JSValue prop, obj;+ JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool);+ idx = get_u16(pc);+ prop = cpool->arr[idx];+ obj = sp[0];+ if (likely(JS_IsPtr(obj))) {+ /* fast case */+ JSObject *p = JS_VALUE_TO_PTR(obj);+ JSProperty *pr;+ if (unlikely(p->mtag != JS_MTAG_OBJECT))+ goto get_field_slow;+ for(;;) {+ /* no array check is necessary because 'prop' is+ guaranteed not to be a numeric property */+ /* XXX: slow due to short ints */+ pr = find_own_property_inlined(ctx, p, prop);+ if (pr) {+ if (unlikely(pr->prop_type != JS_PROP_NORMAL)) {+ /* sp[0] is this_obj, obj is the current+ object */+ goto get_field_slow;+ } else {+ val = pr->value;+ break;+ }+ }+ obj = p->proto;+ if (obj == JS_NULL) {+ val = JS_UNDEFINED;+ break;+ }+ p = JS_VALUE_TO_PTR(obj);+ }+ } else {+ get_field_slow:+ SAVE();+ val = JS_GetPropertyInternal(ctx, obj, prop, TRUE);+ RESTORE();+ if (unlikely(JS_IsExceptionOrTailCall(val))) {+ sp = ctx->sp;+ goto exception;+ }+ }+ pc += 2;+ sp[0] = val;+ }+ BREAK;++ CASE(OP_get_length2):+ sp--;+ sp[0] = sp[1];+ goto get_length_common;+ + CASE(OP_get_length):+ get_length_common:+ {+ JSValue obj;+ obj = sp[0];+ if (likely(JS_IsPtr(obj))) {+ /* fast case */+ JSObject *p = JS_VALUE_TO_PTR(obj);+ if (p->mtag == JS_MTAG_OBJECT) {+ if (p->class_id == JS_CLASS_ARRAY) {+ if (unlikely(p->proto != ctx->class_proto[JS_CLASS_ARRAY] ||+ p->props != ctx->empty_props))+ goto get_length_slow;+ val = JS_NewShortInt(p->u.array.len);+ } else {+ goto get_length_slow;+ }+ } else if (p->mtag == JS_MTAG_STRING) {+ JSString *ps = (JSString *)p;+ if (likely(ps->is_ascii))+ val = JS_NewShortInt(ps->len);+ else+ val = JS_NewShortInt(js_string_utf8_to_utf16_pos(ctx, obj, ps->len * 2));+ } else {+ goto get_length_slow;+ }+ } else if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) {+ val = JS_NewShortInt(JS_VALUE_GET_SPECIAL_VALUE(val) >= 0x10000 ? 2 : 1); + } else {+ get_length_slow:+ SAVE();+ val = JS_GetPropertyInternal(ctx, obj, js_get_atom(ctx, JS_ATOM_length), TRUE);+ RESTORE();+ if (unlikely(JS_IsExceptionOrTailCall(val))) {+ sp = ctx->sp;+ goto exception;+ }+ }+ sp[0] = val;+ }+ BREAK;++ CASE(OP_put_field):+ {+ int idx;+ JSValue prop, obj;+ JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool);++ idx = get_u16(pc);+ prop = cpool->arr[idx];+ obj = sp[1];+ if (likely(JS_IsPtr(obj))) {+ /* fast case */+ JSObject *p = JS_VALUE_TO_PTR(obj);+ JSProperty *pr;+ if (unlikely(p->mtag != JS_MTAG_OBJECT))+ goto put_field_slow;+ /* no array check is necessary because 'prop' is+ guaranteed not to be a numeric property */+ /* XXX: slow due to short ints */+ pr = find_own_property_inlined(ctx, p, prop);+ if (unlikely(!pr))+ goto put_field_slow;+ if (unlikely(pr->prop_type != JS_PROP_NORMAL))+ goto put_field_slow;+ /* XXX: slow */+ if (unlikely(JS_IS_ROM_PTR(ctx, pr)))+ goto put_field_slow;+ pr->value = sp[0];+ sp += 2;+ } else {+ put_field_slow:+ val = *sp++;+ SAVE();+ val = JS_SetPropertyInternal(ctx, sp[0], prop, val, TRUE);+ RESTORE();+ if (unlikely(JS_IsExceptionOrTailCall(val))) {+ sp = ctx->sp;+ goto exception;+ }+ sp++;+ }+ pc += 2;+ }+ BREAK;++ CASE(OP_get_array_el2):+ val = sp[0];+ sp[0] = sp[1];+ goto get_array_el_common;+ CASE(OP_get_array_el):+ val = sp[0];+ sp++;+ get_array_el_common:+ {+ JSValue prop = val, obj;+ obj = sp[0];+ if (JS_IsPtr(obj) && JS_IsInt(prop)) {+ /* fast case with array */+ /* XXX: optimize typed arrays too ? */+ JSObject *p = JS_VALUE_TO_PTR(obj);+ uint32_t idx;+ JSValueArray *arr;+ if (unlikely(p->mtag != JS_MTAG_OBJECT))+ goto get_array_el_slow;+ if (unlikely(p->class_id != JS_CLASS_ARRAY))+ goto get_array_el_slow;+ idx = JS_VALUE_GET_INT(prop);+ if (unlikely(idx >= p->u.array.len))+ goto get_array_el_slow;+ + arr = JS_VALUE_TO_PTR(p->u.array.tab);+ val = arr->arr[idx];+ } else {+ get_array_el_slow:+ SAVE();+ prop = JS_ToPropertyKey(ctx, prop);+ RESTORE();+ if (JS_IsException(prop)) {+ val = prop;+ goto exception;+ }+ SAVE();+ val = JS_GetPropertyInternal(ctx, sp[0], prop, TRUE);+ RESTORE();+ if (unlikely(JS_IsExceptionOrTailCall(val))) {+ sp = ctx->sp;+ goto exception;+ }+ }+ sp[0] = val;+ }+ BREAK;+ + CASE(OP_put_array_el):+ {+ JSValue prop, obj;+ obj = sp[2];+ prop = sp[1];+ if (JS_IsPtr(obj) && JS_IsInt(prop)) {+ /* fast case with array */+ /* XXX: optimize typed arrays too ? */+ JSObject *p = JS_VALUE_TO_PTR(obj);+ uint32_t idx;+ JSValueArray *arr;+ if (unlikely(p->mtag != JS_MTAG_OBJECT))+ goto put_array_el_slow;+ if (unlikely(p->class_id != JS_CLASS_ARRAY))+ goto put_array_el_slow;+ idx = JS_VALUE_GET_INT(prop);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ if (unlikely(idx >= p->u.array.len)) {+ if (idx == p->u.array.len &&+ p->u.array.tab != JS_NULL &&+ idx < arr->size) {+ arr->arr[idx] = sp[0];+ p->u.array.len = idx + 1;+ } else {+ goto put_array_el_slow;+ }+ } else {+ arr->arr[idx] = sp[0];+ }+ sp += 3;+ } else {+ put_array_el_slow:+ SAVE();+ sp[1] = JS_ToPropertyKey(ctx, sp[1]);+ RESTORE();+ if (JS_IsException(sp[1])) {+ val = sp[1];+ goto exception;+ }+ val = *sp++;+ prop = *sp++;+ SAVE();+ val = JS_SetPropertyInternal(ctx, sp[0], prop, val, TRUE);+ RESTORE();+ if (unlikely(JS_IsExceptionOrTailCall(val))) {+ sp = ctx->sp;+ goto exception;+ }+ sp++;+ }+ }+ BREAK;+ + CASE(OP_define_field):+ CASE(OP_define_getter):+ CASE(OP_define_setter):+ {+ int idx;+ JSValue prop;+ JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool);+ + idx = get_u16(pc);+ prop = cpool->arr[idx];+ + SAVE();+ if (opcode == OP_define_field) {+ val = JS_DefinePropertyValue(ctx, sp[1], prop, sp[0]);+ } else if (opcode == OP_define_getter)+ val = JS_DefinePropertyGetSet(ctx, sp[1], prop, sp[0], JS_UNDEFINED, JS_DEF_PROP_HAS_GET);+ else+ val = JS_DefinePropertyGetSet(ctx, sp[1], prop, JS_UNDEFINED, sp[0], JS_DEF_PROP_HAS_SET);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ pc += 2;+ sp++;+ }+ BREAK;++ CASE(OP_set_proto):+ {+ if (JS_IsObject(ctx, sp[0]) || JS_IsNull(sp[0])) {+ SAVE();+ val = js_set_prototype_internal(ctx, sp[1], sp[0]);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ }+ sp++;+ }+ BREAK;+ + CASE(OP_add):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int r;+ if (unlikely(__builtin_add_overflow((int)op1, (int)op2, &r)))+ goto add_slow;+ sp[1] = (uint32_t)r;+ } else +#ifdef JS_USE_SHORT_FLOAT+ if (JS_VALUE_IS_BOTH_SHORT_FLOAT(op1, op2)) {+ double d1, d2;+ d1 = js_get_short_float(op1);+ d2 = js_get_short_float(op2);+ dr = d1 + d2;+ sp++;+ goto float_result;+ } else+#endif+ {+ add_slow:+ SAVE();+ val = js_add_slow(ctx);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ sp[1] = val;+ }+ sp++;+ }+ BREAK;+ CASE(OP_sub):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int r;+ if (unlikely(__builtin_sub_overflow((int)op1, (int)op2, &r)))+ goto binary_arith_slow;+ sp[1] = (uint32_t)r;+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_VALUE_IS_BOTH_SHORT_FLOAT(op1, op2)) {+ double d1, d2;+ d1 = js_get_short_float(op1);+ d2 = js_get_short_float(op2);+ dr = d1 - d2;+ sp++;+ goto float_result;+ } else+#endif+ {+ goto binary_arith_slow;+ }+ sp++;+ }+ BREAK;+ CASE(OP_mul):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int v1, v2;+ int64_t r;+ v1 = (int)op1;+ v2 = (int)op2 >> 1;+ r = (int64_t)v1 * (int64_t)v2;+ if (unlikely(r != (int)r)) {+#if defined(JS_USE_SHORT_FLOAT)+ dr = (double)(r >> 1);+ sp++;+ goto float_result;+#else+ goto binary_arith_slow;+#endif+ }+ /* -0 case */+ if (unlikely(r == 0 && (v1 | v2) < 0)) {+ sp[1] = ctx->minus_zero;+ } else {+ sp[1] = (uint32_t)r;+ }+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_VALUE_IS_BOTH_SHORT_FLOAT(op1, op2)) {+ double d1, d2;+ d1 = js_get_short_float(op1);+ d2 = js_get_short_float(op2);+ dr = d1 * d2;+ sp++;+ goto float_result;+ } else+#endif+ {+ goto binary_arith_slow;+ }+ sp++;+ }+ BREAK;+ CASE(OP_div):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int v1, v2;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+ SAVE();+ val = JS_NewFloat64(ctx, (double)v1 / (double)v2);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ sp[1] = val;+ sp++;+ } else {+ goto binary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_mod):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ 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);+ if (unlikely(v1 < 0 || v2 <= 0))+ goto binary_arith_slow;+ r = v1 % v2;+ sp[1] = JS_NewShortInt(r);+ sp++;+ } else {+ goto binary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_pow):+ binary_arith_slow:+ SAVE();+ val = js_binary_arith_slow(ctx, opcode);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ sp[1] = val;+ sp++;+ BREAK;+ CASE(OP_plus):+ {+ JSValue op1;+ op1 = sp[0];+ if (JS_IsIntOrShortFloat(op1) ||+ (JS_IsPtr(op1) && js_get_mtag(JS_VALUE_TO_PTR(op1)) == JS_MTAG_FLOAT64)) {+ } else {+ goto unary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_neg):+ {+ JSValue op1;+ int v1;+ op1 = sp[0];+ if (JS_IsInt(op1)) {+ v1 = op1;+ if (v1 == 0) {+ sp[0] = ctx->minus_zero;+ } else if (v1 == INT32_MIN) {+#if defined(JS_USE_SHORT_FLOAT)+ dr = -(double)JS_SHORTINT_MIN;+ goto float_result;+#else+ goto unary_arith_slow;+#endif + } else {+ sp[0] = -v1;+ }+ } else+#if defined(JS_USE_SHORT_FLOAT)+ if (JS_IsShortFloat(op1)) {+ dr = -js_get_short_float(op1);+ float_result:+ /* for efficiency, we don't try to store it as a short integer */+ if (likely(fabs(dr) >= 0x1p-127 && fabs(dr) <= 0x1p+128)) {+ val = js_to_short_float(dr);+ } else if (dr == 0.0) {+ if (float64_as_uint64(dr) != 0) {+ /* minus zero often happens, so it is worth having a constant+ value */+ val = ctx->minus_zero;+ } else {+ /* XXX: could have a short float+ representation for zero and minus zero+ so that the float fast case is still+ used when they happen */+ val = JS_NewShortInt(0);+ }+ } else {+ /* slow case: need to allocate it */+ SAVE();+ val = js_alloc_float64(ctx, dr);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ }+ sp[0] = val;+ } else+#endif+ {+ goto unary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_inc):+ {+ JSValue op1;+ int v1;+ op1 = sp[0];+ if (JS_IsInt(op1)) {+ v1 = JS_VALUE_GET_INT(op1);+ if (unlikely(v1 == JS_SHORTINT_MAX))+ goto unary_arith_slow;+ sp[0] = JS_NewShortInt(v1 + 1);+ } else {+ goto unary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_dec):+ {+ JSValue op1;+ int v1;+ op1 = sp[0];+ if (JS_IsInt(op1)) {+ v1 = JS_VALUE_GET_INT(op1);+ if (unlikely(v1 == JS_SHORTINT_MIN))+ goto unary_arith_slow;+ sp[0] = JS_NewShortInt(v1 - 1);+ } else {+ unary_arith_slow:+ SAVE();+ val = js_unary_arith_slow(ctx, opcode);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ sp[0] = val;+ }+ }+ BREAK;+ CASE(OP_post_inc):+ CASE(OP_post_dec):+ {+ JSValue op1;+ int v1;+ op1 = sp[0];+ if (JS_IsInt(op1)) {+ v1 = JS_VALUE_GET_INT(op1) + 2 * (opcode - OP_post_dec) - 1;+ if (v1 < JS_SHORTINT_MIN || v1 > JS_SHORTINT_MAX)+ goto slow_post_inc_dec;+ val = JS_NewShortInt(v1);+ } else {+ slow_post_inc_dec:+ SAVE();+ val = js_post_inc_slow(ctx, opcode);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ }+ *--sp = val;+ }+ BREAK;++ CASE(OP_not):+ {+ JSValue op1;+ op1 = sp[0];+ if (JS_IsInt(op1)) {+ sp[0] = (~op1) & (~1);+ } else {+ SAVE();+ val = js_not_slow(ctx);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ sp[0] = val;+ }+ }+ BREAK;++ CASE(OP_shl):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int32_t r;+ r = JS_VALUE_GET_INT(op1) << (JS_VALUE_GET_INT(op2) & 0x1f);+ if (unlikely(r < JS_SHORTINT_MIN || r > JS_SHORTINT_MAX)) {+#if defined(JS_USE_SHORT_FLOAT)+ dr = (double)r;+ sp++;+ goto float_result;+#else+ goto binary_logic_slow;+#endif+ }+ sp[1] = JS_NewShortInt(r);+ sp++;+ } else {+ goto binary_logic_slow;+ }+ }+ BREAK;+ CASE(OP_shr):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ uint32_t r;+ r = (uint32_t)JS_VALUE_GET_INT(op1) >>+ ((uint32_t)JS_VALUE_GET_INT(op2) & 0x1f);+ if (unlikely(r > JS_SHORTINT_MAX)) {+#if defined(JS_USE_SHORT_FLOAT)+ dr = (double)r;+ sp++;+ goto float_result;+#else+ goto binary_logic_slow;+#endif+ }+ sp[1] = JS_NewShortInt(r);+ sp++;+ } else {+ goto binary_logic_slow;+ }+ }+ BREAK;+ CASE(OP_sar):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[1] = ((int)op1 >> ((uint32_t)JS_VALUE_GET_INT(op2) & 0x1f)) & ~1;+ sp++;+ } else {+ goto binary_logic_slow;+ }+ }+ BREAK;+ CASE(OP_and):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[1] = op1 & op2;+ sp++;+ } else {+ goto binary_logic_slow;+ }+ }+ BREAK;+ CASE(OP_or):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[1] = op1 | op2;+ sp++;+ } else {+ goto binary_logic_slow;+ }+ }+ BREAK;+ CASE(OP_xor):+ {+ JSValue op1, op2;+ op1 = sp[1];+ op2 = sp[0];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[1] = op1 ^ op2;+ sp++;+ } else {+ binary_logic_slow:+ SAVE();+ val = js_binary_logic_slow(ctx, opcode);+ RESTORE();+ if (JS_IsException(val))+ goto exception;+ sp[1] = val;+ sp++;+ }+ }+ BREAK;+ ++#define OP_CMP(opcode, binary_op, slow_call) \+ CASE(opcode): \+ { \+ JSValue op1, op2; \+ op1 = sp[1]; \+ op2 = sp[0]; \+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \+ sp[1] = JS_NewBool(JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \+ sp++; \+ } else { \+ SAVE(); \+ val = slow_call; \+ RESTORE(); \+ if (JS_IsException(val)) \+ goto exception; \+ sp[1] = val; \+ sp++; \+ } \+ } \+ BREAK;+ + OP_CMP(OP_lt, <, js_relational_slow(ctx, opcode));+ OP_CMP(OP_lte, <=, js_relational_slow(ctx, opcode));+ OP_CMP(OP_gt, >, js_relational_slow(ctx, opcode));+ OP_CMP(OP_gte, >=, js_relational_slow(ctx, opcode));+ OP_CMP(OP_eq, ==, js_eq_slow(ctx, 0));+ OP_CMP(OP_neq, !=, js_eq_slow(ctx, 1));+ OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, 0));+ OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, 1));+ CASE(OP_in):+ SAVE();+ val = js_operator_in(ctx);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp[1] = val;+ sp++;+ BREAK;+ CASE(OP_instanceof):+ SAVE();+ val = js_operator_instanceof(ctx);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp[1] = val;+ sp++;+ BREAK;+ CASE(OP_typeof):+ SAVE();+ val = js_operator_typeof(ctx, sp[0]);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp[0] = val;+ BREAK;+ CASE(OP_delete):+ SAVE();+ val = JS_DeleteProperty(ctx, sp[1], sp[0]);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp[1] = val;+ sp++;+ BREAK;+ CASE(OP_for_in_start):+ CASE(OP_for_of_start):+ SAVE();+ val = js_for_of_start(ctx, (opcode == OP_for_in_start));+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp[0] = val;+ BREAK;+ CASE(OP_for_of_next):+ SAVE();+ val = js_for_of_next(ctx);+ RESTORE();+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp -= 2;+ BREAK;+ default:+ {+ JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code);+ SAVE();+ val = JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",+ (int)(pc - byte_code->buf - 1), opcode);+ RESTORE();+ }+ goto exception;+ }+ restart: ;+ } /* switch */+ done:+ ctx->sp = sp;+ ctx->fp = fp;+ ctx->js_call_rec_count--;+ return val;+}++#undef SAVE+#undef RESTORE++static inline int is_ident_first(int c)+{+ return (c >= 'a' && c <= 'z') ||+ (c >= 'A' && c <= 'Z') ||+ c == '_' || c == '$';+}++static inline int is_ident_next(int c)+{+ return is_ident_first(c) || is_num(c);+}++/**********************************************************************/+/* dump utilities */++#ifdef JS_DUMP++static void js_dump_array(JSContext *ctx, JSValueArray *arr, int len)+{+ int i;+ + js_printf(ctx, "[ ");+ for(i = 0; i < len; i++) {+ if (i != 0)+ js_printf(ctx, ", ");+ JS_PrintValue(ctx, arr->arr[i]);+ }+ js_printf(ctx, " ]");+}++/* put constructors into a separate table */+/* XXX: improve by using a table */+static JSValue js_find_class_name(JSContext *ctx, int class_id)+{+ const JSCFunctionDef *fd;+ fd = ctx->c_function_table;+ while ((fd->def_type != JS_CFUNC_constructor_magic &&+ fd->def_type != JS_CFUNC_constructor) ||+ fd->magic != class_id) {+ fd++;+ }+ return reloc_c_func_name(ctx, fd->name);+}++static void js_dump_float64(JSContext *ctx, double d)+{+ char buf[32];+ JSDTOATempMem tmp_mem; /* XXX: potentially large stack size */+ js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &tmp_mem);+ js_printf(ctx, "%s", buf);+}++static void dump_regexp(JSContext *ctx, JSObject *p);++static void js_dump_error(JSContext *ctx, JSObject *p)+{+ JSObject *p1;+ JSProperty *pr;+ JSValue name;+ + /* find the error name without side effect */+ p1 = p;+ if (p->proto != JS_NULL) + p1 = JS_VALUE_TO_PTR(p->proto);+ pr = find_own_property(ctx, p1, js_get_atom(ctx, JS_ATOM_name));+ if (!pr || !JS_IsString(ctx, pr->value))+ name = js_get_atom(ctx, JS_ATOM_Error);+ else+ name = pr->value;+ js_printf(ctx, "%" JSValue_PRI, name);+ if (p->u.error.message != JS_NULL) {+ js_printf(ctx, ": %" JSValue_PRI, p->u.error.message);+ }+ if (p->u.error.stack != JS_NULL) {+ /* remove the trailing '\n' if any */+ js_printf(ctx, "\n%#" JSValue_PRI, p->u.error.stack);+ }+}++static void js_dump_object(JSContext *ctx, JSObject *p, int flags)+{+ if (flags & JS_DUMP_LONG) {+ switch(p->class_id) {+ case JS_CLASS_CLOSURE:+ {+ JSFunctionBytecode *b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode);+ js_printf(ctx, "function ");+ JS_PrintValueF(ctx, b->func_name, JS_DUMP_NOQUOTE);+ js_printf(ctx, "()");+ }+ break;+ case JS_CLASS_C_FUNCTION:+ js_printf(ctx, "function ");+ JS_PrintValueF(ctx, reloc_c_func_name(ctx, ctx->c_function_table[p->u.cfunc.idx].name), JS_DUMP_NOQUOTE);+ js_printf(ctx, "()");+ break;+ case JS_CLASS_ERROR:+ js_dump_error(ctx, p);+ break;+ case JS_CLASS_REGEXP:+ dump_regexp(ctx, p);+ break;+ default:+ case JS_CLASS_ARRAY:+ case JS_CLASS_OBJECT:+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int i, idx;+ uint32_t v;+ double d;+ JSObject *pbuffer;+ JSByteArray *arr;+ JS_PrintValueF(ctx, js_find_class_name(ctx, p->class_id),+ JS_DUMP_NOQUOTE);+ js_printf(ctx, "([ ");+ pbuffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer);+ arr = JS_VALUE_TO_PTR(pbuffer->u.array_buffer.byte_buffer);+ for(i = 0; i < p->u.typed_array.len; i++) {+ if (i != 0)+ js_printf(ctx, ", ");+ idx = i + p->u.typed_array.offset;+ switch(p->class_id) {+ default:+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ v = *((uint8_t *)arr->buf + idx);+ goto ta_i32;+ case JS_CLASS_INT8_ARRAY:+ v = *((int8_t *)arr->buf + idx);+ goto ta_i32;+ case JS_CLASS_INT16_ARRAY:+ v = *((int16_t *)arr->buf + idx);+ goto ta_i32;+ case JS_CLASS_UINT16_ARRAY:+ v = *((uint16_t *)arr->buf + idx);+ goto ta_i32;+ case JS_CLASS_INT32_ARRAY:+ v = *((int32_t *)arr->buf + idx);+ ta_i32:+ js_printf(ctx, "%d", v);+ break;+ case JS_CLASS_UINT32_ARRAY:+ v = *((uint32_t *)arr->buf + idx);+ js_printf(ctx, "%u", v);+ break;+ case JS_CLASS_FLOAT32_ARRAY:+ d = *((float *)arr->buf + idx);+ goto ta_d;+ case JS_CLASS_FLOAT64_ARRAY:+ d = *((double *)arr->buf + idx);+ ta_d:+ js_dump_float64(ctx, d);+ break;+ }+ }+ js_printf(ctx, " ])");+ } else {+ int i, j, prop_count, hash_mask;+ JSProperty *pr;+ JSValueArray *arr;+ BOOL is_first = TRUE;++ arr = JS_VALUE_TO_PTR(p->props);+ prop_count = JS_VALUE_GET_INT(arr->arr[0]);+ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);+ if (p->class_id == JS_CLASS_ARRAY) {+ JSValueArray *tab = JS_VALUE_TO_PTR(p->u.array.tab);+ js_printf(ctx, "[ ");+ for(i = 0; i < p->u.array.len; i++) {+ if (!is_first)+ js_printf(ctx, ", ");+ JS_PrintValue(ctx, tab->arr[i]);+ is_first = FALSE;+ }+ } else {+ if (p->class_id != JS_CLASS_OBJECT) {+ JSValue class_name = js_find_class_name(ctx, p->class_id);+ if (!JS_IsNull(class_name))+ JS_PrintValueF(ctx, class_name, JS_DUMP_NOQUOTE);+ js_putchar(ctx, ' ');+ }+ js_printf(ctx, "{ ");+ }+ for(i = 0, j = 0; j < prop_count; i++) {+ pr = (JSProperty *)&arr->arr[2 + (hash_mask + 1) + 3 * i];+ if (pr->key != JS_UNINITIALIZED) {+ if (!is_first)+ js_printf(ctx, ", ");+ JS_PrintValueF(ctx, pr->key, JS_DUMP_NOQUOTE);+ js_printf(ctx, ": ");+ if (!(flags & JS_DUMP_RAW) && pr->prop_type == JS_PROP_SPECIAL) {+ JS_PrintValue(ctx, get_special_prop(ctx, pr->value));+ } else {+ JS_PrintValue(ctx, pr->value);+ }+ is_first = FALSE;+ j++;+ }+ }+ js_printf(ctx, " %c",+ p->class_id == JS_CLASS_ARRAY ? ']' : '}');+ }+ break;+ }+ } else {+ const char *str;+ if (p->class_id == JS_CLASS_ARRAY)+ str = "Array";+ else if (p->class_id == JS_CLASS_ERROR)+ str = "Error";+ else if (p->class_id == JS_CLASS_CLOSURE ||+ p->class_id == JS_CLASS_C_FUNCTION) {+ str = "Function";+ } else {+ str = "Object";+ }+ js_printf(ctx, "[object %s]", str);+ }+}++static void dump_string(JSContext *ctx, int sep, const uint8_t *buf, size_t len,+ int flags)+{+ BOOL use_quote;+ const uint8_t *p, *p_end;+ size_t i, clen;+ int c;++ use_quote = TRUE;+ if (flags & JS_DUMP_NOQUOTE) {+ if (len >= 1 && is_ident_first(buf[0])) {+ for(i = 1; i < len; i++) {+ if (!is_ident_next(buf[i]))+ goto need_quote;+ }+ use_quote = FALSE;+ }+ need_quote: ;+ }+ + if (!(flags & JS_DUMP_RAW))+ sep = '"';+ if (use_quote)+ js_putchar(ctx, sep);+ p = buf;+ p_end = buf + len;+ while (p < p_end) {+ c = utf8_get(p, &clen);+ 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:+ js_putchar(ctx, '\\');+ js_putchar(ctx, c);+ break;+ default:+ if (c < 32 || (c >= 0xd800 && c < 0xe000)) {+ js_printf(ctx, "\\u%04x", c);+ } else {+ ctx->write_func(ctx->opaque, p, clen);+ }+ break;+ }+ p += clen;+ }+ if (use_quote)+ js_putchar(ctx, sep);+}++void JS_PrintValueF(JSContext *ctx, JSValue val, int flags)+{+ if (JS_IsInt(val)) {+ js_printf(ctx, "%d", JS_VALUE_GET_INT(val));+ } else+#ifdef JS_USE_SHORT_FLOAT+ if (JS_IsShortFloat(val)) {+ js_dump_float64(ctx, js_get_short_float(val));+ } else+#endif+ if (!JS_IsPtr(val)) {+ switch(JS_VALUE_GET_SPECIAL_TAG(val)) {+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ case JS_TAG_UNINITIALIZED:+ case JS_TAG_BOOL:+ js_printf(ctx, "%"JSValue_PRI"", val);+ break;+ case JS_TAG_EXCEPTION:+ js_printf(ctx, "[exception %d]", JS_VALUE_GET_SPECIAL_VALUE(val));+ break;+ case JS_TAG_CATCH_OFFSET:+ js_printf(ctx, "[catch_offset %d]", JS_VALUE_GET_SPECIAL_VALUE(val));+ break;+ case JS_TAG_SHORT_FUNC:+ {+ int idx = JS_VALUE_GET_SPECIAL_VALUE(val);+ js_printf(ctx, "function ");+ JS_PrintValueF(ctx, reloc_c_func_name(ctx, ctx->c_function_table[idx].name), JS_DUMP_NOQUOTE);+ js_printf(ctx, "()");+ }+ break;+ case JS_TAG_STRING_CHAR:+ {+ uint8_t buf[UTF8_CHAR_LEN_MAX + 1];+ int len;+ len = get_short_string(buf, val);+ dump_string(ctx, '`', buf, len, flags);+ }+ break;+ default:+ js_printf(ctx, "[tag %d]", (int)JS_VALUE_GET_SPECIAL_TAG(val));+ break;+ }+ } else {+ void *ptr = JS_VALUE_TO_PTR(val);+ int mtag = ((JSMemBlockHeader *)ptr)->mtag;+ switch(mtag) {+ case JS_MTAG_FLOAT64:+ {+ JSFloat64 *p = ptr;+ js_dump_float64(ctx, p->u.dval);+ }+ break;+ case JS_MTAG_OBJECT:+ js_dump_object(ctx, ptr, flags);+ break;+ case JS_MTAG_STRING:+ {+ JSString *p = ptr;+ int sep;+ sep = p->is_unique ? '\'' : '\"';+ dump_string(ctx, sep, p->buf, p->len, flags);+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ JSValueArray *arr = ptr;+ js_dump_array(ctx, arr, arr->size);+ }+ break;+ case JS_MTAG_BYTE_ARRAY:+ {+ JSByteArray *arr = ptr;+ js_printf(ctx, "byte_array(%" PRIu64 ")", (uint64_t)arr->size);+ }+ break;+ case JS_MTAG_FUNCTION_BYTECODE:+ {+ JSFunctionBytecode *b = ptr;+ js_printf(ctx, "bytecode_function ");+ JS_PrintValueF(ctx, b->func_name, JS_DUMP_NOQUOTE);+ js_printf(ctx, "()");+ }+ break;+ case JS_MTAG_VARREF:+ {+ JSVarRef *pv = ptr;+ js_printf(ctx, "var_ref(");+ if (pv->is_detached)+ JS_PrintValue(ctx, pv->u.value);+ else+ JS_PrintValue(ctx, *pv->u.pvalue);+ js_printf(ctx, ")");+ }+ break;+ default:+ js_printf(ctx, "[mtag %d]", mtag);+ break;+ }+ }+}++void JS_PrintValue(JSContext *ctx, JSValue val)+{+ return JS_PrintValueF(ctx, val, 0);+}++static const char *get_mtag_name(unsigned int mtag)+{+ if (mtag >= countof(js_mtag_name))+ return "?";+ else+ return js_mtag_name[mtag];+}++static uint32_t val_to_offset(JSContext *ctx, JSValue val)+{+ if (!JS_IsPtr(val))+ return 0;+ else+ return (uint8_t *)JS_VALUE_TO_PTR(val) - ctx->heap_base;+}++void JS_DumpMemory(JSContext *ctx, BOOL is_long)+{+ uint8_t *ptr;+ uint32_t mtag_mem_size[JS_MTAG_COUNT];+ uint32_t mtag_count[JS_MTAG_COUNT];+ uint32_t tot_size, i;+ if (is_long) {+ js_printf(ctx, "%10s %s %8s %15s %10s %10s %s\n", "OFFSET", "M", "SIZE", "TAG", "PROTO", "PROPS", "EXTRA");+ }+ for(i = 0; i < JS_MTAG_COUNT; i++) {+ mtag_mem_size[i] = 0;+ mtag_count[i] = 0;+ }+ tot_size = 0;+ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ int mtag, size, gc_mark;+ mtag = ((JSMemBlockHeader *)ptr)->mtag;+ gc_mark = ((JSMemBlockHeader *)ptr)->gc_mark;+ size = get_mblock_size(ptr);+ mtag_mem_size[mtag] += size;+ mtag_count[mtag]++;+ tot_size += size;+ if (is_long) {+ js_printf(ctx, "0x%08x %c %8u %15s",+ (unsigned int)((uint8_t *)ptr - ctx->heap_base),+ gc_mark ? '*' : ' ',+ size,+ get_mtag_name(mtag));+ if (mtag != JS_MTAG_FREE) {+ if (mtag == JS_MTAG_OBJECT) {+ JSObject *p = (JSObject *)ptr;+ js_printf(ctx, " 0x%08x 0x%08x",+ val_to_offset(ctx, p->proto), val_to_offset(ctx, p->props));+ } else {+ js_printf(ctx, " %10s %10s", "", "");+ }+ js_printf(ctx, " ");+ JS_PrintValueF(ctx, JS_VALUE_FROM_PTR(ptr), JS_DUMP_RAW);+ }+ js_printf(ctx, "\n");+ }+ ptr += size;+ }+ + js_printf(ctx, "%15s %8s %8s %8s %8s\n", "TAG", "COUNT", "AVG_SIZE", "SIZE", "RATIO");+ for(i = 0; i < JS_MTAG_COUNT; i++) {+ if (mtag_count[i] != 0) {+ js_printf(ctx, "%15s %8u %8d %8u %7d%%\n",+ get_mtag_name(i),+ (unsigned int)mtag_count[i],+ (int)js_lrint((double)mtag_mem_size[i] / (double)mtag_count[i]),+ (unsigned int)mtag_mem_size[i],+ (int)js_lrint((double)mtag_mem_size[i] / (double)tot_size * 100.0));+ }+ }+ js_printf(ctx, "heap size=%u/%u stack_size=%u\n",+ (unsigned int)(ctx->heap_free - ctx->heap_base),+ (unsigned int)(ctx->stack_top - ctx->heap_base),+ (unsigned int)(ctx->stack_top - (uint8_t *)ctx->sp));+}++static __maybe_unused void JS_DumpUniqueStrings(JSContext *ctx)+{+ int i;+ JSValueArray *arr;+ + arr = JS_VALUE_TO_PTR( ctx->unique_strings);+ js_printf(ctx, "%5s %s\n", "N", "UNIQUE_STRING");+ for(i = 0; i < ctx->unique_strings_len; i++) {+ js_printf(ctx, "%5d ", i);+ JS_PrintValue(ctx, arr->arr[i]);+ js_printf(ctx, "\n");+ }+}+#else+void JS_PrintValueF(JSContext *ctx, JSValue val, int flags)+{+}+void JS_PrintValue(JSContext *ctx, JSValue val)+{+ return JS_PrintValueF(ctx, val, 0);+}+void JS_DumpMemory(JSContext *ctx, BOOL is_long)+{+}+static __maybe_unused void JS_DumpUniqueStrings(JSContext *ctx)+{+}+#endif++void JS_DumpValueF(JSContext *ctx, const char *str,+ JSValue val, int flags)+{+ js_printf(ctx, "%s=", str);+ JS_PrintValueF(ctx, val, flags);+ js_printf(ctx, "\n");+}++void JS_DumpValue(JSContext *ctx, const char *str,+ JSValue val)+{+ JS_DumpValueF(ctx, str, val, 0);+}+++/**************************************************/+/* JS parser */++enum {+ TOK_NUMBER = 128,+ TOK_STRING,+ 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,+ 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,+ TOK_POW,+ TOK_EOF,+ /* keywords */+ TOK_FIRST_KEYWORD,+ TOK_NULL = TOK_FIRST_KEYWORD + JS_ATOM_null,+ TOK_FALSE = TOK_FIRST_KEYWORD + JS_ATOM_false,+ TOK_TRUE = TOK_FIRST_KEYWORD + JS_ATOM_true,+ TOK_IF = TOK_FIRST_KEYWORD + JS_ATOM_if,+ TOK_ELSE = TOK_FIRST_KEYWORD + JS_ATOM_else,+ TOK_RETURN = TOK_FIRST_KEYWORD + JS_ATOM_return,+ TOK_VAR = TOK_FIRST_KEYWORD + JS_ATOM_var,+ TOK_THIS = TOK_FIRST_KEYWORD + JS_ATOM_this,+ TOK_DELETE = TOK_FIRST_KEYWORD + JS_ATOM_delete,+ TOK_VOID = TOK_FIRST_KEYWORD + JS_ATOM_void,+ TOK_TYPEOF = TOK_FIRST_KEYWORD + JS_ATOM_typeof,+ TOK_NEW = TOK_FIRST_KEYWORD + JS_ATOM_new,+ TOK_IN = TOK_FIRST_KEYWORD + JS_ATOM_in,+ TOK_INSTANCEOF = TOK_FIRST_KEYWORD + JS_ATOM_instanceof,+ TOK_DO = TOK_FIRST_KEYWORD + JS_ATOM_do,+ TOK_WHILE = TOK_FIRST_KEYWORD + JS_ATOM_while,+ TOK_FOR = TOK_FIRST_KEYWORD + JS_ATOM_for,+ TOK_BREAK = TOK_FIRST_KEYWORD + JS_ATOM_break,+ TOK_CONTINUE = TOK_FIRST_KEYWORD + JS_ATOM_continue,+ TOK_SWITCH = TOK_FIRST_KEYWORD + JS_ATOM_switch,+ TOK_CASE = TOK_FIRST_KEYWORD + JS_ATOM_case,+ TOK_DEFAULT = TOK_FIRST_KEYWORD + JS_ATOM_default,+ TOK_THROW = TOK_FIRST_KEYWORD + JS_ATOM_throw,+ TOK_TRY = TOK_FIRST_KEYWORD + JS_ATOM_try,+ TOK_CATCH = TOK_FIRST_KEYWORD + JS_ATOM_catch,+ TOK_FINALLY = TOK_FIRST_KEYWORD + JS_ATOM_finally,+ TOK_FUNCTION = TOK_FIRST_KEYWORD + JS_ATOM_function,+ TOK_DEBUGGER = TOK_FIRST_KEYWORD + JS_ATOM_debugger,+ TOK_WITH = TOK_FIRST_KEYWORD + JS_ATOM_with,+ TOK_CLASS = TOK_FIRST_KEYWORD + JS_ATOM_class,+ TOK_CONST = TOK_FIRST_KEYWORD + JS_ATOM_const,+ TOK_ENUM = TOK_FIRST_KEYWORD + JS_ATOM_enum,+ TOK_EXPORT = TOK_FIRST_KEYWORD + JS_ATOM_export,+ TOK_EXTENDS = TOK_FIRST_KEYWORD + JS_ATOM_extends,+ TOK_IMPORT = TOK_FIRST_KEYWORD + JS_ATOM_import,+ TOK_SUPER = TOK_FIRST_KEYWORD + JS_ATOM_super,+ TOK_IMPLEMENTS = TOK_FIRST_KEYWORD + JS_ATOM_implements,+ TOK_INTERFACE = TOK_FIRST_KEYWORD + JS_ATOM_interface,+ TOK_LET = TOK_FIRST_KEYWORD + JS_ATOM_let,+ TOK_PACKAGE = TOK_FIRST_KEYWORD + JS_ATOM_package,+ TOK_PRIVATE = TOK_FIRST_KEYWORD + JS_ATOM_private,+ TOK_PROTECTED = TOK_FIRST_KEYWORD + JS_ATOM_protected,+ TOK_PUBLIC = TOK_FIRST_KEYWORD + JS_ATOM_public,+ TOK_STATIC = TOK_FIRST_KEYWORD + JS_ATOM_static,+ TOK_YIELD = TOK_FIRST_KEYWORD + JS_ATOM_yield,+};++/* this structure is pushed on the JS stack, so all members must be JSValue */+typedef struct BlockEnv {+ JSValue prev; /* JS_NULL or stack index */+ JSValue label_name; /* JS_NULL if none */+ JSValue label_break;+ JSValue label_cont;+ JSValue label_finally;+ JSValue drop_count; /* (int) number of stack elements to drop */+} BlockEnv;++typedef uint32_t JSSourcePos;++typedef struct JSToken {+ int val;+ JSSourcePos source_pos; /* position in source */+ union {+ double d; /* TOK_NUMBER */+ struct {+ uint32_t re_flags; /* regular expression flags */+ uint32_t re_end_pos; /* at the final '/' */+ } regexp;+ } u;+ JSValue value; /* associated value: string for TOK_STRING, TOK_REGEXP;+ identifier for TOK_IDENT or keyword */+} JSToken;++typedef struct JSParseState {+ JSContext *ctx;+ JSToken token;++ BOOL got_lf : 8; /* true if got line feed before the current token */+ /* global eval: variables are defined as global */+ BOOL is_eval : 8;+ /* if true, return the last value. */+ BOOL has_retval : 8;+ /* if true, implicitly define global variables in an+ assignment. */+ BOOL is_repl : 8;+ BOOL has_column : 8; /* column debug info is present */+ /* TRUE if the expression result has been dropped (see PF_DROP) */+ BOOL dropped_result : 8;+ JSValue source_str; /* source string or JS_NULL */+ JSValue filename_str; /* 'filename' converted to string */+ /* zero terminated source buffer. Automatically updated by the GC+ if source_str is a string */+ const uint8_t *source_buf; + uint32_t buf_pos;+ uint32_t buf_len;++ /* current function */+ JSValue cur_func;+ JSValue byte_code;+ uint32_t byte_code_len;+ int last_opcode_pos; /* -1 if no last opcode */+ int last_pc2line_pos; /* pc2line pos for the last opcode */+ JSSourcePos last_pc2line_source_pos;+ + uint32_t pc2line_bit_len;+ JSSourcePos pc2line_source_pos; /* last generated source pos */+ + uint16_t cpool_len;+ /* size of the byte code necessary to define the hoisted functions */+ uint32_t hoisted_code_len;+ + /* argument + defined local variable count */+ uint16_t local_vars_len;+ + int eval_ret_idx; /* variable index for the eval return value, -1+ if no return value */+ JSValue top_break; /* JS_NULL or SP_TO_VALUE(BlockEnv *) */++ /* regexp parsing only */+ uint8_t capture_count;+ uint8_t re_in_js: 1;+ uint8_t multi_line : 1;+ uint8_t dotall : 1;+ uint8_t ignore_case : 1;+ uint8_t is_unicode : 1;++ /* error handling */+ jmp_buf jmp_env;+ char error_msg[64];+} JSParseState;++static int js_parse_json_value(JSParseState *s, int state, int dummy_param);+static JSValue js_parse_regexp(JSParseState *s, int eval_flags);+static size_t js_parse_regexp_flags(int *pre_flags, const uint8_t *buf);+static int re_parse_alternative(JSParseState *s, int state, int dummy_param);+static int re_parse_disjunction(JSParseState *s, int state, int dummy_param);++#ifdef DUMP_BYTECODE+static __maybe_unused void dump_byte_code(JSContext *ctx, JSFunctionBytecode *b)+{+ JSByteArray *arr, *pc2line;+ JSValueArray *cpool, *vars, *ext_vars;+ const JSOpCode *oi;+ int pos, op, size, addr, idx, arg_count, len, i, line_num, col_num;+ int line_num1, col_num1, hoisted_code_len;+ uint8_t *tab;+ uint32_t pc2line_pos;+ + arr = JS_VALUE_TO_PTR(b->byte_code);+ if (b->cpool != JS_NULL)+ cpool = JS_VALUE_TO_PTR(b->cpool);+ else+ cpool = NULL;+ if (b->vars != JS_NULL)+ vars = JS_VALUE_TO_PTR(b->vars);+ else+ vars = NULL;+ if (b->ext_vars != JS_NULL)+ ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ else+ ext_vars = NULL;+ if (b->pc2line != JS_NULL)+ pc2line = JS_VALUE_TO_PTR(b->pc2line);+ else+ pc2line = NULL;++ arg_count = b->arg_count;++ JS_PrintValueF(ctx, b->filename, JS_DUMP_NOQUOTE);+ js_printf(ctx, ": function ");+ JS_PrintValueF(ctx, b->func_name, JS_DUMP_NOQUOTE);+ js_printf(ctx, ":\n");++ if (b->arg_count && vars) {+ js_printf(ctx, " args:");+ for(i = 0; i < b->arg_count; i++) {+ js_printf(ctx, " ");+ JS_PrintValue(ctx, vars->arr[i]);+ }+ js_printf(ctx, "\n");+ }+ if (vars) {+ js_printf(ctx, " locals:");+ for(i = 0; i < vars->size - b->arg_count; i++) {+ js_printf(ctx, " ");+ JS_PrintValue(ctx, vars->arr[i + b->arg_count]);+ }+ js_printf(ctx, "\n");+ }+ if (ext_vars) {+ js_printf(ctx, " refs:");+ for(i = 0; i < b->ext_vars_len; i++) {+ int var_kind, var_idx, decl;+ static const char *var_kind_str[] = { "arg", "var", "ref", "global" };+ js_printf(ctx, " ");+ JS_PrintValue(ctx, ext_vars->arr[2 * i]);+ decl = JS_VALUE_GET_INT(ext_vars->arr[2 * i + 1]);+ var_kind = decl >> 16;+ var_idx = decl & 0xffff;+ js_printf(ctx, " (%s:%d)", var_kind_str[var_kind], var_idx);+ }+ js_printf(ctx, "\n");+ }+ + js_printf(ctx, " cpool_size: %d\n", cpool ? (int)cpool->size : 0);+ js_printf(ctx, " stack_size: %d\n", b->stack_size);+ js_printf(ctx, " opcodes:\n");+ tab = arr->buf;+ len = arr->size;+ pos = 0;+ pc2line_pos = 0;+ hoisted_code_len = 0;+ if (pc2line)+ hoisted_code_len = get_pc2line_hoisted_code_len(pc2line->buf, pc2line->size);+ line_num = 1;+ col_num = 1;+ line_num1 = 0;+ col_num1 = 0;+ while (pos < len) {+ /* extract the debug info */+ if (pc2line && pos >= hoisted_code_len) {+ get_pc2line(&line_num, &col_num, pc2line->buf, pc2line->size,+ &pc2line_pos, b->has_column);+ if (line_num != line_num1 || col_num != col_num1) {+ js_printf(ctx, " # %d", line_num);+ if (b->has_column)+ js_printf(ctx, ", %d", col_num);+ js_printf(ctx, "\n");+ line_num1 = line_num;+ col_num1 = col_num;+ }+ }+ op = tab[pos];+ js_printf(ctx, "%5d: ", pos);+ if (op >= OP_COUNT) {+ js_printf(ctx, "invalid opcode (0x%02x)\n", op); + pos++;+ continue;+ }+ oi = &opcode_info[op];+ size = oi->size;+ if ((pos + size) > len) {+ js_printf(ctx, "truncated opcode (0x%02x)\n", op);+ break;+ }+ js_printf(ctx, "%s", oi->name);+ pos++;+ switch(oi->fmt) {+ case OP_FMT_u8:+ js_printf(ctx, " %u", (int)get_u8(tab + pos));+ break;+ case OP_FMT_i8:+ js_printf(ctx, " %d", (int)get_i8(tab + pos));+ break;+ case OP_FMT_u16:+ case OP_FMT_npop:+ js_printf(ctx, " %u", (int)get_u16(tab + pos));+ break;+ case OP_FMT_i16:+ js_printf(ctx, " %d", (int)get_i16(tab + pos));+ break;+ case OP_FMT_i32:+ js_printf(ctx, " %d", (int)get_i32(tab + pos));+ break;+ case OP_FMT_u32:+ js_printf(ctx, " %u", (int)get_u32(tab + pos));+ break;+ case OP_FMT_none_int:+ js_printf(ctx, " %d", op - OP_push_0);+ break;+#if 0+ case OP_FMT_npopx:+ js_printf(ctx, " %d", op - OP_call0);+ break;+#endif+ case OP_FMT_label8:+ addr = get_i8(tab + pos);+ goto has_addr1;+ case OP_FMT_label16:+ addr = get_i16(tab + pos);+ goto has_addr1;+ case OP_FMT_label:+ addr = get_u32(tab + pos);+ goto has_addr1;+ has_addr1:+ js_printf(ctx, " %u", addr + pos);+ break;+ case OP_FMT_const8:+ idx = get_u8(tab + pos);+ goto has_pool_idx;+ case OP_FMT_const16:+ idx = get_u16(tab + pos);+ goto has_pool_idx;+ has_pool_idx:+ js_printf(ctx, " %u: ", idx);+ if (idx < cpool->size) {+ JS_PrintValue(ctx, cpool->arr[idx]);+ }+ 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:+ js_printf(ctx, " %d: ", idx);+ idx += arg_count;+ if (idx < vars->size) {+ JS_PrintValue(ctx, vars->arr[idx]);+ }+ 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:+ js_printf(ctx, " %d: ", idx);+ if (idx < vars->size) {+ JS_PrintValue(ctx, vars->arr[idx]);+ }+ break;+#if 0+ case OP_FMT_none_var_ref:+ idx = (op - OP_get_var_ref0) % 4;+ goto has_var_ref;+#endif + case OP_FMT_var_ref:+ idx = get_u16(tab + pos);+ // has_var_ref:+ js_printf(ctx, " %d: ", idx);+ if (2 * idx < ext_vars->size) {+ JS_PrintValue(ctx, ext_vars->arr[2 * idx]);+ }+ break;+ case OP_FMT_value:+ js_printf(ctx, " ");+ idx = get_u32(tab + pos);+ JS_PrintValue(ctx, idx);+ break;+ default:+ break;+ }+ js_printf(ctx, "\n");+ pos += oi->size - 1;+ }+}+#endif /* DUMP_BYTECODE */++static void next_token(JSParseState *s);++static void __attribute((unused)) dump_token(JSParseState *s,+ const JSToken *token)+{+ JSContext *ctx = s->ctx;+ switch(token->val) {+ case TOK_NUMBER:+ /* XXX: TODO */+ js_printf(ctx, "number: %d\n", (int)token->u.d);+ break;+ case TOK_IDENT:+ {+ js_printf(ctx, "ident: ");+ JS_PrintValue(s->ctx, token->value);+ js_printf(ctx, "\n");+ }+ break;+ case TOK_STRING:+ {+ js_printf(ctx, "string: ");+ JS_PrintValue(s->ctx, token->value);+ js_printf(ctx, "\n");+ }+ break;+ case TOK_REGEXP:+ {+ js_printf(ctx, "regexp: ");+ JS_PrintValue(s->ctx, token->value);+ js_printf(ctx, "\n");+ }+ break;+ case TOK_EOF:+ js_printf(ctx, "eof\n");+ break;+ default:+ if (s->token.val >= TOK_FIRST_KEYWORD) {+ js_printf(ctx, "token: ");+ JS_PrintValue(s->ctx, token->value);+ js_printf(ctx, "\n");+ } else if (s->token.val >= 128) {+ js_printf(ctx, "token: %d\n", token->val);+ } else {+ js_printf(ctx, "token: '%c'\n", token->val);+ }+ break;+ }+}++/* return the zero based line and column number in the source. */+static int get_line_col(int *pcol_num, const uint8_t *buf, size_t len)+{+ int line_num, col_num, c;+ size_t i;+ + line_num = 0;+ col_num = 0;+ for(i = 0; i < len; i++) {+ c = buf[i];+ if (c == '\n') {+ line_num++;+ col_num = 0;+ } else if (c < 0x80 || c >= 0xc0) {+ col_num++;+ }+ }+ *pcol_num = col_num;+ return line_num;+}++static void __attribute__((format(printf, 2, 3), noreturn)) js_parse_error(JSParseState *s, const char *fmt, ...)+{+ va_list ap;+ va_start(ap, fmt);+ js_vsnprintf(s->error_msg, sizeof(s->error_msg), fmt, ap);+ va_end(ap);+ longjmp(s->jmp_env, 1);+}++static void js_parse_error_mem(JSParseState *s)+{+ return js_parse_error(s, "not enough memory");+}++static void js_parse_error_stack_overflow(JSParseState *s)+{+ return js_parse_error(s, "stack overflow");+}++static void js_parse_expect1(JSParseState *s, int ch)+{+ if (s->token.val != ch)+ js_parse_error(s, "expecting '%c'", ch);+}++static void js_parse_expect(JSParseState *s, int ch)+{+ js_parse_expect1(s, ch);+ next_token(s);+}++static void 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;+ }+ js_parse_error(s, "expecting '%c'", ';');+ }+ next_token(s);+}++#define SKIP_HAS_ARGUMENTS (1 << 0)+#define SKIP_HAS_FUNC_NAME (1 << 1)+#define SKIP_HAS_SEMI (1 << 2) /* semicolon found inside the first level */++/* Skip parenthesis or blocks. The current token should be '(', '[' or+ '{'. 'func_name' can be JS_NULL. */+static int js_skip_parens(JSParseState *s, JSValue *pfunc_name)+{+ uint8_t state[128];+ int level, c, bits = 0;+ + /* protect from underflow */+ level = 0;+ state[level++] = 0;+ for (;;) {+ switch(s->token.val) {+ case '(':+ c = ')';+ goto add_level;+ case '[':+ c = ']';+ goto add_level;+ case '{':+ c = '}';+ add_level:+ if (level >= sizeof(state)) {+ js_parse_error(s, "too many nested blocks");+ }+ state[level++] = c;+ break;+ case ')':+ case ']':+ case '}':+ c = state[--level];+ if (s->token.val != c)+ js_parse_error(s, "expecting '%c'", c);+ break;+ case TOK_EOF:+ js_parse_error(s, "expecting '%c'", state[level - 1]);+ case TOK_IDENT:+ if (s->token.value == js_get_atom(s->ctx, JS_ATOM_arguments))+ bits |= SKIP_HAS_ARGUMENTS;+ if (pfunc_name && s->token.value == *pfunc_name)+ bits |= SKIP_HAS_FUNC_NAME;+ break;+ case ';':+ if (level == 2)+ bits |= SKIP_HAS_SEMI;+ break;+ }+ next_token(s);+ if (level <= 1)+ break;+ }+ return bits;+}++/* skip an expression until ')' */+static void js_skip_expr(JSParseState *s)+{+ for(;;) {+ switch(s->token.val) {+ case ')':+ return;+ case ';':+ case TOK_EOF:+ js_parse_error(s, "expecting '%c'", ')');+ case '(':+ case '[':+ case '{':+ js_skip_parens(s, NULL);+ break;+ default:+ next_token(s);+ break;+ }+ }+}++typedef struct JSParsePos {+ BOOL got_lf : 8;+ BOOL regexp_allowed : 8;+ uint32_t source_pos;+} JSParsePos;++/* 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 TOK_IF:+ case TOK_WHILE:+ case TOK_FOR:+ case TOK_DO:+ case TOK_CASE:+ case TOK_CATCH:+ case ')':+ case ']':+ case TOK_IDENT:+ return FALSE;+ default:+ return TRUE;+ }+}++static void js_parse_get_pos(JSParseState *s, JSParsePos *sp)+{+ sp->source_pos = s->token.source_pos;+ sp->got_lf = s->got_lf;+ sp->regexp_allowed = is_regexp_allowed(s->token.val);+}++static void js_parse_seek_token(JSParseState *s, const JSParsePos *sp)+{+ s->buf_pos = sp->source_pos;+ s->got_lf = sp->got_lf;+ /* the previous token value is only needed so that+ is_regexp_allowed() returns the correct value */+ s->token.val = sp->regexp_allowed ? ' ' : ')';+ next_token(s);+}++/* same as js_skip_parens but go back to the current token */+static int js_parse_skip_parens_token(JSParseState *s)+{+ JSParsePos pos;+ int bits;+ + js_parse_get_pos(s, &pos);+ bits = js_skip_parens(s, NULL);+ js_parse_seek_token(s, &pos);+ return bits;+}++/* return the escape value or -1 */+static int js_parse_escape(const uint8_t *buf, size_t *plen)+{+ int c;+ const uint8_t *p = buf;+ 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 '\'':+ case '\"':+ case '\\':+ break;+ case 'x':+ {+ int h0, h1;++ h0 = from_hex(*p++);+ if (h0 < 0)+ return -1;+ h1 = from_hex(*p++);+ if (h1 < 0)+ return -1;+ c = (h0 << 4) | h1;+ }+ break;+ case 'u':+ {+ int h, i;+ + if (*p == '{') {+ 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 {+ c = 0;+ for(i = 0; i < 4; i++) {+ h = from_hex(*p++);+ if (h < 0) {+ return -1;+ }+ c = (c << 4) | h;+ }+ }+ }+ break;+ case '0':+ c -= '0';+ if (c != 0 || is_num(*p))+ return -1;+ break;+ default:+ return -2;+ }+ *plen = p - buf;+ return c;+}++static JSValue js_parse_string(JSParseState *s, uint32_t *ppos, int sep)+{+ JSContext *ctx = s->ctx;+ JSValue res;+ const uint8_t *buf;+ uint32_t pos;+ uint32_t c;+ size_t escape_len = 0; /* avoid warning */+ StringBuffer b_s, *b = &b_s;+ + if (string_buffer_push(ctx, b, 16))+ js_parse_error_mem(s);+ buf = s->source_buf;+ /* string */+ pos = *ppos;+ for(;;) {+ c = buf[pos];+ if (c == '\0' || c == '\n' || c == '\r') {+ js_parse_error(s, "unexpected end of string");+ }+ pos++;+ if (c == sep)+ break;+ if (c == '\\') {+ if (buf[pos] == '\n') {+ /* ignore escaped newline sequence */+ pos++;+ continue;+ }+ c = js_parse_escape(buf + pos, &escape_len);+ if (c == -1) {+ js_parse_error(s, "invalid escape sequence");+ } else if (c == -2) {+ /* ignore invalid escapes */+ continue;+ }+ pos += escape_len;+ } else if (c >= 0x80) {+ size_t clen;+ pos--;+ c = unicode_from_utf8(buf + pos, UTF8_CHAR_LEN_MAX, &clen);+ pos += clen;+ if (c == -1) {+ js_parse_error(s, "invalid UTF-8 sequence");+ }+ }+ if (string_buffer_putc(ctx, b, c))+ break;+ buf = s->source_buf; /* may be reallocated */+ }+ *ppos = pos;+ res = string_buffer_pop(ctx, b);+ if (JS_IsException(res))+ js_parse_error_mem(s);+ return res;+}++static void js_parse_ident(JSParseState *s, JSToken *token,+ uint32_t *ppos, int c)+{+ JSContext *ctx = s->ctx;+ uint32_t pos;+ JSValue val, val2;+ JSGCRef val2_ref;+ const uint8_t *buf;+ StringBuffer b_s, *b = &b_s;+ + if (string_buffer_push(ctx, b, 16))+ js_parse_error_mem(s);+ string_buffer_putc(ctx, b, c); /* no allocation */+ buf = s->source_buf;+ pos = *ppos;+ while (pos < s->buf_len) {+ c = buf[pos];+ if (!is_ident_next(c))+ break;+ pos++;+ if (string_buffer_putc(ctx, b, c))+ break;+ buf = s->source_buf; /* may be reallocated */+ }+ /* convert to token if necessary */+ token->val = TOK_IDENT;+ val2 = string_buffer_pop(ctx, b);+ JS_PUSH_VALUE(ctx, val2);+ val = JS_MakeUniqueString(ctx, val2);+ JS_POP_VALUE(ctx, val2);+ if (JS_IsException(val))+ js_parse_error_mem(s);+ if (val != val2)+ js_free(ctx, JS_VALUE_TO_PTR(val2));+ token->value = val;+ if (JS_IsPtr(val)) {+ const JSWord *atom_start, *atom_last, *ptr;+ atom_start = ctx->atom_table;+ atom_last = atom_start + JS_ATOM_yield;+ ptr = JS_VALUE_TO_PTR(val);+ if (ptr >= atom_start && ptr <= atom_last) {+ token->val = TOK_NULL + (ptr - atom_start);+ }+ }+ *ppos = pos;+}++static void js_parse_regexp_token(JSParseState *s, uint32_t *ppos)+{+ JSContext *ctx = s->ctx;+ uint32_t pos;+ uint32_t c;+ BOOL in_class;+ size_t clen;+ int re_flags, end_pos, start_pos;+ JSString *p;+ + in_class = FALSE;+ pos = *ppos;+ start_pos = pos;+ for(;;) {+ c = unicode_from_utf8(s->source_buf + pos, UTF8_CHAR_LEN_MAX, &clen);+ if (c == -1) + js_parse_error(s, "invalid UTF-8 sequence");+ pos += clen;+ if (c == '\0' || c == '\n' || c == '\r') {+ goto invalid_char;+ } else if (c == '/') {+ if (!in_class)+ break;+ } else if (c == '[') {+ in_class = TRUE;+ } else if (c == ']') {+ in_class = FALSE;+ } else if (c == '\\') {+ c = unicode_from_utf8(s->source_buf + pos, UTF8_CHAR_LEN_MAX, &clen);+ if (c == -1) + js_parse_error(s, "invalid UTF-8 sequence");+ if (c == '\0' || c == '\n' || c == '\r') {+ invalid_char:+ js_parse_error(s, "unexpected line terminator in regexp");+ }+ pos += clen;+ }+ }+ end_pos = pos - 1;+ + clen = js_parse_regexp_flags(&re_flags, s->source_buf + pos);+ pos += clen;+ if (is_ident_next(s->source_buf[pos]))+ js_parse_error(s, "invalid regular expression flags");++ /* XXX: single char string is not optimized */+ p = js_alloc_string(ctx, end_pos - start_pos);+ if (!p)+ js_parse_error_mem(s);+ p->is_ascii = is_ascii_string((char *)(s->source_buf + start_pos), end_pos - start_pos);+ memcpy(p->buf, s->source_buf + start_pos, end_pos - start_pos);+ + *ppos = pos;+ s->token.val = TOK_REGEXP;+ s->token.value = JS_VALUE_FROM_PTR(p);+ s->token.u.regexp.re_flags = re_flags;+ s->token.u.regexp.re_end_pos = end_pos;+}++static void next_token(JSParseState *s)+{+ uint32_t pos;+ const uint8_t *p;+ int c;+ + pos = s->buf_pos;+ s->got_lf = FALSE;+ s->token.value = JS_NULL;+ p = s->source_buf + s->buf_pos;+ redo:+ s->token.source_pos = p - s->source_buf;+ c = *p;+ switch(c) {+ case 0:+ s->token.val = TOK_EOF;+ break;+ case '\"':+ case '\'':+ p++;+ pos = p - s->source_buf;+ s->token.value = js_parse_string(s, &pos, c);+ s->token.val = TOK_STRING;+ p = s->source_buf + pos;+ break;+ case '\n':+ s->got_lf = TRUE;+ p++;+ goto redo;+ case ' ':+ case '\t':+ case '\f':+ case '\v':+ case '\r':+ p++;+ goto redo;+ case '/':+ if (p[1] == '*') {+ /* comment */+ p += 2;+ for(;;) {+ if (*p == '\0')+ js_parse_error(s, "unexpected end of comment");+ if (p[0] == '*' && p[1] == '/') {+ p += 2;+ break;+ }+ p++;+ }+ goto redo;+ } else if (p[1] == '/') {+ /* line comment */+ p += 2;+ for(;;) {+ if (*p == '\0' || *p == '\n')+ break;+ p++;+ }+ goto redo;+ } else if (is_regexp_allowed(s->token.val)) {+ /* Note: we recognize regexps in the lexer. It does not+ handle all the cases e.g. "({x:1} / 2)" or "a.void / 2" but+ is consistent when we tokenize the input without+ parsing it. */+ p++;+ pos = p - s->source_buf;+ js_parse_regexp_token(s, &pos);+ p = s->source_buf + pos;+ } else if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_DIV_ASSIGN;+ } else {+ p++;+ s->token.val = c;+ }+ break;+ case 'a' ... 'z':+ case 'A' ... 'Z': + case '_': + case '$':+ p++;+ pos = p - s->source_buf;+ js_parse_ident(s, &s->token, &pos, c);+ p = s->source_buf + pos;+ break;+ case '.':+ if (is_digit(p[1]))+ goto parse_number;+ else+ goto def_token;+ case '0':+ /* in strict mode, octal literals are not accepted */+ if (is_digit(p[1]))+ goto invalid_number;+ goto parse_number;+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9':+ /* number */+ parse_number:+ {+ double d;+ JSByteArray *tmp_arr;+ pos = p - s->source_buf;+ tmp_arr = js_alloc_byte_array(s->ctx, sizeof(JSATODTempMem));+ if (!tmp_arr)+ js_parse_error_mem(s);+ p = s->source_buf + pos;+ d = js_atod((const char *)p, (const char **)&p, 0,+ JS_ATOD_ACCEPT_BIN_OCT | JS_ATOD_ACCEPT_UNDERSCORES,+ (JSATODTempMem *)tmp_arr->buf);+ js_free(s->ctx, tmp_arr);+ if (isnan(d)) {+ invalid_number:+ js_parse_error(s, "invalid number literal");+ }+ s->token.val = TOK_NUMBER;+ s->token.u.d = d;+ }+ 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] == '-') {+ 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 {+ 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 {+ 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;+ case '^':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_XOR_ASSIGN;+ } else {+ goto def_token;+ }+ break;+ 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;+ default:+ if (c >= 128) {+ js_parse_error(s, "unexpected character");+ }+ def_token:+ s->token.val = c;+ p++;+ break;+ }+ s->buf_pos = p - s->source_buf;+#if defined(DUMP_TOKEN)+ dump_token(s, &s->token);+#endif+}++/* test if the current token is a label. XXX: we assume there is no+ space between the identifier and the ':' to avoid having to push+ back a token */+static BOOL is_label(JSParseState *s)+{+ return (s->token.val == TOK_IDENT && s->source_buf[s->buf_pos] == ':');+}++static inline uint8_t *get_byte_code(JSParseState *s)+{+ JSByteArray *arr;+ arr = JS_VALUE_TO_PTR(s->byte_code);+ return arr->buf;+}++static void emit_claim_size(JSParseState *s, int n)+{+ JSValue val;+ val = js_resize_byte_array(s->ctx, s->byte_code, s->byte_code_len + n);+ if (JS_IsException(val))+ js_parse_error_mem(s);+ s->byte_code = val;+}++static void emit_u8(JSParseState *s, uint8_t val)+{+ JSByteArray *arr;+ emit_claim_size(s, 1);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[s->byte_code_len++] = val;+}++static void emit_u16(JSParseState *s, uint16_t val)+{+ JSByteArray *arr;+ emit_claim_size(s, 2);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ put_u16(arr->buf + s->byte_code_len, val);+ s->byte_code_len += 2;+}++static void emit_u32(JSParseState *s, uint32_t val)+{+ JSByteArray *arr;+ emit_claim_size(s, 4);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ put_u32(arr->buf + s->byte_code_len, val);+ s->byte_code_len += 4;+}++/* precondition: 1 <= n <= 25. */+static void pc2line_put_bits_short(JSParseState *s, int n, uint32_t bits)+{+ JSFunctionBytecode *b;+ JSValue val1;+ JSByteArray *arr;+ uint32_t index, pos;+ unsigned int val;+ int shift;+ uint8_t *p;++ index = s->pc2line_bit_len;+ pos = index >> 3;++ /* resize the array if needed */+ b = JS_VALUE_TO_PTR(s->cur_func);+ val1 = js_resize_byte_array(s->ctx, b->pc2line, pos + 4);+ if (JS_IsException(val1))+ js_parse_error_mem(s);+ b = JS_VALUE_TO_PTR(s->cur_func);+ b->pc2line = val1;++ arr = JS_VALUE_TO_PTR(val1);+ p = arr->buf + pos;+ val = get_be32(p);+ shift = (32 - (index & 7) - n);+ val &= ~(((1U << n) - 1) << shift); /* reset the bits */+ val |= bits << shift;+ put_be32(p, val);+ s->pc2line_bit_len = index + n;+}++/* precondition: 1 <= n <= 32 */+static void pc2line_put_bits(JSParseState *s, int n, uint32_t bits)+{+ int n_max = 25;+ if (unlikely(n > n_max)) {+ pc2line_put_bits_short(s, n - n_max, bits >> n_max);+ bits &= (1 << n_max) - 1;+ n = n_max;+ }+ pc2line_put_bits_short(s, n, bits);+}++/* 0 <= v < 2^32-1 */+static void put_ugolomb(JSParseState *s, uint32_t v)+{+ int n;+ // printf("put_ugolomb: %u\n", v);+ v++;+ n = 32 - clz32(v);+ if (n > 1)+ pc2line_put_bits(s, n - 1, 0);+ pc2line_put_bits(s, n, v);+}++/* v != -2^31 */+static void put_sgolomb(JSParseState *s, int32_t v1)+{+ uint32_t v = v1;+ put_ugolomb(s, (2 * v) ^ -(v >> 31));+}++//#define DUMP_PC2LINE_STATS++#ifdef DUMP_PC2LINE_STATS+static int pc2line_freq[256];+static int pc2line_freq_tot;+#endif++/* return the difference between the line numbers from 'pos1' to+ 'pos2'. If the difference is zero, '*pcol_num' contains the+ difference between the column numbers. Otherwise it contains the+ zero based absolute column number.+*/+static int get_line_col_delta(int *pcol_num, const uint8_t *buf,+ int pos1, int pos2)+{+ int line_num, col_num, c, i;+ line_num = 0;+ col_num = 0;+ if (pos2 >= pos1) {+ line_num = get_line_col(&col_num, buf + pos1, pos2 - pos1);+ } else {+ line_num = get_line_col(&col_num, buf + pos2, pos1 - pos2);+ line_num = -line_num;+ col_num = -col_num;+ if (line_num != 0) {+ /* find the absolute column position */+ col_num = 0;+ for(i = pos2 - 1; i >= 0; i--) {+ c = buf[i];+ if (c == '\n') {+ break;+ } else if (c < 0x80 || c >= 0xc0) {+ col_num++;+ }+ }+ }+ }+ *pcol_num = col_num;+ return line_num;+}++static void emit_pc2line(JSParseState *s, JSSourcePos pos)+{+ int line_delta, col_delta;++ line_delta = get_line_col_delta(&col_delta, s->source_buf,+ s->pc2line_source_pos, pos);+ put_sgolomb(s, line_delta);+ if (s->has_column) {+ if (line_delta == 0) {+#ifdef DUMP_PC2LINE_STATS+ pc2line_freq[min_int(max_int(col_delta + 128, 0), 255)]++;+ pc2line_freq_tot++;+#endif+ put_sgolomb(s, col_delta);+ } else {+ put_ugolomb(s, col_delta);+ }+ }+ s->pc2line_source_pos = pos;+}++#ifdef DUMP_PC2LINE_STATS+void dump_pc2line(void)+{+ int i;+ for(i = 0; i < 256; i++) {+ if (pc2line_freq[i] != 0) {+ printf("%d: %d %0.2f\n",+ i - 128, pc2line_freq[i],+ -log2((double)pc2line_freq[i] / pc2line_freq_tot));+ }+ }+}+#endif++/* warning: pc2line info must be associated to each generated opcode */+static void emit_op_pos(JSParseState *s, uint8_t op, JSSourcePos source_pos)+{+ s->last_opcode_pos = s->byte_code_len;+ s->last_pc2line_pos = s->pc2line_bit_len;+ s->last_pc2line_source_pos = s->pc2line_source_pos;++ emit_pc2line(s, source_pos);+ emit_u8(s, op);+}++static void emit_op(JSParseState *s, uint8_t op)+{+ emit_op_pos(s, op, s->pc2line_source_pos);+}++static void emit_op_param(JSParseState *s, uint8_t op, uint32_t param,+ JSSourcePos source_pos)+{+ const JSOpCode *oi;++ emit_op_pos(s, op, source_pos);+ oi = &opcode_info[op];+ switch(oi->fmt) {+ case OP_FMT_none:+ break;+ case OP_FMT_npop:+ emit_u16(s, param);+ break;+ default:+ assert(0);+ }+}++/* insert 'n' bytes at position pos */+static void emit_insert(JSParseState *s, int pos, int n)+{+ JSByteArray *arr;+ emit_claim_size(s, n);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ memmove(arr->buf + pos + n, arr->buf + pos, s->byte_code_len - pos);+ s->byte_code_len += n;+}++static inline int get_prev_opcode(JSParseState *s)+{+ if (s->last_opcode_pos < 0) {+ return OP_invalid;+ } else {+ uint8_t *byte_code = get_byte_code(s);+ return byte_code[s->last_opcode_pos];+ }+}++static BOOL js_is_live_code(JSParseState *s) {+ switch (get_prev_opcode(s)) {+ case OP_return:+ case OP_return_undef:+ case OP_throw:+ case OP_goto:+ case OP_ret:+ return FALSE;+ default:+ return TRUE;+ }+}++static void remove_last_op(JSParseState *s)+{+ s->byte_code_len = s->last_opcode_pos;+ s->pc2line_bit_len = s->last_pc2line_pos;+ s->pc2line_source_pos = s->last_pc2line_source_pos;+ s->last_opcode_pos = -1;+}++static void emit_push_short_int(JSParseState *s, int val)+{+ if (val >= -1 && val <= 7) {+ emit_op(s, OP_push_0 + val);+ } else if (val == (int8_t)val) {+ emit_op(s, OP_push_i8);+ emit_u8(s, val);+ } else if (val == (int16_t)val) {+ emit_op(s, OP_push_i16);+ emit_u16(s, val);+ } else {+ emit_op(s, OP_push_value);+ emit_u32(s, JS_NewShortInt(val));+ }+}++static void emit_var(JSParseState *s, int opcode, int var_idx,+ JSSourcePos source_pos)+{+ switch(opcode) {+ case OP_get_loc:+ if (var_idx < 4) {+ emit_op_pos(s, OP_get_loc0 + var_idx, source_pos);+ return;+ } else if (var_idx < 256) {+ emit_op_pos(s, OP_get_loc8, source_pos);+ emit_u8(s, var_idx);+ return;+ }+ break;+ case OP_put_loc:+ if (var_idx < 4) {+ emit_op_pos(s, OP_put_loc0 + var_idx, source_pos);+ return;+ } else if (var_idx < 256) {+ emit_op_pos(s, OP_put_loc8, source_pos);+ emit_u8(s, var_idx);+ return;+ }+ break;+ case OP_get_arg:+ if (var_idx < 4) {+ emit_op_pos(s, OP_get_arg0 + var_idx, source_pos);+ return;+ }+ break;+ case OP_put_arg:+ if (var_idx < 4) {+ emit_op_pos(s, OP_put_arg0 + var_idx, source_pos);+ return;+ }+ break;+ }+ emit_op_pos(s, opcode, source_pos);+ emit_u16(s, var_idx);+}+++typedef enum {+ JS_PARSE_FUNC_STATEMENT,+ JS_PARSE_FUNC_EXPR,+ JS_PARSE_FUNC_METHOD,+} JSParseFunctionEnum;++static void js_parse_function_decl(JSParseState *s,+ JSParseFunctionEnum func_type, JSValue func_name);++/* labels are short integers so they can be used as JSValue. -1 is not+ a valid label. */+#define LABEL_RESOLVED_FLAG (1 << 29)+#define LABEL_OFFSET_MASK ((1 << 29) - 1)++#define LABEL_NONE JS_NewShortInt(-1)++static BOOL label_is_none(JSValue label)+{+ return JS_VALUE_GET_INT(label) < 0;+}++static JSValue new_label(JSParseState *s)+{+ return JS_NewShortInt(LABEL_OFFSET_MASK);+}++static void emit_label_pos(JSParseState *s, JSValue *plabel, int pos)+{+ int label;+ JSByteArray *arr;+ int next;++ label = JS_VALUE_GET_INT(*plabel);+ assert(!(label & LABEL_RESOLVED_FLAG));+ arr = JS_VALUE_TO_PTR(s->byte_code);+ while (label != LABEL_OFFSET_MASK) {+ next = get_u32(arr->buf + label);+ put_u32(arr->buf + label, pos - label);+ label = next;+ }+ *plabel = JS_NewShortInt(pos | LABEL_RESOLVED_FLAG);+}++static void emit_label(JSParseState *s, JSValue *plabel)+{+ emit_label_pos(s, plabel, s->byte_code_len);+ /* prevent get_lvalue from using the last expression as an+ lvalue. */+ s->last_opcode_pos = -1;+}++static void emit_goto(JSParseState *s, int opcode, JSValue *plabel)+{+ int label;+ /* XXX: generate smaller gotos when possible */+ emit_op(s, opcode);+ label = JS_VALUE_GET_INT(*plabel);+ if (label & LABEL_RESOLVED_FLAG) {+ emit_u32(s, (label & LABEL_OFFSET_MASK) - s->byte_code_len);+ } else {+ emit_u32(s, label);+ *plabel = JS_NewShortInt(s->byte_code_len - 4);+ }+}++/* return the constant pool index. 'val' is not duplicated. */+static int cpool_add(JSParseState *s, JSValue val)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;+ int i;+ JSValue new_cpool;+ JSGCRef val_ref;+ + b = JS_VALUE_TO_PTR(s->cur_func);+ arr = JS_VALUE_TO_PTR(b->cpool);+ /* check if the value is already present */+ for(i = 0; i < s->cpool_len; i++) {+ if (arr->arr[i] == val)+ return i;+ }++ if (s->cpool_len > 65535)+ js_parse_error(s, "too many constants");+ JS_PUSH_VALUE(s->ctx, val);+ new_cpool = js_resize_value_array(s->ctx, b->cpool, max_int(s->cpool_len + 1, 4));+ JS_POP_VALUE(s->ctx, val);+ if (JS_IsException(new_cpool))+ js_parse_error_mem(s);+ b = JS_VALUE_TO_PTR(s->cur_func);+ b->cpool = new_cpool;+ arr = JS_VALUE_TO_PTR(b->cpool);+ arr->arr[s->cpool_len++] = val;+ return s->cpool_len - 1;+}++static void js_emit_push_const(JSParseState *s, JSValue val)+{+ int idx;++ if (JS_IsPtr(val)+#ifdef JS_USE_SHORT_FLOAT+ || JS_IsShortFloat(val)+#endif+ ) {+ /* We use a constant pool to avoid scanning the bytecode+ during the GC. XXX: is it a good choice ? */+ idx = cpool_add(s, val);+ emit_op(s, OP_push_const);+ emit_u16(s, idx);+ } else {+ /* no GC mark */+ emit_op(s, OP_push_value);+ emit_u32(s, val);+ }+}++/* return the local variable index or -1 if not found */+static int find_func_var(JSContext *ctx, JSValue func, JSValue name)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;+ int i;++ b = JS_VALUE_TO_PTR(func);+ if (b->vars == JS_NULL)+ return -1;+ arr = JS_VALUE_TO_PTR(b->vars);+ for(i = 0; i < arr->size; i++) {+ if (arr->arr[i] == name)+ return i;+ }+ return -1;+}++static int find_var(JSParseState *s, JSValue name)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;+ int i;++ b = JS_VALUE_TO_PTR(s->cur_func);+ arr = JS_VALUE_TO_PTR(b->vars);+ for(i = 0; i < s->local_vars_len; i++) {+ if (arr->arr[i] == name)+ return i;+ }+ return -1;+}++static JSValue get_ext_var_name(JSParseState *s, int var_idx)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;++ b = JS_VALUE_TO_PTR(s->cur_func);+ arr = JS_VALUE_TO_PTR(b->ext_vars);+ return arr->arr[2 * var_idx];+}++static int find_func_ext_var(JSParseState *s, JSValue func, JSValue name)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;+ int i;++ b = JS_VALUE_TO_PTR(func);+ arr = JS_VALUE_TO_PTR(b->ext_vars);+ for(i = 0; i < b->ext_vars_len; i++) {+ if (arr->arr[2 * i] == name)+ return i;+ }+ return -1;+}++/* return the external variable index or -1 if not found */+static int find_ext_var(JSParseState *s, JSValue name)+{+ return find_func_ext_var(s, s->cur_func, name);+}++/* return the external variable index */+static int add_func_ext_var(JSParseState *s, JSValue func, JSValue name, int decl)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;+ JSValue new_ext_vars;+ JSGCRef name_ref, func_ref;+ + b = JS_VALUE_TO_PTR(func);+ if (b->ext_vars_len >= JS_MAX_LOCAL_VARS) + js_parse_error(s, "too many variable references");+ JS_PUSH_VALUE(s->ctx, func);+ JS_PUSH_VALUE(s->ctx, name);+ new_ext_vars = js_resize_value_array(s->ctx, b->ext_vars, max_int(b->ext_vars_len + 1, 2) * 2);+ JS_POP_VALUE(s->ctx, name);+ JS_POP_VALUE(s->ctx, func);+ if (JS_IsException(new_ext_vars))+ js_parse_error_mem(s);+ b = JS_VALUE_TO_PTR(func);+ b->ext_vars = new_ext_vars;+ arr = JS_VALUE_TO_PTR(b->ext_vars);+ arr->arr[2 * b->ext_vars_len] = name;+ arr->arr[2 * b->ext_vars_len + 1] = JS_NewShortInt(decl);+ b->ext_vars_len++;+ return b->ext_vars_len - 1;+}++/* return the external variable index */+static int add_ext_var(JSParseState *s, JSValue name, int decl)+{+ return add_func_ext_var(s, s->cur_func, name, decl);+}++/* return the local variable index */+static int add_var(JSParseState *s, JSValue name)+{+ JSFunctionBytecode *b;+ JSValueArray *arr;+ JSValue new_vars;+ JSGCRef name_ref;+ + b = JS_VALUE_TO_PTR(s->cur_func);+ if (s->local_vars_len >= JS_MAX_LOCAL_VARS) + js_parse_error(s, "too many local variables");+ JS_PUSH_VALUE(s->ctx, name);+ new_vars = js_resize_value_array(s->ctx, b->vars, max_int(s->local_vars_len + 1, 4));+ JS_POP_VALUE(s->ctx, name);+ if (JS_IsException(new_vars))+ js_parse_error_mem(s);+ b = JS_VALUE_TO_PTR(s->cur_func);+ b->vars = new_vars;+ arr = JS_VALUE_TO_PTR(b->vars);+ arr->arr[s->local_vars_len++] = name;+ return s->local_vars_len - 1;+}++static void get_lvalue(JSParseState *s, int *popcode,+ int *pvar_idx, JSSourcePos *psource_pos, BOOL keep)+{+ int opcode, var_idx;+ JSSourcePos source_pos;+ + /* we check the last opcode to get the lvalue type */+ opcode = get_prev_opcode(s);+ switch(opcode) {+ case OP_get_loc0:+ case OP_get_loc1:+ case OP_get_loc2:+ case OP_get_loc3:+ var_idx = opcode - OP_get_loc0;+ opcode = OP_get_loc;+ break;+ case OP_get_arg0:+ case OP_get_arg1:+ case OP_get_arg2:+ case OP_get_arg3:+ var_idx = opcode - OP_get_arg0;+ opcode = OP_get_arg;+ break;+ case OP_get_loc8:+ var_idx = get_u8(get_byte_code(s) + s->last_opcode_pos + 1);+ opcode = OP_get_loc;+ break;+ case OP_get_loc:+ case OP_get_arg:+ case OP_get_var_ref:+ case OP_get_field:+ var_idx = get_u16(get_byte_code(s) + s->last_opcode_pos + 1);+ break;+ case OP_get_array_el:+ case OP_get_length:+ var_idx = -1;+ break;+ default:+ js_parse_error(s, "invalid lvalue");+ }+ source_pos = s->pc2line_source_pos;++ /* remove the last opcode */+ remove_last_op(s);++ if (keep) {+ /* get the value but keep the object/fields on the stack */+ switch(opcode) {+ case OP_get_loc:+ case OP_get_arg:+ case OP_get_var_ref:+ emit_var(s, opcode, var_idx, source_pos);+ break;+ case OP_get_field:+ emit_op_pos(s, OP_get_field2, source_pos);+ emit_u16(s, var_idx);+ break;+ case OP_get_length:+ emit_op_pos(s, OP_get_length2, source_pos);+ break;+ case OP_get_array_el:+ emit_op(s, OP_dup2);+ emit_op_pos(s, OP_get_array_el, source_pos); /* XXX: add OP_get_array_el3 but need to modify tail call */+ break;+ default:+ abort();+ }+ }++ *popcode = opcode;+ *pvar_idx = var_idx;+ *psource_pos = source_pos;+}++typedef enum {+ PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */+ PUT_LVALUE_NOKEEP_TOP, /* [depth] v -> */+ PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */+ PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */+} PutLValueEnum;++static void put_lvalue(JSParseState *s, int opcode,+ int var_idx, JSSourcePos source_pos,+ PutLValueEnum special)+{+ switch(opcode) {+ case OP_get_loc:+ case OP_get_arg:+ case OP_get_var_ref:+ if (special == PUT_LVALUE_KEEP_TOP)+ emit_op(s, OP_dup);+ if (opcode == OP_get_var_ref && s->is_repl)+ opcode = OP_put_var_ref_nocheck; /* an assignment defines the variable in the REPL */+ else+ opcode++;+ emit_var(s, opcode, var_idx, source_pos);+ break;+ case OP_get_field:+ case OP_get_length:+ switch(special) {+ case PUT_LVALUE_KEEP_TOP:+ emit_op(s, OP_insert2); /* obj a -> a obj a */+ break;+ case PUT_LVALUE_NOKEEP_TOP:+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_swap); /* a obj -> obj a */+ break;+ default:+ case PUT_LVALUE_KEEP_SECOND:+ emit_op(s, OP_perm3); /* obj a b -> a obj b */+ break;+ }+ emit_op_pos(s, OP_put_field, source_pos);+ if (opcode == OP_get_length) {+ emit_u16(s, cpool_add(s, js_get_atom(s->ctx, JS_ATOM_length)));+ } else {+ emit_u16(s, var_idx);+ }+ break;+ case OP_get_array_el:+ switch(special) {+ case PUT_LVALUE_KEEP_TOP:+ emit_op(s, OP_insert3); /* obj prop a -> a obj prop a */+ break;+ case PUT_LVALUE_NOKEEP_TOP:+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM: /* a obj prop -> obj prop a */+ emit_op(s, OP_rot3l); /* obj prop a b -> a obj prop b */+ break;+ default:+ case PUT_LVALUE_KEEP_SECOND:+ emit_op(s, OP_perm4); /* obj prop a b -> a obj prop b */+ break;+ }+ emit_op_pos(s, OP_put_array_el, source_pos);+ break;+ default:+ abort();+ }+}++enum {+ PARSE_PROP_FIELD,+ PARSE_PROP_GET,+ PARSE_PROP_SET,+ PARSE_PROP_METHOD,+};++static int js_parse_property_name(JSParseState *s, JSValue *pname)+{+ JSContext *ctx = s->ctx;+ JSValue name;+ JSGCRef name_ref;+ int prop_type;++ prop_type = PARSE_PROP_FIELD;++ if (s->token.val == TOK_IDENT) {+ int is_set;+ if (s->token.value == js_get_atom(ctx, JS_ATOM_get))+ is_set = 0;+ else if (s->token.value == js_get_atom(ctx, JS_ATOM_set))+ is_set = 1;+ else+ is_set = -1;+ if (is_set >= 0) {+ next_token(s);+ if (s->token.val == ':' || s->token.val == ',' ||+ s->token.val == '}' || s->token.val == '(') {+ /* not a get set */+ name = js_get_atom(ctx, is_set ? JS_ATOM_set : JS_ATOM_get);+ goto done;+ }+ prop_type = PARSE_PROP_GET + is_set;+ }+ }++ if (s->token.val == TOK_IDENT || s->token.val >= TOK_FIRST_KEYWORD) {+ name = s->token.value;+ } else if (s->token.val == TOK_STRING) {+ name = s->token.value;+ } else if (s->token.val == TOK_NUMBER) {+ name = JS_NewFloat64(s->ctx, s->token.u.d);+ if (JS_IsException(name))+ js_parse_error_mem(s);+ } else {+ js_parse_error(s, "invalid property name");+ }+ name = JS_ToPropertyKey(s->ctx, name);+ if (JS_IsException(name))+ js_parse_error_mem(s);+ JS_PUSH_VALUE(ctx, name);+ next_token(s);+ JS_POP_VALUE(ctx, name);+ done:+ if (prop_type == PARSE_PROP_FIELD && s->token.val == '(')+ prop_type = PARSE_PROP_METHOD;+ *pname = name;+ return prop_type;+}++/* recursion free parser definitions */++#define PF_NO_IN (1 << 0) /* the 'in' operator is not accepted*/+#define PF_DROP (1 << 1) /* drop result */+#define PF_ACCEPT_LPAREN (1 << 2) /* js_parse_postfix_expr only */+#define PF_LEVEL_SHIFT 4 /* optional level parameter */+#define PF_LEVEL_MASK (0xf << PF_LEVEL_SHIFT)++typedef enum {+ PARSE_FUNC_js_parse_expr_comma,+ PARSE_FUNC_js_parse_assign_expr,+ PARSE_FUNC_js_parse_cond_expr,+ PARSE_FUNC_js_parse_logical_and_or,+ PARSE_FUNC_js_parse_expr_binary,+ PARSE_FUNC_js_parse_unary,+ PARSE_FUNC_js_parse_postfix_expr,+ PARSE_FUNC_js_parse_statement,+ PARSE_FUNC_js_parse_block,+ PARSE_FUNC_js_parse_json_value,+ PARSE_FUNC_re_parse_alternative,+ PARSE_FUNC_re_parse_disjunction,+} ParseExprFuncEnum;++typedef int JSParseFunc(JSParseState *s, int state, int param);++#define PARSE_STATE_INIT 0xfe+#define PARSE_STATE_RET 0xff++/* may trigger a gc */+static JSValue parse_stack_alloc(JSParseState *s, JSValue val)+{+ JSGCRef val_ref;+ + JS_PUSH_VALUE(s->ctx, val);+ if (JS_StackCheck(s->ctx, 1))+ js_parse_error_stack_overflow(s);+ JS_POP_VALUE(s->ctx, val);+ return val;+}++/* WARNING: 'val' may be modified after this val if it is a pointer */+static void js_parse_push_val(JSParseState *s, JSValue val)+{+ JSContext *ctx = s->ctx;+ if (unlikely(ctx->sp <= ctx->stack_bottom)) {+ val = parse_stack_alloc(s, val);+ }+ *--(ctx->sp) = val;+}++/* update the stack bottom when there is a large stack space */+static JSValue js_parse_pop_val(JSParseState *s)+{+ JSContext *ctx = s->ctx;+ JSValue val;+ val = *(ctx->sp)++;+ if (unlikely(ctx->sp - JS_STACK_SLACK > ctx->stack_bottom))+ ctx->stack_bottom = ctx->sp - JS_STACK_SLACK;+ return val;+}++#define PARSE_PUSH_VAL(s, v) js_parse_push_val(s, v)+#define PARSE_POP_VAL(s, v) v = js_parse_pop_val(s)++#define PARSE_PUSH_INT(s, v) js_parse_push_val(s, JS_NewShortInt(v))+#define PARSE_POP_INT(s, v) v = JS_VALUE_GET_INT(js_parse_pop_val(s))++#define PARSE_START1() \+ switch(state) {\+ case PARSE_STATE_INIT: break;\+ default: abort();\+ case 0: goto parse_state0;\+ }++#define PARSE_START2() \+ switch(state) {\+ case PARSE_STATE_INIT: break;\+ default: abort();\+ case 0: goto parse_state0;\+ case 1: goto parse_state1;\+ }++#define PARSE_START3() \+ switch(state) {\+ case PARSE_STATE_INIT: break;\+ default: abort();\+ case 0: goto parse_state0;\+ case 1: goto parse_state1;\+ case 2: goto parse_state2;\+ }++#define PARSE_START7() \+ switch(state) {\+ case PARSE_STATE_INIT: break;\+ default: abort();\+ case 0: goto parse_state0;\+ case 1: goto parse_state1;\+ case 2: goto parse_state2;\+ case 3: goto parse_state3; \+ case 4: goto parse_state4;\+ case 5: goto parse_state5;\+ case 6: goto parse_state6;\+ }++#define PARSE_START12() \+ switch(state) {\+ case PARSE_STATE_INIT: break;\+ default: abort();\+ case 0: goto parse_state0;\+ case 1: goto parse_state1;\+ case 2: goto parse_state2;\+ case 3: goto parse_state3; \+ case 4: goto parse_state4;\+ case 5: goto parse_state5;\+ case 6: goto parse_state6;\+ case 7: goto parse_state7; \+ case 8: goto parse_state8;\+ case 9: goto parse_state9;\+ case 10: goto parse_state10;\+ case 11: goto parse_state11;\+ }++/* WARNING: local variables are not preserved across PARSE_CALL(). So+ they must be explicitly saved and restored */+#define PARSE_CALL(s, cur_state, func, param) return (cur_state | (PARSE_FUNC_ ## func << 8) | ((param) << 16)); parse_state ## cur_state : ;++/* preserve var1, ... across the call */+#define PARSE_CALL_SAVE1(s, cur_state, func, param, var1) \+ PARSE_PUSH_INT(s, var1); \+ PARSE_CALL(s, cur_state, func, param); \+ PARSE_POP_INT(s, var1);++#define PARSE_CALL_SAVE2(s, cur_state, func, param, var1, var2) \+ PARSE_PUSH_INT(s, var1); \+ PARSE_PUSH_INT(s, var2); \+ PARSE_CALL(s, cur_state, func, param); \+ PARSE_POP_INT(s, var2); \+ PARSE_POP_INT(s, var1);++#define PARSE_CALL_SAVE3(s, cur_state, func, param, var1, var2, var3) \+ PARSE_PUSH_INT(s, var1); \+ PARSE_PUSH_INT(s, var2); \+ PARSE_PUSH_INT(s, var3); \+ PARSE_CALL(s, cur_state, func, param); \+ PARSE_POP_INT(s, var3); \+ PARSE_POP_INT(s, var2); \+ PARSE_POP_INT(s, var1);++#define PARSE_CALL_SAVE4(s, cur_state, func, param, var1, var2, var3, var4) \+ PARSE_PUSH_INT(s, var1); \+ PARSE_PUSH_INT(s, var2); \+ PARSE_PUSH_INT(s, var3); \+ PARSE_PUSH_INT(s, var4); \+ PARSE_CALL(s, cur_state, func, param); \+ PARSE_POP_INT(s, var4); \+ PARSE_POP_INT(s, var3); \+ PARSE_POP_INT(s, var2); \+ PARSE_POP_INT(s, var1);++#define PARSE_CALL_SAVE5(s, cur_state, func, param, var1, var2, var3, var4, var5) \+ PARSE_PUSH_INT(s, var1); \+ PARSE_PUSH_INT(s, var2); \+ PARSE_PUSH_INT(s, var3); \+ PARSE_PUSH_INT(s, var4); \+ PARSE_PUSH_INT(s, var5); \+ PARSE_CALL(s, cur_state, func, param); \+ PARSE_POP_INT(s, var5); \+ PARSE_POP_INT(s, var4); \+ PARSE_POP_INT(s, var3); \+ PARSE_POP_INT(s, var2); \+ PARSE_POP_INT(s, var1);++#define PARSE_CALL_SAVE6(s, cur_state, func, param, var1, var2, var3, var4, var5, var6) \+ PARSE_PUSH_INT(s, var1); \+ PARSE_PUSH_INT(s, var2); \+ PARSE_PUSH_INT(s, var3); \+ PARSE_PUSH_INT(s, var4); \+ PARSE_PUSH_INT(s, var5); \+ PARSE_PUSH_INT(s, var6); \+ PARSE_CALL(s, cur_state, func, param); \+ PARSE_POP_INT(s, var6); \+ PARSE_POP_INT(s, var5); \+ PARSE_POP_INT(s, var4); \+ PARSE_POP_INT(s, var3); \+ PARSE_POP_INT(s, var2); \+ PARSE_POP_INT(s, var1);++static JSParseFunc *parse_func_table[];++static void js_parse_call(JSParseState *s, ParseExprFuncEnum func_idx,+ int param)+{+ JSContext *ctx = s->ctx;+ int ret, state;+ JSValue *stack_top;++ stack_top = ctx->sp;+ state = PARSE_STATE_INIT;+ for(;;) {+ ret = parse_func_table[func_idx](s, state, param);+ state = ret & 0xff;+ if (state == PARSE_STATE_RET) {+ /* the function terminated: go back to the calling+ function if any */+ if (ctx->sp == stack_top)+ break;+ PARSE_POP_INT(s, ret);+ state = ret & 0xff;+ func_idx = (ret >> 8) & 0xff;+ param = -1; /* the parameter is not saved */+ } else {+ /* push the call position and call another function */+ PARSE_PUSH_INT(s, state | (func_idx << 8));+ state = PARSE_STATE_INIT;+ func_idx = (ret >> 8) & 0xff;+ param = (ret >> 16);+ }+ }+}++static BOOL may_drop_result(JSParseState *s, int parse_flags)+{+ return ((parse_flags & PF_DROP) &&+ (s->token.val == ';' || s->token.val == ')' ||+ s->token.val == ','));+}++static void js_emit_push_number(JSParseState *s, double d)+{+ JSValue val;+ + val = JS_NewFloat64(s->ctx, d);+ if (JS_IsException(val))+ js_parse_error_mem(s);+ if (JS_IsInt(val)) {+ emit_push_short_int(s, JS_VALUE_GET_INT(val));+ } else {+ js_emit_push_const(s, val);+ }+}++static int js_parse_postfix_expr(JSParseState *s, int state, int parse_flags)+{+ BOOL is_new = FALSE;++ PARSE_START7();+ switch(s->token.val) {+ case TOK_NUMBER:+ js_emit_push_number(s, s->token.u.d);+ next_token(s);+ break;+ case TOK_STRING:+ {+ js_emit_push_const(s, s->token.value);+ next_token(s);+ }+ break;+ case TOK_REGEXP:+ {+ uint32_t saved_buf_pos, saved_buf_len;+ uint32_t saved_byte_code_len;+ JSValue byte_code;+ JSFunctionBytecode *b;++ js_emit_push_const(s, s->token.value); /* regexp source */++ saved_buf_pos = s->buf_pos;+ saved_buf_len = s->buf_len;+ /* save the current bytecode back to the function */+ b = JS_VALUE_TO_PTR(s->cur_func);+ b->byte_code = s->byte_code;+ saved_byte_code_len = s->byte_code_len;+ + /* modify the parser to parse the regexp. This way we+ avoid instantiating a new JSParseState */+ /* XXX: find a better way as it relies on the regexp+ parser to correctly handle the end of regexp */+ s->buf_pos = s->token.source_pos + 1;+ s->buf_len = s->token.u.regexp.re_end_pos;+ byte_code = js_parse_regexp(s, s->token.u.regexp.re_flags);++ s->buf_pos = saved_buf_pos;+ s->buf_len = saved_buf_len;+ b = JS_VALUE_TO_PTR(s->cur_func);+ s->byte_code = b->byte_code;+ s->byte_code_len = saved_byte_code_len;+ + js_emit_push_const(s, byte_code);+ emit_op(s, OP_regexp);+ next_token(s);+ }+ break;+ case '(':+ next_token(s);+ PARSE_CALL_SAVE1(s, 0, js_parse_expr_comma, 0, parse_flags);+ js_parse_expect(s, ')');+ break;+ case TOK_FUNCTION:+ js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, JS_NULL);+ break;+ case TOK_NULL:+ emit_op(s, OP_null);+ next_token(s);+ break;+ case TOK_THIS:+ emit_op(s, OP_push_this);+ next_token(s);+ break;+ case TOK_FALSE:+ case TOK_TRUE:+ emit_op(s, OP_push_false + (s->token.val == TOK_TRUE));+ next_token(s);+ break;+ case TOK_IDENT:+ {+ JSFunctionBytecode *b;+ JSValue name;+ int var_idx, arg_count, opcode;++ b = JS_VALUE_TO_PTR(s->cur_func);+ arg_count = b->arg_count;+ + name = s->token.value;+ + var_idx = find_var(s, name);+ if (var_idx >= 0) {+ if (var_idx < arg_count) {+ opcode = OP_get_arg;+ } else {+ opcode = OP_get_loc;+ var_idx -= arg_count;+ }+ } else {+ var_idx = find_ext_var(s, name);+ if (var_idx < 0) {+ var_idx = add_ext_var(s, name, (JS_VARREF_KIND_GLOBAL << 16) | 0);+ }+ opcode = OP_get_var_ref;+ }+ emit_var(s, opcode, var_idx, s->token.source_pos);+ next_token(s);+ }+ break;+ case '{':+ {+ JSValue name;+ int prop_idx, prop_type, count_pos;+ BOOL has_proto;+ + next_token(s);+ emit_op(s, OP_object);+ count_pos = s->byte_code_len;+ emit_u16(s, 0);++ has_proto = FALSE;+ while (s->token.val != '}') {+ prop_type = js_parse_property_name(s, &name);+ if (prop_type == PARSE_PROP_FIELD &&+ name == js_get_atom(s->ctx, JS_ATOM___proto__)) {+ if (has_proto)+ js_parse_error(s, "duplicate __proto__ property name");+ has_proto = TRUE;+ prop_idx = -1;+ } else {+ uint8_t *byte_code;+ int count;+ prop_idx = cpool_add(s, name);+ /* increment the count */+ byte_code = get_byte_code(s);+ count = get_u16(byte_code + count_pos);+ put_u16(byte_code + count_pos, min_int(count + 1, 0xffff));+ }+ if (prop_type == PARSE_PROP_FIELD) {+ js_parse_expect(s, ':');+ PARSE_CALL_SAVE4(s, 1, js_parse_assign_expr, 0, prop_idx, parse_flags, has_proto, count_pos);+ if (prop_idx >= 0) {+ emit_op(s, OP_define_field);+ emit_u16(s, prop_idx);+ } else {+ emit_op(s, OP_set_proto);+ }+ } else {+ /* getter/setter/method */+ js_parse_function_decl(s, JS_PARSE_FUNC_METHOD, name);+ if (prop_type == PARSE_PROP_METHOD)+ emit_op(s, OP_define_field);+ else if (prop_type == PARSE_PROP_GET)+ emit_op(s, OP_define_getter);+ else+ emit_op(s, OP_define_setter);+ emit_u16(s, prop_idx);+ }+ if (s->token.val != ',')+ break;+ next_token(s);+ }+ js_parse_expect(s, '}');+ }+ break;+ case '[':+ {+ uint32_t idx;+ + next_token(s);+ /* small regular arrays are created on the stack */+ idx = 0;+ while (s->token.val != ']' && idx < 32) {+ /* SPEC: we don't accept empty elements */+ PARSE_CALL_SAVE2(s, 2, js_parse_assign_expr, 0, idx, parse_flags);+ idx++;+ /* accept trailing comma */+ if (s->token.val == ',') {+ next_token(s);+ } else if (s->token.val != ']') {+ goto done;+ }+ }+ + emit_op_param(s, OP_array_from, idx, s->pc2line_source_pos);+ + while (s->token.val != ']') {+ if (idx >= JS_SHORTINT_MAX)+ js_parse_error(s, "too many elements");+ emit_op(s, OP_dup);+ emit_push_short_int(s, idx);+ PARSE_CALL_SAVE2(s, 3, js_parse_assign_expr, 0, idx, parse_flags);+ emit_op(s, OP_put_array_el);+ idx++;+ /* accept trailing comma */+ if (s->token.val == ',') {+ next_token(s);+ }+ }+ done:+ js_parse_expect(s, ']');+ }+ break;+ case TOK_NEW:+ next_token(s);+ if (s->token.val == '.') {+ next_token(s);+ if (s->token.val != TOK_IDENT ||+ s->token.value != js_get_atom(s->ctx, JS_ATOM_target)) {+ js_parse_error(s, "expecting target");+ }+ next_token(s);+ emit_op(s, OP_new_target);+ } else {+ PARSE_CALL_SAVE1(s, 4, js_parse_postfix_expr, 0, parse_flags);+ if (s->token.val != '(') {+ /* new operator on an object */+ emit_op_param(s, OP_call_constructor, 0, s->token.source_pos);+ } else {+ is_new = TRUE;+ break;+ }+ }+ break;+ default:+ js_parse_error(s, "unexpected character in expression");+ }++ for(;;) {+ if (s->token.val == '(' && (parse_flags & PF_ACCEPT_LPAREN)) {+ int opcode, arg_count;+ uint8_t *byte_code;+ JSSourcePos op_source_pos;+ + /* function call */+ op_source_pos = s->token.source_pos;+ next_token(s);++ if (!is_new) {+ opcode = get_prev_opcode(s);+ byte_code = get_byte_code(s);+ switch(opcode) {+ case OP_get_field:+ byte_code[s->last_opcode_pos] = OP_get_field2;+ break;+ case OP_get_length:+ byte_code[s->last_opcode_pos] = OP_get_length2;+ break;+ case OP_get_array_el:+ byte_code[s->last_opcode_pos] = OP_get_array_el2;+ break;+ case OP_get_var_ref:+ {+ int var_idx = get_u16(byte_code + s->last_opcode_pos + 1);+ if (get_ext_var_name(s, var_idx) == js_get_atom(s->ctx, JS_ATOM_eval)) {+ js_parse_error(s, "direct eval is not supported. Use (1,eval) instead for indirect eval");+ }+ }+ /* fall thru */+ default:+ opcode = OP_invalid;+ break;+ }+ } else {+ opcode = OP_invalid;+ }+ + arg_count = 0;+ if (s->token.val != ')') {+ for(;;) {+ if (arg_count >= JS_MAX_ARGC)+ js_parse_error(s, "too many call arguments");+ arg_count++;+ PARSE_CALL_SAVE5(s, 5, js_parse_assign_expr, 0,+ parse_flags, arg_count, opcode, is_new, op_source_pos);+ if (s->token.val == ')')+ break;+ js_parse_expect(s, ',');+ }+ }+ next_token(s);+ if (opcode == OP_get_field ||+ opcode == OP_get_length ||+ opcode == OP_get_array_el) {+ emit_op_param(s, OP_call_method, arg_count, op_source_pos);+ } else {+ if (is_new) {+ emit_op_param(s, OP_call_constructor, arg_count, op_source_pos);+ } else {+ emit_op_param(s, OP_call, arg_count, op_source_pos);+ }+ }+ is_new = FALSE;+ } else if (s->token.val == '.') {+ JSSourcePos op_source_pos;+ int prop_idx;+ + op_source_pos = s->token.source_pos;+ next_token(s);+ if (!(s->token.val == TOK_IDENT || s->token.val >= TOK_FIRST_KEYWORD)) {+ js_parse_error(s, "expecting field name");+ }+ /* we ensure that no numeric property is used with+ OP_get_field to enable some optimizations. The only+ possible identifiers are NaN and Infinity */+ if (s->token.value == js_get_atom(s->ctx, JS_ATOM_NaN) ||+ s->token.value == js_get_atom(s->ctx, JS_ATOM_Infinity)) {+ js_emit_push_const(s, s->token.value);+ emit_op_pos(s, OP_get_array_el, op_source_pos);+ } else if (s->token.value == js_get_atom(s->ctx, JS_ATOM_length)) {+ emit_op_pos(s, OP_get_length, op_source_pos);+ } else {+ prop_idx = cpool_add(s, s->token.value);+ emit_op_pos(s, OP_get_field, op_source_pos);+ emit_u16(s, prop_idx);+ }+ next_token(s);+ } else if (s->token.val == '[') {+ JSSourcePos op_source_pos;+ op_source_pos = s->token.source_pos;+ next_token(s);+ PARSE_CALL_SAVE3(s, 6, js_parse_expr_comma, 0,+ parse_flags, is_new, op_source_pos);+ js_parse_expect(s, ']');+ emit_op_pos(s, OP_get_array_el, op_source_pos);+ } else if (!s->got_lf && (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {+ int opcode, op, var_idx;+ JSSourcePos op_source_pos, source_pos;+ + op = s->token.val;+ op_source_pos = s->token.source_pos;+ next_token(s);+ get_lvalue(s, &opcode, &var_idx, &source_pos, TRUE);+ if (may_drop_result(s, parse_flags)) {+ s->dropped_result = TRUE;+ emit_op_pos(s, OP_dec + op - TOK_DEC, op_source_pos);+ put_lvalue(s, opcode, var_idx, source_pos, PUT_LVALUE_NOKEEP_TOP);+ } else {+ emit_op_pos(s, OP_post_dec + op - TOK_DEC, op_source_pos);+ put_lvalue(s, opcode, var_idx, source_pos, PUT_LVALUE_KEEP_SECOND);+ }+ } else {+ break;+ }+ }+ return PARSE_STATE_RET;+}++static void js_emit_delete(JSParseState *s)+{+ int opcode;+ + opcode = get_prev_opcode(s);+ switch(opcode) {+ case OP_get_field:+ {+ JSByteArray *byte_code;+ int prop_idx;+ byte_code = JS_VALUE_TO_PTR(s->byte_code);+ prop_idx = get_u16(byte_code->buf + s->last_opcode_pos + 1);+ remove_last_op(s);+ emit_op(s, OP_push_const);+ emit_u16(s, prop_idx);+ }+ break;+ case OP_get_length:+ remove_last_op(s);+ js_emit_push_const(s, js_get_atom(s->ctx, JS_ATOM_length));+ break;+ case OP_get_array_el:+ remove_last_op(s);+ break;+ default:+ js_parse_error(s, "invalid lvalue for delete");+ }+ emit_op(s, OP_delete);+}++static int js_parse_unary(JSParseState *s, int state, int parse_flags)+{+ PARSE_START7();++ switch(s->token.val) {+ case '+':+ case '-':+ case '!':+ case '~':+ {+ int op;+ JSSourcePos op_source_pos;+ + op = s->token.val;+ op_source_pos = s->token.source_pos;+ next_token(s);+ + /* XXX: could handle more cases */+ if (s->token.val == TOK_NUMBER && (op == '-' || op == '+')) {+ double d = s->token.u.d;+ if (op == '-')+ d = -d;+ js_emit_push_number(s, d);+ next_token(s);+ } else {+ PARSE_CALL_SAVE2(s, 0, js_parse_unary, 0, op, op_source_pos);+ switch(op) {+ case '-':+ emit_op_pos(s, OP_neg, op_source_pos);+ break;+ case '+':+ emit_op_pos(s, OP_plus, op_source_pos);+ break;+ case '!':+ emit_op_pos(s, OP_lnot, op_source_pos);+ break;+ case '~':+ emit_op_pos(s, OP_not, op_source_pos);+ break;+ default:+ abort();+ }+ }+ }+ break;+ case TOK_VOID:+ next_token(s);+ PARSE_CALL(s, 1, js_parse_unary, 0);+ emit_op(s, OP_drop);+ emit_op(s, OP_undefined);+ break;+ case TOK_DEC:+ case TOK_INC:+ {+ int opcode, op, var_idx;+ PutLValueEnum special;+ JSSourcePos op_source_pos, source_pos;+ + op = s->token.val;+ op_source_pos = s->token.source_pos;+ next_token(s);+ PARSE_CALL_SAVE3(s, 2, js_parse_unary, 0, op, parse_flags, op_source_pos);+ get_lvalue(s, &opcode, &var_idx, &source_pos, TRUE);+ emit_op_pos(s, OP_dec + op - TOK_DEC, op_source_pos);++ if (may_drop_result(s, parse_flags)) {+ special = PUT_LVALUE_NOKEEP_TOP;+ s->dropped_result = TRUE;+ } else {+ special = PUT_LVALUE_KEEP_TOP;+ }+ put_lvalue(s, opcode, var_idx, source_pos, special);+ }+ break;+ case TOK_TYPEOF:+ {+ next_token(s);+ PARSE_CALL(s, 3, js_parse_unary, 0);+ /* access to undefined variable should not return an+ exception, so we patch the get_var */+ if (get_prev_opcode(s) == OP_get_var_ref) {+ uint8_t *byte_code = get_byte_code(s);+ byte_code[s->last_opcode_pos] = OP_get_var_ref_nocheck;+ }+ emit_op(s, OP_typeof);+ }+ break;+ case TOK_DELETE:+ next_token(s);+ PARSE_CALL(s, 4, js_parse_unary, 0);+ js_emit_delete(s);+ break;+ default:+ PARSE_CALL(s, 5, js_parse_postfix_expr, parse_flags | PF_ACCEPT_LPAREN);+ /* XXX: we do not follow the ES7 grammar in order to have a+ * more natural expression */+ if (s->token.val == TOK_POW) {+ JSSourcePos op_source_pos;+ op_source_pos = s->token.source_pos;+ next_token(s);+ PARSE_CALL_SAVE1(s, 6, js_parse_unary, 0, op_source_pos);+ emit_op_pos(s, OP_pow, op_source_pos);+ }+ break;+ }+ return PARSE_STATE_RET;+}++static int js_parse_expr_binary(JSParseState *s, int state, int parse_flags)+{+ int op, opcode, level;+ JSSourcePos op_source_pos;+ + PARSE_START3();+ level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT;+ if (level == 0) {+ PARSE_CALL(s, 0, js_parse_unary, parse_flags);+ return PARSE_STATE_RET;+ }+ PARSE_CALL_SAVE1(s, 1, js_parse_expr_binary, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags);+ parse_flags &= ~PF_DROP;+ for(;;) {+ op = s->token.val;+ op_source_pos = s->token.source_pos;+ level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT;+ switch(level) {+ case 1:+ switch(op) {+ case '*':+ opcode = OP_mul;+ break;+ case '/':+ opcode = OP_div;+ break;+ case '%':+ opcode = OP_mod;+ break;+ default:+ return PARSE_STATE_RET;+ }+ break;+ case 2:+ switch(op) {+ case '+':+ opcode = OP_add;+ break;+ case '-':+ opcode = OP_sub;+ break;+ default:+ return PARSE_STATE_RET;+ }+ 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 PARSE_STATE_RET;+ }+ 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 (!(parse_flags & PF_NO_IN)) {+ opcode = OP_in;+ } else {+ return PARSE_STATE_RET;+ }+ break;+ default:+ return PARSE_STATE_RET;+ }+ 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 PARSE_STATE_RET;+ }+ break;+ case 6:+ switch(op) {+ case '&':+ opcode = OP_and;+ break;+ default:+ return PARSE_STATE_RET;+ }+ break;+ case 7:+ switch(op) {+ case '^':+ opcode = OP_xor;+ break;+ default:+ return PARSE_STATE_RET;+ }+ break;+ case 8:+ switch(op) {+ case '|':+ opcode = OP_or;+ break;+ default:+ return PARSE_STATE_RET;+ }+ break;+ default:+ abort();+ }+ next_token(s);+ PARSE_CALL_SAVE3(s, 2, js_parse_expr_binary, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags, opcode, op_source_pos);+ emit_op_pos(s, opcode, op_source_pos);+ }+ return PARSE_STATE_RET;+}++static int js_parse_logical_and_or(JSParseState *s, int state, int parse_flags)+{+ JSValue label1;+ int level, op;++ PARSE_START3();+ level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT;+ if (level == 0) {+ PARSE_CALL(s, 0, js_parse_expr_binary, (parse_flags & ~PF_LEVEL_MASK) | (8 << PF_LEVEL_SHIFT));+ return PARSE_STATE_RET;+ }++ PARSE_CALL_SAVE1(s, 1, js_parse_logical_and_or, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags);++ level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT;+ if (level == 1)+ op = TOK_LAND;+ else+ op = TOK_LOR;+ parse_flags &= ~PF_DROP;+ if (s->token.val == op) {+ label1 = new_label(s);++ for(;;) {+ next_token(s);+ emit_op(s, OP_dup);+ emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, &label1);+ emit_op(s, OP_drop);+ + PARSE_PUSH_VAL(s, label1);+ PARSE_CALL_SAVE1(s, 2, js_parse_logical_and_or, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags);+ PARSE_POP_VAL(s, label1);++ level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT;+ if (level == 1)+ op = TOK_LAND;+ else+ op = TOK_LOR;+ + if (s->token.val != op)+ break;+ }++ emit_label(s, &label1);+ }+ return PARSE_STATE_RET;+}++static int js_parse_cond_expr(JSParseState *s, int state, int parse_flags)+{+ JSValue label1, label2;+ + PARSE_START3();++ PARSE_CALL_SAVE1(s, 2, js_parse_logical_and_or, parse_flags | (2 << PF_LEVEL_SHIFT), parse_flags);+ + parse_flags &= ~PF_DROP;+ if (s->token.val == '?') {+ next_token(s);+ label1 = new_label(s);+ emit_goto(s, OP_if_false, &label1);+ + PARSE_PUSH_VAL(s, label1);+ PARSE_CALL_SAVE1(s, 0, js_parse_assign_expr, parse_flags,+ parse_flags);+ PARSE_POP_VAL(s, label1);++ label2 = new_label(s);+ emit_goto(s, OP_goto, &label2);++ js_parse_expect(s, ':');+ + emit_label(s, &label1);+ + PARSE_PUSH_VAL(s, label2);+ PARSE_CALL_SAVE1(s, 1, js_parse_assign_expr, parse_flags,+ parse_flags);+ PARSE_POP_VAL(s, label2);++ emit_label(s, &label2);+ }+ return PARSE_STATE_RET;+}++static int js_parse_assign_expr(JSParseState *s, int state, int parse_flags)+{+ int opcode, op, var_idx;+ PutLValueEnum special;+ JSSourcePos op_source_pos, source_pos;+ + PARSE_START2();++ PARSE_CALL_SAVE1(s, 1, js_parse_cond_expr, parse_flags, parse_flags);+ + op = s->token.val;+ if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_OR_ASSIGN)) {+ op_source_pos = s->token.source_pos;+ next_token(s);+ get_lvalue(s, &opcode, &var_idx, &source_pos, (op != '='));++ PARSE_CALL_SAVE6(s, 0, js_parse_assign_expr, parse_flags & ~PF_DROP,+ op, opcode, var_idx, parse_flags,+ op_source_pos, source_pos);++ if (op != '=') {+ 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,+ OP_pow,+ };+ emit_op_pos(s, assign_opcodes[op - TOK_MUL_ASSIGN], op_source_pos);+ }++ if (may_drop_result(s, parse_flags)) {+ special = PUT_LVALUE_NOKEEP_TOP;+ s->dropped_result = TRUE;+ } else {+ special = PUT_LVALUE_KEEP_TOP;+ }+ put_lvalue(s, opcode, var_idx, source_pos, special);+ }+ return PARSE_STATE_RET;+}++static int js_parse_expr_comma(JSParseState *s, int state, int parse_flags)+{+ BOOL comma = FALSE;++ PARSE_START1();++ for(;;) {+ s->dropped_result = FALSE;+ PARSE_CALL_SAVE2(s, 0, js_parse_assign_expr, parse_flags,+ comma, parse_flags);+ if (comma) {+ /* prevent get_lvalue from using the last expression as an+ lvalue. */+ s->last_opcode_pos = -1;+ }+ if (s->token.val != ',')+ break;+ comma = TRUE;+ if (!s->dropped_result)+ emit_op(s, OP_drop);+ next_token(s);+ }+ if ((parse_flags & PF_DROP) && !s->dropped_result) {+ emit_op(s, OP_drop);+ }+ return PARSE_STATE_RET;+}++static void js_parse_assign_expr2(JSParseState *s, int parse_flags)+{+ js_parse_call(s, PARSE_FUNC_js_parse_assign_expr, parse_flags);+}++static void js_parse_expr2(JSParseState *s, int parse_flags)+{+ js_parse_call(s, PARSE_FUNC_js_parse_expr_comma, parse_flags);+}++static void js_parse_expr(JSParseState *s)+{+ js_parse_expr2(s, 0);+}++static void js_parse_expr_paren(JSParseState *s)+{+ js_parse_expect(s, '(');+ js_parse_expr(s);+ js_parse_expect(s, ')');+}++static BlockEnv *push_break_entry(JSParseState *s, JSValue label_name,+ JSValue label_break, JSValue label_cont,+ int drop_count)+{+ JSContext *ctx = s->ctx;+ JSGCRef label_name_ref;+ int ret, block_env_len;+ BlockEnv *be;+ + block_env_len = sizeof(BlockEnv) / sizeof(JSValue);+ JS_PUSH_VALUE(ctx, label_name);+ ret = JS_StackCheck(ctx, block_env_len);+ JS_POP_VALUE(ctx, label_name);+ if (ret)+ js_parse_error_stack_overflow(s);+ ctx->sp -= block_env_len;+ be = (BlockEnv *)ctx->sp;+ be->prev = s->top_break;+ s->top_break = SP_TO_VALUE(ctx, be);+ be->label_name = label_name;+ be->label_break = label_break;+ be->label_cont = label_cont;+ be->label_finally = LABEL_NONE;+ be->drop_count = JS_NewShortInt(drop_count);+ return be;+}++static void pop_break_entry(JSParseState *s)+{+ JSContext *ctx = s->ctx;+ BlockEnv *be;+ + be = VALUE_TO_SP(ctx, s->top_break);+ s->top_break = be->prev;+ ctx->sp += sizeof(BlockEnv) / sizeof(JSValue);+ ctx->stack_bottom = ctx->sp;+}++static void emit_return(JSParseState *s, BOOL hasval, JSSourcePos source_pos)+{+ JSValue top_val;+ BlockEnv *top;+ int i, drop_count;++ drop_count = 0;+ top_val = s->top_break;+ while (!JS_IsNull(top_val)) {+ top = VALUE_TO_SP(s->ctx, top_val);+ /* no need to drop if no "finally" */+ drop_count += JS_VALUE_GET_INT(top->drop_count); ++ if (!label_is_none(top->label_finally)) {+ if (!hasval) {+ emit_op(s, OP_undefined);+ hasval = TRUE;+ }+ for(i = 0; i < drop_count; i++)+ emit_op(s, OP_nip); /* must keep the stack stop */+ drop_count = 0;+ /* execute the "finally" block */+ emit_goto(s, OP_gosub, &top->label_finally);+ }+ top_val = top->prev;+ }+ emit_op_pos(s, hasval ? OP_return : OP_return_undef, source_pos);+}++static void emit_break(JSParseState *s, JSValue label_name, int is_cont)+{+ JSValue top_val;+ BlockEnv *top;+ int i;+ JSValue *plabel;+ JSGCRef label_name_ref;+ BOOL is_labelled_stmt;+ + top_val = s->top_break;+ while (!JS_IsNull(top_val)) {+ top = VALUE_TO_SP(s->ctx, top_val);+ is_labelled_stmt = (top->label_cont == LABEL_NONE &&+ JS_VALUE_GET_INT(top->drop_count) == 0);+ if ((label_name == JS_NULL && !is_labelled_stmt) ||+ top->label_name == label_name) {+ if (is_cont)+ plabel = &top->label_cont;+ else+ plabel = &top->label_break;+ if (!label_is_none(*plabel)) {+ emit_goto(s, OP_goto, plabel);+ return;+ }+ }+ JS_PUSH_VALUE(s->ctx, label_name);+ for(i = 0; i < JS_VALUE_GET_INT(top->drop_count); i++)+ emit_op(s, OP_drop);+ if (!label_is_none(top->label_finally)) {+ /* 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);+ }+ JS_POP_VALUE(s->ctx, label_name);+ top_val = top->prev;+ }+ if (label_name == JS_NULL) {+ if (is_cont)+ js_parse_error(s, "continue must be inside loop");+ else+ js_parse_error(s, "break must be inside loop or switch");+ } else {+ js_parse_error(s, "break/continue label not found");+ }+}++static int define_var(JSParseState *s, JSVarRefKindEnum *pvar_kind, JSValue name)+{+ JSVarRefKindEnum var_kind;+ int var_idx;++ if (s->is_eval) {+ var_idx = find_ext_var(s, name);+ if (var_idx < 0) {+ var_idx = add_ext_var(s, name, (JS_VARREF_KIND_GLOBAL << 16) | 1);+ } else {+ JSFunctionBytecode *b = JS_VALUE_TO_PTR(s->cur_func);+ JSValueArray *arr = JS_VALUE_TO_PTR(b->ext_vars);+ arr->arr[2 * var_idx + 1] = JS_NewShortInt((JS_VARREF_KIND_GLOBAL << 16) | 1);+ }+ var_kind = JS_VARREF_KIND_VAR_REF;+ } else {+ JSFunctionBytecode *b;+ int arg_count;++ b = JS_VALUE_TO_PTR(s->cur_func);+ arg_count = b->arg_count;+ + var_idx = find_var(s, name);+ if (var_idx >= 0) {+ if (var_idx < arg_count) {+ var_kind = JS_VARREF_KIND_ARG;+ } else {+ var_kind = JS_VARREF_KIND_VAR;+ var_idx -= arg_count;+ }+ } else {+ var_idx = add_var(s, name);+ var_kind = JS_VARREF_KIND_VAR;+ var_idx -= arg_count;+ }+ }+ *pvar_kind = var_kind;+ return var_idx;+}++static void put_var(JSParseState *s, JSVarRefKindEnum var_kind, int var_idx, JSSourcePos source_pos)+{+ int opcode;+ if (var_kind == JS_VARREF_KIND_ARG)+ opcode = OP_put_arg;+ else if (var_kind == JS_VARREF_KIND_VAR)+ opcode = OP_put_loc;+ else+ opcode = OP_put_var_ref_nocheck;+ emit_var(s, opcode, var_idx, source_pos);+}++static void js_parse_var(JSParseState *s, BOOL in_accepted)+{+ JSVarRefKindEnum var_kind;+ int var_idx;+ JSSourcePos ident_source_pos;+ + for(;;) {+ ident_source_pos = s->token.source_pos;+ if (s->token.val != TOK_IDENT)+ js_parse_error(s, "variable name expected");+ if (s->token.value == js_get_atom(s->ctx, JS_ATOM_arguments))+ js_parse_error(s, "invalid variable name");+ var_idx = define_var(s, &var_kind, s->token.value);+ next_token(s);+ if (s->token.val == '=') {+ next_token(s);+ js_parse_assign_expr2(s, in_accepted ? 0 : PF_NO_IN);+ put_var(s, var_kind, var_idx, ident_source_pos);+ }+ if (s->token.val != ',')+ break;+ next_token(s);+ }+}++static void set_eval_ret_undefined(JSParseState *s)+{+ if (s->eval_ret_idx >= 0) {+ emit_op(s, OP_undefined);+ emit_var(s, OP_put_loc, s->eval_ret_idx, s->pc2line_source_pos);+ }+}++static int js_parse_block(JSParseState *s, int state, int dummy_param)+{+ PARSE_START1();+ js_parse_expect(s, '{');+ if (s->token.val != '}') {+ for(;;) {+ PARSE_CALL(s, 0, js_parse_statement, 0);+ if (s->token.val == '}')+ break;+ }+ }+ next_token(s);+ return PARSE_STATE_RET;+}++/* The statement parser assumes that the stack contains the result of+ the last statement. Note: if not in eval code, the return value of+ a statement does not matter */+static int js_parse_statement(JSParseState *s, int state, int dummy_param)+{+ JSValue label_name;+ JSGCRef label_name_ref;+ + PARSE_START12();+ + /* specific label handling */+ if (is_label(s)) {+ JSValue top_val;+ BlockEnv *top;+ + label_name = s->token.value;+ JS_PUSH_VALUE(s->ctx, label_name);+ next_token(s);+ js_parse_expect(s, ':');+ JS_POP_VALUE(s->ctx, label_name);++ for(top_val = s->top_break; !JS_IsNull(top_val); top_val = top->prev) {+ top = VALUE_TO_SP(s->ctx, top_val);+ if (top->label_name == label_name)+ js_parse_error(s, "duplicate label name");+ }++ if (s->token.val != TOK_FOR &&+ s->token.val != TOK_DO &&+ s->token.val != TOK_WHILE) {+ /* labelled regular statement */+ BlockEnv *be;+ push_break_entry(s, label_name, new_label(s), LABEL_NONE, 0);++ PARSE_CALL(s, 11, js_parse_statement, 0);++ be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_label(s, &be->label_break);+ pop_break_entry(s);+ goto done;+ }+ } else {+ label_name = JS_NULL;+ }+ + switch(s->token.val) {+ case '{':+ PARSE_CALL(s, 0, js_parse_block, 0);+ break;+ case TOK_RETURN:+ {+ BOOL has_val;+ JSSourcePos op_source_pos;+ if (s->is_eval)+ js_parse_error(s, "return not in a function");+ op_source_pos = s->token.source_pos;+ next_token(s);+ if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {+ js_parse_expr(s);+ has_val = TRUE;+ } else {+ has_val = FALSE;+ }+ emit_return(s, has_val, op_source_pos);+ js_parse_expect_semi(s);+ }+ break;+ case TOK_THROW:+ {+ JSSourcePos op_source_pos;+ op_source_pos = s->token.source_pos;+ next_token(s);+ if (s->got_lf)+ js_parse_error(s, "line terminator not allowed after throw");+ js_parse_expr(s);+ emit_op_pos(s, OP_throw, op_source_pos);+ js_parse_expect_semi(s);+ }+ break;+ case TOK_VAR:+ next_token(s);+ js_parse_var(s, TRUE);+ js_parse_expect_semi(s);+ break;+ case TOK_IF:+ {+ JSValue label1, label2;+ next_token(s);+ set_eval_ret_undefined(s);+ js_parse_expr_paren(s);+ label1 = new_label(s);+ emit_goto(s, OP_if_false, &label1);++ PARSE_PUSH_VAL(s, label1);+ PARSE_CALL(s, 1, js_parse_statement, 0);+ PARSE_POP_VAL(s, label1);+ + if (s->token.val == TOK_ELSE) {+ next_token(s);+ + label2 = new_label(s);+ emit_goto(s, OP_goto, &label2);++ emit_label(s, &label1);+ + PARSE_PUSH_VAL(s, label2);+ PARSE_CALL(s, 2, js_parse_statement, 0);+ PARSE_POP_VAL(s, label2);+ + label1 = label2;+ }+ emit_label(s, &label1);+ }+ break;+ case TOK_WHILE:+ {+ BlockEnv *be;+ + be = push_break_entry(s, label_name, new_label(s), new_label(s), 0);+ next_token(s);++ set_eval_ret_undefined(s);++ emit_label(s, &be->label_cont);+ js_parse_expr_paren(s);+ emit_goto(s, OP_if_false, &be->label_break);+ + PARSE_CALL(s, 3, js_parse_statement, 0);++ be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_goto(s, OP_goto, &be->label_cont);++ emit_label(s, &be->label_break);++ pop_break_entry(s);+ }+ break;+ case TOK_DO:+ {+ JSValue label1;+ BlockEnv *be;++ be = push_break_entry(s, label_name, new_label(s), new_label(s), 0);+ + label1 = new_label(s);+ + next_token(s);+ set_eval_ret_undefined(s);++ emit_label(s, &label1);++ PARSE_PUSH_VAL(s, label1);+ PARSE_CALL(s, 4, js_parse_statement, 0);+ PARSE_POP_VAL(s, label1);++ be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_label(s, &be->label_cont);+ js_parse_expect(s, TOK_WHILE);+ js_parse_expr_paren(s);+ /* Insert semicolon if missing */+ if (s->token.val == ';') {+ next_token(s);+ }+ emit_goto(s, OP_if_true, &label1);++ emit_label(s, &be->label_break);++ pop_break_entry(s);+ }+ break;+ case TOK_FOR:+ {+ int bits;+ BlockEnv *be;+ + be = push_break_entry(s, label_name, new_label(s), new_label(s), 0);+ + next_token(s);+ set_eval_ret_undefined(s);++ js_parse_expect1(s, '(');+ bits = js_parse_skip_parens_token(s);+ next_token(s);+ + if (!(bits & SKIP_HAS_SEMI)) {+ JSValue label_expr, label_body, label_next;+ int opcode, var_idx;+ + be->drop_count = JS_NewShortInt(1);+ + label_expr = new_label(s);+ label_body = new_label(s);+ label_next = new_label(s);+ + emit_goto(s, OP_goto, &label_expr);+ + emit_label(s, &label_next);+ + if (s->token.val == TOK_VAR) {+ JSVarRefKindEnum var_kind;+ next_token(s);+ var_idx = define_var(s, &var_kind, s->token.value);+ put_var(s, var_kind, var_idx, s->pc2line_source_pos);+ + next_token(s);+ } else {+ JSSourcePos source_pos;+ + /* XXX: js_parse_left_hand_side_expr */+ js_parse_assign_expr2(s, PF_NO_IN);+ + get_lvalue(s, &opcode, &var_idx, &source_pos, FALSE);+ put_lvalue(s, opcode, var_idx, source_pos,+ PUT_LVALUE_NOKEEP_BOTTOM);+ }+ + emit_goto(s, OP_goto, &label_body);+ + if (s->token.val == TOK_IN) {+ opcode = OP_for_in_start;+ } else if (s->token.val == TOK_IDENT &&+ s->token.value == js_get_atom(s->ctx, JS_ATOM_of)) {+ opcode = OP_for_of_start;+ } else {+ js_parse_error(s, "expected 'of' or 'in' in for control expression");+ }+ + next_token(s);+ + emit_label(s, &label_expr);+ js_parse_expr(s);+ emit_op(s, opcode);+ + emit_goto(s, OP_goto, &be->label_cont);+ + js_parse_expect(s, ')');+ + emit_label(s, &label_body);+ + PARSE_PUSH_VAL(s, label_next);+ PARSE_CALL(s, 5, js_parse_statement, 0);+ PARSE_POP_VAL(s, label_next);+ + be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_label(s, &be->label_cont);+ emit_op(s, OP_for_of_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, &be->label_break);+ emit_op(s, OP_drop);+ } else {+ JSValue label_test;+ JSParsePos expr3_pos;+ int tmp_val;+ + /* initial expression */+ if (s->token.val != ';') {+ if (s->token.val == TOK_VAR) {+ next_token(s);+ js_parse_var(s, FALSE);+ } else {+ js_parse_expr2(s, PF_NO_IN | PF_DROP);+ }+ }+ js_parse_expect(s, ';');+ + label_test = new_label(s);+ + /* test expression */+ emit_label(s, &label_test);+ if (s->token.val != ';') {+ js_parse_expr(s);+ emit_goto(s, OP_if_false, &be->label_break);+ }+ js_parse_expect(s, ';');++ if (s->token.val != ')') {+ /* skip the third expression if present */+ js_parse_get_pos(s, &expr3_pos);+ js_skip_expr(s);+ } else {+ expr3_pos.source_pos = -1;+ expr3_pos.got_lf = 0; /* avoid warning */+ expr3_pos.regexp_allowed = 0; /* avoid warning */+ }+ js_parse_expect(s, ')');++ PARSE_PUSH_VAL(s, label_test);+ PARSE_PUSH_INT(s, expr3_pos.got_lf | (expr3_pos.regexp_allowed << 1));+ PARSE_PUSH_INT(s, expr3_pos.source_pos);+ PARSE_CALL(s, 6, js_parse_statement, 0);+ PARSE_POP_INT(s, expr3_pos.source_pos);+ PARSE_POP_INT(s, tmp_val);+ expr3_pos.got_lf = tmp_val & 1;+ expr3_pos.regexp_allowed = tmp_val >> 1;+ PARSE_POP_VAL(s, label_test);+ + be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_label(s, &be->label_cont);++ /* parse the third expression, if present, after the+ statement */+ if (expr3_pos.source_pos != -1) {+ JSParsePos end_pos;+ js_parse_get_pos(s, &end_pos);+ js_parse_seek_token(s, &expr3_pos);+ js_parse_expr2(s, PF_DROP);+ js_parse_seek_token(s, &end_pos);+ }++ emit_goto(s, OP_goto, &label_test);+ + be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_label(s, &be->label_break);+ }+ pop_break_entry(s);+ }+ break;+ case TOK_BREAK:+ case TOK_CONTINUE:+ {+ int is_cont = (s->token.val == TOK_CONTINUE);+ JSValue label_name;+ + next_token(s);+ if (!s->got_lf && s->token.val == TOK_IDENT)+ label_name = s->token.value;+ else+ label_name = JS_NULL;+ emit_break(s, label_name, is_cont);+ if (label_name != JS_NULL) {+ next_token(s);+ }+ js_parse_expect_semi(s);+ }+ break;+ case TOK_SWITCH:+ {+ JSValue label_case;+ int default_label_pos;+ BlockEnv *be;++ be = push_break_entry(s, label_name, new_label(s), LABEL_NONE, 1);++ next_token(s);+ set_eval_ret_undefined(s);++ js_parse_expr_paren(s);++ js_parse_expect(s, '{');+ default_label_pos = -1;+ label_case = LABEL_NONE; /* label to the next case */+ while (s->token.val != '}') {+ if (s->token.val == TOK_CASE) {+ JSValue label1 = LABEL_NONE;+ if (!label_is_none(label_case)) {+ /* skip the case if needed */+ label1 = new_label(s);+ emit_goto(s, OP_goto, &label1);+ emit_label(s, &label_case);+ label_case = LABEL_NONE;+ }+ for (;;) {+ /* parse a sequence of case clauses */+ next_token(s);+ emit_op(s, OP_dup);+ js_parse_expr(s);+ js_parse_expect(s, ':');+ emit_op(s, OP_strict_eq);+ if (s->token.val == TOK_CASE) {+ if (label_is_none(label1))+ label1 = new_label(s);+ emit_goto(s, OP_if_true, &label1);+ } else {+ label_case = new_label(s);+ emit_goto(s, OP_if_false, &label_case);+ if (!label_is_none(label1))+ emit_label(s, &label1);+ break;+ }+ }+ } else if (s->token.val == TOK_DEFAULT) {+ next_token(s);+ js_parse_expect(s, ':');+ if (default_label_pos >= 0)+ js_parse_error(s, "duplicate default");+ if (label_is_none(label_case)) {+ /* falling thru direct from switch expression */+ label_case = new_label(s);+ emit_goto(s, OP_goto, &label_case);+ }+ default_label_pos = s->byte_code_len; + } else {+ if (label_is_none(label_case))+ js_parse_error(s, "invalid switch statement");+ PARSE_PUSH_VAL(s, label_case);+ PARSE_CALL_SAVE1(s, 7, js_parse_statement, 0,+ default_label_pos);+ PARSE_POP_VAL(s, label_case);+ }+ }+ js_parse_expect(s, '}');+ if (default_label_pos >= 0) {+ /* patch the default label */+ emit_label_pos(s, &label_case, default_label_pos);+ } else if (!label_is_none(label_case)) {+ emit_label(s, &label_case);+ }+ be = VALUE_TO_SP(s->ctx, s->top_break);+ emit_label(s, &be->label_break);+ emit_op(s, OP_drop); /* drop the switch expression */++ pop_break_entry(s);+ }+ break;+ case TOK_TRY:+ {+ JSValue label_catch, label_finally, label_end;+ BlockEnv *be;+ + set_eval_ret_undefined(s);+ next_token(s);+ label_catch = new_label(s);+ label_finally = new_label(s);++ emit_goto(s, OP_catch, &label_catch);+ + be = push_break_entry(s, JS_NULL, LABEL_NONE, LABEL_NONE, 1);+ be->label_finally = label_finally;++ PARSE_PUSH_VAL(s, label_catch);+ PARSE_CALL(s, 8, js_parse_block, 0);+ PARSE_POP_VAL(s, label_catch);++ be = VALUE_TO_SP(s->ctx, s->top_break);+ label_finally = be->label_finally;+ pop_break_entry(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);++ label_end = new_label(s);+ emit_goto(s, OP_goto, &label_end);+ + if (s->token.val == TOK_CATCH) {+ JSValue label_catch2;+ int var_idx;+ JSValue name;++ label_catch2 = new_label(s);++ next_token(s);+ js_parse_expect(s, '(');+ if (s->token.val != TOK_IDENT)+ js_parse_error(s, "identifier expected");+ name = s->token.value;+ /* XXX: the local scope is not implemented, so we add+ a normal variable */+ if (find_var(s, name) >= 0 || find_ext_var(s, name) >= 0) {+ js_parse_error(s, "catch variable already exists");+ }+ var_idx = add_var(s, name);+ next_token(s);+ js_parse_expect(s, ')');+ + /* store the exception value in the variable */+ emit_label(s, &label_catch);+ {+ JSFunctionBytecode *b = JS_VALUE_TO_PTR(s->cur_func);+ emit_var(s, OP_put_loc, var_idx - b->arg_count, s->pc2line_source_pos);+ }++ emit_goto(s, OP_catch, &label_catch2);+ + be = push_break_entry(s, JS_NULL, LABEL_NONE, LABEL_NONE, 1);+ be->label_finally = label_finally;+ + PARSE_PUSH_VAL(s, label_end);+ PARSE_PUSH_VAL(s, label_catch2);+ PARSE_CALL(s, 9, js_parse_block, 0);+ PARSE_POP_VAL(s, label_catch2);+ PARSE_POP_VAL(s, label_end);++ be = VALUE_TO_SP(s->ctx, s->top_break);+ label_finally = be->label_finally;+ pop_break_entry(s);++ /* drop the catch2 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);++ /* 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");+ }++ emit_label(s, &label_finally);+ if (s->token.val == TOK_FINALLY) {+ next_token(s);+ /* XXX: we don't return the correct value in eval() */+ /* on the stack: ret_value gosub_ret_value */+ push_break_entry(s, JS_NULL, LABEL_NONE, LABEL_NONE, 2);++ PARSE_PUSH_VAL(s, label_end);+ PARSE_CALL(s, 10, js_parse_block, 0);+ PARSE_POP_VAL(s, label_end);++ pop_break_entry(s);+ }+ emit_op(s, OP_ret);+ emit_label(s, &label_end);+ }+ break;+ case ';':+ /* empty statement */+ next_token(s);+ break;+ default:+ if (s->eval_ret_idx >= 0) {+ /* store the expression value so that it can be returned+ by eval() */+ js_parse_expr(s);+ emit_var(s, OP_put_loc, s->eval_ret_idx, s->pc2line_source_pos);+ } else {+ js_parse_expr2(s, PF_DROP);+ }+ js_parse_expect_semi(s);+ break;+ }+ done:+ return PARSE_STATE_RET;+}++static JSParseFunc *parse_func_table[] = {+ js_parse_expr_comma,+ js_parse_assign_expr,+ js_parse_cond_expr,+ js_parse_logical_and_or,+ js_parse_expr_binary,+ js_parse_unary,+ js_parse_postfix_expr,+ js_parse_statement,+ js_parse_block,+ js_parse_json_value,+ re_parse_alternative,+ re_parse_disjunction,+};++static void js_parse_source_element(JSParseState *s)+{+ if (s->token.val == TOK_FUNCTION) {+ js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, JS_NULL);+ } else {+ js_parse_call(s, PARSE_FUNC_js_parse_statement, 0);+ }+}+ +static JSFunctionBytecode *js_alloc_function_bytecode(JSContext *ctx)+{+ JSFunctionBytecode *b;+ b = js_mallocz(ctx, sizeof(JSFunctionBytecode), JS_MTAG_FUNCTION_BYTECODE);+ if (!b)+ return NULL;+ b->func_name = JS_NULL;+ b->byte_code = JS_NULL;+ b->cpool = JS_NULL;+ b->vars = JS_NULL;+ b->ext_vars = JS_NULL;+ b->filename = JS_NULL;+ b->pc2line = JS_NULL;+ return b;+}++/* the current token must be TOK_FUNCTION for JS_PARSE_FUNC_STATEMENT+ or JS_PARSE_FUNC_EXPR. Otherwise it is '('. */+static void js_parse_function_decl(JSParseState *s,+ JSParseFunctionEnum func_type, JSValue func_name)+{+ JSContext *ctx = s->ctx;+ BOOL is_expr;+ JSFunctionBytecode *b;+ int idx, skip_bits;+ JSVarRefKindEnum var_kind;+ JSValue bfunc;+ JSGCRef func_name_ref, bfunc_ref;+ + is_expr = (func_type != JS_PARSE_FUNC_STATEMENT);++ if (func_type == JS_PARSE_FUNC_STATEMENT ||+ func_type == JS_PARSE_FUNC_EXPR) {+ next_token(s);+ if (s->token.val != TOK_IDENT && !is_expr)+ js_parse_error(s, "function name expected");+ if (s->token.val == TOK_IDENT) {+ func_name = s->token.value;+ JS_PUSH_VALUE(ctx, func_name);+ next_token(s);+ JS_POP_VALUE(ctx, func_name);+ }+ }++ JS_PUSH_VALUE(ctx, func_name);+ b = js_alloc_function_bytecode(s->ctx);+ if (!b)+ js_parse_error_mem(s);+ bfunc = JS_VALUE_FROM_PTR(b);+ JS_PUSH_VALUE(ctx, bfunc);++ b->filename = s->filename_str;+ b->func_name = func_name_ref.val;+ b->source_pos = s->token.source_pos;+ b->has_column = s->has_column;+ + js_parse_expect1(s, '(');+ /* skip the arguments */+ js_skip_parens(s, NULL);+ + js_parse_expect1(s, '{');++ /* skip the code */+ skip_bits = js_skip_parens(s, is_expr ? &func_name_ref.val : NULL);+ + b = JS_VALUE_TO_PTR(bfunc_ref.val);+ b->has_arguments = ((skip_bits & SKIP_HAS_ARGUMENTS) != 0);+ b->has_local_func_name = ((skip_bits & SKIP_HAS_FUNC_NAME) != 0);+ + idx = cpool_add(s, bfunc_ref.val);+ if (is_expr) {+ /* create the function object */+ emit_op(s, OP_fclosure);+ emit_u16(s, idx);+ } else {+ idx = define_var(s, &var_kind, func_name_ref.val);+ /* size of hoisted for OP_fclosure + OP_put_loc/OP_put_arg/OP_put_ref */+ s->hoisted_code_len += 3 + 3;+ if (var_kind == JS_VARREF_KIND_VAR) {+ b = JS_VALUE_TO_PTR(s->cur_func);+ idx += b->arg_count;+ }+ b = JS_VALUE_TO_PTR(bfunc_ref.val);+ /* hoisted function definition: save the variable index to+ define it at the start of the function */+ b->arg_count = idx + 1;+ }+ JS_POP_VALUE(ctx, bfunc);+ JS_POP_VALUE(ctx, func_name);+}++static void define_hoisted_functions(JSParseState *s, BOOL is_eval)+{+ JSValueArray *cpool;+ JSValue val;+ JSFunctionBytecode *b;+ int idx, saved_byte_code_len, arg_count, i, op;+ + /* add pc2line info */+ b = JS_VALUE_TO_PTR(s->cur_func);+ if (b->pc2line != JS_NULL) {+ int h, n;++ /* byte align */+ n = (-s->pc2line_bit_len) & 7;+ if (n != 0)+ pc2line_put_bits(s, n, 0);++ n = s->hoisted_code_len;+ h = 0;+ for(;;) {+ pc2line_put_bits(s, 8, (n & 0x7f) | h);+ n >>= 7;+ if (n == 0)+ break;+ h |= 0x80;+ }+ }++ if (s->hoisted_code_len == 0)+ return;+ emit_insert(s, 0, s->hoisted_code_len);++ b = JS_VALUE_TO_PTR(s->cur_func);+ arg_count = b->arg_count;++ saved_byte_code_len = s->byte_code_len;+ s->byte_code_len = 0;+ cpool = JS_VALUE_TO_PTR(b->cpool);+ for(i = 0; i < s->cpool_len; i++) {+ val = cpool->arr[i];+ if (JS_IsPtr(val)) {+ b = JS_VALUE_TO_PTR(val);+ if (b->mtag == JS_MTAG_FUNCTION_BYTECODE &&+ b->arg_count != 0) {+ idx = b->arg_count - 1;+ /* XXX: could use smaller opcodes */+ if (is_eval) {+ op = OP_put_var_ref_nocheck;+ } else if (idx < arg_count) {+ op = OP_put_arg;+ } else {+ idx -= arg_count;+ op = OP_put_loc;+ }+ /* no realloc possible here */+ emit_u8(s, OP_fclosure);+ emit_u16(s, i);++ emit_u8(s, op);+ emit_u16(s, idx);+ }+ }+ }+ s->byte_code_len = saved_byte_code_len;+}++static void js_parse_function(JSParseState *s)+{+ JSFunctionBytecode *b;+ int arg_count;+ + next_token(s);++ js_parse_expect(s, '(');++ while (s->token.val != ')') {+ JSValue name;+ /* XXX: gc */+ if (s->token.val != TOK_IDENT)+ js_parse_error(s, "missing formal parameter");+ name = s->token.value;+ if (name == js_get_atom(s->ctx, JS_ATOM_eval) ||+ name == js_get_atom(s->ctx, JS_ATOM_arguments)) {+ js_parse_error(s, "invalid argument name");+ }+ if (find_var(s, name) >= 0)+ js_parse_error(s, "duplicate argument name");+ add_var(s, name);+ next_token(s);+ if (s->token.val == ')')+ break;+ js_parse_expect(s, ',');+ }+ b = JS_VALUE_TO_PTR(s->cur_func);+ arg_count = b->arg_count = s->local_vars_len;++ next_token(s);+ + js_parse_expect(s, '{');++ /* initialize the arguments */+ b = JS_VALUE_TO_PTR(s->cur_func);+ if (b->has_arguments) {+ int var_idx;+ var_idx = add_var(s, js_get_atom(s->ctx, JS_ATOM_arguments));+ emit_op(s, OP_arguments);+ put_var(s, JS_VARREF_KIND_VAR, var_idx - arg_count, s->pc2line_source_pos);+ }++ /* XXX: initialize the function name */+ b = JS_VALUE_TO_PTR(s->cur_func);+ if (b->has_local_func_name) {+ int var_idx;+ /* XXX: */+ var_idx = add_var(s, b->func_name);+ emit_op(s, OP_this_func);+ put_var(s, JS_VARREF_KIND_VAR, var_idx - arg_count, s->pc2line_source_pos);+ }+ + while (s->token.val != '}') {+ js_parse_source_element(s);+ }++ if (js_is_live_code(s))+ emit_op(s, OP_return_undef);++ next_token(s);++ define_hoisted_functions(s, FALSE);+ + /* save the bytecode to the function */+ b = JS_VALUE_TO_PTR(s->cur_func);+ b->byte_code = s->byte_code;+}++static void js_parse_program(JSParseState *s)+{+ JSFunctionBytecode *b;++ next_token(s);++ /* hidden variable for the return value */+ if (s->has_retval) {+ s->eval_ret_idx = add_var(s, js_get_atom(s->ctx, JS_ATOM__ret_));+ }+ + while (s->token.val != TOK_EOF) {+ js_parse_source_element(s);+ }++ if (s->eval_ret_idx >= 0) {+ emit_var(s, OP_get_loc, s->eval_ret_idx, s->pc2line_source_pos);+ emit_op(s, OP_return);+ } else {+ emit_op(s, OP_return_undef);+ }++ define_hoisted_functions(s, TRUE);++ /* save the bytecode to the function */+ b = JS_VALUE_TO_PTR(s->cur_func);+ b->byte_code = s->byte_code;+}++#define CVT_VAR_SIZE_MAX 16++typedef struct {+ uint16_t new_var_idx; /* new local var index */+ uint8_t is_local;+} ConvertVarEntry;++static void convert_ext_vars_to_local_vars_bytecode(JSParseState *s,+ uint8_t *byte_code, int byte_code_len,+ int var_start, const ConvertVarEntry *cvt_tab,+ int tab_len)+{+ int pos, var_end, j, op, var_idx;+ const JSOpCode *oi;+ + var_end = var_start + tab_len;+ pos = 0;+ while (pos < byte_code_len) {+ op = byte_code[pos];+ oi = &opcode_info[op];+ switch(op) {+ case OP_get_var_ref:+ case OP_put_var_ref:+ case OP_get_var_ref_nocheck:+ case OP_put_var_ref_nocheck:+ var_idx = get_u16(byte_code + pos + 1);+ if (var_idx >= var_start && var_idx < var_end) {+ j = var_idx - var_start;+ put_u16(byte_code + pos + 1, cvt_tab[j].new_var_idx);+ if (cvt_tab[j].is_local) {+ if (op == OP_get_var_ref || op == OP_get_var_ref_nocheck) {+ byte_code[pos] = OP_get_loc;+ } else {+ byte_code[pos] = OP_put_loc;+ }+ }+ }+ break;+ default:+ break;+ }+ pos += oi->size;+ }+}++/* no allocation */+static void convert_ext_vars_to_local_vars(JSParseState *s)+{+ JSValueArray *ext_vars;+ JSFunctionBytecode *b;+ JSByteArray *bc_arr;+ JSValue var_name, decl;+ int i0, i, j, var_idx, l;+ ConvertVarEntry cvt_tab[CVT_VAR_SIZE_MAX];+ + b = JS_VALUE_TO_PTR(s->cur_func);+ if (s->local_vars_len == 0 || b->ext_vars_len == 0)+ return;+ bc_arr = JS_VALUE_TO_PTR(b->byte_code);+ ext_vars = JS_VALUE_TO_PTR(b->ext_vars);++ /* do it by parts to save memory */+ j = 0;+ for(i0 = 0; i0 < b->ext_vars_len; i0 += CVT_VAR_SIZE_MAX) {+ l = min_int(b->ext_vars_len - i0, CVT_VAR_SIZE_MAX);+ for(i = 0; i < l; i++) {+ var_name = ext_vars->arr[2 * (i0 + i)];+ decl = ext_vars->arr[2 * (i0 + i) + 1];+ var_idx = find_var(s, var_name);+ /* fail safe: we avoid arguments even if they cannot appear */+ if (var_idx >= b->arg_count) {+ cvt_tab[i].new_var_idx = var_idx - b->arg_count;+ cvt_tab[i].is_local = TRUE;+ } else {+ cvt_tab[i].new_var_idx = j;+ cvt_tab[i].is_local = FALSE;+ ext_vars->arr[2 * j] = var_name;+ ext_vars->arr[2 * j + 1] = decl;+ j++;+ }+ }+ if (j != (i0 + l)) {+ convert_ext_vars_to_local_vars_bytecode(s, bc_arr->buf, s->byte_code_len,+ i0, cvt_tab, l);+ }+ }+ b->ext_vars_len = j;+}++/* prepare the analysis of the code starting at position 'pos' */+static void compute_stack_size_push(JSParseState *s, + JSByteArray *arr,+ uint8_t *explore_tab,+ uint32_t pos, int stack_len)+{+ int short_stack_len;+ +#if 0+ js_printf(s->ctx, "%5d: %d\n", pos, stack_len);+#endif+ if (pos >= (uint32_t)arr->size)+ js_parse_error(s, "bytecode buffer overflow (pc=%d)", pos);+ /* XXX: could avoid the division */+ short_stack_len = 1 + ((unsigned)stack_len % 255);+ if (explore_tab[pos] != 0) {+ /* already explored: check that the stack size is consistent */+ if (explore_tab[pos] != short_stack_len) {+ js_parse_error(s, "inconsistent stack size: %d %d (pc=%d)", explore_tab[pos] - 1, short_stack_len - 1, (int)pos);+ }+ } else {+ explore_tab[pos] = short_stack_len;+ /* may initiate a GC */+ PARSE_PUSH_INT(s, pos);+ PARSE_PUSH_INT(s, stack_len);+ }+}++static void compute_stack_size(JSParseState *s, JSValue *pfunc)+{+ JSContext *ctx = s->ctx;+ JSByteArray *explore_arr, *arr;+ JSFunctionBytecode *b;+ uint8_t *explore_tab;+ JSValue *stack_top, explore_arr_val;+ uint32_t pos;+ int op, op_len, pos1, n_pop, stack_len;+ const JSOpCode *oi;+ JSGCRef explore_arr_val_ref;+ + b = JS_VALUE_TO_PTR(*pfunc);+ arr = JS_VALUE_TO_PTR(b->byte_code);++ explore_arr = js_alloc_byte_array(s->ctx, arr->size);+ if (!explore_arr)+ js_parse_error_mem(s);++ b = JS_VALUE_TO_PTR(*pfunc);+ arr = JS_VALUE_TO_PTR(b->byte_code);++ explore_arr_val = JS_VALUE_FROM_PTR(explore_arr);+ explore_tab = explore_arr->buf;+ memset(explore_tab, 0, arr->size);++ JS_PUSH_VALUE(ctx, explore_arr_val);++ stack_top = ctx->sp;++ compute_stack_size_push(s, arr, explore_tab, 0, 0);++ while (ctx->sp < stack_top) {+ PARSE_POP_INT(s, stack_len);+ PARSE_POP_INT(s, pos);+ + /* compute_stack_size_push may have initiated a GC */+ b = JS_VALUE_TO_PTR(*pfunc);+ arr = JS_VALUE_TO_PTR(b->byte_code);+ explore_arr = JS_VALUE_TO_PTR(explore_arr_val_ref.val);+ explore_tab = explore_arr->buf;+ + op = arr->buf[pos++];+ if (op == OP_invalid || op >= OP_COUNT)+ js_parse_error(s, "invalid opcode (pc=%d)", (int)(pos - 1));+ oi = &opcode_info[op];+ op_len = oi->size;+ if ((pos + op_len - 1) > arr->size) {+ js_parse_error(s, "bytecode buffer overflow (pc=%d)", (int)(pos - 1));+ }+ n_pop = oi->n_pop;+ if (oi->fmt == OP_FMT_npop)+ n_pop += get_u16(arr->buf + pos);++ if (stack_len < n_pop) {+ js_parse_error(s, "stack underflow (pc=%d)", (int)(pos - 1));+ }+ stack_len += oi->n_push - n_pop;+ if (stack_len > b->stack_size) {+ if (stack_len > JS_MAX_FUNC_STACK_SIZE)+ js_parse_error(s, "stack overflow (pc=%d)", (int)(pos - 1));+ b->stack_size = stack_len;+ }+ switch(op) {+ case OP_return:+ case OP_return_undef:+ case OP_throw:+ case OP_ret:+ goto done; /* no code after */+ case OP_goto:+ pos += get_u32(arr->buf + pos);+ break;+ case OP_if_true:+ case OP_if_false:+ pos1 = pos + get_u32(arr->buf + pos);+ compute_stack_size_push(s, arr, explore_tab, pos1, stack_len);+ pos += op_len - 1;+ break;+ case OP_gosub:+ pos1 = pos + get_u32(arr->buf + pos);+ compute_stack_size_push(s, arr, explore_tab, pos1, stack_len + 1);+ pos += op_len - 1;+ break;+ default:+ pos += op_len - 1;+ break;+ }+ compute_stack_size_push(s, arr, explore_tab, pos, stack_len);+ done: ;+ }++ JS_POP_VALUE(ctx, explore_arr_val);+ explore_arr = JS_VALUE_TO_PTR(explore_arr_val);+ js_free(s->ctx, explore_arr);+}++static void resolve_var_refs(JSParseState *s, JSValue *pfunc, JSValue *pparent_func)+{+ JSContext *ctx = s->ctx;+ int i, decl, var_idx, arg_count, ext_vars_len;+ JSValueArray *ext_vars;+ JSValue var_name;+ JSFunctionBytecode *b1, *b;++ b = JS_VALUE_TO_PTR(*pfunc);+ if (b->ext_vars_len == 0)+ return;+ b1 = JS_VALUE_TO_PTR(*pparent_func);+ arg_count = b1->arg_count;+ + ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ ext_vars_len = b->ext_vars_len;+ + for(i = 0; i < ext_vars_len; i++) {+ b = JS_VALUE_TO_PTR(*pfunc);+ ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ var_name = ext_vars->arr[2 * i];+ var_idx = find_func_var(ctx, *pparent_func, var_name);+ if (var_idx >= 0) {+ if (var_idx < arg_count) {+ decl = (JS_VARREF_KIND_ARG << 16) | var_idx;+ } else {+ decl = (JS_VARREF_KIND_VAR << 16) | (var_idx - arg_count);+ }+ } else {+ var_idx = find_func_ext_var(s, *pparent_func, var_name);+ if (var_idx < 0) {+ /* the global type may be patched later */+ var_idx = add_func_ext_var(s, *pparent_func, var_name,+ (JS_VARREF_KIND_GLOBAL << 16));+ }+ decl = (JS_VARREF_KIND_VAR_REF << 16) | var_idx;+ }+ b = JS_VALUE_TO_PTR(*pfunc);+ ext_vars = JS_VALUE_TO_PTR(b->ext_vars);+ ext_vars->arr[2 * i + 1] = JS_NewShortInt(decl);+ }+}++static void reset_parse_state(JSParseState *s, uint32_t input_pos,+ JSValue cur_func)+{+ s->buf_pos = input_pos;+ s->token.val = ' ';++ s->cur_func = cur_func;+ s->byte_code = JS_NULL;+ s->byte_code_len = 0;+ s->last_opcode_pos = -1;++ s->pc2line_bit_len = 0;+ s->pc2line_source_pos = 0;+ + s->cpool_len = 0;+ s->hoisted_code_len = 0;+ + s->local_vars_len = 0;++ s->eval_ret_idx = -1;+}++static void js_parse_local_functions(JSParseState *s, JSValue *pfunc)+{+ JSContext *ctx = s->ctx;+ JSValue *pparent_func;+ JSValueArray *cpool;+ int err, cpool_pos;+ JSValue func;+ JSFunctionBytecode *b, *b1;+ JSGCRef func_ref;+ JSValue *stack_top;+ + err = JS_StackCheck(ctx, 3);+ if (err)+ js_parse_error_stack_overflow(s);+ stack_top = ctx->sp;+ + *--ctx->sp = JS_NULL; /* parent_func */+ *--ctx->sp = *pfunc; /* func */+ *--ctx->sp = JS_NewShortInt(0); /* cpool_pos */++ while (ctx->sp < stack_top) {+ pparent_func = &ctx->sp[2];+ pfunc = &ctx->sp[1];+ cpool_pos = JS_VALUE_GET_INT(ctx->sp[0]);+#if 0+ JS_DumpValue(ctx, "func", *pfunc);+ JS_DumpValue(ctx, "parent", *pparent_func);+ JS_DumpValue(ctx, "cpool_pos", ctx->sp[0]);+#endif+ if (cpool_pos == 0) {+ b = JS_VALUE_TO_PTR(*pfunc);+ + convert_ext_vars_to_local_vars(s);+ + js_shrink_byte_array(ctx, &b->byte_code, s->byte_code_len);+ js_shrink_value_array(ctx, &b->cpool, s->cpool_len);+ js_shrink_value_array(ctx, &b->vars, s->local_vars_len);+ js_shrink_byte_array(ctx, &b->pc2line, (s->pc2line_bit_len + 7) / 8);+ + compute_stack_size(s, pfunc);+ }++ b = JS_VALUE_TO_PTR(*pfunc);+ if (b->cpool != JS_NULL) {+ int cpool_size;+ cpool = JS_VALUE_TO_PTR(b->cpool);+ cpool_size = cpool->size;+ for(; cpool_pos < cpool_size; cpool_pos++) {+ b = JS_VALUE_TO_PTR(*pfunc);+ cpool = JS_VALUE_TO_PTR(b->cpool);+ func = cpool->arr[cpool_pos];+ if (!JS_IsPtr(func))+ continue;+ b1 = JS_VALUE_TO_PTR(func);+ if (b1->mtag != JS_MTAG_FUNCTION_BYTECODE)+ continue;+ + reset_parse_state(s, b1->source_pos, func);+ + s->is_eval = FALSE;+ s->is_repl = FALSE;+ s->has_retval = FALSE;+ + JS_PUSH_VALUE(ctx, func);+ js_parse_function(s);+ + /* parse a local function */+ err = JS_StackCheck(ctx, 3);+ JS_POP_VALUE(ctx, func);+ if (err)+ js_parse_error_stack_overflow(s);+ /* set the next cpool position */+ *ctx->sp = JS_NewShortInt(cpool_pos + 1); ++ *--ctx->sp = *pfunc; /* parent_func */+ *--ctx->sp = func; /* func */+ *--ctx->sp = JS_NewShortInt(0); /* cpool_pos */+ goto next;+ }+ }+ + if (*pparent_func != JS_NULL) {+ resolve_var_refs(s, pfunc, pparent_func);+ }+ /* now we can shrink the external vars */+ b = JS_VALUE_TO_PTR(*pfunc);+ js_shrink_value_array(ctx, &b->ext_vars, 2 * b->ext_vars_len);+#ifdef DUMP_FUNC_BYTECODE+ dump_byte_code(ctx, b);+#endif+ /* remove the stack entry */+ ctx->sp += 3;+ ctx->stack_bottom = ctx->sp;+ next: ;+ }+}++/* return the parsed value in s->token.value */+/* XXX: use exact JSON white space definition */+static int js_parse_json_value(JSParseState *s, int state, int dummy_param)+{+ JSContext *ctx = s->ctx;+ const uint8_t *p;+ JSValue val;++ PARSE_START2();+ + p = s->source_buf + s->buf_pos;+ p += skip_spaces((const char *)p);+ s->buf_pos = p - s->source_buf;+ if ((*p >= '0' && *p <= '9') || *p == '-') {+ double d;+ JSByteArray *tmp_arr;+ tmp_arr = js_alloc_byte_array(s->ctx, sizeof(JSATODTempMem));+ if (!tmp_arr)+ js_parse_error_mem(s);+ p = s->source_buf + s->buf_pos;+ d = js_atod((const char *)p, (const char **)&p, 10, 0,+ (JSATODTempMem *)tmp_arr->buf);+ js_free(s->ctx, tmp_arr);+ if (isnan(d))+ js_parse_error(s, "invalid number literal");+ val = JS_NewFloat64(s->ctx, d);+ } else if (*p == 't' &&+ p[1] == 'r' && p[2] == 'u' && p[3] == 'e') {+ p += 4;+ val = JS_TRUE;+ } else if (*p == 'f' &&+ p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') {+ p += 5;+ val = JS_FALSE;+ } else if (*p == 'n' &&+ p[1] == 'u' && p[2] == 'l' && p[3] == 'l') {+ p += 4;+ val = JS_NULL;+ } else if (*p == '\"') {+ uint32_t pos;+ pos = p + 1 - s->source_buf;+ val = js_parse_string(s, &pos, '\"');+ p = s->source_buf + pos;+ } else if (*p == '[') {+ JSValue val2;+ uint32_t idx;+ + val = JS_NewArray(ctx, 0);+ if (JS_IsException(val))+ js_parse_error_mem(s);+ PARSE_PUSH_VAL(s, val); /* 'val' is not usable after this call */+ p = s->source_buf + s->buf_pos + 1;+ p += skip_spaces((const char *)p);+ if (*p != ']') {+ idx = 0;+ for(;;) {+ s->buf_pos = p - s->source_buf;+ PARSE_PUSH_INT(s, idx);+ PARSE_CALL(s, 0, js_parse_json_value, 0);+ PARSE_POP_INT(s, idx);+ val2 = s->token.value;+ val2 = JS_SetPropertyUint32(ctx, *ctx->sp, idx, val2);+ if (JS_IsException(val2))+ js_parse_error_mem(s);+ idx++;+ p = s->source_buf + s->buf_pos;+ p += skip_spaces((const char *)p);+ if (*p != ',')+ break;+ p++;+ }+ }+ if (*p != ']')+ js_parse_error(s, "expecting ']'");+ p++;+ PARSE_POP_VAL(s, val);+ } else if (*p == '{') {+ JSValue val2, prop;+ uint32_t pos;+ + val = JS_NewObject(ctx);+ if (JS_IsException(val))+ js_parse_error_mem(s);+ PARSE_PUSH_VAL(s, val); /* 'val' is not usable after this call */+ p = s->source_buf + s->buf_pos + 1;+ p += skip_spaces((const char *)p);+ if (*p != '}') {+ for(;;) {+ p += skip_spaces((const char *)p);+ s->buf_pos = p - s->source_buf;+ if (*p != '\"')+ js_parse_error(s, "expecting '\"'");+ pos = p + 1 - s->source_buf;+ prop = js_parse_string(s, &pos, '\"');+ prop = JS_ToPropertyKey(ctx, prop);+ if (JS_IsException(prop))+ js_parse_error_mem(s);+ p = s->source_buf + pos;+ p += skip_spaces((const char *)p);+ if (*p != ':')+ js_parse_error(s, "expecting ':'");+ p++;+ s->buf_pos = p - s->source_buf;+ PARSE_PUSH_VAL(s, prop);+ PARSE_CALL(s, 1, js_parse_json_value, 0);+ val2 = s->token.value;+ PARSE_POP_VAL(s, prop);+ val2 = JS_DefinePropertyValue(ctx, *ctx->sp, prop, val2);+ if (JS_IsException(val2))+ js_parse_error_mem(s);+ p = s->source_buf + s->buf_pos;+ p += skip_spaces((const char *)p);+ if (*p != ',')+ break;+ p++;+ }+ }+ if (*p != '}')+ js_parse_error(s, "expecting '}'");+ p++;+ PARSE_POP_VAL(s, val);+ } else {+ js_parse_error(s, "unexpected character");+ }+ s->buf_pos = p - s->source_buf;+ s->token.value = val;+ return PARSE_STATE_RET;+}++static JSValue js_parse_json(JSParseState *s)+{+ s->buf_pos = 0;+ js_parse_call(s, PARSE_FUNC_js_parse_json_value, 0);+ s->buf_pos += skip_spaces((const char *)(s->source_buf + s->buf_pos));+ if (s->buf_pos != s->buf_len) {+ js_parse_error(s, "unexpected character");+ }+ return s->token.value;+}++/* source_str must be a string or JS_NULL. (input, input_len) is+ meaningful only if source_str is JS_NULL. */+static JSValue JS_Parse2(JSContext *ctx, JSValue source_str,+ const char *input, size_t input_len,+ const char *filename, int eval_flags)+{+ JSParseState parse_state, *s;+ JSFunctionBytecode *b;+ JSValue top_func, *saved_sp;+ JSGCRef top_func_ref, *saved_top_gc_ref;+ uint8_t str_buf[5];+ + /* XXX: start gc at the start of parsing ? */+ /* XXX: if the parse state is too large, move it to JSContext */+ s = &parse_state;+ memset(s, 0, sizeof(*s));+ + s->ctx = ctx;+ ctx->parse_state = s;+ s->source_str = JS_NULL;+ s->filename_str = JS_NULL;+ s->has_column = ((eval_flags & JS_EVAL_STRIP_COL) == 0);++ if (JS_IsPtr(source_str)) {+ JSString *p = JS_VALUE_TO_PTR(source_str);+ s->source_str = source_str;+ s->buf_len = p->len;+ s->source_buf = p->buf;+ } else if (JS_VALUE_GET_SPECIAL_TAG(source_str) == JS_TAG_STRING_CHAR) {+ s->buf_len = get_short_string(str_buf, source_str);+ s->source_buf = str_buf;+ } else {+ s->buf_len = input_len;+ s->source_buf = (const uint8_t *)input;+ }+ s->top_break = JS_NULL;+ saved_top_gc_ref = ctx->top_gc_ref;+ saved_sp = ctx->sp;+ + if (setjmp(s->jmp_env)) {+ int line_num, col_num;+ JSValue val;++ ctx->parse_state = NULL;+ ctx->top_gc_ref = saved_top_gc_ref;+ ctx->sp = saved_sp;+ ctx->stack_bottom = ctx->sp;+ + line_num = get_line_col(&col_num, s->source_buf,+ (eval_flags & (JS_EVAL_JSON | JS_EVAL_REGEXP)) ?+ s->buf_pos : s->token.source_pos);+ val = JS_ThrowError(ctx, JS_CLASS_SYNTAX_ERROR, "%s", s->error_msg);+ build_backtrace(ctx, ctx->current_exception, filename, line_num + 1, col_num + 1, 0);+ return val;+ }++ if (eval_flags & JS_EVAL_JSON) {+ top_func = js_parse_json(s);+ } else if (eval_flags & JS_EVAL_REGEXP) {+ top_func = js_parse_regexp(s, eval_flags >> JS_EVAL_REGEXP_FLAGS_SHIFT);+ } else {+ s->filename_str = JS_NewString(ctx, filename);+ if (JS_IsException(s->filename_str))+ js_parse_error_mem(s);+ + b = js_alloc_function_bytecode(ctx);+ if (!b)+ js_parse_error_mem(s);+ b->filename = s->filename_str;+ b->func_name = js_get_atom(ctx, JS_ATOM__eval_);+ b->has_column = s->has_column;+ top_func = JS_VALUE_FROM_PTR(b);+ + reset_parse_state(s, 0, top_func);+ + s->is_eval = TRUE;+ s->has_retval = ((eval_flags & JS_EVAL_RETVAL) != 0);+ s->is_repl = ((eval_flags & JS_EVAL_REPL) != 0);+ + JS_PUSH_VALUE(ctx, top_func);+ + js_parse_program(s);+ + js_parse_local_functions(s, &top_func_ref.val);+ + JS_POP_VALUE(ctx, top_func);+ }+ ctx->parse_state = NULL;+ return top_func;+}++/* warning: it is assumed that input[input_len] = '\0' */+JSValue JS_Parse(JSContext *ctx, const char *input, size_t input_len,+ const char *filename, int eval_flags)+{+ return JS_Parse2(ctx, JS_NULL, input, input_len, filename, eval_flags);+}++JSValue JS_Run(JSContext *ctx, JSValue val)+{+ JSFunctionBytecode *b;+ JSGCRef val_ref;+ int err;+ + if (!JS_IsPtr(val))+ goto fail;+ b = JS_VALUE_TO_PTR(val);+ if (b->mtag != JS_MTAG_FUNCTION_BYTECODE) {+ fail:+ return JS_ThrowTypeError(ctx, "bytecode function expected");+ }++ val = js_closure(ctx, val, NULL);+ if (JS_IsException(val))+ return val;+ JS_PUSH_VALUE(ctx, val);+ err = JS_StackCheck(ctx, 2);+ JS_POP_VALUE(ctx, val);+ if (err)+ return JS_EXCEPTION;+ JS_PushArg(ctx, val);+ JS_PushArg(ctx, JS_NULL);+ val = JS_Call(ctx, 0);+ return val;+}++/* warning: it is assumed that input[input_len] = '\0' */+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,+ const char *filename, int eval_flags)+{+ JSValue val;+ val = JS_Parse(ctx, input, input_len, filename, eval_flags);+ if (JS_IsException(val))+ return val;+ return JS_Run(ctx, val);+}++/**********************************************************************/+/* garbage collector */++/* return the size in bytes */+static int get_mblock_size(const void *ptr)+{+ int mtag = ((JSMemBlockHeader *)ptr)->mtag;+ int size;+ switch(mtag) {+ case JS_MTAG_OBJECT:+ {+ const JSObject *p = ptr;+ size = offsetof(JSObject, u) + p->extra_size * JSW;+ }+ break;+ case JS_MTAG_FLOAT64:+ size = sizeof(JSFloat64);+ break;+ case JS_MTAG_STRING:+ {+ const JSString *p = ptr;+ size = sizeof(JSString) + ((p->len + JSW) & ~(JSW - 1));+ }+ break;+ case JS_MTAG_BYTE_ARRAY:+ {+ const JSByteArray *p = ptr;+ size = sizeof(JSByteArray) + ((p->size + JSW - 1) & ~(JSW - 1));+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ const JSValueArray *p = ptr;+ size = sizeof(JSValueArray) + p->size * sizeof(p->arr[0]);+ }+ break;+ case JS_MTAG_FREE:+ {+ const JSFreeBlock *p = ptr;+ size = sizeof(JSFreeBlock) + p->size * sizeof(JSWord);+ }+ break;+ case JS_MTAG_VARREF:+ {+ const JSVarRef *p = ptr;+ size = sizeof(JSVarRef);+ if (p->is_detached)+ size -= sizeof(JSValue);+ }+ break;+ case JS_MTAG_FUNCTION_BYTECODE:+ size = sizeof(JSFunctionBytecode);+ break;+ default:+ size = 0;+ assert(0);+ }+ return size;+}++/* gc mark pass */++typedef struct {+ JSContext *ctx;+ JSValue *gsp;+ JSValue *gs_bottom;+ JSValue *gs_top;+ BOOL overflow;+} GCMarkState;++static BOOL mtag_has_references(int mtag)+{+ return (mtag == JS_MTAG_OBJECT ||+ mtag == JS_MTAG_VALUE_ARRAY ||+ mtag == JS_MTAG_VARREF ||+ mtag == JS_MTAG_FUNCTION_BYTECODE);+}++static void gc_mark(GCMarkState *s, JSValue val)+{+ JSContext *ctx = s->ctx;+ void *ptr;+ JSMemBlockHeader *mb;++ if (!JS_IsPtr(val))+ return;+ ptr = JS_VALUE_TO_PTR(val);+ if (JS_IS_ROM_PTR(ctx, ptr))+ return;+ mb = ptr;+ if (mb->gc_mark)+ return;+ mb->gc_mark = 1;+ if (mtag_has_references(mb->mtag)) {+ if (mb->mtag == JS_MTAG_VALUE_ARRAY) {+ /* value array are handled specifically to save stack space */+ if ((s->gsp - s->gs_bottom) < 2) {+ s->overflow = TRUE;+ } else {+ *--s->gsp = 0;+ *--s->gsp = val;+ }+ } else {+ if ((s->gsp - s->gs_bottom) < 1) {+ s->overflow = TRUE;+ } else {+ *--s->gsp = val;+ }+ }+ }+}++/* flush the GC mark stack */+static void gc_mark_flush(GCMarkState *s)+{+ void *ptr;+ JSMemBlockHeader *mb;+ JSValue val;+ + while (s->gsp < s->gs_top) {+ val = *s->gsp++;+ ptr = JS_VALUE_TO_PTR(val);+ mb = ptr;++ switch(mb->mtag) {+ case JS_MTAG_OBJECT:+ {+ const JSObject *p = ptr;+ gc_mark(s, p->proto);+ gc_mark(s, p->props);+ switch(p->class_id) {+ case JS_CLASS_CLOSURE:+ {+ int i;+ gc_mark(s, p->u.closure.func_bytecode);+ for(i = 0; i < p->extra_size - 1; i++)+ gc_mark(s, p->u.closure.var_refs[i]);+ }+ break;+ case JS_CLASS_C_FUNCTION:+ if (p->extra_size > 1)+ gc_mark(s, p->u.cfunc.params);+ break;+ case JS_CLASS_ARRAY:+ gc_mark(s, p->u.array.tab);+ break;+ case JS_CLASS_ERROR:+ gc_mark(s, p->u.error.message);+ gc_mark(s, p->u.error.stack);+ break;+ case JS_CLASS_ARRAY_BUFFER:+ gc_mark(s, p->u.array_buffer.byte_buffer);+ break;+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY:+ gc_mark(s, p->u.typed_array.buffer);+ break;+ case JS_CLASS_REGEXP:+ gc_mark(s, p->u.regexp.source);+ gc_mark(s, p->u.regexp.byte_code);+ break;+ }+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ const JSValueArray *p = ptr;+ int pos;++ pos = *s->gsp++;++ /* fast path to skip non objects */+ while (pos < p->size && !JS_IsPtr(p->arr[pos]))+ pos++;++ if (pos < p->size) {+ if ((pos + 1) < p->size) {+ /* the next element needs to be scanned */+ *--s->gsp = pos + 1;+ *--s->gsp = val;+ }+ /* mark the current element */+ gc_mark(s, p->arr[pos]);+ }+ }+ break;+ case JS_MTAG_VARREF:+ {+ const JSVarRef *p = ptr;+ gc_mark(s, p->u.value);+ }+ break;+ case JS_MTAG_FUNCTION_BYTECODE:+ {+ const JSFunctionBytecode *b = ptr;+ gc_mark(s, b->func_name);+ gc_mark(s, b->byte_code);+ gc_mark(s, b->cpool);+ gc_mark(s, b->vars);+ gc_mark(s, b->ext_vars);+ gc_mark(s, b->filename);+ gc_mark(s, b->pc2line);+ }+ break;+ default:+ break;+ }+ }+}++static void gc_mark_root(GCMarkState *s, JSValue val)+{+ gc_mark(s, val);+ gc_mark_flush(s);+}++/* return true if the memory block is marked i.e. it won't be freed by the GC */+static BOOL gc_mb_is_marked(JSValue val)+{+ JSFreeBlock *b;+ if (!JS_IsPtr(val))+ return FALSE;+ b = (JSFreeBlock *)JS_VALUE_TO_PTR(val);+ return b->gc_mark;+}++static void gc_mark_all(JSContext *ctx, BOOL keep_atoms)+{+ GCMarkState s_s, *s = &s_s;+ JSValue *sp, *sp_end;++ s->ctx = ctx;+ /* initialize the GC stack */+ s->overflow = FALSE;+ s->gs_top = ctx->sp;+ s->gsp = s->gs_top;+#if 1+ s->gs_bottom = (JSValue *)ctx->heap_free;+#else+ s->gs_bottom = s->gs_top - 3; /* TEST small stack space */+#endif++ /* keep the atoms if they are in RAM (only used when compiling to file) */+ if ((uint8_t *)ctx->atom_table == ctx->heap_base &&+ keep_atoms) {+ uint8_t *ptr;+ for(ptr = (uint8_t *)ctx->atom_table;+ ptr < (uint8_t *)(ctx->atom_table + JS_ATOM_END);+ ptr += get_mblock_size(ptr)) {+ gc_mark_root(s, JS_VALUE_FROM_PTR(ptr));+ }+ }+ + /* mark all the memory blocks */+ sp_end = ctx->class_proto + 2 * ctx->class_count;+ for(sp = &ctx->current_exception; sp < sp_end; sp++) {+ gc_mark_root(s, *sp);+ }++ for(sp = ctx->sp; sp < (JSValue *)ctx->stack_top; sp++) {+ gc_mark_root(s, *sp);+ }++ {+ JSGCRef *ref;+ for(ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) {+ gc_mark_root(s, ref->val);+ }+ for(ref = ctx->last_gc_ref; ref != NULL; ref = ref->prev) {+ gc_mark_root(s, ref->val);+ }+ }+ if (ctx->parse_state) {+ JSParseState *ps = ctx->parse_state;++ gc_mark_root(s, ps->source_str);+ gc_mark_root(s, ps->filename_str);+ gc_mark_root(s, ps->token.value);+ gc_mark_root(s, ps->cur_func);+ gc_mark_root(s, ps->byte_code);+ }++ /* if the mark stack overflowed, need to scan the heap */+ while (s->overflow) {+ uint8_t *ptr;+ int size;+ JSMemBlockHeader *mb;+ + s->overflow = FALSE;+ + ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ size = get_mblock_size(ptr);+ mb = (JSMemBlockHeader *)ptr;+ if (mb->gc_mark && mtag_has_references(mb->mtag)) {+ if (mb->mtag == JS_MTAG_VALUE_ARRAY)+ *--s->gsp = 0;+ *--s->gsp = JS_VALUE_FROM_PTR(ptr);+ gc_mark_flush(s);+ }+ ptr += size;+ }+ }++ /* update the unique string table (its elements are considered as+ weak string references) */+ if (!JS_IsNull(ctx->unique_strings)) {+ JSValueArray *arr = JS_VALUE_TO_PTR(ctx->unique_strings);+ int i, j;++ j = 0;+ for(i = 0; i < arr->size; i++) {+ if (gc_mb_is_marked(arr->arr[i])) {+ arr->arr[j++] = arr->arr[i];+ }+ }+ ctx->unique_strings_len = j;+ if (j > 0) {+ arr->gc_mark = 1;+ if (j < arr->size) {+ /* shrink the array */+ set_free_block(&arr->arr[j], (arr->size - j) * sizeof(JSValue));+ arr->size = j;+ }+ } else {+ arr->gc_mark = 0;+ ctx->unique_strings = JS_NULL;+ }+ }++ /* update the weak references in the string position cache */+ {+ int i;+ JSStringPosCacheEntry *ce;+ for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) {+ ce = &ctx->string_pos_cache[i];+ if (!gc_mb_is_marked(ce->str))+ ce->str = JS_NULL;+ }+ }+ + /* reset the gc marks and mark the free blocks as free */+ {+ uint8_t *ptr, *ptr1;+ int size;+ JSFreeBlock *b;++ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ size = get_mblock_size(ptr);+ b = (JSFreeBlock *)ptr;+ if (b->gc_mark) {+ b->gc_mark = 0;+ } else {+ JSObject *p = (void *)ptr;+ /* call the user finalizer if needed */+ if (p->mtag == JS_MTAG_OBJECT && p->class_id >= JS_CLASS_USER &&+ ctx->c_finalizer_table[p->class_id - JS_CLASS_USER] != NULL) {+ ctx->c_finalizer_table[p->class_id - JS_CLASS_USER](ctx, p->u.user.opaque);+ }+ /* merge all the consecutive free blocks */+ ptr1 = ptr + size;+ while (ptr1 < ctx->heap_free && ((JSFreeBlock *)ptr1)->gc_mark == 0) {+ ptr1 += get_mblock_size(ptr1);+ }+ size = ptr1 - ptr;+ set_free_block(b, size);+ }+ ptr += size;+ }+ }+}++static JSValue js_value_from_pval(JSContext *ctx, JSValue *pval)+{+ return JS_VALUE_FROM_PTR(pval);+}++static JSValue *js_value_to_pval(JSContext *ctx, JSValue val)+{+ return JS_VALUE_TO_PTR(val);+}++static void gc_thread_pointer(JSContext *ctx, JSValue *pval)+{+ JSValue val;+ JSValue *ptr;+ + val = *pval;+ if (!JS_IsPtr(val))+ return;+ ptr = JS_VALUE_TO_PTR(val);+ if (JS_IS_ROM_PTR(ctx, ptr))+ return;+ /* gc_mark = 0 indicates a normal memory block header, gc_mark = 1+ indicates a pointer to another element */+ *pval = *ptr;+ *ptr = js_value_from_pval(ctx, pval);+}++static void gc_update_threaded_pointers(JSContext *ctx,+ void *ptr, void *new_ptr)+{+ JSValue val, *pv;++ val = *(JSValue *)ptr;+ if (JS_IsPtr(val)) {+ /* update the threaded pointers to the node 'ptr' and+ unthread it. */+ for(;;) {+ pv = js_value_to_pval(ctx, val);+ val = *pv;+ *pv = JS_VALUE_FROM_PTR(new_ptr);+ if (!JS_IsPtr(val))+ break;+ }+ *(JSValue *)ptr = val;+ }+}++static void gc_thread_block(JSContext *ctx, void *ptr)+{+ int mtag;+ + mtag = ((JSMemBlockHeader *)ptr)->mtag;+ switch(mtag) {+ case JS_MTAG_OBJECT:+ {+ JSObject *p = ptr;+ gc_thread_pointer(ctx, &p->proto);+ gc_thread_pointer(ctx, &p->props);+ switch(p->class_id) {+ case JS_CLASS_CLOSURE:+ {+ int i;+ gc_thread_pointer(ctx, &p->u.closure.func_bytecode);+ for(i = 0; i < p->extra_size - 1; i++)+ gc_thread_pointer(ctx, &p->u.closure.var_refs[i]);+ }+ break;+ case JS_CLASS_C_FUNCTION:+ if (p->extra_size > 1)+ gc_thread_pointer(ctx, &p->u.cfunc.params);+ break;+ case JS_CLASS_ARRAY:+ gc_thread_pointer(ctx, &p->u.array.tab);+ break;+ case JS_CLASS_ERROR:+ gc_thread_pointer(ctx, &p->u.error.message);+ gc_thread_pointer(ctx, &p->u.error.stack);+ break;+ case JS_CLASS_ARRAY_BUFFER:+ gc_thread_pointer(ctx, &p->u.array_buffer.byte_buffer);+ break;+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY:+ gc_thread_pointer(ctx, &p->u.typed_array.buffer);+ break;+ case JS_CLASS_REGEXP:+ gc_thread_pointer(ctx, &p->u.regexp.source);+ gc_thread_pointer(ctx, &p->u.regexp.byte_code);+ break;+ }+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ JSValueArray *p = ptr;+ int i;+ for(i = 0; i < p->size; i++) {+ gc_thread_pointer(ctx, &p->arr[i]);+ }+ }+ break;+ case JS_MTAG_VARREF:+ {+ JSVarRef *p = ptr;+ gc_thread_pointer(ctx, &p->u.value);+ }+ break;+ case JS_MTAG_FUNCTION_BYTECODE:+ {+ JSFunctionBytecode *b = ptr;+ gc_thread_pointer(ctx, &b->func_name);+ gc_thread_pointer(ctx, &b->byte_code);+ gc_thread_pointer(ctx, &b->cpool);+ gc_thread_pointer(ctx, &b->vars);+ gc_thread_pointer(ctx, &b->ext_vars);+ gc_thread_pointer(ctx, &b->filename);+ gc_thread_pointer(ctx, &b->pc2line);+ }+ break;+ default:+ break;+ }+}++/* Heap compaction using Jonkers algorithm */+static void gc_compact_heap(JSContext *ctx)+{+ uint8_t *ptr, *new_ptr;+ int size;+ JSValue *sp, *sp_end;+ + /* thread all the external pointers */+ sp_end = ctx->class_proto + 2 * ctx->class_count;+ for(sp = &ctx->unique_strings; sp < sp_end; sp++) {+ gc_thread_pointer(ctx, sp);+ }+ {+ int i;+ JSStringPosCacheEntry *ce;+ for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) {+ ce = &ctx->string_pos_cache[i];+ gc_thread_pointer(ctx, &ce->str);+ }+ }+ + for(sp = ctx->sp; sp < (JSValue *)ctx->stack_top; sp++) {+ gc_thread_pointer(ctx, sp);+ }++ {+ JSGCRef *ref;+ for(ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) {+ gc_thread_pointer(ctx, &ref->val);+ }+ for(ref = ctx->last_gc_ref; ref != NULL; ref = ref->prev) {+ gc_thread_pointer(ctx, &ref->val);+ }+ }++ if (ctx->parse_state) {+ JSParseState *ps = ctx->parse_state;++ gc_thread_pointer(ctx, &ps->source_str);+ gc_thread_pointer(ctx, &ps->filename_str);+ gc_thread_pointer(ctx, &ps->token.value);+ gc_thread_pointer(ctx, &ps->cur_func);+ gc_thread_pointer(ctx, &ps->byte_code);+ }++ /* pass 1: thread the pointers and update the previous ones */+ new_ptr = ctx->heap_base;+ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ gc_update_threaded_pointers(ctx, ptr, new_ptr);+ size = get_mblock_size(ptr);+ if (js_get_mtag(ptr) != JS_MTAG_FREE) {+ gc_thread_block(ctx, ptr);+ new_ptr += size;+ }+ ptr += size;+ }+ + /* pass 2: update the threaded pointers and move the block to its+ final position */+ new_ptr = ctx->heap_base;+ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ gc_update_threaded_pointers(ctx, ptr, new_ptr);+ size = get_mblock_size(ptr);+ if (js_get_mtag(ptr) != JS_MTAG_FREE) {+ if (new_ptr != ptr) {+ memmove(new_ptr, ptr, size);+ }+ new_ptr += size;+ }+ ptr += size;+ }+ ctx->heap_free = new_ptr;++ /* update the source pointer in the parser */+ if (ctx->parse_state) {+ JSParseState *ps = ctx->parse_state;+ if (JS_IsPtr(ps->source_str)) {+ JSString *p = JS_VALUE_TO_PTR(ps->source_str);+ ps->source_buf = p->buf;+ }+ }+ + /* rehash the object properties */+ /* XXX: try to do it in the previous pass (add a specific tag ?) */+ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ size = get_mblock_size(ptr);+ if (js_get_mtag(ptr) == JS_MTAG_OBJECT) {+ js_rehash_props(ctx, (JSObject *)ptr, TRUE);+ }+ ptr += size;+ }+}++static void JS_GC2(JSContext *ctx, BOOL keep_atoms)+{+#ifdef DUMP_GC+ js_printf(ctx, "GC : heap size=%u/%u stack_size=%u\n",+ (uint32_t)(ctx->heap_free - ctx->heap_base),+ (uint32_t)(ctx->stack_top - ctx->heap_base),+ (uint32_t)(ctx->stack_top - (uint8_t *)ctx->sp));+#endif+#if defined(DEBUG_GC)+ /* reduce the dummy block size at each GC to change the addresses+ after compaction */+ /* XXX: only works a finite number of times */+ {+ JSByteArray *arr;+ if (JS_IsPtr(ctx->dummy_block)) {+ arr = JS_VALUE_TO_PTR(ctx->dummy_block);+ if (arr->size >= 8) {+ js_shrink_byte_array(ctx, &ctx->dummy_block, arr->size - 4);+ if (arr->size == 4) {+ js_printf(ctx, "WARNING: debug GC: no longer modifying the addresses\n");+ }+ }+ }+ }+#endif+ gc_mark_all(ctx, keep_atoms);+ gc_compact_heap(ctx);+#ifdef DUMP_GC+ js_printf(ctx, "AFTER: heap size=%u/%u stack_size=%u\n",+ (uint32_t)(ctx->heap_free - ctx->heap_base),+ (uint32_t)(ctx->stack_top - ctx->heap_base),+ (uint32_t)(ctx->stack_top - (uint8_t *)ctx->sp));+#endif+}++void JS_GC(JSContext *ctx)+{+ JS_GC2(ctx, TRUE);+}++/* bytecode saving and loading */++#define JS_BYTECODE_VERSION_32 0x0001+/* bit 15 of bytecode version is a 64-bit indicator */+#define JS_BYTECODE_VERSION (JS_BYTECODE_VERSION_32 | ((JSW & 8) << 12))++void JS_PrepareBytecode(JSContext *ctx,+ JSBytecodeHeader *hdr,+ const uint8_t **pdata_buf, uint32_t *pdata_len,+ JSValue eval_code)+{+ JSGCRef eval_code_ref;+ int i;+ + /* remove all the objects except the compiled code */+ ctx->empty_props = JS_NULL;+ for(i = 0; i < ctx->class_count; i++) {+ ctx->class_proto[i] = JS_NULL;+ ctx->class_obj[i] = JS_NULL;+ }+ ctx->global_obj = JS_NULL;+#ifdef DEBUG_GC+ ctx->dummy_block = JS_NULL;+#endif+ + JS_PUSH_VALUE(ctx, eval_code);+ JS_GC2(ctx, FALSE);+ JS_POP_VALUE(ctx, eval_code);++ hdr->magic = JS_BYTECODE_MAGIC;+ hdr->version = JS_BYTECODE_VERSION;+ hdr->base_addr = (uintptr_t)ctx->heap_base;+ hdr->unique_strings = ctx->unique_strings;+ hdr->main_func = eval_code;++ *pdata_buf = ctx->heap_base;+ *pdata_len = ctx->heap_free - ctx->heap_base;+}++#if JSW == 8++typedef uint32_t JSValue_32;+typedef uint32_t JSWord_32;++#define JS_MB_HEADER_32 \+ JSWord_32 gc_mark: 1; \+ JSWord_32 mtag: (JS_MTAG_BITS - 1)++#define JS_MB_PAD_32(n) (32 - (n))++typedef struct {+ JS_MB_HEADER_32;+ JSWord_32 dummy: JS_MB_PAD_32(JS_MTAG_BITS);+} JSMemBlockHeader_32;++typedef struct {+ JS_MB_HEADER_32;+ JSWord_32 size: JS_MB_PAD_32(JS_MTAG_BITS);+ JSValue_32 arr[];+} JSValueArray_32;++typedef struct {+ JS_MB_HEADER_32;+ JSWord_32 size: JS_MB_PAD_32(JS_MTAG_BITS);+ uint8_t buf[];+} JSByteArray_32;++typedef struct {+ JS_MB_HEADER_32;+ JSWord_32 dummy: JS_MB_PAD_32(JS_MTAG_BITS);+ /* unaligned 64 bit access in 32-bit mode */+ struct __attribute__((packed)) {+ double dval;+ } u;+} JSFloat64_32;++#define JS_STRING_LEN_MAX_32 ((1 << (32 - JS_MTAG_BITS - 3)) - 1)++typedef struct {+ JS_MB_HEADER_32;+ JSWord_32 is_unique: 1;+ JSWord_32 is_ascii: 1;+ /* true if the string content represents a number, only meaningful+ is is_unique = true */+ JSWord_32 is_numeric: 1;+ JSWord_32 len: JS_MB_PAD_32(JS_MTAG_BITS + 3);+ uint8_t buf[];+} JSString_32;++typedef struct {+ JS_MB_HEADER_32;+ JSWord_32 has_arguments : 1; /* only used during parsing */+ JSWord_32 has_local_func_name : 1; /* only used during parsing */+ JSWord_32 has_column : 1; /* column debug info is present */+ JSWord_32 arg_count : 16;+ JSWord_32 dummy: JS_MB_PAD_32(JS_MTAG_BITS + 3 + 16);++ JSValue_32 func_name; /* JS_NULL if anonymous function */+ JSValue_32 byte_code; /* JS_NULL if the function is not parsed yet */+ JSValue_32 cpool; /* constant pool */+ JSValue_32 vars; /* only for debug */+ JSValue_32 ext_vars; /* records of (var_name, var_kind (2 bits) var_idx (16 bits)) */+ uint16_t stack_size; /* maximum stack size */+ uint16_t ext_vars_len; /* XXX: only used during parsing */+ JSValue_32 filename; /* filename in which the function is defined */+ JSValue_32 pc2line; /* JSByteArray or JS_NULL if not initialized */+ uint32_t source_pos; /* only used during parsing (XXX: shrink) */+} JSFunctionBytecode_32;++/* warning: ptr1 and ptr may overlap. However there is always: ptr1 <= ptr. Return 0 if OK. */+static int convert_mblock_64to32(void *ptr1, const void *ptr)+{+ int mtag, i;++ mtag = ((JSMemBlockHeader*)ptr)->mtag;+ switch(mtag) {+ case JS_MTAG_FUNCTION_BYTECODE:+ {+ const JSFunctionBytecode *b = ptr;+ JSFunctionBytecode_32 *b1 = ptr1;+ b1->gc_mark = b->gc_mark;+ b1->mtag = b->mtag;+ b1->has_arguments = b->has_arguments;+ b1->has_local_func_name = b->has_local_func_name;+ b1->has_column = b->has_column;+ b1->arg_count = b->arg_count;+ b1->dummy = 0;+ b1->func_name = b->func_name;+ b1->byte_code = b->byte_code;+ b1->cpool = b->cpool;+ b1->vars = b->vars;+ b1->ext_vars = b->ext_vars;+ b1->stack_size = b->stack_size;+ b1->ext_vars_len = b->ext_vars_len;+ b1->filename = b->filename;+ b1->pc2line = b->pc2line;+ b1->source_pos = b->source_pos;+ }+ break;+ case JS_MTAG_FLOAT64:+ {+ const JSFloat64 *b = ptr;+ JSFloat64_32 *b1 = ptr1;++ b1->gc_mark = b->gc_mark;+ b1->mtag = b->mtag;+ b1->dummy = 0;+ b1->u.dval = b->u.dval;+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ const JSValueArray *b = ptr;+ JSValueArray_32 *b1 = ptr1;++ b1->gc_mark = b->gc_mark;+ b1->mtag = b->mtag;+ b1->size = b->size; /* no test needed as long as JS_VALUE_ARRAY_SIZE_MAX is identical */+ for(i = 0; i < b1->size; i++)+ b1->arr[i] = b->arr[i];+ }+ break;+ case JS_MTAG_BYTE_ARRAY:+ {+ const JSByteArray *b = ptr;+ JSByteArray_32 *b1 = ptr1;++ b1->gc_mark = b->gc_mark;+ b1->mtag = b->mtag;+ b1->size = b->size; /* no test needed as long as JS_BYTE_ARRAY_SIZE_MAX is identical */+ memmove(b1->buf, b->buf, b1->size);+ }+ break;+ case JS_MTAG_STRING:+ {+ const JSString *b = ptr;+ JSString_32 *b1 = ptr1;++ if (b->len > JS_STRING_LEN_MAX_32)+ return -1;+ b1->gc_mark = b->gc_mark;+ b1->mtag = b->mtag;+ b1->is_unique = b->is_unique;+ b1->is_ascii = b->is_ascii;+ b1->is_numeric = b->is_numeric;+ b1->len = b->len;+ memmove(b1->buf, b->buf, b1->len + 1);+ }+ break;+ default:+ abort();+ }+ return 0;+}++/* return the size in bytes */+static int get_mblock_size_32(const void *ptr)+{+ int mtag = ((JSMemBlockHeader_32 *)ptr)->mtag;+ int size;+ switch(mtag) {+ case JS_MTAG_FLOAT64:+ size = sizeof(JSFloat64_32);+ break;+ case JS_MTAG_STRING:+ {+ const JSString_32 *p = ptr;+ size = sizeof(JSString_32) + ((p->len + 4) & ~(4 - 1));+ }+ break;+ case JS_MTAG_BYTE_ARRAY:+ {+ const JSByteArray_32 *p = ptr;+ size = sizeof(JSByteArray_32) + ((p->size + 4 - 1) & ~(4 - 1));+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ const JSValueArray_32 *p = ptr;+ size = sizeof(JSValueArray_32) + p->size * sizeof(p->arr[0]);+ }+ break;+ case JS_MTAG_FUNCTION_BYTECODE:+ size = sizeof(JSFunctionBytecode_32);+ break;+ default:+ size = 0;+ assert(0);+ }+ return size;+}++/* Compact and convert a 64 bit heap to a 32 bit heap at offset+ 0. Only used for code compilation. Return 0 if OK. */+static int gc_compact_heap_64to32(JSContext *ctx)+{+ uint8_t *ptr;+ int size, size_32;+ uintptr_t new_offset;+ + gc_thread_pointer(ctx, &ctx->unique_strings);++ /* thread all the external pointers */+ {+ JSGCRef *ref;+ /* necessary because JS_PUSH_VAL() is called before+ gc_compact_heap_64to32() */+ for(ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) {+ gc_thread_pointer(ctx, &ref->val);+ }+ }++ /* pass 1: thread the pointers and update the previous ones */+ new_offset = 0;+ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ gc_update_threaded_pointers(ctx, ptr, (uint8_t *)new_offset);+ size = get_mblock_size(ptr);+ if (js_get_mtag(ptr) != JS_MTAG_FREE) {+ gc_thread_block(ctx, ptr);+ size_32 = get_mblock_size_32(ptr);+ new_offset += size_32;+ }+ ptr += size;+ }+ + /* pass 2: update the threaded pointers and move the block to its+ final position */+ new_offset = 0;+ ptr = ctx->heap_base;+ while (ptr < ctx->heap_free) {+ gc_update_threaded_pointers(ctx, ptr, (uint8_t *)new_offset);+ size = get_mblock_size(ptr);+ if (js_get_mtag(ptr) != JS_MTAG_FREE) {+ size_32 = get_mblock_size_32(ptr);+ if (convert_mblock_64to32(ctx->heap_base + new_offset, ptr))+ return -1;+ new_offset += size_32;+ }+ ptr += size;+ }+ ctx->heap_free = ctx->heap_base + new_offset;+ return 0;+}++#ifdef JS_USE_SHORT_FLOAT++static int expand_short_float(JSContext *ctx, JSValue *pval)+{+ JSFloat64 *f;+ if (JS_IsShortFloat(*pval)) {+ f = js_malloc(ctx, sizeof(JSFloat64), JS_MTAG_FLOAT64);+ if (!f)+ return -1;+ f->u.dval = js_get_short_float(*pval);+ *pval = JS_VALUE_FROM_PTR(f);+ }+ return 0;+}++/* Expand all the short floats to JSFloat64 structures. Return < 0 if+ not enough memory. */+static int expand_short_floats(JSContext *ctx)+{+ uint8_t *ptr, *p_end;+ int mtag, size;+ + ptr = ctx->heap_base;+ p_end = ctx->heap_free;+ while (ptr < p_end) {+ size = get_mblock_size(ptr);+ mtag = ((JSMemBlockHeader *)ptr)->mtag;+ switch(mtag) {+ case JS_MTAG_FUNCTION_BYTECODE:+ /* we assume no short floats here */+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ JSValueArray *p = (JSValueArray *)ptr;+ int i;+ for(i = 0; i < p->size; i++) {+ if (expand_short_float(ctx, &p->arr[i]))+ return -1;+ }+ }+ break;+ case JS_MTAG_STRING:+ case JS_MTAG_FLOAT64:+ case JS_MTAG_BYTE_ARRAY:+ break;+ default:+ abort();+ }+ ptr += size;+ }+ return 0;+}++#endif /* JS_USE_SHORT_FLOAT */++int JS_PrepareBytecode64to32(JSContext *ctx,+ JSBytecodeHeader32 *hdr,+ const uint8_t **pdata_buf, uint32_t *pdata_len,+ JSValue eval_code)+{+ JSGCRef eval_code_ref;+ int i;+ + /* remove all the objects except the compiled code */+ ctx->empty_props = JS_NULL;+ for(i = 0; i < ctx->class_count; i++) {+ ctx->class_proto[i] = JS_NULL;+ ctx->class_obj[i] = JS_NULL;+ }+ ctx->global_obj = JS_NULL;+#ifdef DEBUG_GC+ ctx->dummy_block = JS_NULL;+#endif+ + JS_PUSH_VALUE(ctx, eval_code);+#ifdef JS_USE_SHORT_FLOAT+ JS_GC2(ctx, FALSE);+ if (expand_short_floats(ctx))+ return -1;+#else+ gc_mark_all(ctx, FALSE);+#endif + if (gc_compact_heap_64to32(ctx))+ return -1;+ JS_POP_VALUE(ctx, eval_code);++ hdr->magic = JS_BYTECODE_MAGIC;+ hdr->version = JS_BYTECODE_VERSION_32;+ hdr->base_addr = 0;+ hdr->unique_strings = ctx->unique_strings;+ hdr->main_func = eval_code;++ *pdata_buf = ctx->heap_base;+ *pdata_len = ctx->heap_free - ctx->heap_base;+ return 0;+}+#endif /* JSW == 8 */++BOOL JS_IsBytecode(const uint8_t *buf, size_t buf_len)+{+ const JSBytecodeHeader *hdr = (const JSBytecodeHeader *)buf;+ return (buf_len >= sizeof(*hdr) && hdr->magic == JS_BYTECODE_MAGIC);+}++typedef struct {+ JSContext *ctx;+ uintptr_t offset;+ BOOL update_atoms;+} BCRelocState;++static void bc_reloc_value(BCRelocState *s, JSValue *pval)+{+ JSContext *ctx = s->ctx;+ JSString *p;+ JSValue val, str;++ val = *pval;+ if (JS_IsPtr(val)) {+ val += s->offset;++ /* unique strings must be unique, so modify the unique string+ value if it already exists in the context */+ if (s->update_atoms) {+ p = JS_VALUE_TO_PTR(val);+ if (p->mtag == JS_MTAG_STRING && p->is_unique) {+ const JSValueArray *arr1;+ int a, i;+ for(i = 0; i < ctx->n_rom_atom_tables; i++) {+ arr1 = ctx->rom_atom_tables[i];+ str = find_atom(ctx, &a, arr1, arr1->size, val); + if (!JS_IsNull(str)) {+ val = str;+ break;+ }+ }+ }+ }+ *pval = val;+ }+}++int JS_RelocateBytecode2(JSContext *ctx, JSBytecodeHeader *hdr,+ uint8_t *buf, uint32_t buf_len,+ uintptr_t new_base_addr, BOOL update_atoms)+{+ uint8_t *ptr, *p_end;+ int size, mtag;+ BCRelocState ss, *s = &ss;+ + if (hdr->magic != JS_BYTECODE_MAGIC)+ return -1;+ if (hdr->version != JS_BYTECODE_VERSION)+ return -1;++ /* XXX: add atom checksum to avoid problems if the stdlib is+ modified */+ s->ctx = ctx;+ s->offset = new_base_addr - hdr->base_addr;+ s->update_atoms = update_atoms;++ bc_reloc_value(s, &hdr->unique_strings);+ bc_reloc_value(s, &hdr->main_func);++ ptr = buf;+ p_end = buf + buf_len;+ while (ptr < p_end) {+ size = get_mblock_size(ptr);+ mtag = ((JSMemBlockHeader *)ptr)->mtag;+ switch(mtag) {+ case JS_MTAG_FUNCTION_BYTECODE:+ {+ JSFunctionBytecode *b = (JSFunctionBytecode *)ptr;+ bc_reloc_value(s, &b->func_name);+ bc_reloc_value(s, &b->byte_code);+ bc_reloc_value(s, &b->cpool);+ bc_reloc_value(s, &b->vars);+ bc_reloc_value(s, &b->ext_vars);+ bc_reloc_value(s, &b->filename);+ bc_reloc_value(s, &b->pc2line);+ }+ break;+ case JS_MTAG_VALUE_ARRAY:+ {+ JSValueArray *p = (JSValueArray *)ptr;+ int i;+ for(i = 0; i < p->size; i++) {+ bc_reloc_value(s, &p->arr[i]);+ }+ }+ break;+ case JS_MTAG_STRING:+ case JS_MTAG_FLOAT64:+ case JS_MTAG_BYTE_ARRAY:+ break;+ default:+ abort();+ }+ ptr += size;+ }+ hdr->base_addr = new_base_addr;+ return 0;+}++/* Relocate the bytecode in 'buf' so that it can be executed+ later. Return 0 if OK, != 0 if error */+int JS_RelocateBytecode(JSContext *ctx,+ uint8_t *buf, uint32_t buf_len)+{+ uint8_t *data_ptr;++ if (buf_len < sizeof(JSBytecodeHeader))+ return -1;+ data_ptr = buf + sizeof(JSBytecodeHeader);+ return JS_RelocateBytecode2(ctx, (JSBytecodeHeader *)buf,+ data_ptr,+ buf_len - sizeof(JSBytecodeHeader),+ (uintptr_t)data_ptr, TRUE);+}++/* Load the precompiled bytecode from 'buf'. 'buf' must be allocated+ as long as the JSContext exists. Use JS_Run() to execute+ it. warning: the bytecode is not checked so it should come from a+ trusted source. */+JSValue JS_LoadBytecode(JSContext *ctx, const uint8_t *buf)+{+ const JSBytecodeHeader *hdr = (const JSBytecodeHeader *)buf;+ + if (ctx->unique_strings_len != 0)+ return JS_ThrowInternalError(ctx, "no atom must be defined in RAM");+ /* XXX: could stack atom_tables */+ if (ctx->n_rom_atom_tables >= N_ROM_ATOM_TABLES_MAX)+ return JS_ThrowInternalError(ctx, "too many rom atom tables");+ if (hdr->magic != JS_BYTECODE_MAGIC)+ return JS_ThrowInternalError(ctx, "invalid bytecode magic");+ if ((hdr->version & 0x8000) != (JS_BYTECODE_VERSION & 0x8000))+ return JS_ThrowInternalError(ctx, "bytecode not saved for %d-bit", JSW * 8);+ if (hdr->version != JS_BYTECODE_VERSION)+ return JS_ThrowInternalError(ctx, "invalid bytecode version");+ if (hdr->base_addr != (uintptr_t)(hdr + 1))+ return JS_ThrowInternalError(ctx, "bytecode not relocated");+ ctx->rom_atom_tables[ctx->n_rom_atom_tables++] = (JSValueArray *)JS_VALUE_TO_PTR(hdr->unique_strings);+ return hdr->main_func;+}++/**********************************************************************/+/* runtime */++JSValue js_function_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ StringBuffer b_s, *b = &b_s;+ JSValue val;+ int i, n;+ + argc &= ~FRAME_CF_CTOR;+ string_buffer_push(ctx, b, 0);+ string_buffer_puts(ctx, b, "(function anonymous(");+ n = argc - 1;+ for(i = 0; i < n; i++) {+ if (i != 0) {+ string_buffer_putc(ctx, b, ',');+ }+ if (string_buffer_concat(ctx, b, argv[i]))+ goto done;+ }+ string_buffer_puts(ctx, b, "\n) {\n");+ if (n >= 0) {+ if (string_buffer_concat(ctx, b, argv[n]))+ goto done;+ }+ string_buffer_puts(ctx, b, "\n})");+ done:+ val = string_buffer_pop(ctx, b);+ if (JS_IsException(val))+ return val;+ val = JS_Parse2(ctx, val, NULL, 0, "<input>", JS_EVAL_RETVAL);+ if (JS_IsException(val))+ return val;+ return JS_Run(ctx, val);+}++JSValue js_function_get_prototype(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue obj;+ JSGCRef obj_ref;+ + if (!JS_IsPtr(*this_val)) {+ if (JS_VALUE_GET_SPECIAL_TAG(*this_val) != JS_TAG_SHORT_FUNC)+ goto fail;+ return JS_UNDEFINED;+ } else {+ JSObject *p = JS_VALUE_TO_PTR(*this_val);+ if (p->mtag != JS_MTAG_OBJECT)+ goto fail;+ if (p->class_id == JS_CLASS_CLOSURE) {+ obj = JS_NewObject(ctx);+ if (JS_IsException(obj))+ return obj;+ } else if (p->class_id == JS_CLASS_C_FUNCTION) {+ /* for C constructors, the prototype property is already present */+ return JS_UNDEFINED;+ } else {+ fail:+ return JS_ThrowTypeError(ctx, "not a function");+ }+ JS_PUSH_VALUE(ctx, obj);+ JS_DefinePropertyValue(ctx, obj, js_get_atom(ctx, JS_ATOM_constructor),+ *this_val);+ JS_POP_VALUE(ctx, obj);+ JS_PUSH_VALUE(ctx, obj);+ JS_DefinePropertyValue(ctx, *this_val, js_get_atom(ctx, JS_ATOM_prototype),+ obj);+ JS_POP_VALUE(ctx, obj);+ }+ return obj;+}++JSValue js_function_set_prototype(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ if (!JS_IsFunctionObject(ctx, *this_val))+ return JS_ThrowTypeError(ctx, "not a function");++ JS_DefinePropertyValue(ctx, *this_val, js_get_atom(ctx, JS_ATOM_prototype),+ argv[0]);+ return JS_UNDEFINED;+}++JSValue js_function_get_length_name(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_name)+{+ JSFunctionBytecode *b;+ JSValue ret = js_function_get_length_name1(ctx, this_val, is_name, &b);+ if (JS_IsNull(ret))+ return JS_ThrowTypeError(ctx, "not a function");+ return ret;+}++JSValue js_function_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue str, val;+ JSGCRef str_ref;++ str = js_function_get_length_name(ctx, this_val, 0, NULL, 1);+ if (JS_IsException(str))+ return str;+ JS_PUSH_VALUE(ctx, str);+ val = JS_NewString(ctx, "function ");+ JS_POP_VALUE(ctx, str);+ str = JS_ConcatString(ctx, val, str);+ JS_PUSH_VALUE(ctx, str);+ val = JS_NewString(ctx, "() {\n [native code]\n}");+ JS_POP_VALUE(ctx, str);+ return JS_ConcatString(ctx, str, val);+}++JSValue js_function_call(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int i;+ argc = max_int(argc, 1);+ if (JS_StackCheck(ctx, argc + 1))+ return JS_EXCEPTION;+ for(i = 0; i < argc - 1; i++)+ JS_PushArg(ctx, argv[argc - 1 - i]);+ JS_PushArg(ctx, *this_val);+ JS_PushArg(ctx, argv[0]);+ /* we avoid recursing on the C stack */+ return JS_NewTailCall(argc - 1);+}++JSValue js_function_apply(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValueArray *arr;+ JSObject *p;+ int len, i;+ p = js_get_object_class(ctx, argv[1], JS_CLASS_ARRAY);+ if (!p)+ return JS_ThrowTypeError(ctx, "not an array");+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ len = p->u.array.len;+ if (len > JS_MAX_ARGC)+ return JS_ThrowTypeError(ctx, "too many call arguments");+ if (JS_StackCheck(ctx, len + 2))+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(argv[1]);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ for(i = 0; i < len; i++)+ JS_PushArg(ctx, arr->arr[len - 1 - i]);+ JS_PushArg(ctx, *this_val);+ JS_PushArg(ctx, argv[0]);+ /* we avoid recursing on the C stack */+ return JS_NewTailCall(len);+}++JSValue js_function_bind(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int arg_count;+ JSValueArray *arr;+ int i;+ + arg_count = max_int(argc - 1, 0);+ arr = js_alloc_value_array(ctx, 0, 2 + arg_count);+ if (!arr)+ return JS_EXCEPTION;+ /* arr[0] = func, arr[1] = this */+ arr->arr[0] = *this_val;+ for(i = 0; i < arg_count + 1; i++)+ arr->arr[1 + i] = argv[i];+ return JS_NewCFunctionParams(ctx, JS_CFUNCTION_bound, JS_VALUE_FROM_PTR(arr));+}++/* XXX: handle constructor case */+JSValue js_function_bound(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, JSValue params)+{+ JSValueArray *arr;+ JSGCRef params_ref;+ int i, err, size, argc2;+ + arr = JS_VALUE_TO_PTR(params);+ size = arr->size;+ JS_PUSH_VALUE(ctx, params);+ err = JS_StackCheck(ctx, size + argc);+ JS_POP_VALUE(ctx, params);+ if (err)+ return JS_EXCEPTION;+ argc2 = size - 2 + argc;+ if (argc2 > JS_MAX_ARGC)+ return JS_ThrowTypeError(ctx, "too many call arguments");+ arr = JS_VALUE_TO_PTR(params);+ for(i = argc - 1; i >= 0; i--)+ JS_PushArg(ctx, argv[i]);+ for(i = size - 1; i >= 2; i--) {+ JS_PushArg(ctx, arr->arr[i]);+ }+ JS_PushArg(ctx, arr->arr[0]); /* func */+ JS_PushArg(ctx, arr->arr[1]); /* this_val */+ /* we avoid recursing on the C stack */+ return JS_NewTailCall(argc2);+}++/**********************************************************************/++JSValue js_number_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double d;+ if (argc & FRAME_CF_CTOR)+ return JS_ThrowTypeError(ctx, "number constructor not supported");+ if (argc == 0) {+ return JS_NewShortInt(0);+ } else {+ if (JS_ToNumber(ctx, &d, argv[0]))+ return JS_EXCEPTION;+ return JS_NewFloat64(ctx, d);+ }+}++static int js_thisNumberValue(JSContext *ctx, double *pres, JSValue val)+{+ if (!JS_IsNumber(ctx, val)) {+ JS_ThrowTypeError(ctx, "not a number");+ return -1;+ }+ return JS_ToNumber(ctx, pres, val);+}++JSValue js_number_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int radix, flags;+ double d;+ + if (js_thisNumberValue(ctx, &d, *this_val))+ return JS_EXCEPTION;+ if (JS_IsUndefined(argv[0])) {+ radix = 10;+ } else {+ if (JS_ToInt32Sat(ctx, &radix, argv[0]))+ return JS_EXCEPTION;+ if (radix < 2 || radix > 36)+ return JS_ThrowRangeError(ctx, "radix must be between 2 and 36");+ }+ /* cannot fail */+ flags = JS_DTOA_FORMAT_FREE;+ if (radix != 10)+ flags |= JS_DTOA_EXP_DISABLED;+ return js_dtoa2(ctx, d, radix, 0, flags);+}++JSValue js_number_toFixed(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int f, flags;+ double d;++ if (js_thisNumberValue(ctx, &d, *this_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) {+ flags = JS_DTOA_FORMAT_FREE;+ } else {+ flags = JS_DTOA_FORMAT_FRAC;+ }+ return js_dtoa2(ctx, d, 10, f, flags);+}++JSValue js_number_toExponential(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int f, flags;+ double d;++ if (js_thisNumberValue(ctx, &d, *this_val))+ return JS_EXCEPTION;+ if (JS_ToInt32Sat(ctx, &f, argv[0]))+ return JS_EXCEPTION;+ if (JS_IsUndefined(argv[0]) || !isfinite(d)) {+ f = 0;+ flags = JS_DTOA_FORMAT_FREE;+ } else {+ if (f < 0 || f > 100)+ return JS_ThrowRangeError(ctx, "invalid number of digits");+ f++;+ flags = JS_DTOA_FORMAT_FIXED;+ }+ return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED);+}++JSValue js_number_toPrecision(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int p, flags;+ double d;++ if (js_thisNumberValue(ctx, &d, *this_val))+ return JS_EXCEPTION;+ if (JS_IsUndefined(argv[0])) {+ flags = JS_DTOA_FORMAT_FREE;+ p = 0;+ } else {+ if (JS_ToInt32Sat(ctx, &p, argv[0]))+ return JS_EXCEPTION;+ if (!isfinite(d)) {+ flags = JS_DTOA_FORMAT_FREE;+ } else {+ if (p < 1 || p > 100)+ return JS_ThrowRangeError(ctx, "invalid number of digits");+ flags = JS_DTOA_FORMAT_FIXED;+ }+ }+ return js_dtoa2(ctx, d, 10, p, flags);+}++JSValue js_number_parseInt(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int radix;+ double d;+ + argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;+ if (JS_ToInt32(ctx, &radix, argv[1]))+ return JS_EXCEPTION;+ if (radix != 0 && (radix < 2 || radix > 36)) {+ d = NAN;+ } else {+ if (js_atod1(ctx, &d, argv[0], radix, JS_ATOD_INT_ONLY))+ return JS_EXCEPTION;+ }+ return JS_NewFloat64(ctx, d);+}++JSValue js_number_parseFloat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double d;+ + argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;+ if (js_atod1(ctx, &d, argv[0], 10, 0))+ return JS_EXCEPTION;+ return JS_NewFloat64(ctx, d);+}++/**********************************************************************/++JSValue js_boolean_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ if (argc & FRAME_CF_CTOR)+ return JS_ThrowTypeError(ctx, "Boolean constructor not supported");+ return JS_NewBool(JS_ToBool(ctx, argv[0]));+}++/**********************************************************************/++JSValue js_string_get_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int len;+ + if (!JS_IsString(ctx, *this_val))+ return JS_ThrowTypeError(ctx, "not a string");+ len = js_string_len(ctx, *this_val);+ return JS_NewShortInt(len);+}++JSValue js_string_set_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ return JS_UNDEFINED; /* ignored */+}++JSValue js_string_slice(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int len, start, end;+ + *this_val = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(*this_val))+ return JS_EXCEPTION;+ len = js_string_len(ctx, *this_val);+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))+ return JS_EXCEPTION;+ end = len;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len))+ return JS_EXCEPTION;+ }+ return js_sub_string(ctx, *this_val, start, max_int(end, start));+}++JSValue js_string_substring(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int a, b, start, end, len;++ *this_val = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(*this_val))+ return JS_EXCEPTION;+ len = js_string_len(ctx, *this_val);+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, 0))+ return JS_EXCEPTION;+ b = len;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, len, 0))+ return JS_EXCEPTION;+ }+ if (a < b) {+ start = a;+ end = b;+ } else {+ start = b;+ end = a;+ }+ return js_sub_string(ctx, *this_val, start, end);+}++JSValue js_string_charAt(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ JSValue ret;+ int idx, c;+ + *this_val = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(*this_val))+ return JS_EXCEPTION;+ if (JS_ToInt32Sat(ctx, &idx, argv[0]))+ return JS_EXCEPTION;+ if (idx < 0)+ goto ret_undef;+ c = string_getcp(ctx, *this_val, idx, (magic == magic_codePointAt));+ if (c == -1) {+ ret_undef:+ if (magic == magic_charCodeAt)+ ret = JS_NewFloat64(ctx, NAN);+ else if (magic == magic_charAt)+ ret = js_get_atom(ctx, JS_ATOM_empty);+ else+ ret = JS_UNDEFINED;+ } else {+ if (magic == magic_charCodeAt || magic == magic_codePointAt)+ ret = JS_NewShortInt(c);+ else+ ret = JS_NewStringChar(c);+ }+ // dump_string_pos_cache(ctx); + return ret;+}++JSValue js_string_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ if (argc & FRAME_CF_CTOR)+ return JS_ThrowTypeError(ctx, "string constructor not supported");+ if (argc <= 0) {+ return js_get_atom(ctx, JS_ATOM_empty);+ } else {+ return JS_ToString(ctx, argv[0]);+ }+}++JSValue js_string_fromCharCode(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_fromCodePoint)+{+ int i;+ StringBuffer b_s, *b = &b_s;++ string_buffer_push(ctx, b, 0);+ for(i = 0; i < argc; i++) {+ int c;+ if (JS_ToInt32(ctx, &c, argv[i]))+ goto fail;+ if (is_fromCodePoint) {+ if (c < 0 || c > 0x10ffff) {+ JS_ThrowRangeError(ctx, "invalid code point");+ goto fail;+ }+ } else {+ c &= 0xffff;+ }+ if (string_buffer_putc(ctx, b, c))+ break;+ }+ return string_buffer_pop(ctx, b);+ fail:+ string_buffer_pop(ctx, b);+ return JS_EXCEPTION;+}++JSValue js_string_concat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int i;+ StringBuffer b_s, *b = &b_s;+ JSValue r;+ + r = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(r))+ return JS_EXCEPTION;+ string_buffer_push(ctx, b, 0);+ if (string_buffer_concat(ctx, b, r))+ goto done;++ for (i = 0; i < argc; i++) {+ if (string_buffer_concat(ctx, b, argv[i]))+ goto done;+ }+ done:+ return string_buffer_pop(ctx, b);+}++JSValue js_string_indexOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int lastIndexOf)+{+ int i, len, v_len, pos, start, stop, ret, inc, j;++ *this_val = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(*this_val))+ return JS_EXCEPTION;+ argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;+ len = js_string_len(ctx, *this_val);+ v_len = js_string_len(ctx, argv[0]);+ if (lastIndexOf) {+ pos = len - v_len;+ if (argc > 1) {+ double d;+ if (JS_ToNumber(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) {+ for(j = 0; j < v_len; j++) {+ if (string_getc(ctx, *this_val, i + j) != string_getc(ctx, argv[0], j)) {+ goto next;+ }+ }+ ret = i;+ break;+ next:+ if (i == stop)+ break;+ }+ }+ return JS_NewShortInt(ret);++fail:+ return JS_EXCEPTION;+}++static int js_string_indexof(JSContext *ctx, JSValue str, JSValue needle,+ int start, int str_len, int needle_len)+{+ int i, j;+ for(i = start; i <= str_len - needle_len; i++) {+ for(j = 0; j < needle_len; j++) {+ if (string_getc(ctx, str, i + j) !=+ string_getc(ctx, needle, j)) {+ goto next;+ }+ + }+ return i;+ next: ;+ }+ return -1;+}++/* Note: ascii only */+JSValue js_string_toLowerCase(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int to_lower)+{+ StringBuffer b_s, *b = &b_s;+ int i, c, len;++ *this_val = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(*this_val))+ return *this_val;+ len = js_string_len(ctx, *this_val);+ string_buffer_push(ctx, b, len);+ for(i = 0; i < len; i++) {+ c = string_getc(ctx, *this_val, i);+ if (to_lower) {+ if (c >= 'A' && c <= 'Z')+ c += 'a' - 'A';+ } else {+ if (c >= 'a' && c <= 'z')+ c += 'A' - 'a';+ }+ string_buffer_putc(ctx, b, c);+ }+ return string_buffer_pop(ctx, b);+}++/* c < 128 */+static force_inline BOOL unicode_is_space_ascii(uint32_t c)+{+ return (c >= 0x0009 && c <= 0x000D) || (c == 0x0020);+}++static BOOL unicode_is_space_non_ascii(uint32_t c)+{+ return (c == 0x00A0 ||+ c == 0x1680 ||+ (c >= 0x2000 && c <= 0x200A) ||+ (c >= 0x2028 && c <= 0x2029) ||+ c == 0x202F ||+ c == 0x205F ||+ c == 0x3000 ||+ c == 0xFEFF);+}++static force_inline BOOL unicode_is_space(uint32_t c)+{+ if (likely(c < 128)) {+ return unicode_is_space_ascii(c);+ } else {+ return unicode_is_space_non_ascii(c);+ }+}++JSValue js_string_trim(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ int a, b, len;++ *this_val = JS_ToStringCheckObject(ctx, *this_val);+ if (JS_IsException(*this_val))+ return *this_val;+ len = js_string_len(ctx, *this_val);+ a = 0;+ b = len;+ if (magic & 1) {+ while (a < len && unicode_is_space(string_getc(ctx, *this_val, a)))+ a++;+ }+ if (magic & 2) {+ while (b > a && unicode_is_space(string_getc(ctx, *this_val, b - 1)))+ b--;+ }+ return js_sub_string(ctx, *this_val, a, b);+}++JSValue js_string_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ if (!JS_IsString(ctx, *this_val))+ return JS_ThrowTypeError(ctx, "not a string");+ return *this_val;+}++JSValue js_string_repeat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ StringBuffer b_s, *b = &b_s;+ JSStringCharBuf buf;+ JSString *p;+ int n;+ int64_t len;+ + if (!JS_IsString(ctx, *this_val))+ return JS_ThrowTypeError(ctx, "not a string");+ if (JS_ToInt32Sat(ctx, &n, argv[0]))+ return -1;+ p = get_string_ptr(ctx, &buf, *this_val);+ if (n < 0 || (len = (int64_t)n * p->len) > JS_STRING_LEN_MAX)+ return JS_ThrowRangeError(ctx, "invalid repeat count");+ if (p->len == 0 || n == 1)+ return *this_val;+ if (string_buffer_push(ctx, b, len))+ return JS_EXCEPTION;+ while (n-- > 0) {+ string_buffer_concat_str(ctx, b, *this_val);+ }+ return string_buffer_pop(ctx, b);+}++/**********************************************************************/++JSValue js_object_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ /* XXX: incomplete */+ argc &= ~FRAME_CF_CTOR;+ if (argc <= 0) {+ return JS_NewObject(ctx);+ } else {+ return argv[0];+ }+}++JSValue js_object_defineProperty(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue *pobj, *pprop, *pdesc;+ JSValue val, getter, setter;+ JSGCRef val_ref, getter_ref;+ int flags;+ + pobj = &argv[0];+ pprop = &argv[1];+ pdesc = &argv[2];++ if (!JS_IsObject(ctx, *pobj))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ *pprop = JS_ToPropertyKey(ctx, *pprop);+ if (JS_IsException(*pprop))+ return JS_EXCEPTION;+ val = JS_UNDEFINED;+ getter = JS_UNDEFINED;+ setter = JS_UNDEFINED;+ flags = 0;+ if (JS_HasProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_value))) {+ flags |= JS_DEF_PROP_HAS_VALUE;+ val = JS_GetProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_value));+ if (JS_IsException(val))+ return JS_EXCEPTION;+ }+ if (JS_HasProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_get))) {+ flags |= JS_DEF_PROP_HAS_GET;+ JS_PUSH_VALUE(ctx, val);+ getter = JS_GetProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_get));+ JS_POP_VALUE(ctx, val);+ if (JS_IsException(getter))+ return JS_EXCEPTION;+ if (!JS_IsUndefined(getter) && !JS_IsFunction(ctx, getter))+ goto bad_getset;+ }+ if (JS_HasProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_set))) {+ flags |= JS_DEF_PROP_HAS_SET;+ JS_PUSH_VALUE(ctx, val);+ JS_PUSH_VALUE(ctx, getter);+ setter = JS_GetProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_set));+ JS_POP_VALUE(ctx, getter);+ JS_POP_VALUE(ctx, val);+ if (JS_IsException(setter))+ return JS_EXCEPTION;+ if (!JS_IsUndefined(setter) && !JS_IsFunction(ctx, setter)) {+ bad_getset:+ return JS_ThrowTypeError(ctx, "invalid getter or setter");+ }+ }+ if (flags & (JS_DEF_PROP_HAS_GET | JS_DEF_PROP_HAS_SET)) {+ if (flags & JS_DEF_PROP_HAS_VALUE)+ return JS_ThrowTypeError(ctx, "cannot have both value and get/set");+ val = getter;+ }+ val = JS_DefinePropertyInternal(ctx, *pobj, *pprop, val, setter,+ flags | JS_DEF_PROP_LOOKUP);+ if (JS_IsException(val))+ return val;+ return *pobj;+}++JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ if (!JS_IsObject(ctx, argv[0]))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ p = JS_VALUE_TO_PTR(argv[0]);+ return p->proto;+}++/* 'obj' must be an object. 'proto' must be JS_NULL or an object */+static JSValue js_set_prototype_internal(JSContext *ctx, JSValue obj, JSValue proto)+{+ JSObject *p, *p1;++ p = JS_VALUE_TO_PTR(obj);+ if (p->proto != proto) {+ if (proto != JS_NULL) {+ /* check if there is a cycle */+ p1 = JS_VALUE_TO_PTR(proto);+ for(;;) {+ if (p1 == p)+ return JS_ThrowTypeError(ctx, "circular prototype chain");+ if (p1->proto == JS_NULL)+ break;+ p1 = JS_VALUE_TO_PTR(p1->proto);+ }+ }+ + p->proto = proto;+ }+ return JS_UNDEFINED;+}++JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue proto;+ + if (!JS_IsObject(ctx, argv[0]))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ proto = argv[1];+ if (proto != JS_NULL && !JS_IsObject(ctx, proto))+ return JS_ThrowTypeError(ctx, "not a prototype");+ if (JS_IsException(js_set_prototype_internal(ctx, argv[0], proto)))+ return JS_EXCEPTION;+ return argv[0];+}++JSValue js_object_create(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue proto;+ proto = argv[0];+ if (proto != JS_NULL && !JS_IsObject(ctx, proto))+ return JS_ThrowTypeError(ctx, "not a prototype");+ if (argc >= 2)+ return JS_ThrowTypeError(ctx, "unsupported additional properties");+ return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT, 0);+}++JSValue js_object_keys(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p, *pret;+ JSValue ret, str;+ JSValueArray *arr, *ret_arr;+ int array_len, prop_count, hash_mask, alloc_size, i, j, pos;+ JSGCRef ret_ref;++ if (!JS_IsObject(ctx, argv[0]))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ p = JS_VALUE_TO_PTR(argv[0]);++ if (p->class_id == JS_CLASS_ARRAY) {+ array_len = p->u.array.len;+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ array_len = p->u.typed_array.len;+ } else {+ array_len = 0;+ }+ + arr = JS_VALUE_TO_PTR(p->props);+ prop_count = JS_VALUE_GET_INT(arr->arr[0]);+ hash_mask = JS_VALUE_GET_INT(arr->arr[1]);++ alloc_size = array_len + prop_count;+ + ret = JS_NewArray(ctx, alloc_size);+ if (JS_IsException(ret))+ return ret;++ pos = 0;+ for(i = 0; i < array_len; i++) {+ JS_PUSH_VALUE(ctx, ret);+ str = JS_ToString(ctx, JS_NewShortInt(i));+ JS_POP_VALUE(ctx, ret);+ if (JS_IsException(str))+ return str;+ pret = JS_VALUE_TO_PTR(ret);+ ret_arr = JS_VALUE_TO_PTR(pret->u.array.tab);+ ret_arr->arr[pos++] = str;+ }+ + for(i = 0, j = 0; j < prop_count; i++) {+ JSProperty *pr;+ p = JS_VALUE_TO_PTR(argv[0]);+ arr = JS_VALUE_TO_PTR(p->props);+ pr = (JSProperty *)&arr->arr[2 + hash_mask + 1 + 3 * i];+ /* exclude deleted properties */+ if (pr->key != JS_UNINITIALIZED) {+ JS_PUSH_VALUE(ctx, ret);+ str = JS_ToString(ctx, pr->key);+ JS_POP_VALUE(ctx, ret);+ if (JS_IsException(str))+ return str;+ pret = JS_VALUE_TO_PTR(ret);+ ret_arr = JS_VALUE_TO_PTR(pret->u.array.tab);+ ret_arr->arr[pos++] = str;+ j++;+ }+ }+ pret = JS_VALUE_TO_PTR(ret);+ pret->u.array.len = pos;+ return ret;+}++JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ JSValue prop;+ int array_len, idx;+ + if (JS_IsNull(*this_val) || JS_IsUndefined(*this_val))+ return JS_ThrowTypeError(ctx, "cannot convert to object");+ if (!JS_IsObject(ctx, *this_val))+ return JS_FALSE; /* XXX: could improve for strings */+ prop = JS_ToPropertyKey(ctx, argv[0]);+ p = JS_VALUE_TO_PTR(*this_val);+ if (p->class_id == JS_CLASS_ARRAY) {+ array_len = p->u.array.len;+ goto check_array;+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ array_len = p->u.typed_array.len;+ check_array:+ if (JS_IsInt(prop)) {+ idx = JS_VALUE_GET_INT(prop);+ return JS_NewBool((idx >= 0 && idx < array_len));+ }+ }+ return JS_NewBool((find_own_property(ctx, p, prop) != NULL));+}++JSValue js_object_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ const char *str;+ char buf[64];+ /* XXX: not fully compliant */+ if (JS_IsIntOrShortFloat(*this_val)) {+ goto number;+ } else if (!JS_IsPtr(*this_val)) {+ switch(JS_VALUE_GET_SPECIAL_TAG(*this_val)) {+ case JS_TAG_NULL:+ str = "Null";+ break;+ case JS_TAG_UNDEFINED:+ str = "Undefined";+ break;+ case JS_TAG_SHORT_FUNC:+ str = "Function";+ break;+ case JS_TAG_BOOL:+ str = "Boolean";+ break;+ case JS_TAG_STRING_CHAR:+ goto string;+ default:+ goto object;+ }+ } else {+ JSObject *p = JS_VALUE_TO_PTR(*this_val);+ switch(p->mtag) {+ case JS_MTAG_OBJECT:+ switch(p->class_id) {+ case JS_CLASS_ARRAY:+ str = "Array";+ break;+ case JS_CLASS_ERROR:+ str = "Error";+ break;+ case JS_CLASS_CLOSURE:+ case JS_CLASS_C_FUNCTION:+ str = "Function";+ break;+ default:+ object:+ str = "Object";+ break;+ }+ break;+ case JS_MTAG_STRING:+ string:+ str = "String";+ break;+ case JS_MTAG_FLOAT64:+ number:+ str = "Number";+ break;+ default:+ goto object;+ }+ }+ js_snprintf(buf, sizeof(buf), "[object %s]", str);+ return JS_NewString(ctx, buf);+}++/**********************************************************************/++JSValue js_error_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ JSValue obj, msg;+ JSObject *p;+ JSGCRef obj_ref;+ + argc &= ~FRAME_CF_CTOR;++ obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[magic], JS_CLASS_ERROR,+ sizeof(JSErrorData));+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(obj);+ p->u.error.message = JS_NULL;+ p->u.error.stack = JS_NULL;+ + if (!JS_IsUndefined(argv[0])) {+ JS_PUSH_VALUE(ctx, obj);+ msg = JS_ToString(ctx, argv[0]);+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(msg))+ return msg;+ p = JS_VALUE_TO_PTR(obj);+ p->u.error.message = msg;+ } else {+ p = JS_VALUE_TO_PTR(obj);+ p->u.error.message = js_get_atom(ctx, JS_ATOM_empty);+ }+ JS_PUSH_VALUE(ctx, obj);+ build_backtrace(ctx, obj, NULL, 0, 0, 1);+ JS_POP_VALUE(ctx, obj);+ return obj;+}++JSValue js_error_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ JSValue name;+ StringBuffer b_s, *b = &b_s;++ if (!JS_IsError(ctx, *this_val))+ return JS_ThrowTypeError(ctx, "not an Error object");+ name = JS_GetProperty(ctx, *this_val, js_get_atom(ctx, JS_ATOM_name));+ if (JS_IsException(name))+ return name;+ if (JS_IsUndefined(name))+ name = js_get_atom(ctx, JS_ATOM_Error);+ else+ name = JS_ToString(ctx, name);+ if (JS_IsException(name))+ return name;+ string_buffer_push(ctx, b, 0);+ string_buffer_concat(ctx, b, name);+ p = JS_VALUE_TO_PTR(*this_val);+ if (p->u.error.message != JS_NULL) {+ string_buffer_puts(ctx, b, ": ");+ p = JS_VALUE_TO_PTR(*this_val);+ string_buffer_concat(ctx, b, p->u.error.message);+ }+ return string_buffer_pop(ctx, b);+}++JSValue js_error_get_message(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ JSObject *p;+ if (!JS_IsError(ctx, *this_val))+ return JS_ThrowTypeError(ctx, "not an Error object");+ p = JS_VALUE_TO_PTR(*this_val);+ if (magic == 0)+ return p->u.error.message;+ else+ return p->u.error.stack;+}++/**********************************************************************/++static JSObject *js_get_array(JSContext *ctx, JSValue obj)+{+ JSObject *p;+ p = js_get_object_class(ctx, obj, JS_CLASS_ARRAY);+ if (!p) {+ JS_ThrowTypeError(ctx, "not an array");+ return NULL;+ }+ return p;+}++JSValue js_array_get_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ return JS_NewShortInt(p->u.array.len);+}++static int js_array_resize(JSContext *ctx, JSValue *this_val, int new_len)+{+ JSObject *p;+ int i;++ if (new_len < 0 || new_len > JS_SHORTINT_MAX) {+ JS_ThrowTypeError(ctx, "invalid array length");+ return -1;+ }+ p = JS_VALUE_TO_PTR(*this_val);+ if (new_len < p->u.array.len) {+ JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab);+ /* shrink the array if the new size is small enough */+ if (new_len < (arr->size / 2) && arr->size >= 4) {+ js_shrink_value_array(ctx, &p->u.array.tab, new_len);+ p = JS_VALUE_TO_PTR(*this_val);+ } else {+ for(i = new_len; i < p->u.array.len; i++)+ arr->arr[i] = JS_UNDEFINED;+ }+ } else if (new_len > p->u.array.len) {+ JSValueArray *arr;+ JSValue new_tab;+ new_tab = js_resize_value_array(ctx, p->u.array.tab, new_len);+ if (JS_IsException(new_tab))+ return -1;+ p = JS_VALUE_TO_PTR(*this_val);+ p->u.array.tab = new_tab;+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ for(i = p->u.array.len; i < new_len; i++)+ arr->arr[i] = JS_UNDEFINED;+ }+ p->u.array.len = new_len;+ return 0;+}++JSValue js_array_set_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int new_len;++ if (!js_get_array(ctx, *this_val))+ return JS_EXCEPTION;+ if (JS_ToInt32(ctx, &new_len, argv[0]))+ return JS_EXCEPTION;+ if (js_array_resize(ctx, this_val, new_len))+ return JS_EXCEPTION;+ return JS_UNDEFINED;+}++JSValue js_array_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue obj;+ JSObject *p;+ int len, i;+ BOOL has_init;+ + argc &= ~FRAME_CF_CTOR;++ if (argc == 1 && JS_IsNumber(ctx, argv[0])) {+ /* XXX: we create undefined properties instead of just setting the length */+ if (JS_ToInt32(ctx, &len, argv[0]))+ return JS_EXCEPTION;+ has_init = FALSE;+ } else {+ len = argc;+ has_init = TRUE;+ }+ + if (len < 0 || len > JS_SHORTINT_MAX)+ return JS_ThrowRangeError(ctx, "invalid array length");+ obj = JS_NewArray(ctx, len);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(obj);+ p->u.array.len = len;++ if (has_init) {+ JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab);+ for(i = 0; i < argc; i++) {+ arr->arr[i] = argv[i];+ }+ }+ return obj;+}++JSValue js_array_push(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_unshift)+{+ JSObject *p;+ int new_len, i, from;+ JSValueArray *arr;+ JSValue new_tab;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ from = p->u.array.len;+ new_len = from + argc;+ if (new_len > JS_SHORTINT_MAX)+ return JS_ThrowRangeError(ctx, "invalid array length");+ new_tab = js_resize_value_array(ctx, p->u.array.tab, new_len);+ if (JS_IsException(new_tab))+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(*this_val);+ p->u.array.tab = new_tab;+ p->u.array.len = new_len;+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ if (is_unshift && argc > 0) {+ memmove(arr->arr + argc, arr->arr, from * sizeof(JSValue));+ from = 0;+ }+ for(i = 0; i < argc; i++) {+ arr->arr[from + i] = argv[i];+ }+ return JS_NewShortInt(new_len);+}++JSValue js_array_pop(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ JSValue ret;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ if (p->u.array.len > 0) {+ JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab);+ ret = arr->arr[--p->u.array.len];+ } else {+ ret = JS_UNDEFINED;+ }+ return ret;+}++JSValue js_array_shift(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ JSValue ret;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ if (p->u.array.len > 0) {+ JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab);+ ret = arr->arr[0];+ p->u.array.len--;+ memmove(arr->arr, arr->arr + 1, p->u.array.len * sizeof(JSValue));+ } else {+ ret = JS_UNDEFINED;+ }+ return ret;+}++JSValue js_array_join(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ uint32_t i, len;+ BOOL is_array;+ JSValue sep, val;+ JSGCRef sep_ref;+ JSObject *p;+ JSValueArray *arr;+ StringBuffer b_s, *b = &b_s;+ + if (!JS_IsObject(ctx, *this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ p = JS_VALUE_TO_PTR(*this_val);+ is_array = (p->class_id == JS_CLASS_ARRAY);+ if (is_array) {+ len = p->u.array.len;+ } else {+ if (js_get_length32(ctx, &len, *this_val))+ return JS_EXCEPTION;+ }++ if (argc > 0 && !JS_IsUndefined(argv[0])) {+ sep = JS_ToString(ctx, argv[0]);+ if (JS_IsException(sep))+ return sep;+ } else {+ sep = JS_NewStringChar(',');+ }+ JS_PUSH_VALUE(ctx, sep);++ string_buffer_push(ctx, b, 0);+ for(i = 0; i < len; i++) {+ if (i > 0) {+ if (string_buffer_concat(ctx, b, sep_ref.val))+ goto exception;+ }+ if (is_array) {+ p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ if (i < p->u.array.len)+ val = arr->arr[i];+ else+ val = JS_UNDEFINED;+ } else {+ val = JS_GetPropertyUint32(ctx, *this_val, i);+ if (JS_IsException(val))+ goto exception;+ }+ if (!JS_IsUndefined(val) && !JS_IsNull(val)) {+ if (string_buffer_concat(ctx, b, val))+ goto exception;+ }+ }+ val = string_buffer_pop(ctx, b);+ JS_POP_VALUE(ctx, sep);+ return val;++ exception:+ string_buffer_pop(ctx, b);+ JS_POP_VALUE(ctx, sep);+ return JS_EXCEPTION;+}++JSValue js_array_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ return js_array_join(ctx, this_val, 0, NULL);+}++JSValue js_array_isArray(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ p = js_get_object_class(ctx, argv[0], JS_CLASS_ARRAY);+ return JS_NewBool(p != NULL);+}++JSValue js_array_reverse(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ int len;+ JSObject *p;+ JSValueArray *arr;++ p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.array.len;+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ js_reverse_val(arr->arr, len);+ return *this_val;+}++JSValue js_array_concat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p;+ int len, i, j, pos;+ int64_t len64;+ JSValue obj, val;+ JSValueArray *arr, *arr1;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ /* do a first pass to estimate the length */+ len64 = p->u.array.len;+ for(i = 0; i < argc; i++) {+ p = js_get_object_class(ctx, argv[i], JS_CLASS_ARRAY);+ if (p) {+ len64 += p->u.array.len;+ } else {+ len64++;+ }+ }+ if (len64 > JS_SHORTINT_MAX)+ return JS_ThrowTypeError(ctx, "Array loo long");+ len = len64;++ obj = JS_NewArray(ctx, len);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(obj);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ + pos = 0;+ for(i = -1; i < argc; i++) {+ val = i == -1 ? *this_val : argv[i];+ p = js_get_object_class(ctx, val, JS_CLASS_ARRAY);+ if (p) {+ arr1 = JS_VALUE_TO_PTR(p->u.array.tab);+ for(j = 0; j < p->u.array.len; j++)+ arr->arr[pos + j] = arr1->arr[j];+ pos += p->u.array.len;+ } else {+ arr->arr[pos++] = val;+ }+ }+ return obj;+}++JSValue js_array_indexOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_lastIndexOf)+{+ JSObject *p;+ int len, n, res;+ JSValueArray *arr;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.array.len;+ if (is_lastIndexOf) {+ n = len - 1;+ } else {+ n = 0;+ }+ if (argc > 1) {+ if (JS_ToInt32Clamp(ctx, &n, argv[1],+ -is_lastIndexOf, len - is_lastIndexOf, len))+ return JS_EXCEPTION;+ }+ /* the array may be modified */+ p = JS_VALUE_TO_PTR(*this_val);+ len = p->u.array.len; /* the length may be modified */+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ res = -1;+ if (is_lastIndexOf) {+ n = min_int(n, len - 1);+ for(;n >= 0; n--) {+ if (js_strict_eq(ctx, argv[0], arr->arr[n])) {+ res = n;+ break;+ }+ }+ } else {+ for(;n < len; n++) {+ if (js_strict_eq(ctx, argv[0], arr->arr[n])) {+ res = n;+ break;+ }+ }+ }+ return JS_NewShortInt(res);+}++JSValue js_array_slice(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p, *p1;+ int len, start, final, k;+ JSValueArray *arr, *arr1;+ JSValue obj;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.array.len;++ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))+ return JS_EXCEPTION;+ final = len;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))+ return JS_EXCEPTION;+ }+ /* the array may have been modified */+ p = JS_VALUE_TO_PTR(*this_val);+ len = p->u.array.len; /* the length may be modified */+ final = min_int(final, len);++ obj = JS_NewArray(ctx, max_int(final - start, 0));+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ p1 = JS_VALUE_TO_PTR(obj);+ arr1 = JS_VALUE_TO_PTR(p1->u.array.tab);+ for(k = start; k < final; k++) {+ arr1->arr[k - start] = arr->arr[k];+ }+ return obj;+}++JSValue js_array_splice(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p, *p1;+ int start, len, item_count, del_count, new_len, i, ret;+ JSValueArray *arr, *arr1;+ JSValue obj;+ JSGCRef obj_ref;+ + p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.array.len;++ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))+ return JS_EXCEPTION;++ 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_ToInt32Clamp(ctx, &del_count, argv[1], 0, len - start, 0))+ return JS_EXCEPTION;+ }+ new_len = len + item_count - del_count;+ + obj = JS_NewArray(ctx, del_count);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(*this_val);+ /* handling this case has no practical use */+ if (p->u.array.len != len)+ return JS_ThrowTypeError(ctx, "array length was modified");+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ p1 = JS_VALUE_TO_PTR(obj);+ arr1 = JS_VALUE_TO_PTR(p1->u.array.tab);++ for(i = 0; i < del_count; i++) {+ arr1->arr[i] = arr->arr[start + i];+ }++ if (item_count != del_count) {+ /* resize */+ if (del_count > item_count) {+ memmove(arr->arr + start + item_count,+ arr->arr + start + del_count,+ (len - (start + del_count)) * sizeof(JSValue));+ }+ JS_PUSH_VALUE(ctx, obj);+ ret = js_array_resize(ctx, this_val, new_len);+ JS_POP_VALUE(ctx, obj);+ if (ret)+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ if (del_count < item_count) {+ memmove(arr->arr + start + item_count,+ arr->arr + start + del_count,+ (len - (start + del_count)) * sizeof(JSValue));+ }+ }++ for(i = 0; i < item_count; i++)+ arr->arr[start + i] = argv[2 + i];+ + return obj;+}++JSValue js_array_every(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int special)+{+ JSObject *p;+ JSValueArray *arr;+ JSValue res, ret, val;+ JSValue *pfunc, *pthis_arg;+ JSGCRef val_ref, ret_ref;+ int len, k, n;++ p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.array.len;++ pfunc = &argv[0];+ pthis_arg = NULL;+ if (argc > 1)+ pthis_arg = &argv[1];++ if (!JS_IsFunction(ctx, *pfunc))+ return JS_ThrowTypeError(ctx, "not a function");+ + switch (special) {+ case js_special_every:+ ret = JS_TRUE;+ break;+ case js_special_some:+ ret = JS_FALSE;+ break;+ case js_special_map:+ ret = JS_NewArray(ctx, len);+ if (JS_IsException(ret))+ return JS_EXCEPTION;+ break;+ case js_special_filter:+ ret = JS_NewArray(ctx, 0);+ if (JS_IsException(ret))+ return JS_EXCEPTION;+ break;+ case js_special_forEach:+ default:+ ret = JS_UNDEFINED;+ break;+ }+ n = 0;++ JS_PUSH_VALUE(ctx, ret);+ for(k = 0; k < len; k++) {+ if (JS_StackCheck(ctx, 5))+ goto exception;++ p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ /* the array length may have been modified by the function call*/+ if (k >= p->u.array.len)+ break;+ val = arr->arr[k];+ + JS_PushArg(ctx, *this_val);+ JS_PushArg(ctx, JS_NewShortInt(k));+ JS_PushArg(ctx, val); /* arg0 */+ JS_PushArg(ctx, *pfunc); /* func */+ JS_PushArg(ctx, pthis_arg ? *pthis_arg : JS_UNDEFINED); /* this */+ JS_PUSH_VALUE(ctx, val);+ res = JS_Call(ctx, 3);+ JS_POP_VALUE(ctx, val);+ if (JS_IsException(res))+ goto exception;+ + switch (special) {+ case js_special_every:+ if (!JS_ToBool(ctx, res)) {+ ret_ref.val = JS_FALSE;+ goto done;+ }+ break;+ case js_special_some:+ if (JS_ToBool(ctx, res)) {+ ret_ref.val = JS_TRUE;+ goto done;+ }+ break;+ case js_special_map:+ /* Note: same as defineProperty for arrays */+ res = JS_SetPropertyUint32(ctx, ret_ref.val, k, res);+ if (JS_IsException(res))+ goto exception;+ break;+ case js_special_filter:+ if (JS_ToBool(ctx, res)) {+ res = JS_SetPropertyUint32(ctx, ret_ref.val, n++, val);+ if (JS_IsException(res))+ goto exception;+ }+ break;+ case js_special_forEach:+ default:+ break;+ }+ }+done:+ JS_POP_VALUE(ctx, ret);+ return ret;+ exception:+ ret_ref.val = JS_EXCEPTION;+ goto done;+}++JSValue js_array_reduce(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int special)+{+ JSObject *p;+ JSValueArray *arr;+ JSValue acc, *pfunc;+ JSGCRef acc_ref;+ int len, k, k1, ret;++ p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.array.len;+ pfunc = &argv[0];++ if (!JS_IsFunction(ctx, *pfunc))+ return JS_ThrowTypeError(ctx, "not a function");++ k = 0;+ if (argc > 1) {+ acc = argv[1];+ } else {+ if (len == 0)+ return JS_ThrowTypeError(ctx, "empty array");+ k1 = (special == js_special_reduceRight) ? len - k - 1 : k;+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ acc = arr->arr[k1];+ k++;+ }+ for (; k < len; k++) {+ JS_PUSH_VALUE(ctx, acc);+ ret = JS_StackCheck(ctx, 6);+ JS_POP_VALUE(ctx, acc);+ if (ret)+ return JS_EXCEPTION;++ k1 = (special == js_special_reduceRight) ? len - k - 1 : k;+ p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ /* Note: the array length may have been modified, hence the check */+ if (k1 >= p->u.array.len)+ break;+ + JS_PushArg(ctx, *this_val);+ JS_PushArg(ctx, JS_NewShortInt(k1));+ JS_PushArg(ctx, arr->arr[k1]);+ JS_PushArg(ctx, acc); /* arg0 */+ JS_PushArg(ctx, *pfunc); /* func */+ JS_PushArg(ctx, JS_UNDEFINED); /* this */+ acc = JS_Call(ctx, 4);+ if (JS_IsException(acc))+ return JS_EXCEPTION;+ }+ return acc;+}++/* heapsort algorithm */+static void rqsort_idx(size_t nmemb,+ int (*cmp)(size_t, size_t, void *),+ void (*swap)(size_t, size_t, void *),+ void *opaque)+{+ size_t i, n, c, r, size;++ size = 1;+ 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(c, c + size, opaque) <= 0)+ c += size;+ if (cmp(r, c, opaque) > 0)+ break;+ swap(r, c, opaque);+ }+ }+ for (i = n - size; i > 0; i -= size) {+ swap(0, i, opaque);++ for (r = 0; (c = r * 2 + size) < i; r = c) {+ if (c < i - size && cmp(c, c + size, opaque) <= 0)+ c += size;+ if (cmp(r, c, opaque) > 0)+ break;+ swap(r, c, opaque);+ }+ }+ }+}++typedef struct {+ JSContext *ctx;+ BOOL exception;+ JSValue *parr;+ JSValue *pfunc;+} JSArraySortContext;++/* return -1, 0, 1 */+static int js_array_sort_cmp(size_t i1, size_t i2, void *opaque)+{+ JSArraySortContext *s = opaque;+ JSContext *ctx = s->ctx;+ JSValueArray *arr;+ int cmp, j1, j2;+ + if (s->exception)+ return 0;++ arr = JS_VALUE_TO_PTR(*s->parr);+ if (s->pfunc) {+ JSValue res;+ /* custom sort function is specified as returning 0 for identical+ * objects: avoid method call overhead.+ */+ if (arr->arr[2 * i1] == arr->arr[2 * i2])+ goto cmp_same;+ if (JS_StackCheck(ctx, 4))+ goto exception;+ arr = JS_VALUE_TO_PTR(*s->parr);++ JS_PushArg(ctx, arr->arr[2 * i2]);+ JS_PushArg(ctx, arr->arr[2 * i1]); /* arg0 */+ JS_PushArg(ctx, *s->pfunc); /* func */+ JS_PushArg(ctx, JS_UNDEFINED); /* this */+ res = JS_Call(ctx, 2);+ if (JS_IsException(res))+ return JS_EXCEPTION;+ if (JS_IsInt(res)) {+ int val = JS_VALUE_GET_INT(res);+ cmp = (val > 0) - (val < 0);+ } else {+ double val;+ if (JS_ToNumber(ctx, &val, res))+ goto exception;+ cmp = (val > 0) - (val < 0);+ }+ } else {+ JSValue str1, str2;+ JSGCRef str1_ref;++ str1 = arr->arr[2 * i1];+ if (!JS_IsString(ctx, str1)) {+ str1 = JS_ToString(ctx, str1);+ if (JS_IsException(str1))+ goto exception;+ arr = JS_VALUE_TO_PTR(*s->parr);+ }+ str2 = arr->arr[2 * i2];+ if (!JS_IsString(ctx, str2)) {+ JS_PUSH_VALUE(ctx, str1);+ str2 = JS_ToString(ctx, str2);+ JS_POP_VALUE(ctx, str1);+ if (JS_IsException(str2))+ goto exception;+ }+ cmp = js_string_compare(ctx, str1, str2);+ }+ if (cmp != 0)+ return cmp;+ cmp_same:+ /* make sort stable: compare array offsets */+ arr = JS_VALUE_TO_PTR(*s->parr);+ j1 = JS_VALUE_GET_INT(arr->arr[2 * i1 + 1]);+ j2 = JS_VALUE_GET_INT(arr->arr[2 * i2 + 1]);+ return (j1 > j2) - (j1 < j2);++exception:+ s->exception = TRUE;+ return 0;+}++static void js_array_sort_swap(size_t i1, size_t i2, void *opaque)+{+ JSArraySortContext *s = opaque;+ JSValueArray *arr;+ JSValue tmp, *tab;+ + arr = JS_VALUE_TO_PTR(*s->parr);+ tab = arr->arr;+ tmp = tab[2 * i1];+ tab[2 * i1] = tab[2 * i2];+ tab[2 * i2] = tmp;++ tmp = tab[2 * i1 + 1];+ tab[2 * i1 + 1] = tab[2 * i2 + 1];+ tab[2 * i2 + 1] = tmp;+}++JSValue js_array_sort(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue *pfunc = &argv[0];+ JSObject *p;+ JSValue tab_val;+ JSGCRef tab_val_ref;+ JSValueArray *tab, *arr;+ int i, len, n;+ JSArraySortContext ss, *s = &ss;+ + if (!JS_IsUndefined(*pfunc)) {+ if (!JS_IsFunction(ctx, *pfunc))+ return JS_ThrowTypeError(ctx, "not a function");+ } else {+ pfunc = NULL;+ }+ p = js_get_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;++ /* create a temporary array for sorting */+ len = p->u.array.len;+ tab = js_alloc_value_array(ctx, 0, len * 2);+ if (!tab)+ return JS_EXCEPTION;++ p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ n = 0;+ for(i = 0; i < len; i++) {+ if (!JS_IsUndefined(arr->arr[i])) {+ tab->arr[2 * n] = arr->arr[i];+ tab->arr[2 * n + 1] = JS_NewShortInt(i);+ n++;+ }+ }+ /* the end of 'tab' is already filled with JS_UNDEFINED */+ tab_val = JS_VALUE_FROM_PTR(tab);+ + JS_PUSH_VALUE(ctx, tab_val);+ s->ctx = ctx;+ s->exception = FALSE;+ s->parr = &tab_val_ref.val;+ s->pfunc = pfunc;+ rqsort_idx(n, js_array_sort_cmp, js_array_sort_swap, s);+ JS_POP_VALUE(ctx, tab_val);+ tab = JS_VALUE_TO_PTR(tab_val);+ if (s->exception) {+ js_free(ctx, tab);+ return JS_EXCEPTION;+ }+ + p = JS_VALUE_TO_PTR(*this_val);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ /* XXX: could resize the array in case it was shrank by the compare function */+ len = min_int(len, p->u.array.len);+ for(i = 0; i < len; i++) {+ arr->arr[i] = tab->arr[2 * i];+ }+ js_free(ctx, tab);+ return *this_val;+}++/**********************************************************************/++/* precondition: a and b are not NaN */+static double js_fmin(double a, double b)+{+ if (a == 0 && b == 0) {+ return uint64_as_float64(float64_as_uint64(a) | float64_as_uint64(b));+ } else if (a <= b) {+ return a;+ } else {+ return b;+ }+}++/* precondition: a and b are not NaN */+static double js_fmax(double a, double b)+{+ if (a == 0 && b == 0) {+ return uint64_as_float64(float64_as_uint64(a) & float64_as_uint64(b));+ } else if (a >= b) {+ return a;+ } else {+ return b;+ }+}++JSValue js_math_min_max(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ BOOL is_max = magic;+ double r, a;+ int i;++ if (unlikely(argc == 0)) {+ return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);+ }++ if (JS_IsInt(argv[0])) {+ int a1, r1 = JS_VALUE_GET_INT(argv[0]);+ for(i = 1; i < argc; i++) {+ if (!JS_IsInt(argv[i])) {+ 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_NewShortInt(r1);+ } else {+ if (JS_ToNumber(ctx, &r, argv[0]))+ return JS_EXCEPTION;+ i = 1;+ generic_case:+ while (i < argc) {+ if (JS_ToNumber(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);+ }+}++double js_math_sign(double a)+{+ if (isnan(a) || a == 0.0)+ return a;+ if (a < 0)+ return -1;+ else+ return 1;+}++double js_math_fround(double a)+{+ return (float)a;+}++JSValue js_math_imul(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *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, (uint32_t)a * (uint32_t)b);+}++JSValue js_math_clz32(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *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);+}++JSValue js_math_atan2(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double y, x;+ + if (JS_ToNumber(ctx, &y, argv[0]))+ return JS_EXCEPTION;+ if (JS_ToNumber(ctx, &x, argv[1]))+ return JS_EXCEPTION;+ return JS_NewFloat64(ctx, js_atan2(y, x));+}++JSValue js_math_pow(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double y, x;+ + if (JS_ToNumber(ctx, &x, argv[0]))+ return JS_EXCEPTION;+ if (JS_ToNumber(ctx, &y, argv[1]))+ return JS_EXCEPTION;+ return JS_NewFloat64(ctx, js_pow(x, y));+}++/* 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;+}++JSValue js_math_random(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double d;+ uint64_t v;++ v = xorshift64star(&ctx->random_state);+ /* 1.0 <= u.d < 2 */+ d = uint64_as_float64(((uint64_t)0x3ff << 52) | (v >> 12));+ return __JS_NewFloat64(ctx, d - 1.0);+}++/* typed array */++#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)++static uint8_t typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {+ 0, 0, 0, 1, 1, 2, 2, 2, 3+};++static int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val)+{+ int v;+ /* XXX: should support 53 bit inteers */+ if (JS_ToInt32Sat(ctx, &v, val))+ return -1;+ if (v < 0 || v > JS_SHORTINT_MAX) {+ JS_ThrowRangeError(ctx, "invalid array index");+ return -1;+ }+ *plen = v;+ return 0;+}++JSValue js_array_buffer_alloc(JSContext *ctx, uint64_t len)+{+ JSByteArray *arr;+ JSValue buffer, obj;+ JSGCRef buffer_ref;+ JSObject *p;++ if (len > JS_SHORTINT_MAX)+ return JS_ThrowRangeError(ctx, "invalid array buffer length");+ arr = js_alloc_byte_array(ctx, len);+ if (!arr)+ return JS_EXCEPTION;+ memset(arr->buf, 0, len);+ buffer = JS_VALUE_FROM_PTR(arr);+ JS_PUSH_VALUE(ctx, buffer);+ obj = JS_NewObjectClass(ctx, JS_CLASS_ARRAY_BUFFER, sizeof(JSArrayBuffer));+ JS_POP_VALUE(ctx, buffer);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(obj);+ p->u.array_buffer.byte_buffer = buffer;+ return obj;+}++JSValue js_array_buffer_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ uint64_t len;+ if (!(argc & FRAME_CF_CTOR))+ return JS_ThrowTypeError(ctx, "must be called with new");+ if (JS_ToIndex(ctx, &len, argv[0]))+ return JS_EXCEPTION;+ return js_array_buffer_alloc(ctx, len);+}++JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p = js_get_object_class(ctx, *this_val, JS_CLASS_ARRAY_BUFFER);+ JSByteArray *arr;+ if (!p)+ return JS_ThrowTypeError(ctx, "expected an ArrayBuffer");+ arr = JS_VALUE_TO_PTR(p->u.array_buffer.byte_buffer);+ return JS_NewShortInt(arr->size);+}++JSValue js_typed_array_base_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ return JS_ThrowTypeError(ctx, "cannot be called");+}++static JSValue js_typed_array_constructor_obj(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ int i, len;+ JSValue val, obj;+ JSGCRef obj_ref;+ JSObject *p;+ + p = JS_VALUE_TO_PTR(argv[0]);+ if (p->class_id == JS_CLASS_ARRAY) {+ len = p->u.array.len;+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ len = p->u.typed_array.len;+ } else {+ return JS_ThrowTypeError(ctx, "unsupported object class");+ }+ val = JS_NewShortInt(len);+ obj = js_typed_array_constructor(ctx, NULL, 1 | FRAME_CF_CTOR, &val, magic);+ if (JS_IsException(obj))+ return obj;++ for(i = 0; i < len; i++) {+ JS_PUSH_VALUE(ctx, obj);+ val = JS_GetProperty(ctx, argv[0], JS_NewShortInt(i));+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(val))+ return val;+ JS_PUSH_VALUE(ctx, obj);+ val = JS_SetPropertyInternal(ctx, obj, JS_NewShortInt(i), val, FALSE);+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(val))+ return val;+ }+ return obj;+}++JSValue js_typed_array_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ int size_log2;+ uint64_t len, offset, byte_length;+ JSObject *p;+ JSByteArray *arr;+ JSValue buffer, obj;+ JSGCRef buffer_ref;+ + if (!(argc & FRAME_CF_CTOR))+ return JS_ThrowTypeError(ctx, "must be called with new");+ size_log2 = typed_array_size_log2[magic - JS_CLASS_UINT8C_ARRAY];+ if (!JS_IsObject(ctx, argv[0])) {+ if (JS_ToIndex(ctx, &len, argv[0]))+ return JS_EXCEPTION;+ buffer = js_array_buffer_alloc(ctx, len << size_log2);+ if (JS_IsException(buffer))+ return JS_EXCEPTION;+ offset = 0;+ } else {+ p = JS_VALUE_TO_PTR(argv[0]);+ if (p->class_id == JS_CLASS_ARRAY_BUFFER) {+ arr = JS_VALUE_TO_PTR(p->u.array_buffer.byte_buffer);+ byte_length = arr->size;+ if (JS_ToIndex(ctx, &offset, argv[1]))+ return JS_EXCEPTION;+ if ((offset & ((1 << size_log2) - 1)) != 0 ||+ offset > byte_length)+ return JS_ThrowRangeError(ctx, "invalid offset");+ if (JS_IsUndefined(argv[2])) {+ if ((byte_length & ((1 << size_log2) - 1)) != 0)+ goto invalid_length;+ len = (byte_length - offset) >> size_log2;+ } else {+ if (JS_ToIndex(ctx, &len, argv[2]))+ return JS_EXCEPTION;+ if ((offset + (len << size_log2)) > byte_length) {+ invalid_length:+ return JS_ThrowRangeError(ctx, "invalid length");+ }+ }+ buffer = argv[0];+ offset >>= size_log2;+ } else {+ return js_typed_array_constructor_obj(ctx, this_val,+ argc, argv, magic);+ }+ }+ + JS_PUSH_VALUE(ctx, buffer);+ obj = JS_NewObjectClass(ctx, magic, sizeof(JSTypedArray));+ JS_POP_VALUE(ctx, buffer);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(obj);+ p->u.typed_array.buffer = buffer;+ p->u.typed_array.offset = offset;+ p->u.typed_array.len = len;+ return obj;+}++static JSObject *get_typed_array(JSContext *ctx, JSValue val)+{+ JSObject *p;+ if (!JS_IsObject(ctx, val))+ goto fail;+ p = JS_VALUE_TO_PTR(val);+ if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {+ fail:+ JS_ThrowTypeError(ctx, "not a TypedArray");+ return NULL;+ }+ return p;+}++JSValue js_typed_array_get_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ JSObject *p;+ int size_log2;++ p = get_typed_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ size_log2 = typed_array_size_log2[p->class_id - JS_CLASS_UINT8C_ARRAY];+ switch(magic) {+ default:+ case 0:+ return JS_NewShortInt(p->u.typed_array.len);+ case 1:+ return JS_NewShortInt(p->u.typed_array.len << size_log2);+ case 2:+ return JS_NewShortInt(p->u.typed_array.offset << size_log2);+ case 3:+ return p->u.typed_array.buffer;+ }+}++JSValue js_typed_array_subarray(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p, *p1;+ JSByteArray *arr;+ int start, final, len;+ uint32_t offset, count;+ JSValue obj;+ + p = get_typed_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ len = p->u.typed_array.len;+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))+ return JS_EXCEPTION;+ if (JS_IsUndefined(argv[1])) {+ final = len;+ } else {+ if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))+ return JS_EXCEPTION;+ }+ p = JS_VALUE_TO_PTR(*this_val);+ offset = p->u.typed_array.offset + start;+ count = max_int(final - start, 0);++ /* check offset and count */+ p1 = JS_VALUE_TO_PTR(p->u.typed_array.buffer);+ arr = JS_VALUE_TO_PTR(p1->u.array_buffer.byte_buffer);+ if (offset + count > arr->size)+ return JS_ThrowRangeError(ctx, "invalid length");+ + obj = JS_NewObjectClass(ctx, p->class_id, sizeof(JSTypedArray));+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ p = JS_VALUE_TO_PTR(*this_val);+ p1 = JS_VALUE_TO_PTR(obj);+ p1->u.typed_array.buffer = p->u.typed_array.buffer;+ p1->u.typed_array.offset = offset;+ p1->u.typed_array.len = count;+ return obj;+}++JSValue js_typed_array_set(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSObject *p, *p1;+ uint32_t dst_len, src_len, i;+ int offset;++ p = get_typed_array(ctx, *this_val);+ if (!p)+ return JS_EXCEPTION;+ if (argc > 1) {+ if (JS_ToInt32Sat(ctx, &offset, argv[1]))+ return JS_EXCEPTION;+ } else {+ offset = 0;+ }+ if (offset < 0)+ goto range_error;+ if (!JS_IsObject(ctx, argv[0]))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ p = JS_VALUE_TO_PTR(*this_val);+ dst_len = p->u.typed_array.len;+ p1 = JS_VALUE_TO_PTR(argv[0]);+ if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ src_len = p1->u.typed_array.len;+ if (src_len > dst_len || offset > dst_len - src_len)+ goto range_error;+ if (p1->class_id == p->class_id) {+ JSObject *src_buffer, *dst_buffer;+ JSByteArray *src_arr, *dst_arr;+ int shift = typed_array_size_log2[p->class_id - JS_CLASS_UINT8C_ARRAY];+ dst_buffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer);+ dst_arr = JS_VALUE_TO_PTR(dst_buffer->u.array_buffer.byte_buffer);+ src_buffer = JS_VALUE_TO_PTR(p1->u.typed_array.buffer);+ src_arr = JS_VALUE_TO_PTR(src_buffer->u.array_buffer.byte_buffer);+ /* same type: must copy to preserve float bits */+ memmove(dst_arr->buf + ((p->u.typed_array.offset + offset) << shift),+ src_arr->buf + (p1->u.typed_array.offset << shift),+ src_len << shift);+ goto done;+ }+ } else {+ if (js_get_length32(ctx, (uint32_t *)&src_len, argv[0]))+ return JS_EXCEPTION;+ if (src_len > dst_len || offset > dst_len - src_len) {+ range_error:+ return JS_ThrowRangeError(ctx, "invalid array length");+ }+ }+ for(i = 0; i < src_len; i++) {+ JSValue val;+ val = JS_GetPropertyUint32(ctx, argv[0], i);+ if (JS_IsException(val))+ return JS_EXCEPTION;+ val = JS_SetPropertyUint32(ctx, *this_val, offset + i, val);+ if (JS_IsException(val))+ return JS_EXCEPTION;+ }+ done:+ return JS_UNDEFINED;+}++/* Date */++JSValue js_date_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ return JS_ThrowTypeError(ctx, "only Date.now() is supported");+}++/* global */++JSValue js_global_eval(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue val;+ + if (!JS_IsString(ctx, argv[0]))+ return argv[0];+ val = JS_Parse2(ctx, argv[0], NULL, 0, "<input>", JS_EVAL_RETVAL);+ if (JS_IsException(val))+ return val;+ return JS_Run(ctx, val);+}++JSValue js_global_isNaN(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double d;+ if (unlikely(JS_ToNumber(ctx, &d, argv[0])))+ return JS_EXCEPTION;+ return JS_NewBool(isnan(d));+}++JSValue js_global_isFinite(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ double d;+ if (unlikely(JS_ToNumber(ctx, &d, argv[0])))+ return JS_EXCEPTION;+ return JS_NewBool(isfinite(d));+}++/* JSON */++JSValue js_json_parse(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue val;+ + val = JS_ToString(ctx, argv[0]);+ if (JS_IsException(val))+ return val;+ return JS_Parse2(ctx, val, NULL, 0, "<input>", JS_EVAL_JSON);+}++static int js_to_quoted_string(JSContext *ctx, StringBuffer *b, JSValue str)+{+ int i, c;+ JSStringCharBuf buf;+ JSString *p;+ JSGCRef str_ref;+ size_t clen;++ JS_PUSH_VALUE(ctx, str);+ string_buffer_putc(ctx, b, '\"');++ i = 0;+ for(;;) {+ /* XXX: inefficient */+ p = get_string_ptr(ctx, &buf, str_ref.val);+ if (i >= p->len)+ break;+ c = utf8_get(p->buf + i, &clen);+ i += clen;++ 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:+ string_buffer_putc(ctx, b, '\\');+ string_buffer_putc(ctx, b, c);+ break;+ default:+ if (c < 32 || (c >= 0xd800 && c < 0xe000)) {+ char buf[7];+ js_snprintf(buf, sizeof(buf), "\\u%04x", c);+ string_buffer_puts(ctx, b, buf);+ } else {+ string_buffer_putc(ctx, b, c);+ }+ break;+ }+ }+ string_buffer_putc(ctx, b, '\"');+ JS_POP_VALUE(ctx, str);+ return 0;+}++#define JSON_REC_SIZE 3++static int check_circular_ref(JSContext *ctx, JSValue *stack_top, JSValue val)+{+ JSValue *sp;+ for(sp = ctx->sp; sp < stack_top; sp += JSON_REC_SIZE) {+ if (sp[0] == val) {+ JS_ThrowTypeError(ctx, "circular reference");+ return -1;+ }+ }+ return 0;+}++/* XXX: no space nor replacer */+JSValue js_json_stringify(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue obj, *stack_top;+ StringBuffer b_s, *b = &b_s;+ int idx, ret;+ +#if 0+ if (JS_IsNumber(ctx, *pspace)) {+ int n;+ if (JS_ToInt32Clamp(ctx, &n, *pspace, 0, 10, 0))+ return JS_EXCEPTION;+ *pspace = JS_NewStringLen(ctx, " ", n);+ } else if (JS_IsString(ctx, *pspace)) {+ *pspace = js_sub_string(ctx, *pspace, 0, 10);+ } else {+ *pspace = js_get_atom(ctx, JS_ATOM_empty);+ }+#endif+ string_buffer_push(ctx, b, 0);+ stack_top = ctx->sp;++ ret = JS_StackCheck(ctx, JSON_REC_SIZE);+ if (ret)+ goto fail;+ *--ctx->sp = JS_NULL; /* keys */+ *--ctx->sp = JS_NewShortInt(0); /* prop index */+ *--ctx->sp = argv[0]; /* object */+ + while (ctx->sp < stack_top) {+ obj = ctx->sp[0];+ if (JS_IsFunction(ctx, obj)) {+ goto output_null;+ } else if (JS_IsObject(ctx, obj)) {+ JSObject *p = JS_VALUE_TO_PTR(obj);+ idx = JS_VALUE_GET_INT(ctx->sp[1]);+ if (p->class_id == JS_CLASS_ARRAY) {+ JSValueArray *arr;+ JSValue val;++ /* array */+ if (idx == 0)+ string_buffer_putc(ctx, b, '[');+ p = JS_VALUE_TO_PTR(ctx->sp[0]);+ if (idx >= p->u.array.len) {+ /* end of array */+ string_buffer_putc(ctx, b, ']');+ ctx->sp += JSON_REC_SIZE;+ } else {+ if (idx != 0)+ string_buffer_putc(ctx, b, ',');+ ctx->sp[1] = JS_NewShortInt(idx + 1);+ ret = JS_StackCheck(ctx, JSON_REC_SIZE);+ if (ret)+ goto fail;+ p = JS_VALUE_TO_PTR(ctx->sp[0]);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ val = arr->arr[idx];+ if (check_circular_ref(ctx, stack_top, val))+ goto fail;+ *--ctx->sp = JS_NULL;+ *--ctx->sp = JS_NewShortInt(0);+ *--ctx->sp = val;+ }+ } else {+ JSValueArray *arr;+ JSValue val, prop;+ JSGCRef val_ref;+ int saved_idx;+ + /* object */+ if (idx == 0) {+ string_buffer_putc(ctx, b, '{');+ ctx->sp[2] = js_object_keys(ctx, NULL, 1, &ctx->sp[0]);+ if (JS_IsException(ctx->sp[2]))+ goto fail;+ }+ saved_idx = idx;+ for(;;) {+ p = JS_VALUE_TO_PTR(ctx->sp[2]); /* keys */+ if (idx >= p->u.array.len) {+ /* end of object */+ string_buffer_putc(ctx, b, '}');+ ctx->sp += JSON_REC_SIZE;+ goto end_obj;+ } else {+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ prop = JS_ToPropertyKey(ctx, arr->arr[idx]);+ val = JS_GetProperty(ctx, ctx->sp[0], prop);+ if (JS_IsException(val))+ goto fail;+ /* skip undefined properties */+ if (!JS_IsUndefined(val))+ break;+ idx++;+ }+ }+ JS_PUSH_VALUE(ctx, val);+ if (saved_idx != 0)+ string_buffer_putc(ctx, b, ',');+ ctx->sp[1] = JS_NewShortInt(idx + 1);+ p = JS_VALUE_TO_PTR(ctx->sp[2]);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ ret = js_to_quoted_string(ctx, b, arr->arr[idx]);+ string_buffer_putc(ctx, b, ':');+ ret |= JS_StackCheck(ctx, JSON_REC_SIZE);+ JS_POP_VALUE(ctx, val);+ if (ret)+ goto fail;+ if (check_circular_ref(ctx, stack_top, val))+ goto fail;+ *--ctx->sp = JS_NULL;+ *--ctx->sp = JS_NewShortInt(0);+ *--ctx->sp = val;+ end_obj: ;+ }+ } else if (JS_IsNumber(ctx, obj)) {+ double d;+ ret = JS_ToNumber(ctx, &d, obj);+ if (ret)+ goto fail;+ if (!isfinite(d))+ goto output_null;+ goto to_string;+ } else if (JS_IsBool(obj)) {+ to_string:+ if (string_buffer_concat(ctx, b, obj))+ goto fail;+ ctx->sp += JSON_REC_SIZE;+ } else if (JS_IsString(ctx, obj)) {+ if (js_to_quoted_string(ctx, b, obj))+ goto fail;+ ctx->sp += JSON_REC_SIZE;+ } else {+ output_null:+ string_buffer_concat(ctx, b, js_get_atom(ctx, JS_ATOM_null));+ ctx->sp += JSON_REC_SIZE;+ }+ }+ return string_buffer_pop(ctx, b);+ + fail:+ ctx->sp = stack_top;+ string_buffer_pop(ctx, b);+ return JS_EXCEPTION;+}++/**********************************************************************/+/* regexp */++typedef enum {+#define REDEF(id, size) REOP_ ## id,+#include "mquickjs_opcode.h"+#undef REDEF+ REOP_COUNT,+} REOPCodeEnum;++#define CAPTURE_COUNT_MAX 255+#define REGISTER_COUNT_MAX 255++typedef struct {+#ifdef DUMP_REOP+ const char *name;+#endif+ uint8_t size;+} REOpCode;++static const REOpCode reopcode_info[REOP_COUNT] = {+#ifdef DUMP_REOP+#define REDEF(id, size) { #id, size },+#else+#define REDEF(id, size) { size },+#endif+#include "mquickjs_opcode.h"+#undef REDEF+};++#define LRE_FLAG_GLOBAL (1 << 0)+#define LRE_FLAG_IGNORECASE (1 << 1)+#define LRE_FLAG_MULTILINE (1 << 2)+#define LRE_FLAG_DOTALL (1 << 3)+#define LRE_FLAG_UNICODE (1 << 4)+#define LRE_FLAG_STICKY (1 << 5)++#define RE_HEADER_FLAGS 0+#define RE_HEADER_CAPTURE_COUNT 2+#define RE_HEADER_REGISTER_COUNT 3++#define RE_HEADER_LEN 4++#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 int lre_get_capture_count(const uint8_t *bc_buf)+{+ return bc_buf[RE_HEADER_CAPTURE_COUNT];+}++static int lre_get_alloc_count(const uint8_t *bc_buf)+{+ return bc_buf[RE_HEADER_CAPTURE_COUNT] * 2 + bc_buf[RE_HEADER_REGISTER_COUNT];+}++static int lre_get_flags(const uint8_t *bc_buf)+{+ return get_u16(bc_buf + RE_HEADER_FLAGS);+}++#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;+ uint32_t val, val2;++ assert(buf_len >= RE_HEADER_LEN);+ re_flags = lre_get_flags(buf);+ bc_len = buf_len - RE_HEADER_LEN;++ printf("flags: 0x%x capture_count=%d reg_count=%d bytecode_len=%d\n",+ re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_REGISTER_COUNT],+ 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_char1:+ case REOP_char2:+ case REOP_char3:+ case REOP_char4:+ {+ int i, n;+ n = opcode - REOP_char1 + 1;+ for(i = 0; i < n; i++) {+ val = buf[pos + 1 + i];+ if (val >= ' ' && val <= 126)+ printf(" '%c'", val);+ else+ printf(" 0x%2x", val);+ }+ }+ break;+ case REOP_goto:+ case REOP_split_goto_first:+ case REOP_split_next_first:+ case REOP_lookahead:+ case REOP_negative_lookahead:+ val = get_u32(buf + pos + 1);+ val += (pos + 5);+ printf(" %u", val);+ break;+ case REOP_loop:+ val2 = buf[pos + 1];+ val = get_u32(buf + pos + 2);+ val += (pos + 6);+ printf(" r%u, %u", val2, val);+ break;+ case REOP_loop_split_goto_first:+ case REOP_loop_split_next_first:+ case REOP_loop_check_adv_split_goto_first:+ case REOP_loop_check_adv_split_next_first:+ {+ uint32_t limit;+ val2 = buf[pos + 1];+ limit = get_u32(buf + pos + 2);+ val = get_u32(buf + pos + 6);+ val += (pos + 10);+ printf(" r%u, %u, %u", val2, limit, val);+ }+ break;+ case REOP_save_start:+ case REOP_save_end:+ case REOP_back_reference:+ case REOP_back_reference_i:+ printf(" %u", buf[pos + 1]);+ break;+ case REOP_save_reset:+ printf(" %u %u", buf[pos + 1], buf[pos + 2]);+ break;+ case REOP_set_i32:+ val = buf[pos + 1];+ val2 = get_u32(buf + pos + 2);+ printf(" r%u, %d", val, val2);+ break;+ case REOP_set_char_pos:+ case REOP_check_advance:+ val = buf[pos + 1];+ printf(" r%u", val);+ break;+ case REOP_range8:+ {+ int n, i;+ n = buf[pos + 1];+ len += n * 2;+ for(i = 0; i < n * 2; i++) {+ val = buf[pos + 2 + i];+ printf(" 0x%02x", val);+ }+ }+ break;+ case REOP_range:+ {+ 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%05x", val);+ }+ }+ break;+ default:+ break;+ }+ printf("\n");+ pos += len;+ }+}+#endif++static void re_emit_op(JSParseState *s, int op)+{+ emit_u8(s, op);+}++static void re_emit_op_u8(JSParseState *s, int op, uint32_t val)+{+ emit_u8(s, op);+ emit_u8(s, val);+}++static void re_emit_op_u16(JSParseState *s, int op, uint32_t val)+{+ emit_u8(s, op);+ emit_u16(s, val);+}++/* return the offset of the u32 value */+static int re_emit_op_u32(JSParseState *s, int op, uint32_t val)+{+ int pos;+ emit_u8(s, op);+ pos = s->byte_code_len;+ emit_u32(s, val);+ return pos;+}++static int re_emit_goto(JSParseState *s, int op, uint32_t val)+{+ int pos;+ emit_u8(s, op);+ pos = s->byte_code_len;+ emit_u32(s, val - (pos + 4));+ return pos;+}++static int re_emit_goto_u8(JSParseState *s, int op, uint32_t arg, uint32_t val)+{+ int pos;+ emit_u8(s, op);+ emit_u8(s, arg);+ pos = s->byte_code_len;+ emit_u32(s, val - (pos + 4));+ return pos;+}++static int re_emit_goto_u8_u32(JSParseState *s, int op, uint32_t arg0, uint32_t arg1, uint32_t val)+{+ int pos;+ emit_u8(s, op);+ emit_u8(s, arg0);+ emit_u32(s, arg1);+ pos = s->byte_code_len;+ emit_u32(s, val - (pos + 4));+ return pos;+}++static void re_emit_char(JSParseState *s, int c)+{+ uint8_t buf[4];+ size_t n, i;+ n = unicode_to_utf8(buf, c);+ re_emit_op(s, REOP_char1 + n - 1);+ for(i = 0; i < n; i++)+ emit_u8(s, buf[i]);+}++static void re_parse_expect(JSParseState *s, int c)+{+ if (s->source_buf[s->buf_pos] != c)+ return js_parse_error(s, "expecting '%c'", c);+ s->buf_pos++;+}++/* return JS_SHORTINT_MAX in case of overflow */+static int parse_digits(const uint8_t **pp)+{+ 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 >= JS_SHORTINT_MAX)+ v = JS_SHORTINT_MAX;+ p++;+ }+ *pp = p;+ return v;+}++/* need_check_adv: false if the opcodes always advance the char pointer+ need_capture_init: true if all the captures in the atom are not set+*/+static BOOL re_need_check_adv_and_capture_init(BOOL *pneed_capture_init,+ const uint8_t *bc_buf, int bc_buf_len)+{+ int pos, opcode, len;+ uint32_t val;+ BOOL need_check_adv, need_capture_init;++ need_check_adv = TRUE;+ need_capture_init = FALSE;+ pos = 0;+ while (pos < bc_buf_len) {+ opcode = bc_buf[pos];+ len = reopcode_info[opcode].size;+ switch(opcode) {+ case REOP_range8:+ val = bc_buf[pos + 1];+ len += val * 2;+ need_check_adv = FALSE;+ break;+ case REOP_range:+ val = get_u16(bc_buf + pos + 1);+ len += val * 8;+ need_check_adv = FALSE;+ break;+ case REOP_char1:+ case REOP_char2:+ case REOP_char3:+ case REOP_char4:+ case REOP_dot:+ case REOP_any:+ case REOP_space:+ case REOP_not_space:+ need_check_adv = FALSE;+ break;+ case REOP_line_start:+ case REOP_line_start_m:+ case REOP_line_end:+ case REOP_line_end_m:+ case REOP_set_i32:+ case REOP_set_char_pos:+ case REOP_word_boundary:+ case REOP_not_word_boundary:+ /* no effect */+ break;+ case REOP_save_start:+ case REOP_save_end:+ case REOP_save_reset:+ break;+ default:+ /* safe behavior: we cannot predict the outcome */+ need_capture_init = TRUE;+ goto done;+ }+ pos += len;+ }+ done:+ *pneed_capture_init = need_capture_init;+ return need_check_adv;+}++/* return the character or a class range (>= CLASS_RANGE_BASE) if inclass+ = TRUE */+static int get_class_atom(JSParseState *s, BOOL inclass)+{+ const uint8_t *p;+ uint32_t c;+ int ret;+ size_t len;+ + p = s->source_buf + s->buf_pos;+ c = *p;+ switch(c) {+ case '\\':+ p++;+ 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:+ 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_unicode)) { /* Annex B.1.4 */+ c &= 0x1f;+ p++;+ } else if (s->is_unicode) {+ goto invalid_escape;+ } else {+ /* otherwise return '\' and 'c' */+ p--;+ c = '\\';+ }+ break;+ case '-':+ if (!inclass && s->is_unicode)+ goto invalid_escape;+ break;+ case '^':+ case '$':+ case '\\':+ case '.':+ case '*':+ case '+':+ case '?':+ case '(':+ case ')':+ case '[':+ case ']':+ case '{':+ case '}':+ case '|':+ case '/':+ /* always valid to escape these characters */+ break;+ default:+ p--;+ ret = js_parse_escape(p, &len);+ if (ret < 0) {+ if (s->is_unicode) {+ invalid_escape:+ s->buf_pos = p - s->source_buf;+ js_parse_error(s, "invalid escape sequence in regular expression");+ } else {+ goto normal_char;+ }+ }+ p += len;+ c = ret;+ break;+ }+ break;+ case '\0':+ case '/': /* safety for end of regexp in JS parser */+ if ((p - s->source_buf) >= s->buf_len)+ js_parse_error(s, "unexpected end");+ goto normal_char;+ default:+ normal_char:+ /* normal char */+ ret = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &len);+ /* Note: should not fail with normal JS strings */+ if (ret < 0)+ js_parse_error(s, "malformed unicode char"); + p += len;+ c = ret;+ break;+ }+ s->buf_pos = p - s->source_buf;+ return c;+}++/* code point ranges for Zs,Zl or Zp property */+static const uint16_t char_range_s[] = {+ 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,+};++static const uint16_t char_range_w[] = {+ 0x0030, 0x0039 + 1,+ 0x0041, 0x005A + 1,+ 0x005F, 0x005F + 1,+ 0x0061, 0x007A + 1,+};++static void re_emit_range_base1(JSParseState *s, const uint16_t *tab, int n)+{+ int i;+ for(i = 0; i < n; i++)+ emit_u32(s, tab[i]);+}++static void re_emit_range_base(JSParseState *s, int c)+{+ BOOL invert;+ invert = c & 1;+ if (invert)+ emit_u32(s, 0);+ switch(c & ~1) {+ case CHAR_RANGE_d:+ emit_u32(s, 0x30);+ emit_u32(s, 0x39 + 1);+ break;+ case CHAR_RANGE_s:+ re_emit_range_base1(s, char_range_s, countof(char_range_s));+ break;+ case CHAR_RANGE_w:+ re_emit_range_base1(s, char_range_w, countof(char_range_w));+ break;+ default:+ abort();+ }+ if (invert)+ emit_u32(s, 0x110000);+}++static int range_sort_cmp(size_t i1, size_t i2, void *opaque)+{+ uint8_t *tab = opaque;+ return get_u32(&tab[8 * i1]) - get_u32(&tab[8 * i2]);+}++static void range_sort_swap(size_t i1, size_t i2, void *opaque)+{+ uint8_t *tab = opaque;+ uint64_t tmp;+ tmp = get_u64(&tab[8 * i1]);+ put_u64(&tab[8 * i1], get_u64(&tab[8 * i2]));+ put_u64(&tab[8 * i2], tmp);+}++/* merge consecutive intervals, remove empty intervals and handle overlapping intervals */ +static int range_compress(uint8_t *tab, int len)+{+ int i, j;+ uint32_t start, end, start2, end2;++ i = 0;+ j = 0;+ while (i < len) {+ start = get_u32(&tab[8 * i]);+ end = get_u32(&tab[8 * i + 4]);+ if (start == end) {+ /* empty interval : remove */+ } else if ((i + 1) < len) {+ start2 = get_u32(&tab[8 * i + 8]);+ end2 = get_u32(&tab[8 * i + 12]);+ if (end < start2) {+ goto copy;+ } else {+ /* union of the intervals */+ put_u32(&tab[8 * i + 8], start);+ put_u32(&tab[8 * i + 12], max_uint32(end, end2));+ }+ } else {+ copy:+ put_u32(&tab[8 * j], start);+ put_u32(&tab[8 * j + 4], end);+ j++;+ }+ i++;+ }+ return j;+}++static void re_range_optimize(JSParseState *s, int range_start, BOOL invert)+{+ int n, n1;+ JSByteArray *arr;++ n = (unsigned)(s->byte_code_len - range_start) / 8;++ arr = JS_VALUE_TO_PTR(s->byte_code);+ rqsort_idx(n, range_sort_cmp, range_sort_swap, arr->buf + range_start);++ /* must compress before inverting */+ n1 = range_compress(arr->buf + range_start, n);+ s->byte_code_len -= (n - n1) * 8;++ if (invert) {+ emit_insert(s, range_start, 4);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ put_u32(arr->buf + range_start, 0);+ emit_u32(s, 0x110000);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ n = n1 + 1;+ n1 = range_compress(arr->buf + range_start, n);+ s->byte_code_len -= (n - n1) * 8;+ }+ n = n1;+ + if (n > 65534)+ js_parse_error(s, "range too big");++ /* compress to 8 bit if possible */+ /* XXX: adjust threshold */+ if (n > 0 && n < 16) {+ uint8_t *tab = arr->buf + range_start;+ int c, i;+ c = get_u32(&tab[8 * (n - 1) + 4]);+ if (c < 254 || (c == 0x110000 &&+ get_u32(&tab[8 * (n - 1)]) < 254)) {+ s->byte_code_len = range_start - 3;+ re_emit_op_u8(s, REOP_range8, n);+ for(i = 0; i < 2 * n; i++) {+ c = get_u32(&tab[4 * i]);+ if (c == 0x110000)+ c = 0xff;+ emit_u8(s, c);+ }+ goto done;+ }+ }+ + put_u16(arr->buf + range_start - 2, n);+ done: ;+}++/* add the intersection of the two intervals and if offset != 0 the+ translated interval */+static void add_interval_intersect(JSParseState *s,+ uint32_t start, uint32_t end,+ uint32_t start1, uint32_t end1,+ int offset)+{+ start = max_uint32(start, start1);+ end = min_uint32(end, end1);+ if (start < end) {+ emit_u32(s, start);+ emit_u32(s, end);+ if (offset != 0) {+ emit_u32(s, start + offset);+ emit_u32(s, end + offset);+ }+ }+}++static void re_parse_char_class(JSParseState *s)+{+ uint32_t c1, c2;+ BOOL invert;+ int range_start;+ + s->buf_pos++; /* skip '[' */++ invert = FALSE;+ if (s->source_buf[s->buf_pos] == '^') {+ s->buf_pos++;+ invert = TRUE;+ }+ + re_emit_op_u16(s, REOP_range, 0);+ range_start = s->byte_code_len;+ + for(;;) {+ if (s->source_buf[s->buf_pos] == ']')+ break;++ c1 = get_class_atom(s, TRUE);+ if (s->source_buf[s->buf_pos] == '-' && s->source_buf[s->buf_pos + 1] != ']') {+ s->buf_pos++;+ if (c1 >= CLASS_RANGE_BASE) + goto invalid_class_range;+ c2 = get_class_atom(s, TRUE);+ if (c2 >= CLASS_RANGE_BASE) + goto invalid_class_range;+ if (c2 < c1) {+ invalid_class_range:+ js_parse_error(s, "invalid class range");+ }+ goto add_range;+ } else {+ if (c1 >= CLASS_RANGE_BASE) {+ re_emit_range_base(s, c1 - CLASS_RANGE_BASE);+ } else {+ c2 = c1;+ add_range:+ c2++;+ if (s->ignore_case) {+ /* add the intervals exclude the cased characters */+ add_interval_intersect(s, c1, c2, 0, 'A', 0);+ add_interval_intersect(s, c1, c2, 'Z' + 1, 'a', 0);+ add_interval_intersect(s, c1, c2, 'z' + 1, INT32_MAX, 0);+ /* include all the possible cases */+ add_interval_intersect(s, c1, c2, 'A', 'Z' + 1, 32);+ add_interval_intersect(s, c1, c2, 'a', 'z' + 1, -32);+ } else {+ emit_u32(s, c1);+ emit_u32(s, c2);+ }+ }+ }+ }+ s->buf_pos++; /* skip ']' */+ re_range_optimize(s, range_start, invert);+}++static void re_parse_quantifier(JSParseState *s, int last_atom_start, int last_capture_count)+{+ int c, quant_min, quant_max;+ JSByteArray *arr;+ BOOL greedy;+ const uint8_t *p;+ + p = s->source_buf + s->buf_pos;+ c = *p;+ switch(c) {+ case '*':+ p++;+ quant_min = 0;+ quant_max = JS_SHORTINT_MAX;+ goto quantifier;+ case '+':+ p++;+ quant_min = 1;+ quant_max = JS_SHORTINT_MAX;+ goto quantifier;+ case '?':+ p++;+ quant_min = 0;+ quant_max = 1;+ goto quantifier;+ case '{':+ {+ if (!is_digit(p[1]))+ goto invalid_quant_count;+ p++;+ quant_min = parse_digits(&p);+ quant_max = quant_min;+ if (*p == ',') {+ p++;+ if (is_digit(*p)) {+ quant_max = parse_digits(&p);+ if (quant_max < quant_min) {+ invalid_quant_count:+ js_parse_error(s, "invalid repetition count");+ }+ } else {+ quant_max = JS_SHORTINT_MAX; /* infinity */+ }+ }+ s->buf_pos = p - s->source_buf;+ re_parse_expect(s, '}');+ p = s->source_buf + s->buf_pos;+ }+ quantifier:+ greedy = TRUE;++ if (*p == '?') {+ p++;+ greedy = FALSE;+ }+ s->buf_pos = p - s->source_buf;++ if (last_atom_start < 0)+ js_parse_error(s, "nothing to repeat");+ {+ BOOL need_capture_init, add_zero_advance_check;+ int len, pos;+ + /* the spec tells that if there is no advance when+ running the atom after the first quant_min times,+ then there is no match. We remove this test when we+ are sure the atom always advances the position. */+ arr = JS_VALUE_TO_PTR(s->byte_code);+ add_zero_advance_check =+ re_need_check_adv_and_capture_init(&need_capture_init,+ arr->buf + last_atom_start,+ s->byte_code_len - last_atom_start);+ + /* general case: need to reset the capture at each+ iteration. We don't do it if there are no captures+ in the atom or if we are sure all captures are+ initialized in the atom. If quant_min = 0, we still+ need to reset once the captures in case the atom+ does not match. */+ if (need_capture_init && last_capture_count != s->capture_count) {+ emit_insert(s, last_atom_start, 3);+ int pos = last_atom_start;+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[pos++] = REOP_save_reset;+ arr->buf[pos++] = last_capture_count;+ arr->buf[pos++] = s->capture_count - 1;+ }++ len = s->byte_code_len - last_atom_start;+ if (quant_min == 0) {+ /* need to reset the capture in case the atom is+ not executed */+ if (!need_capture_init && last_capture_count != s->capture_count) {+ emit_insert(s, last_atom_start, 3);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[last_atom_start++] = REOP_save_reset;+ arr->buf[last_atom_start++] = last_capture_count;+ arr->buf[last_atom_start++] = s->capture_count - 1;+ }+ if (quant_max == 0) {+ s->byte_code_len = last_atom_start;+ } else if (quant_max == 1 || quant_max == JS_SHORTINT_MAX) {+ BOOL has_goto = (quant_max == JS_SHORTINT_MAX);+ emit_insert(s, last_atom_start, 5 + add_zero_advance_check * 2);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[last_atom_start] = REOP_split_goto_first ++ greedy;+ put_u32(arr->buf + last_atom_start + 1,+ len + 5 * has_goto + add_zero_advance_check * 2 * 2);+ if (add_zero_advance_check) {+ arr->buf[last_atom_start + 1 + 4] = REOP_set_char_pos;+ arr->buf[last_atom_start + 1 + 4 + 1] = 0;+ re_emit_op_u8(s, REOP_check_advance, 0);+ }+ if (has_goto)+ re_emit_goto(s, REOP_goto, last_atom_start);+ } else {+ emit_insert(s, last_atom_start, 11 + add_zero_advance_check * 2);+ pos = last_atom_start;+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[pos++] = REOP_split_goto_first + greedy;+ put_u32(arr->buf + pos, 6 + add_zero_advance_check * 2 + len + 10);+ pos += 4;++ arr->buf[pos++] = REOP_set_i32;+ arr->buf[pos++] = 0;+ put_u32(arr->buf + pos, quant_max);+ pos += 4;+ last_atom_start = pos;+ if (add_zero_advance_check) {+ arr->buf[pos++] = REOP_set_char_pos;+ arr->buf[pos++] = 0;+ }+ re_emit_goto_u8_u32(s, (add_zero_advance_check ? REOP_loop_check_adv_split_next_first : REOP_loop_split_next_first) - greedy, 0, quant_max, last_atom_start);+ }+ } else if (quant_min == 1 && quant_max == JS_SHORTINT_MAX &&+ !add_zero_advance_check) {+ re_emit_goto(s, REOP_split_next_first - greedy,+ last_atom_start);+ } else {+ if (quant_min == quant_max)+ add_zero_advance_check = FALSE;+ emit_insert(s, last_atom_start, 6 + add_zero_advance_check * 2);+ /* Note: we assume the string length is < JS_SHORTINT_MAX */+ pos = last_atom_start;+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[pos++] = REOP_set_i32;+ arr->buf[pos++] = 0;+ put_u32(arr->buf + pos, quant_max);+ pos += 4;+ last_atom_start = pos;+ if (add_zero_advance_check) {+ arr->buf[pos++] = REOP_set_char_pos;+ arr->buf[pos++] = 0;+ }+ if (quant_min == quant_max) {+ /* a simple loop is enough */+ re_emit_goto_u8(s, REOP_loop, 0, last_atom_start);+ } else {+ re_emit_goto_u8_u32(s, (add_zero_advance_check ? REOP_loop_check_adv_split_next_first : REOP_loop_split_next_first) - greedy, 0, quant_max - quant_min, last_atom_start);+ }+ }+ last_atom_start = -1;+ }+ break;+ default:+ break;+ }+}++/* return the number of bytes if char otherwise 0 */+static int re_is_char(const uint8_t *buf, int start, int end)+{+ int n;+ if (!(buf[start] >= REOP_char1 && buf[start] <= REOP_char4))+ return 0;+ n = buf[start] - REOP_char1 + 1;+ if ((end - start) != (n + 1))+ return 0;+ return n;+}++static int re_parse_alternative(JSParseState *s, int state, int dummy_param)+{+ int term_start, last_term_start, last_atom_start, last_capture_count, c, n1, n2, i;+ JSByteArray *arr;+ + PARSE_START3();++ last_term_start = -1;+ for(;;) {+ if (s->buf_pos >= s->buf_len)+ break;+ term_start = s->byte_code_len;++ last_atom_start = -1;+ last_capture_count = 0;+ c = s->source_buf[s->buf_pos];+ switch(c) {+ case '|':+ case ')':+ goto done;+ case '^':+ s->buf_pos++;+ re_emit_op(s, s->multi_line ? REOP_line_start_m : REOP_line_start);+ break;+ case '$':+ s->buf_pos++;+ re_emit_op(s, s->multi_line ? REOP_line_end_m : REOP_line_end);+ break;+ case '.':+ s->buf_pos++;+ last_atom_start = s->byte_code_len;+ last_capture_count = s->capture_count;+ re_emit_op(s, s->dotall ? REOP_any : REOP_dot);+ break;+ case '{': + /* As an extension (see ES6 annex B), we accept '{' not+ followed by digits as a normal atom */+ if (!s->is_unicode && !is_digit(s->source_buf[s->buf_pos + 1]))+ goto parse_class_atom;+ /* fall thru */+ case '*':+ case '+':+ case '?':+ js_parse_error(s, "nothing to repeat");+ case '(':+ if (s->source_buf[s->buf_pos + 1] == '?') {+ c = s->source_buf[s->buf_pos + 2];+ if (c == ':') {+ s->buf_pos += 3;+ last_atom_start = s->byte_code_len;+ last_capture_count = s->capture_count;+ PARSE_CALL_SAVE4(s, 0, re_parse_disjunction, 0,+ last_term_start, term_start, last_atom_start, last_capture_count);+ re_parse_expect(s, ')');+ } else if ((c == '=' || c == '!')) {+ int is_neg, pos;+ is_neg = (c == '!');+ s->buf_pos += 3;+ /* lookahead */+ pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0);+ PARSE_CALL_SAVE6(s, 1, re_parse_disjunction, 0,+ last_term_start, term_start, last_atom_start, last_capture_count,+ is_neg, pos);+ re_parse_expect(s, ')');+ re_emit_op(s, REOP_lookahead_match + is_neg);+ /* jump after the 'match' after the lookahead is successful */+ arr = JS_VALUE_TO_PTR(s->byte_code);+ put_u32(arr->buf + pos, s->byte_code_len - (pos + 4));+ } else {+ js_parse_error(s, "invalid group");+ }+ } else {+ int capture_index;+ s->buf_pos++;+ /* capture without group name */+ if (s->capture_count >= CAPTURE_COUNT_MAX)+ js_parse_error(s, "too many captures");+ last_atom_start = s->byte_code_len;+ last_capture_count = s->capture_count;+ capture_index = s->capture_count++;+ re_emit_op_u8(s, REOP_save_start, capture_index);++ PARSE_CALL_SAVE5(s, 2, re_parse_disjunction, 0,+ last_term_start, term_start, last_atom_start, last_capture_count,+ capture_index);++ re_emit_op_u8(s, REOP_save_end, capture_index);++ re_parse_expect(s, ')');+ }+ break;+ case '\\':+ switch(s->source_buf[s->buf_pos + 1]) {+ case 'b':+ case 'B':+ if (s->source_buf[s->buf_pos + 1] != 'b') {+ re_emit_op(s, REOP_not_word_boundary);+ } else {+ re_emit_op(s, REOP_word_boundary);+ }+ s->buf_pos += 2;+ break;+ case '0':+ s->buf_pos += 2;+ c = 0;+ if (is_digit(s->source_buf[s->buf_pos]))+ js_parse_error(s, "invalid decimal escape in regular expression");+ goto normal_char;+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9':+ {+ const uint8_t *p;+ p = s->source_buf + s->buf_pos + 1;+ c = parse_digits(&p);+ s->buf_pos = p - s->source_buf;+ if (c > CAPTURE_COUNT_MAX)+ js_parse_error(s, "back reference is out of range");+ /* the range is checked afterwards as we don't know the number of captures */+ last_atom_start = s->byte_code_len;+ last_capture_count = s->capture_count;+ re_emit_op_u8(s, REOP_back_reference + s->ignore_case, c);+ }+ break;+ default:+ goto parse_class_atom;+ }+ break;+ case '[':+ last_atom_start = s->byte_code_len;+ last_capture_count = s->capture_count;+ re_parse_char_class(s);+ break;+ case ']':+ case '}':+ if (s->is_unicode)+ js_parse_error(s, "syntax error");+ goto parse_class_atom;+ default:+ parse_class_atom:+ c = get_class_atom(s, FALSE);+ normal_char:+ last_atom_start = s->byte_code_len;+ last_capture_count = s->capture_count;+ if (c >= CLASS_RANGE_BASE) {+ int range_start;+ c -= CLASS_RANGE_BASE;+ if (c == CHAR_RANGE_s || c == CHAR_RANGE_S) {+ re_emit_op(s, REOP_space + c - CHAR_RANGE_s);+ } else {+ re_emit_op_u16(s, REOP_range, 0);+ range_start = s->byte_code_len;+ + re_emit_range_base(s, c);+ re_range_optimize(s, range_start, FALSE);+ }+ } else {+ if (s->ignore_case &&+ ((c >= 'A' && c <= 'Z') ||+ (c >= 'a' && c <= 'z'))) {+ /* XXX: could add specific operation */+ if (c >= 'a')+ c -= 32;+ re_emit_op_u8(s, REOP_range8, 2);+ emit_u8(s, c);+ emit_u8(s, c + 1);+ emit_u8(s, c + 32);+ emit_u8(s, c + 32 + 1);+ } else {+ re_emit_char(s, c);+ }+ }+ break;+ }++ /* quantifier */+ if (last_atom_start >= 0) {+ re_parse_quantifier(s, last_atom_start, last_capture_count);+ }++ /* combine several characters when possible */+ arr = JS_VALUE_TO_PTR(s->byte_code);+ if (last_term_start >= 0 &&+ (n1 = re_is_char(arr->buf, last_term_start, term_start)) > 0 &&+ (n2 = re_is_char(arr->buf, term_start, s->byte_code_len)) > 0 &&+ (n1 + n2) <= 4) {+ n1 += n2;+ arr->buf[last_term_start] = REOP_char1 + n1 - 1;+ for(i = 0; i < n2; i++)+ arr->buf[last_term_start + n1 + i] = arr->buf[last_term_start + n1 + i + 1];+ s->byte_code_len--;+ } else {+ last_term_start = term_start;+ }+ }+ done:+ return PARSE_STATE_RET;+}++static int re_parse_disjunction(JSParseState *s, int state, int dummy_param)+{+ int start, len, pos;+ JSByteArray *arr;++ PARSE_START2();+ + start = s->byte_code_len;++ PARSE_CALL_SAVE1(s, 0, re_parse_alternative, 0, start);+ while (s->source_buf[s->buf_pos] == '|') {+ s->buf_pos++;++ len = s->byte_code_len - start;++ /* insert a split before the first alternative */+ emit_insert(s, start, 5);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[start] = REOP_split_next_first;+ put_u32(arr->buf + start + 1, len + 5);++ pos = re_emit_op_u32(s, REOP_goto, 0);++ PARSE_CALL_SAVE2(s, 1, re_parse_alternative, 0, start, pos);++ /* patch the goto */+ len = s->byte_code_len - (pos + 4);+ arr = JS_VALUE_TO_PTR(s->byte_code);+ put_u32(arr->buf + pos, len);+ }+ return PARSE_STATE_RET;+}++/* Allocate the registers as a stack. The control flow is recursive so+ the analysis can be linear. */+static int re_compute_register_count(JSParseState *s, 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;+ 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_set_i32:+ case REOP_set_char_pos:+ bc_buf[pos + 1] = stack_size;+ stack_size++;+ if (stack_size > stack_size_max) {+ if (stack_size > REGISTER_COUNT_MAX)+ js_parse_error(s, "too many regexp registers");+ stack_size_max = stack_size;+ }+ break;+ case REOP_check_advance:+ case REOP_loop:+ case REOP_loop_split_goto_first:+ case REOP_loop_split_next_first:+ assert(stack_size > 0);+ stack_size--;+ bc_buf[pos + 1] = stack_size;+ break;+ case REOP_loop_check_adv_split_goto_first:+ case REOP_loop_check_adv_split_next_first:+ assert(stack_size >= 2);+ stack_size -= 2;+ bc_buf[pos + 1] = stack_size;+ break;+ case REOP_range8:+ val = bc_buf[pos + 1];+ len += val * 2;+ break;+ case REOP_range:+ val = get_u16(bc_buf + pos + 1);+ len += val * 8;+ break;+ case REOP_back_reference:+ case REOP_back_reference_i:+ /* validate back references */+ if (bc_buf[pos + 1] >= s->capture_count)+ js_parse_error(s, "back reference is out of range");+ break;+ }+ pos += len;+ }+ return stack_size_max;+}++/* return a JSByteArray. 'source' must be a string */+static JSValue js_parse_regexp(JSParseState *s, int re_flags)+{+ JSByteArray *arr;+ int register_count;+ + s->multi_line = ((re_flags & LRE_FLAG_MULTILINE) != 0);+ s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0);+ s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0);+ s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0);+ s->byte_code = JS_NULL;+ s->byte_code_len = 0;+ s->capture_count = 1;+ + emit_u16(s, re_flags);+ emit_u8(s, 0); /* number of captures */+ emit_u8(s, 0); /* number of registers */++ if (!(re_flags & LRE_FLAG_STICKY)) {+ 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);++ js_parse_call(s, PARSE_FUNC_re_parse_disjunction, 0);++ re_emit_op_u8(s, REOP_save_end, 0);+ re_emit_op(s, REOP_match);++ if (s->buf_pos != s->buf_len)+ js_parse_error(s, "extraneous characters at the end");++ arr = JS_VALUE_TO_PTR(s->byte_code);+ arr->buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;+ register_count =+ re_compute_register_count(s, arr->buf + RE_HEADER_LEN,+ s->byte_code_len - RE_HEADER_LEN);+ arr->buf[RE_HEADER_REGISTER_COUNT] = register_count;+ + js_shrink_byte_array(s->ctx, &s->byte_code, s->byte_code_len);++#ifdef DUMP_REOP+ arr = JS_VALUE_TO_PTR(s->byte_code);+ lre_dump_bytecode(arr->buf, arr->size);+#endif+ + return s->byte_code;+}++/* regexp interpreter */++#define CP_LS 0x2028+#define CP_PS 0x2029++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 == '_'));+}++/* Note: we canonicalize as in the unicode case, but only handle ASCII characters */+static int lre_canonicalize(uint32_t c)+{+ if (c >= 'A' && c <= 'Z') {+ c = c - 'A' + 'a';+ }+ return c;+}++#define GET_CHAR(c, cptr, cbuf_end) \+ do { \+ size_t clen; \+ c = utf8_get(cptr, &clen); \+ cptr += clen; \+ } while (0)++#define PEEK_CHAR(c, cptr, cbuf_end) \+ do { \+ size_t clen; \+ c = utf8_get(cptr, &clen); \+ } while (0)++#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \+ do { \+ const uint8_t *cptr1 = cptr - 1; \+ size_t clen; \+ while ((*cptr1 & 0xc0) == 0x80) \+ cptr1--; \+ c = utf8_get(cptr1, &clen); \+ } while (0)++typedef enum {+ RE_EXEC_STATE_SPLIT,+ RE_EXEC_STATE_LOOKAHEAD,+ RE_EXEC_STATE_NEGATIVE_LOOKAHEAD,+} REExecStateEnum;++//#define DUMP_REEXEC++/* return 1 if match, 0 if not match or < 0 if error. str must be a+ JSString. capture_buf and byte_code are JSByteArray */+static int lre_exec(JSContext *ctx, JSValue capture_buf,+ JSValue byte_code, JSValue str, int cindex)+{+ const uint8_t *pc, *cptr, *cbuf;+ uint32_t *capture;+ int opcode, capture_count;+ uint32_t val, c, idx;+ const uint8_t *cbuf_end;+ JSValue *sp, *bp, *initial_sp, *saved_stack_bottom;+ JSByteArray *arr; /* temporary use */+ JSString *ps; /* temporary use */+ JSGCRef capture_buf_ref, byte_code_ref, str_ref;++ arr = JS_VALUE_TO_PTR(byte_code);+ pc = arr->buf;+ arr = JS_VALUE_TO_PTR(capture_buf);+ capture = (uint32_t *)arr->buf;+ capture_count = lre_get_capture_count(pc);+ pc += RE_HEADER_LEN;+ ps = JS_VALUE_TO_PTR(str);+ cbuf = ps->buf;+ cbuf_end = cbuf + ps->len;+ cptr = cbuf + cindex;++ saved_stack_bottom = ctx->stack_bottom;+ initial_sp = ctx->sp;+ sp = initial_sp;+ bp = initial_sp;+ +#define LRE_POLL_INTERRUPT() do { \+ if (unlikely(--ctx->interrupt_counter <= 0)) { \+ JSValue ret; \+ int saved_pc, saved_cptr; \+ arr = JS_VALUE_TO_PTR(byte_code); \+ saved_pc = pc - arr->buf; \+ saved_cptr = cptr - cbuf; \+ JS_PUSH_VALUE(ctx, capture_buf); \+ JS_PUSH_VALUE(ctx, byte_code); \+ JS_PUSH_VALUE(ctx, str); \+ ctx->sp = sp; \+ ret = __js_poll_interrupt(ctx); \+ JS_POP_VALUE(ctx, str); \+ JS_POP_VALUE(ctx, byte_code); \+ JS_POP_VALUE(ctx, capture_buf); \+ if (JS_IsException(ret)) { \+ ctx->sp = initial_sp; \+ ctx->stack_bottom = saved_stack_bottom; \+ return -1; \+ } \+ arr = JS_VALUE_TO_PTR(byte_code); \+ pc = arr->buf + saved_pc; \+ ps = JS_VALUE_TO_PTR(str); \+ cbuf = ps->buf; \+ cbuf_end = cbuf + ps->len; \+ cptr = cbuf + saved_cptr; \+ arr = JS_VALUE_TO_PTR(capture_buf); \+ capture = (uint32_t *)arr->buf; \+ } \+ } while(0)++#define CHECK_STACK_SPACE(n) \+ { \+ if (unlikely((sp - ctx->stack_bottom) < (n))) { \+ int ret, saved_pc, saved_cptr; \+ arr = JS_VALUE_TO_PTR(byte_code); \+ saved_pc = pc - arr->buf; \+ saved_cptr = cptr - cbuf; \+ JS_PUSH_VALUE(ctx, capture_buf); \+ JS_PUSH_VALUE(ctx, byte_code); \+ JS_PUSH_VALUE(ctx, str); \+ ctx->sp = sp; \+ ret = JS_StackCheck(ctx, n); \+ JS_POP_VALUE(ctx, str); \+ JS_POP_VALUE(ctx, byte_code); \+ JS_POP_VALUE(ctx, capture_buf); \+ if (ret < 0) { \+ ctx->sp = initial_sp; \+ ctx->stack_bottom = saved_stack_bottom; \+ return -1; \+ } \+ arr = JS_VALUE_TO_PTR(byte_code); \+ pc = arr->buf + saved_pc; \+ ps = JS_VALUE_TO_PTR(str); \+ cbuf = ps->buf; \+ cbuf_end = cbuf + ps->len; \+ cptr = cbuf + saved_cptr; \+ arr = JS_VALUE_TO_PTR(capture_buf); \+ capture = (uint32_t *)arr->buf; \+ } \+ }++#define SAVE_CAPTURE(idx, value) \+ { \+ int __v = (value); \+ CHECK_STACK_SPACE(2); \+ sp[-2] = JS_NewShortInt(idx); \+ sp[-1] = JS_NewShortInt(capture[idx]); \+ sp -= 2; \+ capture[idx] = __v; \+ }++ /* avoid saving the previous value if already saved */+#define SAVE_CAPTURE_CHECK(idx, value) \+ { \+ int __v = (value); \+ JSValue *sp1; \+ sp1 = sp; \+ for(;;) { \+ if (sp1 < bp) { \+ if (JS_VALUE_GET_INT(sp1[0]) == (idx)) \+ break; \+ sp1 += 2; \+ } else { \+ CHECK_STACK_SPACE(2); \+ sp[-2] = JS_NewShortInt(idx); \+ sp[-1] = JS_NewShortInt(capture[idx]); \+ sp -= 2; \+ break; \+ } \+ } \+ capture[idx] = __v; \+ }++#define RE_PC_TYPE_TO_VALUE(pc, type) (((type) << 1) | (((pc) - ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf) << 3))+#define RE_VALUE_TO_PC(val) (((val) >> 3) + ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf)+#define RE_VALUE_TO_TYPE(val) (((val) >> 1) & 3)++#ifdef DUMP_REEXEC+ printf("%5s %5s %5s %5s %s\n", "PC", "CP", "BP", "SP", "OPCODE");+#endif + for(;;) {+ opcode = *pc++;+#ifdef DUMP_REEXEC+ printf("%5ld %5ld %5ld %5ld %s\n",+ pc - 1 - ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf - RE_HEADER_LEN,+ cptr - cbuf,+ bp - initial_sp,+ sp - initial_sp,+ reopcode_info[opcode].name);+#endif + switch(opcode) {+ case REOP_match:+ ctx->sp = initial_sp;+ ctx->stack_bottom = saved_stack_bottom;+ return 1;+ no_match:+ for(;;) {+ REExecStateEnum type;+ if (bp == initial_sp) {+ ctx->sp = initial_sp;+ ctx->stack_bottom = saved_stack_bottom;+ return 0;+ }+ /* undo the modifications to capture[] and regs[] */+ while (sp < bp) {+ int idx2 = JS_VALUE_GET_INT(sp[0]);+ capture[idx2] = JS_VALUE_GET_INT(sp[1]);+ sp += 2;+ }+ + pc = RE_VALUE_TO_PC(sp[0]);+ type = RE_VALUE_TO_TYPE(sp[0]);+ cptr = JS_VALUE_GET_INT(sp[1]) + cbuf;+ bp = VALUE_TO_SP(ctx, sp[2]);+ sp += 3;+ if (type != RE_EXEC_STATE_LOOKAHEAD)+ break;+ }+ LRE_POLL_INTERRUPT();+ break;+ case REOP_lookahead_match:+ /* pop all the saved states until reaching the start of+ the lookahead and keep the updated captures and+ variables and the corresponding undo info. */+ {+ JSValue *sp1, *sp_start, *next_sp;+ REExecStateEnum type;++ sp_start = sp;+ for(;;) {+ sp1 = sp;+ sp = bp;+ pc = RE_VALUE_TO_PC(sp[0]);+ type = RE_VALUE_TO_TYPE(sp[0]);+ cptr = JS_VALUE_GET_INT(sp[1]) + cbuf;+ bp = VALUE_TO_SP(ctx, sp[2]);+ sp[2] = SP_TO_VALUE(ctx, sp1); /* save the next value for the copy step */+ sp += 3;+ if (type == RE_EXEC_STATE_LOOKAHEAD)+ break;+ }+ if (sp != initial_sp) {+ /* keep the undo info if there is a saved state */+ sp1 = sp;+ while (sp1 != sp_start) {+ sp1 -= 3;+ next_sp = VALUE_TO_SP(ctx, sp1[2]);+ while (sp1 != next_sp) {+ *--sp = *--sp1;+ }+ }+ }+ }+ break;+ case REOP_negative_lookahead_match:+ /* pop all the saved states until reaching start of the negative lookahead */+ for(;;) {+ REExecStateEnum type;+ type = RE_VALUE_TO_TYPE(bp[0]);+ /* undo the modifications to capture[] and regs[] */+ while (sp < bp) {+ int idx2 = JS_VALUE_GET_INT(sp[0]);+ capture[idx2] = JS_VALUE_GET_INT(sp[1]);+ sp += 2;+ }+ pc = RE_VALUE_TO_PC(sp[0]);+ type = RE_VALUE_TO_TYPE(sp[0]);+ cptr = JS_VALUE_GET_INT(sp[1]) + cbuf;+ bp = VALUE_TO_SP(ctx, sp[2]);+ sp += 3;+ if (type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD)+ break;+ }+ goto no_match;++ case REOP_char1:+ if ((cbuf_end - cptr) < 1)+ goto no_match;+ if (pc[0] != cptr[0])+ goto no_match;+ pc++;+ cptr++;+ break;+ case REOP_char2:+ if ((cbuf_end - cptr) < 2)+ goto no_match;+ if (get_u16(pc) != get_u16(cptr))+ goto no_match;+ pc += 2;+ cptr += 2;+ break;+ case REOP_char3:+ if ((cbuf_end - cptr) < 3)+ goto no_match;+ if (get_u16(pc) != get_u16(cptr) || pc[2] != cptr[2])+ goto no_match;+ pc += 3;+ cptr += 3;+ break;+ case REOP_char4:+ if ((cbuf_end - cptr) < 4)+ goto no_match;+ if (get_u32(pc) != get_u32(cptr))+ goto no_match;+ pc += 4;+ cptr += 4;+ break;+ case REOP_split_goto_first:+ case REOP_split_next_first:+ {+ const uint8_t *pc1;++ val = get_u32(pc);+ pc += 4;+ CHECK_STACK_SPACE(3);+ if (opcode == REOP_split_next_first) {+ pc1 = pc + (int)val;+ } else {+ pc1 = pc;+ pc = pc + (int)val;+ }+ sp -= 3;+ sp[0] = RE_PC_TYPE_TO_VALUE(pc1, RE_EXEC_STATE_SPLIT);+ sp[1] = JS_NewShortInt(cptr - cbuf);+ sp[2] = SP_TO_VALUE(ctx, bp);+ bp = sp;+ }+ break;+ case REOP_lookahead:+ case REOP_negative_lookahead:+ val = get_u32(pc);+ pc += 4;+ CHECK_STACK_SPACE(3);+ sp -= 3;+ sp[0] = RE_PC_TYPE_TO_VALUE(pc + (int)val,+ RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead);+ sp[1] = JS_NewShortInt(cptr - cbuf);+ sp[2] = SP_TO_VALUE(ctx, bp);+ bp = sp;+ break;+ case REOP_goto:+ val = get_u32(pc);+ pc += 4 + (int)val;+ LRE_POLL_INTERRUPT();+ break;+ case REOP_line_start:+ case REOP_line_start_m:+ if (cptr == cbuf)+ break;+ if (opcode == REOP_line_start)+ goto no_match;+ PEEK_PREV_CHAR(c, cptr, cbuf);+ if (!is_line_terminator(c))+ goto no_match;+ break;+ case REOP_line_end:+ case REOP_line_end_m:+ if (cptr == cbuf_end)+ break;+ if (opcode == REOP_line_end)+ 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_space:+ case REOP_not_space:+ {+ BOOL v1;+ if (cptr == cbuf_end)+ goto no_match;+ c = cptr[0];+ if (c < 128) {+ cptr++;+ v1 = unicode_is_space_ascii(c);+ } else {+ size_t clen;+ c = __utf8_get(cptr, &clen);+ cptr += clen;+ v1 = unicode_is_space_non_ascii(c);+ }+ v1 ^= (opcode - REOP_space);+ if (!v1)+ goto no_match;+ }+ break;+ case REOP_save_start:+ case REOP_save_end:+ val = *pc++;+ assert(val < capture_count);+ idx = 2 * val + opcode - REOP_save_start;+ SAVE_CAPTURE(idx, cptr - cbuf);+ break;+ case REOP_save_reset:+ {+ uint32_t val2;+ val = pc[0];+ val2 = pc[1];+ pc += 2;+ assert(val2 < capture_count);+ CHECK_STACK_SPACE(2 * (val2 - val + 1));+ while (val <= val2) {+ idx = 2 * val;+ SAVE_CAPTURE(idx, 0);+ idx = 2 * val + 1;+ SAVE_CAPTURE(idx, 0);+ val++;+ }+ }+ break;+ case REOP_set_i32:+ idx = pc[0];+ val = get_u32(pc + 1);+ pc += 5;+ SAVE_CAPTURE_CHECK(2 * capture_count + idx, val);+ break;+ case REOP_loop:+ {+ uint32_t val2;+ idx = pc[0];+ val = get_u32(pc + 1);+ pc += 5;++ val2 = capture[2 * capture_count + idx] - 1;+ SAVE_CAPTURE_CHECK(2 * capture_count + idx, val2);+ if (val2 != 0) {+ pc += (int)val;+ LRE_POLL_INTERRUPT();+ }+ }+ break;+ case REOP_loop_split_goto_first:+ case REOP_loop_split_next_first:+ case REOP_loop_check_adv_split_goto_first:+ case REOP_loop_check_adv_split_next_first:+ {+ const uint8_t *pc1;+ uint32_t val2, limit;+ idx = pc[0];+ limit = get_u32(pc + 1);+ val = get_u32(pc + 5);+ pc += 9;++ /* decrement the counter */+ val2 = capture[2 * capture_count + idx] - 1;+ SAVE_CAPTURE_CHECK(2 * capture_count + idx, val2);+ + if (val2 > limit) {+ /* normal loop if counter > limit */+ pc += (int)val;+ LRE_POLL_INTERRUPT();+ } else {+ /* check advance */+ if ((opcode == REOP_loop_check_adv_split_goto_first ||+ opcode == REOP_loop_check_adv_split_next_first) &&+ capture[2 * capture_count + idx + 1] == (cptr - cbuf) &&+ val2 != limit) {+ goto no_match;+ }+ + /* otherwise conditional split */+ if (val2 != 0) {+ CHECK_STACK_SPACE(3);+ if (opcode == REOP_loop_split_next_first ||+ opcode == REOP_loop_check_adv_split_next_first) {+ pc1 = pc + (int)val;+ } else {+ pc1 = pc;+ pc = pc + (int)val;+ }+ sp -= 3;+ sp[0] = RE_PC_TYPE_TO_VALUE(pc1, RE_EXEC_STATE_SPLIT);+ sp[1] = JS_NewShortInt(cptr - cbuf);+ sp[2] = SP_TO_VALUE(ctx, bp);+ bp = sp;+ }+ }+ }+ break;+ case REOP_set_char_pos:+ idx = pc[0];+ pc++;+ SAVE_CAPTURE_CHECK(2 * capture_count + idx, cptr - cbuf);+ break;+ case REOP_check_advance:+ idx = pc[0];+ pc++;+ if (capture[2 * capture_count + idx] == cptr - cbuf)+ goto no_match;+ break;+ case REOP_word_boundary:+ case REOP_not_word_boundary:+ {+ BOOL v1, v2;+ BOOL is_boundary = (opcode == REOP_word_boundary);+ /* char before */+ if (cptr == cbuf) {+ v1 = FALSE;+ } else {+ PEEK_PREV_CHAR(c, cptr, 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 ^ is_boundary)+ goto no_match;+ }+ break;+ /* assumption: 8 bit and small number of ranges */+ case REOP_range8:+ {+ int n, i;+ n = pc[0];+ pc++;+ if (cptr >= cbuf_end)+ goto no_match;+ GET_CHAR(c, cptr, cbuf_end);+ for(i = 0; i < n - 1; i++) {+ if (c >= pc[2 * i] && c < pc[2 * i + 1])+ goto range8_match;+ }+ /* 0xff = max code point value */+ if (c >= pc[2 * i] &&+ (c < pc[2 * i + 1] || pc[2 * i + 1] == 0xff))+ goto range8_match;+ goto no_match;+ range8_match:+ pc += 2 * n;+ }+ 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 || n == 0)+ goto no_match;+ GET_CHAR(c, cptr, cbuf_end);+ 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 range_match;+ }+ goto no_match;+ range_match:+ pc += 8 * n;+ }+ break;+ case REOP_back_reference:+ case REOP_back_reference_i:+ val = pc[0];+ pc++;+ if (capture[2 * val] != -1 && capture[2 * val + 1] != -1) {+ const uint8_t *cptr1, *cptr1_end;+ int c1, c2;++ cptr1 = cbuf + capture[2 * val];+ cptr1_end = cbuf + capture[2 * val + 1];+ while (cptr1 < cptr1_end) {+ if (cptr >= cbuf_end)+ goto no_match;+ GET_CHAR(c1, cptr1, cptr1_end);+ GET_CHAR(c2, cptr, cbuf_end);+ if (opcode == REOP_back_reference_i) {+ c1 = lre_canonicalize(c1);+ c2 = lre_canonicalize(c2);+ }+ if (c1 != c2)+ goto no_match;+ }+ }+ break;+ default:+#ifdef DUMP_REEXEC+ printf("unknown opcode pc=%ld\n", pc - 1 - ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf - RE_HEADER_LEN);+#endif + abort();+ }+ }+}++/* regexp js interface */++/* return the length */+static size_t js_parse_regexp_flags(int *pre_flags, const uint8_t *buf)+{+ const uint8_t *p = buf;+ int mask, re_flags;+ re_flags = 0;+ while (*p != '\0') {+ switch(*p) {+#if 0+ case 'd':+ mask = LRE_FLAG_INDICES;+ break;+#endif + 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_UNICODE;+ break;+#if 0+ case 'v':+ mask = LRE_FLAG_UNICODE_SETS;+ break;+#endif+ case 'y':+ mask = LRE_FLAG_STICKY;+ break;+ default:+ goto done;+ }+ if ((re_flags & mask) != 0) + break;+ re_flags |= mask;+ p++;+ }+ done:+ *pre_flags = re_flags;+ return p - buf;+}++/* pattern and flags must be strings */+static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, JSValue flags)+{+ int re_flags;+ + re_flags = 0;+ if (!JS_IsUndefined(flags)) {+ JSString *ps;+ JSStringCharBuf buf;+ size_t len;+ ps = get_string_ptr(ctx, &buf, flags);+ len = js_parse_regexp_flags(&re_flags, ps->buf);+ if (len != ps->len)+ return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");+ }++ return JS_Parse2(ctx, pattern, NULL, 0, "<regexp>",+ JS_EVAL_REGEXP | (re_flags << JS_EVAL_REGEXP_FLAGS_SHIFT));+}++static JSRegExp *js_get_regexp(JSContext *ctx, JSValue obj)+{+ JSObject *p;+ p = js_get_object_class(ctx, obj, JS_CLASS_REGEXP);+ if (!p) {+ JS_ThrowTypeError(ctx, "not a regular expression");+ return NULL;+ }+ return &p->u.regexp;+}++JSValue js_regexp_get_lastIndex(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSRegExp *re = js_get_regexp(ctx, *this_val);+ if (!re)+ return JS_EXCEPTION;+ return JS_NewInt32(ctx, re->last_index);+}++JSValue js_regexp_get_source(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSRegExp *re = js_get_regexp(ctx, *this_val);+ if (!re)+ return JS_EXCEPTION;+ /* XXX: not complete */+ return re->source;+}++JSValue js_regexp_set_lastIndex(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSRegExp *re;+ int last_index;+ if (JS_ToInt32(ctx, &last_index, argv[0]))+ return JS_EXCEPTION;+ re = js_get_regexp(ctx, *this_val);+ if (!re)+ return JS_EXCEPTION;+ re->last_index = last_index;+ return JS_UNDEFINED;+}++#define RE_FLAG_COUNT 6++/* return the string length */+static size_t js_regexp_flags_str(char *buf, int re_flags)+{+ static const char flag_char[RE_FLAG_COUNT] = { 'g', 'i', 'm', 's', 'u', 'y' };+ char *p = buf;+ int i;+ + for(i = 0; i < RE_FLAG_COUNT; i++) {+ if ((re_flags >> i) & 1)+ *p++ = flag_char[i];+ }+ *p = '\0';+ return p - buf;+}++static void dump_regexp(JSContext *ctx, JSObject *p)+{+ JSStringCharBuf buf;+ JSString *ps;+ char buf2[RE_FLAG_COUNT + 1];+ JSByteArray *arr;+ + js_putchar(ctx, '/');+ ps = get_string_ptr(ctx, &buf, p->u.regexp.source);+ if (ps->len == 0) {+ js_printf(ctx, "(?:)");+ } else {+ js_printf(ctx, "%" JSValue_PRI, p->u.regexp.source);+ }+ arr = JS_VALUE_TO_PTR(p->u.regexp.byte_code);+ js_regexp_flags_str(buf2, lre_get_flags(arr->buf));+ js_printf(ctx, "/%s", buf2);+}++JSValue js_regexp_get_flags(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSRegExp *re;+ JSByteArray *arr;+ size_t len;+ char buf[RE_FLAG_COUNT + 1];++ re = js_get_regexp(ctx, *this_val);+ if (!re)+ return JS_EXCEPTION;+ arr = JS_VALUE_TO_PTR(re->byte_code);+ len = js_regexp_flags_str(buf, lre_get_flags(arr->buf));+ return JS_NewStringLen(ctx, buf, len);+}++JSValue js_regexp_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue obj, byte_code;+ JSObject *p;+ JSGCRef byte_code_ref;++ argc &= ~FRAME_CF_CTOR;+ + argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;+ if (!JS_IsUndefined(argv[1])) {+ argv[1] = JS_ToString(ctx, argv[1]);+ if (JS_IsException(argv[1]))+ return JS_EXCEPTION;+ }+ byte_code = js_compile_regexp(ctx, argv[0], argv[1]);+ if (JS_IsException(byte_code))+ return JS_EXCEPTION;+ JS_PUSH_VALUE(ctx, byte_code);+ obj = JS_NewObjectClass(ctx, JS_CLASS_REGEXP, sizeof(JSRegExp));+ JS_POP_VALUE(ctx, byte_code);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_TO_PTR(obj);+ p->u.regexp.source = argv[0];+ p->u.regexp.byte_code = byte_code;+ p->u.regexp.last_index = 0;+ return obj;+}++enum {+ MAGIC_REGEXP_EXEC,+ MAGIC_REGEXP_TEST,+ MAGIC_REGEXP_SEARCH,+ MAGIC_REGEXP_FORCE_GLOBAL, /* same as exec but force the global flag */+};++JSValue js_regexp_exec(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic)+{+ JSObject *p;+ JSRegExp *re;+ JSValue obj, *capture_buf, res;+ uint32_t *capture, last_index_utf8;+ int rc, capture_count, i, re_flags, last_index;+ JSByteArray *bc_arr, *carr;+ JSGCRef capture_buf_ref, obj_ref;+ JSString *str;+ JSStringCharBuf str_buf;++ re = js_get_regexp(ctx, *this_val);+ if (!re)+ return JS_EXCEPTION;++ argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;++ p = JS_VALUE_TO_PTR(*this_val);+ re = &p->u.regexp;+ last_index = max_int(re->last_index, 0);++ bc_arr = JS_VALUE_TO_PTR(re->byte_code);+ re_flags = lre_get_flags(bc_arr->buf);+ if (magic == MAGIC_REGEXP_FORCE_GLOBAL)+ re_flags |= MAGIC_REGEXP_FORCE_GLOBAL;+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0 ||+ magic == MAGIC_REGEXP_SEARCH) {+ last_index = 0;+ }+ capture_count = lre_get_capture_count(bc_arr->buf);++ carr = js_alloc_byte_array(ctx, sizeof(uint32_t) * lre_get_alloc_count(bc_arr->buf));+ if (!carr)+ goto fail;+ capture_buf = JS_PushGCRef(ctx, &capture_buf_ref);+ *capture_buf = JS_VALUE_FROM_PTR(carr);+ capture = (uint32_t *)carr->buf;+ for(i = 0; i < 2 * capture_count; i++)+ capture[i] = -1;+ + if (last_index <= 0)+ last_index_utf8 = 0;+ else+ last_index_utf8 = js_string_utf16_to_utf8_pos(ctx, argv[0], last_index) / 2;+ if (last_index_utf8 > js_string_byte_len(ctx, argv[0])) {+ rc = 2;+ } else {+ p = JS_VALUE_TO_PTR(*this_val);+ re = &p->u.regexp;+ str = get_string_ptr(ctx, &str_buf, argv[0]);+ /* JS_VALUE_FROM_PTR(str) is acceptable here because the+ GC ignores pointers outside the heap */+ rc = lre_exec(ctx, *capture_buf, re->byte_code, JS_VALUE_FROM_PTR(str),+ last_index_utf8);+ }+ if (rc != 1) {+ if (rc >= 0) {+ if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {+ p = JS_VALUE_TO_PTR(*this_val);+ re = &p->u.regexp;+ re->last_index = 0;+ }+ if (magic == MAGIC_REGEXP_SEARCH)+ obj = JS_NewShortInt(-1);+ else if (magic == MAGIC_REGEXP_TEST)+ obj = JS_FALSE;+ else+ obj = JS_NULL;+ } else {+ goto fail;+ }+ } else {+ capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf;+ if (magic == MAGIC_REGEXP_SEARCH) {+ obj = JS_NewShortInt(js_string_utf8_to_utf16_pos(ctx, argv[0], capture[0] * 2));+ goto done;+ } + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {+ p = JS_VALUE_TO_PTR(*this_val);+ re = &p->u.regexp;+ re->last_index = js_string_utf8_to_utf16_pos(ctx, argv[0], capture[1] * 2);+ }+ if (magic == MAGIC_REGEXP_TEST) {+ obj = JS_TRUE;+ } else {+ obj = JS_NewArray(ctx, capture_count);+ if (JS_IsException(obj))+ goto fail;++ JS_PUSH_VALUE(ctx, obj);+ capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf;+ res = JS_DefinePropertyValue(ctx, obj, js_get_atom(ctx, JS_ATOM_index),+ JS_NewShortInt(js_string_utf8_to_utf16_pos(ctx, argv[0], capture[0] * 2)));+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(res))+ goto fail;++ JS_PUSH_VALUE(ctx, obj);+ res = JS_DefinePropertyValue(ctx, obj, js_get_atom(ctx, JS_ATOM_input),+ argv[0]);+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(res))+ goto fail;++ for(i = 0; i < capture_count; i++) {+ int start, end;+ JSValue val;++ capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf;+ start = capture[2 * i];+ end = capture[2 * i + 1];+ if (start != -1 && end != -1) {+ JSValueArray *arr;+ JS_PUSH_VALUE(ctx, obj);+ val = js_sub_string_utf8(ctx, argv[0], 2 * start, 2 * end);+ JS_POP_VALUE(ctx, obj);+ if (JS_IsException(val))+ goto fail;+ p = JS_VALUE_TO_PTR(obj);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);+ arr->arr[i] = val;+ }+ }+ }+ }+ done:+ JS_PopGCRef(ctx, &capture_buf_ref);+ return obj;+ fail:+ obj = JS_EXCEPTION;+ goto done;+}++/* if regexp replace: capture_buf != NULL, needle = NULL+ if string replace: capture_buf = NULL, captures_len = 1, needle != NULL+*/+static int js_string_concat_subst(JSContext *ctx, StringBuffer *b,+ JSValue *str, JSValue *rep,+ uint32_t pos, uint32_t end_of_match,+ JSValue *capture_buf, uint32_t captures_len,+ JSValue *needle)+{+ JSStringCharBuf buf_rep;+ JSString *p;+ int rep_len, i, j, j0, c, k;++ if (JS_IsFunction(ctx, *rep)) {+ JSValue res, val;+ JSGCRef val_ref;+ int ret;+ + if (JS_StackCheck(ctx, 4 + captures_len))+ return -1;+ JS_PushArg(ctx, *str);+ JS_PushArg(ctx, JS_NewShortInt(pos));+ if (capture_buf) {+ for(k = captures_len - 1; k >= 0; k--) {+ uint32_t *captures = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf;+ if (captures[2 * k] != -1 && captures[2 * k + 1] != -1) {+ val = js_sub_string_utf8(ctx, *str, captures[2 * k] * 2, captures[2 * k + 1] * 2);+ if (JS_IsException(val))+ return -1;+ JS_PUSH_VALUE(ctx, val);+ ret = JS_StackCheck(ctx, 3 + k);+ JS_POP_VALUE(ctx, val);+ if (ret)+ return -1;+ } else {+ val = JS_UNDEFINED;+ }+ JS_PushArg(ctx, val);+ }+ } else {+ JS_PushArg(ctx, *needle);+ }+ JS_PushArg(ctx, *rep); /* function */+ JS_PushArg(ctx, JS_UNDEFINED); /* this_val */+ res = JS_Call(ctx, 2 + captures_len);+ if (JS_IsException(res))+ return -1;+ return string_buffer_concat(ctx, b, res);+ }+ + p = get_string_ptr(ctx, &buf_rep, *rep);+ rep_len = p->len;+ i = 0;+ for(;;) {+ p = get_string_ptr(ctx, &buf_rep, *rep);+ j = i;+ while (j < rep_len && p->buf[j] != '$')+ j++;+ if (j + 1 >= rep_len)+ break;+ j0 = j++; /* j0 = position of '$' */+ c = p->buf[j++];+ string_buffer_concat_utf8(ctx, b, *rep, 2 * i, 2 * j0);+ if (c == '$') {+ string_buffer_putc(ctx, b, '$');+ } else if (c == '&') {+ if (capture_buf) {+ string_buffer_concat_utf16(ctx, b, *str, pos, end_of_match);+ } else {+ string_buffer_concat_str(ctx, b, *needle);+ }+ } else if (c == '`') {+ string_buffer_concat_utf16(ctx, b, *str, 0, pos);+ } else if (c == '\'') {+ string_buffer_concat_utf16(ctx, b, *str, end_of_match, js_string_len(ctx, *str));+ } else if (c >= '0' && c <= '9') {+ k = c - '0';+ if (j < rep_len) {+ c = p->buf[j];+ if (c >= '0' && c <= '9') {+ k = k * 10 + c - '0';+ j++;+ }+ }+ if (k >= 1 && k < captures_len) {+ uint32_t *captures = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf;+ if (captures[2 * k] != -1 && captures[2 * k + 1] != -1) {+ string_buffer_concat_utf8(ctx, b, *str,+ captures[2 * k] * 2, captures[2 * k + 1] * 2);+ }+ } else {+ goto no_rep;+ }+ } else {+ no_rep:+ string_buffer_concat_utf8(ctx, b, *rep, 2 * j0, 2 * j);+ }+ i = j;+ }+ return string_buffer_concat_utf8(ctx, b, *rep, 2 * i, 2 * rep_len);+}++JSValue js_string_replace(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_replaceAll)+{+ StringBuffer b_s, *b = &b_s;+ int pos, endOfLastMatch, needle_len, input_len;+ BOOL is_first, is_regexp;++ *this_val = JS_ToString(ctx, *this_val);+ if (JS_IsException(*this_val))+ return JS_EXCEPTION;+ is_regexp = (JS_GetClassID(ctx, argv[0]) == JS_CLASS_REGEXP);+ if (!is_regexp) {+ argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;+ }+ if (!JS_IsFunction(ctx, argv[1])) {+ argv[1] = JS_ToString(ctx, argv[1]);+ if (JS_IsException(argv[1]))+ return JS_EXCEPTION;+ }+ input_len = js_string_len(ctx, *this_val);+ endOfLastMatch = 0;++ string_buffer_push(ctx, b, 0);+ + if (is_regexp) {+ int start, end, last_index, ret, re_flags, i, capture_count;+ JSObject *p;+ JSByteArray *bc_arr, *carr;+ JSValue *capture_buf;+ uint32_t *capture;+ JSGCRef capture_buf_ref;+ + p = JS_VALUE_TO_PTR(argv[0]);+ bc_arr = JS_VALUE_TO_PTR(p->u.regexp.byte_code);+ re_flags = lre_get_flags(bc_arr->buf);+ capture_count = lre_get_capture_count(bc_arr->buf);++ if (re_flags & LRE_FLAG_GLOBAL)+ p->u.regexp.last_index = 0;+ + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {+ last_index = 0;+ } else {+ last_index = max_int(p->u.regexp.last_index, 0);+ }+ + carr = js_alloc_byte_array(ctx, sizeof(uint32_t) * lre_get_alloc_count(bc_arr->buf));+ if (!carr) {+ string_buffer_pop(ctx, b);+ return JS_EXCEPTION;+ }+ capture_buf = JS_PushGCRef(ctx, &capture_buf_ref);+ *capture_buf = JS_VALUE_FROM_PTR(carr);+ capture = (uint32_t *)carr->buf;+ for(i = 0; i < 2 * capture_count; i++)+ capture[i] = -1;++ for(;;) {+ if (last_index > input_len) {+ ret = 0;+ } else {+ JSString *str;+ JSStringCharBuf str_buf;+ p = JS_VALUE_TO_PTR(argv[0]);+ str = get_string_ptr(ctx, &str_buf, *this_val);+ /* JS_VALUE_FROM_PTR(str) is acceptable here because the+ GC ignores pointers outside the heap */+ ret = lre_exec(ctx, *capture_buf, p->u.regexp.byte_code,+ JS_VALUE_FROM_PTR(str),+ js_string_utf16_to_utf8_pos(ctx, *this_val, last_index) / 2);+ }+ if (ret < 0) {+ JS_PopGCRef(ctx, &capture_buf_ref);+ string_buffer_pop(ctx, b);+ return JS_EXCEPTION;+ }+ if (ret == 0) {+ if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {+ p = JS_VALUE_TO_PTR(argv[0]);+ p->u.regexp.last_index = 0;+ }+ break;+ }+ capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf;+ start = js_string_utf8_to_utf16_pos(ctx, *this_val, capture[0] * 2);+ end = js_string_utf8_to_utf16_pos(ctx, *this_val, capture[1] * 2);+ string_buffer_concat_utf16(ctx, b, *this_val, endOfLastMatch, start);+ js_string_concat_subst(ctx, b, this_val, &argv[1],+ start, end, capture_buf, capture_count, NULL);+ endOfLastMatch = end;+ if (!(re_flags & LRE_FLAG_GLOBAL)) {+ if (re_flags & LRE_FLAG_STICKY) {+ p = JS_VALUE_TO_PTR(argv[0]);+ p->u.regexp.last_index = end;+ }+ break;+ }+ if (end == start) {+ int c = string_getcp(ctx, *this_val, end, TRUE);+ /* since regexp are unicode by default, replace is also unicode by default */+ end += 1 + (c >= 0x10000);+ }+ last_index = end;+ }+ JS_PopGCRef(ctx, &capture_buf_ref);+ } else {+ needle_len = js_string_len(ctx, argv[0]);+ + is_first = TRUE;+ for(;;) {+ if (unlikely(needle_len == 0)) {+ if (is_first)+ pos = 0;+ else if (endOfLastMatch >= input_len)+ pos = -1;+ else+ pos = endOfLastMatch + 1;+ } else {+ pos = js_string_indexof(ctx, *this_val, argv[0], endOfLastMatch,+ input_len, needle_len);+ }+ if (pos < 0) {+ if (is_first) {+ string_buffer_pop(ctx, b);+ return *this_val;+ } else {+ break;+ }+ }+ + string_buffer_concat_utf16(ctx, b, *this_val, endOfLastMatch, pos);+ + js_string_concat_subst(ctx, b, this_val, &argv[1],+ pos, pos + needle_len, NULL, 1, &argv[0]);++ endOfLastMatch = pos + needle_len;+ is_first = FALSE;+ if (!is_replaceAll)+ break;+ }+ }+ string_buffer_concat_utf16(ctx, b, *this_val, endOfLastMatch, input_len);+ return string_buffer_pop(ctx, b);+}++// split(sep, limit)+JSValue js_string_split(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSValue *A, T, ret, *z;+ uint32_t lim, lengthA;+ int p, q, s, e;+ BOOL undef_sep;+ JSGCRef A_ref, z_ref;+ BOOL is_regexp;+ + *this_val = JS_ToString(ctx, *this_val);+ if (JS_IsException(*this_val))+ return JS_EXCEPTION;+ if (JS_IsUndefined(argv[1])) {+ lim = 0xffffffff;+ } else {+ if (JS_ToUint32(ctx, &lim, argv[1]) < 0)+ return JS_EXCEPTION;+ }+ is_regexp = (JS_GetClassID(ctx, argv[0]) == JS_CLASS_REGEXP);+ if (!is_regexp) {+ undef_sep = JS_IsUndefined(argv[0]);+ argv[0] = JS_ToString(ctx, argv[0]);+ if (JS_IsException(argv[0]))+ return JS_EXCEPTION;+ } else {+ undef_sep = FALSE;+ }+ + A = JS_PushGCRef(ctx, &A_ref);+ z = JS_PushGCRef(ctx, &z_ref);+ *A = JS_NewArray(ctx, 0);+ if (JS_IsException(*A))+ goto exception;+ lengthA = 0;++ s = js_string_len(ctx, *this_val);+ p = 0;+ if (lim == 0)+ goto done;+ if (undef_sep)+ goto add_tail;++ if (is_regexp) {+ int numberOfCaptures, i, re_flags;+ JSObject *p1;+ JSValueArray *arr;+ JSByteArray *bc_arr;+ + p1 = JS_VALUE_TO_PTR(argv[0]);+ bc_arr = JS_VALUE_TO_PTR(p1->u.regexp.byte_code);+ re_flags = lre_get_flags(bc_arr->buf);+ + if (s == 0) {+ p1 = JS_VALUE_TO_PTR(argv[0]);+ p1->u.regexp.last_index = 0;+ *z = js_regexp_exec(ctx, &argv[0], 1, this_val, MAGIC_REGEXP_FORCE_GLOBAL);+ if (JS_IsException(*z))+ goto exception;+ if (JS_IsNull(*z))+ goto add_tail;+ goto done;+ }+ q = 0;+ while (q < s) {+ p1 = JS_VALUE_TO_PTR(argv[0]);+ p1->u.regexp.last_index = q;+ /* XXX: need sticky behavior */+ *z = js_regexp_exec(ctx, &argv[0], 1, this_val, MAGIC_REGEXP_FORCE_GLOBAL);+ if (JS_IsException(*z))+ goto exception;+ if (JS_IsNull(*z)) {+ if (!(re_flags & LRE_FLAG_STICKY)) {+ break;+ } else {+ int c = string_getcp(ctx, *this_val, q, TRUE);+ /* since regexp are unicode by default, split is also unicode by default */+ q += 1 + (c >= 0x10000);+ }+ } else {+ if (!(re_flags & LRE_FLAG_STICKY)) {+ JSValue res;+ res = JS_GetProperty(ctx, *z, js_get_atom(ctx, JS_ATOM_index));+ if (JS_IsException(res))+ goto exception;+ q = JS_VALUE_GET_INT(res);+ }+ p1 = JS_VALUE_TO_PTR(argv[0]);+ e = p1->u.regexp.last_index;+ if (e > s)+ e = s;+ if (e == p) {+ int c = string_getcp(ctx, *this_val, q, TRUE);+ /* since regexp are unicode by default, split is also unicode by default */+ q += 1 + (c >= 0x10000);+ } else {+ T = js_sub_string(ctx, *this_val, p, q);+ if (JS_IsException(T))+ goto exception;+ ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T);+ if (JS_IsException(ret))+ goto exception;+ if (lengthA == lim)+ goto done;+ p1 = JS_VALUE_TO_PTR(*z);+ numberOfCaptures = p1->u.array.len;+ for(i = 1; i < numberOfCaptures; i++) {+ p1 = JS_VALUE_TO_PTR(*z);+ arr = JS_VALUE_TO_PTR(p1->u.array.tab);+ T = arr->arr[i];+ ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T);+ if (JS_IsException(ret))+ goto exception;+ }+ q = p = e;+ }+ }+ }+ } else {+ int r = js_string_len(ctx, argv[0]);+ if (s == 0) {+ if (r != 0)+ goto add_tail;+ goto done;+ }+ + for (q = 0; (q += !r) <= s - r - !r; q = p = e + r) {+ + e = js_string_indexof(ctx, *this_val, argv[0], q, s, r);+ if (e < 0)+ break;+ T = js_sub_string(ctx, *this_val, p, e);+ if (JS_IsException(T))+ goto exception;+ ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T);+ if (JS_IsException(ret))+ goto exception;+ if (lengthA == lim)+ goto done;+ }+ }+add_tail:+ T = js_sub_string(ctx, *this_val, p, s);+ if (JS_IsException(T))+ goto exception;+ ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T);+ if (JS_IsException(ret))+ goto exception;+ +done:+ JS_PopGCRef(ctx, &z_ref);+ return JS_PopGCRef(ctx, &A_ref);++exception:+ JS_PopGCRef(ctx, &z_ref);+ JS_PopGCRef(ctx, &A_ref);+ return JS_EXCEPTION;+}++JSValue js_string_match(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ JSRegExp *re;+ int global, n;+ JSValue *A, *result, ret;+ JSObject *p;+ JSValueArray *arr;+ JSByteArray *barr;+ JSGCRef A_ref, result_ref;+ + re = js_get_regexp(ctx, argv[0]);+ if (!re)+ return JS_EXCEPTION;+ barr = JS_VALUE_TO_PTR(re->byte_code);+ global = lre_get_flags(barr->buf) & LRE_FLAG_GLOBAL;+ if (!global)+ return js_regexp_exec(ctx, &argv[0], 1, this_val, 0);++ p = JS_VALUE_TO_PTR(argv[0]);+ re = &p->u.regexp;+ re->last_index = 0;++ A = JS_PushGCRef(ctx, &A_ref);+ result = JS_PushGCRef(ctx, &result_ref);+ *A = JS_NULL;+ n = 0;+ for(;;) {+ *result = js_regexp_exec(ctx, &argv[0], 1, this_val, 0);+ if (JS_IsException(*result))+ goto fail;+ if (*result == JS_NULL)+ break;+ if (*A == JS_NULL) {+ *A = JS_NewArray(ctx, 1);+ if (JS_IsException(*A))+ goto fail;+ }++ p = JS_VALUE_TO_PTR(*result);+ arr = JS_VALUE_TO_PTR(p->u.array.tab);++ ret = JS_SetPropertyUint32(ctx, *A, n++, arr->arr[0]);+ if (JS_IsException(ret)) {+ fail:+ *A = JS_EXCEPTION;+ break;+ }+ }+ JS_PopGCRef(ctx, &result_ref);+ return JS_PopGCRef(ctx, &A_ref);+}++JSValue js_string_search(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv)+{+ return js_regexp_exec(ctx, &argv[0], 1, this_val, MAGIC_REGEXP_SEARCH);+}
+ mquickjs/mquickjs.h view
@@ -0,0 +1,382 @@+/*+ * Micro QuickJS Javascript Engine+ *+ * Copyright (c) 2017-2025 Fabrice Bellard+ * Copyright (c) 2017-2025 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 MQUICKJS_H+#define MQUICKJS_H++#include <inttypes.h>++#if defined(__GNUC__) || defined(__clang__)+#define __js_printf_like(f, a) __attribute__((format(printf, f, a)))+#else+#define __js_printf_like(a, b)+#endif++#if INTPTR_MAX >= INT64_MAX+#define JS_PTR64 /* pointers are 64 bit wide instead of 32 bit wide */+#endif++typedef struct JSContext JSContext;++#ifdef JS_PTR64+typedef uint64_t JSWord;+typedef uint64_t JSValue;+#define JSW 8+#define JSValue_PRI PRIo64+#define JS_USE_SHORT_FLOAT+#else+typedef uint32_t JSWord;+typedef uint32_t JSValue;+#define JSW 4+#define JSValue_PRI PRIo32+#endif++#define JS_BOOL int++enum {+ JS_TAG_INT = 0, /* 31 bit integer (1 bit) */+ JS_TAG_PTR = 1, /* pointer (2 bits) */+ JS_TAG_SPECIAL = 3, /* other special values (2 bits) */+ JS_TAG_BOOL = JS_TAG_SPECIAL | (0 << 2), /* (5 bits) */+ JS_TAG_NULL = JS_TAG_SPECIAL | (1 << 2), /* (5 bits) */+ JS_TAG_UNDEFINED = JS_TAG_SPECIAL | (2 << 2), /* (5 bits) */+ JS_TAG_EXCEPTION = JS_TAG_SPECIAL | (3 << 2), /* (5 bits) */+ JS_TAG_SHORT_FUNC = JS_TAG_SPECIAL | (4 << 2), /* (5 bits) */+ JS_TAG_UNINITIALIZED = JS_TAG_SPECIAL | (5 << 2), /* (5 bits) */+ JS_TAG_STRING_CHAR = JS_TAG_SPECIAL | (6 << 2), /* (5 bits) */+ JS_TAG_CATCH_OFFSET = JS_TAG_SPECIAL | (7 << 2), /* (5 bits) */+#ifdef JS_USE_SHORT_FLOAT+ JS_TAG_SHORT_FLOAT = 5, /* 3 bits */+#endif+};++#define JS_TAG_SPECIAL_BITS 5++#define JS_VALUE_GET_INT(v) ((int)(v) >> 1)+#define JS_VALUE_GET_SPECIAL_VALUE(v) ((int)(v) >> JS_TAG_SPECIAL_BITS)+#define JS_VALUE_GET_SPECIAL_TAG(v) ((v) & ((1 << JS_TAG_SPECIAL_BITS) - 1))+#define JS_VALUE_MAKE_SPECIAL(tag, v) ((tag) | ((v) << JS_TAG_SPECIAL_BITS))++#define JS_NULL JS_VALUE_MAKE_SPECIAL(JS_TAG_NULL, 0)+#define JS_UNDEFINED JS_VALUE_MAKE_SPECIAL(JS_TAG_UNDEFINED, 0)+#define JS_UNINITIALIZED JS_VALUE_MAKE_SPECIAL(JS_TAG_UNINITIALIZED, 0)+#define JS_FALSE JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, 0)+#define JS_TRUE JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, 1)++#define JS_EX_NORMAL 0 /* all exceptions except not enough memory */+#define JS_EX_CALL 1 /* specific exception to generate a tail call. The call flags are added */+#define JS_EXCEPTION JS_VALUE_MAKE_SPECIAL(JS_TAG_EXCEPTION, JS_EX_NORMAL)++typedef enum {+ JS_CLASS_OBJECT,+ JS_CLASS_ARRAY,+ JS_CLASS_C_FUNCTION,+ JS_CLASS_CLOSURE,+ JS_CLASS_NUMBER,+ JS_CLASS_BOOLEAN,+ JS_CLASS_STRING,+ JS_CLASS_DATE,+ JS_CLASS_REGEXP,++ JS_CLASS_ERROR,+ JS_CLASS_EVAL_ERROR,+ JS_CLASS_RANGE_ERROR,+ JS_CLASS_REFERENCE_ERROR,+ JS_CLASS_SYNTAX_ERROR,+ JS_CLASS_TYPE_ERROR,+ JS_CLASS_URI_ERROR,+ JS_CLASS_INTERNAL_ERROR,++ JS_CLASS_ARRAY_BUFFER,+ JS_CLASS_TYPED_ARRAY,++ JS_CLASS_UINT8C_ARRAY,+ JS_CLASS_INT8_ARRAY,+ JS_CLASS_UINT8_ARRAY,+ JS_CLASS_INT16_ARRAY,+ JS_CLASS_UINT16_ARRAY,+ JS_CLASS_INT32_ARRAY,+ JS_CLASS_UINT32_ARRAY,+ JS_CLASS_FLOAT32_ARRAY,+ JS_CLASS_FLOAT64_ARRAY,++ JS_CLASS_USER, /* user classes start from this value */+} JSObjectClassEnum;++/* predefined functions */+typedef enum {+ JS_CFUNCTION_bound,+ JS_CFUNCTION_USER, /* user functions start from this value */+} JSCFunctionEnum;++/* temporary buffer to hold C strings */+typedef struct {+ uint8_t buf[5]; +} JSCStringBuf;++typedef struct JSGCRef {+ JSValue val;+ struct JSGCRef *prev;+} JSGCRef;++/* stack of JSGCRef */+JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);+JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);++#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0)+#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref)++/* list of JSGCRef (they can be removed in any order, slower) */+JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref);+void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref);++JSValue JS_NewFloat64(JSContext *ctx, double d);+JSValue JS_NewInt32(JSContext *ctx, int32_t val);+JSValue JS_NewUint32(JSContext *ctx, uint32_t val);+JSValue JS_NewInt64(JSContext *ctx, int64_t val);++static inline JS_BOOL JS_IsInt(JSValue v)+{+ return (v & 1) == JS_TAG_INT;+}++static inline JS_BOOL JS_IsPtr(JSValue v)+{+ return (v & (JSW - 1)) == JS_TAG_PTR;+}++#ifdef JS_USE_SHORT_FLOAT+static inline JS_BOOL JS_IsShortFloat(JSValue v)+{+ return (v & (JSW - 1)) == JS_TAG_SHORT_FLOAT;+}+#endif++static inline JS_BOOL JS_IsBool(JSValue v)+{+ return JS_VALUE_GET_SPECIAL_TAG(v) == JS_TAG_BOOL;+}++static inline JS_BOOL JS_IsNull(JSValue v)+{+ return v == JS_NULL;+}++static inline JS_BOOL JS_IsUndefined(JSValue v)+{+ return v == JS_UNDEFINED;+}++static inline JS_BOOL JS_IsUninitialized(JSValue v)+{+ return v == JS_UNINITIALIZED;+}++static inline JS_BOOL JS_IsException(JSValue v)+{+ return v == JS_EXCEPTION;+}++static inline JSValue JS_NewBool(int val)+{+ return JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, (val != 0));+}++JS_BOOL JS_IsNumber(JSContext *ctx, JSValue val);+JS_BOOL JS_IsString(JSContext *ctx, JSValue val);+JS_BOOL JS_IsError(JSContext *ctx, JSValue val);+JS_BOOL JS_IsFunction(JSContext *ctx, JSValue val);++int JS_GetClassID(JSContext *ctx, JSValue val);+void JS_SetOpaque(JSContext *ctx, JSValue val, void *opaque);+void *JS_GetOpaque(JSContext *ctx, JSValue val);++typedef JSValue JSCFunction(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv);+/* no JS function call be called from a C finalizer */+typedef void (*JSCFinalizer)(JSContext *ctx, void *opaque);++typedef enum JSCFunctionDefEnum { /* XXX: should rename for namespace isolation */+ JS_CFUNC_generic,+ JS_CFUNC_generic_magic,+ JS_CFUNC_constructor,+ JS_CFUNC_constructor_magic,+ JS_CFUNC_generic_params,+ JS_CFUNC_f_f,+} JSCFunctionDefEnum;++typedef union JSCFunctionType {+ JSCFunction *generic;+ JSValue (*generic_magic)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, int magic);+ JSCFunction *constructor;+ JSValue (*constructor_magic)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, int magic);+ JSValue (*generic_params)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, JSValue params);+ double (*f_f)(double f);+} JSCFunctionType;++typedef struct JSCFunctionDef {+ JSCFunctionType func;+ JSValue name;+ uint8_t def_type;+ uint8_t arg_count;+ int16_t magic;+} JSCFunctionDef;++typedef struct {+ const JSWord *stdlib_table;+ const JSCFunctionDef *c_function_table;+ const JSCFinalizer *c_finalizer_table;+ uint32_t stdlib_table_len;+ uint32_t stdlib_table_align;+ uint32_t sorted_atoms_offset;+ uint32_t global_object_offset;+ uint32_t class_count;+} JSSTDLibraryDef;++typedef void JSWriteFunc(void *opaque, const void *buf, size_t buf_len);+/* return != 0 if the JS code needs to be interrupted */+typedef int JSInterruptHandler(JSContext *ctx, void *opaque);++JSContext *JS_NewContext(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def);+/* if prepare_compilation is true, the context will be used to compile+ to a binary file. JS_NewContext2() is not expected to be used in+ the embedded version */+JSContext *JS_NewContext2(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def, JS_BOOL prepare_compilation);+void JS_FreeContext(JSContext *ctx);+void JS_SetContextOpaque(JSContext *ctx, void *opaque);+void JS_SetInterruptHandler(JSContext *ctx, JSInterruptHandler *interrupt_handler);+void JS_SetRandomSeed(JSContext *ctx, uint64_t seed);+JSValue JS_GetGlobalObject(JSContext *ctx);+JSValue JS_Throw(JSContext *ctx, JSValue obj);+JSValue __js_printf_like(3, 4) JS_ThrowError(JSContext *ctx, JSObjectClassEnum error_num,+ const char *fmt, ...);+#define JS_ThrowTypeError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_TYPE_ERROR, fmt, ##__VA_ARGS__)+#define JS_ThrowReferenceError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_REFERENCE_ERROR, fmt, ##__VA_ARGS__)+#define JS_ThrowInternalError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_INTERNAL_ERROR, fmt, ##__VA_ARGS__)+#define JS_ThrowRangeError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_RANGE_ERROR, fmt, ##__VA_ARGS__)+#define JS_ThrowSyntaxError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_SYNTAX_ERROR, fmt, ##__VA_ARGS__)+JSValue JS_ThrowOutOfMemory(JSContext *ctx);+JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, const char *str);+JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue obj, uint32_t idx);+JSValue JS_SetPropertyStr(JSContext *ctx, JSValue this_obj,+ const char *str, JSValue val);+JSValue JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj,+ uint32_t idx, JSValue val);+JSValue JS_NewObjectClassUser(JSContext *ctx, int class_id);+JSValue JS_NewObject(JSContext *ctx);+JSValue JS_NewArray(JSContext *ctx, int initial_len);+/* create a C function with an object parameter (closure) */+JSValue JS_NewCFunctionParams(JSContext *ctx, int func_idx, JSValue params);++#define JS_EVAL_RETVAL (1 << 0) /* return the last value instead of undefined (slower code) */+#define JS_EVAL_REPL (1 << 1) /* implicitly defined global variables in assignments */+#define JS_EVAL_STRIP_COL (1 << 2) /* strip column number debug information (save memory) */+#define JS_EVAL_JSON (1 << 3) /* parse as JSON and return the object */+#define JS_EVAL_REGEXP (1 << 4) /* internal use */+#define JS_EVAL_REGEXP_FLAGS_SHIFT 8 /* internal use */+JSValue JS_Parse(JSContext *ctx, const char *input, size_t input_len,+ const char *filename, int eval_flags);+JSValue JS_Run(JSContext *ctx, JSValue val);+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,+ const char *filename, int eval_flags);+void JS_GC(JSContext *ctx);+JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len);+JSValue JS_NewString(JSContext *ctx, const char *buf);+const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val, JSCStringBuf *buf);+const char *JS_ToCString(JSContext *ctx, JSValue val, JSCStringBuf *buf);+JSValue JS_ToString(JSContext *ctx, JSValue val);+int JS_ToInt32(JSContext *ctx, int *pres, JSValue val);+int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val);+int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val);+int JS_ToNumber(JSContext *ctx, double *pres, JSValue val);++JSValue JS_GetException(JSContext *ctx);+int JS_StackCheck(JSContext *ctx, uint32_t len);+void JS_PushArg(JSContext *ctx, JSValue val);+#define FRAME_CF_CTOR (1 << 16) /* also ored with argc in+ C constructors */+JSValue JS_Call(JSContext *ctx, int call_flags);++#define JS_BYTECODE_MAGIC 0xacfb++typedef struct {+ uint16_t magic; /* JS_BYTECODE_MAGIC */+ uint16_t version;+ uintptr_t base_addr;+ JSValue unique_strings;+ JSValue main_func;+} JSBytecodeHeader;++/* only used on the host when compiling to file */+void JS_PrepareBytecode(JSContext *ctx,+ JSBytecodeHeader *hdr,+ const uint8_t **pdata_buf, uint32_t *pdata_len,+ JSValue eval_code);+/* only used on the host when compiling to file */+int JS_RelocateBytecode2(JSContext *ctx, JSBytecodeHeader *hdr,+ uint8_t *buf, uint32_t buf_len,+ uintptr_t new_base_addr, JS_BOOL update_atoms);+#if JSW == 8+typedef struct {+ uint16_t magic; /* JS_BYTECODE_MAGIC */+ uint16_t version;+ uint32_t base_addr;+ uint32_t unique_strings;+ uint32_t main_func;+} JSBytecodeHeader32;++/* only used on the host when compiling to file. A 32 bit bytecode is generated on a 64 bit host. */+int JS_PrepareBytecode64to32(JSContext *ctx,+ JSBytecodeHeader32 *hdr,+ const uint8_t **pdata_buf, uint32_t *pdata_len,+ JSValue eval_code);+#endif++JS_BOOL JS_IsBytecode(const uint8_t *buf, size_t buf_len);+/* Relocate the bytecode in 'buf' so that it can be executed+ later. Return 0 if OK, != 0 if error */+int JS_RelocateBytecode(JSContext *ctx,+ uint8_t *buf, uint32_t buf_len);+/* Load the precompiled bytecode from 'buf'. 'buf' must be allocated+ as long as the JSContext exists. Use JS_Run() to execute+ it. warning: the bytecode is not checked so it should come from a+ trusted source. */+JSValue JS_LoadBytecode(JSContext *ctx, const uint8_t *buf);++/* debug functions */+void JS_SetLogFunc(JSContext *ctx, JSWriteFunc *write_func);+void JS_PrintValue(JSContext *ctx, JSValue val);+#define JS_DUMP_LONG (1 << 0) /* display object/array content */+#define JS_DUMP_NOQUOTE (1 << 1) /* strings: no quote for identifiers */+/* for low level dumps: don't dump special properties and use specific+ quotes to distinguish string chars, unique strings and normal+ strings */+#define JS_DUMP_RAW (1 << 2)+void JS_PrintValueF(JSContext *ctx, JSValue val, int flags);+void JS_DumpValueF(JSContext *ctx, const char *str,+ JSValue val, int flags);+void JS_DumpValue(JSContext *ctx, const char *str,+ JSValue val);+void JS_DumpMemory(JSContext *ctx, JS_BOOL is_long);++#endif /* MQUICKJS_H */
+ mquickjs/mquickjs_atom.h view
@@ -0,0 +1,76 @@+#define JS_ATOM_null 0+#define JS_ATOM_false 2+#define JS_ATOM_true 4+#define JS_ATOM_if 6+#define JS_ATOM_else 8+#define JS_ATOM_return 10+#define JS_ATOM_var 12+#define JS_ATOM_this 14+#define JS_ATOM_delete 16+#define JS_ATOM_void 18+#define JS_ATOM_typeof 20+#define JS_ATOM_new 22+#define JS_ATOM_in 24+#define JS_ATOM_instanceof 26+#define JS_ATOM_do 29+#define JS_ATOM_while 31+#define JS_ATOM_for 33+#define JS_ATOM_break 35+#define JS_ATOM_continue 37+#define JS_ATOM_switch 40+#define JS_ATOM_case 42+#define JS_ATOM_default 44+#define JS_ATOM_throw 46+#define JS_ATOM_try 48+#define JS_ATOM_catch 50+#define JS_ATOM_finally 52+#define JS_ATOM_function 54+#define JS_ATOM_debugger 57+#define JS_ATOM_with 60+#define JS_ATOM_class 62+#define JS_ATOM_const 64+#define JS_ATOM_enum 66+#define JS_ATOM_export 68+#define JS_ATOM_extends 70+#define JS_ATOM_import 72+#define JS_ATOM_super 74+#define JS_ATOM_implements 76+#define JS_ATOM_interface 79+#define JS_ATOM_let 82+#define JS_ATOM_package 84+#define JS_ATOM_private 86+#define JS_ATOM_protected 88+#define JS_ATOM_public 91+#define JS_ATOM_static 93+#define JS_ATOM_yield 95+#define JS_ATOM_empty 97+#define JS_ATOM_toString 99+#define JS_ATOM_valueOf 102+#define JS_ATOM_number 104+#define JS_ATOM_object 106+#define JS_ATOM_undefined 108+#define JS_ATOM_string 111+#define JS_ATOM_boolean 113+#define JS_ATOM__ret_ 115+#define JS_ATOM__eval_ 117+#define JS_ATOM_eval 119+#define JS_ATOM_arguments 121+#define JS_ATOM_value 124+#define JS_ATOM_get 126+#define JS_ATOM_set 128+#define JS_ATOM_prototype 130+#define JS_ATOM_constructor 133+#define JS_ATOM_length 136+#define JS_ATOM_target 138+#define JS_ATOM_of 140+#define JS_ATOM_NaN 142+#define JS_ATOM_Infinity 144+#define JS_ATOM__Infinity 147+#define JS_ATOM_name 150+#define JS_ATOM_Error 152+#define JS_ATOM___proto__ 154+#define JS_ATOM_index 157+#define JS_ATOM_input 159++#define JS_ATOM_END 161+
+ mquickjs/mquickjs_build.h view
@@ -0,0 +1,97 @@+/*+ * Micro QuickJS build utility+ *+ * Copyright (c) 2017-2025 Fabrice Bellard+ * Copyright (c) 2017-2025 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 MQUICKJS_BUILD_H+#define MQUICKJS_BUILD_H++#include <stdlib.h>+#include <inttypes.h>++enum {+ JS_DEF_END,+ JS_DEF_CFUNC,+ JS_DEF_CGETSET,+ JS_DEF_PROP_DOUBLE,+ JS_DEF_PROP_UNDEFINED,+ JS_DEF_PROP_STRING,+ JS_DEF_PROP_NULL,+ JS_DEF_CLASS,+};++typedef struct JSClassDef JSClassDef;++typedef struct JSPropDef {+ int def_type;+ const char *name;+ union {+ struct {+ uint8_t length;+ const char *magic;+ const char *cproto_name;+ const char *func_name;+ } func;+ struct {+ const char *magic;+ const char *cproto_name;+ const char *get_func_name;+ const char *set_func_name;+ } getset;+ double f64;+ const JSClassDef *class1;+ const char *str;+ } u;+} JSPropDef;++typedef struct JSClassDef {+ const char *name;+ int length;+ const char *cproto_name;+ const char *func_name;+ const char *class_id;+ const JSPropDef *class_props; /* NULL if none */+ const JSPropDef *proto_props; /* NULL if none */+ const JSClassDef *parent_class; /* NULL if none */+ const char *finalizer_name; /* "NULL" if none */+} JSClassDef;++#define JS_PROP_END { JS_DEF_END }+#define JS_CFUNC_DEF(name, length, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", "generic", #func_name } } }+#define JS_CFUNC_MAGIC_DEF(name, length, func_name, magic) { JS_DEF_CFUNC, name, { .func = { length, #magic, "generic_magic", #func_name } } }+#define JS_CFUNC_SPECIAL_DEF(name, length, proto, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", #proto, #func_name } } }+#define JS_CGETSET_DEF(name, get_name, set_name) { JS_DEF_CGETSET, name, { .getset = { "0", "generic", #get_name, #set_name } } }+#define JS_CGETSET_MAGIC_DEF(name, get_name, set_name, magic) { JS_DEF_CGETSET, name, { .getset = { #magic, "generic_magic", #get_name, #set_name } } }+#define JS_PROP_CLASS_DEF(name, cl) { JS_DEF_CLASS, name, { .class1 = cl } }+#define JS_PROP_DOUBLE_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } }+#define JS_PROP_UNDEFINED_DEF(name, flags) { JS_DEF_PROP_UNDEFINED, name }+#define JS_PROP_NULL_DEF(name, flags) { JS_DEF_PROP_NULL, name }+#define JS_PROP_STRING_DEF(name, cstr, flags) { JS_DEF_PROP_STRING, name, { .str = cstr } }++#define JS_CLASS_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name }+#define JS_CLASS_MAGIC_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor_magic", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name }+#define JS_OBJECT_DEF(name, obj_props) { name, 0, NULL, NULL, NULL, obj_props, NULL, NULL, NULL }++int build_atoms(const char *stdlib_name, const JSPropDef *global_obj,+ const JSPropDef *c_function_decl, int argc, char **argv);++#endif /* MQUICKJS_BUILD_H */
+ mquickjs/mquickjs_opcode.h view
@@ -0,0 +1,264 @@+/*+ * Micro QuickJS opcode definitions+ *+ * Copyright (c) 2017-2025 Fabrice Bellard+ * Copyright (c) 2017-2025 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.+ */+#ifdef FMT+FMT(none)+FMT(none_int)+FMT(none_loc)+FMT(none_arg)+FMT(none_var_ref)+FMT(u8)+FMT(i8)+FMT(loc8)+FMT(const8)+FMT(label8)+FMT(u16)+FMT(i16)+FMT(label16)+FMT(npop)+FMT(npopx)+FMT(loc)+FMT(arg)+FMT(var_ref)+FMT(u32)+FMT(i32)+FMT(const16)+FMT(label)+FMT(value)+#undef FMT+#endif /* FMT */++#ifdef DEF++#ifndef def+#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)+#endif++DEF(invalid, 1, 0, 0, none) /* never emitted */++/* push values */+DEF( push_value, 5, 0, 1, value)+DEF( push_const, 3, 0, 1, const16)+DEF( fclosure, 3, 0, 1, const16)+DEF( undefined, 1, 0, 1, none)+DEF( null, 1, 0, 1, none)+DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */+DEF( push_false, 1, 0, 1, none)+DEF( push_true, 1, 0, 1, none)+DEF( object, 3, 0, 1, u16)+DEF( this_func, 1, 0, 1, none)+DEF( arguments, 1, 0, 1, none)+DEF( new_target, 1, 0, 1, none)++DEF( drop, 1, 1, 0, none) /* a -> */+DEF( nip, 1, 2, 1, none) /* a b -> b */+//DEF( nip1, 1, 3, 2, none) /* a b c -> b c */+DEF( dup, 1, 1, 2, none) /* a -> a a */+DEF( dup1, 1, 2, 3, none) /* a b -> a a b */+DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */+//DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */+DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */+DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */+//DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */+DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */+DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */+//DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */+DEF( swap, 1, 2, 2, none) /* a b -> b a */+//DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */+DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */+//DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */+//DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */+//DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */++DEF(call_constructor, 3, 1, 1, npop) /* func args... -> ret (arguments are not counted in n_pop) */+DEF( call, 3, 1, 1, npop) /* func args... -> ret (arguments are not counted in n_pop) */+DEF( call_method, 3, 2, 1, npop) /* this func args.. -> ret (arguments are not counted in n_pop) */+DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */+DEF( return, 1, 1, 0, none)+DEF( return_undef, 1, 0, 0, none)+DEF( throw, 1, 1, 0, none)+DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a bytecode string */++DEF( get_field, 3, 1, 1, const16) /* obj -> val */+DEF( get_field2, 3, 1, 2, const16) /* obj -> obj val */+DEF( put_field, 3, 2, 0, const16) /* obj val -> */+DEF( get_array_el, 1, 2, 1, none) /* obj prop -> val */+DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */+DEF( put_array_el, 1, 3, 0, none) /* obj prop val -> */+DEF( get_length, 1, 1, 1, none) /* obj -> val */+DEF( get_length2, 1, 1, 2, none) /* obj -> obj val */+DEF( define_field, 3, 2, 1, const16) /* obj val -> obj */+DEF( define_getter, 3, 2, 1, const16) /* obj val -> obj */+DEF( define_setter, 3, 2, 1, const16) /* obj val -> obj */+DEF( set_proto, 1, 2, 1, none) /* obj proto -> obj */++DEF( get_loc, 3, 0, 1, loc)+DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */+DEF( get_arg, 3, 0, 1, arg)+DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */+DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */+DEF(get_var_ref_nocheck, 3, 0, 1, var_ref) +DEF(put_var_ref_nocheck, 3, 1, 0, var_ref)+DEF( if_false, 5, 1, 0, label)+DEF( if_true, 5, 1, 0, label) /* must come after if_false */+DEF( goto, 5, 0, 0, label) /* must come after if_true */+DEF( catch, 5, 0, 1, label)+DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */+DEF( ret, 1, 1, 0, none) /* used to return from the finally block */++DEF( for_in_start, 1, 1, 1, none) /* obj -> iter */+DEF( for_of_start, 1, 1, 1, none) /* obj -> iter */+DEF( for_of_next, 1, 1, 3, none) /* iter -> iter val done */++/* arithmetic/logic operations */+DEF( neg, 1, 1, 1, none)+DEF( plus, 1, 1, 1, none)+DEF( dec, 1, 1, 1, none)+DEF( inc, 1, 1, 1, none)+DEF( post_dec, 1, 1, 2, none)+DEF( post_inc, 1, 1, 2, none)+DEF( not, 1, 1, 1, none)+DEF( lnot, 1, 1, 1, none)+DEF( typeof, 1, 1, 1, none)+DEF( delete, 1, 2, 1, none) /* obj prop -> ret */++DEF( mul, 1, 2, 1, none)+DEF( div, 1, 2, 1, none)+DEF( mod, 1, 2, 1, none)+DEF( add, 1, 2, 1, none)+DEF( sub, 1, 2, 1, none)+DEF( pow, 1, 2, 1, none)+DEF( shl, 1, 2, 1, none)+DEF( sar, 1, 2, 1, none)+DEF( shr, 1, 2, 1, none)+DEF( lt, 1, 2, 1, none)+DEF( lte, 1, 2, 1, none)+DEF( gt, 1, 2, 1, none)+DEF( gte, 1, 2, 1, none)+DEF( instanceof, 1, 2, 1, none)+DEF( in, 1, 2, 1, none)+DEF( eq, 1, 2, 1, none)+DEF( neq, 1, 2, 1, none)+DEF( strict_eq, 1, 2, 1, none)+DEF( strict_neq, 1, 2, 1, none)+DEF( and, 1, 2, 1, none)+DEF( xor, 1, 2, 1, none)+DEF( or, 1, 2, 1, none)+/* must be the last non short and non temporary opcode */+DEF( nop, 1, 0, 0, none) ++DEF( push_minus1, 1, 0, 1, none_int)+DEF( push_0, 1, 0, 1, none_int)+DEF( push_1, 1, 0, 1, none_int)+DEF( push_2, 1, 0, 1, none_int)+DEF( push_3, 1, 0, 1, none_int)+DEF( push_4, 1, 0, 1, none_int)+DEF( push_5, 1, 0, 1, none_int)+DEF( push_6, 1, 0, 1, none_int)+DEF( push_7, 1, 0, 1, none_int)+DEF( push_i8, 2, 0, 1, i8)+DEF( push_i16, 3, 0, 1, i16)+DEF( push_const8, 2, 0, 1, const8)+DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */+DEF(push_empty_string, 1, 0, 1, none)++DEF( get_loc8, 2, 0, 1, loc8)+DEF( put_loc8, 2, 1, 0, loc8) /* must follow get_loc8 */++DEF( get_loc0, 1, 0, 1, none_loc)+DEF( get_loc1, 1, 0, 1, none_loc)+DEF( get_loc2, 1, 0, 1, none_loc)+DEF( get_loc3, 1, 0, 1, none_loc)+DEF( put_loc0, 1, 1, 0, none_loc) /* must follow get_loc */+DEF( put_loc1, 1, 1, 0, none_loc)+DEF( put_loc2, 1, 1, 0, none_loc)+DEF( put_loc3, 1, 1, 0, none_loc)+DEF( get_arg0, 1, 0, 1, none_arg)+DEF( get_arg1, 1, 0, 1, none_arg)+DEF( get_arg2, 1, 0, 1, none_arg)+DEF( get_arg3, 1, 0, 1, none_arg)+DEF( put_arg0, 1, 1, 0, none_arg) /* must follow get_arg */+DEF( put_arg1, 1, 1, 0, none_arg)+DEF( put_arg2, 1, 1, 0, none_arg)+DEF( put_arg3, 1, 1, 0, none_arg)+#if 0+DEF( if_false8, 2, 1, 0, label8)+DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */+DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */+DEF( goto16, 3, 0, 0, label16)++DEF( call0, 1, 1, 1, npopx)+DEF( call1, 1, 1, 1, npopx)+DEF( call2, 1, 1, 1, npopx)+DEF( call3, 1, 1, 1, npopx)+#endif++#undef DEF+#undef def+#endif /* DEF */++#ifdef REDEF++/* regular expression bytecode */+REDEF(invalid, 1) /* never used */+REDEF(char1, 2)+REDEF(char2, 3)+REDEF(char3, 4)+REDEF(char4, 5)+REDEF(dot, 1)+REDEF(any, 1) /* same as dot but match any character including line terminator */+REDEF(space, 1)+REDEF(not_space, 1) /* must come after */+REDEF(line_start, 1)+REDEF(line_start_m, 1)+REDEF(line_end, 1)+REDEF(line_end_m, 1)+REDEF(goto, 5)+REDEF(split_goto_first, 5)+REDEF(split_next_first, 5)+REDEF(match, 1)+REDEF(lookahead_match, 1)+REDEF(negative_lookahead_match, 1) /* must come after */+REDEF(save_start, 2) /* save start position */+REDEF(save_end, 2) /* save end position, must come after saved_start */+REDEF(save_reset, 3) /* reset save positions */+REDEF(loop, 6) /* decrement the top the stack and goto if != 0 */+REDEF(loop_split_goto_first, 10) /* loop and then split */+REDEF(loop_split_next_first, 10)+REDEF(loop_check_adv_split_goto_first, 10) /* loop and then check advance and split */+REDEF(loop_check_adv_split_next_first, 10)+REDEF(set_i32, 6) /* store the immediate value to a register */+REDEF(word_boundary, 1)+REDEF(not_word_boundary, 1)+REDEF(back_reference, 2)+REDEF(back_reference_i, 2)+REDEF(range8, 2) /* variable length */+REDEF(range, 3) /* variable length */+REDEF(lookahead, 5)+REDEF(negative_lookahead, 5) /* must come after */+REDEF(set_char_pos, 2) /* store the character position to a register */+REDEF(check_advance, 2) /* check that the register is different from the character position */++#endif /* REDEF */
+ mquickjs/mquickjs_priv.h view
@@ -0,0 +1,268 @@+/* microj private definitions */+#ifndef MICROJS_PRIV_H+#define MICROJS_PRIV_H++#include "mquickjs.h"+#include "libm.h"++#define JS_DUMP /* 2.6 kB */+//#define DUMP_EXEC+//#define DUMP_FUNC_BYTECODE /* dump the bytecode of each compiled function */+//#define DUMP_REOP /* dump regexp bytecode */+//#define DUMP_GC+//#define DUMP_TOKEN /* dump parsed tokens */+/* run GC before at each malloc() and modify the allocated data pointers */+//#define DEBUG_GC+#if defined(DUMP_FUNC_BYTECODE) || defined(DUMP_EXEC)+#define DUMP_BYTECODE /* include the dump_byte_code() function */+#endif++#define JS_VALUE_TO_PTR(v) (void *)((uintptr_t)(v) - 1)+#define JS_VALUE_FROM_PTR(ptr) (JSWord)((uintptr_t)(ptr) + 1)++#define JS_IS_ROM_PTR(ctx, ptr) ((uintptr_t)(ptr) < (uintptr_t)ctx || (uintptr_t)(ptr) >= (uintptr_t)ctx->stack_top)++enum {+ JS_MTAG_FREE,+ /* javascript values */+ JS_MTAG_OBJECT,+ JS_MTAG_FLOAT64,+ JS_MTAG_STRING,+ /* other special memory blocks */+ JS_MTAG_FUNCTION_BYTECODE,+ JS_MTAG_VALUE_ARRAY,+ JS_MTAG_BYTE_ARRAY,+ JS_MTAG_VARREF,++ JS_MTAG_COUNT,+};++/* JS_MTAG_BITS bits are reserved at the start of every memory block */+#define JS_MTAG_BITS 4++#define JS_MB_HEADER \+ JSWord gc_mark: 1; \+ JSWord mtag: (JS_MTAG_BITS - 1)++typedef enum {+ JS_PROP_NORMAL,+ JS_PROP_GETSET, /* value is a two element JSValueArray */+ JS_PROP_VARREF, /* value is a JSVarRef (used for global variables) */+ JS_PROP_SPECIAL, /* for the prototype and constructor properties in ROM */+} JSPropTypeEnum;++#define JS_MB_HEADER_DEF(tag) ((tag) << 1)+#define JS_VALUE_ARRAY_HEADER(size) (JS_MB_HEADER_DEF(JS_MTAG_VALUE_ARRAY) | ((size) << JS_MTAG_BITS))++#define JS_ROM_VALUE(offset) JS_VALUE_FROM_PTR(&js_stdlib_table[offset])++/* runtime helpers */+JSValue js_function_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_get_prototype(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_set_prototype(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_get_length_name(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_name);+JSValue js_function_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_call(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_apply(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_bind(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_function_bound(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, JSValue params);++JSValue js_array_get_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_set_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_number_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_number_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_number_toFixed(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_number_toExponential(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_number_toPrecision(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_number_parseInt(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_number_parseFloat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_boolean_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_string_get_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_set_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_slice(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_substring(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+enum {+ magic_internalAt,+ magic_charAt,+ magic_charCodeAt,+ magic_codePointAt,+};+JSValue js_string_charAt(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);+JSValue js_string_concat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_indexOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int lastIndexOf);+JSValue js_string_match(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_replace(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_replaceAll);+JSValue js_string_search(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_split(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_toLowerCase(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int to_lower);+JSValue js_string_trim(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);+JSValue js_string_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_repeat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_object_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_defineProperty(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_create(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_keys(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_object_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_string_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_string_fromCharCode(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_fromCodePoint);++JSValue js_error_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);+JSValue js_error_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_error_get_message(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);++JSValue js_array_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_push(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_unshift);+JSValue js_array_pop(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_shift(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_join(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_toString(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_isArray(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_reverse(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_concat(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_indexOf(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_lastIndexOf);+JSValue js_array_slice(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_splice(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_sort(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++#define js_special_every 0+#define js_special_some 1+#define js_special_forEach 2+#define js_special_map 3+#define js_special_filter 4++JSValue js_array_every(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int special);++#define js_special_reduce 0+#define js_special_reduceRight 1++JSValue js_array_reduce(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int special);++JSValue js_math_min_max(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);+double js_math_sign(double a);+double js_math_fround(double a);+JSValue js_math_imul(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_math_clz32(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_math_atan2(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_math_pow(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_math_random(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_array_buffer_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_typed_array_base_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_typed_array_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);+JSValue js_typed_array_get_length(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int magic);+JSValue js_typed_array_subarray(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_typed_array_set(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_date_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_global_eval(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_global_isNaN(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_global_isFinite(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_json_parse(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_json_stringify(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);++JSValue js_regexp_constructor(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_regexp_get_lastIndex(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_regexp_set_lastIndex(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_regexp_get_source(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_regexp_get_flags(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv);+JSValue js_regexp_exec(JSContext *ctx, JSValue *this_val,+ int argc, JSValue *argv, int is_test);++#endif /* MICROJS_PRIV_H */
+ mquickjs/softfp_template.h view
@@ -0,0 +1,970 @@+/*+ * SoftFP Library+ * + * Copyright (c) 2016 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.+ */+#if F_SIZE == 32+#define F_UINT uint32_t+#define F_ULONG uint64_t+#define MANT_SIZE 23+#define EXP_SIZE 8+#elif F_SIZE == 64+#define F_UHALF uint32_t+#define F_UINT uint64_t+#ifdef HAVE_INT128+#define F_ULONG uint128_t+#endif+#define MANT_SIZE 52+#define EXP_SIZE 11+#elif F_SIZE == 128+#define F_UHALF uint64_t+#define F_UINT uint128_t+#define MANT_SIZE 112+#define EXP_SIZE 15+#else+#error unsupported F_SIZE+#endif++#define EXP_MASK ((1 << EXP_SIZE) - 1)+#define MANT_MASK (((F_UINT)1 << MANT_SIZE) - 1)+#define SIGN_MASK ((F_UINT)1 << (F_SIZE - 1))+#define IMANT_SIZE (F_SIZE - 2) /* internal mantissa size */+#define RND_SIZE (IMANT_SIZE - MANT_SIZE)+#define QNAN_MASK ((F_UINT)1 << (MANT_SIZE - 1))+#define EXP_BIAS ((1 << (EXP_SIZE - 1)) - 1)++/* quiet NaN */+#define F_QNAN glue(F_QNAN, F_SIZE)+#define clz glue(clz, F_SIZE)+#define pack_sf glue(pack_sf, F_SIZE)+#define unpack_sf glue(unpack_sf, F_SIZE)+#define rshift_rnd glue(rshift_rnd, F_SIZE)+#define round_pack_sf glue(roundpack_sf, F_SIZE)+#define normalize_sf glue(normalize_sf, F_SIZE)+#define normalize2_sf glue(normalize2_sf, F_SIZE)+#define issignan_sf glue(issignan_sf, F_SIZE)+#define isnan_sf glue(isnan_sf, F_SIZE)+#define add_sf glue(add_sf, F_SIZE)+#define mul_sf glue(mul_sf, F_SIZE)+#define fma_sf glue(fma_sf, F_SIZE)+#define div_sf glue(div_sf, F_SIZE)+#define sqrt_sf glue(sqrt_sf, F_SIZE)+#define normalize_subnormal_sf glue(normalize_subnormal_sf, F_SIZE)+#define divrem_u glue(divrem_u, F_SIZE)+#define sqrtrem_u glue(sqrtrem_u, F_SIZE)+#define mul_u glue(mul_u, F_SIZE)+#define cvt_sf32_sf glue(cvt_sf32_sf, F_SIZE)+#define cvt_sf64_sf glue(cvt_sf64_sf, F_SIZE)++static const F_UINT F_QNAN = (((F_UINT)EXP_MASK << MANT_SIZE) | ((F_UINT)1 << (MANT_SIZE - 1)));++static inline F_UINT pack_sf(uint32_t a_sign, uint32_t a_exp, F_UINT a_mant)+{+ return ((F_UINT)a_sign << (F_SIZE - 1)) |+ ((F_UINT)a_exp << MANT_SIZE) | + (a_mant & MANT_MASK);+}++static inline F_UINT unpack_sf(uint32_t *pa_sign, int32_t *pa_exp,+ F_UINT a)+{+ *pa_sign = a >> (F_SIZE - 1);+ *pa_exp = (a >> MANT_SIZE) & EXP_MASK;+ return a & MANT_MASK;+} ++static F_UINT rshift_rnd(F_UINT a, int d)+{+ F_UINT mask;+ if (d != 0) {+ if (d >= F_SIZE) {+ a = (a != 0);+ } else {+ mask = ((F_UINT)1 << d) - 1;+ a = (a >> d) | ((a & mask) != 0);+ }+ }+ return a;+}++#if F_USE_FFLAGS+#define FFLAGS_PARAM , uint32_t *pfflags+#define FFLAGS_ARG , pfflags+#else+#define FFLAGS_PARAM+#define FFLAGS_ARG+#endif++/* a_mant is considered to have its MSB at F_SIZE - 2 bits */+static F_UINT round_pack_sf(uint32_t a_sign, int a_exp, F_UINT a_mant,+ RoundingModeEnum rm FFLAGS_PARAM)+{+ int diff;+ uint32_t addend, rnd_bits;++ switch(rm) {+ case RM_RNE:+ case RM_RMM:+ addend = (1 << (RND_SIZE - 1));+ break;+ case RM_RTZ:+ addend = 0;+ break;+ default:+ case RM_RDN:+ case RM_RUP:+ // printf("s=%d rm=%d m=%x\n", a_sign, rm, a_mant);+ if (a_sign ^ (rm & 1))+ addend = (1 << RND_SIZE) - 1;+ else+ addend = 0;+ break;+ }++ /* potentially subnormal */+ if (a_exp <= 0) {+ BOOL is_subnormal;+ /* Note: we set the underflow flag if the rounded result+ is subnormal and inexact */+ is_subnormal = (a_exp < 0 || + (a_mant + addend) < ((F_UINT)1 << (F_SIZE - 1)));+ diff = 1 - a_exp;+ a_mant = rshift_rnd(a_mant, diff);+ rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);+ if (is_subnormal && rnd_bits != 0) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_UNDERFLOW;+#endif + }+ a_exp = 1;+ } else {+ rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);+ }+#if F_USE_FFLAGS+ if (rnd_bits != 0)+ *pfflags |= FFLAG_INEXACT;+#endif + a_mant = (a_mant + addend) >> RND_SIZE;+ /* half way: select even result */+ if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))+ a_mant &= ~1;+ /* Note the rounding adds at least 1, so this is the maximum+ value */+ a_exp += a_mant >> (MANT_SIZE + 1);+ if (a_mant <= MANT_MASK) {+ /* denormalized or zero */+ a_exp = 0;+ } else if (a_exp >= EXP_MASK) {+ /* overflow */+ if (addend == 0) {+ a_exp = EXP_MASK - 1;+ a_mant = MANT_MASK;+ } else {+ /* infinity */+ a_exp = EXP_MASK;+ a_mant = 0;+ }+#if F_USE_FFLAGS+ *pfflags |= FFLAG_OVERFLOW | FFLAG_INEXACT;+#endif+ }+ return pack_sf(a_sign, a_exp, a_mant);+}++/* a_mant is considered to have at most F_SIZE - 1 bits */+static F_UINT normalize_sf(uint32_t a_sign, int a_exp, F_UINT a_mant,+ RoundingModeEnum rm FFLAGS_PARAM)+{+ int shift;+ shift = clz(a_mant) - (F_SIZE - 1 - IMANT_SIZE);+ assert(shift >= 0);+ a_exp -= shift;+ a_mant <<= shift;+ return round_pack_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);+}++static inline F_UINT normalize_subnormal_sf(int32_t *pa_exp, F_UINT a_mant)+{+ int shift;+ shift = MANT_SIZE - ((F_SIZE - 1 - clz(a_mant)));+ *pa_exp = 1 - shift;+ return a_mant << shift;+}++#if F_USE_FFLAGS+F_STATIC BOOL issignan_sf(F_UINT a)+{+ uint32_t a_exp1;+ F_UINT a_mant;+ a_exp1 = (a >> (MANT_SIZE - 1)) & ((1 << (EXP_SIZE + 1)) - 1);+ a_mant = a & MANT_MASK;+ return (a_exp1 == (2 * EXP_MASK) && a_mant != 0);+}+#endif++#ifndef F_NORMALIZE_ONLY++F_STATIC BOOL isnan_sf(F_UINT a)+{+ uint32_t a_exp;+ F_UINT a_mant;+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ a_mant = a & MANT_MASK;+ return (a_exp == EXP_MASK && a_mant != 0);+}+++F_STATIC F_UINT add_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)+{+ uint32_t a_sign, b_sign, a_exp, b_exp;+ F_UINT tmp, a_mant, b_mant;++ /* swap so that abs(a) >= abs(b) */+ if ((a & ~SIGN_MASK) < (b & ~SIGN_MASK)) {+ tmp = a;+ a = b;+ b = tmp;+ }+ a_sign = a >> (F_SIZE - 1);+ b_sign = b >> (F_SIZE - 1);+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ b_exp = (b >> MANT_SIZE) & EXP_MASK;+ a_mant = (a & MANT_MASK) << 3;+ b_mant = (b & MANT_MASK) << 3;+ if (unlikely(a_exp == EXP_MASK)) {+ if (a_mant != 0) {+ /* NaN result */+#if F_USE_FFLAGS+ if (!(a_mant & (QNAN_MASK << 3)) || issignan_sf(b))+ *pfflags |= FFLAG_INVALID_OP;+#endif+ return F_QNAN;+ } else if (b_exp == EXP_MASK && a_sign != b_sign) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif+ return F_QNAN;+ } else {+ /* infinity */+ return a;+ }+ }+ if (a_exp == 0) {+ a_exp = 1;+ } else {+ a_mant |= (F_UINT)1 << (MANT_SIZE + 3);+ }+ if (b_exp == 0) {+ b_exp = 1;+ } else {+ b_mant |= (F_UINT)1 << (MANT_SIZE + 3);+ }+ b_mant = rshift_rnd(b_mant, a_exp - b_exp);+ if (a_sign == b_sign) {+ /* same signs : add the absolute values */+ a_mant += b_mant;+ } else {+ /* different signs : subtract the absolute values */+ a_mant -= b_mant;+ if (a_mant == 0) {+ /* zero result : the sign needs a specific handling */+ a_sign = (rm == RM_RDN);+ }+ }+ a_exp += (RND_SIZE - 3);+ return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);+}++F_STATIC F_UINT glue(sub_sf, F_SIZE)(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)+{+ return add_sf(a, b ^ SIGN_MASK, rm FFLAGS_ARG);+}++#ifdef F_ULONG++static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b)+{+ F_ULONG r;+ r = (F_ULONG)a * (F_ULONG)b;+ *plow = r;+ return r >> F_SIZE;+}++#else++#define FH_SIZE (F_SIZE / 2)++static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b)+{+ F_UHALF a0, a1, b0, b1, r0, r1, r2, r3;+ F_UINT r00, r01, r10, r11, c;+ a0 = a;+ a1 = a >> FH_SIZE;+ b0 = b;+ b1 = b >> FH_SIZE;++ r00 = (F_UINT)a0 * (F_UINT)b0;+ r01 = (F_UINT)a0 * (F_UINT)b1;+ r10 = (F_UINT)a1 * (F_UINT)b0;+ r11 = (F_UINT)a1 * (F_UINT)b1;+ + r0 = r00;+ c = (r00 >> FH_SIZE) + (F_UHALF)r01 + (F_UHALF)r10;+ r1 = c;+ c = (c >> FH_SIZE) + (r01 >> FH_SIZE) + (r10 >> FH_SIZE) + (F_UHALF)r11;+ r2 = c;+ r3 = (c >> FH_SIZE) + (r11 >> FH_SIZE);++ *plow = ((F_UINT)r1 << FH_SIZE) | r0;+ return ((F_UINT)r3 << FH_SIZE) | r2;+}++#undef FH_SIZE++#endif++F_STATIC F_UINT mul_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)+{+ uint32_t a_sign, b_sign, r_sign;+ int32_t a_exp, b_exp, r_exp;+ F_UINT a_mant, b_mant, r_mant, r_mant_low;++ a_sign = a >> (F_SIZE - 1);+ b_sign = b >> (F_SIZE - 1);+ r_sign = a_sign ^ b_sign;+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ b_exp = (b >> MANT_SIZE) & EXP_MASK;+ a_mant = a & MANT_MASK;+ b_mant = b & MANT_MASK;+ if (a_exp == EXP_MASK || b_exp == EXP_MASK) {+ if (isnan_sf(a) || isnan_sf(b)) {+#if F_USE_FFLAGS+ if (issignan_sf(a) || issignan_sf(b)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN;+ } else {+ /* infinity */+ if ((a_exp == EXP_MASK && (b_exp == 0 && b_mant == 0)) ||+ (b_exp == EXP_MASK && (a_exp == 0 && a_mant == 0))) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return F_QNAN;+ } else {+ return pack_sf(r_sign, EXP_MASK, 0);+ }+ }+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf(r_sign, 0, 0); /* zero */+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ if (b_exp == 0) {+ if (b_mant == 0)+ return pack_sf(r_sign, 0, 0); /* zero */+ b_mant = normalize_subnormal_sf(&b_exp, b_mant);+ } else {+ b_mant |= (F_UINT)1 << MANT_SIZE;+ }+ r_exp = a_exp + b_exp - (1 << (EXP_SIZE - 1)) + 2;+ + r_mant = mul_u(&r_mant_low,a_mant << RND_SIZE, b_mant << (RND_SIZE + 1));+ r_mant |= (r_mant_low != 0);+ return normalize_sf(r_sign, r_exp, r_mant, rm FFLAGS_ARG);+}++#ifdef F_ULONG++static F_UINT divrem_u(F_UINT *pr, F_UINT ah, F_UINT al, F_UINT b)+{+ F_ULONG a;+ a = ((F_ULONG)ah << F_SIZE) | al;+ *pr = a % b;+ return a / b;+}++#else++/* XXX: optimize */+static F_UINT divrem_u(F_UINT *pr, F_UINT a1, F_UINT a0, F_UINT b)+{+ int i, qb, ab;++ assert(a1 < b);+ for(i = 0; i < F_SIZE; i++) {+ ab = a1 >> (F_SIZE - 1);+ a1 = (a1 << 1) | (a0 >> (F_SIZE - 1));+ if (ab || a1 >= b) {+ a1 -= b;+ qb = 1;+ } else {+ qb = 0;+ }+ a0 = (a0 << 1) | qb;+ }+ *pr = a1;+ return a0;+}++#endif++F_STATIC F_UINT div_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM)+{+ uint32_t a_sign, b_sign, r_sign;+ int32_t a_exp, b_exp, r_exp;+ F_UINT a_mant, b_mant, r_mant, r;++ a_sign = a >> (F_SIZE - 1);+ b_sign = b >> (F_SIZE - 1);+ r_sign = a_sign ^ b_sign;+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ b_exp = (b >> MANT_SIZE) & EXP_MASK;+ a_mant = a & MANT_MASK;+ b_mant = b & MANT_MASK;+ if (a_exp == EXP_MASK) {+ if (a_mant != 0 || isnan_sf(b)) {+#if F_USE_FFLAGS+ if (issignan_sf(a) || issignan_sf(b)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN;+ } else if (b_exp == EXP_MASK) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return F_QNAN;+ } else {+ return pack_sf(r_sign, EXP_MASK, 0);+ }+ } else if (b_exp == EXP_MASK) {+ if (b_mant != 0) {+#if F_USE_FFLAGS+ if (issignan_sf(a) || issignan_sf(b)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN;+ } else {+ return pack_sf(r_sign, 0, 0);+ }+ }++ if (b_exp == 0) {+ if (b_mant == 0) { + /* zero */+ if (a_exp == 0 && a_mant == 0) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return F_QNAN;+ } else {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_DIVIDE_ZERO;+#endif + return pack_sf(r_sign, EXP_MASK, 0);+ }+ }+ b_mant = normalize_subnormal_sf(&b_exp, b_mant);+ } else {+ b_mant |= (F_UINT)1 << MANT_SIZE;+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf(r_sign, 0, 0); /* zero */+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ r_exp = a_exp - b_exp + (1 << (EXP_SIZE - 1)) - 1;+ r_mant = divrem_u(&r, a_mant, 0, b_mant << 2);+ if (r != 0)+ r_mant |= 1;+ return normalize_sf(r_sign, r_exp, r_mant, rm FFLAGS_ARG);+}++#ifdef F_ULONG++/* compute sqrt(a) with a = ah*2^F_SIZE+al and a < 2^(F_SIZE - 2)+ return true if not exact square. */+static int sqrtrem_u(F_UINT *pr, F_UINT ah, F_UINT al)+{+ F_ULONG a, u, s;+ int l, inexact;++ /* 2^l >= a */+ if (ah != 0) {+ l = 2 * F_SIZE - clz(ah - 1);+ } else {+ if (al == 0) {+ *pr = 0;+ return 0;+ }+ l = F_SIZE - clz(al - 1);+ }+ a = ((F_ULONG)ah << F_SIZE) | al;+ u = (F_ULONG)1 << ((l + 1) / 2);+ for(;;) {+ s = u;+ u = ((a / s) + s) / 2;+ if (u >= s)+ break;+ }+ inexact = (a - s * s) != 0;+ *pr = s;+ return inexact;+}++#else++static int sqrtrem_u(F_UINT *pr, F_UINT a1, F_UINT a0)+{+ int l, inexact;+ F_UINT u, s, r, q, sq0, sq1;++ /* 2^l >= a */+ if (a1 != 0) {+ l = 2 * F_SIZE - clz(a1 - 1);+ } else {+ if (a0 == 0) {+ *pr = 0;+ return 0;+ }+ l = F_SIZE - clz(a0 - 1);+ }+ u = (F_UINT)1 << ((l + 1) / 2);+ for(;;) {+ s = u;+ q = divrem_u(&r, a1, a0, s);+ u = (q + s) / 2;+ if (u >= s)+ break;+ }+ sq1 = mul_u(&sq0, s, s);+ inexact = (sq0 != a0 || sq1 != a1);+ *pr = s;+ return inexact;+}++#endif++F_STATIC F_UINT sqrt_sf(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp;+ F_UINT a_mant;++ a_sign = a >> (F_SIZE - 1);+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ a_mant = a & MANT_MASK;+ if (a_exp == EXP_MASK) {+ if (a_mant != 0) {+#if F_USE_FFLAGS+ if (issignan_sf(a)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN;+ } else if (a_sign) {+ goto neg_error;+ } else {+ return a; /* +infinity */+ }+ }+ if (a_sign) {+ if (a_exp == 0 && a_mant == 0)+ return a; /* -zero */+ neg_error:+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return F_QNAN;+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf(0, 0, 0); /* zero */+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ a_exp -= EXP_MASK / 2;+ /* simpler to handle an even exponent */+ if (a_exp & 1) {+ a_exp--;+ a_mant <<= 1;+ }+ a_exp = (a_exp >> 1) + EXP_MASK / 2;+ a_mant <<= (F_SIZE - 4 - MANT_SIZE);+ if (sqrtrem_u(&a_mant, a_mant, 0))+ a_mant |= 1;+ return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);+}++/* comparisons */++F_STATIC int glue(eq_quiet_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)+{+ if (isnan_sf(a) || isnan_sf(b)) {+#if F_USE_FFLAGS+ if (issignan_sf(a) || issignan_sf(b)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return 0;+ }++ if ((F_UINT)((a | b) << 1) == 0)+ return 1; /* zero case */+ return (a == b);+}++F_STATIC int glue(le_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)+{+ uint32_t a_sign, b_sign;++ if (isnan_sf(a) || isnan_sf(b)) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return 0;+ }++ a_sign = a >> (F_SIZE - 1);+ b_sign = b >> (F_SIZE - 1);+ if (a_sign != b_sign) {+ return (a_sign || ((F_UINT)((a | b) << 1) == 0));+ } else {+ if (a_sign) {+ return (a >= b);+ } else {+ return (a <= b);+ }+ }+}++F_STATIC int glue(lt_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)+{+ uint32_t a_sign, b_sign;++ if (isnan_sf(a) || isnan_sf(b)) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return 0;+ }++ a_sign = a >> (F_SIZE - 1);+ b_sign = b >> (F_SIZE - 1);+ if (a_sign != b_sign) {+ return (a_sign && ((F_UINT)((a | b) << 1) != 0));+ } else {+ if (a_sign) {+ return (a > b);+ } else {+ return (a < b);+ }+ }+}++/* return -1 (a < b), 0 (a = b), 1 (a > b) or 2 (a = nan or b =+ nan) */+F_STATIC int glue(cmp_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)+{+ uint32_t a_sign, b_sign;++ if (isnan_sf(a) || isnan_sf(b)) {+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return 2;+ }++ a_sign = a >> (F_SIZE - 1);+ b_sign = b >> (F_SIZE - 1);+ if (a_sign != b_sign) {+ if ((F_UINT)((a | b) << 1) != 0)+ return 1 - 2 * a_sign;+ else+ return 0; /* -0 = +0 */+ } else {+ if (a < b)+ return 2 * a_sign - 1;+ else if (a > b)+ return 1 - 2 * a_sign;+ else+ return 0;+ }+}++/* conversions between floats */++#if F_SIZE >= 64++F_STATIC F_UINT cvt_sf32_sf(uint32_t a FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp;+ F_UINT a_mant;++ a_mant = unpack_sf32(&a_sign, &a_exp, a);+ if (a_exp == 0xff) {+ if (a_mant != 0) {+ /* NaN */+#if F_USE_FFLAGS+ if (issignan_sf32(a)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN;+ } else {+ /* infinity */+ return pack_sf(a_sign, EXP_MASK, 0);+ }+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf(a_sign, 0, 0); /* zero */+ a_mant = normalize_subnormal_sf32(&a_exp, a_mant);+ }+ /* convert the exponent value */+ a_exp = a_exp - 0x7f + (EXP_MASK / 2);+ /* shift the mantissa */+ a_mant <<= (MANT_SIZE - 23);+ /* We assume the target float is large enough to that no+ normalization is necessary */+ return pack_sf(a_sign, a_exp, a_mant);+}++F_STATIC uint32_t glue(glue(cvt_sf, F_SIZE), _sf32)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp;+ F_UINT a_mant;++ a_mant = unpack_sf(&a_sign, &a_exp, a);+ if (a_exp == EXP_MASK) {+ if (a_mant != 0) {+ /* NaN */+#if F_USE_FFLAGS+ if (issignan_sf(a)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN32;+ } else {+ /* infinity */+ return pack_sf32(a_sign, 0xff, 0);+ }+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf32(a_sign, 0, 0); /* zero */+ normalize_subnormal_sf(&a_exp, a_mant);+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ /* convert the exponent value */+ a_exp = a_exp - (EXP_MASK / 2) + 0x7f;+ /* shift the mantissa */+ a_mant = rshift_rnd(a_mant, MANT_SIZE - (32 - 2));+ return normalize_sf32(a_sign, a_exp, a_mant, rm FFLAGS_ARG);+}++#endif++#if F_SIZE >= 128++F_STATIC F_UINT cvt_sf64_sf(uint64_t a FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp;+ F_UINT a_mant;++ a_mant = unpack_sf64(&a_sign, &a_exp, a);++ if (a_exp == 0x7ff) {+ if (a_mant != 0) {+ /* NaN */+#if F_USE_FFLAGS+ if (issignan_sf64(a)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif+ return F_QNAN;+ } else {+ /* infinity */+ return pack_sf(a_sign, EXP_MASK, 0);+ }+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf(a_sign, 0, 0); /* zero */+ a_mant = normalize_subnormal_sf64(&a_exp, a_mant);+ }+ /* convert the exponent value */+ a_exp = a_exp - 0x3ff + (EXP_MASK / 2);+ /* shift the mantissa */+ a_mant <<= (MANT_SIZE - 52);+ return pack_sf(a_sign, a_exp, a_mant);+}++F_STATIC uint64_t glue(glue(cvt_sf, F_SIZE), _sf64)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp;+ F_UINT a_mant;++ a_mant = unpack_sf(&a_sign, &a_exp, a);+ if (a_exp == EXP_MASK) {+ if (a_mant != 0) {+ /* NaN */+#if F_USE_FFLAGS+ if (issignan_sf(a)) {+ *pfflags |= FFLAG_INVALID_OP;+ }+#endif + return F_QNAN64;+ } else {+ /* infinity */+ return pack_sf64(a_sign, 0x7ff, 0);+ }+ }+ if (a_exp == 0) {+ if (a_mant == 0)+ return pack_sf64(a_sign, 0, 0); /* zero */+ normalize_subnormal_sf(&a_exp, a_mant);+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ /* convert the exponent value */+ a_exp = a_exp - (EXP_MASK / 2) + 0x3ff;+ /* shift the mantissa */+ a_mant = rshift_rnd(a_mant, MANT_SIZE - (64 - 2));+ return normalize_sf64(a_sign, a_exp, a_mant, rm, pfflags);+}++#endif++#undef clz++#define ICVT_SIZE 32+#include "softfp_template_icvt.h"++#define ICVT_SIZE 64+#include "softfp_template_icvt.h"++/* additional libm functions */++/* return a mod b (exact) */+F_STATIC F_UINT glue(fmod_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp, b_exp, n;+ F_UINT a_mant, b_mant, a_abs, b_abs;+ + a_abs = a & ~SIGN_MASK;+ b_abs = b & ~SIGN_MASK;+ if (b_abs == 0 ||+ a_abs >= ((F_UINT)EXP_MASK << MANT_SIZE) ||+ b_abs > ((F_UINT)EXP_MASK << MANT_SIZE)) {+ /* XXX: flags */+ return F_QNAN;+ }+ if (a_abs < b_abs) {+ return a; /* |a| < |b| return a */+ } else if (a_abs == b_abs) {+ return a & SIGN_MASK; /* |a| = |b| return copy_sign(0, a) */+ }+ + a_sign = a >> (F_SIZE - 1);+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ b_exp = (b >> MANT_SIZE) & EXP_MASK;+ a_mant = (a & MANT_MASK);+ b_mant = (b & MANT_MASK);++ if (a_exp == 0) {+ a_mant = normalize_subnormal_sf(&a_exp, a_mant);+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ if (b_exp == 0) {+ b_mant = normalize_subnormal_sf(&b_exp, b_mant);+ } else {+ b_mant |= (F_UINT)1 << MANT_SIZE;+ }+ n = a_exp - b_exp;+ if (a_mant >= b_mant)+ a_mant -= b_mant;+ /* here a_mant < b_mant and n >= 0 */+ /* multiply a_mant by 2^n */+ /* XXX: do it faster */+ while (n != 0) {+ a_mant <<= 1;+ if (a_mant >= b_mant)+ a_mant -= b_mant;+ n--;+ }+ /* Note: the rounding mode does not matter because the result is+ exact */+ return normalize_sf(a_sign, b_exp, a_mant << RND_SIZE, RM_RNE FFLAGS_ARG);+}+#endif /* F_NORMALIZE_ONLY */++#undef F_SIZE+#undef F_UINT+#undef F_ULONG+#undef F_UHALF+#undef MANT_SIZE+#undef EXP_SIZE+#undef EXP_MASK+#undef MANT_MASK+#undef SIGN_MASK+#undef IMANT_SIZE+#undef RND_SIZE+#undef QNAN_MASK+#undef F_QNAN+#undef F_NORMALIZE_ONLY+#undef EXP_BIAS++#undef pack_sf+#undef unpack_sf+#undef rshift_rnd+#undef round_pack_sf+#undef normalize_sf+#undef normalize2_sf+#undef issignan_sf+#undef isnan_sf+#undef add_sf+#undef mul_sf+#undef fma_sf+#undef div_sf+#undef sqrt_sf+#undef normalize_subnormal_sf+#undef divrem_u+#undef sqrtrem_u+#undef mul_u+#undef cvt_sf32_sf+#undef cvt_sf64_sf
+ mquickjs/softfp_template_icvt.h view
@@ -0,0 +1,172 @@+/*+ * SoftFP Library+ * + * Copyright (c) 2016 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.+ */+#if ICVT_SIZE == 32+#define ICVT_UINT uint32_t+#define ICVT_INT int32_t+#elif ICVT_SIZE == 64+#define ICVT_UINT uint64_t+#define ICVT_INT int64_t+#elif ICVT_SIZE == 128+#define ICVT_UINT uint128_t+#define ICVT_INT int128_t+#else+#error unsupported icvt+#endif++/* conversions between float and integers */+static ICVT_INT glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,+ BOOL is_unsigned FFLAGS_PARAM)+{+ uint32_t a_sign, addend, rnd_bits;+ int32_t a_exp;+ F_UINT a_mant;+ ICVT_UINT r, r_max;++ a_sign = a >> (F_SIZE - 1);+ a_exp = (a >> MANT_SIZE) & EXP_MASK;+ a_mant = a & MANT_MASK;+ if (a_exp == EXP_MASK && a_mant != 0)+ a_sign = 0; /* NaN is like +infinity */+ if (a_exp == 0) {+ a_exp = 1;+ } else {+ a_mant |= (F_UINT)1 << MANT_SIZE;+ }+ a_mant <<= RND_SIZE;+ a_exp = a_exp - (EXP_MASK / 2) - MANT_SIZE;++ if (is_unsigned)+ r_max = (ICVT_UINT)a_sign - 1;+ else+ r_max = ((ICVT_UINT)1 << (ICVT_SIZE - 1)) - (ICVT_UINT)(a_sign ^ 1);+ if (a_exp >= 0) {+ if (a_exp <= (ICVT_SIZE - 1 - MANT_SIZE)) {+ r = (ICVT_UINT)(a_mant >> RND_SIZE) << a_exp;+ if (r > r_max)+ goto overflow;+ } else {+ overflow:+#if F_USE_FFLAGS+ *pfflags |= FFLAG_INVALID_OP;+#endif + return r_max;+ }+ } else {+ a_mant = rshift_rnd(a_mant, -a_exp);++ switch(rm) {+ case RM_RNE:+ case RM_RMM:+ addend = (1 << (RND_SIZE - 1));+ break;+ case RM_RTZ:+ addend = 0;+ break;+ default:+ case RM_RDN:+ case RM_RUP:+ if (a_sign ^ (rm & 1))+ addend = (1 << RND_SIZE) - 1;+ else+ addend = 0;+ break;+ }+ + rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);+ a_mant = (a_mant + addend) >> RND_SIZE;+ /* half way: select even result */+ if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))+ a_mant &= ~1;+ if (a_mant > r_max)+ goto overflow;+ r = a_mant;+#if F_USE_FFLAGS+ if (rnd_bits != 0)+ *pfflags |= FFLAG_INEXACT;+#endif + }+ if (a_sign)+ r = -r;+ return r;+}++F_STATIC ICVT_INT __maybe_unused glue(glue(glue(cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)+{+ return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(a, rm, + FALSE FFLAGS_ARG);+}++F_STATIC ICVT_UINT __maybe_unused glue(glue(glue(cvt_sf, F_SIZE), _u), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM)+{+ return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE) (a, rm, + TRUE FFLAGS_ARG);+}++/* conversions between float and integers */+static F_UINT glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a, + RoundingModeEnum rm,+ BOOL is_unsigned FFLAGS_PARAM)+{+ uint32_t a_sign;+ int32_t a_exp;+ F_UINT a_mant;+ ICVT_UINT r, mask;+ int l;++ if (!is_unsigned && a < 0) {+ a_sign = 1;+ r = -(ICVT_UINT)a;+ } else {+ a_sign = 0;+ r = a;+ }+ a_exp = (EXP_MASK / 2) + F_SIZE - 2;+ /* need to reduce range before generic float normalization */+ l = ICVT_SIZE - glue(clz, ICVT_SIZE)(r) - (F_SIZE - 1);+ if (l > 0) {+ mask = r & (((ICVT_UINT)1 << l) - 1);+ r = (r >> l) | ((r & mask) != 0);+ a_exp += l;+ }+ a_mant = r;+ return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG);+}++F_STATIC F_UINT __maybe_unused glue(glue(glue(cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a, + RoundingModeEnum rm+ FFLAGS_PARAM)+{+ return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, FALSE FFLAGS_ARG);+}++F_STATIC F_UINT __maybe_unused glue(glue(glue(cvt_u, ICVT_SIZE), _sf), F_SIZE)(ICVT_UINT a, + RoundingModeEnum rm+ FFLAGS_PARAM)+{+ return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, TRUE FFLAGS_ARG);+}++#undef ICVT_SIZE+#undef ICVT_INT+#undef ICVT_UINT
+ mquickjs/stdlib_wrapper.c view
@@ -0,0 +1,44 @@+/* Wrapper to include the generated stdlib definition with stub functions */+#include <stdio.h>+#include <string.h>+#include "mquickjs.h"++/* Stub implementations for required functions */+static JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ return JS_NewFloat64(ctx, 0.0); /* TODO: implement with real time */+}++static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ int i;+ JSCStringBuf buf;+ for (i = 0; i < argc; i++) {+ if (i > 0) printf(" ");+ const char *str = JS_ToCString(ctx, argv[i], &buf);+ if (str) printf("%s", str);+ }+ printf("\n");+ return JS_UNDEFINED;+}++static JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ return JS_NewFloat64(ctx, 0.0); /* TODO: implement with real time */+}++static JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ JS_GC(ctx);+ return JS_UNDEFINED;+}++static JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ return JS_ThrowTypeError(ctx, "load() not implemented");+}++static JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ return JS_ThrowTypeError(ctx, "setTimeout() not implemented");+}++static JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) {+ return JS_ThrowTypeError(ctx, "clearTimeout() not implemented");+}++#include "mqjs_stdlib.h"
+ src/MQuickJS.hs view
@@ -0,0 +1,615 @@+{-# LANGUAGE BangPatterns, QuasiQuotes, TemplateHaskell, OverloadedStrings, ScopedTypeVariables, FlexibleContexts #-}++{-|+Module : MQuickJS+Description : Haskell bindings to the [Micro QuickJS](https://github.com/bellard/mquickjs) library+License : MIT++This is a very basic wrapper for the [Micro QuickJS](https://github.com/bellard/mquickjs) library.++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 MQuickJS (JSValue, JSContextPtr, mquickjs, mquickjsWithMemory, mquickjsMultithreaded, mquickjsMultithreadedWithMemory, 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 (Key,Value(..), encode, toJSON)+import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Key as Key+import Data.Scientific (fromFloatDigits, toRealFloat, toBoundedInteger, isInteger)+import Data.Text (Text)+import Data.Vector (fromList, imapM_)+import Data.Aeson.KeyMap (KeyMap, empty, insert, toList)+import Data.String.Conv (toS)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import Control.Concurrent (rtsSupportsBoundThreads, runInBoundThread)++import MQuickJS.Types+import MQuickJS.Error+++C.context mquickjsCtx+C.include "<stddef.h>"+C.include "mquickjs.h"++-- Reference to the stdlib defined in example_stdlib.c+C.verbatim "extern const JSSTDLibraryDef js_stdlib;"++foreign import ccall "JS_NewContext"+ jsNewContext :: Ptr Word8 -> CSize -> Ptr () -> IO (Ptr JSContext)++foreign import ccall "JS_FreeContext"+ jsFreeContext :: Ptr JSContext -> IO ()++++-- Micro QuickJS uses automatic GC, no manual reference counting needed+jsFreeValue :: JSContextPtr -> JSValue -> IO ()+jsFreeValue _ _ = return () -- No-op in Micro QuickJS++++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)); } |]++-- Micro QuickJS uses JS_GetClassID instead of JS_IsArray+jsIsArray :: MonadIO m => JSContextPtr -> JSValue -> m Bool+jsIsArray ctxPtr val = do+ classId <- liftIO $ [C.block| int { return JS_GetClassID($(JSContext *ctxPtr), $(JSValue val)); } |]+ return $ classId == 1 -- JS_CLASS_ARRAY = 1++-- Micro QuickJS uses JS_GetClassID for Date detection (JS_CLASS_DATE = 7)+jsIsDate :: MonadIO m => JSContextPtr -> JSValue -> m Bool+jsIsDate ctxPtr val = do+ classId <- liftIO $ [C.block| int { return JS_GetClassID($(JSContext *ctxPtr), $(JSValue val)); } |]+ return $ classId == 7 -- JS_CLASS_DATE = 7+++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"+++-- Micro QuickJS type detection - simpler approach using helper functions+jsIs :: (MonadIO m, MonadThrow m) => JSContextPtr -> JSValue -> m JSTypeEnum+jsIs ctx jsval = liftIO $ do+ -- Check for exception first+ isException <- [C.block| int { return JS_IsException($(JSValue jsval)); } |]+ if isException /= 0 then return $ JSTypeFromTag JSTagException+ else do+ -- Check for null/undefined+ isNull <- [C.block| int { return $(JSValue jsval) == JS_NULL; } |]+ if isNull /= 0 then return $ JSTypeFromTag JSTagNull+ else do+ isUndef <- [C.block| int { return $(JSValue jsval) == JS_UNDEFINED; } |]+ if isUndef /= 0 then return $ JSTypeFromTag JSTagUndefined+ else do+ -- Check for boolean+ isBoolTrue <- [C.block| int { return $(JSValue jsval) == JS_TRUE; } |]+ isBoolFalse <- [C.block| int { return $(JSValue jsval) == JS_FALSE; } |]+ if isBoolTrue /= 0 || isBoolFalse /= 0 then return $ JSTypeFromTag JSTagBool+ else do+ -- Check for number (int or float)+ isNum <- [C.block| int { return JS_IsNumber($(JSContext *ctx), $(JSValue jsval)); } |]+ if isNum /= 0 then return JSIsNumber+ else do+ -- Check for string+ isStr <- [C.block| int { return JS_IsString($(JSContext *ctx), $(JSValue jsval)); } |]+ if isStr /= 0 then return $ JSTypeFromTag JSTagPtr -- Using JSTagPtr for strings+ else do+ -- Check class ID for arrays, dates, objects+ classId <- [C.block| int { return JS_GetClassID($(JSContext *ctx), $(JSValue jsval)); } |]+ case classId of+ 1 -> return JSIsArray -- JS_CLASS_ARRAY+ 7 -> return JSIsDate -- JS_CLASS_DATE+ _ -> return $ JSTypeFromTag JSTagPtr -- Generic object++++-- Micro QuickJS null value constant+jsNullValue :: JSValue+jsNullValue = 7 -- JS_NULL = JS_VALUE_MAKE_SPECIAL(JS_TAG_NULL, 0) = 7++-- Micro QuickJS: JS_NewBool doesn't need ctx parameter+jsNewBool :: JSContextPtr -> Bool -> IO JSValue+jsNewBool _ bool = do+ let b = if bool then 1 else 0+ return $ [C.pure| JSValue { JS_NewBool($(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 = do+ isEx <- liftIO $ [C.block| int { return JS_IsException($(JSValue val)); } |]+ when (isEx /= 0) $ do+ err <- getErrorMessage ctxPtr+ throwM $ JSException loc err++++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+ -- Micro QuickJS: JS_NewArray takes initial length parameter+ arrVal <- liftIO $ [C.block| JSValue { return JS_NewArray($(JSContext *ctxPtr), 0); } |]++ 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+ -- Micro QuickJS: JS_SetPropertyUint32 returns JSValue (exception or undefined)+ res <- liftIO $ [C.block| JSValue {+ return JS_SetPropertyUint32($(JSContext *ctxPtr), $(JSValue arrVal), $(uint32_t idx), $(JSValue val));+ } |]+ checkIsException "jsonToJSValue/Array/set" ctxPtr res++ return arrVal+jsonToJSValue ctxPtr (Object o) = do+ objVal <- liftIO $ [C.block| JSValue { return 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++ -- Micro QuickJS: JS_SetPropertyStr returns JSValue (exception or undefined)+ res <- liftIO $ useAsCString (encodeUtf8 $ Key.toText key) $ \cstringPtr ->+ [C.block| JSValue {+ return JS_SetPropertyStr($(JSContext *ctxPtr), $(JSValue objVal), $(const char *cstringPtr), $(JSValue val));+ } |]+ checkIsException "jsonToJSValue/Object/set" ctxPtr res++ return objVal+++-- Micro QuickJS: Use JS_ToNumber for boolean conversion (no JS_ToBool)+jsToBool :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> m Bool+jsToBool ctxPtr val = do+ (res, code) <- liftIO $ C.withPtr $ \doublePtr ->+ [C.block| int { return JS_ToNumber($(JSContext *ctxPtr), $(double *doublePtr), $(JSValue val)); } |]+ if code == 0 then return (res /= 0)+ else getErrorMessage ctxPtr >>= throwM . JSException "jsToBool"++-- Micro QuickJS: Use JS_ToInt32 (no JS_ToInt64)+jsToInt64 :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> m Int64+jsToInt64 ctxPtr val = do+ (res, code) <- liftIO $ C.withPtr $ \intPtr ->+ [C.block| int { return JS_ToInt32($(JSContext *ctxPtr), $(int *intPtr), $(JSValue val)); } |]+ if code == 0 then return (fromIntegral res)+ else getErrorMessage ctxPtr >>= throwM . JSException "jsToInt64"++-- Micro QuickJS: JS_ToNumber instead of JS_ToFloat64+jsToFloat64 :: (MonadThrow m, MonadIO m) => JSContextPtr -> JSValue -> m CDouble+jsToFloat64 ctxPtr val = do+ (res, code) <- liftIO $ C.withPtr $ \doublePtr ->+ [C.block| int { return JS_ToNumber($(JSContext *ctxPtr), $(double *doublePtr), $(JSValue val)); } |]+ if code == 0 then return res+ else getErrorMessage ctxPtr >>= throwM . JSException "jsToFloat64"++++-- Micro QuickJS: JS_ToCString requires JSCStringBuf parameter, no JS_FreeCString needed+jsToString :: MonadIO m => JSContextPtr -> JSValue -> m ByteString+jsToString ctxPtr val = liftIO $ do+ -- JSCStringBuf is 5 bytes+ allocaBytes 5 $ \bufPtr -> do+ cstring <- [C.block| const char * {+ return JS_ToCString($(JSContext *ctxPtr), $(JSValue val), (JSCStringBuf *)$(void *bufPtr));+ } |]+ if cstring == nullPtr then return ""+ else packCString cstring -- No free needed in Micro QuickJS+++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+ 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+ JSIsArray -> do+ len <- do+ lenVal <- jsGetPropertyStr ctx jsval "length"+ len' <- jsToInt64 ctx lenVal+ return len'+ vs <- jsArrayToJSON ctx jsval 0 (fromIntegral len)+ return $ Array $ fromList vs+ JSIsDate -> do+ -- Micro QuickJS: Use stack-based calling for Date.getTime()+ timestampRaw <- liftIO $ [C.block| JSValue {+ JSValue date = $(JSValue jsval);+ JSContext *ctx = $(JSContext *ctx);+ JSValue getter = JS_GetPropertyStr(ctx, date, "getTime");+ // Stack-based call: push this, func, then call+ JS_PushArg(ctx, date); // this+ JS_PushArg(ctx, getter); // func+ return JS_Call(ctx, 0); // 0 args+ } |]+ checkIsException "jsToJSON/Date" ctx timestampRaw+ timestamp <- jsToFloat64 ctx timestampRaw+ return $ toJSON $ posixSecondsToUTCTime $ realToFrac $ timestamp / 1000+ JSTypeFromTag JSTagPtr -> do+ -- JSTagPtr can be string or object - check which one+ isStr <- liftIO $ [C.block| int { return JS_IsString($(JSContext *ctx), $(JSValue jsval)); } |]+ if isStr /= 0 then do+ s <- jsToString ctx jsval+ return $ String $ toS s+ else 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.block| JSValue {+ return JS_GetPropertyUint32($(JSContext *ctxPtr), $(JSValue jsval), $(uint32_t idx));+ } |]++ checkIsException "jsArrayToJSON" ctxPtr val+ jsToJSON ctxPtr val++ 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 ()+++++-- Micro QuickJS: Use JavaScript Object.keys() for property enumeration+-- since JS_GetOwnPropertyNames is not available in the public API+jsObjectToJSON :: (MonadCatch m, MonadIO m) => JSContextPtr -> JSValue -> m (KeyMap Value)+jsObjectToJSON ctxPtr obj = do+ -- Get Object.keys(obj) via JavaScript evaluation+ keysArray <- liftIO $ [C.block| JSValue {+ JSContext *ctx = $(JSContext *ctxPtr);+ JSValue obj = $(JSValue obj);++ // Get Object constructor from global+ JSValue global = JS_GetGlobalObject(ctx);+ JSValue objectCtor = JS_GetPropertyStr(ctx, global, "Object");+ JSValue keysFunc = JS_GetPropertyStr(ctx, objectCtor, "keys");++ // Stack-based call: Object.keys(obj)+ JS_PushArg(ctx, obj); // arg+ JS_PushArg(ctx, keysFunc); // func+ JS_PushArg(ctx, objectCtor); // this+ return JS_Call(ctx, 1); // 1 argument+ } |]++ checkIsException "jsObjectToJSON/keys" ctxPtr keysArray++ -- Get array length+ lenVal <- jsGetPropertyStr ctxPtr keysArray "length"+ len <- jsToInt64 ctxPtr lenVal++ -- Iterate through keys+ collectProps ctxPtr obj keysArray 0 (fromIntegral len)+ where+ collectProps :: (MonadCatch m, MonadIO m) => JSContextPtr -> JSValue -> JSValue -> Int -> Int -> m (KeyMap Value)+ collectProps ctx objVal keysArr !index end+ | index < end = do+ let idx = fromIntegral index++ -- Get key at index+ keyVal <- liftIO $ [C.block| JSValue {+ return JS_GetPropertyUint32($(JSContext *ctx), $(JSValue keysArr), $(uint32_t idx));+ } |]+ checkIsException "jsObjectToJSON/getKey" ctx keyVal+ keyStr <- jsToString ctx keyVal++ -- Get property value+ propVal <- liftIO $ useAsCString keyStr $ \ckey ->+ [C.block| JSValue {+ return JS_GetPropertyStr($(JSContext *ctx), $(JSValue objVal), $(const char *ckey));+ } |]+ checkIsException "jsObjectToJSON/getProp" ctx propVal+ val <- jsToJSON ctx propVal++ rest <- collectProps ctx objVal keysArr (index + 1) end+ return $ insert (Key.fromText $ toS keyStr) val rest++ | otherwise = return empty++++getErrorMessage :: MonadIO m => JSContextPtr -> m Text+getErrorMessage ctxPtr = liftIO $ do+ ex <- [C.block| JSValue { return JS_GetException($(JSContext *ctxPtr)); } |]+ res <- jsToString ctxPtr ex+ return $ toS res++++jsGetPropertyStr :: MonadIO m => JSContextPtr -> JSValue -> ByteString -> m JSValue+jsGetPropertyStr ctxPtr val str = liftIO $+ useAsCString str $ \prop ->+ [C.block| JSValue { return JS_GetPropertyStr($(JSContext *ctxPtr), $(JSValue val), $(const char *prop)); } |]+++-- Micro QuickJS: Stack-based function calling+-- Push args in reverse order, then func, then this, then call with argc+-- See mquickjs.c: arg[n-1] first, then ..., then arg[0], then func, then this_obj+jsCall :: JSContextPtr -> JSValue -> CInt -> (Ptr JSValue) -> IO JSValue+jsCall ctxt fun_obj argc argv = do+ -- Push arguments in reverse order (last arg first), then func, then this (JS_NULL)+ forM_ (reverse [0..(fromIntegral argc - 1)]) $ \i -> do+ arg <- peekElemOff argv i+ let argVal = arg+ [C.block| void {+ JSContext *ctx = $(JSContext *ctxt);+ JSValue v = $(JSValue argVal);+ JS_PushArg(ctx, v);+ } |]+ let funVal = fun_obj+ [C.block| void {+ JSContext *ctx = $(JSContext *ctxt);+ JS_PushArg(ctx, $(JSValue funVal));+ JS_PushArg(ctx, JS_NULL);+ } |]+ [C.block| JSValue { return JS_Call($(JSContext *ctxt), $(int argc)); } |]+++jsEval :: JSContextPtr -> CString -> CSize -> CString -> CInt -> IO JSValue+jsEval ctxPtr input input_len filename eval_flags =+ [C.block| JSValue { return 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 WithRetval -- Micro QuickJS needs JS_EVAL_RETVAL flag to return the value++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.block| JSValue { return JS_GetGlobalObject($(JSContext *ctxPtr)); } |]++ fun <- jsGetPropertyStr ctxPtr globalObject funName++ ty <- jsIs ctxPtr fun+ case ty of+ JSTypeFromTag JSTagException -> do+ err <- getErrorMessage ctxPtr+ throwM $ JSException "callRaw" err+ JSTypeFromTag JSTagUndefined -> throwM $ JSValueUndefined $ toS funName+ JSTypeFromTag JSTagPtr -> do+ -- Micro QuickJS: Stack-based calling+ res <- liftIO $ withArrayLen args $ \len argv -> jsCall ctxPtr fun (fromIntegral len) argv+ return res+ _ -> throwM $ JSValueIncorrectType {name = toS funName, expected = JSTypeFromTag JSTagPtr, 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:++>mquickjs $ do+> res <- eval "1+2"+> liftIO $ print res++Declare a function and call it on an argument:++>mquickjs $ do+> _ <- eval_ "f = (x) => x+1"+> res <- eval "f(2)"+> liftIO $ print res++Pass a Haskell value to the JS runtime:++>mquickjs $ do+> _ <- eval_ "f = (x) => x+1"+> res <- withJSValue (3::Int) $ \x -> call "f" [x]+> liftIO $ print res++-}+mquickjs :: MonadIO m => ReaderT (Ptr JSContext) m b -> m b+mquickjs = mquickjsWithMemory (10 * 1024 * 1024) -- 10MB default++mquickjsWithMemory :: MonadIO m => Int -> ReaderT (Ptr JSContext) m b -> m b+mquickjsWithMemory memSize f = do+ (memBuf, ctx) <- liftIO $ do+ _memBuf <- mallocBytes memSize+ _ctx <- [C.block| JSContext * {+ return JS_NewContext($(uint8_t *_memBuf), $(size_t memSizeC), &js_stdlib);+ } |]+ return (_memBuf, _ctx)++ res <- runReaderT f ctx+ cleanup memBuf ctx+ return res+ where+ memSizeC = fromIntegral memSize+ cleanup memBuf ctx = liftIO $ do+ jsFreeContext ctx+ free memBuf++{-|+This env differs from regular 'mquickjs', 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 mquickjs 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)+-}+mquickjsMultithreaded :: MonadUnliftIO m => ReaderT (Ptr JSContext) m b -> m b+mquickjsMultithreaded = mquickjsMultithreadedWithMemory (10 * 1024 * 1024) -- 10MB default++mquickjsMultithreadedWithMemory :: MonadUnliftIO m => Int -> ReaderT (Ptr JSContext) m b -> m b+mquickjsMultithreadedWithMemory memSize f+ | rtsSupportsBoundThreads = do+ (u :: UnliftIO m) <- askUnliftIO++ liftIO $ runInBoundThread $ do+ memBuf <- mallocBytes memSize+ ctx <- [C.block| JSContext * {+ return JS_NewContext($(uint8_t *memBuf), $(size_t memSizeC), &js_stdlib);+ } |]++ res <- unliftIO u $ runReaderT f ctx+ cleanup memBuf ctx+ return res+ | otherwise = mquickjsWithMemory memSize f+ where+ memSizeC = fromIntegral memSize+ cleanup memBuf ctx = do+ jsFreeContext ctx+ free memBuf
+ src/MQuickJS/Error.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE ExistentialQuantification, DuplicateRecordFields, GeneralizedNewtypeDeriving, DerivingStrategies, DeriveGeneric, RecordWildCards #-}++module MQuickJS.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 MQuickJS.Types+++data SomeJSRuntimeException = forall e . Exception e => SomeJSRuntimeException e deriving Typeable++instance Show SomeJSRuntimeException where+ show (SomeJSRuntimeException e) = show e++instance Exception SomeJSRuntimeException++jsRuntimeExceptionToException :: Exception e => e -> SomeException+jsRuntimeExceptionToException = toException . SomeJSRuntimeException++jsRuntimeExceptionFromException :: Exception e => SomeException -> Maybe e+jsRuntimeExceptionFromException x = do+ SomeJSRuntimeException a <- fromException x+ cast a++++instance ToJSON CLong where+ toJSON cl = toJSON (fromIntegral cl :: Integer)++data UnknownJSTag = UnknownJSTag {raw_tag :: !CLong}+ deriving (Generic, Typeable)++instance Exception UnknownJSTag where+ toException = jsRuntimeExceptionToException+ fromException = jsRuntimeExceptionFromException+++instance Show UnknownJSTag where+ show UnknownJSTag{..} = "Uknown JS tag: " ++ show raw_tag+++data UnsupportedTypeTag = UnsupportedTypeTag {_tag :: JSTagEnum}+ deriving (Generic, Typeable)++instance Exception UnsupportedTypeTag where+ toException = jsRuntimeExceptionToException+ fromException = jsRuntimeExceptionFromException++instance Show UnsupportedTypeTag where+ show UnsupportedTypeTag{..} = "Unsupported type tag: " ++ show _tag+++data JSException = JSException {location :: Text, message :: Text}+ deriving (Generic, Typeable)++instance Exception JSException where+ toException = jsRuntimeExceptionToException+ fromException = jsRuntimeExceptionFromException+++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)++instance Exception JSValueUndefined where+ toException = jsRuntimeExceptionToException+ fromException = jsRuntimeExceptionFromException+++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)++instance Exception JSValueIncorrectType where+ toException = jsRuntimeExceptionToException+ fromException = jsRuntimeExceptionFromException+++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)++instance Exception InternalError where+ toException = jsRuntimeExceptionToException+ fromException = jsRuntimeExceptionFromException++instance Show InternalError where+ show InternalError{..} = "Internal error occured:\n" ++ toS message
+ src/MQuickJS/Types.hsc view
@@ -0,0 +1,113 @@+{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, MultiParamTypeClasses, FlexibleInstances, GeneralizedNewtypeDeriving #-}++module MQuickJS.Types where++import qualified Data.Map as Map+import Data.Bits (Bits)+import Data.Word (Word64, Word32)+import Foreign.C.Types+import Foreign.Ptr (plusPtr)+import Foreign.Storable (Storable(..))+import Language.C.Inline+import Language.C.Inline.Context (Context(..), TypesTable)+import qualified Language.C.Types as C++#include <stddef.h>+#include "mquickjs.h"++-- Micro QuickJS uses a simple uint64_t or uint32_t for JSValue (no struct)+-- Note: We use Word64/Word32 instead of CULong/CUInt because CULong is only+-- 32 bits on Windows even on 64-bit systems (LLP64 data model), while the C+-- code uses uint64_t which is always 64 bits on 64-bit platforms.+#if INTPTR_MAX >= INT64_MAX+type JSValue = Word64 -- 64-bit (matches C's uint64_t)+#else+type JSValue = Word32 -- 32-bit (matches C's uint32_t)+#endif++type JSValueConst = JSValue++newtype JSContext = JSContext { _unusedContext :: CUChar }+++type JSBool = CInt++-- Note: JSAtom, JSPropertyEnum, and JSRefCountHeader are not exposed in Micro QuickJS public API++++class ToCType ty cty where+ toCType :: ty -> cty+++class FromCType ty cty where+ fromCType :: cty -> Maybe ty+++-- Micro QuickJS has a simpler tag system+-- Note: JS_TAG_SPECIAL is not included as a separate constructor because+-- JS_TAG_BOOL has the same value (3). The specific special tags (Bool, Null, etc.)+-- are the user-facing values.+data JSTagEnum = JSTagInt+ | JSTagPtr+ | JSTagBool+ | JSTagNull+ | JSTagUndefined+ | JSTagUninitialized+ | JSTagCatchOffset+ | JSTagException+ deriving (Show, Eq)++instance Num a => ToCType JSTagEnum a where+ toCType JSTagInt = #{const JS_TAG_INT}+ toCType JSTagPtr = #{const JS_TAG_PTR}+ 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}+++instance (Eq a, Num a) => FromCType JSTagEnum a where+ fromCType (#{const JS_TAG_INT}) = Just JSTagInt+ fromCType (#{const JS_TAG_PTR}) = Just JSTagPtr+ 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 _ = Nothing++data JSTypeEnum = JSTypeFromTag JSTagEnum+ | JSIsNumber+ | JSIsArray+ | JSIsDate+ | JSIsError+ deriving Show++-- Micro QuickJS eval flags (different from original QuickJS)+data JSEvalType = Global | WithRetval++instance Num a => ToCType JSEvalType a where+ toCType Global = 0 -- No special flags for global eval+ toCType WithRetval = #{const JS_EVAL_RETVAL} -- Return last value instead of undefined+++mquickjsCtx :: Context+mquickjsCtx = baseCtx <> fptrCtx <> ctx+ where+ ctx = mempty+ { ctxTypesTable = mquickjsTypesTable+ }++mquickjsTypesTable :: TypesTable+mquickjsTypesTable = Map.fromList+ [+ (C.TypeName "JSValue", [t| JSValue |])+ , (C.TypeName "JSValueConst", [t| JSValueConst |])+ , (C.TypeName "JSContext", [t| JSContext |])+ , (C.TypeName "JSBool", [t| JSBool |])+ ]+
+ test/Spec.hs view
@@ -0,0 +1,904 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Test.Tasty (TestTree, testGroup, defaultMain, after, DependencyType(..))+import Test.Tasty.HUnit (testCase, assertBool)+import Test.Tasty.QuickCheck (testProperty, QuickCheckTests(..), QuickCheckVerbose(..))+import Test.HUnit (Assertion, (@?=), assertFailure)+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 (try, SomeException, MonadCatch(..))+import Data.Text (pack)+import qualified Data.HashMap.Strict as HM+import qualified Data.Aeson.KeyMap as KM+import qualified Data.Aeson.Key as K+import qualified Data.Vector as V+import MQuickJS+import MQuickJS.Error (SomeJSRuntimeException, JSValueUndefined, JSException)+import Control.Monad (guard)+import qualified Data.Text as T++genText = do+ k <- QC.choose (0,200)+ t <- pack <$> QC.vectorOf k (QC.oneof $ map pure $ ['0'..'~'])+ pure t+++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 . KM.fromList <$> (zip <$> QC.vectorOf k (K.fromText <$> 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)++-- | There's an Arbitrary instance for Value floating around, but our tests don't pass+-- with it because it produces null characters in text. These are not valid+-- javascript strings, so we have our own.+newtype GenVal = GenVal Value deriving Show+instance QC.Arbitrary GenVal where+ arbitrary = GenVal <$> QC.sized genVal++marshall_to_from_JSValue :: GenVal -> QC.Property+marshall_to_from_JSValue (GenVal val) = QC.monadicIO $ do+ val' <- QC.run $ mquickjsMultithreaded $ withJSValue val $ \jsval ->+ fromJSValue_ jsval+ pure $ (val QC.=== val')++-- | Basic evaluation tests+evalTests :: TestTree+evalTests = testGroup "eval"+ [ testCase "simple arithmetic" $ mquickjsMultithreaded $ do+ v <- eval "1 + 2;"+ liftIO $ v @?= Number 3++ , testCase "multiplication" $ mquickjsMultithreaded $ do+ v <- eval "6 * 7;"+ liftIO $ v @?= Number 42++ , testCase "division returns float" $ mquickjsMultithreaded $ do+ v <- eval "10 / 4;"+ liftIO $ v @?= Number 2.5++ , testCase "modulo operator" $ mquickjsMultithreaded $ do+ v <- eval "17 % 5;"+ liftIO $ v @?= Number 2++ , testCase "negative numbers" $ mquickjsMultithreaded $ do+ v <- eval "-42;"+ liftIO $ v @?= Number (-42)++ , testCase "floating point arithmetic" $ mquickjsMultithreaded $ do+ v <- eval "3.14 * 2;"+ liftIO $ v @?= Number 6.28++ , testCase "string literal" $ mquickjsMultithreaded $ do+ v <- eval "'hello';"+ liftIO $ v @?= String "hello"++ , testCase "string concatenation" $ mquickjsMultithreaded $ do+ v <- eval "'hello' + ' ' + 'world';"+ liftIO $ v @?= String "hello world"++ , testCase "boolean true" $ mquickjsMultithreaded $ do+ v <- eval "true;"+ liftIO $ v @?= Bool True++ , testCase "boolean false" $ mquickjsMultithreaded $ do+ v <- eval "false;"+ liftIO $ v @?= Bool False++ , testCase "boolean expression" $ mquickjsMultithreaded $ do+ v <- eval "5 > 3;"+ liftIO $ v @?= Bool True++ , testCase "null value" $ mquickjsMultithreaded $ do+ v <- eval "null;"+ liftIO $ v @?= Null++ , testCase "undefined becomes null" $ mquickjsMultithreaded $ do+ v <- eval "undefined;"+ liftIO $ v @?= Null++ , testCase "array literal" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3];"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3])++ , testCase "empty array" $ mquickjsMultithreaded $ do+ v <- eval "[];"+ liftIO $ v @?= Array V.empty++ , testCase "mixed array" $ mquickjsMultithreaded $ do+ v <- eval "[1, 'two', true, null];"+ liftIO $ v @?= Array (V.fromList [Number 1, String "two", Bool True, Null])++ , testCase "object literal" $ mquickjsMultithreaded $ do+ v <- eval "({a: 1, b: 2});"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "a", Number 1), (K.fromText "b", Number 2)])++ , testCase "empty object" $ mquickjsMultithreaded $ do+ v <- eval "({});"+ liftIO $ v @?= Object KM.empty++ , testCase "nested object" $ mquickjsMultithreaded $ do+ v <- eval "({outer: {inner: 42}});"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "outer", Object (KM.fromList [(K.fromText "inner", Number 42)]))])++ , testCase "nested array" $ mquickjsMultithreaded $ do+ v <- eval "[[1, 2], [3, 4]];"+ liftIO $ v @?= Array (V.fromList [Array (V.fromList [Number 1, Number 2]), Array (V.fromList [Number 3, Number 4])])++ , testCase "ternary operator" $ mquickjsMultithreaded $ do+ v <- eval "true ? 'yes' : 'no';"+ liftIO $ v @?= String "yes"++ , testCase "logical AND" $ mquickjsMultithreaded $ do+ v <- eval "true && false;"+ liftIO $ v @?= Bool False++ , testCase "logical OR" $ mquickjsMultithreaded $ do+ v <- eval "true || false;"+ liftIO $ v @?= Bool True++ , testCase "equality check" $ mquickjsMultithreaded $ do+ v <- eval "5 === 5;"+ liftIO $ v @?= Bool True++ , testCase "inequality check" $ mquickjsMultithreaded $ do+ v <- eval "5 !== 3;"+ liftIO $ v @?= Bool True+ ]++-- | Function definition and calling tests+-- Note: Micro QuickJS has limited ES6 support - no arrow functions, const/let, etc.+functionTests :: TestTree+functionTests = testGroup "functions"+ [ testCase "define and call simple function" $ mquickjsMultithreaded $ do+ eval_ "function add(a, b) { return a + b; }"+ v <- eval "add(2, 3);"+ liftIO $ v @?= Number 5++ , testCase "function with var" $ mquickjsMultithreaded $ do+ eval_ "function double(x) { return x * 2; }"+ v <- eval "double(21);"+ liftIO $ v @?= Number 42++ , testCase "call with withJSValue" $ mquickjsMultithreaded $ do+ eval_ "function increment(x) { return x + 1; }"+ v <- withJSValue (5 :: Int) $ \x -> call "increment" [x]+ liftIO $ v @?= Number 6++ , testCase "call with multiple args" $ mquickjsMultithreaded $ do+ eval_ "function subtract(a, b) { return a - b; }"+ v <- withJSValue (10 :: Int) $ \a ->+ withJSValue (3 :: Int) $ \b ->+ call "subtract" [a, b]+ liftIO $ v @?= Number 7++ , testCase "call with string argument" $ mquickjsMultithreaded $ do+ eval_ "function greet(name) { return 'Hello, ' + name + '!'; }"+ v <- withJSValue ("World" :: T.Text) $ \name -> call "greet" [name]+ liftIO $ v @?= String "Hello, World!"++ , testCase "call with array argument" $ mquickjsMultithreaded $ do+ eval_ "function sumArray(arr) { var total = 0; for (var i = 0; i < arr.length; i++) { total = total + arr[i]; } return total; }"+ v <- withJSValue ([1, 2, 3, 4, 5] :: [Int]) $ \arr -> call "sumArray" [arr]+ liftIO $ v @?= Number 15++ , testCase "call with object argument" $ mquickjsMultithreaded $ do+ eval_ "function getField(obj) { return obj.value; }"+ let obj = Object (KM.fromList [(K.fromText "value", Number 99)])+ v <- withJSValue obj $ \o -> call "getField" [o]+ liftIO $ v @?= Number 99++ , testCase "function returning object" $ mquickjsMultithreaded $ do+ eval_ "function makePoint(x, y) { return {x: x, y: y}; }"+ v <- eval "makePoint(10, 20);"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "x", Number 10), (K.fromText "y", Number 20)])++ , testCase "function returning array" $ mquickjsMultithreaded $ do+ eval_ "function makeArray(n) { var arr = []; for (var i = 0; i < n; i++) { arr.push(i); } return arr; }"+ v <- eval "makeArray(5);"+ liftIO $ v @?= Array (V.fromList [Number 0, Number 1, Number 2, Number 3, Number 4])++ , testCase "recursive function" $ mquickjsMultithreaded $ do+ eval_ "function factorial(n) { return n <= 1 ? 1 : n * factorial(n - 1); }"+ v <- eval "factorial(5);"+ liftIO $ v @?= Number 120++ , testCase "closure" $ mquickjsMultithreaded $ do+ eval_ "function makeCounter() { var count = 0; return function() { count = count + 1; return count; }; }"+ eval_ "var counter = makeCounter();"+ v1 <- eval "counter();"+ v2 <- eval "counter();"+ v3 <- eval "counter();"+ liftIO $ v1 @?= Number 1+ liftIO $ v2 @?= Number 2+ liftIO $ v3 @?= Number 3++ , testCase "higher-order function" $ mquickjsMultithreaded $ do+ eval_ "function apply(f, x) { return f(x); }"+ eval_ "function square(x) { return x * x; }"+ v <- eval "apply(square, 5);"+ liftIO $ v @?= Number 25+ ]++-- | Error handling tests+errorTests :: TestTree+errorTests = testGroup "error handling"+ [ testCase "throw string" $ mquickjsMultithreaded $+ try (eval "throw 'Error'") >>= \case+ Left (_ :: SomeJSRuntimeException) -> return ()+ Right _ -> liftIO $ assertFailure "should fail with an Exception..."++ , testCase "throw Error object" $ mquickjsMultithreaded $+ try (eval "throw new Error('Something went wrong')") >>= \case+ Left (_ :: SomeJSRuntimeException) -> return ()+ Right _ -> liftIO $ assertFailure "should fail with an Exception..."++ , testCase "reference error" $ mquickjsMultithreaded $+ try (eval "nonExistentVariable") >>= \case+ Left (_ :: SomeJSRuntimeException) -> return ()+ Right _ -> liftIO $ assertFailure "should fail with ReferenceError..."++ , testCase "syntax error" $ mquickjsMultithreaded $+ try (eval "function {") >>= \case+ Left (_ :: SomeJSRuntimeException) -> return ()+ Right _ -> liftIO $ assertFailure "should fail with SyntaxError..."++ , testCase "type error" $ mquickjsMultithreaded $+ try (eval "null.foo") >>= \case+ Left (_ :: SomeJSRuntimeException) -> return ()+ Right _ -> liftIO $ assertFailure "should fail with TypeError..."++ , testCase "call undefined function" $ mquickjsMultithreaded $+ try (call "nonExistentFunction" []) >>= \case+ Left (_ :: SomeJSRuntimeException) -> return ()+ Right _ -> liftIO $ assertFailure "should fail when calling undefined function..."++ , testCase "division by zero returns infinity" $ mquickjsMultithreaded $ do+ v <- eval "1 / 0;"+ -- JavaScript returns Infinity for division by zero+ case v of+ Number n -> liftIO $ assertBool "should be infinite" (isInfinite (fromRational (toRational n) :: Double))+ _ -> liftIO $ assertFailure "expected Number"+ ]++-- | String handling tests+-- Note: Micro QuickJS has limited ES6 support - no template literals+stringTests :: TestTree+stringTests = testGroup "strings"+ [ testCase "empty string" $ mquickjsMultithreaded $ do+ v <- eval "''"+ liftIO $ v @?= String ""++ , testCase "string with spaces" $ mquickjsMultithreaded $ do+ v <- eval "' hello '"+ liftIO $ v @?= String " hello "++ , testCase "string length" $ mquickjsMultithreaded $ do+ v <- eval "'hello'.length"+ liftIO $ v @?= Number 5++ , testCase "string methods - toUpperCase" $ mquickjsMultithreaded $ do+ v <- eval "'hello'.toUpperCase()"+ liftIO $ v @?= String "HELLO"++ , testCase "string methods - toLowerCase" $ mquickjsMultithreaded $ do+ v <- eval "'HELLO'.toLowerCase()"+ liftIO $ v @?= String "hello"++ , testCase "string methods - substring" $ mquickjsMultithreaded $ do+ v <- eval "'hello world'.substring(0, 5)"+ liftIO $ v @?= String "hello"++ , testCase "string methods - split" $ mquickjsMultithreaded $ do+ v <- eval "'a,b,c'.split(',')"+ liftIO $ v @?= Array (V.fromList [String "a", String "b", String "c"])++ , testCase "string methods - trim" $ mquickjsMultithreaded $ do+ v <- eval "' hello '.trim()"+ liftIO $ v @?= String "hello"++ , testCase "string with newlines" $ mquickjsMultithreaded $ do+ v <- eval "'line1\\nline2'"+ liftIO $ v @?= String "line1\nline2"++ , testCase "string with tabs" $ mquickjsMultithreaded $ do+ v <- eval "'col1\\tcol2'"+ liftIO $ v @?= String "col1\tcol2"++ , testCase "string with special chars" $ mquickjsMultithreaded $ do+ v <- eval "'hello\\\\world'"+ liftIO $ v @?= String "hello\\world"++ , testCase "string with quotes" $ mquickjsMultithreaded $ do+ v <- eval "'it\\'s a test'"+ liftIO $ v @?= String "it's a test"++ , testCase "string concatenation interpolation" $ mquickjsMultithreaded $ do+ eval_ "var name = 'world'"+ v <- eval "'hello ' + name"+ liftIO $ v @?= String "hello world"+ ]++-- | Array operation tests+-- Note: Micro QuickJS has limited ES6 array methods, using ES5-compatible approaches+arrayTests :: TestTree+arrayTests = testGroup "arrays"+ [ testCase "array length" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3].length"+ liftIO $ v @?= Number 3++ , testCase "array indexing" $ mquickjsMultithreaded $ do+ v <- eval "[10, 20, 30][1]"+ liftIO $ v @?= Number 20++ , testCase "array push and access" $ mquickjsMultithreaded $ do+ eval_ "var arr = [1, 2]; arr.push(3);"+ v <- eval "arr"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3])++ , testCase "array pop" $ mquickjsMultithreaded $ do+ v <- eval "var arr = [1, 2, 3]; arr.pop()"+ liftIO $ v @?= Number 3++ , testCase "array shift" $ mquickjsMultithreaded $ do+ v <- eval "var arr = [1, 2, 3]; arr.shift()"+ liftIO $ v @?= Number 1++ , testCase "array unshift" $ mquickjsMultithreaded $ do+ eval_ "var arr = [2, 3]; arr.unshift(1);"+ v <- eval "arr"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3])++ , testCase "array concat" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2].concat([3, 4])"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3, Number 4])++ , testCase "array slice" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3, 4, 5].slice(1, 4)"+ liftIO $ v @?= Array (V.fromList [Number 2, Number 3, Number 4])++ , testCase "array reverse" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3].reverse()"+ liftIO $ v @?= Array (V.fromList [Number 3, Number 2, Number 1])++ , testCase "array join" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3].join('-')"+ liftIO $ v @?= String "1-2-3"++ , testCase "array indexOf" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3, 2].indexOf(2)"+ liftIO $ v @?= Number 1++ , testCase "array lastIndexOf" $ mquickjsMultithreaded $ do+ v <- eval "[1, 2, 3, 2].lastIndexOf(2)"+ liftIO $ v @?= Number 3++ , testCase "array sort numbers" $ mquickjsMultithreaded $ do+ eval_ "function compareNumbers(a, b) { return a - b; }"+ v <- eval "[3, 1, 4, 1, 5].sort(compareNumbers)"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 1, Number 3, Number 4, Number 5])++ , testCase "array sort strings" $ mquickjsMultithreaded $ do+ v <- eval "['banana', 'apple', 'cherry'].sort()"+ liftIO $ v @?= Array (V.fromList [String "apple", String "banana", String "cherry"])++ , testCase "array splice" $ mquickjsMultithreaded $ do+ eval_ "var arr = [1, 2, 3, 4, 5]; arr.splice(2, 2);"+ v <- eval "arr"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 5])+ ]++-- | Object operation tests+-- Note: Micro QuickJS has limited ES6 support - using ES5 compatible patterns+objectTests :: TestTree+objectTests = testGroup "objects"+ [ testCase "object property access dot notation" $ mquickjsMultithreaded $ do+ v <- eval "({a: 1, b: 2}).a"+ liftIO $ v @?= Number 1++ , testCase "object property access bracket notation" $ mquickjsMultithreaded $ do+ v <- eval "({a: 1, b: 2})['b']"+ liftIO $ v @?= Number 2++ , testCase "Object.keys" $ mquickjsMultithreaded $ do+ v <- eval "Object.keys({a: 1, b: 2, c: 3})"+ liftIO $ v @?= Array (V.fromList [String "a", String "b", String "c"])++ , testCase "nested property access" $ mquickjsMultithreaded $ do+ v <- eval "({outer: {inner: {value: 42}}}).outer.inner.value"+ liftIO $ v @?= Number 42++ , testCase "object with method" $ mquickjsMultithreaded $ do+ v <- eval "({value: 5, double: function() { return this.value * 2; }}).double()"+ liftIO $ v @?= Number 10++ , testCase "object property assignment" $ mquickjsMultithreaded $ do+ eval_ "var obj = {}; obj.x = 10; obj.y = 20;"+ v <- eval "obj"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "x", Number 10), (K.fromText "y", Number 20)])++ , testCase "delete object property" $ mquickjsMultithreaded $ do+ eval_ "var obj = {a: 1, b: 2}; delete obj.a;"+ v <- eval "obj"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "b", Number 2)])++ , testCase "in operator" $ mquickjsMultithreaded $ do+ v <- eval "'a' in {a: 1, b: 2}"+ liftIO $ v @?= Bool True++ , testCase "hasOwnProperty" $ mquickjsMultithreaded $ do+ v <- eval "({a: 1}).hasOwnProperty('a')"+ liftIO $ v @?= Bool True++ , testCase "object with array property" $ mquickjsMultithreaded $ do+ v <- eval "({items: [1, 2, 3]}).items"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3])+ ]++-- | Built-in Math tests+mathTests :: TestTree+mathTests = testGroup "Math"+ [ testCase "Math.PI" $ mquickjsMultithreaded $ do+ Number v <- eval "Math.PI"+ liftIO $ assertBool "PI should be approximately 3.14159" (abs (realToFrac v - 3.14159265358979) < 0.00001)++ , testCase "Math.abs" $ mquickjsMultithreaded $ do+ v <- eval "Math.abs(-42)"+ liftIO $ v @?= Number 42++ , testCase "Math.floor" $ mquickjsMultithreaded $ do+ v <- eval "Math.floor(3.7)"+ liftIO $ v @?= Number 3++ , testCase "Math.ceil" $ mquickjsMultithreaded $ do+ v <- eval "Math.ceil(3.2)"+ liftIO $ v @?= Number 4++ , testCase "Math.round" $ mquickjsMultithreaded $ do+ v <- eval "Math.round(3.5)"+ liftIO $ v @?= Number 4++ , testCase "Math.max" $ mquickjsMultithreaded $ do+ v <- eval "Math.max(1, 5, 3)"+ liftIO $ v @?= Number 5++ , testCase "Math.min" $ mquickjsMultithreaded $ do+ v <- eval "Math.min(1, 5, 3)"+ liftIO $ v @?= Number 1++ , testCase "Math.pow" $ mquickjsMultithreaded $ do+ v <- eval "Math.pow(2, 10)"+ liftIO $ v @?= Number 1024++ , testCase "Math.sqrt" $ mquickjsMultithreaded $ do+ v <- eval "Math.sqrt(16)"+ liftIO $ v @?= Number 4++ , testCase "Math.random produces number between 0 and 1" $ mquickjsMultithreaded $ do+ Number v <- eval "Math.random()"+ liftIO $ assertBool "random should be >= 0 and < 1" (v >= 0 && v < 1)+ ]++-- | JSON tests+jsonTests :: TestTree+jsonTests = testGroup "JSON"+ [ testCase "JSON.stringify object" $ mquickjsMultithreaded $ do+ v <- eval "JSON.stringify({a: 1, b: 2})"+ -- Note: key order may vary+ case v of+ String s -> liftIO $ assertBool "should contain expected content"+ (T.isInfixOf "\"a\":1" s && T.isInfixOf "\"b\":2" s)+ _ -> liftIO $ assertFailure "expected String"++ , testCase "JSON.stringify array" $ mquickjsMultithreaded $ do+ v <- eval "JSON.stringify([1, 2, 3])"+ liftIO $ v @?= String "[1,2,3]"++ , testCase "JSON.parse object" $ mquickjsMultithreaded $ do+ v <- eval "JSON.parse('{\"x\": 10, \"y\": 20}')"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "x", Number 10), (K.fromText "y", Number 20)])++ , testCase "JSON.parse array" $ mquickjsMultithreaded $ do+ v <- eval "JSON.parse('[1, 2, 3]')"+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3])++ , testCase "JSON roundtrip" $ mquickjsMultithreaded $ do+ v <- eval "JSON.parse(JSON.stringify({nested: {value: 42}}))"+ liftIO $ v @?= Object (KM.fromList [(K.fromText "nested", Object (KM.fromList [(K.fromText "value", Number 42)]))])+ ]++-- | Edge cases and special values tests+-- Note: Micro QuickJS has limited ES6 support - using ES5 compatible patterns+edgeCaseTests :: TestTree+edgeCaseTests = testGroup "edge cases"+ [ testCase "large integer" $ mquickjsMultithreaded $ do+ v <- eval "1000000000"+ liftIO $ v @?= Number 1000000000++ , testCase "very small float" $ mquickjsMultithreaded $ do+ Number v <- eval "0.0000001"+ liftIO $ assertBool "should be small positive number" (v > 0 && v < 0.001)++ , testCase "NaN check" $ mquickjsMultithreaded $ do+ v <- eval "isNaN(NaN)"+ liftIO $ v @?= Bool True++ , testCase "Infinity" $ mquickjsMultithreaded $ do+ v <- eval "Infinity > 1000000"+ liftIO $ v @?= Bool True++ , testCase "negative Infinity" $ mquickjsMultithreaded $ do+ v <- eval "-Infinity < -1000000"+ liftIO $ v @?= Bool True++ , testCase "multiple statements with var" $ mquickjsMultithreaded $ do+ v <- eval "var x = 5; var y = 10; x + y;"+ liftIO $ v @?= Number 15++ , testCase "var declaration" $ mquickjsMultithreaded $ do+ eval_ "var PI = 3.14159"+ v <- eval "PI"+ liftIO $ v @?= Number 3.14159++ , testCase "var reassignment" $ mquickjsMultithreaded $ do+ eval_ "var counter = 0"+ eval_ "counter = counter + 1"+ v <- eval "counter"+ liftIO $ v @?= Number 1++ , testCase "typeof operator" $ mquickjsMultithreaded $ do+ v1 <- eval "typeof 42"+ v2 <- eval "typeof 'hello'"+ v3 <- eval "typeof true"+ v4 <- eval "typeof {}"+ v5 <- eval "typeof []"+ liftIO $ v1 @?= String "number"+ liftIO $ v2 @?= String "string"+ liftIO $ v3 @?= String "boolean"+ liftIO $ v4 @?= String "object"+ liftIO $ v5 @?= String "object"++ , testCase "array with many elements" $ mquickjsMultithreaded $ do+ eval_ "function makeArray(n) { var arr = []; for (var i = 0; i < n; i++) { arr.push(i); } return arr; }"+ v <- eval "makeArray(100)"+ case v of+ Array arr -> liftIO $ V.length arr @?= 100+ _ -> liftIO $ assertFailure "expected Array"++ , testCase "deeply nested structure" $ mquickjsMultithreaded $ do+ v <- eval "({a: {b: {c: {d: {e: 'deep'}}}}})"+ liftIO $ v @?= Object (KM.fromList+ [(K.fromText "a", Object (KM.fromList+ [(K.fromText "b", Object (KM.fromList+ [(K.fromText "c", Object (KM.fromList+ [(K.fromText "d", Object (KM.fromList+ [(K.fromText "e", String "deep")]))]))]))]))])++ , testCase "scientific notation" $ mquickjsMultithreaded $ do+ v <- eval "1e6"+ liftIO $ v @?= Number 1000000++ , testCase "hexadecimal" $ mquickjsMultithreaded $ do+ v <- eval "0xFF"+ liftIO $ v @?= Number 255++ , testCase "binary literal" $ mquickjsMultithreaded $ do+ v <- eval "parseInt('1010', 2)"+ liftIO $ v @?= Number 10+ ]++-- | Control flow tests+-- Note: Micro QuickJS has limited ES6 support - using ES5 compatible patterns+controlFlowTests :: TestTree+controlFlowTests = testGroup "control flow"+ [ testCase "if-else expression" $ mquickjsMultithreaded $ do+ eval_ "function test(x) { if (x > 0) { return 'positive'; } else { return 'non-positive'; } }"+ v1 <- eval "test(5)"+ v2 <- eval "test(-5)"+ liftIO $ v1 @?= String "positive"+ liftIO $ v2 @?= String "non-positive"++ , testCase "for loop" $ mquickjsMultithreaded $ do+ eval_ "function sumTo(n) { var sum = 0; for (var i = 1; i <= n; i++) { sum = sum + i; } return sum; }"+ v <- eval "sumTo(10)"+ liftIO $ v @?= Number 55++ , testCase "while loop" $ mquickjsMultithreaded $ do+ eval_ "function countdown(n) { var result = []; while (n > 0) { result.push(n); n = n - 1; } return result; }"+ v <- eval "countdown(5)"+ liftIO $ v @?= Array (V.fromList [Number 5, Number 4, Number 3, Number 2, Number 1])++ , testCase "for-in loop" $ mquickjsMultithreaded $ do+ eval_ "function getKeys(obj) { var keys = []; for (var k in obj) { keys.push(k); } return keys; }"+ v <- eval "getKeys({a: 1, b: 2})"+ liftIO $ v @?= Array (V.fromList [String "a", String "b"])++ , testCase "switch statement" $ mquickjsMultithreaded $ do+ eval_ "function dayName(n) { switch(n) { case 0: return 'Sunday'; case 1: return 'Monday'; default: return 'Other'; } }"+ v1 <- eval "dayName(0)"+ v2 <- eval "dayName(1)"+ v3 <- eval "dayName(5)"+ liftIO $ v1 @?= String "Sunday"+ liftIO $ v2 @?= String "Monday"+ liftIO $ v3 @?= String "Other"++ , testCase "try-catch in JS" $ mquickjsMultithreaded $ do+ eval_ "function safeDiv(a, b) { try { if (b === 0) throw 'Division by zero'; return a / b; } catch(e) { return null; } }"+ v1 <- eval "safeDiv(10, 2)"+ v2 <- eval "safeDiv(10, 0)"+ liftIO $ v1 @?= Number 5+ liftIO $ v2 @?= Null++ , testCase "do-while loop" $ mquickjsMultithreaded $ do+ eval_ "function doWhileTest() { var x = 0; do { x = x + 1; } while (x < 5); return x; }"+ v <- eval "doWhileTest()"+ liftIO $ v @?= Number 5++ , testCase "break statement" $ mquickjsMultithreaded $ do+ eval_ "function findFirst(arr, target) { for (var i = 0; i < arr.length; i++) { if (arr[i] === target) { return i; } } return -1; }"+ v <- eval "findFirst([1, 2, 3, 4, 5], 3)"+ liftIO $ v @?= Number 2++ , testCase "continue statement" $ mquickjsMultithreaded $ do+ eval_ "function sumEven(n) { var sum = 0; for (var i = 1; i <= n; i++) { if (i % 2 !== 0) continue; sum = sum + i; } return sum; }"+ v <- eval "sumEven(10)"+ liftIO $ v @?= Number 30+ ]++-- | Tests for different entry point functions+entryPointTests :: TestTree+entryPointTests = testGroup "entry points"+ [ testCase "mquickjs basic eval" $ mquickjs $ do+ v <- eval "1 + 2"+ liftIO $ v @?= Number 3++ , testCase "mquickjsWithMemory basic eval" $ mquickjsWithMemory (10 * 1024 * 1024) $ do+ v <- eval "1 + 2"+ liftIO $ v @?= Number 3++ , testCase "mquickjsMultithreaded basic eval" $ mquickjsMultithreaded $ do+ v <- eval "1 + 2"+ liftIO $ v @?= Number 3++ , testCase "mquickjsMultithreadedWithMemory basic eval" $ mquickjsMultithreadedWithMemory (10 * 1024 * 1024) $ do+ v <- eval "1 + 2"+ liftIO $ v @?= Number 3++ , testCase "mquickjsWithMemory custom allocation" $ mquickjsWithMemory (50 * 1024 * 1024) $ do+ -- Test with larger memory allocation+ eval_ "var arr = []; for (var i = 0; i < 10000; i++) { arr.push({index: i, data: 'test'}); }"+ v <- eval "arr.length"+ liftIO $ v @?= Number 10000++ , testCase "mquickjsMultithreadedWithMemory custom allocation" $ mquickjsMultithreadedWithMemory (50 * 1024 * 1024) $ do+ -- Test with larger memory allocation+ eval_ "var arr = []; for (var i = 0; i < 10000; i++) { arr.push({index: i, data: 'test'}); }"+ v <- eval "arr.length"+ liftIO $ v @?= Number 10000+ ]++-- | Tests for Haskell-JavaScript interop via withJSValue and call+interopTests :: TestTree+interopTests = testGroup "Haskell-JS interop"+ [ testCase "pass Int to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue (42 :: Int) $ \x -> call "identity" [x]+ liftIO $ v @?= Number 42++ , testCase "pass negative Int to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue ((-100) :: Int) $ \x -> call "identity" [x]+ liftIO $ v @?= Number (-100)++ , testCase "pass Double to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue (3.14159 :: Double) $ \x -> call "identity" [x]+ case v of+ Number n -> liftIO $ assertBool "should be close to pi" (abs (realToFrac n - 3.14159) < 0.0001)+ _ -> liftIO $ assertFailure "expected Number"++ , testCase "pass Bool True to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue True $ \x -> call "identity" [x]+ liftIO $ v @?= Bool True++ , testCase "pass Bool False to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue False $ \x -> call "identity" [x]+ liftIO $ v @?= Bool False++ , testCase "pass Null to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue Null $ \x -> call "identity" [x]+ liftIO $ v @?= Null++ , testCase "pass Text to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue ("hello world" :: T.Text) $ \x -> call "identity" [x]+ liftIO $ v @?= String "hello world"++ , testCase "pass empty Text to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue ("" :: T.Text) $ \x -> call "identity" [x]+ liftIO $ v @?= String ""++ , testCase "pass list of Ints to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue ([1, 2, 3] :: [Int]) $ \x -> call "identity" [x]+ liftIO $ v @?= Array (V.fromList [Number 1, Number 2, Number 3])++ , testCase "pass empty list to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue ([] :: [Int]) $ \x -> call "identity" [x]+ liftIO $ v @?= Array V.empty++ , testCase "pass nested list to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ v <- withJSValue ([[1, 2], [3, 4]] :: [[Int]]) $ \x -> call "identity" [x]+ liftIO $ v @?= Array (V.fromList [Array (V.fromList [Number 1, Number 2]), Array (V.fromList [Number 3, Number 4])])++ , testCase "pass Aeson Object to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ let obj = Object (KM.fromList [(K.fromText "name", String "test"), (K.fromText "value", Number 42)])+ v <- withJSValue obj $ \x -> call "identity" [x]+ liftIO $ v @?= obj++ , testCase "pass empty Object to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ let obj = Object KM.empty+ v <- withJSValue obj $ \x -> call "identity" [x]+ liftIO $ v @?= obj++ , testCase "pass nested Object to JS" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ let inner = Object (KM.fromList [(K.fromText "value", Number 99)])+ let obj = Object (KM.fromList [(K.fromText "nested", inner)])+ v <- withJSValue obj $ \x -> call "identity" [x]+ liftIO $ v @?= obj++ , testCase "JS function transforms Haskell Int" $ mquickjsMultithreaded $ do+ eval_ "function double(x) { return x * 2; }"+ v <- withJSValue (21 :: Int) $ \x -> call "double" [x]+ liftIO $ v @?= Number 42++ , testCase "JS function transforms Haskell String" $ mquickjsMultithreaded $ do+ eval_ "function shout(s) { return s.toUpperCase() + '!'; }"+ v <- withJSValue ("hello" :: T.Text) $ \x -> call "shout" [x]+ liftIO $ v @?= String "HELLO!"++ , testCase "JS function transforms Haskell Array" $ mquickjsMultithreaded $ do+ eval_ "function sumArray(arr) { var sum = 0; for (var i = 0; i < arr.length; i++) { sum = sum + arr[i]; } return sum; }"+ v <- withJSValue ([1, 2, 3, 4, 5] :: [Int]) $ \arr -> call "sumArray" [arr]+ liftIO $ v @?= Number 15++ , testCase "JS function extracts Object field" $ mquickjsMultithreaded $ do+ eval_ "function getName(obj) { return obj.name; }"+ let obj = Object (KM.fromList [(K.fromText "name", String "Alice"), (K.fromText "age", Number 30)])+ v <- withJSValue obj $ \x -> call "getName" [x]+ liftIO $ v @?= String "Alice"++ , testCase "JS function modifies Object" $ mquickjsMultithreaded $ do+ eval_ "function addField(obj) { obj.added = true; return obj; }"+ let obj = Object (KM.fromList [(K.fromText "original", Number 1)])+ v <- withJSValue obj $ \x -> call "addField" [x]+ liftIO $ v @?= Object (KM.fromList [(K.fromText "original", Number 1), (K.fromText "added", Bool True)])++ , testCase "pass multiple different types" $ mquickjsMultithreaded $ do+ -- Note: Test with single argument to avoid argument order issues+ eval_ "function formatValue(val) { return 'value: ' + val; }"+ v <- withJSValue (42 :: Int) $ \num -> call "formatValue" [num]+ liftIO $ v @?= String "value: 42"++ , testCase "nested withJSValue calls" $ mquickjsMultithreaded $ do+ eval_ "function add(a, b) { return a + b; }"+ v <- withJSValue (10 :: Int) $ \a -> do+ withJSValue (20 :: Int) $ \b -> do+ call "add" [a, b]+ liftIO $ v @?= Number 30++ , testCase "call function with zero arguments" $ mquickjsMultithreaded $ do+ eval_ "function getFortyTwo() { return 42; }"+ v <- call "getFortyTwo" []+ liftIO $ v @?= Number 42++ , testCase "call function that returns undefined" $ mquickjsMultithreaded $ do+ eval_ "function returnNothing() { }"+ v <- call "returnNothing" []+ liftIO $ v @?= Null++ , testCase "pass array and get element back" $ mquickjsMultithreaded $ do+ eval_ "function getSecond(arr) { return arr[1]; }"+ v <- withJSValue ([10, 20, 30] :: [Int]) $ \arr -> call "getSecond" [arr]+ liftIO $ v @?= Number 20++ , testCase "pass object and get nested value" $ mquickjsMultithreaded $ do+ eval_ "function getDeep(obj) { return obj.level1.level2.value; }"+ let obj = Object (KM.fromList+ [(K.fromText "level1", Object (KM.fromList+ [(K.fromText "level2", Object (KM.fromList+ [(K.fromText "value", Number 999)]))]))])+ v <- withJSValue obj $ \x -> call "getDeep" [x]+ liftIO $ v @?= Number 999++ , testCase "JS modifies array elements" $ mquickjsMultithreaded $ do+ eval_ "function doubleAll(arr) { var result = []; for (var i = 0; i < arr.length; i++) { result.push(arr[i] * 2); } return result; }"+ v <- withJSValue ([1, 2, 3] :: [Int]) $ \arr -> call "doubleAll" [arr]+ liftIO $ v @?= Array (V.fromList [Number 2, Number 4, Number 6])++ , testCase "roundtrip complex structure" $ mquickjsMultithreaded $ do+ eval_ "function identity(x) { return x; }"+ let complex = Object (KM.fromList+ [ (K.fromText "name", String "test")+ , (K.fromText "values", Array (V.fromList [Number 1, Number 2, Number 3]))+ , (K.fromText "nested", Object (KM.fromList [(K.fromText "flag", Bool True)]))+ , (K.fromText "nothing", Null)+ ])+ v <- withJSValue complex $ \x -> call "identity" [x]+ liftIO $ v @?= complex++ , testCase "use Haskell value in JS expression via global" $ mquickjsMultithreaded $ do+ -- Declare global variable first, then set it+ eval_ "var globalValue = 0;"+ eval_ "function setGlobal(val) { globalValue = val; }"+ eval_ "function useGlobal() { return globalValue * 2; }"+ _ <- withJSValue (21 :: Int) $ \x -> call "setGlobal" [x]+ v <- call "useGlobal" []+ liftIO $ v @?= Number 42++ , testCase "multiple sequential calls" $ mquickjsMultithreaded $ do+ eval_ "var accumulator = 0;"+ eval_ "function addTo(x) { accumulator = accumulator + x; return accumulator; }"+ v1 <- withJSValue (10 :: Int) $ \x -> call "addTo" [x]+ v2 <- withJSValue (20 :: Int) $ \x -> call "addTo" [x]+ v3 <- withJSValue (12 :: Int) $ \x -> call "addTo" [x]+ liftIO $ v1 @?= Number 10+ liftIO $ v2 @?= Number 30+ liftIO $ v3 @?= Number 42++ , testCase "pass mixed array to JS" $ mquickjsMultithreaded $ do+ eval_ "function getTypes(arr) { var types = []; for (var i = 0; i < arr.length; i++) { types.push(typeof arr[i]); } return types; }"+ let mixed = Array (V.fromList [Number 1, String "two", Bool True, Null])+ v <- withJSValue mixed $ \arr -> call "getTypes" [arr]+ liftIO $ v @?= Array (V.fromList [String "number", String "string", String "boolean", String "object"])+ ]++tests :: TestTree+tests =+ -- adjustOption (\_ -> QuickCheckTests 10) $+ -- adjustOption (\_ -> QuickCheckVerbose True) $+ testGroup "MQuickJS"+ [ testCase "empty mquickjs call" (mquickjsMultithreaded $ pure ())+ , entryPointTests+ , evalTests+ , functionTests+ , errorTests+ , stringTests+ , arrayTests+ , objectTests+ , mathTests+ , jsonTests+ , edgeCaseTests+ , controlFlowTests+ , interopTests+ , testProperty "marshalling Value to JSValue and back" marshall_to_from_JSValue+ ]++main = defaultMain tests