diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -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/).
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -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)
diff --git a/mquickjs-hs.cabal b/mquickjs-hs.cabal
new file mode 100644
--- /dev/null
+++ b/mquickjs-hs.cabal
@@ -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
diff --git a/mquickjs/cutils.c b/mquickjs/cutils.c
new file mode 100644
--- /dev/null
+++ b/mquickjs/cutils.c
@@ -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;
+}
diff --git a/mquickjs/cutils.h b/mquickjs/cutils.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/cutils.h
@@ -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 */
diff --git a/mquickjs/dtoa.c b/mquickjs/dtoa.c
new file mode 100644
--- /dev/null
+++ b/mquickjs/dtoa.c
@@ -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;
+}
diff --git a/mquickjs/dtoa.h b/mquickjs/dtoa.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/dtoa.h
@@ -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);
diff --git a/mquickjs/libm.c b/mquickjs/libm.c
new file mode 100644
--- /dev/null
+++ b/mquickjs/libm.c
@@ -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);
+}
+
diff --git a/mquickjs/libm.h b/mquickjs/libm.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/libm.h
@@ -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);
diff --git a/mquickjs/list.h b/mquickjs/list.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/list.h
@@ -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 */
diff --git a/mquickjs/mqjs_stdlib.h b/mquickjs/mqjs_stdlib.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/mqjs_stdlib.h
@@ -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,
+};
+
diff --git a/mquickjs/mquickjs.c b/mquickjs/mquickjs.c
new file mode 100644
--- /dev/null
+++ b/mquickjs/mquickjs.c
@@ -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);
+}
diff --git a/mquickjs/mquickjs.h b/mquickjs/mquickjs.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/mquickjs.h
@@ -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 */
diff --git a/mquickjs/mquickjs_atom.h b/mquickjs/mquickjs_atom.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/mquickjs_atom.h
@@ -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
+
diff --git a/mquickjs/mquickjs_build.h b/mquickjs/mquickjs_build.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/mquickjs_build.h
@@ -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 */
diff --git a/mquickjs/mquickjs_opcode.h b/mquickjs/mquickjs_opcode.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/mquickjs_opcode.h
@@ -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 */
diff --git a/mquickjs/mquickjs_priv.h b/mquickjs/mquickjs_priv.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/mquickjs_priv.h
@@ -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 */
diff --git a/mquickjs/softfp_template.h b/mquickjs/softfp_template.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/softfp_template.h
@@ -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
diff --git a/mquickjs/softfp_template_icvt.h b/mquickjs/softfp_template_icvt.h
new file mode 100644
--- /dev/null
+++ b/mquickjs/softfp_template_icvt.h
@@ -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
diff --git a/mquickjs/stdlib_wrapper.c b/mquickjs/stdlib_wrapper.c
new file mode 100644
--- /dev/null
+++ b/mquickjs/stdlib_wrapper.c
@@ -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"
diff --git a/src/MQuickJS.hs b/src/MQuickJS.hs
new file mode 100644
--- /dev/null
+++ b/src/MQuickJS.hs
@@ -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
diff --git a/src/MQuickJS/Error.hs b/src/MQuickJS/Error.hs
new file mode 100644
--- /dev/null
+++ b/src/MQuickJS/Error.hs
@@ -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
diff --git a/src/MQuickJS/Types.hsc b/src/MQuickJS/Types.hsc
new file mode 100644
--- /dev/null
+++ b/src/MQuickJS/Types.hsc
@@ -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 |])
+  ]
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -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
