diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..80a1ec7bc3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## 31.05.2025 +- updated QuickJs to 2025-04-26 build + +## 31.05.2025 +- fixed last release for building with MSVC \ No newline at end of file diff --git a/JSTest.sln b/JSTest.sln new file mode 100644 index 0000000000..d3451d0f71 --- /dev/null +++ b/JSTest.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSTest", "JSTest.vcxproj", "{05D4BCD1-30A9-4341-BD68-C89506F0AD7F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Debug|x64.ActiveCfg = Debug|x64 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Debug|x64.Build.0 = Debug|x64 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Debug|x86.ActiveCfg = Debug|Win32 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Debug|x86.Build.0 = Debug|Win32 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Release|x64.ActiveCfg = Release|x64 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Release|x64.Build.0 = Release|x64 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Release|x86.ActiveCfg = Release|Win32 + {05D4BCD1-30A9-4341-BD68-C89506F0AD7F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/JSTest.vcxproj b/JSTest.vcxproj new file mode 100644 index 0000000000..4b5b250ad9 --- /dev/null +++ b/JSTest.vcxproj @@ -0,0 +1,207 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + true + true + true + true + + + + + + + + + true + true + true + true + + + true + true + true + true + + + false + false + false + false + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + 17.0 + Win32Proj + {05d4bcd1-30a9-4341-bd68-c89506f0ad7f} + JSTest + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + TurnOffAllWarnings + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ./ + + + Console + true + + + + + TurnOffAllWarnings + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ./ + + + Console + true + true + true + + + + + TurnOffAllWarnings + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + ./ + + + Console + true + + + + + TurnOffAllWarnings + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ./ + + + Console + true + true + true + + + + + + \ No newline at end of file diff --git a/JSTest.vcxproj.filters b/JSTest.vcxproj.filters new file mode 100644 index 0000000000..38fc34b174 --- /dev/null +++ b/JSTest.vcxproj.filters @@ -0,0 +1,102 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {20602fca-a07b-410d-9e82-527e5997b761} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + + + Header Files + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + quickjs + + + \ No newline at end of file diff --git a/qjs.cpp b/qjs.cpp index 79fd87f8ba..47ccfa5467 100644 --- a/qjs.cpp +++ b/qjs.cpp @@ -13,12 +13,14 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt) ctx = JS_NewContext(rt); if (!ctx) return NULL; +#ifdef CONFIG_BIGNUM if (bignum_ext) { JS_AddIntrinsicBigFloat(ctx); JS_AddIntrinsicBigDecimal(ctx); JS_AddIntrinsicOperators(ctx); JS_EnableBignumExt(ctx, true); } +#endif /* system modules */ js_init_module_std(ctx, "std"); js_init_module_os(ctx, "os"); @@ -34,7 +36,7 @@ int main(int argc, char ** argv) js_std_init_handlers(rt); /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader, js_module_check_attributes, NULL); qjs::Context context(JS_NewCustomContext(rt)); auto ctx = context.ctx; diff --git a/quickjs/CMakeLists.txt b/quickjs/CMakeLists.txt index 2664223d55..b1c18f3fec 100644 --- a/quickjs/CMakeLists.txt +++ b/quickjs/CMakeLists.txt @@ -2,7 +2,7 @@ project(quickjs LANGUAGES C) file(STRINGS VERSION version) -set(quickjs_src quickjs.c libbf.c libunicode.c libregexp.c cutils.c quickjs-libc.c) +set(quickjs_src quickjs.c dtoa.c libunicode.c libregexp.c cutils.c quickjs-libc.c) set(quickjs_def CONFIG_VERSION="${version}" _GNU_SOURCE CONFIG_BIGNUM) add_library(quickjs ${quickjs_src}) diff --git a/quickjs/VERSION b/quickjs/VERSION index 08d122201d..c76e76d1f1 100644 --- a/quickjs/VERSION +++ b/quickjs/VERSION @@ -1 +1 @@ -2023-12-09 +2025-04-26 diff --git a/quickjs/cutils.c b/quickjs/cutils.c index a02fb76886..80099905fb 100644 --- a/quickjs/cutils.c +++ b/quickjs/cutils.c @@ -1,6 +1,6 @@ /* * C utilities - * + * * Copyright (c) 2017 Fabrice Bellard * Copyright (c) 2018 Charlie Gordon * @@ -29,6 +29,28 @@ #include "cutils.h" +#ifdef _MSC_VER + + // From: https://stackoverflow.com/a/26085827 +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + + return 0; +} +#endif void pstrcpy(char *buf, int buf_size, const char *str) { int c; @@ -140,7 +162,7 @@ int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) if (dbuf_realloc(s, s->size + len)) return -1; } - memcpy(s->buf + s->size, data, len); + memcpy_no_ub(s->buf + s->size, data, len); s->size += len; return 0; } @@ -172,10 +194,12 @@ int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, va_list ap; char buf[128]; int len; - + va_start(ap, fmt); len = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); + if (len < 0) + return -1; if (len < sizeof(buf)) { /* fast case */ return dbuf_put(s, (uint8_t *)buf, len); diff --git a/quickjs/cutils.h b/quickjs/cutils.h index 31f7cd84a0..ceb18522ed 100644 --- a/quickjs/cutils.h +++ b/quickjs/cutils.h @@ -1,6 +1,6 @@ /* * C utilities - * + * * Copyright (c) 2017 Fabrice Bellard * Copyright (c) 2018 Charlie Gordon * @@ -26,16 +26,35 @@ #define CUTILS_H #include +#include #include +#ifdef _MSC_VER +#include +#include +#else +#include +#endif + /* 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)) +#ifndef __has_attribute + #define likely(x) (x) + #define unlikely(x) (x) + #define force_inline __forceinline + #define no_inline __declspec(noinline) + #define __maybe_unused + #define __attribute__(x) + #define __attribute(x) + typedef intptr_t ssize_t; +#else + #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)) +#endif #define xglue(x, y) x ## y #define glue(x, y) xglue(x, y) @@ -48,6 +67,16 @@ #ifndef countof #define countof(x) (sizeof(x) / sizeof((x)[0])) #endif +#ifndef container_of +/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ +#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) +#endif + +#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define minimum_length(n) static n +#else +#define minimum_length(n) n +#endif typedef int BOOL; @@ -63,6 +92,12 @@ 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); +/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */ +static inline void memcpy_no_ub(void *dest, const void *src, size_t n) { + if (n) + memcpy(dest, src, n); +} + static inline int max_int(int a, int b) { if (a > b) @@ -114,27 +149,91 @@ static inline int64_t min_int64(int64_t a, int64_t b) /* WARNING: undefined if a = 0 */ static inline int clz32(unsigned int a) { +#ifdef _MSC_VER + unsigned long idx; + _BitScanReverse(&idx, a); + return 31 ^ idx; +#else return __builtin_clz(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int clz64(uint64_t a) { +#ifdef _MSC_VER + unsigned long where; + // BitScanReverse scans from MSB to LSB for first set bit. + // Returns 0 if no set bit is found. +#if INTPTR_MAX >= INT64_MAX // 64-bit + if (_BitScanReverse64(&where, a)) + return (int)(63 - where); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&where, (uint32_t)(a >> 32))) + return (int)(63 - (where + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&where, (uint32_t)(a))) + return (int)(63 - where); +#endif + return 64; // Undefined Behavior. +#else return __builtin_clzll(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int ctz32(unsigned int a) { +#ifdef _MSC_VER + unsigned long idx; + _BitScanForward(&idx, a); + return 31 ^ idx; +#else return __builtin_ctz(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int ctz64(uint64_t a) { +#ifdef _MSC_VER + unsigned long where; + // Search from LSB to MSB for first set bit. + // Returns zero if no set bit is found. +#if INTPTR_MAX >= INT64_MAX // 64-bit + if (_BitScanForward64(&where, a)) + return (int)(where); +#else + // Win32 doesn't have _BitScanForward64 so emulate it with two 32 bit calls. + // Scan the Low Word. + if (_BitScanForward(&where, (uint32_t)(a))) + return (int)(where); + // Scan the High Word. + if (_BitScanForward(&where, (uint32_t)(a >> 32))) + return (int)(where + 32); // Create a bit offset from the LSB. +#endif + return 64; +#else return __builtin_ctzll(a); +#endif } +#ifdef _MSC_VER +#pragma pack(push, 1) +struct packed_u64 { + uint64_t v; +}; + +struct packed_u32 { + uint32_t v; +}; + +struct packed_u16 { + uint16_t v; +}; +#pragma pack(pop) +#else struct __attribute__((packed)) packed_u64 { uint64_t v; }; @@ -146,6 +245,7 @@ struct __attribute__((packed)) packed_u32 { struct __attribute__((packed)) packed_u16 { uint16_t v; }; +#endif static inline uint64_t get_u64(const uint8_t *tab) { @@ -207,28 +307,34 @@ static inline void put_u8(uint8_t *tab, uint8_t val) *tab = val; } +#ifndef bswap16 static inline uint16_t bswap16(uint16_t x) { return (x >> 8) | (x << 8); } +#endif +#ifndef bswap32 static inline uint32_t bswap32(uint32_t v) { return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); } +#endif +#ifndef bswap64 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)) | + 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)); } +#endif /* XXX: should take an extra argument to pass slack information to the caller */ typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); @@ -278,6 +384,36 @@ static inline void dbuf_set_error(DynBuf *s) int unicode_to_utf8(uint8_t *buf, unsigned int c); int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); +static inline BOOL is_surrogate(uint32_t c) +{ + return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF +} + +static inline BOOL is_hi_surrogate(uint32_t c) +{ + return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF +} + +static inline BOOL is_lo_surrogate(uint32_t c) +{ + return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF +} + +static inline uint32_t get_hi_surrogate(uint32_t c) +{ + return (c >> 10) - (0x10000 >> 10) + 0xD800; +} + +static inline uint32_t get_lo_surrogate(uint32_t c) +{ + return (c & 0x3FF) | 0xDC00; +} + +static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo) +{ + return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00); +} + static inline int from_hex(int c) { if (c >= '0' && c <= '9') @@ -294,4 +430,80 @@ void rqsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *, void *), void *arg); +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; +} + +static inline double fromfp16(uint16_t v) +{ + double d; + uint32_t v1; + v1 = v & 0x7fff; + if (unlikely(v1 >= 0x7c00)) + v1 += 0x1f8000; /* NaN or infinity */ + d = uint64_as_float64(((uint64_t)(v >> 15) << 63) | ((uint64_t)v1 << (52 - 10))); + return d * 0x1p1008; +} + +static inline uint16_t tofp16(double d) +{ + uint64_t a, addend; + uint32_t v, sgn; + int shift; + + a = float64_as_uint64(d); + sgn = a >> 63; + a = a & 0x7fffffffffffffff; + if (unlikely(a > 0x7ff0000000000000)) { + /* nan */ + v = 0x7c01; + } else if (a < 0x3f10000000000000) { /* 0x1p-14 */ + /* subnormal f16 number or zero */ + if (a <= 0x3e60000000000000) { /* 0x1p-25 */ + v = 0x0000; /* zero */ + } else { + shift = 1051 - (a >> 52); + a = ((uint64_t)1 << 52) | (a & (((uint64_t)1 << 52) - 1)); + addend = ((a >> shift) & 1) + (((uint64_t)1 << (shift - 1)) - 1); + v = (a + addend) >> shift; + } + } else { + /* normal number or infinity */ + a -= 0x3f00000000000000; /* adjust the exponent */ + /* round */ + addend = ((a >> (52 - 10)) & 1) + (((uint64_t)1 << (52 - 11)) - 1); + v = (a + addend) >> (52 - 10); + /* overflow ? */ + if (unlikely(v > 0x7c00)) + v = 0x7c00; + } + return v | (sgn << 15); +} + +static inline int isfp16nan(uint16_t v) +{ + return (v & 0x7FFF) > 0x7C00; +} + +static inline int isfp16zero(uint16_t v) +{ + return (v & 0x7FFF) == 0; +} + #endif /* CUTILS_H */ diff --git a/quickjs/dtoa.c b/quickjs/dtoa.c new file mode 100644 index 0000000000..dc139e6b1f --- /dev/null +++ b/quickjs/dtoa.c @@ -0,0 +1,1621 @@ +/* + * Tiny float64 printing and parsing 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "dtoa.h" + +#pragma warning(disable:4146) + +/* + 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> 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 guarranteed 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 guarateed 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--; + } + /* garanteed 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/quickjs/dtoa.h b/quickjs/dtoa.h new file mode 100644 index 0000000000..de76f1a3fe --- /dev/null +++ b/quickjs/dtoa.h @@ -0,0 +1,83 @@ +/* + * Tiny float64 printing and parsing 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. + */ + +//#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/quickjs/libbf.c b/quickjs/libbf.c deleted file mode 100644 index 234b956b4d..0000000000 --- a/quickjs/libbf.c +++ /dev/null @@ -1,8473 +0,0 @@ -/* - * Tiny arbitrary precision floating point library - * - * Copyright (c) 2017-2021 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 -#include -#include -#include -#include -#include - -#ifdef __AVX2__ -#include -#endif - -#include "cutils.h" -#include "libbf.h" - -/* enable it to check the multiplication result */ -//#define USE_MUL_CHECK -#ifdef CONFIG_BIGNUM -/* enable it to use FFT/NTT multiplication */ -#define USE_FFT_MUL -/* enable decimal floating point support */ -#define USE_BF_DEC -#endif - -//#define inline __attribute__((always_inline)) - -#ifdef __AVX2__ -#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ -#else -#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ -#endif - -/* XXX: adjust */ -#define DIVNORM_LARGE_THRESHOLD 50 -#define UDIV1NORM_THRESHOLD 3 - -#if LIMB_BITS == 64 -#define FMT_LIMB1 "%" PRIx64 -#define FMT_LIMB "%016" PRIx64 -#define PRId_LIMB PRId64 -#define PRIu_LIMB PRIu64 - -#else - -#define FMT_LIMB1 "%x" -#define FMT_LIMB "%08x" -#define PRId_LIMB "d" -#define PRIu_LIMB "u" - -#endif - -typedef intptr_t mp_size_t; - -typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); - -#ifdef USE_FFT_MUL - -#define FFT_MUL_R_OVERLAP_A (1 << 0) -#define FFT_MUL_R_OVERLAP_B (1 << 1) -#define FFT_MUL_R_NORESIZE (1 << 2) - -static no_inline int fft_mul(bf_context_t *s, - bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags); -static void fft_clear_cache(bf_context_t *s); -#endif -#ifdef USE_BF_DEC -static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); -#endif - - -/* could leading zeros */ -static inline int clz(limb_t a) -{ - if (a == 0) { - return LIMB_BITS; - } else { -#if LIMB_BITS == 64 - return clz64(a); -#else - return clz32(a); -#endif - } -} - -static inline int ctz(limb_t a) -{ - if (a == 0) { - return LIMB_BITS; - } else { -#if LIMB_BITS == 64 - return ctz64(a); -#else - return ctz32(a); -#endif - } -} - -static inline int ceil_log2(limb_t a) -{ - if (a <= 1) - return 0; - else - return LIMB_BITS - clz(a - 1); -} - -/* b must be >= 1 */ -static inline slimb_t ceil_div(slimb_t a, slimb_t b) -{ - if (a >= 0) - return (a + b - 1) / b; - else - return a / b; -} - -/* b must be >= 1 */ -static inline slimb_t floor_div(slimb_t a, slimb_t b) -{ - if (a >= 0) { - return a / b; - } else { - return (a - b + 1) / b; - } -} - -/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ -static inline limb_t smod(slimb_t a, slimb_t b) -{ - a = a % (slimb_t)b; - if (a < 0) - a += b; - return a; -} - -/* signed addition with saturation */ -static inline slimb_t sat_add(slimb_t a, slimb_t b) -{ - slimb_t r; - r = a + b; - /* overflow ? */ - if (((a ^ r) & (b ^ r)) < 0) - r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); - return r; -} - -static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) -{ - if (shift != 0) - low = (low >> shift) | (high << (LIMB_BITS - shift)); - return low; -} - -static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) -{ - if (shift != 0) - return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); - else - return a1; -} - -#define malloc(s) malloc_is_forbidden(s) -#define free(p) free_is_forbidden(p) -#define realloc(p, s) realloc_is_forbidden(p, s) - -void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, - void *realloc_opaque) -{ - memset(s, 0, sizeof(*s)); - s->realloc_func = realloc_func; - s->realloc_opaque = realloc_opaque; -} - -void bf_context_end(bf_context_t *s) -{ - bf_clear_cache(s); -} - -void bf_init(bf_context_t *s, bf_t *r) -{ - r->ctx = s; - r->sign = 0; - r->expn = BF_EXP_ZERO; - r->len = 0; - r->tab = NULL; -} - -/* return 0 if OK, -1 if alloc error */ -int bf_resize(bf_t *r, limb_t len) -{ - limb_t *tab; - - if (len != r->len) { - tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); - if (!tab && len != 0) - return -1; - r->tab = tab; - r->len = len; - } - return 0; -} - -/* return 0 or BF_ST_MEM_ERROR */ -int bf_set_ui(bf_t *r, uint64_t a) -{ - r->sign = 0; - if (a == 0) { - r->expn = BF_EXP_ZERO; - bf_resize(r, 0); /* cannot fail */ - } -#if LIMB_BITS == 32 - else if (a <= 0xffffffff) -#else - else -#endif - { - int shift; - if (bf_resize(r, 1)) - goto fail; - shift = clz(a); - r->tab[0] = a << shift; - r->expn = LIMB_BITS - shift; - } -#if LIMB_BITS == 32 - else { - uint32_t a1, a0; - int shift; - if (bf_resize(r, 2)) - goto fail; - a0 = a; - a1 = a >> 32; - shift = clz(a1); - r->tab[0] = a0 << shift; - r->tab[1] = shld(a1, a0, shift); - r->expn = 2 * LIMB_BITS - shift; - } -#endif - return 0; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* return 0 or BF_ST_MEM_ERROR */ -int bf_set_si(bf_t *r, int64_t a) -{ - int ret; - - if (a < 0) { - ret = bf_set_ui(r, -a); - r->sign = 1; - } else { - ret = bf_set_ui(r, a); - } - return ret; -} - -void bf_set_nan(bf_t *r) -{ - bf_resize(r, 0); /* cannot fail */ - r->expn = BF_EXP_NAN; - r->sign = 0; -} - -void bf_set_zero(bf_t *r, int is_neg) -{ - bf_resize(r, 0); /* cannot fail */ - r->expn = BF_EXP_ZERO; - r->sign = is_neg; -} - -void bf_set_inf(bf_t *r, int is_neg) -{ - bf_resize(r, 0); /* cannot fail */ - r->expn = BF_EXP_INF; - r->sign = is_neg; -} - -/* return 0 or BF_ST_MEM_ERROR */ -int bf_set(bf_t *r, const bf_t *a) -{ - if (r == a) - return 0; - if (bf_resize(r, a->len)) { - bf_set_nan(r); - return BF_ST_MEM_ERROR; - } - r->sign = a->sign; - r->expn = a->expn; - memcpy(r->tab, a->tab, a->len * sizeof(limb_t)); - return 0; -} - -/* equivalent to bf_set(r, a); bf_delete(a) */ -void bf_move(bf_t *r, bf_t *a) -{ - bf_context_t *s = r->ctx; - if (r == a) - return; - bf_free(s, r->tab); - *r = *a; -} - -static limb_t get_limbz(const bf_t *a, limb_t idx) -{ - if (idx >= a->len) - return 0; - else - return a->tab[idx]; -} - -/* get LIMB_BITS at bit position 'pos' in tab */ -static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) -{ - limb_t i, a0, a1; - int p; - - i = pos >> LIMB_LOG2_BITS; - p = pos & (LIMB_BITS - 1); - if (i < len) - a0 = tab[i]; - else - a0 = 0; - if (p == 0) { - return a0; - } else { - i++; - if (i < len) - a1 = tab[i]; - else - a1 = 0; - return (a0 >> p) | (a1 << (LIMB_BITS - p)); - } -} - -static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) -{ - slimb_t i; - i = pos >> LIMB_LOG2_BITS; - if (i < 0 || i >= len) - return 0; - return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; -} - -static inline limb_t limb_mask(int start, int last) -{ - limb_t v; - int n; - n = last - start + 1; - if (n == LIMB_BITS) - v = -1; - else - v = (((limb_t)1 << n) - 1) << start; - return v; -} - -static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) -{ - mp_size_t i; - for(i = 0; i < n; i++) { - if (tab[i] != 0) - return 1; - } - return 0; -} - -/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ -static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) -{ - slimb_t pos; - limb_t v; - - pos = bit_pos >> LIMB_LOG2_BITS; - if (pos < 0) - return 0; - v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); - if (v != 0) - return 1; - pos--; - while (pos >= 0) { - if (r->tab[pos] != 0) - return 1; - pos--; - } - return 0; -} - -/* return the addend for rounding. Note that prec can be <= 0 (for - BF_FLAG_RADPNT_PREC) */ -static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, - slimb_t prec, int rnd_mode) -{ - int add_one, inexact; - limb_t bit1, bit0; - - if (rnd_mode == BF_RNDF) { - bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ - } else { - /* starting limb for bit 'prec + 1' */ - bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); - } - - /* get the bit at 'prec' */ - bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); - inexact = (bit1 | bit0) != 0; - - add_one = 0; - switch(rnd_mode) { - case BF_RNDZ: - break; - case BF_RNDN: - if (bit1) { - if (bit0) { - add_one = 1; - } else { - /* round to even */ - add_one = - get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); - } - } - break; - case BF_RNDD: - case BF_RNDU: - if (r->sign == (rnd_mode == BF_RNDD)) - add_one = inexact; - break; - case BF_RNDA: - add_one = inexact; - break; - case BF_RNDNA: - case BF_RNDF: - add_one = bit1; - break; - default: - abort(); - } - - if (inexact) - *pret |= BF_ST_INEXACT; - return add_one; -} - -static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) -{ - slimb_t i, l, e_max; - int rnd_mode; - - rnd_mode = flags & BF_RND_MASK; - if (prec == BF_PREC_INF || - rnd_mode == BF_RNDN || - rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDA || - (rnd_mode == BF_RNDD && sign == 1) || - (rnd_mode == BF_RNDU && sign == 0)) { - bf_set_inf(r, sign); - } else { - /* set to maximum finite number */ - l = (prec + LIMB_BITS - 1) / LIMB_BITS; - if (bf_resize(r, l)) { - bf_set_nan(r); - return BF_ST_MEM_ERROR; - } - r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1), - LIMB_BITS - 1); - for(i = 1; i < l; i++) - r->tab[i] = (limb_t)-1; - e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - r->expn = e_max; - r->sign = sign; - } - return BF_ST_OVERFLOW | BF_ST_INEXACT; -} - -/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be - infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result - is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of - overflow not returning infinity. */ -static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, - int ret) -{ - limb_t v, a; - int shift, add_one, rnd_mode; - slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - - /* e_min and e_max are computed to match the IEEE 754 conventions */ - e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_range + 3; - e_max = e_range; - - if (flags & BF_FLAG_RADPNT_PREC) { - /* 'prec' is the precision after the radix point */ - if (prec1 != BF_PREC_INF) - prec = r->expn + prec1; - else - prec = prec1; - } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { - /* restrict the precision in case of potentially subnormal - result */ - assert(prec1 != BF_PREC_INF); - prec = prec1 - (e_min - r->expn); - } else { - prec = prec1; - } - - /* round to prec bits */ - rnd_mode = flags & BF_RND_MASK; - add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); - - if (prec <= 0) { - if (add_one) { - bf_resize(r, 1); /* cannot fail */ - r->tab[0] = (limb_t)1 << (LIMB_BITS - 1); - r->expn += 1 - prec; - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } else { - goto underflow; - } - } else if (add_one) { - limb_t carry; - - /* add one starting at digit 'prec - 1' */ - bit_pos = l * LIMB_BITS - 1 - (prec - 1); - pos = bit_pos >> LIMB_LOG2_BITS; - carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1)); - - for(i = pos; i < l; i++) { - v = r->tab[i] + carry; - carry = (v < carry); - r->tab[i] = v; - if (carry == 0) - break; - } - if (carry) { - /* shift right by one digit */ - v = 1; - for(i = l - 1; i >= pos; i--) { - a = r->tab[i]; - r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1)); - v = a; - } - r->expn++; - } - } - - /* check underflow */ - if (unlikely(r->expn < e_min)) { - if (flags & BF_FLAG_SUBNORMAL) { - /* if inexact, also set the underflow flag */ - if (ret & BF_ST_INEXACT) - ret |= BF_ST_UNDERFLOW; - } else { - underflow: - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - bf_set_zero(r, r->sign); - return ret; - } - } - - /* check overflow */ - if (unlikely(r->expn > e_max)) - return bf_set_overflow(r, r->sign, prec1, flags); - - /* keep the bits starting at 'prec - 1' */ - bit_pos = l * LIMB_BITS - 1 - (prec - 1); - i = bit_pos >> LIMB_LOG2_BITS; - if (i >= 0) { - shift = bit_pos & (LIMB_BITS - 1); - if (shift != 0) - r->tab[i] &= limb_mask(shift, LIMB_BITS - 1); - } else { - i = 0; - } - /* remove trailing zeros */ - while (r->tab[i] == 0) - i++; - if (i > 0) { - l -= i; - memmove(r->tab, r->tab + i, l * sizeof(limb_t)); - } - bf_resize(r, l); /* cannot fail */ - return ret; -} - -/* 'r' must be a finite number. */ -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) -{ - limb_t l, v, a; - int shift, ret; - slimb_t i; - - // bf_print_str("bf_renorm", r); - l = r->len; - while (l > 0 && r->tab[l - 1] == 0) - l--; - if (l == 0) { - /* zero */ - r->expn = BF_EXP_ZERO; - bf_resize(r, 0); /* cannot fail */ - ret = 0; - } else { - r->expn -= (r->len - l) * LIMB_BITS; - /* shift to have the MSB set to '1' */ - v = r->tab[l - 1]; - shift = clz(v); - if (shift != 0) { - v = 0; - for(i = 0; i < l; i++) { - a = r->tab[i]; - r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift)); - v = a; - } - r->expn -= shift; - } - ret = __bf_round(r, prec1, flags, l, 0); - } - // bf_print_str("r_final", r); - return ret; -} - -/* return true if rounding can be done at precision 'prec' assuming - the exact result r is such that |r-a| <= 2^(EXP(a)-k). */ -/* XXX: check the case where the exponent would be incremented by the - rounding */ -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) -{ - BOOL is_rndn; - slimb_t bit_pos, n; - limb_t bit; - - if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN) - return FALSE; - if (rnd_mode == BF_RNDF) { - return (k >= (prec + 1)); - } - if (a->expn == BF_EXP_ZERO) - return FALSE; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - if (k < (prec + 2)) - return FALSE; - bit_pos = a->len * LIMB_BITS - 1 - prec; - n = k - prec; - /* bit pattern for RNDN or RNDNA: 0111.. or 1000... - for other rounding modes: 000... or 111... - */ - bit = get_bit(a->tab, a->len, bit_pos); - bit_pos--; - n--; - bit ^= is_rndn; - /* XXX: slow, but a few iterations on average */ - while (n != 0) { - if (get_bit(a->tab, a->len, bit_pos) != bit) - return TRUE; - bit_pos--; - n--; - } - return FALSE; -} - -/* Cannot fail with BF_ST_MEM_ERROR. */ -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) -{ - if (r->len == 0) - return 0; - return __bf_round(r, prec, flags, r->len, 0); -} - -/* for debugging */ -static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n) -{ - limb_t i; - printf("%s: len=%" PRId_LIMB "\n", str, n); - for(i = 0; i < n; i++) { - printf("%" PRId_LIMB ": " FMT_LIMB "\n", - i, tab[i]); - } -} - -void mp_print_str(const char *str, const limb_t *tab, limb_t n) -{ - slimb_t i; - printf("%s= 0x", str); - for(i = n - 1; i >= 0; i--) { - if (i != (n - 1)) - printf("_"); - printf(FMT_LIMB, tab[i]); - } - printf("\n"); -} - -static __maybe_unused void mp_print_str_h(const char *str, - const limb_t *tab, limb_t n, - limb_t high) -{ - slimb_t i; - printf("%s= 0x", str); - printf(FMT_LIMB, high); - for(i = n - 1; i >= 0; i--) { - printf("_"); - printf(FMT_LIMB, tab[i]); - } - printf("\n"); -} - -/* for debugging */ -void bf_print_str(const char *str, const bf_t *a) -{ - slimb_t i; - printf("%s=", str); - - if (a->expn == BF_EXP_NAN) { - printf("NaN"); - } else { - if (a->sign) - putchar('-'); - if (a->expn == BF_EXP_ZERO) { - putchar('0'); - } else if (a->expn == BF_EXP_INF) { - printf("Inf"); - } else { - printf("0x0."); - for(i = a->len - 1; i >= 0; i--) - printf(FMT_LIMB, a->tab[i]); - printf("p%" PRId_LIMB, a->expn); - } - } - printf("\n"); -} - -/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0 - if a = b and > 0 otherwise. */ -int bf_cmpu(const bf_t *a, const bf_t *b) -{ - slimb_t i; - limb_t len, v1, v2; - - if (a->expn != b->expn) { - if (a->expn < b->expn) - return -1; - else - return 1; - } - len = bf_max(a->len, b->len); - for(i = len - 1; i >= 0; i--) { - v1 = get_limbz(a, a->len - len + i); - v2 = get_limbz(b, b->len - len + i); - if (v1 != v2) { - if (v1 < v2) - return -1; - else - return 1; - } - } - return 0; -} - -/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */ -int bf_cmp_full(const bf_t *a, const bf_t *b) -{ - int res; - - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - if (a->expn == b->expn) - res = 0; - else if (a->expn == BF_EXP_NAN) - res = 1; - else - res = -1; - } else if (a->sign != b->sign) { - res = 1 - 2 * a->sign; - } else { - res = bf_cmpu(a, b); - if (a->sign) - res = -res; - } - return res; -} - -/* Standard floating point comparison: return 2 if one of the operands - is NaN (unordered) or -1, 0, 1 depending on the ordering assuming - -0 == +0 */ -int bf_cmp(const bf_t *a, const bf_t *b) -{ - int res; - - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - res = 2; - } else if (a->sign != b->sign) { - if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) - res = 0; - else - res = 1 - 2 * a->sign; - } else { - res = bf_cmpu(a, b); - if (a->sign) - res = -res; - } - return res; -} - -/* Compute the number of bits 'n' matching the pattern: - a= X1000..0 - b= X0111..1 - - When computing a-b, the result will have at least n leading zero - bits. - - Precondition: a > b and a.expn - b.expn = 0 or 1 -*/ -static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) -{ - slimb_t bit_offset, b_offset, n; - int p, p1; - limb_t v1, v2, mask; - - bit_offset = a->len * LIMB_BITS - 1; - b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + - a->expn - b->expn; - n = 0; - - /* first search the equals bits */ - for(;;) { - v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); - v2 = get_bits(b->tab, b->len, bit_offset + b_offset); - // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); - if (v1 != v2) - break; - n += LIMB_BITS; - bit_offset -= LIMB_BITS; - } - /* find the position of the first different bit */ - p = clz(v1 ^ v2) + 1; - n += p; - /* then search for '0' in a and '1' in b */ - p = LIMB_BITS - p; - if (p > 0) { - /* search in the trailing p bits of v1 and v2 */ - mask = limb_mask(0, p - 1); - p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); - n += p1; - if (p1 != p) - goto done; - } - bit_offset -= LIMB_BITS; - for(;;) { - v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); - v2 = get_bits(b->tab, b->len, bit_offset + b_offset); - // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); - if (v1 != 0 || v2 != -1) { - /* different: count the matching bits */ - p1 = bf_min(clz(v1), clz(~v2)); - n += p1; - break; - } - n += LIMB_BITS; - bit_offset -= LIMB_BITS; - } - done: - return n; -} - -static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int b_neg) -{ - const bf_t *tmp; - int is_sub, ret, cmp_res, a_sign, b_sign; - - a_sign = a->sign; - b_sign = b->sign ^ b_neg; - is_sub = a_sign ^ b_sign; - cmp_res = bf_cmpu(a, b); - if (cmp_res < 0) { - tmp = a; - a = b; - b = tmp; - a_sign = b_sign; /* b_sign is never used later */ - } - /* abs(a) >= abs(b) */ - if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { - /* zero result */ - bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); - ret = 0; - } else if (a->len == 0 || b->len == 0) { - ret = 0; - if (a->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN) { - /* at least one operand is NaN */ - bf_set_nan(r); - } else if (b->expn == BF_EXP_INF && is_sub) { - /* infinities with different signs */ - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bf_set_inf(r, a_sign); - } - } else { - /* at least one zero and not subtract */ - bf_set(r, a); - r->sign = a_sign; - goto renorm; - } - } else { - slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; - limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; - - r->sign = a_sign; - r->expn = a->expn; - d = a->expn - b->expn; - /* must add more precision for the leading cancelled bits in - subtraction */ - if (is_sub) { - if (d <= 1) - cancelled_bits = count_cancelled_bits(a, b); - else - cancelled_bits = 1; - } else { - cancelled_bits = 0; - } - - /* add two extra bits for rounding */ - precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); - r_len = bf_min(precl, tot_len); - if (bf_resize(r, r_len)) - goto fail; - a_offset = a->len - r_len; - b_bit_offset = (b->len - r_len) * LIMB_BITS + d; - - /* compute the bits before for the rounding */ - carry = is_sub; - z = 0; - sub_mask = -is_sub; - i = r_len - tot_len; - while (i < 0) { - slimb_t ap, bp; - BOOL inflag; - - ap = a_offset + i; - bp = b_bit_offset + i * LIMB_BITS; - inflag = FALSE; - if (ap >= 0 && ap < a->len) { - v1 = a->tab[ap]; - inflag = TRUE; - } else { - v1 = 0; - } - if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { - v2 = get_bits(b->tab, b->len, bp); - inflag = TRUE; - } else { - v2 = 0; - } - if (!inflag) { - /* outside 'a' and 'b': go directly to the next value - inside a or b so that the running time does not - depend on the exponent difference */ - i = 0; - if (ap < 0) - i = bf_min(i, -a_offset); - /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 - equivalent to - i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) - */ - if (bp + LIMB_BITS <= 0) - i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); - } else { - i++; - } - v2 ^= sub_mask; - u = v1 + v2; - carry1 = u < v1; - u += carry; - carry = (u < carry) | carry1; - z |= u; - } - /* and the result */ - for(i = 0; i < r_len; i++) { - v1 = get_limbz(a, a_offset + i); - v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); - v2 ^= sub_mask; - u = v1 + v2; - carry1 = u < v1; - u += carry; - carry = (u < carry) | carry1; - r->tab[i] = u; - } - /* set the extra bits for the rounding */ - r->tab[0] |= (z != 0); - - /* carry is only possible in add case */ - if (!is_sub && carry) { - if (bf_resize(r, r_len + 1)) - goto fail; - r->tab[r_len] = 1; - r->expn += LIMB_BITS; - } - renorm: - ret = bf_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_add_internal(r, a, b, prec, flags, 0); -} - -static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_add_internal(r, a, b, prec, flags, 1); -} - -limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, - limb_t n, limb_t carry) -{ - slimb_t i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i v; - v = a - k; - k = (v > a) | k1; - res[i] = v; - } - return k; -} - -/* compute 0 - op2 */ -static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) -{ - int i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i v; - v = a - k; - k = (v > a) | k1; - res[i] = v; - } - return k; -} - -limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) -{ - mp_size_t i; - limb_t k, a, v; - - k=b; - for(i=0;i v; - tab[i] = a; - if (k == 0) - break; - } - return k; -} - -/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). - 1 <= shift <= LIMB_BITS - 1 */ -static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t high) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); - l = a; - } - return l & (((limb_t)1 << shift) - 1); -} - -/* tabr[] = taba[] * b + l. Return the high carry */ -static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t l) -{ - limb_t i; - dlimb_t t; - - for(i = 0; i < n; i++) { - t = (dlimb_t)taba[i] * (dlimb_t)b + l; - tabr[i] = t; - l = t >> LIMB_BITS; - } - return l; -} - -/* tabr[] += taba[] * b, return the high word. */ -static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b) -{ - limb_t i, l; - dlimb_t t; - - l = 0; - for(i = 0; i < n; i++) { - t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; - tabr[i] = t; - l = t >> LIMB_BITS; - } - return l; -} - -/* size of the result : op1_size + op2_size. */ -static void mp_mul_basecase(limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size) -{ - limb_t i, r; - - result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); - for(i=1;i= FFT_MUL_THRESHOLD)) { - bf_t r_s, *r = &r_s; - r->tab = result; - /* XXX: optimize memory usage in API */ - if (fft_mul(s, r, (limb_t *)op1, op1_size, - (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) - return -1; - } else -#endif - { - mp_mul_basecase(result, op1, op1_size, op2, op2_size); - } - return 0; -} - -/* tabr[] -= taba[] * b. Return the value to substract to the high - word. */ -static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b) -{ - limb_t i, l; - dlimb_t t; - - l = 0; - for(i = 0; i < n; i++) { - t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; - tabr[i] = t; - l = -(t >> LIMB_BITS); - } - return l; -} - -/* WARNING: d must be >= 2^(LIMB_BITS-1) */ -static inline limb_t udiv1norm_init(limb_t d) -{ - limb_t a0, a1; - a1 = -d - 1; - a0 = -1; - return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; -} - -/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 - / d' with 0 <= a1 < d. */ -static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, - limb_t d, limb_t d_inv) -{ - limb_t n1m, n_adj, q, r, ah; - dlimb_t a; - n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); - n_adj = a0 + (n1m & d); - a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; - q = (a >> LIMB_BITS) + a1; - /* compute a - q * r and update q so that the remainder is\ - between 0 and d - 1 */ - a = ((dlimb_t)a1 << LIMB_BITS) | a0; - a = a - (dlimb_t)q * d - d; - ah = a >> LIMB_BITS; - q += 1 + ah; - r = (limb_t)a + (ah & d); - *pr = r; - return q; -} - -/* b must be >= 1 << (LIMB_BITS - 1) */ -static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r) -{ - slimb_t i; - - if (n >= UDIV1NORM_THRESHOLD) { - limb_t b_inv; - b_inv = udiv1norm_init(b); - for(i = n - 1; i >= 0; i--) { - tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); - } - } else { - dlimb_t a1; - for(i = n - 1; i >= 0; i--) { - a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; - tabr[i] = a1 / b; - r = a1 % b; - } - } - return r; -} - -static int mp_divnorm_large(bf_context_t *s, - limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb); - -/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb - - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' - is modified and contains the remainder (nb limbs). tabq[0..na-nb] - contains the quotient with tabq[na - nb] <= 1. */ -static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb) -{ - limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; - slimb_t i, j; - - b1 = tabb[nb - 1]; - if (nb == 1) { - taba[0] = mp_div1norm(tabq, taba, na, b1, 0); - return 0; - } - n = na - nb; - if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { - return mp_divnorm_large(s, tabq, taba, na, tabb, nb); - } - - if (n >= UDIV1NORM_THRESHOLD) - b1_inv = udiv1norm_init(b1); - else - b1_inv = 0; - - /* first iteration: the quotient is only 0 or 1 */ - q = 1; - for(j = nb - 1; j >= 0; j--) { - if (taba[n + j] != tabb[j]) { - if (taba[n + j] < tabb[j]) - q = 0; - break; - } - } - tabq[n] = q; - if (q) { - mp_sub(taba + n, taba + n, tabb, nb, 0); - } - - for(i = n - 1; i >= 0; i--) { - if (unlikely(taba[i + nb] >= b1)) { - q = -1; - } else if (b1_inv) { - q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); - } else { - dlimb_t al; - al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; - q = al / b1; - r = al % b1; - } - r = mp_sub_mul1(taba + i, tabb, nb, q); - - v = taba[i + nb]; - a = v - r; - c = (a > v); - taba[i + nb] = a; - - if (c != 0) { - /* negative result */ - for(;;) { - q--; - c = mp_add(taba + i, taba + i, tabb, nb, 0); - /* propagate carry and test if positive result */ - if (c != 0) { - if (++taba[i + nb] == 0) { - break; - } - } - } - } - tabq[i] = q; - } - return 0; -} - -/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' - has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. - - See Modern Computer Arithmetic by Richard P. Brent and Paul - Zimmermann, algorithm 3.5 */ -int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) -{ - mp_size_t l, h, k, i; - limb_t *tabxh, *tabt, c, *tabu; - - if (n <= 2) { - /* return ceil(B^(2*n)/a) - 1 */ - /* XXX: could avoid allocation */ - tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); - tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); - if (!tabt || !tabu) - goto fail; - for(i = 0; i < 2 * n; i++) - tabu[i] = 0; - tabu[2 * n] = 1; - if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) - goto fail; - for(i = 0; i < n + 1; i++) - tabr[i] = tabt[i]; - if (mp_scan_nz(tabu, n) == 0) { - /* only happens for a=B^n/2 */ - mp_sub_ui(tabr, 1, n + 1); - } - } else { - l = (n - 1) / 2; - h = n - l; - /* n=2p -> l=p-1, h = p + 1, k = p + 3 - n=2p+1-> l=p, h = p + 1; k = p + 2 - */ - tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); - tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); - if (!tabt || !tabu) - goto fail; - tabxh = tabr + l; - if (mp_recip(s, tabxh, taba + l, h)) - goto fail; - if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ - goto fail; - while (tabt[n + h] != 0) { - mp_sub_ui(tabxh, 1, h + 1); - c = mp_sub(tabt, tabt, taba, n, 0); - mp_sub_ui(tabt + n, c, h + 1); - } - /* T = B^(n+h) - T */ - mp_neg(tabt, tabt, n + h + 1, 0); - tabt[n + h]++; - if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) - goto fail; - /* n + 2*h - l + 2 limbs */ - k = 2 * h - l; - for(i = 0; i < l; i++) - tabr[i] = tabu[i + k]; - mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); - } - bf_free(s, tabt); - bf_free(s, tabu); - return 0; - fail: - bf_free(s, tabt); - bf_free(s, tabu); - return -1; -} - -/* return -1, 0 or 1 */ -static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) -{ - mp_size_t i; - for(i = n - 1; i >= 0; i--) { - if (taba[i] != tabb[i]) { - if (taba[i] < tabb[i]) - return -1; - else - return 1; - } - } - return 0; -} - -//#define DEBUG_DIVNORM_LARGE -//#define DEBUG_DIVNORM_LARGE2 - -/* subquadratic divnorm */ -static int mp_divnorm_large(bf_context_t *s, - limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb) -{ - limb_t *tabb_inv, nq, *tabt, i, n; - nq = na - nb; -#ifdef DEBUG_DIVNORM_LARGE - printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); - mp_print_str("a", taba, na); - mp_print_str("b", tabb, nb); -#endif - assert(nq >= 1); - n = nq; - if (nq < nb) - n++; - tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); - tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); - if (!tabb_inv || !tabt) - goto fail; - - if (n >= nb) { - for(i = 0; i < n - nb; i++) - tabt[i] = 0; - for(i = 0; i < nb; i++) - tabt[i + n - nb] = tabb[i]; - } else { - /* truncate B: need to increment it so that the approximate - inverse is smaller that the exact inverse */ - for(i = 0; i < n; i++) - tabt[i] = tabb[i + nb - n]; - if (mp_add_ui(tabt, 1, n)) { - /* tabt = B^n : tabb_inv = B^n */ - memset(tabb_inv, 0, n * sizeof(limb_t)); - tabb_inv[n] = 1; - goto recip_done; - } - } - if (mp_recip(s, tabb_inv, tabt, n)) - goto fail; - recip_done: - /* Q=A*B^-1 */ - if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) - goto fail; - - for(i = 0; i < nq + 1; i++) - tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; -#ifdef DEBUG_DIVNORM_LARGE - mp_print_str("q", tabq, nq + 1); -#endif - - bf_free(s, tabt); - bf_free(s, tabb_inv); - tabb_inv = NULL; - - /* R=A-B*Q */ - tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); - if (!tabt) - goto fail; - if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) - goto fail; - /* we add one more limb for the result */ - mp_sub(taba, taba, tabt, nb + 1, 0); - bf_free(s, tabt); - /* the approximated quotient is smaller than than the exact one, - hence we may have to increment it */ -#ifdef DEBUG_DIVNORM_LARGE2 - int cnt = 0; - static int cnt_max; -#endif - for(;;) { - if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) - break; - taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); - mp_add_ui(tabq, 1, nq + 1); -#ifdef DEBUG_DIVNORM_LARGE2 - cnt++; -#endif - } -#ifdef DEBUG_DIVNORM_LARGE2 - if (cnt > cnt_max) { - cnt_max = cnt; - printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); - } -#endif - return 0; - fail: - bf_free(s, tabb_inv); - bf_free(s, tabt); - return -1; -} - -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - int ret, r_sign; - - if (a->len < b->len) { - const bf_t *tmp = a; - a = b; - b = tmp; - } - r_sign = a->sign ^ b->sign; - /* here b->len <= a->len */ - if (b->len == 0) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - ret = 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { - if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || - (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bf_set_inf(r, r_sign); - ret = 0; - } - } else { - bf_set_zero(r, r_sign); - ret = 0; - } - } else { - bf_t tmp, *r1 = NULL; - limb_t a_len, b_len, precl; - limb_t *a_tab, *b_tab; - - a_len = a->len; - b_len = b->len; - - if ((flags & BF_RND_MASK) == BF_RNDF) { - /* faithful rounding does not require using the full inputs */ - precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - a_len = bf_min(a_len, precl); - b_len = bf_min(b_len, precl); - } - a_tab = a->tab + a->len - a_len; - b_tab = b->tab + b->len - b_len; - -#ifdef USE_FFT_MUL - if (b_len >= FFT_MUL_THRESHOLD) { - int mul_flags = 0; - if (r == a) - mul_flags |= FFT_MUL_R_OVERLAP_A; - if (r == b) - mul_flags |= FFT_MUL_R_OVERLAP_B; - if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) - goto fail; - } else -#endif - { - if (r == a || r == b) { - bf_init(r->ctx, &tmp); - r1 = r; - r = &tmp; - } - if (bf_resize(r, a_len + b_len)) { -#ifdef USE_FFT_MUL - fail: -#endif - bf_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; - } - mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len); - } - r->sign = r_sign; - r->expn = a->expn + b->expn; - ret = bf_normalize_and_round(r, prec, flags); - done: - if (r == &tmp) - bf_move(r1, &tmp); - } - return ret; -} - -/* multiply 'r' by 2^e */ -int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) -{ - slimb_t e_max; - if (r->len == 0) - return 0; - e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; - e = bf_max(e, -e_max); - e = bf_min(e, e_max); - r->expn += e; - return __bf_round(r, prec, flags, r->len, 0); -} - -/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, - Infinite or Nan. */ -slimb_t bf_get_exp_min(const bf_t *a) -{ - slimb_t i; - limb_t v; - int k; - - for(i = 0; i < a->len; i++) { - v = a->tab[i]; - if (v != 0) { - k = ctz(v); - return a->expn - (a->len - i) * LIMB_BITS + k; - } - } - return 0; -} - -/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the - integer defined as floor(a/b) and r = a - q * b. */ -static void bf_tdivremu(bf_t *q, bf_t *r, - const bf_t *a, const bf_t *b) -{ - if (bf_cmpu(a, b) < 0) { - bf_set_ui(q, 0); - bf_set(r, a); - } else { - bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ); - bf_rint(q, BF_RNDZ); - bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ); - bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ); - } -} - -static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - int ret, r_sign; - limb_t n, nb, precl; - - r_sign = a->sign ^ b->sign; - if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else if (a->expn == BF_EXP_INF) { - bf_set_inf(r, r_sign); - return 0; - } else { - bf_set_zero(r, r_sign); - return 0; - } - } else if (a->expn == BF_EXP_ZERO) { - if (b->expn == BF_EXP_ZERO) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, r_sign); - return 0; - } - } else if (b->expn == BF_EXP_ZERO) { - bf_set_inf(r, r_sign); - return BF_ST_DIVIDE_ZERO; - } - - /* number of limbs of the quotient (2 extra bits for rounding) */ - precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - nb = b->len; - n = bf_max(a->len, precl); - - { - limb_t *taba, na; - slimb_t d; - - na = n + nb; - taba = bf_malloc(s, (na + 1) * sizeof(limb_t)); - if (!taba) - goto fail; - d = na - a->len; - memset(taba, 0, d * sizeof(limb_t)); - memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); - if (bf_resize(r, n + 1)) - goto fail1; - if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) { - fail1: - bf_free(s, taba); - goto fail; - } - /* see if non zero remainder */ - if (mp_scan_nz(taba, nb)) - r->tab[0] |= 1; - bf_free(r->ctx, taba); - r->expn = a->expn - b->expn + LIMB_BITS; - r->sign = r_sign; - ret = bf_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* division and remainder. - - rnd_mode is the rounding mode for the quotient. The additional - rounding mode BF_RND_EUCLIDIAN is supported. - - 'q' is an integer. 'r' is rounded with prec and flags (prec can be - BF_PREC_INF). -*/ -int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode) -{ - bf_t a1_s, *a1 = &a1_s; - bf_t b1_s, *b1 = &b1_s; - int q_sign, ret; - BOOL is_ceil, is_rndn; - - assert(q != a && q != b); - assert(r != a && r != b); - assert(q != r); - - if (a->len == 0 || b->len == 0) { - bf_set_zero(q, 0); - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set(r, a); - return bf_round(r, prec, flags); - } - } - - q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - switch(rnd_mode) { - default: - case BF_RNDZ: - case BF_RNDN: - case BF_RNDNA: - is_ceil = FALSE; - break; - case BF_RNDD: - is_ceil = q_sign; - break; - case BF_RNDU: - is_ceil = q_sign ^ 1; - break; - case BF_RNDA: - is_ceil = TRUE; - break; - case BF_DIVREM_EUCLIDIAN: - is_ceil = a->sign; - break; - } - - a1->expn = a->expn; - a1->tab = a->tab; - a1->len = a->len; - a1->sign = 0; - - b1->expn = b->expn; - b1->tab = b->tab; - b1->len = b->len; - b1->sign = 0; - - /* XXX: could improve to avoid having a large 'q' */ - bf_tdivremu(q, r, a1, b1); - if (bf_is_nan(q) || bf_is_nan(r)) - goto fail; - - if (r->len != 0) { - if (is_rndn) { - int res; - b1->expn--; - res = bf_cmpu(r, b1); - b1->expn++; - if (res > 0 || - (res == 0 && - (rnd_mode == BF_RNDNA || - get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) { - goto do_sub_r; - } - } else if (is_ceil) { - do_sub_r: - ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); - ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); - if (ret & BF_ST_MEM_ERROR) - goto fail; - } - } - - r->sign ^= a->sign; - q->sign = q_sign; - return bf_round(r, prec, flags); - fail: - bf_set_nan(q); - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bf_t q_s, *q = &q_s; - int ret; - - bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); - bf_delete(q); - return ret; -} - -static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags) -{ -#if LIMB_BITS == 32 - return bf_get_int32(pres, a, flags); -#else - return bf_get_int64(pres, a, flags); -#endif -} - -int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bf_t q_s, *q = &q_s; - int ret; - - bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); - bf_get_limb(pq, q, BF_GET_INT_MOD); - bf_delete(q); - return ret; -} - -static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m) -{ - dlimb_t t; - t = (dlimb_t)a * (dlimb_t)b; - return t % m; -} - -#if defined(USE_MUL_CHECK) -static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) -{ - slimb_t i; - dlimb_t t; - - for(i = n - 1; i >= 0; i--) { - t = ((dlimb_t)r << LIMB_BITS) | tab[i]; - r = t % m; - } - return r; -} -#endif - -static const uint16_t sqrt_table[192] = { -128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255, -}; - -/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and - r=a-s^2. 0 <= r <= 2 * s */ -static limb_t mp_sqrtrem1(limb_t *pr, limb_t a) -{ - limb_t s1, r1, s, r, q, u, num; - - /* use a table for the 16 -> 8 bit sqrt */ - s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64]; - r1 = (a >> (LIMB_BITS - 16)) - s1 * s1; - if (r1 > 2 * s1) { - r1 -= 2 * s1 + 1; - s1++; - } - - /* one iteration to get a 32 -> 16 bit sqrt */ - num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff); - q = num / (2 * s1); /* q <= 2^8 */ - u = num % (2 * s1); - s = (s1 << 8) + q; - r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff); - r -= q * q; - if ((slimb_t)r < 0) { - s--; - r += 2 * s + 1; - } - -#if LIMB_BITS == 64 - s1 = s; - r1 = r; - /* one more iteration for 64 -> 32 bit sqrt */ - num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff); - q = num / (2 * s1); /* q <= 2^16 */ - u = num % (2 * s1); - s = (s1 << 16) + q; - r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff); - r -= q * q; - if ((slimb_t)r < 0) { - s--; - r += 2 * s + 1; - } -#endif - *pr = r; - return s; -} - -/* return floor(sqrt(a)) */ -limb_t bf_isqrt(limb_t a) -{ - limb_t s, r; - int k; - - if (a == 0) - return 0; - k = clz(a) & ~1; - s = mp_sqrtrem1(&r, a << k); - s >>= (k >> 1); - return s; -} - -static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba) -{ - limb_t s1, r1, s, q, u, a0, a1; - dlimb_t r, num; - int l; - - a0 = taba[0]; - a1 = taba[1]; - s1 = mp_sqrtrem1(&r1, a1); - l = LIMB_BITS / 2; - num = ((dlimb_t)r1 << l) | (a0 >> l); - q = num / (2 * s1); - u = num % (2 * s1); - s = (s1 << l) + q; - r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1)); - if (unlikely((q >> l) != 0)) - r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */ - else - r -= q * q; - if ((slimb_t)(r >> LIMB_BITS) < 0) { - s--; - r += 2 * (dlimb_t)s + 1; - } - tabs[0] = s; - taba[0] = r; - return r >> LIMB_BITS; -} - -//#define DEBUG_SQRTREM - -/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest - limb of the remainder. */ -static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n, - limb_t *tmp_buf, limb_t *prh) -{ - limb_t l, h, rh, ql, qh, c, i; - - if (n == 1) { - *prh = mp_sqrtrem2(tabs, taba); - return 0; - } -#ifdef DEBUG_SQRTREM - mp_print_str("a", taba, 2 * n); -#endif - l = n / 2; - h = n - l; - if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh)) - return -1; -#ifdef DEBUG_SQRTREM - mp_print_str("s1", tabs + l, h); - mp_print_str_h("r1", taba + 2 * l, h, qh); - mp_print_str_h("r2", taba + l, n, qh); -#endif - - /* the remainder is in taba + 2 * l. Its high bit is in qh */ - if (qh) { - mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); - } - /* instead of dividing by 2*s, divide by s (which is normalized) - and update q and r */ - if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h)) - return -1; - qh += tmp_buf[l]; - for(i = 0; i < l; i++) - tabs[i] = tmp_buf[i]; - ql = mp_shr(tabs, tabs, l, 1, qh & 1); - qh = qh >> 1; /* 0 or 1 */ - if (ql) - rh = mp_add(taba + l, taba + l, tabs + l, h, 0); - else - rh = 0; -#ifdef DEBUG_SQRTREM - mp_print_str_h("q", tabs, l, qh); - mp_print_str_h("u", taba + l, h, rh); -#endif - - mp_add_ui(tabs + l, qh, h); -#ifdef DEBUG_SQRTREM - mp_print_str_h("s2", tabs, n, sh); -#endif - - /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ - /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ - if (qh) { - c = qh; - } else { - if (mp_mul(s, taba + n, tabs, l, tabs, l)) - return -1; - c = mp_sub(taba, taba, taba + n, 2 * l, 0); - } - rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l); - if ((slimb_t)rh < 0) { - mp_sub_ui(tabs, 1, n); - rh += mp_add_mul1(taba, tabs, n, 2); - rh += mp_add_ui(taba, 1, n); - } - *prh = rh; - return 0; -} - -/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS - - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 - * s. tabs has n limbs. r is returned in the lower n limbs of - taba. Its r[n] is the returned value of the function. */ -/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and - inspirated from its GMP implementation */ -int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) -{ - limb_t tmp_buf1[8]; - limb_t *tmp_buf; - mp_size_t n2; - int ret; - n2 = n / 2 + 1; - if (n2 <= countof(tmp_buf1)) { - tmp_buf = tmp_buf1; - } else { - tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); - if (!tmp_buf) - return -1; - } - ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n); - if (tmp_buf != tmp_buf1) - bf_free(s, tmp_buf); - return ret; -} - -/* Integer square root with remainder. 'a' must be an integer. r = - floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result - is inexact. 'rem' can be NULL if the remainder is not needed. */ -int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) -{ - int ret; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bf_set(r, a); - } - if (rem1) - bf_set_ui(rem1, 0); - ret = 0; - } else if (a->sign) { - invalid_op: - bf_set_nan(r); - if (rem1) - bf_set_ui(rem1, 0); - ret = BF_ST_INVALID_OP; - } else { - bf_t rem_s, *rem; - - bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ); - bf_rint(r, BF_RNDZ); - /* see if the result is exact by computing the remainder */ - if (rem1) { - rem = rem1; - } else { - rem = &rem_s; - bf_init(r->ctx, rem); - } - /* XXX: could avoid recomputing the remainder */ - bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); - bf_neg(rem); - bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); - if (bf_is_nan(rem)) { - ret = BF_ST_MEM_ERROR; - goto done; - } - if (rem->len != 0) { - ret = BF_ST_INEXACT; - } else { - ret = 0; - } - done: - if (!rem1) - bf_delete(rem); - } - return ret; -} - -int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = a->ctx; - int ret; - - assert(r != a); - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bf_set(r, a); - } - ret = 0; - } else if (a->sign) { - invalid_op: - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - limb_t *a1; - slimb_t n, n1; - limb_t res; - - /* convert the mantissa to an integer with at least 2 * - prec + 4 bits */ - n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS); - if (bf_resize(r, n)) - goto fail; - a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); - if (!a1) - goto fail; - n1 = bf_min(2 * n, a->len); - memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); - memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); - if (a->expn & 1) { - res = mp_shr(a1, a1, 2 * n, 1, 0); - } else { - res = 0; - } - if (mp_sqrtrem(s, r->tab, a1, n)) { - bf_free(s, a1); - goto fail; - } - if (!res) { - res = mp_scan_nz(a1, n + 1); - } - bf_free(s, a1); - if (!res) { - res = mp_scan_nz(a->tab, a->len - n1); - } - if (res != 0) - r->tab[0] |= 1; - r->sign = 0; - r->expn = (a->expn + 1) >> 1; - ret = bf_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, bf_op2_func_t *func) -{ - bf_t tmp; - int ret; - - if (r == a || r == b) { - bf_init(r->ctx, &tmp); - ret = func(&tmp, a, b, prec, flags); - bf_move(r, &tmp); - } else { - ret = func(r, a, b, prec, flags); - } - return ret; -} - -int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2(r, a, b, prec, flags, __bf_add); -} - -int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2(r, a, b, prec, flags, __bf_sub); -} - -int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2(r, a, b, prec, flags, __bf_div); -} - -int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, - bf_flags_t flags) -{ - bf_t b; - int ret; - bf_init(r->ctx, &b); - ret = bf_set_ui(&b, b1); - ret |= bf_mul(r, a, &b, prec, flags); - bf_delete(&b); - return ret; -} - -int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bf_t b; - int ret; - bf_init(r->ctx, &b); - ret = bf_set_si(&b, b1); - ret |= bf_mul(r, a, &b, prec, flags); - bf_delete(&b); - return ret; -} - -int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bf_t b; - int ret; - - bf_init(r->ctx, &b); - ret = bf_set_si(&b, b1); - ret |= bf_add(r, a, &b, prec, flags); - bf_delete(&b); - return ret; -} - -static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, - bf_flags_t flags) -{ - int ret, n_bits, i; - - assert(r != a); - if (b == 0) - return bf_set_ui(r, 1); - ret = bf_set(r, a); - n_bits = LIMB_BITS - clz(b); - for(i = n_bits - 2; i >= 0; i--) { - ret |= bf_mul(r, r, r, prec, flags); - if ((b >> i) & 1) - ret |= bf_mul(r, r, a, prec, flags); - } - return ret; -} - -static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, - limb_t prec, bf_flags_t flags) -{ - bf_t a; - int ret; - -#ifdef USE_BF_DEC - if (a1 == 10 && b <= LIMB_DIGITS) { - /* use precomputed powers. We do not round at this point - because we expect the caller to do it */ - ret = bf_set_ui(r, mp_pow_dec[b]); - } else -#endif - { - bf_init(r->ctx, &a); - ret = bf_set_ui(&a, a1); - ret |= bf_pow_ui(r, &a, b, prec, flags); - bf_delete(&a); - } - return ret; -} - -/* convert to integer (infinite precision) */ -int bf_rint(bf_t *r, int rnd_mode) -{ - return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); -} - -/* logical operations */ -#define BF_LOGIC_OR 0 -#define BF_LOGIC_XOR 1 -#define BF_LOGIC_AND 2 - -static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) -{ - switch(op) { - case BF_LOGIC_OR: - return a | b; - case BF_LOGIC_XOR: - return a ^ b; - default: - case BF_LOGIC_AND: - return a & b; - } -} - -static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) -{ - bf_t b1_s, a1_s, *a, *b; - limb_t a_sign, b_sign, r_sign; - slimb_t l, i, a_bit_offset, b_bit_offset; - limb_t v1, v2, v1_mask, v2_mask, r_mask; - int ret; - - assert(r != a1 && r != b1); - - if (a1->expn <= 0) - a_sign = 0; /* minus zero is considered as positive */ - else - a_sign = a1->sign; - - if (b1->expn <= 0) - b_sign = 0; /* minus zero is considered as positive */ - else - b_sign = b1->sign; - - if (a_sign) { - a = &a1_s; - bf_init(r->ctx, a); - if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { - b = NULL; - goto fail; - } - } else { - a = (bf_t *)a1; - } - - if (b_sign) { - b = &b1_s; - bf_init(r->ctx, b); - if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) - goto fail; - } else { - b = (bf_t *)b1; - } - - r_sign = bf_logic_op1(a_sign, b_sign, op); - if (op == BF_LOGIC_AND && r_sign == 0) { - /* no need to compute extra zeros for and */ - if (a_sign == 0 && b_sign == 0) - l = bf_min(a->expn, b->expn); - else if (a_sign == 0) - l = a->expn; - else - l = b->expn; - } else { - l = bf_max(a->expn, b->expn); - } - /* Note: a or b can be zero */ - l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; - if (bf_resize(r, l)) - goto fail; - a_bit_offset = a->len * LIMB_BITS - a->expn; - b_bit_offset = b->len * LIMB_BITS - b->expn; - v1_mask = -a_sign; - v2_mask = -b_sign; - r_mask = -r_sign; - for(i = 0; i < l; i++) { - v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask; - v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask; - r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask; - } - r->expn = l * LIMB_BITS; - r->sign = r_sign; - bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */ - if (r_sign) { - if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ)) - goto fail; - } - ret = 0; - done: - if (a == &a1_s) - bf_delete(a); - if (b == &b1_s) - bf_delete(b); - return ret; - fail: - bf_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; -} - -/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ -int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) -{ - return bf_logic_op(r, a, b, BF_LOGIC_OR); -} - -/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ -int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) -{ - return bf_logic_op(r, a, b, BF_LOGIC_XOR); -} - -/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ -int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) -{ - return bf_logic_op(r, a, b, BF_LOGIC_AND); -} - -/* conversion between fixed size types */ - -typedef union { - double d; - uint64_t u; -} Float64Union; - -int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) -{ - Float64Union u; - int e, ret; - uint64_t m; - - ret = 0; - if (a->expn == BF_EXP_NAN) { - u.u = 0x7ff8000000000000; /* quiet nan */ - } else { - bf_t b_s, *b = &b_s; - - bf_init(a->ctx, b); - bf_set(b, a); - if (bf_is_finite(b)) { - ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11)); - } - if (b->expn == BF_EXP_INF) { - e = (1 << 11) - 1; - m = 0; - } else if (b->expn == BF_EXP_ZERO) { - e = 0; - m = 0; - } else { - e = b->expn + 1023 - 1; -#if LIMB_BITS == 32 - if (b->len == 2) { - m = ((uint64_t)b->tab[1] << 32) | b->tab[0]; - } else { - m = ((uint64_t)b->tab[0] << 32); - } -#else - m = b->tab[0]; -#endif - if (e <= 0) { - /* subnormal */ - m = m >> (12 - e); - e = 0; - } else { - m = (m << 1) >> 12; - } - } - u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63); - bf_delete(b); - } - *pres = u.d; - return ret; -} - -int bf_set_float64(bf_t *a, double d) -{ - Float64Union u; - uint64_t m; - int shift, e, sgn; - - u.d = d; - sgn = u.u >> 63; - e = (u.u >> 52) & ((1 << 11) - 1); - m = u.u & (((uint64_t)1 << 52) - 1); - if (e == ((1 << 11) - 1)) { - if (m != 0) { - bf_set_nan(a); - } else { - bf_set_inf(a, sgn); - } - } else if (e == 0) { - if (m == 0) { - bf_set_zero(a, sgn); - } else { - /* subnormal number */ - m <<= 12; - shift = clz64(m); - m <<= shift; - e = -shift; - goto norm; - } - } else { - m = (m << 11) | ((uint64_t)1 << 63); - norm: - a->expn = e - 1023 + 1; -#if LIMB_BITS == 32 - if (bf_resize(a, 2)) - goto fail; - a->tab[0] = m; - a->tab[1] = m >> 32; -#else - if (bf_resize(a, 1)) - goto fail; - a->tab[0] = m; -#endif - a->sign = sgn; - } - return 0; -fail: - bf_set_nan(a); - return BF_ST_MEM_ERROR; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there - is an overflow and 0 otherwise. */ -int bf_get_int32(int *pres, const bf_t *a, int flags) -{ - uint32_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = BF_ST_INVALID_OP; - if (flags & BF_GET_INT_MOD) { - v = 0; - } else if (a->expn == BF_EXP_INF) { - v = (uint32_t)INT32_MAX + a->sign; - } else { - v = INT32_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 31) { - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } else if (!(flags & BF_GET_INT_MOD)) { - ret = BF_ST_INVALID_OP; - if (a->sign) { - v = (uint32_t)INT32_MAX + 1; - if (a->expn == 32 && - (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) { - ret = 0; - } - } else { - v = INT32_MAX; - } - } else { - v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } - *pres = v; - return ret; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there - is an overflow and 0 otherwise. */ -int bf_get_int64(int64_t *pres, const bf_t *a, int flags) -{ - uint64_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = BF_ST_INVALID_OP; - if (flags & BF_GET_INT_MOD) { - v = 0; - } else if (a->expn == BF_EXP_INF) { - v = (uint64_t)INT64_MAX + a->sign; - } else { - v = INT64_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 63) { -#if LIMB_BITS == 32 - if (a->expn <= 32) - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); - else - v = (((uint64_t)a->tab[a->len - 1] << 32) | - get_limbz(a, a->len - 2)) >> (64 - a->expn); -#else - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); -#endif - if (a->sign) - v = -v; - ret = 0; - } else if (!(flags & BF_GET_INT_MOD)) { - ret = BF_ST_INVALID_OP; - if (a->sign) { - uint64_t v1; - v = (uint64_t)INT64_MAX + 1; - if (a->expn == 64) { - v1 = a->tab[a->len - 1]; -#if LIMB_BITS == 32 - v1 = (v1 << 32) | get_limbz(a, a->len - 2); -#endif - if (v1 == v) - ret = 0; - } - } else { - v = INT64_MAX; - } - } else { - slimb_t bit_pos = a->len * LIMB_BITS - a->expn; - v = get_bits(a->tab, a->len, bit_pos); -#if LIMB_BITS == 32 - v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32; -#endif - if (a->sign) - v = -v; - ret = 0; - } - *pres = v; - return ret; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there - is an overflow and 0 otherwise. */ -int bf_get_uint64(uint64_t *pres, const bf_t *a) -{ - uint64_t v; - int ret; - if (a->expn == BF_EXP_NAN) { - goto overflow; - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->sign) { - v = 0; - ret = BF_ST_INVALID_OP; - } else if (a->expn <= 64) { -#if LIMB_BITS == 32 - if (a->expn <= 32) - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); - else - v = (((uint64_t)a->tab[a->len - 1] << 32) | - get_limbz(a, a->len - 2)) >> (64 - a->expn); -#else - v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); -#endif - ret = 0; - } else { - overflow: - v = UINT64_MAX; - ret = BF_ST_INVALID_OP; - } - *pres = v; - return ret; -} - -/* base conversion from radix */ - -static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = { -#if LIMB_BITS == 32 -32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, -#else -64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, -#endif -}; - -static limb_t get_limb_radix(int radix) -{ - int i, k; - limb_t radixl; - - k = digits_per_limb_table[radix - 2]; - radixl = radix; - for(i = 1; i < k; i++) - radixl *= radix; - return radixl; -} - -/* return != 0 if error */ -static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab, - limb_t n, int level, limb_t n0, - limb_t radix, bf_t *pow_tab) -{ - int ret; - if (n == 1) { - ret = bf_set_ui(r, tab[0]); - } else { - bf_t T_s, *T = &T_s, *B; - limb_t n1, n2; - - n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; - n1 = n - n2; - // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2); - B = &pow_tab[level]; - if (B->len == 0) { - ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); - if (ret) - return ret; - } - ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0, - radix, pow_tab); - if (ret) - return ret; - ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ); - if (ret) - return ret; - bf_init(r->ctx, T); - ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0, - radix, pow_tab); - if (!ret) - ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ); - bf_delete(T); - } - return ret; - // bf_print_str(" r=", r); -} - -/* return 0 if OK != 0 if memory error */ -static int bf_integer_from_radix(bf_t *r, const limb_t *tab, - limb_t n, limb_t radix) -{ - bf_context_t *s = r->ctx; - int pow_tab_len, i, ret; - limb_t radixl; - bf_t *pow_tab; - - radixl = get_limb_radix(radix); - pow_tab_len = ceil_log2(n) + 2; /* XXX: check */ - pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); - if (!pow_tab) - return -1; - for(i = 0; i < pow_tab_len; i++) - bf_init(r->ctx, &pow_tab[i]); - ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab); - for(i = 0; i < pow_tab_len; i++) { - bf_delete(&pow_tab[i]); - } - bf_free(s, pow_tab); - return ret; -} - -/* compute and round T * radix^expn. */ -int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, - slimb_t expn, limb_t prec, bf_flags_t flags) -{ - int ret, expn_sign, overflow; - slimb_t e, extra_bits, prec1, ziv_extra_bits; - bf_t B_s, *B = &B_s; - - if (T->len == 0) { - return bf_set(r, T); - } else if (expn == 0) { - ret = bf_set(r, T); - ret |= bf_round(r, prec, flags); - return ret; - } - - e = expn; - expn_sign = 0; - if (e < 0) { - e = -e; - expn_sign = 1; - } - bf_init(r->ctx, B); - if (prec == BF_PREC_INF) { - /* infinite precision: only used if the result is known to be exact */ - ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN); - if (expn_sign) { - ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN); - } else { - ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); - } - } else { - ziv_extra_bits = 16; - for(;;) { - prec1 = prec + ziv_extra_bits; - /* XXX: correct overflow/underflow handling */ - /* XXX: rigorous error analysis needed */ - extra_bits = ceil_log2(e) * 2 + 1; - ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); - overflow = !bf_is_finite(B); - /* XXX: if bf_pow_ui_ui returns an exact result, can stop - after the next operation */ - if (expn_sign) - ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); - else - ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); - if (ret & BF_ST_MEM_ERROR) - break; - if ((ret & BF_ST_INEXACT) && - !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) && - !overflow) { - /* and more precision and retry */ - ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); - } else { - /* XXX: need to use __bf_round() to pass the inexact - flag for the subnormal case */ - ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); - break; - } - } - } - bf_delete(B); - return ret; -} - -static inline int to_digit(int c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'A' && c <= 'Z') - return c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - return c - 'a' + 10; - else - return 36; -} - -/* add a limb at 'pos' and decrement pos. new space is created if - needed. Return 0 if OK, -1 if memory error */ -static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) -{ - slimb_t pos; - pos = *ppos; - if (unlikely(pos < 0)) { - limb_t new_size, d, *new_tab; - new_size = bf_max(a->len + 1, a->len * 3 / 2); - new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size); - if (!new_tab) - return -1; - a->tab = new_tab; - d = new_size - a->len; - memmove(a->tab + d, a->tab, a->len * sizeof(limb_t)); - a->len = new_size; - pos += d; - } - a->tab[pos--] = v; - *ppos = pos; - return 0; -} - -static int bf_tolower(int c) -{ - if (c >= 'A' && c <= 'Z') - c = c - 'A' + 'a'; - return c; -} - -static int strcasestart(const char *str, const char *val, const char **ptr) -{ - const char *p, *q; - p = str; - q = val; - while (*q != '\0') { - if (bf_tolower(*p) != *q) - return 0; - p++; - q++; - } - if (ptr) - *ptr = p; - return 1; -} - -static int bf_atof_internal(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags, BOOL is_dec) -{ - const char *p, *p_start; - int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift; - limb_t cur_limb; - slimb_t pos, expn, int_len, digit_count; - BOOL has_decpt, is_bin_exp; - bf_t a_s, *a; - - *pexponent = 0; - p = str; - if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && - strcasestart(p, "nan", &p)) { - bf_set_nan(r); - ret = 0; - goto done; - } - is_neg = 0; - - if (p[0] == '+') { - p++; - p_start = p; - } else if (p[0] == '-') { - is_neg = 1; - p++; - p_start = p; - } else { - p_start = p; - } - if (p[0] == '0') { - if ((p[1] == 'x' || p[1] == 'X') && - (radix == 0 || radix == 16) && - !(flags & BF_ATOF_NO_HEX)) { - radix = 16; - p += 2; - } else if ((p[1] == 'o' || p[1] == 'O') && - radix == 0 && (flags & BF_ATOF_BIN_OCT)) { - p += 2; - radix = 8; - } else if ((p[1] == 'b' || p[1] == 'B') && - radix == 0 && (flags & BF_ATOF_BIN_OCT)) { - p += 2; - radix = 2; - } else { - goto no_prefix; - } - /* there must be a digit after the prefix */ - if (to_digit((uint8_t)*p) >= radix) { - bf_set_nan(r); - ret = 0; - goto done; - } - no_prefix: ; - } else { - if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && - strcasestart(p, "inf", &p)) { - bf_set_inf(r, is_neg); - ret = 0; - goto done; - } - } - - if (radix == 0) - radix = 10; - if (is_dec) { - assert(radix == 10); - radix_bits = 0; - a = r; - } else if ((radix & (radix - 1)) != 0) { - radix_bits = 0; /* base is not a power of two */ - a = &a_s; - bf_init(r->ctx, a); - } else { - radix_bits = ceil_log2(radix); - a = r; - } - - /* skip leading zeros */ - /* XXX: could also skip zeros after the decimal point */ - while (*p == '0') - p++; - - if (radix_bits) { - shift = digits_per_limb = LIMB_BITS; - } else { - radix_bits = 0; - shift = digits_per_limb = digits_per_limb_table[radix - 2]; - } - cur_limb = 0; - bf_resize(a, 1); - pos = 0; - has_decpt = FALSE; - int_len = digit_count = 0; - for(;;) { - limb_t c; - if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) { - if (has_decpt) - break; - has_decpt = TRUE; - int_len = digit_count; - p++; - } - c = to_digit(*p); - if (c >= radix) - break; - digit_count++; - p++; - if (radix_bits) { - shift -= radix_bits; - if (shift <= 0) { - cur_limb |= c >> (-shift); - if (bf_add_limb(a, &pos, cur_limb)) - goto mem_error; - if (shift < 0) - cur_limb = c << (LIMB_BITS + shift); - else - cur_limb = 0; - shift += LIMB_BITS; - } else { - cur_limb |= c << shift; - } - } else { - cur_limb = cur_limb * radix + c; - shift--; - if (shift == 0) { - if (bf_add_limb(a, &pos, cur_limb)) - goto mem_error; - shift = digits_per_limb; - cur_limb = 0; - } - } - } - if (!has_decpt) - int_len = digit_count; - - /* add the last limb and pad with zeros */ - if (shift != digits_per_limb) { - if (radix_bits == 0) { - while (shift != 0) { - cur_limb *= radix; - shift--; - } - } - if (bf_add_limb(a, &pos, cur_limb)) { - mem_error: - ret = BF_ST_MEM_ERROR; - if (!radix_bits) - bf_delete(a); - bf_set_nan(r); - goto done; - } - } - - /* reset the next limbs to zero (we prefer to reallocate in the - renormalization) */ - memset(a->tab, 0, (pos + 1) * sizeof(limb_t)); - - if (p == p_start) { - ret = 0; - if (!radix_bits) - bf_delete(a); - bf_set_nan(r); - goto done; - } - - /* parse the exponent, if any */ - expn = 0; - is_bin_exp = FALSE; - if (((radix == 10 && (*p == 'e' || *p == 'E')) || - (radix != 10 && (*p == '@' || - (radix_bits && (*p == 'p' || *p == 'P'))))) && - p > p_start) { - is_bin_exp = (*p == 'p' || *p == 'P'); - p++; - exp_is_neg = 0; - if (*p == '+') { - p++; - } else if (*p == '-') { - exp_is_neg = 1; - p++; - } - for(;;) { - int c; - c = to_digit(*p); - if (c >= 10) - break; - if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { - /* exponent overflow */ - if (exp_is_neg) { - bf_set_zero(r, is_neg); - ret = BF_ST_UNDERFLOW | BF_ST_INEXACT; - } else { - bf_set_inf(r, is_neg); - ret = BF_ST_OVERFLOW | BF_ST_INEXACT; - } - goto done; - } - p++; - expn = expn * 10 + c; - } - if (exp_is_neg) - expn = -expn; - } - if (is_dec) { - a->expn = expn + int_len; - a->sign = is_neg; - ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags); - } else if (radix_bits) { - /* XXX: may overflow */ - if (!is_bin_exp) - expn *= radix_bits; - a->expn = expn + (int_len * radix_bits); - a->sign = is_neg; - ret = bf_normalize_and_round(a, prec, flags); - } else { - limb_t l; - pos++; - l = a->len - pos; /* number of limbs */ - if (l == 0) { - bf_set_zero(r, is_neg); - ret = 0; - } else { - bf_t T_s, *T = &T_s; - - expn -= l * digits_per_limb - int_len; - bf_init(r->ctx, T); - if (bf_integer_from_radix(T, a->tab + pos, l, radix)) { - bf_set_nan(r); - ret = BF_ST_MEM_ERROR; - } else { - T->sign = is_neg; - if (flags & BF_ATOF_EXPONENT) { - /* return the exponent */ - *pexponent = expn; - ret = bf_set(r, T); - } else { - ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags); - } - } - bf_delete(T); - } - bf_delete(a); - } - done: - if (pnext) - *pnext = p; - return ret; -} - -/* - Return (status, n, exp). 'status' is the floating point status. 'n' - is the parsed number. - - If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of - two, the parsed number is equal to r * - (*pexponent)^radix. Otherwise *pexponent = 0. -*/ -int bf_atof2(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags) -{ - return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags, - FALSE); -} - -int bf_atof(bf_t *r, const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags) -{ - slimb_t dummy_exp; - return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE); -} - -/* base conversion to radix */ - -#if LIMB_BITS == 64 -#define RADIXL_10 UINT64_C(10000000000000000000) -#else -#define RADIXL_10 UINT64_C(1000000000) -#endif - -static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = { -#if LIMB_BITS == 32 -{ 0x80000000, 0x00000000,}, -{ 0x50c24e60, 0xd4d4f4a7,}, -{ 0x40000000, 0x00000000,}, -{ 0x372068d2, 0x0a1ee5ca,}, -{ 0x3184648d, 0xb8153e7a,}, -{ 0x2d983275, 0x9d5369c4,}, -{ 0x2aaaaaaa, 0xaaaaaaab,}, -{ 0x28612730, 0x6a6a7a54,}, -{ 0x268826a1, 0x3ef3fde6,}, -{ 0x25001383, 0xbac8a744,}, -{ 0x23b46706, 0x82c0c709,}, -{ 0x229729f1, 0xb2c83ded,}, -{ 0x219e7ffd, 0xa5ad572b,}, -{ 0x20c33b88, 0xda7c29ab,}, -{ 0x20000000, 0x00000000,}, -{ 0x1f50b57e, 0xac5884b3,}, -{ 0x1eb22cc6, 0x8aa6e26f,}, -{ 0x1e21e118, 0x0c5daab2,}, -{ 0x1d9dcd21, 0x439834e4,}, -{ 0x1d244c78, 0x367a0d65,}, -{ 0x1cb40589, 0xac173e0c,}, -{ 0x1c4bd95b, 0xa8d72b0d,}, -{ 0x1bead768, 0x98f8ce4c,}, -{ 0x1b903469, 0x050f72e5,}, -{ 0x1b3b433f, 0x2eb06f15,}, -{ 0x1aeb6f75, 0x9c46fc38,}, -{ 0x1aa038eb, 0x0e3bfd17,}, -{ 0x1a593062, 0xb38d8c56,}, -{ 0x1a15f4c3, 0x2b95a2e6,}, -{ 0x19d630dc, 0xcc7ddef9,}, -{ 0x19999999, 0x9999999a,}, -{ 0x195fec80, 0x8a609431,}, -{ 0x1928ee7b, 0x0b4f22f9,}, -{ 0x18f46acf, 0x8c06e318,}, -{ 0x18c23246, 0xdc0a9f3d,}, -#else -{ 0x80000000, 0x00000000, 0x00000000,}, -{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,}, -{ 0x40000000, 0x00000000, 0x00000000,}, -{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,}, -{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,}, -{ 0x2d983275, 0x9d5369c4, 0x4dec1661,}, -{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,}, -{ 0x28612730, 0x6a6a7a53, 0x810fabde,}, -{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,}, -{ 0x25001383, 0xbac8a744, 0x385a3349,}, -{ 0x23b46706, 0x82c0c709, 0x3f891718,}, -{ 0x229729f1, 0xb2c83ded, 0x15fba800,}, -{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,}, -{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,}, -{ 0x20000000, 0x00000000, 0x00000000,}, -{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,}, -{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,}, -{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,}, -{ 0x1d9dcd21, 0x439834e3, 0x81667575,}, -{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,}, -{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,}, -{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,}, -{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,}, -{ 0x1b903469, 0x050f72e5, 0x0cf5488e,}, -{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,}, -{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,}, -{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,}, -{ 0x1a593062, 0xb38d8c56, 0x7998ab45,}, -{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,}, -{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,}, -{ 0x19999999, 0x99999999, 0x9999999a,}, -{ 0x195fec80, 0x8a609430, 0xe1106014,}, -{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,}, -{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,}, -{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,}, -#endif -}; - -static const limb_t log2_radix[BF_RADIX_MAX - 1] = { -#if LIMB_BITS == 32 -0x20000000, -0x32b80347, -0x40000000, -0x4a4d3c26, -0x52b80347, -0x59d5d9fd, -0x60000000, -0x6570068e, -0x6a4d3c26, -0x6eb3a9f0, -0x72b80347, -0x766a008e, -0x79d5d9fd, -0x7d053f6d, -0x80000000, -0x82cc7edf, -0x8570068e, -0x87ef05ae, -0x8a4d3c26, -0x8c8ddd45, -0x8eb3a9f0, -0x90c10501, -0x92b80347, -0x949a784c, -0x966a008e, -0x982809d6, -0x99d5d9fd, -0x9b74948f, -0x9d053f6d, -0x9e88c6b3, -0xa0000000, -0xa16bad37, -0xa2cc7edf, -0xa4231623, -0xa570068e, -#else -0x2000000000000000, -0x32b803473f7ad0f4, -0x4000000000000000, -0x4a4d3c25e68dc57f, -0x52b803473f7ad0f4, -0x59d5d9fd5010b366, -0x6000000000000000, -0x6570068e7ef5a1e8, -0x6a4d3c25e68dc57f, -0x6eb3a9f01975077f, -0x72b803473f7ad0f4, -0x766a008e4788cbcd, -0x79d5d9fd5010b366, -0x7d053f6d26089673, -0x8000000000000000, -0x82cc7edf592262d0, -0x8570068e7ef5a1e8, -0x87ef05ae409a0289, -0x8a4d3c25e68dc57f, -0x8c8ddd448f8b845a, -0x8eb3a9f01975077f, -0x90c10500d63aa659, -0x92b803473f7ad0f4, -0x949a784bcd1b8afe, -0x966a008e4788cbcd, -0x982809d5be7072dc, -0x99d5d9fd5010b366, -0x9b74948f5532da4b, -0x9d053f6d26089673, -0x9e88c6b3626a72aa, -0xa000000000000000, -0xa16bad3758efd873, -0xa2cc7edf592262d0, -0xa4231623369e78e6, -0xa570068e7ef5a1e8, -#endif -}; - -/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or - b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed - when radix is not a power of two. */ -slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, - int is_ceil1) -{ - int is_neg; - limb_t a; - BOOL is_ceil; - - is_ceil = is_ceil1; - a = a1; - if (a1 < 0) { - a = -a; - is_neg = 1; - } else { - is_neg = 0; - } - is_ceil ^= is_neg; - if ((radix & (radix - 1)) == 0) { - int radix_bits; - /* radix is a power of two */ - radix_bits = ceil_log2(radix); - if (is_inv) { - if (is_ceil) - a += radix_bits - 1; - a = a / radix_bits; - } else { - a = a * radix_bits; - } - } else { - const uint32_t *tab; - limb_t b0, b1; - dlimb_t t; - - if (is_inv) { - tab = inv_log2_radix[radix - 2]; -#if LIMB_BITS == 32 - b1 = tab[0]; - b0 = tab[1]; -#else - b1 = ((limb_t)tab[0] << 32) | tab[1]; - b0 = (limb_t)tab[2] << 32; -#endif - t = (dlimb_t)b0 * (dlimb_t)a; - t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS); - a = t >> (LIMB_BITS - 1); - } else { - b0 = log2_radix[radix - 2]; - t = (dlimb_t)b0 * (dlimb_t)a; - a = t >> (LIMB_BITS - 3); - } - /* a = floor(result) and 'result' cannot be an integer */ - a += is_ceil; - } - if (is_neg) - a = -a; - return a; -} - -/* 'n' is the number of output limbs */ -static int bf_integer_to_radix_rec(bf_t *pow_tab, - limb_t *out, const bf_t *a, limb_t n, - int level, limb_t n0, limb_t radixl, - unsigned int radixl_bits) -{ - limb_t n1, n2, q_prec; - int ret; - - assert(n >= 1); - if (n == 1) { - out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); - } else if (n == 2) { - dlimb_t t; - slimb_t pos; - pos = a->len * LIMB_BITS - a->expn; - t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) | - get_bits(a->tab, a->len, pos); - if (likely(radixl == RADIXL_10)) { - /* use division by a constant when possible */ - out[0] = t % RADIXL_10; - out[1] = t / RADIXL_10; - } else { - out[0] = t % radixl; - out[1] = t / radixl; - } - } else { - bf_t Q, R, *B, *B_inv; - int q_add; - bf_init(a->ctx, &Q); - bf_init(a->ctx, &R); - n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; - n1 = n - n2; - B = &pow_tab[2 * level]; - B_inv = &pow_tab[2 * level + 1]; - ret = 0; - if (B->len == 0) { - /* compute BASE^n2 */ - ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); - /* we use enough bits for the maximum possible 'n1' value, - i.e. n2 + 1 */ - ret |= bf_set_ui(&R, 1); - ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN); - } - // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2); - q_prec = n1 * radixl_bits; - ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); - ret |= bf_rint(&Q, BF_RNDZ); - - ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ); - ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ); - - if (ret & BF_ST_MEM_ERROR) - goto fail; - /* adjust if necessary */ - q_add = 0; - while (R.sign && R.len != 0) { - if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ)) - goto fail; - q_add--; - } - while (bf_cmpu(&R, B) >= 0) { - if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ)) - goto fail; - q_add++; - } - if (q_add != 0) { - if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ)) - goto fail; - } - if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, - radixl, radixl_bits)) - goto fail; - if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, - radixl, radixl_bits)) { - fail: - bf_delete(&Q); - bf_delete(&R); - return -1; - } - bf_delete(&Q); - bf_delete(&R); - } - return 0; -} - -/* return 0 if OK != 0 if memory error */ -static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) -{ - bf_context_t *s = r->ctx; - limb_t r_len; - bf_t *pow_tab; - int i, pow_tab_len, ret; - - r_len = r->len; - pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */ - pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); - if (!pow_tab) - return -1; - for(i = 0; i < pow_tab_len; i++) - bf_init(r->ctx, &pow_tab[i]); - - ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, - ceil_log2(radixl)); - - for(i = 0; i < pow_tab_len; i++) { - bf_delete(&pow_tab[i]); - } - bf_free(s, pow_tab); - return ret; -} - -/* a must be >= 0. 'P' is the wanted number of digits in radix - 'radix'. 'r' is the mantissa represented as an integer. *pE - contains the exponent. Return != 0 if memory error. */ -static int bf_convert_to_radix(bf_t *r, slimb_t *pE, - const bf_t *a, int radix, - limb_t P, bf_rnd_t rnd_mode, - BOOL is_fixed_exponent) -{ - slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0; - bf_t B_s, *B = &B_s; - int e_sign, ret, res; - - if (a->len == 0) { - /* zero case */ - *pE = 0; - return bf_set(r, a); - } - - if (is_fixed_exponent) { - E = *pE; - } else { - /* compute the new exponent */ - E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE); - } - // bf_print_str("a", a); - // printf("E=%ld P=%ld radix=%d\n", E, P, radix); - - for(;;) { - e = P - E; - e_sign = 0; - if (e < 0) { - e = -e; - e_sign = 1; - } - /* Note: precision for log2(radix) is not critical here */ - prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE); - ziv_extra_bits = 16; - for(;;) { - prec = prec0 + ziv_extra_bits; - /* XXX: rigorous error analysis needed */ - extra_bits = ceil_log2(e) * 2 + 1; - ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, - BF_RNDN | BF_FLAG_EXT_EXP); - if (!e_sign) - ret |= bf_mul(r, r, a, prec + extra_bits, - BF_RNDN | BF_FLAG_EXT_EXP); - else - ret |= bf_div(r, a, r, prec + extra_bits, - BF_RNDN | BF_FLAG_EXT_EXP); - if (ret & BF_ST_MEM_ERROR) - return BF_ST_MEM_ERROR; - /* if the result is not exact, check that it can be safely - rounded to an integer */ - if ((ret & BF_ST_INEXACT) && - !bf_can_round(r, r->expn, rnd_mode, prec)) { - /* and more precision and retry */ - ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); - continue; - } else { - ret = bf_rint(r, rnd_mode); - if (ret & BF_ST_MEM_ERROR) - return BF_ST_MEM_ERROR; - break; - } - } - if (is_fixed_exponent) - break; - /* check that the result is < B^P */ - /* XXX: do a fast approximate test first ? */ - bf_init(r->ctx, B); - ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ); - if (ret) { - bf_delete(B); - return ret; - } - res = bf_cmpu(r, B); - bf_delete(B); - if (res < 0) - break; - /* try a larger exponent */ - E++; - } - *pE = E; - return 0; -} - -static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) -{ - int digit, i; - - if (radix == 10) { - /* specific case with constant divisor */ - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % 10; - n = (limb_t)n / 10; - buf[i] = digit + '0'; - } - } else { - for(i = len - 1; i >= 0; i--) { - digit = (limb_t)n % radix; - n = (limb_t)n / radix; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } - } -} - -/* for power of 2 radixes */ -static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len) -{ - int digit, i; - unsigned int mask; - - mask = (1 << radix_bits) - 1; - for(i = len - 1; i >= 0; i--) { - digit = n & mask; - n >>= radix_bits; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - buf[i] = digit; - } -} - -/* 'a' must be an integer if the is_dec = FALSE or if the radix is not - a power of two. A dot is added before the 'dot_pos' digit. dot_pos - = n_digits does not display the dot. 0 <= dot_pos <= - n_digits. n_digits >= 1. */ -static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, - limb_t dot_pos, BOOL is_dec) -{ - limb_t i, v, l; - slimb_t pos, pos_incr; - int digits_per_limb, buf_pos, radix_bits, first_buf_pos; - char buf[65]; - bf_t a_s, *a; - - if (is_dec) { - digits_per_limb = LIMB_DIGITS; - a = (bf_t *)a1; - radix_bits = 0; - pos = a->len; - pos_incr = 1; - first_buf_pos = 0; - } else if ((radix & (radix - 1)) == 0) { - a = (bf_t *)a1; - radix_bits = ceil_log2(radix); - digits_per_limb = LIMB_BITS / radix_bits; - pos_incr = digits_per_limb * radix_bits; - /* digits are aligned relative to the radix point */ - pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); - first_buf_pos = 0; - } else { - limb_t n, radixl; - - digits_per_limb = digits_per_limb_table[radix - 2]; - radixl = get_limb_radix(radix); - a = &a_s; - bf_init(a1->ctx, a); - n = (n_digits + digits_per_limb - 1) / digits_per_limb; - if (bf_resize(a, n)) { - dbuf_set_error(s); - goto done; - } - if (bf_integer_to_radix(a, a1, radixl)) { - dbuf_set_error(s); - goto done; - } - radix_bits = 0; - pos = n; - pos_incr = 1; - first_buf_pos = pos * digits_per_limb - n_digits; - } - buf_pos = digits_per_limb; - i = 0; - while (i < n_digits) { - if (buf_pos == digits_per_limb) { - pos -= pos_incr; - if (radix_bits == 0) { - v = get_limbz(a, pos); - limb_to_a(buf, v, radix, digits_per_limb); - } else { - v = get_bits(a->tab, a->len, pos); - limb_to_a2(buf, v, radix_bits, digits_per_limb); - } - buf_pos = first_buf_pos; - first_buf_pos = 0; - } - if (i < dot_pos) { - l = dot_pos; - } else { - if (i == dot_pos) - dbuf_putc(s, '.'); - l = n_digits; - } - l = bf_min(digits_per_limb - buf_pos, l - i); - dbuf_put(s, (uint8_t *)(buf + buf_pos), l); - buf_pos += l; - i += l; - } - done: - if (a != a1) - bf_delete(a); -} - -static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) -{ - bf_context_t *s = opaque; - return bf_realloc(s, ptr, size); -} - -/* return the length in bytes. A trailing '\0' is added */ -static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, - limb_t prec, bf_flags_t flags, BOOL is_dec) -{ - bf_context_t *ctx = a2->ctx; - DynBuf s_s, *s = &s_s; - int radix_bits; - - // bf_print_str("ftoa", a2); - // printf("radix=%d\n", radix); - dbuf_init2(s, ctx, bf_dbuf_realloc); - if (a2->expn == BF_EXP_NAN) { - dbuf_putstr(s, "NaN"); - } else { - if (a2->sign) - dbuf_putc(s, '-'); - if (a2->expn == BF_EXP_INF) { - if (flags & BF_FTOA_JS_QUIRKS) - dbuf_putstr(s, "Infinity"); - else - dbuf_putstr(s, "Inf"); - } else { - int fmt, ret; - slimb_t n_digits, n, i, n_max, n1; - bf_t a1_s, *a1 = &a1_s; - - if ((radix & (radix - 1)) != 0) - radix_bits = 0; - else - radix_bits = ceil_log2(radix); - - fmt = flags & BF_FTOA_FORMAT_MASK; - bf_init(ctx, a1); - if (fmt == BF_FTOA_FORMAT_FRAC) { - if (is_dec || radix_bits != 0) { - if (bf_set(a1, a2)) - goto fail1; -#ifdef USE_BF_DEC - if (is_dec) { - if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) - goto fail1; - n = a1->expn; - } else -#endif - { - if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) - goto fail1; - n = ceil_div(a1->expn, radix_bits); - } - if (flags & BF_FTOA_ADD_PREFIX) { - if (radix == 16) - dbuf_putstr(s, "0x"); - else if (radix == 8) - dbuf_putstr(s, "0o"); - else if (radix == 2) - dbuf_putstr(s, "0b"); - } - if (a1->expn == BF_EXP_ZERO) { - dbuf_putstr(s, "0"); - if (prec > 0) { - dbuf_putstr(s, "."); - for(i = 0; i < prec; i++) { - dbuf_putc(s, '0'); - } - } - } else { - n_digits = prec + n; - if (n <= 0) { - /* 0.x */ - dbuf_putstr(s, "0."); - for(i = 0; i < -n; i++) { - dbuf_putc(s, '0'); - } - if (n_digits > 0) { - output_digits(s, a1, radix, n_digits, n_digits, is_dec); - } - } else { - output_digits(s, a1, radix, n_digits, n, is_dec); - } - } - } else { - size_t pos, start; - bf_t a_s, *a = &a_s; - - /* make a positive number */ - a->tab = a2->tab; - a->len = a2->len; - a->expn = a2->expn; - a->sign = 0; - - /* one more digit for the rounding */ - n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); - n_digits = n + prec; - n1 = n; - if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, - flags & BF_RND_MASK, TRUE)) - goto fail1; - start = s->size; - output_digits(s, a1, radix, n_digits, n, is_dec); - /* remove leading zeros because we allocated one more digit */ - pos = start; - while ((pos + 1) < s->size && s->buf[pos] == '0' && - s->buf[pos + 1] != '.') - pos++; - if (pos > start) { - memmove(s->buf + start, s->buf + pos, s->size - pos); - s->size -= (pos - start); - } - } - } else { -#ifdef USE_BF_DEC - if (is_dec) { - if (bf_set(a1, a2)) - goto fail1; - if (fmt == BF_FTOA_FORMAT_FIXED) { - n_digits = prec; - n_max = n_digits; - if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) - goto fail1; - } else { - /* prec is ignored */ - prec = n_digits = a1->len * LIMB_DIGITS; - /* remove the trailing zero digits */ - while (n_digits > 1 && - get_digit(a1->tab, a1->len, prec - n_digits) == 0) { - n_digits--; - } - n_max = n_digits + 4; - } - n = a1->expn; - } else -#endif - if (radix_bits != 0) { - if (bf_set(a1, a2)) - goto fail1; - if (fmt == BF_FTOA_FORMAT_FIXED) { - slimb_t prec_bits; - n_digits = prec; - n_max = n_digits; - /* align to the radix point */ - prec_bits = prec * radix_bits - - smod(-a1->expn, radix_bits); - if (bf_round(a1, prec_bits, - (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) - goto fail1; - } else { - limb_t digit_mask; - slimb_t pos; - /* position of the digit before the most - significant digit in bits */ - pos = a1->len * LIMB_BITS + - smod(-a1->expn, radix_bits); - n_digits = ceil_div(pos, radix_bits); - /* remove the trailing zero digits */ - digit_mask = ((limb_t)1 << radix_bits) - 1; - while (n_digits > 1 && - (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { - n_digits--; - } - n_max = n_digits + 4; - } - n = ceil_div(a1->expn, radix_bits); - } else { - bf_t a_s, *a = &a_s; - - /* make a positive number */ - a->tab = a2->tab; - a->len = a2->len; - a->expn = a2->expn; - a->sign = 0; - - if (fmt == BF_FTOA_FORMAT_FIXED) { - n_digits = prec; - n_max = n_digits; - } else { - slimb_t n_digits_max, n_digits_min; - - assert(prec != BF_PREC_INF); - n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE); - /* max number of digits for non exponential - notation. The rational is to have the same rule - as JS i.e. n_max = 21 for 64 bit float in base 10. */ - n_max = n_digits + 4; - if (fmt == BF_FTOA_FORMAT_FREE_MIN) { - bf_t b_s, *b = &b_s; - - /* find the minimum number of digits by - dichotomy. */ - /* XXX: inefficient */ - n_digits_max = n_digits; - n_digits_min = 1; - bf_init(ctx, b); - while (n_digits_min < n_digits_max) { - n_digits = (n_digits_min + n_digits_max) / 2; - if (bf_convert_to_radix(a1, &n, a, radix, n_digits, - flags & BF_RND_MASK, FALSE)) { - bf_delete(b); - goto fail1; - } - /* convert back to a number and compare */ - ret = bf_mul_pow_radix(b, a1, radix, n - n_digits, - prec, - (flags & ~BF_RND_MASK) | - BF_RNDN); - if (ret & BF_ST_MEM_ERROR) { - bf_delete(b); - goto fail1; - } - if (bf_cmpu(b, a) == 0) { - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; - } - } - bf_delete(b); - n_digits = n_digits_max; - } - } - if (bf_convert_to_radix(a1, &n, a, radix, n_digits, - flags & BF_RND_MASK, FALSE)) { - fail1: - bf_delete(a1); - goto fail; - } - } - if (a1->expn == BF_EXP_ZERO && - fmt != BF_FTOA_FORMAT_FIXED && - !(flags & BF_FTOA_FORCE_EXP)) { - /* just output zero */ - dbuf_putstr(s, "0"); - } else { - if (flags & BF_FTOA_ADD_PREFIX) { - if (radix == 16) - dbuf_putstr(s, "0x"); - else if (radix == 8) - dbuf_putstr(s, "0o"); - else if (radix == 2) - dbuf_putstr(s, "0b"); - } - if (a1->expn == BF_EXP_ZERO) - n = 1; - if ((flags & BF_FTOA_FORCE_EXP) || - n <= -6 || n > n_max) { - const char *fmt; - /* exponential notation */ - output_digits(s, a1, radix, n_digits, 1, is_dec); - if (radix_bits != 0 && radix <= 16) { - if (flags & BF_FTOA_JS_QUIRKS) - fmt = "p%+" PRId_LIMB; - else - fmt = "p%" PRId_LIMB; - dbuf_printf(s, fmt, (n - 1) * radix_bits); - } else { - if (flags & BF_FTOA_JS_QUIRKS) - fmt = "%c%+" PRId_LIMB; - else - fmt = "%c%" PRId_LIMB; - dbuf_printf(s, fmt, - radix <= 10 ? 'e' : '@', n - 1); - } - } else if (n <= 0) { - /* 0.x */ - dbuf_putstr(s, "0."); - for(i = 0; i < -n; i++) { - dbuf_putc(s, '0'); - } - output_digits(s, a1, radix, n_digits, n_digits, is_dec); - } else { - if (n_digits <= n) { - /* no dot */ - output_digits(s, a1, radix, n_digits, n_digits, is_dec); - for(i = 0; i < (n - n_digits); i++) - dbuf_putc(s, '0'); - } else { - output_digits(s, a1, radix, n_digits, n, is_dec); - } - } - } - } - bf_delete(a1); - } - } - dbuf_putc(s, '\0'); - if (dbuf_error(s)) - goto fail; - if (plen) - *plen = s->size - 1; - return (char *)s->buf; - fail: - bf_free(ctx, s->buf); - if (plen) - *plen = 0; - return NULL; -} - -char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags) -{ - return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE); -} - -/***************************************************************/ -/* transcendental functions */ - -/* Note: the algorithm is from MPFR */ -static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1, - limb_t n2, BOOL need_P) -{ - bf_context_t *s = T->ctx; - if ((n2 - n1) == 1) { - if (n1 == 0) { - bf_set_ui(P, 3); - } else { - bf_set_ui(P, n1); - P->sign = 1; - } - bf_set_ui(Q, 2 * n1 + 1); - Q->expn += 2; - bf_set(T, P); - } else { - limb_t m; - bf_t T1_s, *T1 = &T1_s; - bf_t P1_s, *P1 = &P1_s; - bf_t Q1_s, *Q1 = &Q1_s; - - m = n1 + ((n2 - n1) >> 1); - bf_const_log2_rec(T, P, Q, n1, m, TRUE); - bf_init(s, T1); - bf_init(s, P1); - bf_init(s, Q1); - bf_const_log2_rec(T1, P1, Q1, m, n2, need_P); - bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ); - bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ); - bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ); - if (need_P) - bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ); - bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ); - bf_delete(T1); - bf_delete(P1); - bf_delete(Q1); - } -} - -/* compute log(2) with faithful rounding at precision 'prec' */ -static void bf_const_log2_internal(bf_t *T, limb_t prec) -{ - limb_t w, N; - bf_t P_s, *P = &P_s; - bf_t Q_s, *Q = &Q_s; - - w = prec + 15; - N = w / 3 + 1; - bf_init(T->ctx, P); - bf_init(T->ctx, Q); - bf_const_log2_rec(T, P, Q, 0, N, FALSE); - bf_div(T, T, Q, prec, BF_RNDN); - bf_delete(P); - bf_delete(Q); -} - -/* PI constant */ - -#define CHUD_A 13591409 -#define CHUD_B 545140134 -#define CHUD_C 640320 -#define CHUD_BITS_PER_TERM 47 - -static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g, - limb_t prec) -{ - bf_context_t *s = P->ctx; - int64_t c; - - if (a == (b - 1)) { - bf_t T0, T1; - - bf_init(s, &T0); - bf_init(s, &T1); - bf_set_ui(G, 2 * b - 1); - bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN); - bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN); - bf_set_ui(&T0, CHUD_B); - bf_mul_ui(&T0, &T0, b, prec, BF_RNDN); - bf_set_ui(&T1, CHUD_A); - bf_add(&T0, &T0, &T1, prec, BF_RNDN); - bf_mul(P, G, &T0, prec, BF_RNDN); - P->sign = b & 1; - - bf_set_ui(Q, b); - bf_mul_ui(Q, Q, b, prec, BF_RNDN); - bf_mul_ui(Q, Q, b, prec, BF_RNDN); - bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN); - bf_delete(&T0); - bf_delete(&T1); - } else { - bf_t P2, Q2, G2; - - bf_init(s, &P2); - bf_init(s, &Q2); - bf_init(s, &G2); - - c = (a + b) / 2; - chud_bs(P, Q, G, a, c, 1, prec); - chud_bs(&P2, &Q2, &G2, c, b, need_g, prec); - - /* Q = Q1 * Q2 */ - /* G = G1 * G2 */ - /* P = P1 * Q2 + P2 * G1 */ - bf_mul(&P2, &P2, G, prec, BF_RNDN); - if (!need_g) - bf_set_ui(G, 0); - bf_mul(P, P, &Q2, prec, BF_RNDN); - bf_add(P, P, &P2, prec, BF_RNDN); - bf_delete(&P2); - - bf_mul(Q, Q, &Q2, prec, BF_RNDN); - bf_delete(&Q2); - if (need_g) - bf_mul(G, G, &G2, prec, BF_RNDN); - bf_delete(&G2); - } -} - -/* compute Pi with faithful rounding at precision 'prec' using the - Chudnovsky formula */ -static void bf_const_pi_internal(bf_t *Q, limb_t prec) -{ - bf_context_t *s = Q->ctx; - int64_t n, prec1; - bf_t P, G; - - /* number of serie terms */ - n = prec / CHUD_BITS_PER_TERM + 1; - /* XXX: precision analysis */ - prec1 = prec + 32; - - bf_init(s, &P); - bf_init(s, &G); - - chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF); - - bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN); - bf_add(&P, &G, &P, prec1, BF_RNDN); - bf_div(Q, Q, &P, prec1, BF_RNDF); - - bf_set_ui(&P, CHUD_C); - bf_sqrt(&G, &P, prec1, BF_RNDF); - bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF); - bf_mul(Q, Q, &G, prec, BF_RNDN); - bf_delete(&P); - bf_delete(&G); -} - -static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, - BFConstCache *c, - void (*func)(bf_t *res, limb_t prec), int sign) -{ - limb_t ziv_extra_bits, prec1; - - ziv_extra_bits = 32; - for(;;) { - prec1 = prec + ziv_extra_bits; - if (c->prec < prec1) { - if (c->val.len == 0) - bf_init(T->ctx, &c->val); - func(&c->val, prec1); - c->prec = prec1; - } else { - prec1 = c->prec; - } - bf_set(T, &c->val); - T->sign = sign; - if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { - /* and more precision and retry */ - ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); - } else { - break; - } - } - return bf_round(T, prec, flags); -} - -static void bf_const_free(BFConstCache *c) -{ - bf_delete(&c->val); - memset(c, 0, sizeof(*c)); -} - -int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); -} - -/* return rounded pi * (1 - 2 * sign) */ -static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, - sign); -} - -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) -{ - return bf_const_pi_signed(T, 0, prec, flags); -} - -void bf_clear_cache(bf_context_t *s) -{ -#ifdef USE_FFT_MUL - fft_clear_cache(s); -#endif - bf_const_free(&s->log2_cache); - bf_const_free(&s->pi_cache); -} - -/* ZivFunc should compute the result 'r' with faithful rounding at - precision 'prec'. For efficiency purposes, the final bf_round() - does not need to be done in the function. */ -typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque); - -static int bf_ziv_rounding(bf_t *r, const bf_t *a, - limb_t prec, bf_flags_t flags, - ZivFunc *f, void *opaque) -{ - int rnd_mode, ret; - slimb_t prec1, ziv_extra_bits; - - rnd_mode = flags & BF_RND_MASK; - if (rnd_mode == BF_RNDF) { - /* no need to iterate */ - f(r, a, prec, opaque); - ret = 0; - } else { - ziv_extra_bits = 32; - for(;;) { - prec1 = prec + ziv_extra_bits; - ret = f(r, a, prec1, opaque); - if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) { - /* overflow or underflow should never happen because - it indicates the rounding cannot be done correctly, - but we do not catch all the cases */ - return ret; - } - /* if the result is exact, we can stop */ - if (!(ret & BF_ST_INEXACT)) { - ret = 0; - break; - } - if (bf_can_round(r, prec, rnd_mode, prec1)) { - ret = BF_ST_INEXACT; - break; - } - ziv_extra_bits = ziv_extra_bits * 2; - // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); - } - } - if (r->len == 0) - return ret; - else - return __bf_round(r, prec, flags, r->len, ret); -} - -/* add (1 - 2*e_sign) * 2^e */ -static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, - limb_t prec, int flags) -{ - bf_t T_s, *T = &T_s; - int ret; - /* small argument case: result = 1 + epsilon * sign(x) */ - bf_init(a->ctx, T); - bf_set_ui(T, 1); - T->sign = e_sign; - T->expn += e; - ret = bf_add(r, r, T, prec, flags); - bf_delete(T); - return ret; -} - -/* Compute the exponential using faithful rounding at precision 'prec'. - Note: the algorithm is from MPFR */ -static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - slimb_t n, K, l, i, prec1; - - assert(r != a); - - /* argument reduction: - T = a - n*log(2) with 0 <= T < log(2) and n integer. - */ - bf_init(s, T); - if (a->expn <= -1) { - /* 0 <= abs(a) <= 0.5 */ - if (a->sign) - n = -1; - else - n = 0; - } else { - bf_const_log2(T, LIMB_BITS, BF_RNDZ); - bf_div(T, a, T, LIMB_BITS, BF_RNDD); - bf_get_limb(&n, T, 0); - } - - K = bf_isqrt((prec + 1) / 2); - l = (prec - 1) / K + 1; - /* XXX: precision analysis ? */ - prec1 = prec + (K + 2 * l + 18) + K + 8; - if (a->expn > 0) - prec1 += a->expn; - // printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1); - - bf_const_log2(T, prec1, BF_RNDF); - bf_mul_si(T, T, n, prec1, BF_RNDN); - bf_sub(T, a, T, prec1, BF_RNDN); - - /* reduce the range of T */ - bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ); - - /* Taylor expansion around zero : - 1 + x + x^2/2 + ... + x^n/n! - = (1 + x * (1 + x/2 * (1 + ... (x/n)))) - */ - { - bf_t U_s, *U = &U_s; - - bf_init(s, U); - bf_set_ui(r, 1); - for(i = l ; i >= 1; i--) { - bf_set_ui(U, i); - bf_div(U, T, U, prec1, BF_RNDN); - bf_mul(r, r, U, prec1, BF_RNDN); - bf_add_si(r, r, 1, prec1, BF_RNDN); - } - bf_delete(U); - } - bf_delete(T); - - /* undo the range reduction */ - for(i = 0; i < K; i++) { - bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); - } - - /* undo the argument reduction */ - bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); - - return BF_ST_INEXACT; -} - -/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ -static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, - const bf_t *a_low, const bf_t *a_high, - limb_t prec, bf_flags_t flags) -{ - bf_t T_s, *T = &T_s; - bf_t log2_s, *log2 = &log2_s; - slimb_t e_min, e_max; - - if (a_high->expn <= 0) - return 0; - - e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_max + 3; - if (flags & BF_FLAG_SUBNORMAL) - e_min -= (prec - 1); - - bf_init(s, T); - bf_init(s, log2); - bf_const_log2(log2, LIMB_BITS, BF_RNDU); - bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); - /* a_low > e_max * log(2) implies exp(a) > e_max */ - if (bf_cmp_lt(T, a_low) > 0) { - /* overflow */ - bf_delete(T); - bf_delete(log2); - return bf_set_overflow(r, 0, prec, flags); - } - /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ - bf_const_log2(log2, LIMB_BITS, BF_RNDD); - bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); - if (bf_cmp_lt(a_high, T)) { - int rnd_mode = flags & BF_RND_MASK; - - /* underflow */ - bf_delete(T); - bf_delete(log2); - if (rnd_mode == BF_RNDU) { - /* set the smallest value */ - bf_set_ui(r, 1); - r->expn = e_min; - } else { - bf_set_zero(r, 0); - } - return BF_ST_UNDERFLOW | BF_ST_INEXACT; - } - bf_delete(log2); - bf_delete(T); - return 0; -} - -int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - int ret; - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF) { - if (a->sign) - bf_set_zero(r, 0); - else - bf_set_inf(r, 0); - } else { - bf_set_ui(r, 1); - } - return 0; - } - - ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); - if (ret) - return ret; - if (a->expn < 0 && (-a->expn) >= (prec + 2)) { - /* small argument case: result = 1 + epsilon * sign(x) */ - bf_set_ui(r, 1); - return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); - } - - return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); -} - -static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - bf_t U_s, *U = &U_s; - bf_t V_s, *V = &V_s; - slimb_t n, prec1, l, i, K; - - assert(r != a); - - bf_init(s, T); - /* argument reduction 1 */ - /* T=a*2^n with 2/3 <= T <= 4/3 */ - { - bf_t U_s, *U = &U_s; - bf_set(T, a); - n = T->expn; - T->expn = 0; - /* U= ~ 2/3 */ - bf_init(s, U); - bf_set_ui(U, 0xaaaaaaaa); - U->expn = 0; - if (bf_cmp_lt(T, U)) { - T->expn++; - n--; - } - bf_delete(U); - } - // printf("n=%ld\n", n); - // bf_print_str("T", T); - - /* XXX: precision analysis */ - /* number of iterations for argument reduction 2 */ - K = bf_isqrt((prec + 1) / 2); - /* order of Taylor expansion */ - l = prec / (2 * K) + 1; - /* precision of the intermediate computations */ - prec1 = prec + K + 2 * l + 32; - - bf_init(s, U); - bf_init(s, V); - - /* Note: cancellation occurs here, so we use more precision (XXX: - reduce the precision by computing the exact cancellation) */ - bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN); - - /* argument reduction 2 */ - for(i = 0; i < K; i++) { - /* T = T / (1 + sqrt(1 + T)) */ - bf_add_si(U, T, 1, prec1, BF_RNDN); - bf_sqrt(V, U, prec1, BF_RNDF); - bf_add_si(U, V, 1, prec1, BF_RNDN); - bf_div(T, T, U, prec1, BF_RNDN); - } - - { - bf_t Y_s, *Y = &Y_s; - bf_t Y2_s, *Y2 = &Y2_s; - bf_init(s, Y); - bf_init(s, Y2); - - /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x) - = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1) - with Y=Y^2 - = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...))) - */ - bf_add_si(Y, T, 2, prec1, BF_RNDN); - bf_div(Y, T, Y, prec1, BF_RNDN); - - bf_mul(Y2, Y, Y, prec1, BF_RNDN); - bf_set_ui(r, 0); - for(i = l; i >= 1; i--) { - bf_set_ui(U, 1); - bf_set_ui(V, 2 * i + 1); - bf_div(U, U, V, prec1, BF_RNDN); - bf_add(r, r, U, prec1, BF_RNDN); - bf_mul(r, r, Y2, prec1, BF_RNDN); - } - bf_add_si(r, r, 1, prec1, BF_RNDN); - bf_mul(r, r, Y, prec1, BF_RNDN); - bf_delete(Y); - bf_delete(Y2); - } - bf_delete(V); - bf_delete(U); - - /* multiplication by 2 for the Taylor expansion and undo the - argument reduction 2*/ - bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ); - - /* undo the argument reduction 1 */ - bf_const_log2(T, prec1, BF_RNDF); - bf_mul_si(T, T, n, prec1, BF_RNDN); - bf_add(r, r, T, prec1, BF_RNDN); - - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - if (a->sign) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_inf(r, 0); - return 0; - } - } else { - bf_set_inf(r, 1); - return 0; - } - } - if (a->sign) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } - bf_init(s, T); - bf_set_ui(T, 1); - if (bf_cmp_eq(a, T)) { - bf_set_zero(r, 0); - bf_delete(T); - return 0; - } - bf_delete(T); - - return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL); -} - -/* x and y finite and x > 0 */ -static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - const bf_t *y = opaque; - bf_t T_s, *T = &T_s; - limb_t prec1; - - bf_init(s, T); - /* XXX: proof for the added precision */ - prec1 = prec + 32; - bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); - bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); - if (bf_is_nan(T)) - bf_set_nan(r); - else - bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ - bf_delete(T); - return BF_ST_INEXACT; -} - -/* x and y finite, x > 0, y integer and y fits on one limb */ -static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - const bf_t *y = opaque; - bf_t T_s, *T = &T_s; - limb_t prec1; - int ret; - slimb_t y1; - - bf_get_limb(&y1, y, 0); - if (y1 < 0) - y1 = -y1; - /* XXX: proof for the added precision */ - prec1 = prec + ceil_log2(y1) * 2 + 8; - ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); - if (y->sign) { - bf_init(s, T); - bf_set_ui(T, 1); - ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); - bf_delete(T); - } - return ret; -} - -/* x must be a finite non zero float. Return TRUE if there is a - floating point number r such as x=r^(2^n) and return this floating - point number 'r'. Otherwise return FALSE and r is undefined. */ -static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - slimb_t e, i, er; - limb_t v; - - /* x = m*2^e with m odd integer */ - e = bf_get_exp_min(x); - /* fast check on the exponent */ - if (n > (LIMB_BITS - 1)) { - if (e != 0) - return FALSE; - er = 0; - } else { - if ((e & (((limb_t)1 << n) - 1)) != 0) - return FALSE; - er = e >> n; - } - /* every perfect odd square = 1 modulo 8 */ - v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e); - if ((v & 7) != 1) - return FALSE; - - bf_init(s, T); - bf_set(T, x); - T->expn -= e; - for(i = 0; i < n; i++) { - if (i != 0) - bf_set(T, r); - if (bf_sqrtrem(r, NULL, T) != 0) - return FALSE; - } - r->expn += er; - return TRUE; -} - -/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */ -int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - bf_t ytmp_s; - BOOL y_is_int, y_is_odd; - int r_sign, ret, rnd_mode; - slimb_t y_emin; - - if (x->len == 0 || y->len == 0) { - if (y->expn == BF_EXP_ZERO) { - /* pow(x, 0) = 1 */ - bf_set_ui(r, 1); - } else if (x->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else { - int cmp_x_abs_1; - bf_set_ui(r, 1); - cmp_x_abs_1 = bf_cmpu(x, r); - if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && - (y->expn >= BF_EXP_INF)) { - bf_set_nan(r); - } else if (cmp_x_abs_1 == 0 && - (!x->sign || y->expn != BF_EXP_NAN)) { - /* pow(1, y) = 1 even if y = NaN */ - /* pow(-1, +/-inf) = 1 */ - } else if (y->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (y->expn == BF_EXP_INF) { - if (y->sign == (cmp_x_abs_1 > 0)) { - bf_set_zero(r, 0); - } else { - bf_set_inf(r, 0); - } - } else { - y_emin = bf_get_exp_min(y); - y_is_odd = (y_emin == 0); - if (y->sign == (x->expn == BF_EXP_ZERO)) { - bf_set_inf(r, y_is_odd & x->sign); - if (y->sign) { - /* pow(0, y) with y < 0 */ - return BF_ST_DIVIDE_ZERO; - } - } else { - bf_set_zero(r, y_is_odd & x->sign); - } - } - } - return 0; - } - bf_init(s, T); - bf_set(T, x); - y_emin = bf_get_exp_min(y); - y_is_int = (y_emin >= 0); - rnd_mode = flags & BF_RND_MASK; - if (x->sign) { - if (!y_is_int) { - bf_set_nan(r); - bf_delete(T); - return BF_ST_INVALID_OP; - } - y_is_odd = (y_emin == 0); - r_sign = y_is_odd; - /* change the directed rounding mode if the sign of the result - is changed */ - if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU)) - flags ^= 1; - bf_neg(T); - } else { - r_sign = 0; - } - - bf_set_ui(r, 1); - if (bf_cmp_eq(T, r)) { - /* abs(x) = 1: nothing more to do */ - ret = 0; - } else { - /* check the overflow/underflow cases */ - { - bf_t al_s, *al = &al_s; - bf_t ah_s, *ah = &ah_s; - limb_t precl = LIMB_BITS; - - bf_init(s, al); - bf_init(s, ah); - /* compute bounds of log(abs(x)) * y with a low precision */ - /* XXX: compute bf_log() once */ - /* XXX: add a fast test before this slow test */ - bf_log(al, T, precl, BF_RNDD); - bf_log(ah, T, precl, BF_RNDU); - bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); - bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); - ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); - bf_delete(al); - bf_delete(ah); - if (ret) - goto done; - } - - if (y_is_int) { - slimb_t T_bits, e; - int_pow: - T_bits = T->expn - bf_get_exp_min(T); - if (T_bits == 1) { - /* pow(2^b, y) = 2^(b*y) */ - bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); - bf_get_limb(&e, T, 0); - bf_set_ui(r, 1); - ret = bf_mul_2exp(r, e, prec, flags); - } else if (prec == BF_PREC_INF) { - slimb_t y1; - /* specific case for infinite precision (integer case) */ - bf_get_limb(&y1, y, 0); - assert(!y->sign); - /* x must be an integer, so abs(x) >= 2 */ - if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { - bf_delete(T); - return bf_set_overflow(r, 0, BF_PREC_INF, flags); - } - ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); - } else { - if (y->expn <= 31) { - /* small enough power: use exponentiation in all cases */ - } else if (y->sign) { - /* cannot be exact */ - goto general_case; - } else { - if (rnd_mode == BF_RNDF) - goto general_case; /* no need to track exact results */ - /* see if the result has a chance to be exact: - if x=a*2^b (a odd), x^y=a^y*2^(b*y) - x^y needs a precision of at least floor_log2(a)*y bits - */ - bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); - bf_get_limb(&e, r, 0); - if (prec < e) - goto general_case; - } - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); - } - } else { - if (rnd_mode != BF_RNDF) { - bf_t *y1; - if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { - /* the problem is reduced to a power to an integer */ -#if 0 - printf("\nn=%" PRId64 "\n", -(int64_t)y_emin); - bf_print_str("T", T); - bf_print_str("r", r); -#endif - bf_set(T, r); - y1 = &ytmp_s; - y1->tab = y->tab; - y1->len = y->len; - y1->sign = y->sign; - y1->expn = y->expn - y_emin; - y = y1; - goto int_pow; - } - } - general_case: - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); - } - } - done: - bf_delete(T); - r->sign = r_sign; - return ret; -} - -/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */ -static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - bf_init(s, T); - bf_set(T, x); - bf_mul(r, T, T, prec1, BF_RNDN); - bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ); - bf_add(T, T, r, prec1, BF_RNDN); - bf_neg(T); - bf_sqrt(r, T, prec1, BF_RNDF); - bf_delete(T); -} - -static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) -{ - bf_context_t *s1 = a->ctx; - bf_t T_s, *T = &T_s; - bf_t U_s, *U = &U_s; - bf_t r_s, *r = &r_s; - slimb_t K, prec1, i, l, mod, prec2; - int is_neg; - - assert(c != a && s != a); - - bf_init(s1, T); - bf_init(s1, U); - bf_init(s1, r); - - /* XXX: precision analysis */ - K = bf_isqrt(prec / 2); - l = prec / (2 * K) + 1; - prec1 = prec + 2 * K + l + 8; - - /* after the modulo reduction, -pi/4 <= T <= pi/4 */ - if (a->expn <= -1) { - /* abs(a) <= 0.25: no modulo reduction needed */ - bf_set(T, a); - mod = 0; - } else { - slimb_t cancel; - cancel = 0; - for(;;) { - prec2 = prec1 + a->expn + cancel; - bf_const_pi(U, prec2, BF_RNDF); - bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); - bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); - // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); - if (mod == 0 || (T->expn != BF_EXP_ZERO && - (T->expn + prec2) >= (prec1 - 1))) - break; - /* increase the number of bits until the precision is good enough */ - cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2); - } - mod &= 3; - } - - is_neg = T->sign; - - /* compute cosm1(x) = cos(x) - 1 */ - bf_mul(T, T, T, prec1, BF_RNDN); - bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ); - - /* Taylor expansion: - -x^2/2 + x^4/4! - x^6/6! + ... - */ - bf_set_ui(r, 1); - for(i = l ; i >= 1; i--) { - bf_set_ui(U, 2 * i - 1); - bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ); - bf_div(U, T, U, prec1, BF_RNDN); - bf_mul(r, r, U, prec1, BF_RNDN); - bf_neg(r); - if (i != 1) - bf_add_si(r, r, 1, prec1, BF_RNDN); - } - bf_delete(U); - - /* undo argument reduction: - cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2) - */ - for(i = 0; i < K; i++) { - bf_mul(T, r, r, prec1, BF_RNDN); - bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); - bf_add(r, r, T, prec1, BF_RNDN); - bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); - } - bf_delete(T); - - if (c) { - if ((mod & 1) == 0) { - bf_add_si(c, r, 1, prec1, BF_RNDN); - } else { - bf_sqrt_sin(c, r, prec1); - c->sign = is_neg ^ 1; - } - c->sign ^= mod >> 1; - } - if (s) { - if ((mod & 1) == 0) { - bf_sqrt_sin(s, r, prec1); - s->sign = is_neg; - } else { - bf_add_si(s, r, 1, prec1, BF_RNDN); - } - s->sign ^= mod >> 1; - } - bf_delete(r); - return BF_ST_INEXACT; -} - -static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - return bf_sincos(NULL, r, a, prec); -} - -int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_ui(r, 1); - return 0; - } - } - - /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + - O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ - if (a->expn < 0) { - slimb_t e; - e = 2 * a->expn - 1; - if (e < -(prec + 2)) { - bf_set_ui(r, 1); - return bf_add_epsilon(r, r, e, 1, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); -} - -static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - return bf_sincos(r, NULL, a, prec); -} - -int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - - /* small argument case: result = x+r(x) with r(x) = -x^3/6 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 2); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); -} - -static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - limb_t prec1; - - /* XXX: precision analysis */ - prec1 = prec + 8; - bf_init(s, T); - bf_sincos(r, T, a, prec1); - bf_div(r, r, T, prec1, BF_RNDF); - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - - /* small argument case: result = x+r(x) with r(x) = x^3/3 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 1); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); -} - -/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to - avoid cancellation) */ -static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, - void *opaque) -{ - bf_context_t *s = r->ctx; - BOOL add_pi2 = (BOOL)(intptr_t)opaque; - bf_t T_s, *T = &T_s; - bf_t U_s, *U = &U_s; - bf_t V_s, *V = &V_s; - bf_t X2_s, *X2 = &X2_s; - int cmp_1; - slimb_t prec1, i, K, l; - - /* XXX: precision analysis */ - K = bf_isqrt((prec + 1) / 2); - l = prec / (2 * K) + 1; - prec1 = prec + K + 2 * l + 32; - // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); - - bf_init(s, T); - cmp_1 = (a->expn >= 1); /* a >= 1 */ - if (cmp_1) { - bf_set_ui(T, 1); - bf_div(T, T, a, prec1, BF_RNDN); - } else { - bf_set(T, a); - } - - /* abs(T) <= 1 */ - - /* argument reduction */ - - bf_init(s, U); - bf_init(s, V); - bf_init(s, X2); - for(i = 0; i < K; i++) { - /* T = T / (1 + sqrt(1 + T^2)) */ - bf_mul(U, T, T, prec1, BF_RNDN); - bf_add_si(U, U, 1, prec1, BF_RNDN); - bf_sqrt(V, U, prec1, BF_RNDN); - bf_add_si(V, V, 1, prec1, BF_RNDN); - bf_div(T, T, V, prec1, BF_RNDN); - } - - /* Taylor series: - x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1) - */ - bf_mul(X2, T, T, prec1, BF_RNDN); - bf_set_ui(r, 0); - for(i = l; i >= 1; i--) { - bf_set_si(U, 1); - bf_set_ui(V, 2 * i + 1); - bf_div(U, U, V, prec1, BF_RNDN); - bf_neg(r); - bf_add(r, r, U, prec1, BF_RNDN); - bf_mul(r, r, X2, prec1, BF_RNDN); - } - bf_neg(r); - bf_add_si(r, r, 1, prec1, BF_RNDN); - bf_mul(r, r, T, prec1, BF_RNDN); - - /* undo the argument reduction */ - bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ); - - bf_delete(U); - bf_delete(V); - bf_delete(X2); - - i = add_pi2; - if (cmp_1 > 0) { - /* undo the inversion : r = sign(a)*PI/2 - r */ - bf_neg(r); - i += 1 - 2 * a->sign; - } - /* add i*(pi/2) with -1 <= i <= 2 */ - if (i != 0) { - bf_const_pi(T, prec1, BF_RNDF); - if (i != 2) - bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ); - T->sign = (i < 0); - bf_add(r, T, r, prec1, BF_RNDN); - } - - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - /* -PI/2 or PI/2 */ - bf_const_pi_signed(r, a->sign, prec, flags); - bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - bf_delete(T); - if (res == 0) { - /* short cut: abs(a) == 1 -> +/-pi/4 */ - bf_const_pi_signed(r, a->sign, prec, flags); - bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } - - /* small argument case: result = x+r(x) with r(x) = -x^3/3 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 1); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE); -} - -static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - const bf_t *x = opaque; - bf_t T_s, *T = &T_s; - limb_t prec1; - int ret; - - if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } - - /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */ - bf_init(s, T); - prec1 = prec + 32; - if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) { - bf_set_ui(T, 1); - T->sign = y->sign ^ x->sign; - } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) { - bf_set_zero(T, y->sign ^ x->sign); - } else { - bf_div(T, y, x, prec1, BF_RNDF); - } - ret = bf_atan(r, T, prec1, BF_RNDF); - - if (x->sign) { - /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */ - bf_const_pi(T, prec1, BF_RNDF); - T->sign = y->sign; - bf_add(r, r, T, prec1, BF_RNDN); - ret |= BF_ST_INEXACT; - } - - bf_delete(T); - return ret; -} - -int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, - limb_t prec, bf_flags_t flags) -{ - return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x); -} - -static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) -{ - bf_context_t *s = r->ctx; - BOOL is_acos = (BOOL)(intptr_t)opaque; - bf_t T_s, *T = &T_s; - limb_t prec1, prec2; - - /* asin(x) = atan(x/sqrt(1-x^2)) - acos(x) = pi/2 - asin(x) */ - prec1 = prec + 8; - /* increase the precision in x^2 to compensate the cancellation in - (1-x^2) if x is close to 1 */ - /* XXX: use less precision when possible */ - if (a->expn >= 0) - prec2 = BF_PREC_INF; - else - prec2 = prec1; - bf_init(s, T); - bf_mul(T, a, a, prec2, BF_RNDN); - bf_neg(T); - bf_add_si(T, T, 1, prec2, BF_RNDN); - - bf_sqrt(r, T, prec1, BF_RNDN); - bf_div(T, a, r, prec1, BF_RNDN); - if (is_acos) - bf_neg(T); - bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos); - bf_delete(T); - return BF_ST_INEXACT; -} - -int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_set_zero(r, a->sign); - return 0; - } - } - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - bf_delete(T); - if (res > 0) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } - - /* small argument case: result = x+r(x) with r(x) = x^3/6 + - O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ - if (a->expn < 0) { - slimb_t e; - e = sat_add(2 * a->expn, a->expn - 2); - if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { - bf_set(r, a); - return bf_add_epsilon(r, r, e, a->sign, prec, flags); - } - } - - return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE); -} - -int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = r->ctx; - bf_t T_s, *T = &T_s; - int res; - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bf_const_pi(r, prec, flags); - bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); - return BF_ST_INEXACT; - } - } - bf_init(s, T); - bf_set_ui(T, 1); - res = bf_cmpu(a, T); - bf_delete(T); - if (res > 0) { - bf_set_nan(r); - return BF_ST_INVALID_OP; - } else if (res == 0 && a->sign == 0) { - bf_set_zero(r, 0); - return 0; - } - - return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); -} - -/***************************************************************/ -/* decimal floating point numbers */ - -#ifdef USE_BF_DEC - -#define adddq(r1, r0, a1, a0) \ - do { \ - limb_t __t = r0; \ - r0 += (a0); \ - r1 += (a1) + (r0 < __t); \ - } while (0) - -#define subdq(r1, r0, a1, a0) \ - do { \ - limb_t __t = r0; \ - r0 -= (a0); \ - r1 -= (a1) + (r0 > __t); \ - } while (0) - -#if LIMB_BITS == 64 - -/* Note: we assume __int128 is available */ -#define muldq(r1, r0, a, b) \ - do { \ - unsigned __int128 __t; \ - __t = (unsigned __int128)(a) * (unsigned __int128)(b); \ - r0 = __t; \ - r1 = __t >> 64; \ - } while (0) - -#define divdq(q, r, a1, a0, b) \ - do { \ - unsigned __int128 __t; \ - limb_t __b = (b); \ - __t = ((unsigned __int128)(a1) << 64) | (a0); \ - q = __t / __b; \ - r = __t % __b; \ - } while (0) - -#else - -#define muldq(r1, r0, a, b) \ - do { \ - uint64_t __t; \ - __t = (uint64_t)(a) * (uint64_t)(b); \ - r0 = __t; \ - r1 = __t >> 32; \ - } while (0) - -#define divdq(q, r, a1, a0, b) \ - do { \ - uint64_t __t; \ - limb_t __b = (b); \ - __t = ((uint64_t)(a1) << 32) | (a0); \ - q = __t / __b; \ - r = __t % __b; \ - } while (0) - -#endif /* LIMB_BITS != 64 */ - -#if LIMB_DIGITS == 19 - -/* WARNING: hardcoded for b = 1e19. It is assumed that: - 0 <= a1 < 2^63 */ -#define divdq_base(q, r, a1, a0)\ -do {\ - uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ - __a0 = a0;\ - __a1 = a1;\ - __t0 = __a1;\ - __t0 = shld(__t0, __a0, 1);\ - muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ - muldq(__t1, __t0, q, __b);\ - subdq(__a1, __a0, __t1, __t0);\ - subdq(__a1, __a0, 1, __b * 2); \ - __t0 = (slimb_t)__a1 >> 1; \ - q += 2 + __t0;\ - adddq(__a1, __a0, 0, __b & __t0);\ - q += __a1; \ - __a0 += __b & __a1; \ - r = __a0;\ -} while(0) - -#elif LIMB_DIGITS == 9 - -/* WARNING: hardcoded for b = 1e9. It is assumed that: - 0 <= a1 < 2^29 */ -#define divdq_base(q, r, a1, a0)\ -do {\ - uint32_t __t0, __t1, __b = BF_DEC_BASE; \ - __t0 = a1;\ - __t1 = a0;\ - __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ - muldq(q, __t1, __t0, 2305843009U);\ - r = a0 - q * __b;\ - __t1 = (r >= __b);\ - q += __t1;\ - if (__t1)\ - r -= __b;\ -} while(0) - -#endif - -/* fast integer division by a fixed constant */ - -typedef struct FastDivData { - limb_t m1; /* multiplier */ - int8_t shift1; - int8_t shift2; -} FastDivData; - -/* From "Division by Invariant Integers using Multiplication" by - Torborn Granlund and Peter L. Montgomery */ -/* d must be != 0 */ -static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) -{ - int l; - limb_t q, r, m1; - if (d == 1) - l = 0; - else - l = 64 - clz64(d - 1); - divdq(q, r, ((limb_t)1 << l) - d, 0, d); - (void)r; - m1 = q + 1; - // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1); - s->m1 = m1; - s->shift1 = l; - if (s->shift1 > 1) - s->shift1 = 1; - s->shift2 = l - 1; - if (s->shift2 < 0) - s->shift2 = 0; -} - -static inline limb_t fast_udiv(limb_t a, const FastDivData *s) -{ - limb_t t0, t1; - muldq(t1, t0, s->m1, a); - t0 = (a - t1) >> s->shift1; - return (t1 + t0) >> s->shift2; -} - -/* contains 10^i */ -const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { - 1U, - 10U, - 100U, - 1000U, - 10000U, - 100000U, - 1000000U, - 10000000U, - 100000000U, - 1000000000U, -#if LIMB_BITS == 64 - 10000000000U, - 100000000000U, - 1000000000000U, - 10000000000000U, - 100000000000000U, - 1000000000000000U, - 10000000000000000U, - 100000000000000000U, - 1000000000000000000U, - 10000000000000000000U, -#endif -}; - -/* precomputed from fast_udiv_init(10^i) */ -static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { -#if LIMB_BITS == 32 - { 0x00000001, 0, 0 }, - { 0x9999999a, 1, 3 }, - { 0x47ae147b, 1, 6 }, - { 0x0624dd30, 1, 9 }, - { 0xa36e2eb2, 1, 13 }, - { 0x4f8b588f, 1, 16 }, - { 0x0c6f7a0c, 1, 19 }, - { 0xad7f29ac, 1, 23 }, - { 0x5798ee24, 1, 26 }, - { 0x12e0be83, 1, 29 }, -#else - { 0x0000000000000001, 0, 0 }, - { 0x999999999999999a, 1, 3 }, - { 0x47ae147ae147ae15, 1, 6 }, - { 0x0624dd2f1a9fbe77, 1, 9 }, - { 0xa36e2eb1c432ca58, 1, 13 }, - { 0x4f8b588e368f0847, 1, 16 }, - { 0x0c6f7a0b5ed8d36c, 1, 19 }, - { 0xad7f29abcaf48579, 1, 23 }, - { 0x5798ee2308c39dfa, 1, 26 }, - { 0x12e0be826d694b2f, 1, 29 }, - { 0xb7cdfd9d7bdbab7e, 1, 33 }, - { 0x5fd7fe17964955fe, 1, 36 }, - { 0x19799812dea11198, 1, 39 }, - { 0xc25c268497681c27, 1, 43 }, - { 0x6849b86a12b9b01f, 1, 46 }, - { 0x203af9ee756159b3, 1, 49 }, - { 0xcd2b297d889bc2b7, 1, 53 }, - { 0x70ef54646d496893, 1, 56 }, - { 0x2725dd1d243aba0f, 1, 59 }, - { 0xd83c94fb6d2ac34d, 1, 63 }, -#endif -}; - -/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ -static inline limb_t fast_shr_dec(limb_t a, int shift) -{ - return fast_udiv(a, &mp_pow_div[shift]); -} - -/* division and remainder by 10^shift */ -#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] - -limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, - mp_size_t n, limb_t carry) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, a, v; - - k=carry; - for(i=0;i v; - if (k) - a += base; - res[i] = a; - } - return k; -} - -limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, v, a; - - k=b; - for(i=0;i v; - if (k) - a += base; - tab[i]=a; - if (k == 0) - break; - } - return k; -} - -/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ -limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b, limb_t l) -{ - mp_size_t i; - limb_t t0, t1, r; - - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - divdq_base(l, r, t1, t0); - tabr[i] = r; - } - return l; -} - -/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add - to the high word */ -limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b) -{ - mp_size_t i; - limb_t l, t0, t1, r; - - l = 0; - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - adddq(t1, t0, 0, tabr[i]); - divdq_base(l, r, t1, t0); - tabr[i] = r; - } - return l; -} - -/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to - substract to the high word. */ -limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t l, t0, t1, r, a, v, c; - - /* XXX: optimize */ - l = 0; - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - divdq_base(l, r, t1, t0); - v = tabr[i]; - a = v - r; - c = a > v; - if (c) - a += base; - /* never bigger than base because r = 0 when l = base - 1 */ - l += c; - tabr[i] = a; - } - return l; -} - -/* size of the result : op1_size + op2_size. */ -void mp_mul_basecase_dec(limb_t *result, - const limb_t *op1, mp_size_t op1_size, - const limb_t *op2, mp_size_t op2_size) -{ - mp_size_t i; - limb_t r; - - result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); - - for(i=1;i> 1; - if (r) - r = base_div2; - for(i = na - 1; i >= 0; i--) { - t0 = taba[i]; - tabr[i] = (t0 >> 1) + r; - r = 0; - if (t0 & 1) - r = base_div2; - } - if (r) - r = 1; - } else -#endif - if (na >= UDIV1NORM_THRESHOLD) { - shift = clz(b); - if (shift == 0) { - /* normalized case: b >= 2^(LIMB_BITS-1) */ - limb_t b_inv; - b_inv = udiv1norm_init(b); - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - q = udiv1norm(&r, t1, t0, b, b_inv); - tabr[i] = q; - } - } else { - limb_t b_inv; - b <<= shift; - b_inv = udiv1norm_init(b); - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); - t0 <<= shift; - q = udiv1norm(&r, t1, t0, b, b_inv); - r >>= shift; - tabr[i] = q; - } - } - } else { - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - divdq(q, r, t1, t0, b); - tabr[i] = q; - } - } - return r; -} - -static __maybe_unused void mp_print_str_dec(const char *str, - const limb_t *tab, slimb_t n) -{ - slimb_t i; - printf("%s=", str); - for(i = n - 1; i >= 0; i--) { - if (i != n - 1) - printf("_"); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); - } - printf("\n"); -} - -static __maybe_unused void mp_print_str_h_dec(const char *str, - const limb_t *tab, slimb_t n, - limb_t high) -{ - slimb_t i; - printf("%s=", str); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); - for(i = n - 1; i >= 0; i--) { - printf("_"); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); - } - printf("\n"); -} - -//#define DEBUG_DIV_SLOW - -#define DIV_STATIC_ALLOC_LEN 16 - -/* return q = a / b and r = a % b. - - taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] - must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] - >= B / 2. - - The remainder is is returned in taba and contains nb libms. tabq - contains na - nb + 1 limbs. No overlap is permitted. - - Running time of the standard method: (na - nb + 1) * nb - Return 0 if OK, -1 if memory alloc error -*/ -/* XXX: optimize */ -static int mp_div_dec(bf_context_t *s, limb_t *tabq, - limb_t *taba, mp_size_t na, - const limb_t *tabb1, mp_size_t nb) -{ - limb_t base = BF_DEC_BASE; - limb_t r, mult, t0, t1, a, c, q, v, *tabb; - mp_size_t i, j; - limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; - -#ifdef DEBUG_DIV_SLOW - mp_print_str_dec("a", taba, na); - mp_print_str_dec("b", tabb1, nb); -#endif - - /* normalize tabb */ - r = tabb1[nb - 1]; - assert(r != 0); - i = na - nb; - if (r >= BF_DEC_BASE / 2) { - mult = 1; - tabb = (limb_t *)tabb1; - q = 1; - for(j = nb - 1; j >= 0; j--) { - if (taba[i + j] != tabb[j]) { - if (taba[i + j] < tabb[j]) - q = 0; - break; - } - } - tabq[i] = q; - if (q) { - mp_sub_dec(taba + i, taba + i, tabb, nb, 0); - } - i--; - } else { - mult = base / (r + 1); - if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { - tabb = static_tabb; - } else { - tabb = bf_malloc(s, sizeof(limb_t) * nb); - if (!tabb) - return -1; - } - mp_mul1_dec(tabb, tabb1, nb, mult, 0); - taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); - } - -#ifdef DEBUG_DIV_SLOW - printf("mult=" FMT_LIMB "\n", mult); - mp_print_str_dec("a_norm", taba, na + 1); - mp_print_str_dec("b_norm", tabb, nb); -#endif - - for(; i >= 0; i--) { - if (unlikely(taba[i + nb] >= tabb[nb - 1])) { - /* XXX: check if it is really possible */ - q = base - 1; - } else { - muldq(t1, t0, taba[i + nb], base); - adddq(t1, t0, 0, taba[i + nb - 1]); - divdq(q, r, t1, t0, tabb[nb - 1]); - } - // printf("i=%d q1=%ld\n", i, q); - - r = mp_sub_mul1_dec(taba + i, tabb, nb, q); - // mp_dump("r1", taba + i, nb, bd); - // printf("r2=%ld\n", r); - - v = taba[i + nb]; - a = v - r; - c = a > v; - if (c) - a += base; - taba[i + nb] = a; - - if (c != 0) { - /* negative result */ - for(;;) { - q--; - c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); - /* propagate carry and test if positive result */ - if (c != 0) { - if (++taba[i + nb] == base) { - break; - } - } - } - } - tabq[i] = q; - } - -#ifdef DEBUG_DIV_SLOW - mp_print_str_dec("q", tabq, na - nb + 1); - mp_print_str_dec("r", taba, nb); -#endif - - /* remove the normalization */ - if (mult != 1) { - mp_div1_dec(taba, taba, nb, mult, 0); - if (unlikely(tabb != static_tabb)) - bf_free(s, tabb); - } - return 0; -} - -/* divide by 10^shift */ -static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, - limb_t shift, limb_t high) -{ - mp_size_t i; - limb_t l, a, q, r; - - assert(shift >= 1 && shift < LIMB_DIGITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - fast_shr_rem_dec(q, r, a, shift); - tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; - l = r; - } - return l; -} - -/* multiply by 10^shift */ -static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, - limb_t shift, limb_t low) -{ - mp_size_t i; - limb_t l, a, q, r; - - assert(shift >= 1 && shift < LIMB_DIGITS); - l = low; - for(i = 0; i < n; i++) { - a = tab[i]; - fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); - tab_r[i] = r * mp_pow_dec[shift] + l; - l = q; - } - return l; -} - -static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) -{ - int k; - dlimb_t a, b, r; - limb_t taba1[2], s, r0, r1; - - /* convert to binary and normalize */ - a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; - k = clz(a >> LIMB_BITS) & ~1; - b = a << k; - taba1[0] = b; - taba1[1] = b >> LIMB_BITS; - mp_sqrtrem2(&s, taba1); - s >>= (k >> 1); - /* convert the remainder back to decimal */ - r = a - (dlimb_t)s * (dlimb_t)s; - divdq_base(r1, r0, r >> LIMB_BITS, r); - taba[0] = r0; - tabs[0] = s; - return r1; -} - -//#define DEBUG_SQRTREM_DEC - -/* tmp_buf must contain (n / 2 + 1 limbs) */ -static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, - limb_t *tmp_buf) -{ - limb_t l, h, rh, ql, qh, c, i; - - if (n == 1) - return mp_sqrtrem2_dec(tabs, taba); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("a", taba, 2 * n); -#endif - l = n / 2; - h = n - l; - qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("s1", tabs + l, h); - mp_print_str_h_dec("r1", taba + 2 * l, h, qh); - mp_print_str_h_dec("r2", taba + l, n, qh); -#endif - - /* the remainder is in taba + 2 * l. Its high bit is in qh */ - if (qh) { - mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); - } - /* instead of dividing by 2*s, divide by s (which is normalized) - and update q and r */ - mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); - qh += tmp_buf[l]; - for(i = 0; i < l; i++) - tabs[i] = tmp_buf[i]; - ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); - qh = qh >> 1; /* 0 or 1 */ - if (ql) - rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); - else - rh = 0; -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_h_dec("q", tabs, l, qh); - mp_print_str_h_dec("u", taba + l, h, rh); -#endif - - mp_add_ui_dec(tabs + l, qh, h); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("s2", tabs, n); -#endif - - /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ - /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ - if (qh) { - c = qh; - } else { - mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); - c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); - } - rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); - if ((slimb_t)rh < 0) { - mp_sub_ui_dec(tabs, 1, n); - rh += mp_add_mul1_dec(taba, tabs, n, 2); - rh += mp_add_ui_dec(taba, 1, n); - } - return rh; -} - -/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, - r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n - limbs. r is returned in the lower n limbs of taba. Its r[n] is the - returned value of the function. */ -int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) -{ - limb_t tmp_buf1[8]; - limb_t *tmp_buf; - mp_size_t n2; - n2 = n / 2 + 1; - if (n2 <= countof(tmp_buf1)) { - tmp_buf = tmp_buf1; - } else { - tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); - if (!tmp_buf) - return -1; - } - taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); - if (tmp_buf != tmp_buf1) - bf_free(s, tmp_buf); - return 0; -} - -/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ -static int clz_dec(limb_t a) -{ - if (a == 0) - return LIMB_DIGITS; - switch(LIMB_BITS - 1 - clz(a)) { - case 0: /* 1-1 */ - return LIMB_DIGITS - 1; - case 1: /* 2-3 */ - return LIMB_DIGITS - 1; - case 2: /* 4-7 */ - return LIMB_DIGITS - 1; - case 3: /* 8-15 */ - if (a < 10) - return LIMB_DIGITS - 1; - else - return LIMB_DIGITS - 2; - case 4: /* 16-31 */ - return LIMB_DIGITS - 2; - case 5: /* 32-63 */ - return LIMB_DIGITS - 2; - case 6: /* 64-127 */ - if (a < 100) - return LIMB_DIGITS - 2; - else - return LIMB_DIGITS - 3; - case 7: /* 128-255 */ - return LIMB_DIGITS - 3; - case 8: /* 256-511 */ - return LIMB_DIGITS - 3; - case 9: /* 512-1023 */ - if (a < 1000) - return LIMB_DIGITS - 3; - else - return LIMB_DIGITS - 4; - case 10: /* 1024-2047 */ - return LIMB_DIGITS - 4; - case 11: /* 2048-4095 */ - return LIMB_DIGITS - 4; - case 12: /* 4096-8191 */ - return LIMB_DIGITS - 4; - case 13: /* 8192-16383 */ - if (a < 10000) - return LIMB_DIGITS - 4; - else - return LIMB_DIGITS - 5; - case 14: /* 16384-32767 */ - return LIMB_DIGITS - 5; - case 15: /* 32768-65535 */ - return LIMB_DIGITS - 5; - case 16: /* 65536-131071 */ - if (a < 100000) - return LIMB_DIGITS - 5; - else - return LIMB_DIGITS - 6; - case 17: /* 131072-262143 */ - return LIMB_DIGITS - 6; - case 18: /* 262144-524287 */ - return LIMB_DIGITS - 6; - case 19: /* 524288-1048575 */ - if (a < 1000000) - return LIMB_DIGITS - 6; - else - return LIMB_DIGITS - 7; - case 20: /* 1048576-2097151 */ - return LIMB_DIGITS - 7; - case 21: /* 2097152-4194303 */ - return LIMB_DIGITS - 7; - case 22: /* 4194304-8388607 */ - return LIMB_DIGITS - 7; - case 23: /* 8388608-16777215 */ - if (a < 10000000) - return LIMB_DIGITS - 7; - else - return LIMB_DIGITS - 8; - case 24: /* 16777216-33554431 */ - return LIMB_DIGITS - 8; - case 25: /* 33554432-67108863 */ - return LIMB_DIGITS - 8; - case 26: /* 67108864-134217727 */ - if (a < 100000000) - return LIMB_DIGITS - 8; - else - return LIMB_DIGITS - 9; -#if LIMB_BITS == 64 - case 27: /* 134217728-268435455 */ - return LIMB_DIGITS - 9; - case 28: /* 268435456-536870911 */ - return LIMB_DIGITS - 9; - case 29: /* 536870912-1073741823 */ - if (a < 1000000000) - return LIMB_DIGITS - 9; - else - return LIMB_DIGITS - 10; - case 30: /* 1073741824-2147483647 */ - return LIMB_DIGITS - 10; - case 31: /* 2147483648-4294967295 */ - return LIMB_DIGITS - 10; - case 32: /* 4294967296-8589934591 */ - return LIMB_DIGITS - 10; - case 33: /* 8589934592-17179869183 */ - if (a < 10000000000) - return LIMB_DIGITS - 10; - else - return LIMB_DIGITS - 11; - case 34: /* 17179869184-34359738367 */ - return LIMB_DIGITS - 11; - case 35: /* 34359738368-68719476735 */ - return LIMB_DIGITS - 11; - case 36: /* 68719476736-137438953471 */ - if (a < 100000000000) - return LIMB_DIGITS - 11; - else - return LIMB_DIGITS - 12; - case 37: /* 137438953472-274877906943 */ - return LIMB_DIGITS - 12; - case 38: /* 274877906944-549755813887 */ - return LIMB_DIGITS - 12; - case 39: /* 549755813888-1099511627775 */ - if (a < 1000000000000) - return LIMB_DIGITS - 12; - else - return LIMB_DIGITS - 13; - case 40: /* 1099511627776-2199023255551 */ - return LIMB_DIGITS - 13; - case 41: /* 2199023255552-4398046511103 */ - return LIMB_DIGITS - 13; - case 42: /* 4398046511104-8796093022207 */ - return LIMB_DIGITS - 13; - case 43: /* 8796093022208-17592186044415 */ - if (a < 10000000000000) - return LIMB_DIGITS - 13; - else - return LIMB_DIGITS - 14; - case 44: /* 17592186044416-35184372088831 */ - return LIMB_DIGITS - 14; - case 45: /* 35184372088832-70368744177663 */ - return LIMB_DIGITS - 14; - case 46: /* 70368744177664-140737488355327 */ - if (a < 100000000000000) - return LIMB_DIGITS - 14; - else - return LIMB_DIGITS - 15; - case 47: /* 140737488355328-281474976710655 */ - return LIMB_DIGITS - 15; - case 48: /* 281474976710656-562949953421311 */ - return LIMB_DIGITS - 15; - case 49: /* 562949953421312-1125899906842623 */ - if (a < 1000000000000000) - return LIMB_DIGITS - 15; - else - return LIMB_DIGITS - 16; - case 50: /* 1125899906842624-2251799813685247 */ - return LIMB_DIGITS - 16; - case 51: /* 2251799813685248-4503599627370495 */ - return LIMB_DIGITS - 16; - case 52: /* 4503599627370496-9007199254740991 */ - return LIMB_DIGITS - 16; - case 53: /* 9007199254740992-18014398509481983 */ - if (a < 10000000000000000) - return LIMB_DIGITS - 16; - else - return LIMB_DIGITS - 17; - case 54: /* 18014398509481984-36028797018963967 */ - return LIMB_DIGITS - 17; - case 55: /* 36028797018963968-72057594037927935 */ - return LIMB_DIGITS - 17; - case 56: /* 72057594037927936-144115188075855871 */ - if (a < 100000000000000000) - return LIMB_DIGITS - 17; - else - return LIMB_DIGITS - 18; - case 57: /* 144115188075855872-288230376151711743 */ - return LIMB_DIGITS - 18; - case 58: /* 288230376151711744-576460752303423487 */ - return LIMB_DIGITS - 18; - case 59: /* 576460752303423488-1152921504606846975 */ - if (a < 1000000000000000000) - return LIMB_DIGITS - 18; - else - return LIMB_DIGITS - 19; -#endif - default: - return 0; - } -} - -/* for debugging */ -void bfdec_print_str(const char *str, const bfdec_t *a) -{ - slimb_t i; - printf("%s=", str); - - if (a->expn == BF_EXP_NAN) { - printf("NaN"); - } else { - if (a->sign) - putchar('-'); - if (a->expn == BF_EXP_ZERO) { - putchar('0'); - } else if (a->expn == BF_EXP_INF) { - printf("Inf"); - } else { - printf("0."); - for(i = a->len - 1; i >= 0; i--) - printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); - printf("e%" PRId_LIMB, a->expn); - } - } - printf("\n"); -} - -/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ -static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) -{ - slimb_t pos; - limb_t v, q; - int shift; - - if (bit_pos < 0) - return 0; - pos = (limb_t)bit_pos / LIMB_DIGITS; - shift = (limb_t)bit_pos % LIMB_DIGITS; - fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); - (void)q; - if (v != 0) - return 1; - pos--; - while (pos >= 0) { - if (r->tab[pos] != 0) - return 1; - pos--; - } - return 0; -} - -static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) -{ - slimb_t i; - int shift; - i = floor_div(pos, LIMB_DIGITS); - if (i < 0 || i >= len) - return 0; - shift = pos - i * LIMB_DIGITS; - return fast_shr_dec(tab[i], shift) % 10; -} - -#if 0 -static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos) -{ - limb_t a0, a1; - int shift; - slimb_t i; - - i = floor_div(pos, LIMB_DIGITS); - shift = pos - i * LIMB_DIGITS; - if (i >= 0 && i < len) - a0 = tab[i]; - else - a0 = 0; - if (shift == 0) { - return a0; - } else { - i++; - if (i >= 0 && i < len) - a1 = tab[i]; - else - a1 = 0; - return fast_shr_dec(a0, shift) + - fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) * - mp_pow_dec[shift]; - } -} -#endif - -/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ -static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, - slimb_t prec, int rnd_mode) -{ - int add_one, inexact; - limb_t digit1, digit0; - - // bfdec_print_str("get_rnd_add", r); - if (rnd_mode == BF_RNDF) { - digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ - } else { - /* starting limb for bit 'prec + 1' */ - digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); - } - - /* get the digit at 'prec' */ - digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); - inexact = (digit1 | digit0) != 0; - - add_one = 0; - switch(rnd_mode) { - case BF_RNDZ: - break; - case BF_RNDN: - if (digit1 == 5) { - if (digit0) { - add_one = 1; - } else { - /* round to even */ - add_one = - get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; - } - } else if (digit1 > 5) { - add_one = 1; - } - break; - case BF_RNDD: - case BF_RNDU: - if (r->sign == (rnd_mode == BF_RNDD)) - add_one = inexact; - break; - case BF_RNDNA: - case BF_RNDF: - add_one = (digit1 >= 5); - break; - case BF_RNDA: - add_one = inexact; - break; - default: - abort(); - } - - if (inexact) - *pret |= BF_ST_INEXACT; - return add_one; -} - -/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l' (1 <= l <= r->len). prec1 can be - BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with - BF_ST_MEM_ERROR. - */ -static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) -{ - int shift, add_one, rnd_mode, ret; - slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - - /* XXX: align to IEEE 754 2008 for decimal numbers ? */ - e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_range + 3; - e_max = e_range; - - if (flags & BF_FLAG_RADPNT_PREC) { - /* 'prec' is the precision after the decimal point */ - if (prec1 != BF_PREC_INF) - prec = r->expn + prec1; - else - prec = prec1; - } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { - /* restrict the precision in case of potentially subnormal - result */ - assert(prec1 != BF_PREC_INF); - prec = prec1 - (e_min - r->expn); - } else { - prec = prec1; - } - - /* round to prec bits */ - rnd_mode = flags & BF_RND_MASK; - ret = 0; - add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); - - if (prec <= 0) { - if (add_one) { - bfdec_resize(r, 1); /* cannot fail because r is non zero */ - r->tab[0] = BF_DEC_BASE / 10; - r->expn += 1 - prec; - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } else { - goto underflow; - } - } else if (add_one) { - limb_t carry; - - /* add one starting at digit 'prec - 1' */ - bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); - pos = bit_pos / LIMB_DIGITS; - carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; - carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); - if (carry) { - /* shift right by one digit */ - mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); - r->expn++; - } - } - - /* check underflow */ - if (unlikely(r->expn < e_min)) { - if (flags & BF_FLAG_SUBNORMAL) { - /* if inexact, also set the underflow flag */ - if (ret & BF_ST_INEXACT) - ret |= BF_ST_UNDERFLOW; - } else { - underflow: - bfdec_set_zero(r, r->sign); - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } - } - - /* check overflow */ - if (unlikely(r->expn > e_max)) { - bfdec_set_inf(r, r->sign); - ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; - return ret; - } - - /* keep the bits starting at 'prec - 1' */ - bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); - i = floor_div(bit_pos, LIMB_DIGITS); - if (i >= 0) { - shift = smod(bit_pos, LIMB_DIGITS); - if (shift != 0) { - r->tab[i] = fast_shr_dec(r->tab[i], shift) * - mp_pow_dec[shift]; - } - } else { - i = 0; - } - /* remove trailing zeros */ - while (r->tab[i] == 0) - i++; - if (i > 0) { - l -= i; - memmove(r->tab, r->tab + i, l * sizeof(limb_t)); - } - bfdec_resize(r, l); /* cannot fail */ - return ret; -} - -/* Cannot fail with BF_ST_MEM_ERROR. */ -int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) -{ - if (r->len == 0) - return 0; - return __bfdec_round(r, prec, flags, r->len); -} - -/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ -int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) -{ - limb_t l, v; - int shift, ret; - - // bfdec_print_str("bf_renorm", r); - l = r->len; - while (l > 0 && r->tab[l - 1] == 0) - l--; - if (l == 0) { - /* zero */ - r->expn = BF_EXP_ZERO; - bfdec_resize(r, 0); /* cannot fail */ - ret = 0; - } else { - r->expn -= (r->len - l) * LIMB_DIGITS; - /* shift to have the MSB set to '1' */ - v = r->tab[l - 1]; - shift = clz_dec(v); - if (shift != 0) { - mp_shl_dec(r->tab, r->tab, l, shift, 0); - r->expn -= shift; - } - ret = __bfdec_round(r, prec1, flags, l); - } - // bf_print_str("r_final", r); - return ret; -} - -int bfdec_set_ui(bfdec_t *r, uint64_t v) -{ -#if LIMB_BITS == 32 - if (v >= BF_DEC_BASE * BF_DEC_BASE) { - if (bfdec_resize(r, 3)) - goto fail; - r->tab[0] = v % BF_DEC_BASE; - v /= BF_DEC_BASE; - r->tab[1] = v % BF_DEC_BASE; - r->tab[2] = v / BF_DEC_BASE; - r->expn = 3 * LIMB_DIGITS; - } else -#endif - if (v >= BF_DEC_BASE) { - if (bfdec_resize(r, 2)) - goto fail; - r->tab[0] = v % BF_DEC_BASE; - r->tab[1] = v / BF_DEC_BASE; - r->expn = 2 * LIMB_DIGITS; - } else { - if (bfdec_resize(r, 1)) - goto fail; - r->tab[0] = v; - r->expn = LIMB_DIGITS; - } - r->sign = 0; - return bfdec_normalize_and_round(r, BF_PREC_INF, 0); - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_set_si(bfdec_t *r, int64_t v) -{ - int ret; - if (v < 0) { - ret = bfdec_set_ui(r, -v); - r->sign = 1; - } else { - ret = bfdec_set_ui(r, v); - } - return ret; -} - -static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) -{ - bf_context_t *s = r->ctx; - int is_sub, cmp_res, a_sign, b_sign, ret; - - a_sign = a->sign; - b_sign = b->sign ^ b_neg; - is_sub = a_sign ^ b_sign; - cmp_res = bfdec_cmpu(a, b); - if (cmp_res < 0) { - const bfdec_t *tmp; - tmp = a; - a = b; - b = tmp; - a_sign = b_sign; /* b_sign is never used later */ - } - /* abs(a) >= abs(b) */ - if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { - /* zero result */ - bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); - ret = 0; - } else if (a->len == 0 || b->len == 0) { - ret = 0; - if (a->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN) { - /* at least one operand is NaN */ - bfdec_set_nan(r); - ret = 0; - } else if (b->expn == BF_EXP_INF && is_sub) { - /* infinities with different signs */ - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bfdec_set_inf(r, a_sign); - } - } else { - /* at least one zero and not subtract */ - if (bfdec_set(r, a)) - return BF_ST_MEM_ERROR; - r->sign = a_sign; - goto renorm; - } - } else { - slimb_t d, a_offset, b_offset, i, r_len; - limb_t carry; - limb_t *b1_tab; - int b_shift; - mp_size_t b1_len; - - d = a->expn - b->expn; - - /* XXX: not efficient in time and memory if the precision is - not infinite */ - r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); - if (bfdec_resize(r, r_len)) - goto fail; - r->sign = a_sign; - r->expn = a->expn; - - a_offset = r_len - a->len; - for(i = 0; i < a_offset; i++) - r->tab[i] = 0; - for(i = 0; i < a->len; i++) - r->tab[a_offset + i] = a->tab[i]; - - b_shift = d % LIMB_DIGITS; - if (b_shift == 0) { - b1_len = b->len; - b1_tab = (limb_t *)b->tab; - } else { - b1_len = b->len + 1; - b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); - if (!b1_tab) - goto fail; - b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * - mp_pow_dec[LIMB_DIGITS - b_shift]; - } - b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); - - if (is_sub) { - carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, - b1_tab, b1_len, 0); - if (carry != 0) { - carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, - r_len - (b_offset + b1_len)); - assert(carry == 0); - } - } else { - carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, - b1_tab, b1_len, 0); - if (carry != 0) { - carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, - r_len - (b_offset + b1_len)); - } - if (carry != 0) { - if (bfdec_resize(r, r_len + 1)) { - if (b_shift != 0) - bf_free(s, b1_tab); - goto fail; - } - r->tab[r_len] = 1; - r->expn += LIMB_DIGITS; - } - } - if (b_shift != 0) - bf_free(s, b1_tab); - renorm: - ret = bfdec_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bfdec_add_internal(r, a, b, prec, flags, 0); -} - -static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bfdec_add_internal(r, a, b, prec, flags, 1); -} - -int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_add); -} - -int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_sub); -} - -int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - int ret, r_sign; - - if (a->len < b->len) { - const bfdec_t *tmp = a; - a = b; - b = tmp; - } - r_sign = a->sign ^ b->sign; - /* here b->len <= a->len */ - if (b->len == 0) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - ret = 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { - if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || - (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bfdec_set_inf(r, r_sign); - ret = 0; - } - } else { - bfdec_set_zero(r, r_sign); - ret = 0; - } - } else { - bfdec_t tmp, *r1 = NULL; - limb_t a_len, b_len; - limb_t *a_tab, *b_tab; - - a_len = a->len; - b_len = b->len; - a_tab = a->tab; - b_tab = b->tab; - - if (r == a || r == b) { - bfdec_init(r->ctx, &tmp); - r1 = r; - r = &tmp; - } - if (bfdec_resize(r, a_len + b_len)) { - bfdec_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; - } - mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); - r->sign = r_sign; - r->expn = a->expn + b->expn; - ret = bfdec_normalize_and_round(r, prec, flags); - done: - if (r == &tmp) - bfdec_move(r1, &tmp); - } - return ret; -} - -int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bfdec_t b; - int ret; - bfdec_init(r->ctx, &b); - ret = bfdec_set_si(&b, b1); - ret |= bfdec_mul(r, a, &b, prec, flags); - bfdec_delete(&b); - return ret; -} - -int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bfdec_t b; - int ret; - - bfdec_init(r->ctx, &b); - ret = bfdec_set_si(&b, b1); - ret |= bfdec_add(r, a, &b, prec, flags); - bfdec_delete(&b); - return ret; -} - -static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags) -{ - int ret, r_sign; - limb_t n, nb, precl; - - r_sign = a->sign ^ b->sign; - if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else if (a->expn == BF_EXP_INF) { - bfdec_set_inf(r, r_sign); - return 0; - } else { - bfdec_set_zero(r, r_sign); - return 0; - } - } else if (a->expn == BF_EXP_ZERO) { - if (b->expn == BF_EXP_ZERO) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bfdec_set_zero(r, r_sign); - return 0; - } - } else if (b->expn == BF_EXP_ZERO) { - bfdec_set_inf(r, r_sign); - return BF_ST_DIVIDE_ZERO; - } - - nb = b->len; - if (prec == BF_PREC_INF) { - /* infinite precision: return BF_ST_INVALID_OP if not an exact - result */ - /* XXX: check */ - precl = nb + 1; - } else if (flags & BF_FLAG_RADPNT_PREC) { - /* number of digits after the decimal point */ - /* XXX: check (2 extra digits for rounding + 2 digits) */ - precl = (bf_max(a->expn - b->expn, 0) + 2 + - prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; - } else { - /* number of limbs of the quotient (2 extra digits for rounding) */ - precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; - } - n = bf_max(a->len, precl); - - { - limb_t *taba, na, i; - slimb_t d; - - na = n + nb; - taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); - if (!taba) - goto fail; - d = na - a->len; - memset(taba, 0, d * sizeof(limb_t)); - memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); - if (bfdec_resize(r, n + 1)) - goto fail1; - if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { - fail1: - bf_free(r->ctx, taba); - goto fail; - } - /* see if non zero remainder */ - for(i = 0; i < nb; i++) { - if (taba[i] != 0) - break; - } - bf_free(r->ctx, taba); - if (i != nb) { - if (prec == BF_PREC_INF) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - r->tab[0] |= 1; - } - } - r->expn = a->expn - b->expn + LIMB_DIGITS; - r->sign = r_sign; - ret = bfdec_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_div); -} - -/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the - integer defined as floor(a/b) and r = a - q * b. */ -static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, - const bfdec_t *a, const bfdec_t *b) -{ - if (bfdec_cmpu(a, b) < 0) { - bfdec_set_ui(q, 0); - bfdec_set(r, a); - } else { - bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); - bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); - bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); - } -} - -/* division and remainder. - - rnd_mode is the rounding mode for the quotient. The additional - rounding mode BF_RND_EUCLIDIAN is supported. - - 'q' is an integer. 'r' is rounded with prec and flags (prec can be - BF_PREC_INF). -*/ -int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode) -{ - bf_context_t *s = q->ctx; - bfdec_t a1_s, *a1 = &a1_s; - bfdec_t b1_s, *b1 = &b1_s; - bfdec_t r1_s, *r1 = &r1_s; - int q_sign, res; - BOOL is_ceil, is_rndn; - - assert(q != a && q != b); - assert(r != a && r != b); - assert(q != r); - - if (a->len == 0 || b->len == 0) { - bfdec_set_zero(q, 0); - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bfdec_set(r, a); - return bfdec_round(r, prec, flags); - } - } - - q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - switch(rnd_mode) { - default: - case BF_RNDZ: - case BF_RNDN: - case BF_RNDNA: - is_ceil = FALSE; - break; - case BF_RNDD: - is_ceil = q_sign; - break; - case BF_RNDU: - is_ceil = q_sign ^ 1; - break; - case BF_RNDA: - is_ceil = TRUE; - break; - case BF_DIVREM_EUCLIDIAN: - is_ceil = a->sign; - break; - } - - a1->expn = a->expn; - a1->tab = a->tab; - a1->len = a->len; - a1->sign = 0; - - b1->expn = b->expn; - b1->tab = b->tab; - b1->len = b->len; - b1->sign = 0; - - // bfdec_print_str("a1", a1); - // bfdec_print_str("b1", b1); - /* XXX: could improve to avoid having a large 'q' */ - bfdec_tdivremu(s, q, r, a1, b1); - if (bfdec_is_nan(q) || bfdec_is_nan(r)) - goto fail; - // bfdec_print_str("q", q); - // bfdec_print_str("r", r); - - if (r->len != 0) { - if (is_rndn) { - bfdec_init(s, r1); - if (bfdec_set(r1, r)) - goto fail; - if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { - bfdec_delete(r1); - goto fail; - } - res = bfdec_cmpu(r1, b); - bfdec_delete(r1); - if (res > 0 || - (res == 0 && - (rnd_mode == BF_RNDNA || - (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { - goto do_sub_r; - } - } else if (is_ceil) { - do_sub_r: - res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); - res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); - if (res & BF_ST_MEM_ERROR) - goto fail; - } - } - - r->sign ^= a->sign; - q->sign = q_sign; - return bfdec_round(r, prec, flags); - fail: - bfdec_set_nan(q); - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bfdec_t q_s, *q = &q_s; - int ret; - - bfdec_init(r->ctx, q); - ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); - bfdec_delete(q); - return ret; -} - -/* convert to integer (infinite precision) */ -int bfdec_rint(bfdec_t *r, int rnd_mode) -{ - return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); -} - -int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = a->ctx; - int ret, k; - limb_t *a1, v; - slimb_t n, n1, prec1; - limb_t res; - - assert(r != a); - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bfdec_set(r, a); - } - ret = 0; - } else if (a->sign || prec == BF_PREC_INF) { - invalid_op: - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - if (flags & BF_FLAG_RADPNT_PREC) { - prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); - } else { - prec1 = prec; - } - /* convert the mantissa to an integer with at least 2 * - prec + 4 digits */ - n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); - if (bfdec_resize(r, n)) - goto fail; - a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); - if (!a1) - goto fail; - n1 = bf_min(2 * n, a->len); - memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); - memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); - if (a->expn & 1) { - res = mp_shr_dec(a1, a1, 2 * n, 1, 0); - } else { - res = 0; - } - /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 - because mp_sqrtrem2_dec already does it */ - k = 0; - if (n > 1) { - v = a1[2 * n - 1]; - while (v < BF_DEC_BASE / 4) { - k++; - v *= 4; - } - if (k != 0) - mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); - } - if (mp_sqrtrem_dec(s, r->tab, a1, n)) { - bf_free(s, a1); - goto fail; - } - if (k != 0) - mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); - if (!res) { - res = mp_scan_nz(a1, n + 1); - } - bf_free(s, a1); - if (!res) { - res = mp_scan_nz(a->tab, a->len - n1); - } - if (res != 0) - r->tab[0] |= 1; - r->sign = 0; - r->expn = (a->expn + 1) >> 1; - ret = bfdec_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there - is an overflow and 0 otherwise. No memory error is possible. */ -int bfdec_get_int32(int *pres, const bfdec_t *a) -{ - uint32_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = 0; - if (a->expn == BF_EXP_INF) { - v = (uint32_t)INT32_MAX + a->sign; - /* XXX: return overflow ? */ - } else { - v = INT32_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 9) { - v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } else if (a->expn == 10) { - uint64_t v1; - uint32_t v_max; -#if LIMB_BITS == 64 - v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); -#else - v1 = (uint64_t)a->tab[a->len - 1] * 10 + - get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); -#endif - v_max = (uint32_t)INT32_MAX + a->sign; - if (v1 > v_max) { - v = v_max; - ret = BF_ST_OVERFLOW; - } else { - v = v1; - if (a->sign) - v = -v; - ret = 0; - } - } else { - v = (uint32_t)INT32_MAX + a->sign; - ret = BF_ST_OVERFLOW; - } - *pres = v; - return ret; -} - -/* power to an integer with infinite precision */ -int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) -{ - int ret, n_bits, i; - - assert(r != a); - if (b == 0) - return bfdec_set_ui(r, 1); - ret = bfdec_set(r, a); - n_bits = LIMB_BITS - clz(b); - for(i = n_bits - 2; i >= 0; i--) { - ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); - if ((b >> i) & 1) - ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); - } - return ret; -} - -char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) -{ - return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE); -} - -int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, - limb_t prec, bf_flags_t flags) -{ - slimb_t dummy_exp; - return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, - flags, TRUE); -} - -#endif /* USE_BF_DEC */ - -#ifdef USE_FFT_MUL -/***************************************************************/ -/* Integer multiplication with FFT */ - -/* or LIMB_BITS at bit position 'pos' in tab */ -static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val) -{ - limb_t i; - int p; - - i = pos >> LIMB_LOG2_BITS; - p = pos & (LIMB_BITS - 1); - if (i < len) - tab[i] |= val << p; - if (p != 0) { - i++; - if (i < len) { - tab[i] |= val >> (LIMB_BITS - p); - } - } -} - -#if defined(__AVX2__) - -typedef double NTTLimb; - -/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ -#define NTT_MOD_LOG2_MIN 50 -#define NTT_MOD_LOG2_MAX 51 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 39 -static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, }, - { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd, - 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a, - 0x00066015555557e3, 0x000725960b60b623, - 0x0002fc1fa1d6ce12, -}; - -#else - -typedef limb_t NTTLimb; - -#if LIMB_BITS == 64 - -#define NTT_MOD_LOG2_MIN 61 -#define NTT_MOD_LOG2_MAX 62 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 51 -static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, }, - { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4, - 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638, - 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f, - 0x3a5493e93e93e94a, -}; - -#elif LIMB_BITS == 32 - -/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ -#define NTT_MOD_LOG2_MIN 29 -#define NTT_MOD_LOG2_MAX 30 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 20 -static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, }, - { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b, - 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938, - 0x000000000932ab3e, 0x000000002f40eef8, - 0x000000002e760905, -}; - -#endif /* LIMB_BITS */ - -#endif /* !AVX2 */ - -#if defined(__AVX2__) -#define NTT_TRIG_K_MAX 18 -#else -#define NTT_TRIG_K_MAX 19 -#endif - -typedef struct BFNTTState { - bf_context_t *ctx; - - /* used for mul_mod_fast() */ - limb_t ntt_mods_div[NB_MODS]; - - limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1]; - limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1]; - NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1]; - /* 1/2^n mod m */ - limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2]; -#if defined(__AVX2__) - __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2]; - __m256d ntt_mods_vec[NB_MODS]; - __m256d ntt_mods_inv_vec[NB_MODS]; -#else - limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2]; -#endif -} BFNTTState; - -static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx); - -/* add modulo with up to (LIMB_BITS-1) bit modulo */ -static inline limb_t add_mod(limb_t a, limb_t b, limb_t m) -{ - limb_t r; - r = a + b; - if (r >= m) - r -= m; - return r; -} - -/* sub modulo with up to LIMB_BITS bit modulo */ -static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m) -{ - limb_t r; - r = a - b; - if (r > a) - r += m; - return r; -} - -/* return (r0+r1*B) mod m - precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) -*/ -static inline limb_t mod_fast(dlimb_t r, - limb_t m, limb_t m_inv) -{ - limb_t a1, q, t0, r1, r0; - - a1 = r >> NTT_MOD_LOG2_MIN; - - q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS; - r = r - (dlimb_t)q * m - m * 2; - r1 = r >> LIMB_BITS; - t0 = (slimb_t)r1 >> 1; - r += m & t0; - r0 = r; - r1 = r >> LIMB_BITS; - r0 += m & r1; - return r0; -} - -/* faster version using precomputed modulo inverse. - precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */ -static inline limb_t mul_mod_fast(limb_t a, limb_t b, - limb_t m, limb_t m_inv) -{ - dlimb_t r; - r = (dlimb_t)a * (dlimb_t)b; - return mod_fast(r, m, m_inv); -} - -static inline limb_t init_mul_mod_fast(limb_t m) -{ - dlimb_t t; - assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX); - assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN); - t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN); - return t / m; -} - -/* Faster version used when the multiplier is constant. 0 <= a < 2^64, - 0 <= b < m. */ -static inline limb_t mul_mod_fast2(limb_t a, limb_t b, - limb_t m, limb_t b_inv) -{ - limb_t r, q; - - q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; - r = a * b - q * m; - if (r >= m) - r -= m; - return r; -} - -/* Faster version used when the multiplier is constant. 0 <= a < 2^64, - 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r + - m'. */ -static inline limb_t mul_mod_fast3(limb_t a, limb_t b, - limb_t m, limb_t b_inv) -{ - limb_t r, q; - - q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; - r = a * b - q * m; - return r; -} - -static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m) -{ - return ((dlimb_t)b << LIMB_BITS) / m; -} - -#ifdef __AVX2__ - -static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) -{ - slimb_t v; - v = a; - if (v < 0) - v += m; - if (v >= m) - v -= m; - return v; -} - -static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m) -{ - return (slimb_t)a; -} - -static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m) -{ - if (a >= (m / 2)) - a -= m; - return (slimb_t)a; -} - -/* return r + m if r < 0 otherwise r. */ -static inline __m256d ntt_mod1(__m256d r, __m256d m) -{ - return _mm256_blendv_pd(r, r + m, r); -} - -/* input: abs(r) < 2 * m. Output: abs(r) < m */ -static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f) -{ - return _mm256_blendv_pd(r, r + m2f, r) - mf; -} - -/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */ -static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf, - __m256d m_inv) -{ - __m256d r, q, ab1, ab0, qm0, qm1; - ab1 = a * b; - q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */ - qm1 = q * mf; - qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */ - ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */ - r = (ab1 - qm1) + (ab0 - qm0); - return r; -} - -static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align) -{ - void *ptr; - void **ptr1; - ptr = bf_malloc(s, size + sizeof(void *) + align - 1); - if (!ptr) - return NULL; - ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) & - ~(align - 1)); - ptr1[-1] = ptr; - return ptr1; -} - -static void bf_aligned_free(bf_context_t *s, void *ptr) -{ - if (!ptr) - return; - bf_free(s, ((void **)ptr)[-1]); -} - -static void *ntt_malloc(BFNTTState *s, size_t size) -{ - return bf_aligned_malloc(s->ctx, size, 64); -} - -static void ntt_free(BFNTTState *s, void *ptr) -{ - bf_aligned_free(s->ctx, ptr); -} - -static no_inline int ntt_fft(BFNTTState *s, - NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) -{ - limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j; - NTTLimb *tab_in, *tab_out, *tmp, *trig; - __m256d m_inv, mf, m2f, c, a0, a1, b0, b1; - limb_t m; - int l; - - m = ntt_mods[m_idx]; - - m_inv = _mm256_set1_pd(1.0 / (double)m); - mf = _mm256_set1_pd(m); - m2f = _mm256_set1_pd(m * 2); - - n = (limb_t)1 << fft_len_log2; - assert(n >= 8); - stride_in = n / 2; - - tab_in = in_buf; - tab_out = tmp_buf; - trig = get_trig(s, fft_len_log2, inverse, m_idx); - if (!trig) - return -1; - p = 0; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - c = _mm256_load_pd(trig); - trig += 4; - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - a0 = _mm256_permute2f128_pd(b0, b1, 0x20); - a1 = _mm256_permute2f128_pd(b0, b1, 0x31); - a0 = _mm256_permute4x64_pd(a0, 0xd8); - a1 = _mm256_permute4x64_pd(a1, 0xd8); - _mm256_store_pd(&tab_out[p], a0); - _mm256_store_pd(&tab_out[p + 4], a1); - p += 2 * 4; - } - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - - trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx); - if (!trig) - return -1; - p = 0; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]); - trig += 2; - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - a0 = _mm256_permute2f128_pd(b0, b1, 0x20); - a1 = _mm256_permute2f128_pd(b0, b1, 0x31); - _mm256_store_pd(&tab_out[p], a0); - _mm256_store_pd(&tab_out[p + 4], a1); - p += 2 * 4; - } - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - - nb_blocks = n / 4; - fft_per_block = 4; - - l = fft_len_log2 - 2; - while (nb_blocks != 2) { - nb_blocks >>= 1; - p = 0; - k = 0; - trig = get_trig(s, l, inverse, m_idx); - if (!trig) - return -1; - for(i = 0; i < nb_blocks; i++) { - c = _mm256_set1_pd(trig[0]); - trig++; - for(j = 0; j < fft_per_block; j += 4) { - a0 = _mm256_load_pd(&tab_in[k + j]); - a1 = _mm256_load_pd(&tab_in[k + j + stride_in]); - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - _mm256_store_pd(&tab_out[p + j], b0); - _mm256_store_pd(&tab_out[p + j + fft_per_block], b1); - } - k += fft_per_block; - p += 2 * fft_per_block; - } - fft_per_block <<= 1; - l--; - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - } - - tab_out = out_buf; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mod(a0 - a1, mf, m2f); - _mm256_store_pd(&tab_out[k], b0); - _mm256_store_pd(&tab_out[k + stride_in], b1); - } - return 0; -} - -static void ntt_vec_mul(BFNTTState *s, - NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2, - int k_tot, int m_idx) -{ - limb_t i, c_inv, n, m; - __m256d m_inv, mf, a, b, c; - - m = ntt_mods[m_idx]; - c_inv = s->ntt_len_inv[m_idx][k_tot][0]; - m_inv = _mm256_set1_pd(1.0 / (double)m); - mf = _mm256_set1_pd(m); - c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m)); - n = (limb_t)1 << fft_len_log2; - for(i = 0; i < n; i += 4) { - a = _mm256_load_pd(&tab1[i]); - b = _mm256_load_pd(&tab2[i]); - a = ntt_mul_mod(a, b, mf, m_inv); - a = ntt_mul_mod(a, c, mf, m_inv); - _mm256_store_pd(&tab1[i], a); - } -} - -static no_inline void mul_trig(NTTLimb *buf, - limb_t n, limb_t c1, limb_t m, limb_t m_inv1) -{ - limb_t i, c2, c3, c4; - __m256d c, c_mul, a0, mf, m_inv; - assert(n >= 2); - - mf = _mm256_set1_pd(m); - m_inv = _mm256_set1_pd(1.0 / (double)m); - - c2 = mul_mod_fast(c1, c1, m, m_inv1); - c3 = mul_mod_fast(c2, c1, m, m_inv1); - c4 = mul_mod_fast(c2, c2, m, m_inv1); - c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m), - int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m)); - c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m)); - for(i = 0; i < n; i += 4) { - a0 = _mm256_load_pd(&buf[i]); - a0 = ntt_mul_mod(a0, c, mf, m_inv); - _mm256_store_pd(&buf[i], a0); - c = ntt_mul_mod(c, c_mul, mf, m_inv); - } -} - -#else - -static void *ntt_malloc(BFNTTState *s, size_t size) -{ - return bf_malloc(s->ctx, size); -} - -static void ntt_free(BFNTTState *s, void *ptr) -{ - bf_free(s->ctx, ptr); -} - -static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) -{ - if (a >= m) - a -= m; - return a; -} - -static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) -{ - return a; -} - -static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) -{ - limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2; - NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv; - int l; - - m = ntt_mods[m_idx]; - m2 = 2 * m; - n = (limb_t)1 << fft_len_log2; - nb_blocks = n; - fft_per_block = 1; - stride_in = n / 2; - tab_in = in_buf; - tab_out = tmp_buf; - l = fft_len_log2; - while (nb_blocks != 2) { - nb_blocks >>= 1; - p = 0; - k = 0; - trig = get_trig(s, l, inverse, m_idx); - if (!trig) - return -1; - for(i = 0; i < nb_blocks; i++) { - c = trig[0]; - c_inv = trig[1]; - trig += 2; - for(j = 0; j < fft_per_block; j++) { - a0 = tab_in[k + j]; - a1 = tab_in[k + j + stride_in]; - b0 = add_mod(a0, a1, m2); - b1 = a0 - a1 + m2; - b1 = mul_mod_fast3(b1, c, m, c_inv); - tab_out[p + j] = b0; - tab_out[p + j + fft_per_block] = b1; - } - k += fft_per_block; - p += 2 * fft_per_block; - } - fft_per_block <<= 1; - l--; - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - } - /* no twiddle in last step */ - tab_out = out_buf; - for(k = 0; k < stride_in; k++) { - a0 = tab_in[k]; - a1 = tab_in[k + stride_in]; - b0 = add_mod(a0, a1, m2); - b1 = sub_mod(a0, a1, m2); - tab_out[k] = b0; - tab_out[k + stride_in] = b1; - } - return 0; -} - -static void ntt_vec_mul(BFNTTState *s, - NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2, - int k_tot, int m_idx) -{ - limb_t i, norm, norm_inv, a, n, m, m_inv; - - m = ntt_mods[m_idx]; - m_inv = s->ntt_mods_div[m_idx]; - norm = s->ntt_len_inv[m_idx][k_tot][0]; - norm_inv = s->ntt_len_inv[m_idx][k_tot][1]; - n = (limb_t)1 << fft_len_log2; - for(i = 0; i < n; i++) { - a = tab1[i]; - /* need to reduce the range so that the product is < - 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */ - if (a >= m) - a -= m; - a = mul_mod_fast(a, tab2[i], m, m_inv); - a = mul_mod_fast3(a, norm, m, norm_inv); - tab1[i] = a; - } -} - -static no_inline void mul_trig(NTTLimb *buf, - limb_t n, limb_t c_mul, limb_t m, limb_t m_inv) -{ - limb_t i, c0, c_mul_inv; - - c0 = 1; - c_mul_inv = init_mul_mod_fast2(c_mul, m); - for(i = 0; i < n; i++) { - buf[i] = mul_mod_fast(buf[i], c0, m, m_inv); - c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv); - } -} - -#endif /* !AVX2 */ - -static no_inline NTTLimb *get_trig(BFNTTState *s, - int k, int inverse, int m_idx) -{ - NTTLimb *tab; - limb_t i, n2, c, c_mul, m, c_mul_inv; - - if (k > NTT_TRIG_K_MAX) - return NULL; - - tab = s->ntt_trig[m_idx][inverse][k]; - if (tab) - return tab; - n2 = (limb_t)1 << (k - 1); - m = ntt_mods[m_idx]; -#ifdef __AVX2__ - tab = ntt_malloc(s, sizeof(NTTLimb) * n2); -#else - tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); -#endif - if (!tab) - return NULL; - c = 1; - c_mul = s->ntt_proot_pow[m_idx][inverse][k]; - c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k]; - for(i = 0; i < n2; i++) { -#ifdef __AVX2__ - tab[i] = int_to_ntt_limb2(c, m); -#else - tab[2 * i] = int_to_ntt_limb(c, m); - tab[2 * i + 1] = init_mul_mod_fast2(c, m); -#endif - c = mul_mod_fast2(c, c_mul, m, c_mul_inv); - } - s->ntt_trig[m_idx][inverse][k] = tab; - return tab; -} - -void fft_clear_cache(bf_context_t *s1) -{ - int m_idx, inverse, k; - BFNTTState *s = s1->ntt_state; - if (s) { - for(m_idx = 0; m_idx < NB_MODS; m_idx++) { - for(inverse = 0; inverse < 2; inverse++) { - for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) { - if (s->ntt_trig[m_idx][inverse][k]) { - ntt_free(s, s->ntt_trig[m_idx][inverse][k]); - s->ntt_trig[m_idx][inverse][k] = NULL; - } - } - } - } -#if defined(__AVX2__) - bf_aligned_free(s1, s); -#else - bf_free(s1, s); -#endif - s1->ntt_state = NULL; - } -} - -#define STRIP_LEN 16 - -/* dst = buf1, src = buf2 */ -static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, - int k1, int k2, limb_t n1, limb_t n2, int inverse, - limb_t m_idx) -{ - limb_t i, j, c_mul, c0, m, m_inv, strip_len, l; - NTTLimb *buf2, *buf3; - - buf2 = NULL; - buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1); - if (!buf3) - goto fail; - if (k2 == 0) { - if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx)) - goto fail; - } else { - strip_len = STRIP_LEN; - buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len); - if (!buf2) - goto fail; - m = ntt_mods[m_idx]; - m_inv = s->ntt_mods_div[m_idx]; - c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2]; - c_mul = 1; - assert((n2 % strip_len) == 0); - for(j = 0; j < n2; j += strip_len) { - for(i = 0; i < n1; i++) { - for(l = 0; l < strip_len; l++) { - buf2[i + l * n1] = buf1[i * n2 + (j + l)]; - } - } - for(l = 0; l < strip_len; l++) { - if (inverse) - mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx)) - goto fail; - if (!inverse) - mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - c_mul = mul_mod_fast(c_mul, c0, m, m_inv); - } - - for(i = 0; i < n1; i++) { - for(l = 0; l < strip_len; l++) { - buf1[i * n2 + (j + l)] = buf2[i + l *n1]; - } - } - } - ntt_free(s, buf2); - } - ntt_free(s, buf3); - return 0; - fail: - ntt_free(s, buf2); - ntt_free(s, buf3); - return -1; -} - - -/* dst = buf1, src = buf2, tmp = buf3 */ -static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, - int k, int k_tot, limb_t m_idx) -{ - limb_t n1, n2, i; - int k1, k2; - - if (k <= NTT_TRIG_K_MAX) { - k1 = k; - } else { - /* recursive split of the FFT */ - k1 = bf_min(k / 2, NTT_TRIG_K_MAX); - } - k2 = k - k1; - n1 = (limb_t)1 << k1; - n2 = (limb_t)1 << k2; - - if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx)) - return -1; - if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx)) - return -1; - if (k2 == 0) { - ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx); - } else { - for(i = 0; i < n1; i++) { - ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); - } - } - if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) - return -1; - return 0; -} - - -static no_inline void limb_to_ntt(BFNTTState *s, - NTTLimb *tabr, limb_t fft_len, - const limb_t *taba, limb_t a_len, int dpl, - int first_m_idx, int nb_mods) -{ - slimb_t i, n; - dlimb_t a, b; - int j, shift; - limb_t base_mask1, a0, a1, a2, r, m, m_inv; - -#if 0 - for(i = 0; i < a_len; i++) { - printf("%" PRId64 ": " FMT_LIMB "\n", - (int64_t)i, taba[i]); - } -#endif - memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods); - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl); - for(i = 0; i < n; i++) { - a0 = get_bits(taba, a_len, i * dpl); - if (dpl <= LIMB_BITS) { - a0 &= base_mask1; - a = a0; - } else { - a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS); - if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) { - a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS); - } else { - if (dpl > 2 * LIMB_BITS) { - a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) & - base_mask1; - } else { - a1 &= base_mask1; - a2 = 0; - } - // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0); - a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | - ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) | - ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)); - a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1; - } - } - for(j = 0; j < nb_mods; j++) { - m = ntt_mods[first_m_idx + j]; - m_inv = s->ntt_mods_div[first_m_idx + j]; - r = mod_fast(a, m, m_inv); - if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) { - b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0; - r = mod_fast(b, m, m_inv); - } - tabr[i + j * fft_len] = int_to_ntt_limb(r, m); - } - } -} - -#if defined(__AVX2__) - -#define VEC_LEN 4 - -typedef union { - __m256d v; - double d[4]; -} VecUnion; - -static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, - const NTTLimb *buf, int fft_len_log2, int dpl, - int nb_mods) -{ - const limb_t *mods = ntt_mods + NB_MODS - nb_mods; - const __m256d *mods_cr_vec, *mf, *m_inv; - VecUnion y[NB_MODS]; - limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; - slimb_t i, len, pos; - int j, k, l, shift, n_limb1, p; - dlimb_t t; - - j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; - mods_cr_vec = s->ntt_mods_cr_vec + j; - mf = s->ntt_mods_vec + NB_MODS - nb_mods; - m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods; - - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; - for(j = 0; j < NB_MODS; j++) - carry[j] = 0; - for(j = 0; j < NB_MODS; j++) - u[j] = 0; /* avoid warnings */ - memset(tabr, 0, sizeof(limb_t) * r_len); - fft_len = (limb_t)1 << fft_len_log2; - len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); - len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1); - i = 0; - while (i < len) { - for(j = 0; j < nb_mods; j++) - y[j].v = *(__m256d *)&buf[i + fft_len * j]; - - /* Chinese remainder to get mixed radix representation */ - l = 0; - for(j = 0; j < nb_mods - 1; j++) { - y[j].v = ntt_mod1(y[j].v, mf[j]); - for(k = j + 1; k < nb_mods; k++) { - y[k].v = ntt_mul_mod(y[k].v - y[j].v, - mods_cr_vec[l], mf[k], m_inv[k]); - l++; - } - } - y[j].v = ntt_mod1(y[j].v, mf[j]); - - for(p = 0; p < VEC_LEN; p++) { - /* back to normal representation */ - u[0] = (int64_t)y[nb_mods - 1].d[p]; - l = 1; - for(j = nb_mods - 2; j >= 1; j--) { - r = (int64_t)y[j].d[p]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r; - l++; - } - /* XXX: for nb_mods = 5, l should be 4 */ - - /* last step adds the carry */ - r = (int64_t)y[0].d[p]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r + carry[k]; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r + carry[l]; - -#if 0 - printf("%" PRId64 ": ", i); - for(j = nb_mods - 1; j >= 0; j--) { - printf(" %019" PRIu64, u[j]); - } - printf("\n"); -#endif - - /* write the digits */ - pos = i * dpl; - for(j = 0; j < n_limb1; j++) { - put_bits(tabr, r_len, pos, u[j]); - pos += LIMB_BITS; - } - put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); - /* shift by dpl digits and set the carry */ - if (shift == 0) { - for(j = n_limb1 + 1; j < nb_mods; j++) - carry[j - (n_limb1 + 1)] = u[j]; - } else { - for(j = n_limb1; j < nb_mods - 1; j++) { - carry[j - n_limb1] = (u[j] >> shift) | - (u[j + 1] << (LIMB_BITS - shift)); - } - carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; - } - i++; - } - } -} -#else -static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, - const NTTLimb *buf, int fft_len_log2, int dpl, - int nb_mods) -{ - const limb_t *mods = ntt_mods + NB_MODS - nb_mods; - const limb_t *mods_cr, *mods_cr_inv; - limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; - slimb_t i, len, pos; - int j, k, l, shift, n_limb1; - dlimb_t t; - - j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; - mods_cr = ntt_mods_cr + j; - mods_cr_inv = s->ntt_mods_cr_inv + j; - - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; - for(j = 0; j < NB_MODS; j++) - carry[j] = 0; - for(j = 0; j < NB_MODS; j++) - u[j] = 0; /* avoid warnings */ - memset(tabr, 0, sizeof(limb_t) * r_len); - fft_len = (limb_t)1 << fft_len_log2; - len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); - for(i = 0; i < len; i++) { - for(j = 0; j < nb_mods; j++) { - y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]); - } - - /* Chinese remainder to get mixed radix representation */ - l = 0; - for(j = 0; j < nb_mods - 1; j++) { - for(k = j + 1; k < nb_mods; k++) { - limb_t m; - m = mods[k]; - /* Note: there is no overflow in the sub_mod() because - the modulos are sorted by increasing order */ - y[k] = mul_mod_fast2(y[k] - y[j] + m, - mods_cr[l], m, mods_cr_inv[l]); - l++; - } - } - - /* back to normal representation */ - u[0] = y[nb_mods - 1]; - l = 1; - for(j = nb_mods - 2; j >= 1; j--) { - r = y[j]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r; - l++; - } - - /* last step adds the carry */ - r = y[0]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r + carry[k]; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r + carry[l]; - -#if 0 - printf("%" PRId64 ": ", (int64_t)i); - for(j = nb_mods - 1; j >= 0; j--) { - printf(" " FMT_LIMB, u[j]); - } - printf("\n"); -#endif - - /* write the digits */ - pos = i * dpl; - for(j = 0; j < n_limb1; j++) { - put_bits(tabr, r_len, pos, u[j]); - pos += LIMB_BITS; - } - put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); - /* shift by dpl digits and set the carry */ - if (shift == 0) { - for(j = n_limb1 + 1; j < nb_mods; j++) - carry[j - (n_limb1 + 1)] = u[j]; - } else { - for(j = n_limb1; j < nb_mods - 1; j++) { - carry[j - n_limb1] = (u[j] >> shift) | - (u[j + 1] << (LIMB_BITS - shift)); - } - carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; - } - } -} -#endif - -static int ntt_static_init(bf_context_t *s1) -{ - BFNTTState *s; - int inverse, i, j, k, l; - limb_t c, c_inv, c_inv2, m, m_inv; - - if (s1->ntt_state) - return 0; -#if defined(__AVX2__) - s = bf_aligned_malloc(s1, sizeof(*s), 64); -#else - s = bf_malloc(s1, sizeof(*s)); -#endif - if (!s) - return -1; - memset(s, 0, sizeof(*s)); - s1->ntt_state = s; - s->ctx = s1; - - for(j = 0; j < NB_MODS; j++) { - m = ntt_mods[j]; - m_inv = init_mul_mod_fast(m); - s->ntt_mods_div[j] = m_inv; -#if defined(__AVX2__) - s->ntt_mods_vec[j] = _mm256_set1_pd(m); - s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m); -#endif - c_inv2 = (m + 1) / 2; /* 1/2 */ - c_inv = 1; - for(i = 0; i <= NTT_PROOT_2EXP; i++) { - s->ntt_len_inv[j][i][0] = c_inv; - s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m); - c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv); - } - - for(inverse = 0; inverse < 2; inverse++) { - c = ntt_proot[inverse][j]; - for(i = 0; i < NTT_PROOT_2EXP; i++) { - s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c; - s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] = - init_mul_mod_fast2(c, m); - c = mul_mod_fast(c, c, m, m_inv); - } - } - } - - l = 0; - for(j = 0; j < NB_MODS - 1; j++) { - for(k = j + 1; k < NB_MODS; k++) { -#if defined(__AVX2__) - s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l], - ntt_mods[k])); -#else - s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l], - ntt_mods[k]); -#endif - l++; - } - } - return 0; -} - -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) -{ - int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found; - int int_bits, nb_mods_found; - limb_t cost, min_cost; - - min_cost = -1; - dpl_found = 0; - nb_mods_found = 4; - fft_len_log2_found = 0; - for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) { - int_bits = ntt_int_bits[NB_MODS - nb_mods]; - dpl = bf_min((int_bits - 4) / 2, - 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX); - for(;;) { - fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl); - if (fft_len_log2 > NTT_PROOT_2EXP) - goto next; - n_bits = fft_len_log2 + 2 * dpl; - if (n_bits <= int_bits) { - cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods; - // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost); - if (cost < min_cost) { - min_cost = cost; - dpl_found = dpl; - nb_mods_found = nb_mods; - fft_len_log2_found = fft_len_log2; - } - break; - } - dpl--; - if (dpl == 0) - break; - } - next: ; - } - if (!dpl_found) - abort(); - /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */ - if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) && - ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >= - len * LIMB_BITS) { - dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN; - } - *pnb_mods = nb_mods_found; - *pdpl = dpl_found; - return fft_len_log2_found; -} - -/* return 0 if OK, -1 if memory error */ -static no_inline int fft_mul(bf_context_t *s1, - bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags) -{ - BFNTTState *s; - int dpl, fft_len_log2, j, nb_mods, reduced_mem; - slimb_t len, fft_len; - NTTLimb *buf1, *buf2, *ptr; -#if defined(USE_MUL_CHECK) - limb_t ha, hb, hr, h_ref; -#endif - - if (ntt_static_init(s1)) - return -1; - s = s1->ntt_state; - - /* find the optimal number of digits per limb (dpl) */ - len = a_len + b_len; - fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len); - fft_len = (uint64_t)1 << fft_len_log2; - // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl); -#if defined(USE_MUL_CHECK) - ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0); - hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0); -#endif - if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) { - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); - } else if (mul_flags & FFT_MUL_R_OVERLAP_B) { - limb_t *tmp_tab, tmp_len; - /* it is better to free 'b' first */ - tmp_tab = a_tab; - a_tab = b_tab; - b_tab = tmp_tab; - tmp_len = a_len; - a_len = b_len; - b_len = tmp_len; - } - buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); - if (!buf1) - return -1; - limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl, - NB_MODS - nb_mods, nb_mods); - if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == - FFT_MUL_R_OVERLAP_A) { - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); - } - reduced_mem = (fft_len_log2 >= 14); - if (!reduced_mem) { - buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); - if (!buf2) - goto fail; - limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, - NB_MODS - nb_mods, nb_mods); - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); /* in case res == b */ - } else { - buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len); - if (!buf2) - goto fail; - } - for(j = 0; j < nb_mods; j++) { - if (reduced_mem) { - limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, - NB_MODS - nb_mods + j, 1); - ptr = buf2; - } else { - ptr = buf2 + fft_len * j; - } - if (ntt_conv(s, buf1 + fft_len * j, ptr, - fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) - goto fail; - } - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); /* in case res == b and reduced mem */ - ntt_free(s, buf2); - buf2 = NULL; - if (!(mul_flags & FFT_MUL_R_NORESIZE)) { - if (bf_resize(res, len)) - goto fail; - } - ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods); - ntt_free(s, buf1); -#if defined(USE_MUL_CHECK) - hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0); - h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD); - if (hr != h_ref) { - printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n", - len, fft_len_log2, dpl, nb_mods); - // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref); - exit(1); - } -#endif - return 0; - fail: - ntt_free(s, buf1); - ntt_free(s, buf2); - return -1; -} - -#else /* USE_FFT_MUL */ - -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) -{ - return 0; -} - -#endif /* !USE_FFT_MUL */ diff --git a/quickjs/libbf.h b/quickjs/libbf.h deleted file mode 100644 index 48e9d956a2..0000000000 --- a/quickjs/libbf.h +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Tiny arbitrary precision floating point library - * - * Copyright (c) 2017-2021 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 LIBBF_H -#define LIBBF_H - -#include -#include - -#if INTPTR_MAX >= INT64_MAX -#define LIMB_LOG2_BITS 6 -#else -#define LIMB_LOG2_BITS 5 -#endif - -#define LIMB_BITS (1 << LIMB_LOG2_BITS) - -#if LIMB_BITS == 64 -typedef __int128 int128_t; -typedef unsigned __int128 uint128_t; -typedef int64_t slimb_t; -typedef uint64_t limb_t; -typedef uint128_t dlimb_t; -#define BF_RAW_EXP_MIN INT64_MIN -#define BF_RAW_EXP_MAX INT64_MAX - -#define LIMB_DIGITS 19 -#define BF_DEC_BASE UINT64_C(10000000000000000000) - -#else - -typedef int32_t slimb_t; -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -#define BF_RAW_EXP_MIN INT32_MIN -#define BF_RAW_EXP_MAX INT32_MAX - -#define LIMB_DIGITS 9 -#define BF_DEC_BASE 1000000000U - -#endif - -/* in bits */ -/* minimum number of bits for the exponent */ -#define BF_EXP_BITS_MIN 3 -/* maximum number of bits for the exponent */ -#define BF_EXP_BITS_MAX (LIMB_BITS - 3) -/* extended range for exponent, used internally */ -#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) -/* minimum possible precision */ -#define BF_PREC_MIN 2 -/* minimum possible precision */ -#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) -/* some operations support infinite precision */ -#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ - -#if LIMB_BITS == 64 -#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) -#else -#define BF_CHKSUM_MOD 975620677U -#endif - -#define BF_EXP_ZERO BF_RAW_EXP_MIN -#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) -#define BF_EXP_NAN BF_RAW_EXP_MAX - -/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, - +/-infinity is represented with expn = BF_EXP_INF and len = 0, - NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) - */ -typedef struct { - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bf_t; - -typedef struct { - /* must be kept identical to bf_t */ - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bfdec_t; - -typedef enum { - BF_RNDN, /* round to nearest, ties to even */ - BF_RNDZ, /* round to zero */ - BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ - BF_RNDU, /* round to +inf */ - BF_RNDNA, /* round to nearest, ties away from zero */ - BF_RNDA, /* round away from zero */ - BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, - inexact flag is always set) */ -} bf_rnd_t; - -/* allow subnormal numbers. Only available if the number of exponent - bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ -#define BF_FLAG_SUBNORMAL (1 << 3) -/* 'prec' is the precision after the radix point instead of the whole - mantissa. Can only be used with bf_round() and - bfdec_[add|sub|mul|div|sqrt|round](). */ -#define BF_FLAG_RADPNT_PREC (1 << 4) - -#define BF_RND_MASK 0x7 -#define BF_EXP_BITS_SHIFT 5 -#define BF_EXP_BITS_MASK 0x3f - -/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ -#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) - -/* contains the rounding mode and number of exponents bits */ -typedef uint32_t bf_flags_t; - -typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); - -typedef struct { - bf_t val; - limb_t prec; -} BFConstCache; - -typedef struct bf_context_t { - void *realloc_opaque; - bf_realloc_func_t *realloc_func; - BFConstCache log2_cache; - BFConstCache pi_cache; - struct BFNTTState *ntt_state; -} bf_context_t; - -static inline int bf_get_exp_bits(bf_flags_t flags) -{ - int e; - e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; - if (e == BF_EXP_BITS_MASK) - return BF_EXP_BITS_MAX + 1; - else - return BF_EXP_BITS_MAX - e; -} - -static inline bf_flags_t bf_set_exp_bits(int n) -{ - return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; -} - -/* returned status */ -#define BF_ST_INVALID_OP (1 << 0) -#define BF_ST_DIVIDE_ZERO (1 << 1) -#define BF_ST_OVERFLOW (1 << 2) -#define BF_ST_UNDERFLOW (1 << 3) -#define BF_ST_INEXACT (1 << 4) -/* indicate that a memory allocation error occured. NaN is returned */ -#define BF_ST_MEM_ERROR (1 << 5) - -#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ - -static inline slimb_t bf_max(slimb_t a, slimb_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline slimb_t bf_min(slimb_t a, slimb_t b) -{ - if (a < b) - return a; - else - return b; -} - -void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, - void *realloc_opaque); -void bf_context_end(bf_context_t *s); -/* free memory allocated for the bf cache data */ -void bf_clear_cache(bf_context_t *s); - -static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) -{ - return s->realloc_func(s->realloc_opaque, ptr, size); -} - -/* 'size' must be != 0 */ -static inline void *bf_malloc(bf_context_t *s, size_t size) -{ - return bf_realloc(s, NULL, size); -} - -static inline void bf_free(bf_context_t *s, void *ptr) -{ - /* must test ptr otherwise equivalent to malloc(0) */ - if (ptr) - bf_realloc(s, ptr, 0); -} - -void bf_init(bf_context_t *s, bf_t *r); - -static inline void bf_delete(bf_t *r) -{ - bf_context_t *s = r->ctx; - /* we accept to delete a zeroed bf_t structure */ - if (s && r->tab) { - bf_realloc(s, r->tab, 0); - } -} - -static inline void bf_neg(bf_t *r) -{ - r->sign ^= 1; -} - -static inline int bf_is_finite(const bf_t *a) -{ - return (a->expn < BF_EXP_INF); -} - -static inline int bf_is_nan(const bf_t *a) -{ - return (a->expn == BF_EXP_NAN); -} - -static inline int bf_is_zero(const bf_t *a) -{ - return (a->expn == BF_EXP_ZERO); -} - -static inline void bf_memcpy(bf_t *r, const bf_t *a) -{ - *r = *a; -} - -int bf_set_ui(bf_t *r, uint64_t a); -int bf_set_si(bf_t *r, int64_t a); -void bf_set_nan(bf_t *r); -void bf_set_zero(bf_t *r, int is_neg); -void bf_set_inf(bf_t *r, int is_neg); -int bf_set(bf_t *r, const bf_t *a); -void bf_move(bf_t *r, bf_t *a); -int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); -int bf_set_float64(bf_t *a, double d); - -int bf_cmpu(const bf_t *a, const bf_t *b); -int bf_cmp_full(const bf_t *a, const bf_t *b); -int bf_cmp(const bf_t *a, const bf_t *b); -static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) == 0; -} - -static inline int bf_cmp_le(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) <= 0; -} - -static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) < 0; -} - -int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); -int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); -int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -#define BF_DIVREM_EUCLIDIAN BF_RNDF -int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode); -int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -/* round to integer with infinite precision */ -int bf_rint(bf_t *r, int rnd_mode); -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); -int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); -int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -slimb_t bf_get_exp_min(const bf_t *a); -int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); -int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); -int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); - -/* additional flags for bf_atof */ -/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ -#define BF_ATOF_NO_HEX (1 << 16) -/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ -#define BF_ATOF_BIN_OCT (1 << 17) -/* Do not parse NaN or Inf */ -#define BF_ATOF_NO_NAN_INF (1 << 18) -/* return the exponent separately */ -#define BF_ATOF_EXPONENT (1 << 19) - -int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -/* this version accepts prec = BF_PREC_INF and returns the radix - exponent */ -int bf_atof2(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, - slimb_t expn, limb_t prec, bf_flags_t flags); - - -/* Conversion of floating point number to string. Return a null - terminated string or NULL if memory error. *plen contains its - length if plen != NULL. The exponent letter is "e" for base 10, - "p" for bases 2, 8, 16 with a binary exponent and "@" for the other - bases. */ - -#define BF_FTOA_FORMAT_MASK (3 << 16) - -/* fixed format: prec significant digits rounded with (flags & - BF_RND_MASK). Exponential notation is used if too many zeros are - needed.*/ -#define BF_FTOA_FORMAT_FIXED (0 << 16) -/* fractional format: prec digits after the decimal point rounded with - (flags & BF_RND_MASK) */ -#define BF_FTOA_FORMAT_FRAC (1 << 16) -/* free format: - - For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum - number of digits to represent 'a'. The precision and the rounding - mode are ignored. - - For the non binary radices with bf_ftoa(): use as many digits as - necessary so that bf_atof() return the same number when using - precision 'prec', rounding to nearest and the subnormal - configuration of 'flags'. The result is meaningful only if 'a' is - already rounded to 'prec' bits. If the subnormal flag is set, the - exponent in 'flags' must also be set to the desired exponent range. -*/ -#define BF_FTOA_FORMAT_FREE (2 << 16) -/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits - (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for - binary radices with bf_ftoa() and for bfdec_ftoa(). */ -#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) - -/* force exponential notation for fixed or free format */ -#define BF_FTOA_FORCE_EXP (1 << 20) -/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for - base 2 if non zero value */ -#define BF_FTOA_ADD_PREFIX (1 << 21) -/* return "Infinity" instead of "Inf" and add a "+" for positive - exponents */ -#define BF_FTOA_JS_QUIRKS (1 << 22) - -char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags); - -/* modulo 2^n instead of saturation. NaN and infinity return 0 */ -#define BF_GET_INT_MOD (1 << 0) -int bf_get_int32(int *pres, const bf_t *a, int flags); -int bf_get_int64(int64_t *pres, const bf_t *a, int flags); -int bf_get_uint64(uint64_t *pres, const bf_t *a); - -/* the following functions are exported for testing only. */ -void mp_print_str(const char *str, const limb_t *tab, limb_t n); -void bf_print_str(const char *str, const bf_t *a); -int bf_resize(bf_t *r, limb_t len); -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); -slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, - int is_ceil1); -int mp_mul(bf_context_t *s, limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size); -limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, - limb_t n, limb_t carry); -limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); -int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); -int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); -limb_t bf_isqrt(limb_t a); - -/* transcendental functions */ -int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ -int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); -int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, - limb_t prec, bf_flags_t flags); -int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); - -/* decimal floating point */ - -static inline void bfdec_init(bf_context_t *s, bfdec_t *r) -{ - bf_init(s, (bf_t *)r); -} -static inline void bfdec_delete(bfdec_t *r) -{ - bf_delete((bf_t *)r); -} - -static inline void bfdec_neg(bfdec_t *r) -{ - r->sign ^= 1; -} - -static inline int bfdec_is_finite(const bfdec_t *a) -{ - return (a->expn < BF_EXP_INF); -} - -static inline int bfdec_is_nan(const bfdec_t *a) -{ - return (a->expn == BF_EXP_NAN); -} - -static inline int bfdec_is_zero(const bfdec_t *a) -{ - return (a->expn == BF_EXP_ZERO); -} - -static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) -{ - bf_memcpy((bf_t *)r, (const bf_t *)a); -} - -int bfdec_set_ui(bfdec_t *r, uint64_t a); -int bfdec_set_si(bfdec_t *r, int64_t a); - -static inline void bfdec_set_nan(bfdec_t *r) -{ - bf_set_nan((bf_t *)r); -} -static inline void bfdec_set_zero(bfdec_t *r, int is_neg) -{ - bf_set_zero((bf_t *)r, is_neg); -} -static inline void bfdec_set_inf(bfdec_t *r, int is_neg) -{ - bf_set_inf((bf_t *)r, is_neg); -} -static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) -{ - return bf_set((bf_t *)r, (bf_t *)a); -} -static inline void bfdec_move(bfdec_t *r, bfdec_t *a) -{ - bf_move((bf_t *)r, (bf_t *)a); -} -static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmpu((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmp_full((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmp((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) == 0; -} -static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) <= 0; -} -static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) < 0; -} - -int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode); -int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bfdec_rint(bfdec_t *r, int rnd_mode); -int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); -int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); -int bfdec_get_int32(int *pres, const bfdec_t *a); -int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); - -char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); -int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, - limb_t prec, bf_flags_t flags); - -/* the following functions are exported for testing only. */ -extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; -void bfdec_print_str(const char *str, const bfdec_t *a); -static inline int bfdec_resize(bfdec_t *r, limb_t len) -{ - return bf_resize((bf_t *)r, len); -} -int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); - -#endif /* LIBBF_H */ diff --git a/quickjs/libregexp-opcode.h b/quickjs/libregexp-opcode.h index f90c23b347..ebab751dfc 100644 --- a/quickjs/libregexp-opcode.h +++ b/quickjs/libregexp-opcode.h @@ -1,6 +1,6 @@ /* * Regular Expression Engine - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,11 +26,15 @@ DEF(invalid, 1) /* never used */ DEF(char, 3) +DEF(char_i, 3) DEF(char32, 5) +DEF(char32_i, 5) DEF(dot, 1) DEF(any, 1) /* same as dot but match any character including line terminator */ DEF(line_start, 1) +DEF(line_start_m, 1) DEF(line_end, 1) +DEF(line_end_m, 1) DEF(goto, 5) DEF(split_goto_first, 5) DEF(split_next_first, 5) @@ -42,16 +46,21 @@ DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ DEF(push_i32, 5) /* push integer on the stack */ DEF(drop, 1) DEF(word_boundary, 1) +DEF(word_boundary_i, 1) DEF(not_word_boundary, 1) +DEF(not_word_boundary_i, 1) DEF(back_reference, 2) -DEF(backward_back_reference, 2) /* must come after back_reference */ +DEF(back_reference_i, 2) /* must come after */ +DEF(backward_back_reference, 2) /* must come after */ +DEF(backward_back_reference_i, 2) /* must come after */ DEF(range, 3) /* variable length */ +DEF(range_i, 3) /* variable length */ DEF(range32, 3) /* variable length */ +DEF(range32_i, 3) /* variable length */ DEF(lookahead, 5) DEF(negative_lookahead, 5) DEF(push_char_pos, 1) /* push the character position on the stack */ -DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character - position */ +DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */ DEF(prev, 1) /* go to the previous char */ DEF(simple_greedy_quant, 17) diff --git a/quickjs/libregexp.c b/quickjs/libregexp.c index dab8fa1e2e..2b33c86953 100644 --- a/quickjs/libregexp.c +++ b/quickjs/libregexp.c @@ -1,6 +1,6 @@ /* * Regular Expression Engine - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -30,13 +30,11 @@ #include "cutils.h" #include "libregexp.h" +#include "libunicode.h" /* TODO: - - Add full unicode canonicalize rules for character ranges (not - really useful but needed for exact "ignorecase" compatibility). - - Add a lock step execution mode (=linear time execution guaranteed) when the regular expression is "simple" i.e. no backreference nor complicated lookahead. The opcodes are designed for this execution @@ -56,6 +54,9 @@ typedef enum { #define CAPTURE_COUNT_MAX 255 #define STACK_SIZE_MAX 255 +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define INTERRUPT_COUNTER_INIT 10000 /* unicode code points */ #define CP_LS 0x2028 @@ -69,8 +70,10 @@ typedef struct { const uint8_t *buf_end; const uint8_t *buf_start; int re_flags; - BOOL is_utf16; + BOOL is_unicode; + BOOL unicode_sets; /* if set, is_unicode is also set */ BOOL ignore_case; + BOOL multi_line; BOOL dotall; int capture_count; int total_capture_count; /* -1 = not computed yet */ @@ -101,10 +104,11 @@ static const REOpCode reopcode_info[REOP_COUNT] = { }; #define RE_HEADER_FLAGS 0 -#define RE_HEADER_CAPTURE_COUNT 1 -#define RE_HEADER_STACK_SIZE 2 +#define RE_HEADER_CAPTURE_COUNT 2 +#define RE_HEADER_STACK_SIZE 3 +#define RE_HEADER_BYTECODE_LEN 4 -#define RE_HEADER_LEN 7 +#define RE_HEADER_LEN 8 static inline int is_digit(int c) { return c >= '0' && c <= '9'; @@ -120,31 +124,262 @@ static int dbuf_insert(DynBuf *s, int pos, int len) return 0; } -/* canonicalize with the specific JS regexp rules */ -static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16) +typedef struct REString { + struct REString *next; + uint32_t hash; + uint32_t len; + uint32_t buf[]; +} REString; + +typedef struct { + /* the string list is the union of 'char_range' and of the strings + in hash_table[]. The strings in hash_table[] have a length != + 1. */ + CharRange cr; + uint32_t n_strings; + uint32_t hash_size; + int hash_bits; + REString **hash_table; +} REStringList; + +static uint32_t re_string_hash(int len, const uint32_t *buf) { - uint32_t res[LRE_CC_RES_LEN_MAX]; - int len; - if (is_utf16) { - if (likely(c < 128)) { - if (c >= 'A' && c <= 'Z') - c = c - 'A' + 'a'; - } else { - lre_case_conv(res, c, 2); - c = res[0]; + int i; + uint32_t h; + h = 1; + for(i = 0; i < len; i++) + h = h * 263 + buf[i]; + return h * 0x61C88647; +} + +static void re_string_list_init(REParseState *s1, REStringList *s) +{ + cr_init(&s->cr, s1->opaque, lre_realloc); + s->n_strings = 0; + s->hash_size = 0; + s->hash_bits = 0; + s->hash_table = NULL; +} + +static void re_string_list_free(REStringList *s) +{ + REString *p, *p_next; + int i; + for(i = 0; i < s->hash_size; i++) { + for(p = s->hash_table[i]; p != NULL; p = p_next) { + p_next = p->next; + lre_realloc(s->cr.mem_opaque, p, 0); } + } + lre_realloc(s->cr.mem_opaque, s->hash_table, 0); + + cr_free(&s->cr); +} + +static void lre_print_char(int c, BOOL is_range) +{ + if (c == '\'' || c == '\\' || + (is_range && (c == '-' || c == ']'))) { + printf("\\%c", c); + } else if (c >= ' ' && c <= 126) { + printf("%c", c); } else { - if (likely(c < 128)) { - if (c >= 'a' && c <= 'z') - c = c - 'a' + 'A'; - } else { - /* legacy regexp: to upper case if single char >= 128 */ - len = lre_case_conv(res, c, FALSE); - if (len == 1 && res[0] >= 128) - c = res[0]; + printf("\\u{%04x}", c); + } +} + +static __maybe_unused void re_string_list_dump(const char *str, const REStringList *s) +{ + REString *p; + const CharRange *cr; + int i, j, k; + + printf("%s:\n", str); + printf(" ranges: ["); + cr = &s->cr; + for(i = 0; i < cr->len; i += 2) { + lre_print_char(cr->points[i], TRUE); + if (cr->points[i] != cr->points[i + 1] - 1) { + printf("-"); + lre_print_char(cr->points[i + 1] - 1, TRUE); + } + } + printf("]\n"); + + j = 0; + for(i = 0; i < s->hash_size; i++) { + for(p = s->hash_table[i]; p != NULL; p = p->next) { + printf(" %d/%d: '", j, s->n_strings); + for(k = 0; k < p->len; k++) { + lre_print_char(p->buf[k], FALSE); + } + printf("'\n"); + j++; } } - return c; +} + +static int re_string_find2(REStringList *s, int len, const uint32_t *buf, + uint32_t h0, BOOL add_flag) +{ + uint32_t h = 0; /* avoid warning */ + REString *p; + if (s->n_strings != 0) { + h = h0 >> (32 - s->hash_bits); + for(p = s->hash_table[h]; p != NULL; p = p->next) { + if (p->hash == h0 && p->len == len && + !memcmp(p->buf, buf, len * sizeof(buf[0]))) { + return 1; + } + } + } + /* not found */ + if (!add_flag) + return 0; + /* increase the size of the hash table if needed */ + if (unlikely((s->n_strings + 1) > s->hash_size)) { + REString **new_hash_table, *p_next; + int new_hash_bits, i; + uint32_t new_hash_size; + new_hash_bits = max_int(s->hash_bits + 1, 4); + new_hash_size = 1 << new_hash_bits; + new_hash_table = lre_realloc(s->cr.mem_opaque, NULL, + sizeof(new_hash_table[0]) * new_hash_size); + if (!new_hash_table) + return -1; + memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size); + for(i = 0; i < s->hash_size; i++) { + for(p = s->hash_table[i]; p != NULL; p = p_next) { + p_next = p->next; + h = p->hash >> (32 - new_hash_bits); + p->next = new_hash_table[h]; + new_hash_table[h] = p; + } + } + lre_realloc(s->cr.mem_opaque, s->hash_table, 0); + s->hash_bits = new_hash_bits; + s->hash_size = new_hash_size; + s->hash_table = new_hash_table; + h = h0 >> (32 - s->hash_bits); + } + + p = lre_realloc(s->cr.mem_opaque, NULL, sizeof(REString) + len * sizeof(buf[0])); + if (!p) + return -1; + p->next = s->hash_table[h]; + s->hash_table[h] = p; + s->n_strings++; + p->hash = h0; + p->len = len; + memcpy(p->buf, buf, sizeof(buf[0]) * len); + return 1; +} + +static int re_string_find(REStringList *s, int len, const uint32_t *buf, + BOOL add_flag) +{ + uint32_t h0; + h0 = re_string_hash(len, buf); + return re_string_find2(s, len, buf, h0, add_flag); +} + +/* return -1 if memory error, 0 if OK */ +static int re_string_add(REStringList *s, int len, const uint32_t *buf) +{ + if (len == 1) { + return cr_union_interval(&s->cr, buf[0], buf[0]); + } + if (re_string_find(s, len, buf, TRUE) < 0) + return -1; + return 0; +} + +/* a = a op b */ +static int re_string_list_op(REStringList *a, REStringList *b, int op) +{ + int i, ret; + REString *p, **pp; + + if (cr_op1(&a->cr, b->cr.points, b->cr.len, op)) + return -1; + + switch(op) { + case CR_OP_UNION: + if (b->n_strings != 0) { + for(i = 0; i < b->hash_size; i++) { + for(p = b->hash_table[i]; p != NULL; p = p->next) { + if (re_string_find2(a, p->len, p->buf, p->hash, TRUE) < 0) + return -1; + } + } + } + break; + case CR_OP_INTER: + case CR_OP_SUB: + for(i = 0; i < a->hash_size; i++) { + pp = &a->hash_table[i]; + for(;;) { + p = *pp; + if (p == NULL) + break; + ret = re_string_find2(b, p->len, p->buf, p->hash, FALSE); + if (op == CR_OP_SUB) + ret = !ret; + if (!ret) { + /* remove it */ + *pp = p->next; + a->n_strings--; + lre_realloc(a->cr.mem_opaque, p, 0); + } else { + /* keep it */ + pp = &p->next; + } + } + } + break; + default: + abort(); + } + return 0; +} + +static int re_string_list_canonicalize(REParseState *s1, + REStringList *s, BOOL is_unicode) +{ + if (cr_regexp_canonicalize(&s->cr, is_unicode)) + return -1; + if (s->n_strings != 0) { + REStringList a_s, *a = &a_s; + int i, j; + REString *p; + + /* XXX: simplify */ + re_string_list_init(s1, a); + + a->n_strings = s->n_strings; + a->hash_size = s->hash_size; + a->hash_bits = s->hash_bits; + a->hash_table = s->hash_table; + + s->n_strings = 0; + s->hash_size = 0; + s->hash_bits = 0; + s->hash_table = NULL; + + for(i = 0; i < a->hash_size; i++) { + for(p = a->hash_table[i]; p != NULL; p = p->next) { + for(j = 0; j < p->len; j++) { + p->buf[j] = lre_canonicalize(p->buf[j], is_unicode); + } + if (re_string_add(s, p->len, p->buf)) { + re_string_list_free(a); + return -1; + } + } + } + re_string_list_free(a); + } + return 0; } static const uint16_t char_range_d[] = { @@ -170,32 +405,6 @@ static const uint16_t char_range_s[] = { 0xFEFF, 0xFEFF + 1, }; -BOOL lre_is_space(int c) -{ - int i, n, low, high; - n = (countof(char_range_s) - 1) / 2; - for(i = 0; i < n; i++) { - low = char_range_s[2 * i + 1]; - if (c < low) - return FALSE; - high = char_range_s[2 * i + 2]; - if (c < high) - return TRUE; - } - return FALSE; -} - -uint32_t const lre_id_start_table_ascii[4] = { - /* $ A-Z _ a-z */ - 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE -}; - -uint32_t const lre_id_continue_table_ascii[4] = { - /* $ 0-9 A-Z _ a-z */ - 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE -}; - - static const uint16_t char_range_w[] = { 4, 0x0030, 0x0039 + 1, @@ -215,80 +424,55 @@ typedef enum { CHAR_RANGE_W, } CharRangeEnum; -static const uint16_t *char_range_table[] = { +static const uint16_t * const char_range_table[] = { char_range_d, char_range_s, char_range_w, }; -static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) +static int cr_init_char_range(REParseState *s, REStringList *cr, uint32_t c) { BOOL invert; const uint16_t *c_pt; int len, i; - + invert = c & 1; c_pt = char_range_table[c >> 1]; len = *c_pt++; - cr_init(cr, s->opaque, lre_realloc); + re_string_list_init(s, cr); for(i = 0; i < len * 2; i++) { - if (cr_add_point(cr, c_pt[i])) + if (cr_add_point(&cr->cr, c_pt[i])) goto fail; } if (invert) { - if (cr_invert(cr)) + if (cr_invert(&cr->cr)) goto fail; } return 0; fail: - cr_free(cr); + re_string_list_free(cr); return -1; } -static int cr_canonicalize(CharRange *cr) -{ - CharRange a; - uint32_t pt[2]; - int i, ret; - - cr_init(&a, cr->mem_opaque, lre_realloc); - pt[0] = 'a'; - pt[1] = 'z' + 1; - ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER); - if (ret) - goto fail; - /* convert to upper case */ - /* XXX: the generic unicode case would be much more complicated - and not really useful */ - for(i = 0; i < a.len; i++) { - a.points[i] += 'A' - 'a'; - } - /* Note: for simplicity we keep the lower case ranges */ - ret = cr_union1(cr, a.points, a.len); - fail: - cr_free(&a); - return ret; -} - #ifdef DUMP_REOP static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, int buf_len) { int pos, len, opcode, bc_len, re_flags, i; uint32_t val; - + assert(buf_len >= RE_HEADER_LEN); - re_flags= buf[0]; - bc_len = get_u32(buf + 3); + re_flags = lre_get_flags(buf); + bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); assert(bc_len + RE_HEADER_LEN <= buf_len); printf("flags: 0x%x capture_count=%d stack_size=%d\n", - re_flags, buf[1], buf[2]); + re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); if (re_flags & LRE_FLAG_NAMED_GROUPS) { const char *p; p = (char *)buf + RE_HEADER_LEN + bc_len; printf("named groups: "); - for(i = 1; i < buf[1]; i++) { + for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) { if (i != 1) printf(","); printf("<%s>", p); @@ -316,6 +500,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, printf("%s", reopcode_info[opcode].name); switch(opcode) { case REOP_char: + case REOP_char_i: val = get_u16(buf + pos + 1); if (val >= ' ' && val <= 126) printf(" '%c'", val); @@ -323,6 +508,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, printf(" 0x%04x", val); break; case REOP_char32: + case REOP_char32_i: val = get_u32(buf + pos + 1); if (val >= ' ' && val <= 126) printf(" '%c'", val); @@ -335,7 +521,6 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, case REOP_loop: case REOP_lookahead: case REOP_negative_lookahead: - case REOP_bne_char_pos: val = get_u32(buf + pos + 1); val += (pos + 5); printf(" %u", val); @@ -350,7 +535,9 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, case REOP_save_start: case REOP_save_end: case REOP_back_reference: + case REOP_back_reference_i: case REOP_backward_back_reference: + case REOP_backward_back_reference_i: printf(" %u", buf[pos + 1]); break; case REOP_save_reset: @@ -361,6 +548,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, printf(" %d", val); break; case REOP_range: + case REOP_range_i: { int n, i; n = get_u16(buf + pos + 1); @@ -372,6 +560,7 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, } break; case REOP_range32: + case REOP_range32_i: { int n, i; n = get_u16(buf + pos + 1); @@ -448,7 +637,7 @@ static int parse_digits(const uint8_t **pp, BOOL allow_overflow) const uint8_t *p; uint64_t v; int c; - + p = *pp; v = 0; for(;;) { @@ -520,7 +709,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16) { int h, n, i; uint32_t c1; - + if (*p == '{' && allow_utf16) { p++; c = 0; @@ -550,7 +739,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16) } c = (c << 4) | h; } - if (c >= 0xd800 && c < 0xdc00 && + if (is_hi_surrogate(c) && allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { /* convert an escaped surrogate pair into a unicode char */ @@ -561,9 +750,9 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16) break; c1 = (c1 << 4) | h; } - if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) { + if (i == 4 && is_lo_surrogate(c1)) { p += 6; - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + c = from_surrogate(c, c1); } } } @@ -610,8 +799,16 @@ static BOOL is_unicode_char(int c) (c == '_')); } -static int parse_unicode_property(REParseState *s, CharRange *cr, - const uint8_t **pp, BOOL is_inv) +/* XXX: memory error test */ +static void seq_prop_cb(void *opaque, const uint32_t *seq, int seq_len) +{ + REStringList *sl = opaque; + re_string_add(sl, seq_len, seq); +} + +static int parse_unicode_property(REParseState *s, REStringList *cr, + const uint8_t **pp, BOOL is_inv, + BOOL allow_sequence_prop) { const uint8_t *p; char name[64], value[64]; @@ -651,51 +848,76 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { script_ext = TRUE; do_script: - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_script(cr, value, script_ext); + re_string_list_init(s, cr); + ret = unicode_script(&cr->cr, value, script_ext); if (ret) { - cr_free(cr); + re_string_list_free(cr); if (ret == -2) return re_parse_error(s, "unknown unicode script"); else goto out_of_memory; } } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_general_category(cr, value); + re_string_list_init(s, cr); + ret = unicode_general_category(&cr->cr, value); if (ret) { - cr_free(cr); + re_string_list_free(cr); if (ret == -2) return re_parse_error(s, "unknown unicode general category"); else goto out_of_memory; } } else if (value[0] == '\0') { - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_general_category(cr, name); + re_string_list_init(s, cr); + ret = unicode_general_category(&cr->cr, name); if (ret == -1) { - cr_free(cr); + re_string_list_free(cr); goto out_of_memory; } if (ret < 0) { - ret = unicode_prop(cr, name); - if (ret) { - cr_free(cr); - if (ret == -2) - goto unknown_property_name; - else - goto out_of_memory; + ret = unicode_prop(&cr->cr, name); + if (ret == -1) { + re_string_list_free(cr); + goto out_of_memory; } } + if (ret < 0 && !is_inv && allow_sequence_prop) { + CharRange cr_tmp; + cr_init(&cr_tmp, s->opaque, lre_realloc); + ret = unicode_sequence_prop(name, seq_prop_cb, cr, &cr_tmp); + cr_free(&cr_tmp); + if (ret == -1) { + re_string_list_free(cr); + goto out_of_memory; + } + } + if (ret < 0) + goto unknown_property_name; } else { unknown_property_name: return re_parse_error(s, "unknown unicode property name"); } + /* the ordering of case folding and inversion differs with + unicode_sets. 'unicode_sets' ordering is more consistent */ + /* XXX: the spec seems incorrect, we do it as the other engines + seem to do it. */ + if (s->ignore_case && s->unicode_sets) { + if (re_string_list_canonicalize(s, cr, s->is_unicode)) { + re_string_list_free(cr); + goto out_of_memory; + } + } if (is_inv) { - if (cr_invert(cr)) { - cr_free(cr); - return -1; + if (cr_invert(&cr->cr)) { + re_string_list_free(cr); + goto out_of_memory; + } + } + if (s->ignore_case && !s->unicode_sets) { + if (re_string_list_canonicalize(s, cr, s->is_unicode)) { + re_string_list_free(cr); + goto out_of_memory; } } *pp = p; @@ -705,16 +927,67 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, } #endif /* CONFIG_ALL_UNICODE */ +static int get_class_atom(REParseState *s, REStringList *cr, + const uint8_t **pp, BOOL inclass); + +static int parse_class_string_disjunction(REParseState *s, REStringList *cr, + const uint8_t **pp) +{ + const uint8_t *p; + DynBuf str; + int c; + + p = *pp; + if (*p != '{') + return re_parse_error(s, "expecting '{' after \\q"); + + dbuf_init2(&str, s->opaque, lre_realloc); + re_string_list_init(s, cr); + + p++; + for(;;) { + str.size = 0; + while (*p != '}' && *p != '|') { + c = get_class_atom(s, NULL, &p, FALSE); + if (c < 0) + goto fail; + if (dbuf_put_u32(&str, c)) { + re_parse_out_of_memory(s); + goto fail; + } + } + if (re_string_add(cr, str.size / 4, (uint32_t *)str.buf)) { + re_parse_out_of_memory(s); + goto fail; + } + if (*p == '}') + break; + p++; + } + if (s->ignore_case) { + if (re_string_list_canonicalize(s, cr, TRUE)) + goto fail; + } + p++; /* skip the '}' */ + dbuf_free(&str); + *pp = p; + return 0; + fail: + dbuf_free(&str); + re_string_list_free(cr); + return -1; +} + /* return -1 if error otherwise the character or a class range - (CLASS_RANGE_BASE). In case of class range, 'cr' is + (CLASS_RANGE_BASE) if cr != NULL. In case of class range, 'cr' is initialized. Otherwise, it is ignored. */ -static int get_class_atom(REParseState *s, CharRange *cr, +static int get_class_atom(REParseState *s, REStringList *cr, const uint8_t **pp, BOOL inclass) { const uint8_t *p; uint32_t c; int ret; - + p = *pp; c = *p; @@ -743,6 +1016,8 @@ static int get_class_atom(REParseState *s, CharRange *cr, case 'W': c = CHAR_RANGE_W; class_range: + if (!cr) + goto default_escape; if (cr_init_char_range(s, cr, c)) return -1; c = CLASS_RANGE_BASE; @@ -752,10 +1027,10 @@ static int get_class_atom(REParseState *s, CharRange *cr, if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (((c >= '0' && c <= '9') || c == '_') && - inclass && !s->is_utf16)) { /* Annex B.1.4 */ + inclass && !s->is_unicode)) { /* Annex B.1.4 */ c &= 0x1f; p++; - } else if (s->is_utf16) { + } else if (s->is_unicode) { goto invalid_escape; } else { /* otherwise return '\' and 'c' */ @@ -763,27 +1038,54 @@ static int get_class_atom(REParseState *s, CharRange *cr, 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; #ifdef CONFIG_ALL_UNICODE case 'p': case 'P': - if (s->is_utf16) { - if (parse_unicode_property(s, cr, &p, (c == 'P'))) + if (s->is_unicode && cr) { + if (parse_unicode_property(s, cr, &p, (c == 'P'), s->unicode_sets)) return -1; c = CLASS_RANGE_BASE; break; } - /* fall thru */ + goto default_escape; #endif + case 'q': + if (s->unicode_sets && cr && inclass) { + if (parse_class_string_disjunction(s, cr, &p)) + return -1; + c = CLASS_RANGE_BASE; + break; + } + goto default_escape; default: + default_escape: p--; - ret = lre_parse_escape(&p, s->is_utf16 * 2); + ret = lre_parse_escape(&p, s->is_unicode * 2); if (ret >= 0) { c = ret; } else { - if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) { - /* always valid to escape these characters */ - goto normal_char; - } else if (s->is_utf16) { + if (s->is_unicode) { invalid_escape: return re_parse_error(s, "invalid escape sequence in regular expression"); } else { @@ -800,12 +1102,54 @@ static int get_class_atom(REParseState *s, CharRange *cr, return re_parse_error(s, "unexpected end"); } /* fall thru */ + goto normal_char; + + case '&': + case '!': + case '#': + case '$': + case '%': + case '*': + case '+': + case ',': + case '.': + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '@': + case '^': + case '`': + case '~': + if (s->unicode_sets && p[1] == c) { + /* forbidden double characters */ + return re_parse_error(s, "invalid class set operation in regular expression"); + } + goto normal_char; + + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '/': + case '-': + case '|': + if (s->unicode_sets) { + /* invalid characters in unicode sets */ + return re_parse_error(s, "invalid character in class in regular expression"); + } + goto normal_char; + default: normal_char: /* normal char */ if (c >= 128) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if ((unsigned)c > 0xffff && !s->is_utf16) { + if ((unsigned)c > 0xffff && !s->is_unicode) { /* XXX: should handle non BMP-1 code points */ return re_parse_error(s, "malformed unicode char"); } @@ -822,13 +1166,11 @@ static int re_emit_range(REParseState *s, const CharRange *cr) { int len, i; uint32_t high; - + len = (unsigned)cr->len / 2; if (len >= 65535) return re_parse_error(s, "too many ranges"); if (len == 0) { - /* not sure it can really happen. Emit a match that is always - false */ re_emit_op_u32(s, REOP_char32, -1); } else { high = cr->points[cr->len - 1]; @@ -837,7 +1179,7 @@ static int re_emit_range(REParseState *s, const CharRange *cr) if (high <= 0xffff) { /* can use 16 bit ranges with the conversion that 0xffff = infinity */ - re_emit_op_u16(s, REOP_range, len); + re_emit_op_u16(s, s->ignore_case ? REOP_range_i : REOP_range, len); for(i = 0; i < cr->len; i += 2) { dbuf_put_u16(&s->byte_code, cr->points[i]); high = cr->points[i + 1] - 1; @@ -846,7 +1188,7 @@ static int re_emit_range(REParseState *s, const CharRange *cr) dbuf_put_u16(&s->byte_code, high); } } else { - re_emit_op_u16(s, REOP_range32, len); + re_emit_op_u16(s, s->ignore_case ? REOP_range32_i : REOP_range32, len); for(i = 0; i < cr->len; i += 2) { dbuf_put_u32(&s->byte_code, cr->points[i]); dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); @@ -856,176 +1198,362 @@ static int re_emit_range(REParseState *s, const CharRange *cr) return 0; } -static int re_parse_char_class(REParseState *s, const uint8_t **pp) +static int re_string_cmp_len(const void *a, const void *b, void *arg) +{ + REString *p1 = *(REString **)a; + REString *p2 = *(REString **)b; + return (p1->len < p2->len) - (p1->len > p2->len); +} + +static void re_emit_char(REParseState *s, int c) +{ + if (c <= 0xffff) + re_emit_op_u16(s, s->ignore_case ? REOP_char_i : REOP_char, c); + else + re_emit_op_u32(s, s->ignore_case ? REOP_char32_i : REOP_char32, c); +} + +static int re_emit_string_list(REParseState *s, const REStringList *sl) +{ + REString **tab, *p; + int i, j, split_pos, last_match_pos, n; + BOOL has_empty_string, is_last; + + // re_string_list_dump("sl", sl); + if (sl->n_strings == 0) { + /* simple case: only characters */ + if (re_emit_range(s, &sl->cr)) + return -1; + } else { + /* at least one string list is present : match the longest ones first */ + /* XXX: add a new op_switch opcode to compile as a trie */ + tab = lre_realloc(s->opaque, NULL, sizeof(tab[0]) * sl->n_strings); + if (!tab) { + re_parse_out_of_memory(s); + return -1; + } + has_empty_string = FALSE; + n = 0; + for(i = 0; i < sl->hash_size; i++) { + for(p = sl->hash_table[i]; p != NULL; p = p->next) { + if (p->len == 0) { + has_empty_string = TRUE; + } else { + tab[n++] = p; + } + } + } + assert(n <= sl->n_strings); + + rqsort(tab, n, sizeof(tab[0]), re_string_cmp_len, NULL); + + last_match_pos = -1; + for(i = 0; i < n; i++) { + p = tab[i]; + is_last = !has_empty_string && sl->cr.len == 0 && i == (n - 1); + if (!is_last) + split_pos = re_emit_op_u32(s, REOP_split_next_first, 0); + else + split_pos = 0; + for(j = 0; j < p->len; j++) { + re_emit_char(s, p->buf[j]); + } + if (!is_last) { + last_match_pos = re_emit_op_u32(s, REOP_goto, last_match_pos); + put_u32(s->byte_code.buf + split_pos, s->byte_code.size - (split_pos + 4)); + } + } + + if (sl->cr.len != 0) { + /* char range */ + is_last = !has_empty_string; + if (!is_last) + split_pos = re_emit_op_u32(s, REOP_split_next_first, 0); + else + split_pos = 0; /* not used */ + if (re_emit_range(s, &sl->cr)) { + lre_realloc(s->opaque, tab, 0); + return -1; + } + if (!is_last) + put_u32(s->byte_code.buf + split_pos, s->byte_code.size - (split_pos + 4)); + } + + /* patch the 'goto match' */ + while (last_match_pos != -1) { + int next_pos = get_u32(s->byte_code.buf + last_match_pos); + put_u32(s->byte_code.buf + last_match_pos, s->byte_code.size - (last_match_pos + 4)); + last_match_pos = next_pos; + } + + lre_realloc(s->opaque, tab, 0); + } + return 0; +} + +static int re_parse_nested_class(REParseState *s, REStringList *cr, const uint8_t **pp); + +static int re_parse_class_set_operand(REParseState *s, REStringList *cr, const uint8_t **pp) +{ + int c1; + const uint8_t *p = *pp; + + if (*p == '[') { + if (re_parse_nested_class(s, cr, pp)) + return -1; + } else { + c1 = get_class_atom(s, cr, pp, TRUE); + if (c1 < 0) + return -1; + if (c1 < CLASS_RANGE_BASE) { + /* create a range with a single character */ + re_string_list_init(s, cr); + if (s->ignore_case) + c1 = lre_canonicalize(c1, s->is_unicode); + if (cr_union_interval(&cr->cr, c1, c1)) { + re_string_list_free(cr); + return -1; + } + } + } + return 0; +} + +static int re_parse_nested_class(REParseState *s, REStringList *cr, const uint8_t **pp) { const uint8_t *p; uint32_t c1, c2; - CharRange cr_s, *cr = &cr_s; - CharRange cr1_s, *cr1 = &cr1_s; - BOOL invert; - - cr_init(cr, s->opaque, lre_realloc); + int ret; + REStringList cr1_s, *cr1 = &cr1_s; + BOOL invert, is_first; + + if (lre_check_stack_overflow(s->opaque, 0)) + return re_parse_error(s, "stack overflow"); + + re_string_list_init(s, cr); p = *pp; p++; /* skip '[' */ + invert = FALSE; if (*p == '^') { p++; invert = TRUE; } + + /* handle unions */ + is_first = TRUE; for(;;) { if (*p == ']') break; - c1 = get_class_atom(s, cr1, &p, TRUE); - if ((int)c1 < 0) - goto fail; - if (*p == '-' && p[1] != ']') { - const uint8_t *p0 = p + 1; - if (c1 >= CLASS_RANGE_BASE) { - if (s->is_utf16) { - cr_free(cr1); - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - c2 = get_class_atom(s, cr1, &p0, TRUE); - if ((int)c2 < 0) + if (*p == '[' && s->unicode_sets) { + if (re_parse_nested_class(s, cr1, &p)) goto fail; - if (c2 >= CLASS_RANGE_BASE) { - cr_free(cr1); - if (s->is_utf16) { - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - p = p0; - if (c2 < c1) { - invalid_class_range: - re_parse_error(s, "invalid class range"); - goto fail; - } - if (cr_union_interval(cr, c1, c2)) - goto memory_error; + goto class_union; } else { - class_atom: - if (c1 >= CLASS_RANGE_BASE) { - int ret; - ret = cr_union1(cr, cr1->points, cr1->len); - cr_free(cr1); - if (ret) - goto memory_error; + c1 = get_class_atom(s, cr1, &p, TRUE); + if ((int)c1 < 0) + goto fail; + if (*p == '-' && p[1] != ']') { + const uint8_t *p0 = p + 1; + if (p[1] == '-' && s->unicode_sets && is_first) + goto class_atom; /* first character class followed by '--' */ + if (c1 >= CLASS_RANGE_BASE) { + if (s->is_unicode) { + re_string_list_free(cr1); + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + c2 = get_class_atom(s, cr1, &p0, TRUE); + if ((int)c2 < 0) + goto fail; + if (c2 >= CLASS_RANGE_BASE) { + re_string_list_free(cr1); + if (s->is_unicode) { + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + p = p0; + if (c2 < c1) { + invalid_class_range: + re_parse_error(s, "invalid class range"); + goto fail; + } + if (s->ignore_case) { + CharRange cr2_s, *cr2 = &cr2_s; + cr_init(cr2, s->opaque, lre_realloc); + if (cr_add_interval(cr2, c1, c2 + 1) || + cr_regexp_canonicalize(cr2, s->is_unicode) || + cr_op1(&cr->cr, cr2->points, cr2->len, CR_OP_UNION)) { + cr_free(cr2); + goto memory_error; + } + cr_free(cr2); + } else { + if (cr_union_interval(&cr->cr, c1, c2)) + goto memory_error; + } + is_first = FALSE; /* union operation */ } else { - if (cr_union_interval(cr, c1, c1)) - goto memory_error; + class_atom: + if (c1 >= CLASS_RANGE_BASE) { + class_union: + ret = re_string_list_op(cr, cr1, CR_OP_UNION); + re_string_list_free(cr1); + if (ret) + goto memory_error; + } else { + if (s->ignore_case) + c1 = lre_canonicalize(c1, s->is_unicode); + if (cr_union_interval(&cr->cr, c1, c1)) + goto memory_error; + } } } + if (s->unicode_sets && is_first) { + if (*p == '&' && p[1] == '&' && p[2] != '&') { + /* handle '&&' */ + for(;;) { + if (*p == ']') { + break; + } else if (*p == '&' && p[1] == '&' && p[2] != '&') { + p += 2; + } else { + goto invalid_operation; + } + if (re_parse_class_set_operand(s, cr1, &p)) + goto fail; + ret = re_string_list_op(cr, cr1, CR_OP_INTER); + re_string_list_free(cr1); + if (ret) + goto memory_error; + } + } else if (*p == '-' && p[1] == '-') { + /* handle '--' */ + for(;;) { + if (*p == ']') { + break; + } else if (*p == '-' && p[1] == '-') { + p += 2; + } else { + invalid_operation: + re_parse_error(s, "invalid operation in regular expression"); + goto fail; + } + if (re_parse_class_set_operand(s, cr1, &p)) + goto fail; + ret = re_string_list_op(cr, cr1, CR_OP_SUB); + re_string_list_free(cr1); + if (ret) + goto memory_error; + } + } + } + is_first = FALSE; } - if (s->ignore_case) { - if (cr_canonicalize(cr)) - goto memory_error; - } + + p++; /* skip ']' */ + *pp = p; if (invert) { - if (cr_invert(cr)) + /* XXX: add may_contain_string syntax check to be fully + compliant. The test here accepts more input than the + spec. */ + if (cr->n_strings != 0) { + re_parse_error(s, "negated character class with strings in regular expression debugger eval code"); + goto fail; + } + if (cr_invert(&cr->cr)) goto memory_error; } - if (re_emit_range(s, cr)) - goto fail; - cr_free(cr); - p++; /* skip ']' */ - *pp = p; return 0; memory_error: re_parse_out_of_memory(s); fail: - cr_free(cr); + re_string_list_free(cr); + return -1; +} + +static int re_parse_char_class(REParseState *s, const uint8_t **pp) +{ + REStringList cr_s, *cr = &cr_s; + + if (re_parse_nested_class(s, cr, pp)) + return -1; + if (re_emit_string_list(s, cr)) + goto fail; + re_string_list_free(cr); + return 0; + fail: + re_string_list_free(cr); return -1; } /* Return: - 1 if the opcodes in bc_buf[] always advance the character pointer. - 0 if the character pointer may not be advanced. - -1 if the code may depend on side effects of its previous execution (backreference) + - true if the opcodes may not advance the char pointer + - false if the opcodes always advance the char pointer */ -static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len) +static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len) { - int pos, opcode, ret, len, i; - uint32_t val, last; - BOOL has_back_reference; - uint8_t capture_bitmap[CAPTURE_COUNT_MAX]; - - ret = -2; /* not known yet */ + int pos, opcode, len; + uint32_t val; + BOOL ret; + + ret = TRUE; pos = 0; - has_back_reference = FALSE; - memset(capture_bitmap, 0, sizeof(capture_bitmap)); - while (pos < bc_buf_len) { opcode = bc_buf[pos]; len = reopcode_info[opcode].size; switch(opcode) { case REOP_range: + case REOP_range_i: val = get_u16(bc_buf + pos + 1); len += val * 4; goto simple_char; case REOP_range32: + case REOP_range32_i: val = get_u16(bc_buf + pos + 1); len += val * 8; goto simple_char; case REOP_char: + case REOP_char_i: case REOP_char32: + case REOP_char32_i: case REOP_dot: case REOP_any: simple_char: - if (ret == -2) - ret = 1; + ret = FALSE; break; case REOP_line_start: + case REOP_line_start_m: case REOP_line_end: + case REOP_line_end_m: case REOP_push_i32: case REOP_push_char_pos: case REOP_drop: case REOP_word_boundary: + case REOP_word_boundary_i: case REOP_not_word_boundary: + case REOP_not_word_boundary_i: case REOP_prev: /* no effect */ break; case REOP_save_start: case REOP_save_end: - val = bc_buf[pos + 1]; - capture_bitmap[val] |= 1; - break; case REOP_save_reset: - { - val = bc_buf[pos + 1]; - last = bc_buf[pos + 2]; - while (val < last) - capture_bitmap[val++] |= 1; - } - break; case REOP_back_reference: + case REOP_back_reference_i: case REOP_backward_back_reference: - val = bc_buf[pos + 1]; - capture_bitmap[val] |= 2; - has_back_reference = TRUE; + case REOP_backward_back_reference_i: break; default: - /* safe behvior: we cannot predict the outcome */ - if (ret == -2) - ret = 0; - break; + /* safe behavior: we cannot predict the outcome */ + return TRUE; } pos += len; } - if (has_back_reference) { - /* check if there is back reference which references a capture - made in the some code */ - for(i = 0; i < CAPTURE_COUNT_MAX; i++) { - if (capture_bitmap[i] == 3) - return -1; - } - } - if (ret == -2) - ret = 0; return ret; } @@ -1035,7 +1563,7 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) { int pos, opcode, len, count; uint32_t val; - + count = 0; pos = 0; while (pos < bc_buf_len) { @@ -1043,24 +1571,32 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) len = reopcode_info[opcode].size; switch(opcode) { case REOP_range: + case REOP_range_i: val = get_u16(bc_buf + pos + 1); len += val * 4; goto simple_char; case REOP_range32: + case REOP_range32_i: val = get_u16(bc_buf + pos + 1); len += val * 8; goto simple_char; case REOP_char: + case REOP_char_i: case REOP_char32: + case REOP_char32_i: case REOP_dot: case REOP_any: simple_char: count++; break; case REOP_line_start: + case REOP_line_start_m: case REOP_line_end: + case REOP_line_end_m: case REOP_word_boundary: + case REOP_word_boundary_i: case REOP_not_word_boundary: + case REOP_not_word_boundary_i: break; default: return -1; @@ -1090,10 +1626,10 @@ static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) break; } else if (c >= 128) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c >= 0xD800 && c <= 0xDBFF) { + if (is_hi_surrogate(c)) { d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - if (d >= 0xDC00 && d <= 0xDFFF) { - c = 0x10000 + 0x400 * (c - 0xD800) + (d - 0xDC00); + if (is_lo_surrogate(d)) { + c = from_surrogate(c, d); p = p1; } } @@ -1200,10 +1736,11 @@ static int find_group_name(REParseState *s, const char *name) const char *p, *buf_end; size_t len, name_len; int capture_index; - - name_len = strlen(name); + p = (char *)s->group_names.buf; + if (!p) return -1; buf_end = (char *)s->group_names.buf + s->group_names.size; + name_len = strlen(name); capture_index = 1; while (p < buf_end) { len = strlen(p); @@ -1217,13 +1754,48 @@ static int find_group_name(REParseState *s, const char *name) static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir); +static int re_parse_modifiers(REParseState *s, const uint8_t **pp) +{ + const uint8_t *p = *pp; + int mask = 0; + int val; + + for(;;) { + if (*p == 'i') { + val = LRE_FLAG_IGNORECASE; + } else if (*p == 'm') { + val = LRE_FLAG_MULTILINE; + } else if (*p == 's') { + val = LRE_FLAG_DOTALL; + } else { + break; + } + if (mask & val) + return re_parse_error(s, "duplicate modifier: '%c'", *p); + mask |= val; + p++; + } + *pp = p; + return mask; +} + +static BOOL update_modifier(BOOL val, int add_mask, int remove_mask, + int mask) +{ + if (add_mask & mask) + val = TRUE; + if (remove_mask & mask) + val = FALSE; + return val; +} + static int re_parse_term(REParseState *s, BOOL is_backward_dir) { const uint8_t *p; int c, last_atom_start, quant_min, quant_max, last_capture_count; BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; - CharRange cr_s, *cr = &cr_s; - + REStringList cr_s, *cr = &cr_s; + last_atom_start = -1; last_capture_count = 0; p = s->buf_ptr; @@ -1231,11 +1803,11 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) switch(c) { case '^': p++; - re_emit_op(s, REOP_line_start); + re_emit_op(s, s->multi_line ? REOP_line_start_m : REOP_line_start); break; case '$': p++; - re_emit_op(s, REOP_line_end); + re_emit_op(s, s->multi_line ? REOP_line_end_m : REOP_line_end); break; case '.': p++; @@ -1248,7 +1820,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) re_emit_op(s, REOP_prev); break; case '{': - if (s->is_utf16) { + if (s->is_unicode) { return re_parse_error(s, "syntax error"); } else if (!is_digit(p[1])) { /* Annex B: we accept '{' not followed by digits as a @@ -1285,6 +1857,44 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) p = s->buf_ptr; if (re_parse_expect(s, &p, ')')) return -1; + } else if (p[2] == 'i' || p[2] == 'm' || p[2] == 's' || p[2] == '-') { + BOOL saved_ignore_case, saved_multi_line, saved_dotall; + int add_mask, remove_mask; + p += 2; + remove_mask = 0; + add_mask = re_parse_modifiers(s, &p); + if (add_mask < 0) + return -1; + if (*p == '-') { + p++; + remove_mask = re_parse_modifiers(s, &p); + if (remove_mask < 0) + return -1; + } + if ((add_mask == 0 && remove_mask == 0) || + (add_mask & remove_mask) != 0) { + return re_parse_error(s, "invalid modifiers"); + } + if (re_parse_expect(s, &p, ':')) + return -1; + saved_ignore_case = s->ignore_case; + saved_multi_line = s->multi_line; + saved_dotall = s->dotall; + s->ignore_case = update_modifier(s->ignore_case, add_mask, remove_mask, LRE_FLAG_IGNORECASE); + s->multi_line = update_modifier(s->multi_line, add_mask, remove_mask, LRE_FLAG_MULTILINE); + s->dotall = update_modifier(s->dotall, add_mask, remove_mask, LRE_FLAG_DOTALL); + + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + s->ignore_case = saved_ignore_case; + s->multi_line = saved_multi_line; + s->dotall = saved_dotall; } else if ((p[2] == '=' || p[2] == '!')) { is_neg = (p[2] == '!'); is_backward_lookahead = FALSE; @@ -1300,7 +1910,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) lookahead: /* Annex B allows lookahead to be used as an atom for the quantifiers */ - if (!s->is_utf16 && !is_backward_lookahead) { + if (!s->is_unicode && !is_backward_lookahead) { last_atom_start = s->byte_code.size; last_capture_count = s->capture_count; } @@ -1346,15 +1956,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) capture_index = s->capture_count++; re_emit_op_u8(s, REOP_save_start + is_backward_dir, capture_index); - + s->buf_ptr = p; if (re_parse_disjunction(s, is_backward_dir)) return -1; p = s->buf_ptr; - + re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, capture_index); - + if (re_parse_expect(s, &p, ')')) return -1; } @@ -1363,20 +1973,24 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) switch(p[1]) { case 'b': case 'B': - re_emit_op(s, REOP_word_boundary + (p[1] != 'b')); + if (p[1] != 'b') { + re_emit_op(s, s->ignore_case ? REOP_not_word_boundary_i : REOP_not_word_boundary); + } else { + re_emit_op(s, s->ignore_case ? REOP_word_boundary_i : REOP_word_boundary); + } p += 2; break; case 'k': { const uint8_t *p1; int dummy_res; - + p1 = p; if (p1[2] != '<') { /* annex B: we tolerate invalid group names in non unicode mode if there is no named capture definition */ - if (s->is_utf16 || re_has_named_captures(s)) + if (s->is_unicode || re_has_named_captures(s)) return re_parse_error(s, "expecting group name"); else goto parse_class_atom; @@ -1384,7 +1998,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) p1 += 3; if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), &p1)) { - if (s->is_utf16 || re_has_named_captures(s)) + if (s->is_unicode || re_has_named_captures(s)) return re_parse_error(s, "invalid group name"); else goto parse_class_atom; @@ -1395,7 +2009,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) after (inefficient, but hopefully not common */ c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); if (c < 0) { - if (s->is_utf16 || re_has_named_captures(s)) + if (s->is_unicode || re_has_named_captures(s)) return re_parse_error(s, "group name not defined"); else goto parse_class_atom; @@ -1407,7 +2021,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) case '0': p += 2; c = 0; - if (s->is_utf16) { + if (s->is_unicode) { if (is_digit(*p)) { return re_parse_error(s, "invalid decimal escape in regular expression"); } @@ -1423,13 +2037,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) goto normal_char; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': - case '9': + case '9': { const uint8_t *q = ++p; - + c = parse_digits(&p, FALSE); if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { - if (!s->is_utf16) { + if (!s->is_unicode) { /* Annex B.1.4: accept legacy octal */ p = q; if (*p <= '7') { @@ -1452,7 +2066,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) emit_back_reference: last_atom_start = s->byte_code.size; last_capture_count = s->capture_count; - re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c); + + re_emit_op_u8(s, REOP_back_reference + 2 * is_backward_dir + s->ignore_case, c); } break; default: @@ -1471,7 +2086,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) break; case ']': case '}': - if (s->is_utf16) + if (s->is_unicode) return re_parse_error(s, "syntax error"); goto parse_class_atom; default: @@ -1486,18 +2101,14 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) re_emit_op(s, REOP_prev); if (c >= CLASS_RANGE_BASE) { int ret; - /* Note: canonicalization is not needed */ - ret = re_emit_range(s, cr); - cr_free(cr); + ret = re_emit_string_list(s, cr); + re_string_list_free(cr); if (ret) return -1; } else { if (s->ignore_case) - c = lre_canonicalize(c, s->is_utf16); - if (c <= 0xffff) - re_emit_op_u16(s, REOP_char, c); - else - re_emit_op_u32(s, REOP_char32, c); + c = lre_canonicalize(c, s->is_unicode); + re_emit_char(s, c); } if (is_backward_dir) re_emit_op(s, REOP_prev); @@ -1529,7 +2140,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) /* As an extension (see ES6 annex B), we accept '{' not followed by digits as a normal atom */ if (!is_digit(p[1])) { - if (s->is_utf16) + if (s->is_unicode) goto invalid_quant_count; break; } @@ -1548,7 +2159,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) quant_max = INT32_MAX; /* infinity */ } } - if (*p != '}' && !s->is_utf16) { + if (*p != '}' && !s->is_unicode) { /* Annex B: normal atom if invalid '{' syntax */ p = p1; break; @@ -1567,7 +2178,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } if (greedy) { int len, pos; - + if (quant_max > 0) { /* specific optimization for simple quantifiers */ if (dbuf_error(&s->byte_code)) @@ -1576,7 +2187,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) s->byte_code.size - last_atom_start); if (len > 0) { re_emit_op(s, REOP_match); - + if (dbuf_insert(&s->byte_code, last_atom_start, 17)) goto out_of_memory; pos = last_atom_start; @@ -1593,15 +2204,17 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) goto done; } } - + if (dbuf_error(&s->byte_code)) goto out_of_memory; - add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start, - s->byte_code.size - last_atom_start) == 0); - } else { - add_zero_advance_check = FALSE; } - + /* the spec tells that if there is no advance when + running the atom after the first quant_min times, + then there is no match. We remove this test when we + are sure the atom always advances the position. */ + add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start); + { int len, pos; len = s->byte_code.size - last_atom_start; @@ -1617,38 +2230,34 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } if (quant_max == 0) { s->byte_code.size = last_atom_start; - } else if (quant_max == 1) { - if (dbuf_insert(&s->byte_code, last_atom_start, 5)) - goto out_of_memory; - s->byte_code.buf[last_atom_start] = REOP_split_goto_first + - greedy; - put_u32(s->byte_code.buf + last_atom_start + 1, len); - } else if (quant_max == INT32_MAX) { + } else if (quant_max == 1 || quant_max == INT32_MAX) { + BOOL has_goto = (quant_max == INT32_MAX); if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check)) goto out_of_memory; s->byte_code.buf[last_atom_start] = REOP_split_goto_first + greedy; put_u32(s->byte_code.buf + last_atom_start + 1, - len + 5 + add_zero_advance_check); + len + 5 * has_goto + add_zero_advance_check * 2); if (add_zero_advance_check) { - /* avoid infinite loop by stoping the - recursion if no advance was made in the - atom (only works if the atom has no - side effect) */ s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; - re_emit_goto(s, REOP_bne_char_pos, last_atom_start); - } else { - re_emit_goto(s, REOP_goto, last_atom_start); + re_emit_op(s, REOP_check_advance); } + if (has_goto) + re_emit_goto(s, REOP_goto, last_atom_start); } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 10)) + if (dbuf_insert(&s->byte_code, last_atom_start, 10 + add_zero_advance_check)) goto out_of_memory; pos = last_atom_start; s->byte_code.buf[pos++] = REOP_push_i32; put_u32(s->byte_code.buf + pos, quant_max); pos += 4; s->byte_code.buf[pos++] = REOP_split_goto_first + greedy; - put_u32(s->byte_code.buf + pos, len + 5); + put_u32(s->byte_code.buf + pos, len + 5 + add_zero_advance_check * 2); + pos += 4; + if (add_zero_advance_check) { + s->byte_code.buf[pos++] = REOP_push_char_pos; + re_emit_op(s, REOP_check_advance); + } re_emit_goto(s, REOP_loop, last_atom_start + 5); re_emit_op(s, REOP_drop); } @@ -1672,22 +2281,25 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) if (quant_max == INT32_MAX) { pos = s->byte_code.size; re_emit_op_u32(s, REOP_split_goto_first + greedy, - len + 5 + add_zero_advance_check); + len + 5 + add_zero_advance_check * 2); if (add_zero_advance_check) re_emit_op(s, REOP_push_char_pos); /* copy the atom */ dbuf_put_self(&s->byte_code, last_atom_start, len); if (add_zero_advance_check) - re_emit_goto(s, REOP_bne_char_pos, pos); - else - re_emit_goto(s, REOP_goto, pos); + re_emit_op(s, REOP_check_advance); + re_emit_goto(s, REOP_goto, pos); } else if (quant_max > quant_min) { re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min); pos = s->byte_code.size; - re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5); + re_emit_op_u32(s, REOP_split_goto_first + greedy, + len + 5 + add_zero_advance_check * 2); + if (add_zero_advance_check) + re_emit_op(s, REOP_push_char_pos); /* copy the atom */ dbuf_put_self(&s->byte_code, last_atom_start, len); - + if (add_zero_advance_check) + re_emit_op(s, REOP_check_advance); re_emit_goto(s, REOP_loop, pos); re_emit_op(s, REOP_drop); } @@ -1739,14 +2351,14 @@ static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) } return 0; } - + static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) { int start, len, pos; if (lre_check_stack_overflow(s->opaque, 0)) return re_parse_error(s, "stack overflow"); - + start = s->byte_code.size; if (re_parse_alternative(s, is_backward_dir)) return -1; @@ -1766,7 +2378,7 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) if (re_parse_alternative(s, is_backward_dir)) return -1; - + /* patch the goto */ len = s->byte_code.size - (pos + 4); put_u32(s->byte_code.buf + pos, len); @@ -1779,7 +2391,7 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) { int stack_size, stack_size_max, pos, opcode, len; uint32_t val; - + stack_size = 0; stack_size_max = 0; bc_buf += RE_HEADER_LEN; @@ -1801,15 +2413,17 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) } break; case REOP_drop: - case REOP_bne_char_pos: + case REOP_check_advance: assert(stack_size > 0); stack_size--; break; case REOP_range: + case REOP_range_i: val = get_u16(bc_buf + pos + 1); len += val * 4; break; case REOP_range32: + case REOP_range32_i: val = get_u16(bc_buf + pos + 1); len += val * 8; break; @@ -1830,29 +2444,31 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, REParseState s_s, *s = &s_s; int stack_size; BOOL is_sticky; - + memset(s, 0, sizeof(*s)); s->opaque = opaque; s->buf_ptr = (const uint8_t *)buf; s->buf_end = s->buf_ptr + buf_len; s->buf_start = s->buf_ptr; s->re_flags = re_flags; - s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0); + s->is_unicode = ((re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0); is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); + s->multi_line = ((re_flags & LRE_FLAG_MULTILINE) != 0); s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); + s->unicode_sets = ((re_flags & LRE_FLAG_UNICODE_SETS) != 0); s->capture_count = 1; s->total_capture_count = -1; s->has_named_captures = -1; - + dbuf_init2(&s->byte_code, opaque, lre_realloc); dbuf_init2(&s->group_names, opaque, lre_realloc); - dbuf_putc(&s->byte_code, re_flags); /* first element is the flags */ + dbuf_put_u16(&s->byte_code, re_flags); /* first element is the flags */ dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ dbuf_putc(&s->byte_code, 0); /* stack size */ dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ - + if (!is_sticky) { /* iterate thru all positions (about the same as .*?( ... ) ) . We do it without an explicit loop so that lock step @@ -1874,7 +2490,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, } re_emit_op_u8(s, REOP_save_end, 0); - + re_emit_op(s, REOP_match); if (*s->buf_ptr != '\0') { @@ -1886,28 +2502,30 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, re_parse_out_of_memory(s); goto error; } - + stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size); if (stack_size < 0) { re_parse_error(s, "too many imbricated quantifiers"); goto error; } - + s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; - put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN); + put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, + s->byte_code.size - RE_HEADER_LEN); /* add the named groups if needed */ if (s->group_names.size > (s->capture_count - 1)) { dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size); - s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS; + put_u16(s->byte_code.buf + RE_HEADER_FLAGS, + lre_get_flags(s->byte_code.buf) | LRE_FLAG_NAMED_GROUPS); } dbuf_free(&s->group_names); - + #ifdef DUMP_REOP lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); #endif - + error_msg[0] = '\0'; *plen = s->byte_code.size; return s->byte_code.buf; @@ -1926,93 +2544,86 @@ static BOOL is_word_char(uint32_t c) (c == '_')); } -#define GET_CHAR(c, cptr, cbuf_end) \ +#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ do { \ if (cbuf_type == 0) { \ c = *cptr++; \ } else { \ - uint32_t __c1; \ - c = *(uint16_t *)cptr; \ - cptr += 2; \ - if (c >= 0xd800 && c < 0xdc00 && \ - cbuf_type == 2 && cptr < cbuf_end) { \ - __c1 = *(uint16_t *)cptr; \ - if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ - c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ - cptr += 2; \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c) && cbuf_type == 2) { \ + if (_p < _end && is_lo_surrogate(*_p)) { \ + c = from_surrogate(c, *_p++); \ } \ } \ + cptr = (const void *)_p; \ } \ } while (0) -#define PEEK_CHAR(c, cptr, cbuf_end) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[0]; \ - } else { \ - uint32_t __c1; \ - c = ((uint16_t *)cptr)[0]; \ - if (c >= 0xd800 && c < 0xdc00 && \ - cbuf_type == 2 && (cptr + 2) < cbuf_end) { \ - __c1 = ((uint16_t *)cptr)[1]; \ - if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ - c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ +#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c) && cbuf_type == 2) { \ + if (_p < _end && is_lo_surrogate(*_p)) { \ + c = from_surrogate(c, *_p); \ } \ } \ - } \ + } \ } while (0) -#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[-1]; \ - } else { \ - uint32_t __c1; \ - c = ((uint16_t *)cptr)[-1]; \ - if (c >= 0xdc00 && c < 0xe000 && \ - cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \ - __c1 = ((uint16_t *)cptr)[-2]; \ - if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \ - c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \ +#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[-1]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c) && cbuf_type == 2) { \ + if (_p > _start && is_hi_surrogate(_p[-1])) { \ + c = from_surrogate(*--_p, c); \ } \ } \ } \ } while (0) -#define GET_PREV_CHAR(c, cptr, cbuf_start) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - c = cptr[0]; \ - } else { \ - uint32_t __c1; \ - cptr -= 2; \ - c = ((uint16_t *)cptr)[0]; \ - if (c >= 0xdc00 && c < 0xe000 && \ - cbuf_type == 2 && cptr > cbuf_start) { \ - __c1 = ((uint16_t *)cptr)[-1]; \ - if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \ - cptr -= 2; \ - c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \ +#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c) && cbuf_type == 2) { \ + if (_p > _start && is_hi_surrogate(_p[-1])) { \ + c = from_surrogate(*--_p, c); \ } \ } \ + cptr = (const void *)_p; \ } \ } while (0) -#define PREV_CHAR(cptr, cbuf_start) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - } else { \ - cptr -= 2; \ - if (cbuf_type == 2) { \ - c = ((uint16_t *)cptr)[0]; \ - if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \ - c = ((uint16_t *)cptr)[-1]; \ - if (c >= 0xd800 && c < 0xdc00) \ - cptr -= 2; \ +#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + if (is_lo_surrogate(*_p) && cbuf_type == 2) { \ + if (_p > _start && is_hi_surrogate(_p[-1])) { \ + --_p; \ } \ } \ + cptr = (const void *)_p; \ } \ } while (0) @@ -2038,12 +2649,11 @@ typedef struct { const uint8_t *cbuf; const uint8_t *cbuf_end; /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ - int cbuf_type; + int cbuf_type; int capture_count; int stack_size_max; - BOOL multi_line; - BOOL ignore_case; - BOOL is_utf16; + BOOL is_unicode; + int interrupt_counter; void *opaque; /* used for stack overflow check */ size_t state_size; @@ -2090,7 +2700,17 @@ static int push_state(REExecContext *s, return 0; } -/* return 1 if match, 0 if not match or -1 if error. */ +static int lre_poll_timeout(REExecContext *s) +{ + if (unlikely(--s->interrupt_counter <= 0)) { + s->interrupt_counter = INTERRUPT_COUNTER_INIT; + if (lre_check_timeout(s->opaque)) + return LRE_RET_TIMEOUT; + } + return 0; +} + +/* return 1 if match, 0 if not match or < 0 if error. */ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, StackInt *stack, int stack_len, const uint8_t *pc, const uint8_t *cptr, @@ -2100,7 +2720,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, int cbuf_type; uint32_t val, c; const uint8_t *cbuf_end; - + cbuf_type = s->cbuf_type; cbuf_end = s->cbuf_end; @@ -2121,6 +2741,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, ret = 0; recurse: for(;;) { + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; if (s->state_stack_len == 0) return ret; rs = (REExecState *)(s->state_stack + @@ -2152,7 +2774,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, /* go backward */ char_count = get_u32(pc + 12); for(i = 0; i < char_count; i++) { - PREV_CHAR(cptr, s->cbuf); + PREV_CHAR(cptr, s->cbuf, cbuf_type); } pc = (pc + 16) + (int)get_u32(pc); rs->cptr = cptr; @@ -2178,18 +2800,20 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, } break; case REOP_char32: + case REOP_char32_i: val = get_u32(pc); pc += 4; goto test_char; case REOP_char: + case REOP_char_i: val = get_u16(pc); pc += 2; test_char: if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c, cptr, cbuf_end); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_utf16); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (opcode == REOP_char_i || opcode == REOP_char32_i) { + c = lre_canonicalize(c, s->is_unicode); } if (val != c) goto no_match; @@ -2198,7 +2822,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, case REOP_split_next_first: { const uint8_t *pc1; - + val = get_u32(pc); pc += 4; if (opcode == REOP_split_next_first) { @@ -2210,7 +2834,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, ret = push_state(s, capture, stack, stack_len, pc1, cptr, RE_EXEC_STATE_SPLIT, 0); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; break; } case REOP_lookahead: @@ -2222,42 +2846,46 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, 0); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; break; - + case REOP_goto: val = get_u32(pc); pc += 4 + (int)val; + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; break; case REOP_line_start: + case REOP_line_start_m: if (cptr == s->cbuf) break; - if (!s->multi_line) + if (opcode == REOP_line_start) goto no_match; - PEEK_PREV_CHAR(c, cptr, s->cbuf); + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); if (!is_line_terminator(c)) goto no_match; break; case REOP_line_end: + case REOP_line_end_m: if (cptr == cbuf_end) break; - if (!s->multi_line) + if (opcode == REOP_line_end) goto no_match; - PEEK_CHAR(c, cptr, cbuf_end); + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); 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); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); 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); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); break; case REOP_save_start: case REOP_save_end: @@ -2292,45 +2920,55 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, pc += 4; if (--stack[stack_len - 1] != 0) { pc += (int)val; + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; } break; case REOP_push_char_pos: stack[stack_len++] = (uintptr_t)cptr; break; - case REOP_bne_char_pos: - val = get_u32(pc); - pc += 4; - if (stack[--stack_len] != (uintptr_t)cptr) - pc += (int)val; + case REOP_check_advance: + if (stack[--stack_len] == (uintptr_t)cptr) + goto no_match; break; case REOP_word_boundary: + case REOP_word_boundary_i: case REOP_not_word_boundary: + case REOP_not_word_boundary_i: { BOOL v1, v2; + int ignore_case = (opcode == REOP_word_boundary_i || opcode == REOP_not_word_boundary_i); + BOOL is_boundary = (opcode == REOP_word_boundary || opcode == REOP_word_boundary_i); /* char before */ if (cptr == s->cbuf) { v1 = FALSE; } else { - PEEK_PREV_CHAR(c, cptr, s->cbuf); + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); + if (ignore_case) + c = lre_canonicalize(c, s->is_unicode); v1 = is_word_char(c); } /* current char */ if (cptr >= cbuf_end) { v2 = FALSE; } else { - PEEK_CHAR(c, cptr, cbuf_end); + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); + if (ignore_case) + c = lre_canonicalize(c, s->is_unicode); v2 = is_word_char(c); } - if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) + if (v1 ^ v2 ^ is_boundary) goto no_match; } break; case REOP_back_reference: + case REOP_back_reference_i: case REOP_backward_back_reference: + case REOP_backward_back_reference_i: { const uint8_t *cptr1, *cptr1_end, *cptr1_start; uint32_t c1, c2; - + val = *pc++; if (val >= s->capture_count) goto no_match; @@ -2338,16 +2976,17 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, cptr1_end = capture[2 * val + 1]; if (!cptr1_start || !cptr1_end) break; - if (opcode == REOP_back_reference) { + if (opcode == REOP_back_reference || + opcode == REOP_back_reference_i) { cptr1 = cptr1_start; while (cptr1 < cptr1_end) { if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c1, cptr1, cptr1_end); - GET_CHAR(c2, cptr, cbuf_end); - if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_utf16); - c2 = lre_canonicalize(c2, s->is_utf16); + GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); + GET_CHAR(c2, cptr, cbuf_end, cbuf_type); + if (opcode == REOP_back_reference_i) { + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); } if (c1 != c2) goto no_match; @@ -2357,11 +2996,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, while (cptr1 > cptr1_start) { if (cptr == s->cbuf) goto no_match; - GET_PREV_CHAR(c1, cptr1, cptr1_start); - GET_PREV_CHAR(c2, cptr, s->cbuf); - if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_utf16); - c2 = lre_canonicalize(c2, s->is_utf16); + GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); + GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); + if (opcode == REOP_backward_back_reference_i) { + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); } if (c1 != c2) goto no_match; @@ -2370,17 +3009,18 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, } break; case REOP_range: + case REOP_range_i: { int n; uint32_t low, high, idx_min, idx_max, idx; - + n = get_u16(pc); /* n must be >= 1 */ pc += 2; if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c, cptr, cbuf_end); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_utf16); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (opcode == REOP_range_i) { + c = lre_canonicalize(c, s->is_unicode); } idx_min = 0; low = get_u16(pc + 0 * 4); @@ -2410,17 +3050,18 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, } break; case REOP_range32: + case REOP_range32_i: { int n; uint32_t low, high, idx_min, idx_max, idx; - + n = get_u16(pc); /* n must be >= 1 */ pc += 2; if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c, cptr, cbuf_end); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_utf16); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (opcode == REOP_range32_i) { + c = lre_canonicalize(c, s->is_unicode); } idx_min = 0; low = get_u32(pc + 0 * 8); @@ -2450,7 +3091,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, /* go to the previous char */ if (cptr == s->cbuf) goto no_match; - PREV_CHAR(cptr, s->cbuf); + PREV_CHAR(cptr, s->cbuf, cbuf_type); break; case REOP_simple_greedy_quant: { @@ -2458,19 +3099,22 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, size_t q; intptr_t res; const uint8_t *pc1; - + next_pos = get_u32(pc); quant_min = get_u32(pc + 4); quant_max = get_u32(pc + 8); pc += 16; pc1 = pc; pc += (int)next_pos; - + q = 0; for(;;) { + if (lre_poll_timeout(s)) + return LRE_RET_TIMEOUT; res = lre_exec_backtrack(s, capture, stack, stack_len, pc1, cptr, TRUE); - if (res == -1) + if (res == LRE_RET_MEMORY_ERROR || + res == LRE_RET_TIMEOUT) return res; if (!res) break; @@ -2488,7 +3132,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, RE_EXEC_STATE_GREEDY_QUANT, q - quant_min); if (ret < 0) - return -1; + return LRE_RET_MEMORY_ERROR; } } break; @@ -2498,7 +3142,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, } } -/* Return 1 if match, 0 if not match or -1 if error. cindex is the +/* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the starting position of the match and must be such as 0 <= cindex <= clen. */ int lre_exec(uint8_t **capture, @@ -2508,18 +3152,17 @@ int lre_exec(uint8_t **capture, REExecContext s_s, *s = &s_s; int re_flags, i, alloca_size, ret; StackInt *stack_buf; - - re_flags = bc_buf[RE_HEADER_FLAGS]; - s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; - s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; - s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0; + + re_flags = lre_get_flags(bc_buf); + s->is_unicode = (re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0; s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; s->cbuf = cbuf; s->cbuf_end = cbuf + (clen << cbuf_type); s->cbuf_type = cbuf_type; - if (s->cbuf_type == 1 && s->is_utf16) + if (s->cbuf_type == 1 && s->is_unicode) s->cbuf_type = 2; + s->interrupt_counter = INTERRUPT_COUNTER_INIT; s->opaque = opaque; s->state_size = sizeof(REExecState) + @@ -2528,7 +3171,7 @@ int lre_exec(uint8_t **capture, s->state_stack = NULL; s->state_stack_len = 0; s->state_stack_size = 0; - + for(i = 0; i < s->capture_count * 2; i++) capture[i] = NULL; alloca_size = s->stack_size_max * sizeof(stack_buf[0]); @@ -2546,7 +3189,7 @@ int lre_get_capture_count(const uint8_t *bc_buf) int lre_get_flags(const uint8_t *bc_buf) { - return bc_buf[RE_HEADER_FLAGS]; + return get_u16(bc_buf + RE_HEADER_FLAGS); } /* Return NULL if no group names. Otherwise, return a pointer to @@ -2556,8 +3199,8 @@ const char *lre_get_groupnames(const uint8_t *bc_buf) uint32_t re_bytecode_len; if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) return NULL; - re_bytecode_len = get_u32(bc_buf + 3); - return (const char *)(bc_buf + 7 + re_bytecode_len); + re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN); + return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); } #ifdef TEST @@ -2574,27 +3217,28 @@ void *lre_realloc(void *opaque, void *ptr, size_t size) int main(int argc, char **argv) { - int len, ret, i; + int len, flags, ret, i; uint8_t *bc; char error_msg[64]; uint8_t *capture[CAPTURE_COUNT_MAX * 2]; const char *input; int input_len, capture_count; - - if (argc < 3) { - printf("usage: %s regexp input\n", argv[0]); - exit(1); + + if (argc < 4) { + printf("usage: %s regexp flags input\n", argv[0]); + return 1; } + flags = atoi(argv[2]); bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], - strlen(argv[1]), 0, NULL); + strlen(argv[1]), flags, NULL); if (!bc) { fprintf(stderr, "error: %s\n", error_msg); exit(1); } - input = argv[2]; + input = argv[3]; input_len = strlen(input); - + ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); printf("ret=%d\n", ret); if (ret == 1) { diff --git a/quickjs/libregexp.h b/quickjs/libregexp.h index 9aedb7e937..da76e4cef6 100644 --- a/quickjs/libregexp.h +++ b/quickjs/libregexp.h @@ -1,6 +1,6 @@ /* * Regular Expression Engine - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,19 +25,20 @@ #define LIBREGEXP_H #include - -#include "libunicode.h" - -#define LRE_BOOL int /* for documentation purposes */ +#include #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_UTF16 (1 << 4) +#define LRE_FLAG_UNICODE (1 << 4) #define LRE_FLAG_STICKY (1 << 5) - +#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ +#define LRE_FLAG_UNICODE_SETS (1 << 8) + +#define LRE_RET_MEMORY_ERROR (-1) +#define LRE_RET_TIMEOUT (-2) uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, const char *buf, size_t buf_len, int re_flags, @@ -50,43 +51,11 @@ int lre_exec(uint8_t **capture, int cbuf_type, void *opaque); int lre_parse_escape(const uint8_t **pp, int allow_utf16); -LRE_BOOL lre_is_space(int c); -/* must be provided by the user */ -LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +/* must be provided by the user, return non zero if overflow */ +int lre_check_stack_overflow(void *opaque, size_t alloca_size); +/* must be provided by the user, return non zero if time out */ +int lre_check_timeout(void *opaque); void *lre_realloc(void *opaque, void *ptr, size_t size); -/* JS identifier test */ -extern uint32_t const lre_id_start_table_ascii[4]; -extern uint32_t const lre_id_continue_table_ascii[4]; - -static inline int lre_js_is_ident_first(int c) -{ - if ((uint32_t)c < 128) { - return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; - } else { -#ifdef CONFIG_ALL_UNICODE - return lre_is_id_start(c); -#else - return !lre_is_space(c); -#endif - } -} - -static inline int lre_js_is_ident_next(int c) -{ - if ((uint32_t)c < 128) { - return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; - } else { - /* ZWNJ and ZWJ are accepted in identifiers */ -#ifdef CONFIG_ALL_UNICODE - return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; -#else - return !lre_is_space(c) || c == 0x200C || c == 0x200D; -#endif - } -} - -#undef LRE_BOOL - #endif /* LIBREGEXP_H */ diff --git a/quickjs/libunicode-table.h b/quickjs/libunicode-table.h index b64178b488..67df6b3a3c 100644 --- a/quickjs/libunicode-table.h +++ b/quickjs/libunicode-table.h @@ -3,7 +3,7 @@ #include -static const uint32_t case_conv_table1[370] = { +static const uint32_t case_conv_table1[378] = { 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, @@ -13,140 +13,143 @@ static const uint32_t case_conv_table1[370] = { 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, - 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100, - 0x00cf8130, 0x00d00640, 0x00d30130, 0x00d38240, - 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240, - 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240, - 0x00de0240, 0x00df8100, 0x00e20350, 0x00e38350, - 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240, - 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130, - 0x00fb8130, 0x00fc2840, 0x01100130, 0x01111240, - 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131, - 0x011f8201, 0x01208240, 0x01218130, 0x01220130, - 0x01228130, 0x01230a40, 0x01280101, 0x01288101, - 0x01290101, 0x01298100, 0x012a0100, 0x012b0200, - 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100, - 0x01308101, 0x01318100, 0x01328101, 0x01330101, - 0x01340100, 0x01348100, 0x01350101, 0x01358101, - 0x01360101, 0x01378100, 0x01388101, 0x01390100, - 0x013a8100, 0x013e8101, 0x01400100, 0x01410101, - 0x01418100, 0x01438101, 0x01440100, 0x01448100, - 0x01450200, 0x01460100, 0x01490100, 0x014e8101, - 0x014f0101, 0x01a28173, 0x01b80440, 0x01bb0240, - 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330, - 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130, - 0x01d18930, 0x01d60100, 0x01d68300, 0x01d801d3, - 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100, - 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173, - 0x01ea8173, 0x01eb0173, 0x01eb8100, 0x01ec1840, - 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100, - 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130, - 0x01fd0240, 0x01fe8330, 0x02001030, 0x02082030, - 0x02182000, 0x02281000, 0x02302240, 0x02453640, - 0x02600130, 0x02608e40, 0x02678100, 0x02686040, - 0x0298a630, 0x02b0a600, 0x02c381b5, 0x08502631, - 0x08638131, 0x08668131, 0x08682b00, 0x087e8300, - 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174, - 0x0e408174, 0x0e410174, 0x0e418174, 0x0e420174, - 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180, - 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, - 0x0ec70101, 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, - 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, - 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800, - 0x0f840830, 0x0f880600, 0x0f8c0630, 0x0f900800, - 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600, - 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3, - 0x0fa98100, 0x0faa01d3, 0x0faa8100, 0x0fab01d3, - 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130, - 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200, - 0x0fb90400, 0x0fbb0200, 0x0fbc0201, 0x0fbd0201, - 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8, - 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200, - 0x0fd901b9, 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, - 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161, - 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, - 0x0fe301b2, 0x0fe381d8, 0x0fe40430, 0x0fe60162, - 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, - 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201, - 0x0ff101d3, 0x0ff181d3, 0x0ff201ba, 0x0ff28101, - 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230, - 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, - 0x0ffb01b2, 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, - 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0, - 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001, - 0x10c18240, 0x125b1a31, 0x12681a01, 0x16003031, - 0x16183001, 0x16300240, 0x16310130, 0x16318130, - 0x16320130, 0x16328100, 0x16330100, 0x16338640, - 0x16368130, 0x16370130, 0x16378130, 0x16380130, - 0x16390240, 0x163a8240, 0x163f0230, 0x16406440, - 0x16758440, 0x16790240, 0x16802600, 0x16938100, - 0x16968100, 0x53202e40, 0x53401c40, 0x53910e40, - 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40, - 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101, - 0x53cb1440, 0x53d50130, 0x53d58130, 0x53d60130, - 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130, - 0x53d90130, 0x53d98131, 0x53da1040, 0x53e20131, - 0x53e28130, 0x53e30130, 0x53e38440, 0x53e80240, - 0x53eb0440, 0x53fa8240, 0x55a98101, 0x55b85020, - 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, 0x7d8181da, - 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb, - 0x7d8a01bb, 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb, - 0x7f909a31, 0x7fa09a01, 0x82002831, 0x82142801, - 0x82582431, 0x826c2401, 0x82b80b31, 0x82be0f31, - 0x82c60731, 0x82ca0231, 0x82cb8b01, 0x82d18f01, - 0x82d98701, 0x82dd8201, 0x86403331, 0x86603301, + 0x00cd0100, 0x00cd8101, 0x00ce0130, 0x00ce8130, + 0x00cf0100, 0x00cf8130, 0x00d00640, 0x00d30130, + 0x00d38240, 0x00d48130, 0x00d60240, 0x00d70130, + 0x00d78240, 0x00d88230, 0x00d98440, 0x00db8130, + 0x00dc0240, 0x00de0240, 0x00df8100, 0x00e20350, + 0x00e38350, 0x00e50350, 0x00e69040, 0x00ee8100, + 0x00ef1240, 0x00f801b4, 0x00f88350, 0x00fa0240, + 0x00fb0130, 0x00fb8130, 0x00fc2840, 0x01100130, + 0x01111240, 0x011d0131, 0x011d8240, 0x011e8130, + 0x011f0131, 0x011f8201, 0x01208240, 0x01218130, + 0x01220130, 0x01228130, 0x01230a40, 0x01280101, + 0x01288101, 0x01290101, 0x01298100, 0x012a0100, + 0x012b0200, 0x012c8100, 0x012d8100, 0x012e0101, + 0x01300100, 0x01308101, 0x01318100, 0x01320101, + 0x01328101, 0x01330101, 0x01340100, 0x01348100, + 0x01350101, 0x01358101, 0x01360101, 0x01378100, + 0x01388101, 0x01390100, 0x013a8100, 0x013e8101, + 0x01400100, 0x01410101, 0x01418100, 0x01438101, + 0x01440100, 0x01448100, 0x01450200, 0x01460100, + 0x01490100, 0x014e8101, 0x014f0101, 0x01a28173, + 0x01b80440, 0x01bb0240, 0x01bd8300, 0x01bf8130, + 0x01c30130, 0x01c40330, 0x01c60130, 0x01c70230, + 0x01c801d0, 0x01c89130, 0x01d18930, 0x01d60100, + 0x01d68300, 0x01d801d3, 0x01d89100, 0x01e10173, + 0x01e18900, 0x01e60100, 0x01e68200, 0x01e78130, + 0x01e80173, 0x01e88173, 0x01ea8173, 0x01eb0173, + 0x01eb8100, 0x01ec1840, 0x01f80173, 0x01f88173, + 0x01f90100, 0x01f98100, 0x01fa01a0, 0x01fa8173, + 0x01fb8240, 0x01fc8130, 0x01fd0240, 0x01fe8330, + 0x02001030, 0x02082030, 0x02182000, 0x02281000, + 0x02302240, 0x02453640, 0x02600130, 0x02608e40, + 0x02678100, 0x02686040, 0x0298a630, 0x02b0a600, + 0x02c381b5, 0x08502631, 0x08638131, 0x08668131, + 0x08682b00, 0x087e8300, 0x09d05011, 0x09f80610, + 0x09fc0620, 0x0e400174, 0x0e408174, 0x0e410174, + 0x0e418174, 0x0e420174, 0x0e428174, 0x0e430174, + 0x0e438180, 0x0e440180, 0x0e448240, 0x0e482b30, + 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, 0x0ec70101, + 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, 0x0f4b81b6, + 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, 0x0f4d8180, + 0x0f4f0130, 0x0f506040, 0x0f800800, 0x0f840830, + 0x0f880600, 0x0f8c0630, 0x0f900800, 0x0f940830, + 0x0f980800, 0x0f9c0830, 0x0fa00600, 0x0fa40630, + 0x0fa801b0, 0x0fa88100, 0x0fa901d3, 0x0fa98100, + 0x0faa01d3, 0x0faa8100, 0x0fab01d3, 0x0fab8100, + 0x0fac8130, 0x0fad8130, 0x0fae8130, 0x0faf8130, + 0x0fb00800, 0x0fb40830, 0x0fb80200, 0x0fb90400, + 0x0fbb0201, 0x0fbc0201, 0x0fbd0201, 0x0fbe0201, + 0x0fc008b7, 0x0fc40867, 0x0fc808b8, 0x0fcc0868, + 0x0fd008b8, 0x0fd40868, 0x0fd80200, 0x0fd901b9, + 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, 0x0fdb81d7, + 0x0fdc0230, 0x0fdd0230, 0x0fde0161, 0x0fdf0173, + 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, 0x0fe301b2, + 0x0fe381d8, 0x0fe40430, 0x0fe60162, 0x0fe80201, + 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, 0x0feb81d0, + 0x0fec0230, 0x0fed0230, 0x0ff00201, 0x0ff101d3, + 0x0ff181d3, 0x0ff201ba, 0x0ff28101, 0x0ff301b0, + 0x0ff381d3, 0x0ff40231, 0x0ff50230, 0x0ff60131, + 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, 0x0ffb01b2, + 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, 0x0ffe0162, + 0x109301a0, 0x109501a0, 0x109581a0, 0x10990131, + 0x10a70101, 0x10b01031, 0x10b81001, 0x10c18240, + 0x125b1a31, 0x12681a01, 0x16003031, 0x16183001, + 0x16300240, 0x16310130, 0x16318130, 0x16320130, + 0x16328100, 0x16330100, 0x16338640, 0x16368130, + 0x16370130, 0x16378130, 0x16380130, 0x16390240, + 0x163a8240, 0x163f0230, 0x16406440, 0x16758440, + 0x16790240, 0x16802600, 0x16938100, 0x16968100, + 0x53202e40, 0x53401c40, 0x53910e40, 0x53993e40, + 0x53bc8440, 0x53be8130, 0x53bf0a40, 0x53c58240, + 0x53c68130, 0x53c80440, 0x53ca0101, 0x53cb1440, + 0x53d50130, 0x53d58130, 0x53d60130, 0x53d68130, + 0x53d70130, 0x53d80130, 0x53d88130, 0x53d90130, + 0x53d98131, 0x53da1040, 0x53e20131, 0x53e28130, + 0x53e30130, 0x53e38440, 0x53e58130, 0x53e60240, + 0x53e80240, 0x53eb0640, 0x53ee0130, 0x53fa8240, + 0x55a98101, 0x55b85020, 0x7d8001b2, 0x7d8081b2, + 0x7d8101b2, 0x7d8181da, 0x7d8201da, 0x7d8281b3, + 0x7d8301b3, 0x7d8981bb, 0x7d8a01bb, 0x7d8a81bb, + 0x7d8b01bc, 0x7d8b81bb, 0x7f909a31, 0x7fa09a01, + 0x82002831, 0x82142801, 0x82582431, 0x826c2401, + 0x82b80b31, 0x82be0f31, 0x82c60731, 0x82ca0231, + 0x82cb8b01, 0x82d18f01, 0x82d98701, 0x82dd8201, + 0x86403331, 0x86603301, 0x86a81631, 0x86b81601, 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001, 0xf4802231, 0xf4912201, }; -static const uint8_t case_conv_table2[370] = { +static const uint8_t case_conv_table2[378] = { 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00, - 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00, - 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59, - 0x3f, 0x5d, 0x5c, 0x00, 0x46, 0x61, 0x63, 0x42, - 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, - 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, 0x20, - 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22, - 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24, - 0x27, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x3e, 0x1e, - 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e, - 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x48, - 0x2c, 0x43, 0x2e, 0x4b, 0x30, 0x4c, 0x32, 0x44, - 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e, - 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12, - 0x7b, 0xa3, 0x7c, 0x78, 0x79, 0x8a, 0x92, 0x98, - 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75, - 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98, - 0x97, 0x96, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5, - 0x4f, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22, - 0x22, 0x2a, 0x34, 0x35, 0xa6, 0xa7, 0x36, 0x1f, - 0x49, 0x00, 0x00, 0x97, 0x01, 0x5a, 0xda, 0x1d, - 0x36, 0x05, 0x00, 0xc4, 0xc3, 0xc6, 0xc5, 0xc8, - 0xc7, 0xca, 0xc9, 0xcc, 0xcb, 0xc4, 0xd5, 0x45, - 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2, - 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07, - 0x0f, 0x80, 0x9f, 0x00, 0x21, 0x80, 0xa3, 0xed, - 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6, - 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29, - 0xfd, 0x15, 0x12, 0x06, 0x16, 0xf8, 0xdd, 0x06, - 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf, - 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0, - 0x6d, 0x37, 0x38, 0x39, 0x15, 0x14, 0x17, 0x16, - 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7, - 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50, - 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0xa4, - 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, - 0x00, 0x5a, 0x00, 0x47, 0x00, 0x5b, 0x56, 0x58, - 0x60, 0x5e, 0x70, 0x69, 0x6f, 0x4e, 0x00, 0x3b, - 0x67, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa8, - 0x8a, 0x8b, 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, - 0x94, 0xb0, 0x6f, 0xb2, 0x5d, 0x5c, 0x5f, 0x5e, - 0x61, 0x60, 0x66, 0x67, 0x68, 0x69, 0x62, 0x63, - 0x64, 0x65, 0x6b, 0x6a, 0x6d, 0x6c, 0x6f, 0x6e, - 0x71, 0x70, + 0x08, 0x00, 0x53, 0x4b, 0x52, 0x00, 0x53, 0x00, + 0x54, 0x00, 0x3b, 0x55, 0x56, 0x00, 0x58, 0x5a, + 0x40, 0x5f, 0x5e, 0x00, 0x47, 0x52, 0x63, 0x65, + 0x43, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, + 0x00, 0x6e, 0x00, 0x70, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, + 0x20, 0x36, 0x00, 0x28, 0x00, 0x24, 0x00, 0x24, + 0x25, 0x2d, 0x00, 0x13, 0x6d, 0x6f, 0x00, 0x29, + 0x27, 0x2a, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x41, + 0x1e, 0x42, 0x1f, 0x4e, 0x3c, 0x40, 0x22, 0x21, + 0x44, 0x21, 0x43, 0x26, 0x28, 0x27, 0x29, 0x23, + 0x2b, 0x4b, 0x2d, 0x46, 0x2f, 0x4c, 0x31, 0x4d, + 0x33, 0x47, 0x45, 0x99, 0x00, 0x00, 0x97, 0x91, + 0x7f, 0x80, 0x85, 0x86, 0x12, 0x82, 0x84, 0x78, + 0x79, 0x12, 0x7d, 0xa3, 0x7e, 0x7a, 0x7b, 0x8c, + 0x92, 0x98, 0xa6, 0xa0, 0x87, 0x00, 0x9a, 0xa1, + 0x95, 0x77, 0x33, 0x95, 0x00, 0x90, 0x00, 0x76, + 0x9b, 0x9a, 0x99, 0x98, 0x00, 0x00, 0xa0, 0x00, + 0x9e, 0x00, 0xa3, 0xa2, 0x15, 0x31, 0x32, 0x33, + 0xb7, 0xb8, 0x55, 0xac, 0xab, 0x12, 0x14, 0x1e, + 0x21, 0x22, 0x22, 0x2a, 0x34, 0x35, 0x00, 0xa8, + 0xa9, 0x39, 0x22, 0x4c, 0x00, 0x00, 0x97, 0x01, + 0x5a, 0xda, 0x1d, 0x36, 0x05, 0x00, 0xc7, 0xc6, + 0xc9, 0xc8, 0xcb, 0xca, 0xcd, 0xcc, 0xcf, 0xce, + 0xc4, 0xd8, 0x45, 0xd9, 0x42, 0xda, 0x46, 0xdb, + 0xd1, 0xd3, 0xd5, 0xd7, 0xdd, 0xdc, 0xf1, 0xf9, + 0x01, 0x11, 0x0a, 0x12, 0x80, 0x9f, 0x00, 0x21, + 0x80, 0xa3, 0xf0, 0x00, 0xc0, 0x40, 0xc6, 0x60, + 0xea, 0xde, 0xe6, 0x99, 0xc0, 0x00, 0x00, 0x06, + 0x60, 0xdf, 0x29, 0x00, 0x15, 0x12, 0x06, 0x16, + 0xfb, 0xe0, 0x09, 0x15, 0x12, 0x84, 0x0b, 0xc6, + 0x16, 0x02, 0xe2, 0x06, 0xc0, 0x40, 0x00, 0x46, + 0x60, 0xe1, 0xe3, 0x6d, 0x37, 0x38, 0x39, 0x18, + 0x17, 0x1a, 0x19, 0x00, 0x1d, 0x1c, 0x1f, 0x1e, + 0x00, 0x61, 0xba, 0x67, 0x45, 0x48, 0x00, 0x50, + 0x64, 0x4f, 0x51, 0x00, 0x00, 0x49, 0x00, 0x00, + 0x00, 0xa5, 0xa6, 0xa7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x4a, 0x00, + 0x5d, 0x57, 0x59, 0x62, 0x60, 0x72, 0x6b, 0x71, + 0x54, 0x00, 0x3e, 0x69, 0xbb, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x25, 0x00, 0x48, 0xaa, 0x8a, 0x8b, + 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, + 0x6f, 0xb2, 0x63, 0x62, 0x65, 0x64, 0x67, 0x66, + 0x6c, 0x6d, 0x6e, 0x6f, 0x68, 0x69, 0x6a, 0x6b, + 0x71, 0x70, 0x73, 0x72, 0x75, 0x74, 0x77, 0x76, + 0x79, 0x78, }; static const uint16_t case_conv_ext[58] = { @@ -160,41 +163,44 @@ static const uint16_t case_conv_ext[58] = { 0x006b, 0x00e5, }; -static const uint8_t unicode_prop_Cased1_table[196] = { +static const uint8_t unicode_prop_Cased1_table[193] = { 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, - 0x80, 0x8c, 0x80, 0x8d, 0x81, 0x8d, 0x02, 0x80, - 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, - 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30, - 0x08, 0x01, 0x15, 0x20, 0x00, 0x39, 0x99, 0x31, - 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6, - 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x4b, 0x72, - 0x80, 0x4c, 0x02, 0xf8, 0x02, 0x80, 0x8f, 0x80, - 0xb0, 0x40, 0xdb, 0x08, 0x80, 0x41, 0xd0, 0x80, - 0x8c, 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, - 0x00, 0x14, 0x28, 0x10, 0x11, 0x02, 0x01, 0x18, - 0x0b, 0x24, 0x4b, 0x26, 0x01, 0x01, 0x86, 0xe5, - 0x80, 0x60, 0x79, 0xb6, 0x81, 0x40, 0x91, 0x81, - 0xbd, 0x88, 0x94, 0x05, 0x80, 0x98, 0x80, 0xa2, - 0x00, 0x80, 0x9b, 0x12, 0x82, 0x43, 0x34, 0xa2, - 0x06, 0x80, 0x8d, 0x60, 0x5c, 0x15, 0x01, 0x10, - 0xa9, 0x80, 0x88, 0x60, 0xcc, 0x44, 0xd4, 0x80, - 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, - 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, - 0x03, 0x04, 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, - 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, - 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, - 0x9e, 0x80, 0x98, 0x07, 0x47, 0x33, 0x89, 0x80, - 0x93, 0x2d, 0x41, 0x04, 0xbd, 0x50, 0xc1, 0x99, - 0x85, 0x99, 0x85, 0x99, + 0x80, 0x9b, 0x81, 0x8d, 0x02, 0x80, 0xe1, 0x80, + 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, 0x11, 0x03, + 0x04, 0x08, 0x01, 0x08, 0x30, 0x08, 0x01, 0x15, + 0x20, 0x00, 0x39, 0x99, 0x31, 0x9d, 0x84, 0x40, + 0x94, 0x80, 0xd6, 0x82, 0xa6, 0x80, 0x41, 0x62, + 0x80, 0xa6, 0x80, 0x4b, 0x72, 0x80, 0x4c, 0x02, + 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb, + 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, + 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, + 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b, + 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, + 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, + 0x05, 0x80, 0x98, 0x80, 0xa2, 0x00, 0x80, 0x9b, + 0x12, 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8d, + 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, 0x88, + 0x60, 0xcc, 0x44, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x07, 0x47, 0x33, 0x89, 0x80, 0x93, 0x2d, 0x41, + 0x04, 0xbd, 0x50, 0xc1, 0x99, 0x85, 0x99, 0x85, + 0x99, }; -static const uint8_t unicode_prop_Cased1_index[21] = { - 0xb9, 0x02, 0xe0, 0xc0, 0x1d, 0x20, 0xe5, 0x2c, - 0x20, 0xb1, 0x07, 0x21, 0xc1, 0xd6, 0x21, 0x4a, - 0xf1, 0x01, 0x8a, 0xf1, 0x01, +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0x80, // 002B9 at 36 + 0xa0, 0x1e, 0x40, // 01EA0 at 66 + 0x9e, 0xa6, 0x40, // 0A69E at 98 + 0xbb, 0x07, 0x01, // 107BB at 128 + 0xdb, 0xd6, 0x01, // 1D6DB at 160 + 0x8a, 0xf1, 0x01, // 1F18A at 192 (upper bound) }; -static const uint8_t unicode_prop_Case_Ignorable_table[737] = { +static const uint8_t unicode_prop_Case_Ignorable_table[764] = { 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, @@ -203,7 +209,7 @@ static const uint8_t unicode_prop_Case_Ignorable_table[737] = { 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08, - 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0d, 0x87, + 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0c, 0x88, 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86, 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, @@ -257,26 +263,29 @@ static const uint8_t unicode_prop_Case_Ignorable_table[737] = { 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, 0x28, 0xa9, 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, - 0x41, 0x82, 0x81, 0xcf, 0x82, 0xc5, 0x8a, 0xb0, - 0x83, 0xfa, 0x80, 0xb5, 0x8e, 0xa8, 0x01, 0x81, - 0x89, 0x82, 0xb0, 0x19, 0x09, 0x03, 0x80, 0x89, - 0x80, 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, - 0x8b, 0x81, 0xb3, 0x88, 0x89, 0x19, 0x80, 0xde, - 0x11, 0x00, 0x0d, 0x01, 0x80, 0x40, 0x9c, 0x02, - 0x87, 0x94, 0x81, 0xb8, 0x0a, 0x80, 0xa4, 0x32, - 0x84, 0x40, 0xc2, 0x39, 0x10, 0x80, 0x96, 0x80, - 0xd3, 0x28, 0x03, 0x08, 0x81, 0x40, 0xed, 0x1d, - 0x08, 0x81, 0x9a, 0x81, 0xd4, 0x39, 0x00, 0x81, - 0xe9, 0x00, 0x01, 0x28, 0x80, 0xe4, 0x11, 0x18, - 0x84, 0x41, 0x02, 0x88, 0x01, 0x40, 0xff, 0x08, - 0x03, 0x80, 0x40, 0x8f, 0x19, 0x0b, 0x80, 0x9f, - 0x89, 0xa7, 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, - 0xad, 0x8c, 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, - 0xd1, 0x95, 0x0e, 0x01, 0x01, 0xf9, 0x2a, 0x00, - 0x08, 0x30, 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, - 0x5a, 0x81, 0x8a, 0x81, 0xb3, 0x24, 0x00, 0x80, - 0x54, 0xec, 0x90, 0x85, 0x8e, 0x60, 0x36, 0x99, - 0x84, 0xba, 0x86, 0x88, 0x83, 0x44, 0x0a, 0x80, + 0xa5, 0x80, 0x99, 0x20, 0x80, 0x41, 0x3a, 0x81, + 0xce, 0x83, 0xc5, 0x8a, 0xb0, 0x83, 0xfa, 0x80, + 0xb5, 0x8e, 0xa8, 0x01, 0x81, 0x89, 0x82, 0xb0, + 0x19, 0x09, 0x03, 0x80, 0x89, 0x80, 0xb1, 0x82, + 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b, 0x81, 0xb3, + 0x88, 0x89, 0x19, 0x80, 0xde, 0x11, 0x00, 0x0d, + 0x01, 0x80, 0x40, 0x9c, 0x02, 0x87, 0x94, 0x81, + 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0xc5, 0x85, + 0x8c, 0x00, 0x00, 0x80, 0x8d, 0x81, 0xd4, 0x39, + 0x10, 0x80, 0x96, 0x80, 0xd3, 0x28, 0x03, 0x08, + 0x81, 0x40, 0xed, 0x1d, 0x08, 0x81, 0x9a, 0x81, + 0xd4, 0x39, 0x00, 0x81, 0xe9, 0x00, 0x01, 0x28, + 0x80, 0xe4, 0x00, 0x01, 0x18, 0x84, 0x41, 0x02, + 0x88, 0x01, 0x40, 0xff, 0x08, 0x03, 0x80, 0x40, + 0x8f, 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7, 0x29, + 0x1f, 0x80, 0x88, 0x29, 0x82, 0xad, 0x8c, 0x01, + 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, + 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, + 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 0x81, 0x8a, + 0x81, 0xb3, 0x24, 0x00, 0x80, 0x96, 0x80, 0x54, + 0xd4, 0x90, 0x85, 0x8e, 0x60, 0x2c, 0xc7, 0x8b, + 0x12, 0x49, 0xbf, 0x84, 0xba, 0x86, 0x88, 0x83, + 0x41, 0xfb, 0x82, 0xa7, 0x81, 0x41, 0xe1, 0x80, 0xbe, 0x90, 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, 0x18, 0x30, 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, 0x5b, 0xad, 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, @@ -285,24 +294,39 @@ static const uint8_t unicode_prop_Case_Ignorable_table[737] = { 0x20, 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x84, 0xbd, 0xa0, 0x80, 0x40, 0x9f, 0x8d, 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x41, 0xfa, 0x84, - 0x43, 0xdf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, - 0x6c, 0x0c, 0x00, 0x80, 0x9d, 0xdf, 0xff, 0x40, - 0xef, + 0x40, 0xfd, 0x81, 0x42, 0xdf, 0x86, 0xec, 0x87, + 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, 0x80, 0x9d, + 0xdf, 0xff, 0x40, 0xef, }; -static const uint8_t unicode_prop_Case_Ignorable_index[69] = { - 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, - 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, - 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, - 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, - 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, - 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x75, 0x10, 0x01, - 0xeb, 0x12, 0x21, 0x41, 0x16, 0x01, 0x5c, 0x1a, - 0x01, 0x43, 0x1f, 0x01, 0x2e, 0xcf, 0x41, 0x25, - 0xe0, 0x01, 0xf0, 0x01, 0x0e, +static const uint8_t unicode_prop_Case_Ignorable_index[72] = { + 0xbe, 0x05, 0x00, // 005BE at 32 + 0xfe, 0x07, 0x00, // 007FE at 64 + 0x52, 0x0a, 0xa0, // 00A52 at 101 + 0xc1, 0x0b, 0x00, // 00BC1 at 128 + 0x82, 0x0d, 0x00, // 00D82 at 160 + 0x3f, 0x10, 0x80, // 0103F at 196 + 0xd4, 0x17, 0x40, // 017D4 at 226 + 0xcf, 0x1a, 0x20, // 01ACF at 257 + 0xf5, 0x1c, 0x00, // 01CF5 at 288 + 0x80, 0x20, 0x00, // 02080 at 320 + 0x16, 0xa0, 0x00, // 0A016 at 352 + 0xc6, 0xa8, 0x00, // 0A8C6 at 384 + 0xc2, 0xaa, 0x60, // 0AAC2 at 419 + 0x56, 0xfe, 0x20, // 0FE56 at 449 + 0xb1, 0x07, 0x01, // 107B1 at 480 + 0x02, 0x10, 0x01, // 11002 at 512 + 0x42, 0x12, 0x41, // 11242 at 546 + 0xc4, 0x14, 0x21, // 114C4 at 577 + 0xe1, 0x19, 0x81, // 119E1 at 612 + 0x48, 0x1d, 0x01, // 11D48 at 640 + 0x44, 0x6b, 0x01, // 16B44 at 672 + 0x83, 0xd1, 0x21, // 1D183 at 705 + 0x3e, 0xe1, 0x01, // 1E13E at 736 + 0xf0, 0x01, 0x0e, // E01F0 at 768 (upper bound) }; -static const uint8_t unicode_prop_ID_Start_table[1100] = { +static const uint8_t unicode_prop_ID_Start_table[1133] = { 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, @@ -346,7 +370,7 @@ static const uint8_t unicode_prop_ID_Start_table[1100] = { 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80, 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3, - 0x81, 0x88, 0x86, 0xaa, 0x0a, 0xa8, 0x18, 0x28, + 0x81, 0x8a, 0x84, 0xaa, 0x0a, 0xa8, 0x18, 0x28, 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80, 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13, @@ -362,8 +386,8 @@ static const uint8_t unicode_prop_ID_Start_table[1100] = { 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c, 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, - 0x88, 0x81, 0xe6, 0x81, 0xbf, 0x21, 0x00, 0x04, - 0x97, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3, + 0x88, 0x81, 0xe6, 0x81, 0xc2, 0x09, 0x00, 0x07, + 0x94, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3, 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b, 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80, 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10, @@ -383,91 +407,117 @@ static const uint8_t unicode_prop_ID_Start_table[1100] = { 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, 0x04, 0xa9, 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7, 0x87, 0xb3, 0x8b, 0x8a, 0x80, 0x8e, 0x06, 0x01, - 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0xc2, 0x41, - 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, 0x28, 0xa9, - 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, 0x01, 0x10, - 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92, - 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, 0xb7, 0x29, - 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, 0xa9, 0x9c, - 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, 0xb5, 0x89, - 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, 0xc8, 0xb6, - 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0x41, 0x5b, 0xa9, - 0x29, 0xcd, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91, + 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0x82, 0xb3, + 0x8b, 0x41, 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, + 0x28, 0xa9, 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, + 0x01, 0x10, 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, + 0xc0, 0x92, 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, + 0xb7, 0x29, 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, + 0xa9, 0x9c, 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, + 0xb5, 0x89, 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, + 0xc8, 0xb6, 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0xa5, + 0x9b, 0x88, 0x96, 0x40, 0xf9, 0xa9, 0x29, 0x8f, + 0x82, 0xba, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91, 0xad, 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09, 0x80, 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, 0x01, 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83, 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0x92, 0x81, 0xbe, 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, 0x86, 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, - 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x40, 0x9d, - 0xb4, 0x91, 0x83, 0x93, 0x82, 0x9d, 0xaf, 0x93, - 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, - 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, - 0x9a, 0xa4, 0x86, 0x40, 0xb8, 0xab, 0xf3, 0xbf, - 0x9e, 0x39, 0x01, 0x38, 0x08, 0x97, 0x8e, 0x00, - 0x80, 0xdd, 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, - 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, - 0x92, 0x80, 0x91, 0xc8, 0x41, 0x06, 0x88, 0x80, - 0xa4, 0x90, 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, - 0xa5, 0x94, 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, - 0x80, 0x41, 0x46, 0x92, 0x8e, 0x00, 0x8c, 0x80, - 0xa1, 0xfb, 0x80, 0xce, 0x43, 0x99, 0xe5, 0xee, - 0x90, 0x40, 0xc3, 0x4a, 0x4b, 0xe0, 0x8e, 0x44, - 0x2f, 0x90, 0x85, 0x4f, 0xb8, 0x42, 0x46, 0x60, - 0x21, 0xb8, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce, - 0x90, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, - 0x84, 0x92, 0x42, 0xaf, 0xbf, 0xff, 0xca, 0x20, - 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, 0x57, 0xf7, - 0x87, 0x44, 0xd5, 0xa9, 0x88, 0x60, 0x22, 0xe6, - 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, 0x80, 0x9c, - 0x11, 0x80, 0x8d, 0x1f, 0x41, 0x8b, 0x49, 0x03, - 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, 0x89, 0x57, - 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, - 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, - 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, - 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, - 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, - 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x47, - 0x33, 0x9e, 0x2d, 0x41, 0x04, 0xbd, 0x40, 0x91, - 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x40, 0x9d, - 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x42, 0xf3, 0x30, - 0x18, 0x08, 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, - 0x30, 0x44, 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, - 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, - 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, - 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, - 0x51, 0x43, 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, - 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, - 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, 0x53, - 0x4a, 0x84, 0x50, 0x5f, + 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x9d, 0x89, + 0x00, 0x08, 0x80, 0xa5, 0x00, 0x98, 0x00, 0x80, + 0xab, 0xb4, 0x91, 0x83, 0x93, 0x82, 0x9d, 0xaf, + 0x93, 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, + 0xa3, 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, + 0xc6, 0x9a, 0xa4, 0x86, 0x40, 0xb8, 0xab, 0xf3, + 0xbf, 0x9e, 0x39, 0x01, 0x38, 0x08, 0x97, 0x8e, + 0x00, 0x80, 0xdd, 0x39, 0xa6, 0x8f, 0x00, 0x80, + 0x9b, 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, + 0xad, 0x92, 0x80, 0x91, 0xc8, 0x40, 0xc6, 0xa0, + 0x9e, 0x88, 0x80, 0xa4, 0x90, 0x80, 0xb0, 0x9d, + 0xef, 0x30, 0x08, 0xa5, 0x94, 0x80, 0x98, 0x28, + 0x08, 0x9f, 0x8d, 0x80, 0x41, 0x46, 0x92, 0x8e, + 0x00, 0x8c, 0x80, 0xa1, 0xfb, 0x80, 0xce, 0x43, + 0x99, 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0x4b, + 0xe0, 0x8e, 0x44, 0x2f, 0x90, 0x85, 0x98, 0x4f, + 0x9a, 0x84, 0x42, 0x46, 0x5a, 0xb8, 0x9d, 0x46, + 0xe1, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce, 0x90, + 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, 0x84, + 0x92, 0x41, 0xaf, 0xac, 0x40, 0xd2, 0xbf, 0xff, + 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, + 0x57, 0xf7, 0x87, 0x44, 0xd5, 0xa8, 0x89, 0x60, + 0x22, 0xe6, 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, + 0x80, 0x9c, 0x11, 0x80, 0x8d, 0x1f, 0x41, 0x8b, + 0x49, 0x03, 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, + 0x89, 0x57, 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x07, 0x47, 0x33, 0x9e, 0x2d, 0x41, 0x04, 0xbd, + 0x40, 0x91, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, + 0x40, 0x9d, 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x40, + 0xe3, 0x9d, 0x08, 0x41, 0xee, 0x30, 0x18, 0x08, + 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, 0x30, 0x44, + 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, + 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, + 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43, + 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, + 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, + 0x42, 0x6d, 0x49, 0xa1, 0x42, 0x1d, 0x45, 0xe1, + 0x53, 0x4a, 0x84, 0x50, 0x5f, }; -static const uint8_t unicode_prop_ID_Start_index[105] = { - 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, - 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, - 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, - 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, - 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, - 0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20, - 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, - 0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3, - 0x0c, 0x21, 0x73, 0x11, 0x61, 0x34, 0x13, 0x01, - 0x1b, 0x17, 0x21, 0x8a, 0x1a, 0x01, 0x34, 0x1f, - 0x21, 0xbf, 0x6a, 0x01, 0x23, 0xb1, 0xa1, 0xad, - 0xd4, 0x01, 0x6f, 0xd7, 0x01, 0xff, 0xe7, 0x61, - 0x5e, 0xee, 0x01, 0xe1, 0xeb, 0x22, 0xb0, 0x23, - 0x03, +static const uint8_t unicode_prop_ID_Start_index[108] = { + 0xf6, 0x03, 0x20, // 003F6 at 33 + 0xa6, 0x07, 0x00, // 007A6 at 64 + 0xa9, 0x09, 0x20, // 009A9 at 97 + 0xb1, 0x0a, 0x00, // 00AB1 at 128 + 0xba, 0x0b, 0x20, // 00BBA at 161 + 0x3b, 0x0d, 0x20, // 00D3B at 193 + 0xc7, 0x0e, 0x20, // 00EC7 at 225 + 0x49, 0x12, 0x00, // 01249 at 256 + 0x9b, 0x16, 0x00, // 0169B at 288 + 0xac, 0x19, 0x00, // 019AC at 320 + 0xc0, 0x1d, 0x80, // 01DC0 at 356 + 0x80, 0x20, 0x20, // 02080 at 385 + 0x70, 0x2d, 0x00, // 02D70 at 416 + 0x00, 0x32, 0x00, // 03200 at 448 + 0xdd, 0xa7, 0x00, // 0A7DD at 480 + 0x4c, 0xaa, 0x20, // 0AA4C at 513 + 0xc7, 0xd7, 0x20, // 0D7C7 at 545 + 0xfc, 0xfd, 0x20, // 0FDFC at 577 + 0x9d, 0x02, 0x21, // 1029D at 609 + 0x96, 0x05, 0x01, // 10596 at 640 + 0x9f, 0x08, 0x01, // 1089F at 672 + 0x49, 0x0c, 0x21, // 10C49 at 705 + 0x76, 0x10, 0x21, // 11076 at 737 + 0xa9, 0x12, 0x01, // 112A9 at 768 + 0xb0, 0x14, 0x01, // 114B0 at 800 + 0x42, 0x19, 0x41, // 11942 at 834 + 0x90, 0x1c, 0x01, // 11C90 at 864 + 0xf1, 0x2f, 0x21, // 12FF1 at 897 + 0x90, 0x6b, 0x21, // 16B90 at 929 + 0x33, 0xb1, 0x21, // 1B133 at 961 + 0x06, 0xd5, 0x01, // 1D506 at 992 + 0xc3, 0xd7, 0x01, // 1D7C3 at 1024 + 0xff, 0xe7, 0x21, // 1E7FF at 1057 + 0x63, 0xee, 0x01, // 1EE63 at 1088 + 0x5e, 0xee, 0x42, // 2EE5E at 1122 + 0xb0, 0x23, 0x03, // 323B0 at 1152 (upper bound) }; -static const uint8_t unicode_prop_ID_Continue1_table[660] = { +static const uint8_t unicode_prop_ID_Continue1_table[695] = { 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, - 0x04, 0xaa, 0x82, 0xbb, 0x87, 0xa9, 0x97, 0x80, + 0x04, 0xaa, 0x82, 0xba, 0x88, 0xa9, 0x97, 0x80, 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, @@ -496,70 +546,88 @@ static const uint8_t unicode_prop_ID_Continue1_table[660] = { 0xae, 0x90, 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x8d, 0xaf, 0x93, 0x87, 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, - 0x28, 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x3e, 0x81, - 0x92, 0x80, 0xfa, 0x8c, 0x18, 0x82, 0x8b, 0x4b, - 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, 0x9f, 0x42, - 0x29, 0x85, 0xe8, 0x81, 0x60, 0x75, 0x84, 0x89, - 0xc4, 0x03, 0x89, 0x9f, 0x81, 0xcf, 0x81, 0x41, - 0x0f, 0x02, 0x03, 0x80, 0x96, 0x23, 0x80, 0xd2, - 0x81, 0xb1, 0x91, 0x89, 0x89, 0x85, 0x91, 0x8c, - 0x8a, 0x9b, 0x87, 0x98, 0x8c, 0xab, 0x83, 0xae, - 0x8d, 0x8e, 0x89, 0x8a, 0x80, 0x89, 0x89, 0xae, - 0x8d, 0x8b, 0x07, 0x09, 0x89, 0xa0, 0x82, 0xb1, - 0x00, 0x11, 0x0c, 0x08, 0x80, 0xa8, 0x24, 0x81, - 0x40, 0xeb, 0x38, 0x09, 0x89, 0x60, 0x4f, 0x23, - 0x80, 0x42, 0xe0, 0x8f, 0x8f, 0x8f, 0x11, 0x97, - 0x82, 0x40, 0xbf, 0x89, 0xa4, 0x80, 0x42, 0xbc, - 0x80, 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, - 0x24, 0x89, 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, - 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, - 0x89, 0x41, 0x70, 0x81, 0xcf, 0x82, 0xc5, 0x8a, - 0xb0, 0x83, 0xf9, 0x82, 0xb4, 0x8e, 0x9e, 0x8a, - 0x09, 0x89, 0x83, 0xac, 0x8a, 0x30, 0xac, 0x89, - 0x2a, 0xa3, 0x8d, 0x80, 0x89, 0x21, 0xab, 0x80, - 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x80, 0x8b, 0xd1, - 0x8b, 0x28, 0x08, 0x40, 0x9c, 0x8b, 0x84, 0x89, - 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, 0x88, 0x80, - 0x89, 0x09, 0x32, 0x84, 0x40, 0xbf, 0x91, 0x88, - 0x89, 0x18, 0xd0, 0x93, 0x8b, 0x89, 0x40, 0xd4, - 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, 0x8e, 0x89, - 0xd0, 0x8c, 0x87, 0x89, 0xd2, 0x8e, 0x83, 0x89, - 0x40, 0xf1, 0x8e, 0x40, 0xa4, 0x89, 0xc5, 0x28, - 0x09, 0x18, 0x00, 0x81, 0x8b, 0x89, 0xf6, 0x31, - 0x32, 0x80, 0x9b, 0x89, 0xa7, 0x30, 0x1f, 0x80, - 0x88, 0x8a, 0xad, 0x8f, 0x41, 0x94, 0x38, 0x87, - 0x8f, 0x89, 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, - 0x00, 0x08, 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, - 0x27, 0x89, 0x41, 0x48, 0x83, 0x88, 0x08, 0x80, - 0xaf, 0x32, 0x84, 0x8c, 0x89, 0x54, 0xe5, 0x05, - 0x8e, 0x60, 0x36, 0x09, 0x89, 0xd5, 0x89, 0xa5, - 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, 0xf4, 0x00, - 0xb6, 0x33, 0xd0, 0x80, 0x8a, 0x81, 0x60, 0x4c, - 0xaa, 0x81, 0x52, 0x60, 0xad, 0x81, 0x96, 0x42, - 0x1d, 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x40, - 0x93, 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, - 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, - 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, - 0x80, 0x40, 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, - 0x80, 0xbc, 0x8d, 0x41, 0xf1, 0x8d, 0x43, 0xd5, - 0x86, 0xec, 0x34, 0x89, 0x52, 0x95, 0x89, 0x6c, - 0x05, 0x05, 0x40, 0xef, + 0x28, 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x0b, 0x81, + 0xb0, 0x81, 0x92, 0x80, 0xfa, 0x8c, 0x18, 0x82, + 0x8b, 0x4b, 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, + 0x9f, 0x42, 0x29, 0x85, 0xe8, 0x81, 0xdf, 0x80, + 0x60, 0x75, 0x23, 0x89, 0xc4, 0x03, 0x89, 0x9f, + 0x81, 0xcf, 0x81, 0x41, 0x0f, 0x02, 0x03, 0x80, + 0x96, 0x23, 0x80, 0xd2, 0x81, 0xb1, 0x91, 0x89, + 0x89, 0x85, 0x91, 0x8c, 0x8a, 0x9b, 0x87, 0x98, + 0x8c, 0xab, 0x83, 0xae, 0x8d, 0x8e, 0x89, 0x8a, + 0x80, 0x89, 0x89, 0xae, 0x8d, 0x8b, 0x07, 0x09, + 0x89, 0xa0, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, + 0x80, 0xa8, 0x24, 0x81, 0x40, 0xeb, 0x38, 0x09, + 0x89, 0x60, 0x4f, 0x23, 0x80, 0x42, 0xe0, 0x8f, + 0x8f, 0x8f, 0x11, 0x97, 0x82, 0x40, 0xbf, 0x89, + 0xa4, 0x80, 0xa4, 0x80, 0x42, 0x96, 0x80, 0x40, + 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, 0x24, 0x89, + 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, + 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, 0x89, 0x85, + 0x89, 0x9e, 0x84, 0x41, 0x3c, 0x81, 0xce, 0x83, + 0xc5, 0x8a, 0xb0, 0x83, 0xf9, 0x82, 0xb4, 0x8e, + 0x9e, 0x8a, 0x09, 0x89, 0x83, 0xac, 0x8a, 0x30, + 0xac, 0x89, 0x2a, 0xa3, 0x8d, 0x80, 0x89, 0x21, + 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x80, + 0x8b, 0xd1, 0x8b, 0x28, 0x08, 0x40, 0x9c, 0x8b, + 0x84, 0x89, 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, + 0x88, 0x80, 0x89, 0x09, 0x32, 0x84, 0xc2, 0x88, + 0x00, 0x08, 0x03, 0x04, 0x00, 0x8d, 0x81, 0xd1, + 0x91, 0x88, 0x89, 0x18, 0xd0, 0x93, 0x8b, 0x89, + 0x40, 0xd4, 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, + 0x8e, 0x89, 0xd0, 0x8c, 0x87, 0x89, 0x85, 0x93, + 0xb8, 0x8e, 0x83, 0x89, 0x40, 0xf1, 0x8e, 0x40, + 0xa4, 0x89, 0xc5, 0x28, 0x09, 0x18, 0x00, 0x81, + 0x8b, 0x89, 0xf6, 0x31, 0x32, 0x80, 0x9b, 0x89, + 0xa7, 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad, 0x8f, + 0x41, 0x55, 0x89, 0xb4, 0x38, 0x87, 0x8f, 0x89, + 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, + 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, + 0x41, 0x48, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, + 0x84, 0x8c, 0x8a, 0x54, 0xe4, 0x05, 0x8e, 0x60, + 0x2c, 0xc7, 0x9b, 0x49, 0x25, 0x89, 0xd5, 0x89, + 0xa5, 0x84, 0xba, 0x86, 0x98, 0x89, 0x42, 0x15, + 0x89, 0x41, 0xd4, 0x00, 0xb6, 0x33, 0xd0, 0x80, + 0x8a, 0x81, 0x60, 0x4c, 0xaa, 0x81, 0x50, 0x50, + 0x89, 0x42, 0x05, 0xad, 0x81, 0x96, 0x42, 0x1d, + 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x40, 0x93, + 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, 0x83, + 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, 0x45, + 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, 0x80, + 0x40, 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, 0x80, + 0xbc, 0x8d, 0x41, 0xf1, 0x8d, 0x40, 0xf3, 0x08, + 0x89, 0x42, 0xd4, 0x86, 0xec, 0x34, 0x89, 0x52, + 0x95, 0x89, 0x6c, 0x05, 0x05, 0x40, 0xef, }; -static const uint8_t unicode_prop_ID_Continue1_index[63] = { - 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, - 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, - 0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00, - 0x41, 0x20, 0x00, 0x0c, 0xa8, 0x80, 0x37, 0xaa, - 0x20, 0x50, 0xfe, 0x20, 0x3a, 0x0d, 0x21, 0x74, - 0x11, 0x01, 0x5a, 0x14, 0x21, 0x44, 0x19, 0x81, - 0x5a, 0x1d, 0xa1, 0xf5, 0x6a, 0x21, 0x45, 0xd2, - 0x41, 0xaf, 0xe2, 0x21, 0xf0, 0x01, 0x0e, +static const uint8_t unicode_prop_ID_Continue1_index[66] = { + 0xfa, 0x06, 0x00, // 006FA at 32 + 0x70, 0x09, 0x00, // 00970 at 64 + 0xf0, 0x0a, 0x40, // 00AF0 at 98 + 0x57, 0x0c, 0x00, // 00C57 at 128 + 0xf0, 0x0d, 0x60, // 00DF0 at 163 + 0xc7, 0x0f, 0x20, // 00FC7 at 193 + 0xea, 0x17, 0x40, // 017EA at 226 + 0x05, 0x1b, 0x00, // 01B05 at 256 + 0x0e, 0x20, 0x00, // 0200E at 288 + 0xa0, 0xa6, 0x20, // 0A6A0 at 321 + 0xe6, 0xa9, 0x20, // 0A9E6 at 353 + 0x10, 0xfe, 0x00, // 0FE10 at 384 + 0x40, 0x0a, 0x01, // 10A40 at 416 + 0xc3, 0x10, 0x01, // 110C3 at 448 + 0x4e, 0x13, 0x01, // 1134E at 480 + 0x41, 0x16, 0x01, // 11641 at 512 + 0x0b, 0x1a, 0x01, // 11A0B at 544 + 0xaa, 0x1d, 0x01, // 11DAA at 576 + 0x7a, 0x6d, 0x21, // 16D7A at 609 + 0x45, 0xd2, 0x21, // 1D245 at 641 + 0xaf, 0xe2, 0x01, // 1E2AF at 672 + 0xf0, 0x01, 0x0e, // E01F0 at 704 (upper bound) }; #ifdef CONFIG_ALL_UNICODE -static const uint8_t unicode_cc_table[899] = { +static const uint8_t unicode_cc_table[916] = { 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, @@ -583,7 +651,7 @@ static const uint8_t unicode_cc_table[899] = { 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, - 0xaa, 0x02, 0xdc, 0xb0, 0x0b, 0xc0, 0x02, 0xdc, + 0xaa, 0x02, 0xdc, 0xb0, 0x0a, 0xc1, 0x02, 0xdc, 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, @@ -641,55 +709,75 @@ static const uint8_t unicode_cc_table[899] = { 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4, 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0, 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0, - 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb1, - 0x52, 0xc1, 0xb0, 0x1f, 0x02, 0xdc, 0xb0, 0x15, - 0x01, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x03, 0xdc, - 0xb0, 0x00, 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, - 0xb0, 0x8f, 0x00, 0x09, 0xa8, 0x00, 0x09, 0x8d, - 0x00, 0x09, 0xb0, 0x08, 0x00, 0x09, 0x00, 0x07, - 0xb0, 0x14, 0xc2, 0xaf, 0x01, 0x09, 0xb0, 0x0d, - 0x00, 0x07, 0xb0, 0x1b, 0x00, 0x09, 0x88, 0x00, - 0x07, 0xb0, 0x39, 0x00, 0x09, 0x00, 0x07, 0xb0, - 0x81, 0x00, 0x07, 0x00, 0x09, 0xb0, 0x1f, 0x01, - 0x07, 0x8f, 0x00, 0x09, 0x97, 0xc6, 0x82, 0xc4, - 0xb0, 0x9c, 0x00, 0x09, 0x82, 0x00, 0x07, 0x96, - 0xc0, 0xb0, 0x32, 0x00, 0x09, 0x00, 0x07, 0xb0, - 0xca, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x4d, 0x00, - 0x09, 0xb0, 0x45, 0x00, 0x09, 0x00, 0x07, 0xb0, - 0x42, 0x00, 0x09, 0xb0, 0xdc, 0x00, 0x09, 0x00, - 0x07, 0xb0, 0xd1, 0x01, 0x09, 0x83, 0x00, 0x07, - 0xb0, 0x6b, 0x00, 0x09, 0xb0, 0x22, 0x00, 0x09, - 0x91, 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, - 0x74, 0x00, 0x09, 0xb0, 0xd1, 0x00, 0x07, 0x80, - 0x01, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x78, - 0x01, 0x09, 0xb8, 0x43, 0x7c, 0x04, 0x01, 0xb0, - 0x0a, 0xc6, 0xb4, 0x88, 0x01, 0x06, 0xb8, 0x44, - 0x7b, 0x00, 0x01, 0xb8, 0x0c, 0x95, 0x01, 0xd8, - 0x02, 0x01, 0x82, 0x00, 0xe2, 0x04, 0xd8, 0x87, - 0x07, 0xdc, 0x81, 0xc4, 0x01, 0xdc, 0x9d, 0xc3, - 0xb0, 0x63, 0xc2, 0xb8, 0x05, 0x8a, 0xc6, 0x80, - 0xd0, 0x81, 0xc6, 0x80, 0xc1, 0x80, 0xc4, 0xb0, - 0x33, 0xc0, 0xb0, 0x6f, 0xc6, 0xb1, 0x46, 0xc0, - 0xb0, 0x0c, 0xc3, 0xb1, 0xcb, 0x01, 0xe8, 0x00, - 0xdc, 0xc0, 0xb3, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, - 0xc5, 0x00, 0x07, + 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb0, + 0x10, 0xc4, 0xb1, 0x0c, 0xc1, 0xb0, 0x1f, 0x02, + 0xdc, 0xb0, 0x15, 0x01, 0xdc, 0xc2, 0x00, 0xdc, + 0xc0, 0x03, 0xdc, 0xb0, 0x00, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xb0, 0x8f, 0x00, 0x09, 0xa8, + 0x00, 0x09, 0x8d, 0x00, 0x09, 0xb0, 0x08, 0x00, + 0x09, 0x00, 0x07, 0xb0, 0x14, 0xc2, 0xaf, 0x01, + 0x09, 0xb0, 0x0d, 0x00, 0x07, 0xb0, 0x1b, 0x00, + 0x09, 0x88, 0x00, 0x07, 0xb0, 0x39, 0x00, 0x09, + 0x00, 0x07, 0xb0, 0x81, 0x00, 0x07, 0x00, 0x09, + 0xb0, 0x1f, 0x01, 0x07, 0x8f, 0x00, 0x09, 0x97, + 0xc6, 0x82, 0xc4, 0xb0, 0x28, 0x02, 0x09, 0xb0, + 0x40, 0x00, 0x09, 0x82, 0x00, 0x07, 0x96, 0xc0, + 0xb0, 0x32, 0x00, 0x09, 0x00, 0x07, 0xb0, 0xca, + 0x00, 0x09, 0x00, 0x07, 0xb0, 0x4d, 0x00, 0x09, + 0xb0, 0x45, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x42, + 0x00, 0x09, 0xb0, 0xdc, 0x00, 0x09, 0x00, 0x07, + 0xb0, 0xd1, 0x01, 0x09, 0x83, 0x00, 0x07, 0xb0, + 0x6b, 0x00, 0x09, 0xb0, 0x22, 0x00, 0x09, 0x91, + 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x74, + 0x00, 0x09, 0xb0, 0xd1, 0x00, 0x07, 0x80, 0x01, + 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x78, 0x01, + 0x09, 0xb8, 0x39, 0xbb, 0x00, 0x09, 0xb8, 0x01, + 0x8f, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb4, 0x88, + 0x01, 0x06, 0xb8, 0x44, 0x7b, 0x00, 0x01, 0xb8, + 0x0c, 0x95, 0x01, 0xd8, 0x02, 0x01, 0x82, 0x00, + 0xe2, 0x04, 0xd8, 0x87, 0x07, 0xdc, 0x81, 0xc4, + 0x01, 0xdc, 0x9d, 0xc3, 0xb0, 0x63, 0xc2, 0xb8, + 0x05, 0x8a, 0xc6, 0x80, 0xd0, 0x81, 0xc6, 0x80, + 0xc1, 0x80, 0xc4, 0xb0, 0x33, 0xc0, 0xb0, 0x6f, + 0xc6, 0xb1, 0x46, 0xc0, 0xb0, 0x0c, 0xc3, 0xb1, + 0xcb, 0x01, 0xe8, 0x00, 0xdc, 0xc0, 0xb0, 0xcd, + 0xc0, 0x00, 0xdc, 0xb2, 0xaf, 0x06, 0xdc, 0xb0, + 0x3c, 0xc5, 0x00, 0x07, }; static const uint8_t unicode_cc_index[87] = { - 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, - 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, - 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, - 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, - 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3, - 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, - 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, - 0x00, 0x39, 0x0a, 0x01, 0x51, 0x0f, 0x01, 0x73, - 0x11, 0x01, 0x75, 0x13, 0x01, 0x2b, 0x17, 0x21, - 0x3f, 0x1c, 0x21, 0x9e, 0xbc, 0x21, 0x08, 0xe0, - 0x01, 0x44, 0xe9, 0x01, 0x4b, 0xe9, 0x01, + 0x4d, 0x03, 0x00, // 0034D at 32 + 0x97, 0x05, 0x20, // 00597 at 65 + 0xc6, 0x05, 0x00, // 005C6 at 96 + 0xe7, 0x06, 0x00, // 006E7 at 128 + 0x45, 0x07, 0x00, // 00745 at 160 + 0x9c, 0x08, 0x00, // 0089C at 192 + 0x4d, 0x09, 0x00, // 0094D at 224 + 0x3c, 0x0b, 0x00, // 00B3C at 256 + 0x3d, 0x0d, 0x00, // 00D3D at 288 + 0x36, 0x0f, 0x00, // 00F36 at 320 + 0x38, 0x10, 0x20, // 01038 at 353 + 0x3a, 0x19, 0x00, // 0193A at 384 + 0xcb, 0x1a, 0x20, // 01ACB at 417 + 0xd3, 0x1c, 0x00, // 01CD3 at 448 + 0xcf, 0x1d, 0x00, // 01DCF at 480 + 0xe2, 0x20, 0x00, // 020E2 at 512 + 0x2e, 0x30, 0x20, // 0302E at 545 + 0x2b, 0xa9, 0x20, // 0A92B at 577 + 0xed, 0xab, 0x00, // 0ABED at 608 + 0x39, 0x0a, 0x01, // 10A39 at 640 + 0x4c, 0x0f, 0x01, // 10F4C at 672 + 0x35, 0x11, 0x21, // 11135 at 705 + 0x66, 0x13, 0x01, // 11366 at 736 + 0x40, 0x16, 0x01, // 11640 at 768 + 0x47, 0x1a, 0x01, // 11A47 at 800 + 0xf0, 0x6a, 0x21, // 16AF0 at 833 + 0x8a, 0xd1, 0x01, // 1D18A at 864 + 0xec, 0xe4, 0x21, // 1E4EC at 897 + 0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound) }; -static const uint32_t unicode_decomp_table1[699] = { +static const uint32_t unicode_decomp_table1[709] = { 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, @@ -832,42 +920,45 @@ static const uint32_t unicode_decomp_table1[699] = { 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f, - 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41e04d83, - 0x41e70f91, 0x44268192, 0x442ac092, 0x444b8112, - 0x44d2c112, 0x452ec212, 0x456e8112, 0x464e0092, - 0x74578392, 0x746ec312, 0x75000d1f, 0x75068d1f, - 0x750d0d1f, 0x7513839f, 0x7515891f, 0x751a0d1f, - 0x75208d1f, 0x75271015, 0x752f439f, 0x7531459f, - 0x75340d1f, 0x753a8d1f, 0x75410395, 0x7543441f, - 0x7545839f, 0x75478d1f, 0x754e0795, 0x7552839f, - 0x75548d1f, 0x755b0d1f, 0x75618d1f, 0x75680d1f, - 0x756e8d1f, 0x75750d1f, 0x757b8d1f, 0x75820d1f, - 0x75888d1f, 0x758f0d1f, 0x75958d1f, 0x759c0d1f, - 0x75a28d1f, 0x75a90103, 0x75aa089f, 0x75ae4081, - 0x75ae839f, 0x75b04081, 0x75b08c9f, 0x75b6c081, - 0x75b7032d, 0x75b8889f, 0x75bcc081, 0x75bd039f, - 0x75bec081, 0x75bf0c9f, 0x75c54081, 0x75c5832d, - 0x75c7089f, 0x75cb4081, 0x75cb839f, 0x75cd4081, - 0x75cd8c9f, 0x75d3c081, 0x75d4032d, 0x75d5889f, - 0x75d9c081, 0x75da039f, 0x75dbc081, 0x75dc0c9f, - 0x75e24081, 0x75e2832d, 0x75e4089f, 0x75e84081, - 0x75e8839f, 0x75ea4081, 0x75ea8c9f, 0x75f0c081, - 0x75f1042d, 0x75f3851f, 0x75f6051f, 0x75f8851f, - 0x75fb051f, 0x75fd851f, 0x780c049f, 0x780e419f, - 0x780f059f, 0x7811c203, 0x7812d0ad, 0x781b0103, - 0x7b80022d, 0x7b814dad, 0x7b884203, 0x7b89c081, - 0x7b8a452d, 0x7b8d0403, 0x7b908081, 0x7b91dc03, - 0x7ba0052d, 0x7ba2c8ad, 0x7ba84483, 0x7baac8ad, - 0x7c400097, 0x7c404521, 0x7c440d25, 0x7c4a8087, - 0x7c4ac115, 0x7c4b4117, 0x7c4c0d1f, 0x7c528217, - 0x7c538099, 0x7c53c097, 0x7c5a8197, 0x7c640097, - 0x7c80012f, 0x7c808081, 0x7c841603, 0x7c9004c1, - 0x7c940103, 0x7efc051f, 0xbe0001ac, 0xbe00d110, - 0xbe0947ac, 0xbe0d3910, 0xbe29872c, 0xbe2d022c, - 0xbe2e3790, 0xbe49ff90, 0xbe69bc10, + 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41724092, + 0x41790092, 0x41e04d83, 0x41e70f91, 0x44268192, + 0x442ac092, 0x444b8112, 0x44d2c112, 0x44e0c192, + 0x44e38092, 0x44e44092, 0x44f14212, 0x452ec212, + 0x456e8112, 0x464e0092, 0x58484412, 0x5b5a0192, + 0x73358d1f, 0x733c051f, 0x74578392, 0x746ec312, + 0x75000d1f, 0x75068d1f, 0x750d0d1f, 0x7513839f, + 0x7515891f, 0x751a0d1f, 0x75208d1f, 0x75271015, + 0x752f439f, 0x7531459f, 0x75340d1f, 0x753a8d1f, + 0x75410395, 0x7543441f, 0x7545839f, 0x75478d1f, + 0x754e0795, 0x7552839f, 0x75548d1f, 0x755b0d1f, + 0x75618d1f, 0x75680d1f, 0x756e8d1f, 0x75750d1f, + 0x757b8d1f, 0x75820d1f, 0x75888d1f, 0x758f0d1f, + 0x75958d1f, 0x759c0d1f, 0x75a28d1f, 0x75a90103, + 0x75aa089f, 0x75ae4081, 0x75ae839f, 0x75b04081, + 0x75b08c9f, 0x75b6c081, 0x75b7032d, 0x75b8889f, + 0x75bcc081, 0x75bd039f, 0x75bec081, 0x75bf0c9f, + 0x75c54081, 0x75c5832d, 0x75c7089f, 0x75cb4081, + 0x75cb839f, 0x75cd4081, 0x75cd8c9f, 0x75d3c081, + 0x75d4032d, 0x75d5889f, 0x75d9c081, 0x75da039f, + 0x75dbc081, 0x75dc0c9f, 0x75e24081, 0x75e2832d, + 0x75e4089f, 0x75e84081, 0x75e8839f, 0x75ea4081, + 0x75ea8c9f, 0x75f0c081, 0x75f1042d, 0x75f3851f, + 0x75f6051f, 0x75f8851f, 0x75fb051f, 0x75fd851f, + 0x780c049f, 0x780e419f, 0x780f059f, 0x7811c203, + 0x7812d0ad, 0x781b0103, 0x7b80022d, 0x7b814dad, + 0x7b884203, 0x7b89c081, 0x7b8a452d, 0x7b8d0403, + 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad, + 0x7ba84483, 0x7baac8ad, 0x7c400097, 0x7c404521, + 0x7c440d25, 0x7c4a8087, 0x7c4ac115, 0x7c4b4117, + 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097, + 0x7c5a8197, 0x7c640097, 0x7c80012f, 0x7c808081, + 0x7c841603, 0x7c9004c1, 0x7c940103, 0x7efc051f, + 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910, + 0xbe29872c, 0xbe2d022c, 0xbe2e3790, 0xbe49ff90, + 0xbe69bc10, }; -static const uint16_t unicode_decomp_table2[699] = { +static const uint16_t unicode_decomp_table2[709] = { 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, @@ -939,26 +1030,27 @@ static const uint16_t unicode_decomp_table2[699] = { 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada, 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52, 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24, - 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c7e, 0x1cc4, 0x1cd2, 0x1cd7, - 0x1ce0, 0x1ce9, 0x1cfb, 0x1d04, 0x1d09, 0x1d29, 0x1d44, 0x1d46, - 0x1d48, 0x1d4a, 0x1d4c, 0x1d4e, 0x1d50, 0x1d52, 0x1d72, 0x1d74, - 0x1d76, 0x1d78, 0x1d7a, 0x1d81, 0x1d83, 0x1d85, 0x1d87, 0x1d96, - 0x1d98, 0x1d9a, 0x1d9c, 0x1d9e, 0x1da0, 0x1da2, 0x1da4, 0x1da6, - 0x1da8, 0x1daa, 0x1dac, 0x1dae, 0x1db0, 0x1db2, 0x1db6, 0x03f4, - 0x1db8, 0x2207, 0x1dba, 0x2202, 0x1dbc, 0x1dc4, 0x03f4, 0x1dc6, - 0x2207, 0x1dc8, 0x2202, 0x1dca, 0x1dd2, 0x03f4, 0x1dd4, 0x2207, - 0x1dd6, 0x2202, 0x1dd8, 0x1de0, 0x03f4, 0x1de2, 0x2207, 0x1de4, - 0x2202, 0x1de6, 0x1dee, 0x03f4, 0x1df0, 0x2207, 0x1df2, 0x2202, - 0x1df4, 0x1dfe, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, - 0x1e0c, 0x1e0e, 0x1e16, 0x1e39, 0x1e3d, 0x1e43, 0x1e60, 0x062d, - 0x1e68, 0x1e74, 0x062c, 0x1e84, 0x1ef4, 0x1f00, 0x1f13, 0x1f25, - 0x1f38, 0x1f3a, 0x1f3e, 0x1f44, 0x1f4a, 0x1f4c, 0x1f50, 0x1f52, - 0x1f5a, 0x1f5d, 0x1f5f, 0x1f65, 0x1f67, 0x30b5, 0x1f6d, 0x1fc5, - 0x1fdb, 0x1fdf, 0x1fe1, 0x1fe6, 0x2033, 0x2044, 0x2145, 0x2155, - 0x215b, 0x2255, 0x2373, + 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c4d, 0x1c52, 0x1c88, 0x1cce, + 0x1cdc, 0x1ce1, 0x1cea, 0x1cf3, 0x1d01, 0x1d06, 0x1d0b, 0x1d1d, + 0x1d2f, 0x1d38, 0x1d3d, 0x1d61, 0x1d6f, 0x1d71, 0x1d73, 0x1d93, + 0x1dae, 0x1db0, 0x1db2, 0x1db4, 0x1db6, 0x1db8, 0x1dba, 0x1dbc, + 0x1ddc, 0x1dde, 0x1de0, 0x1de2, 0x1de4, 0x1deb, 0x1ded, 0x1def, + 0x1df1, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, + 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, + 0x1e20, 0x03f4, 0x1e22, 0x2207, 0x1e24, 0x2202, 0x1e26, 0x1e2e, + 0x03f4, 0x1e30, 0x2207, 0x1e32, 0x2202, 0x1e34, 0x1e3c, 0x03f4, + 0x1e3e, 0x2207, 0x1e40, 0x2202, 0x1e42, 0x1e4a, 0x03f4, 0x1e4c, + 0x2207, 0x1e4e, 0x2202, 0x1e50, 0x1e58, 0x03f4, 0x1e5a, 0x2207, + 0x1e5c, 0x2202, 0x1e5e, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, + 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e80, 0x1ea3, 0x1ea7, 0x1ead, + 0x1eca, 0x062d, 0x1ed2, 0x1ede, 0x062c, 0x1eee, 0x1f5e, 0x1f6a, + 0x1f7d, 0x1f8f, 0x1fa2, 0x1fa4, 0x1fa8, 0x1fae, 0x1fb4, 0x1fb6, + 0x1fba, 0x1fbc, 0x1fc4, 0x1fc7, 0x1fc9, 0x1fcf, 0x1fd1, 0x30b5, + 0x1fd7, 0x202f, 0x2045, 0x2049, 0x204b, 0x2050, 0x209d, 0x20ae, + 0x21af, 0x21bf, 0x21c5, 0x22bf, 0x23dd, }; -static const uint8_t unicode_decomp_data[9345] = { +static const uint8_t unicode_decomp_data[9451] = { 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, @@ -1864,273 +1956,286 @@ static const uint8_t unicode_decomp_data[9345] = { 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20, 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, 0x21, 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, 0x25, - 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, 0x99, 0x02, - 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, 0x66, 0xab, - 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, 0x57, 0x02, - 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, 0xa9, 0x02, - 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, 0x9b, 0x02, - 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, 0x84, 0x02, - 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, 0x04, 0xdf, - 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, 0x8e, 0x02, - 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, 0x77, 0x02, - 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, 0x7d, 0x02, - 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, 0xa6, 0x02, - 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, 0x71, 0x2c, - 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, 0xa2, 0x02, - 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, - 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, 0xba, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, 0x10, - 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, 0x31, - 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, 0x11, 0x55, - 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, 0x13, - 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, 0xb0, - 0x14, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x14, 0xbd, - 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, 0xb9, - 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, 0x19, - 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, 0x65, - 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, 0x6f, - 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, 0x71, - 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, 0x55, - 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, 0x65, - 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, 0x6e, - 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, 0x6f, - 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, 0x00, - 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, 0x4e, - 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, - 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, 0x00, - 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, - 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, 0x41, - 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, 0x49, - 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, 0x53, 0x00, - 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, - 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, - 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, - 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, 0x03, + 0xd2, 0x05, 0x07, 0x03, 0x01, 0xda, 0x05, 0x07, + 0x03, 0x01, 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, + 0x99, 0x02, 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, + 0x66, 0xab, 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, + 0x57, 0x02, 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, + 0xa9, 0x02, 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, + 0x9b, 0x02, 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, + 0x84, 0x02, 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, + 0x04, 0xdf, 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, + 0x8e, 0x02, 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, + 0x77, 0x02, 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, + 0x7d, 0x02, 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, + 0xa6, 0x02, 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, + 0x71, 0x2c, 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, + 0xa2, 0x02, 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, + 0xc2, 0x01, 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, + 0xba, 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, + 0xba, 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, + 0x05, 0x31, 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, + 0x11, 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, + 0x57, 0x13, 0x55, 0x82, 0x13, 0xc9, 0x13, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x13, 0xbb, 0x13, 0x05, + 0x05, 0x8b, 0x13, 0xc2, 0x13, 0x05, 0x90, 0x13, + 0xc9, 0x13, 0x05, 0xc2, 0x13, 0xc2, 0x13, 0x00, + 0x00, 0x00, 0x00, 0xc2, 0x13, 0xb8, 0x13, 0xc2, + 0x13, 0xc9, 0x13, 0x05, 0x55, 0xb9, 0x14, 0xba, + 0x14, 0xb9, 0x14, 0xb0, 0x14, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x14, 0xbd, 0x14, 0x55, 0x50, 0xb8, + 0x15, 0xaf, 0x15, 0xb9, 0x15, 0xaf, 0x15, 0x55, + 0x35, 0x19, 0x30, 0x19, 0x05, 0x1e, 0x61, 0x1e, + 0x61, 0x1e, 0x61, 0x29, 0x61, 0x1e, 0x61, 0x1f, + 0x61, 0x29, 0x61, 0x1f, 0x61, 0x1e, 0x61, 0x20, + 0x61, 0x21, 0x61, 0x1f, 0x61, 0x22, 0x61, 0x1f, + 0x61, 0x21, 0x61, 0x20, 0x61, 0x55, 0x55, 0x55, + 0x55, 0x67, 0x6d, 0x67, 0x6d, 0x63, 0x6d, 0x67, + 0x6d, 0x69, 0x6d, 0x67, 0x6d, 0x55, 0x05, 0x41, + 0x00, 0x30, 0x00, 0x57, 0xd1, 0x65, 0xd1, 0x58, + 0xd1, 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, + 0xd1, 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, + 0xd1, 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, + 0x55, 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, + 0xd1, 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, + 0xd1, 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, + 0xd1, 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, + 0x00, 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, + 0x00, 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, + 0x63, 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, + 0x45, 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, + 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, + 0x00, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, + 0x53, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, - 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, - 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, - 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, 0x00, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, - 0x30, 0x04, 0x3a, 0x04, 0x3e, 0x04, 0x4b, 0x04, - 0x4d, 0x04, 0x4e, 0x04, 0x89, 0xa6, 0x30, 0x04, - 0xa9, 0x26, 0x28, 0xb9, 0x7f, 0x9f, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0a, - 0x0b, 0x0e, 0x0f, 0x11, 0x13, 0x14, 0x15, 0x16, - 0x17, 0x18, 0x1a, 0x1b, 0x61, 0x26, 0x25, 0x2f, - 0x7b, 0x51, 0xa6, 0xb1, 0x04, 0x27, 0x06, 0x00, - 0x01, 0x05, 0x08, 0x2a, 0x06, 0x1e, 0x08, 0x03, - 0x0d, 0x20, 0x19, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, - 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, - 0x06, 0x0c, 0x0e, 0x10, 0x44, 0x90, 0x77, 0x45, - 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, 0x47, 0x06, - 0x33, 0x06, 0x17, 0x10, 0x11, 0x12, 0x13, 0x00, - 0x06, 0x0e, 0x02, 0x0f, 0x34, 0x06, 0x2a, 0x06, - 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, - 0x00, 0x00, 0x3a, 0x06, 0x2d, 0x06, 0x00, 0x00, - 0x4a, 0x06, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, - 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, 0x00, 0x00, - 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, 0x34, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x2e, 0x06, 0x00, 0x00, - 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x00, 0x00, - 0xba, 0x06, 0x00, 0x00, 0x6f, 0x06, 0x00, 0x00, - 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, 0x47, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x2d, 0x06, 0x37, 0x06, - 0x4a, 0x06, 0x43, 0x06, 0x00, 0x00, 0x45, 0x06, - 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, 0x41, 0x06, - 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, 0x34, 0x06, + 0x30, 0x00, 0x30, 0x04, 0x3a, 0x04, 0x3e, 0x04, + 0x4b, 0x04, 0x4d, 0x04, 0x4e, 0x04, 0x89, 0xa6, + 0x30, 0x04, 0xa9, 0x26, 0x28, 0xb9, 0x7f, 0x9f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x0a, 0x0b, 0x0e, 0x0f, 0x11, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x61, 0x26, + 0x25, 0x2f, 0x7b, 0x51, 0xa6, 0xb1, 0x04, 0x27, + 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, 0x06, 0x1e, + 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b, 0x1c, + 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, + 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x44, 0x90, + 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, + 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11, 0x12, + 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, - 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x6e, 0x06, - 0x00, 0x00, 0xa1, 0x06, 0x27, 0x06, 0x00, 0x01, - 0x05, 0x08, 0x20, 0x21, 0x0b, 0x06, 0x10, 0x23, - 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, - 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, - 0x0c, 0x0e, 0x10, 0x28, 0x06, 0x2c, 0x06, 0x2f, - 0x06, 0x00, 0x00, 0x48, 0x06, 0x32, 0x06, 0x2d, - 0x06, 0x37, 0x06, 0x4a, 0x06, 0x2a, 0x06, 0x1a, - 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, - 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, - 0x30, 0x2e, 0x30, 0x00, 0x2c, 0x00, 0x28, 0x00, - 0x41, 0x00, 0x29, 0x00, 0x14, 0x30, 0x53, 0x00, - 0x15, 0x30, 0x43, 0x52, 0x43, 0x44, 0x57, 0x5a, - 0x41, 0x00, 0x48, 0x56, 0x4d, 0x56, 0x53, 0x44, - 0x53, 0x53, 0x50, 0x50, 0x56, 0x57, 0x43, 0x4d, - 0x43, 0x4d, 0x44, 0x4d, 0x52, 0x44, 0x4a, 0x4b, - 0x30, 0x30, 0x00, 0x68, 0x68, 0x4b, 0x62, 0x57, - 0x5b, 0xcc, 0x53, 0xc7, 0x30, 0x8c, 0x4e, 0x1a, - 0x59, 0xe3, 0x89, 0x29, 0x59, 0xa4, 0x4e, 0x20, - 0x66, 0x21, 0x71, 0x99, 0x65, 0x4d, 0x52, 0x8c, - 0x5f, 0x8d, 0x51, 0xb0, 0x65, 0x1d, 0x52, 0x42, - 0x7d, 0x1f, 0x75, 0xa9, 0x8c, 0xf0, 0x58, 0x39, - 0x54, 0x14, 0x6f, 0x95, 0x62, 0x55, 0x63, 0x00, - 0x4e, 0x09, 0x4e, 0x4a, 0x90, 0xe6, 0x5d, 0x2d, - 0x4e, 0xf3, 0x53, 0x07, 0x63, 0x70, 0x8d, 0x53, - 0x62, 0x81, 0x79, 0x7a, 0x7a, 0x08, 0x54, 0x80, - 0x6e, 0x09, 0x67, 0x08, 0x67, 0x33, 0x75, 0x72, - 0x52, 0xb6, 0x55, 0x4d, 0x91, 0x14, 0x30, 0x15, - 0x30, 0x2c, 0x67, 0x09, 0x4e, 0x8c, 0x4e, 0x89, - 0x5b, 0xb9, 0x70, 0x53, 0x62, 0xd7, 0x76, 0xdd, - 0x52, 0x57, 0x65, 0x97, 0x5f, 0xef, 0x53, 0x30, - 0x00, 0x38, 0x4e, 0x05, 0x00, 0x09, 0x22, 0x01, - 0x60, 0x4f, 0xae, 0x4f, 0xbb, 0x4f, 0x02, 0x50, - 0x7a, 0x50, 0x99, 0x50, 0xe7, 0x50, 0xcf, 0x50, - 0x9e, 0x34, 0x3a, 0x06, 0x4d, 0x51, 0x54, 0x51, - 0x64, 0x51, 0x77, 0x51, 0x1c, 0x05, 0xb9, 0x34, - 0x67, 0x51, 0x8d, 0x51, 0x4b, 0x05, 0x97, 0x51, - 0xa4, 0x51, 0xcc, 0x4e, 0xac, 0x51, 0xb5, 0x51, - 0xdf, 0x91, 0xf5, 0x51, 0x03, 0x52, 0xdf, 0x34, - 0x3b, 0x52, 0x46, 0x52, 0x72, 0x52, 0x77, 0x52, - 0x15, 0x35, 0x02, 0x00, 0x20, 0x80, 0x80, 0x00, - 0x08, 0x00, 0x00, 0xc7, 0x52, 0x00, 0x02, 0x1d, - 0x33, 0x3e, 0x3f, 0x50, 0x82, 0x8a, 0x93, 0xac, - 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, 0x0a, 0x70, 0x70, - 0xca, 0x53, 0xdf, 0x53, 0x63, 0x0b, 0xeb, 0x53, - 0xf1, 0x53, 0x06, 0x54, 0x9e, 0x54, 0x38, 0x54, - 0x48, 0x54, 0x68, 0x54, 0xa2, 0x54, 0xf6, 0x54, - 0x10, 0x55, 0x53, 0x55, 0x63, 0x55, 0x84, 0x55, - 0x84, 0x55, 0x99, 0x55, 0xab, 0x55, 0xb3, 0x55, - 0xc2, 0x55, 0x16, 0x57, 0x06, 0x56, 0x17, 0x57, - 0x51, 0x56, 0x74, 0x56, 0x07, 0x52, 0xee, 0x58, - 0xce, 0x57, 0xf4, 0x57, 0x0d, 0x58, 0x8b, 0x57, - 0x32, 0x58, 0x31, 0x58, 0xac, 0x58, 0xe4, 0x14, - 0xf2, 0x58, 0xf7, 0x58, 0x06, 0x59, 0x1a, 0x59, - 0x22, 0x59, 0x62, 0x59, 0xa8, 0x16, 0xea, 0x16, - 0xec, 0x59, 0x1b, 0x5a, 0x27, 0x5a, 0xd8, 0x59, - 0x66, 0x5a, 0xee, 0x36, 0xfc, 0x36, 0x08, 0x5b, - 0x3e, 0x5b, 0x3e, 0x5b, 0xc8, 0x19, 0xc3, 0x5b, - 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, 0x5b, 0x18, 0x1b, - 0xff, 0x5b, 0x06, 0x5c, 0x53, 0x5f, 0x22, 0x5c, - 0x81, 0x37, 0x60, 0x5c, 0x6e, 0x5c, 0xc0, 0x5c, - 0x8d, 0x5c, 0xe4, 0x1d, 0x43, 0x5d, 0xe6, 0x1d, - 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, 0x5d, 0xe1, 0x5d, - 0xe2, 0x5d, 0x2f, 0x38, 0xfd, 0x5d, 0x28, 0x5e, - 0x3d, 0x5e, 0x69, 0x5e, 0x62, 0x38, 0x83, 0x21, - 0x7c, 0x38, 0xb0, 0x5e, 0xb3, 0x5e, 0xb6, 0x5e, - 0xca, 0x5e, 0x92, 0xa3, 0xfe, 0x5e, 0x31, 0x23, - 0x31, 0x23, 0x01, 0x82, 0x22, 0x5f, 0x22, 0x5f, - 0xc7, 0x38, 0xb8, 0x32, 0xda, 0x61, 0x62, 0x5f, - 0x6b, 0x5f, 0xe3, 0x38, 0x9a, 0x5f, 0xcd, 0x5f, - 0xd7, 0x5f, 0xf9, 0x5f, 0x81, 0x60, 0x3a, 0x39, - 0x1c, 0x39, 0x94, 0x60, 0xd4, 0x26, 0xc7, 0x60, - 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x02, 0x08, - 0x00, 0x80, 0x08, 0x00, 0x00, 0x08, 0x80, 0x28, - 0x80, 0x02, 0x00, 0x00, 0x02, 0x48, 0x61, 0x00, - 0x04, 0x06, 0x04, 0x32, 0x46, 0x6a, 0x5c, 0x67, - 0x96, 0xaa, 0xae, 0xc8, 0xd3, 0x5d, 0x62, 0x00, - 0x54, 0x77, 0xf3, 0x0c, 0x2b, 0x3d, 0x63, 0xfc, - 0x62, 0x68, 0x63, 0x83, 0x63, 0xe4, 0x63, 0xf1, - 0x2b, 0x22, 0x64, 0xc5, 0x63, 0xa9, 0x63, 0x2e, - 0x3a, 0x69, 0x64, 0x7e, 0x64, 0x9d, 0x64, 0x77, - 0x64, 0x6c, 0x3a, 0x4f, 0x65, 0x6c, 0x65, 0x0a, - 0x30, 0xe3, 0x65, 0xf8, 0x66, 0x49, 0x66, 0x19, - 0x3b, 0x91, 0x66, 0x08, 0x3b, 0xe4, 0x3a, 0x92, - 0x51, 0x95, 0x51, 0x00, 0x67, 0x9c, 0x66, 0xad, - 0x80, 0xd9, 0x43, 0x17, 0x67, 0x1b, 0x67, 0x21, - 0x67, 0x5e, 0x67, 0x53, 0x67, 0xc3, 0x33, 0x49, - 0x3b, 0xfa, 0x67, 0x85, 0x67, 0x52, 0x68, 0x85, - 0x68, 0x6d, 0x34, 0x8e, 0x68, 0x1f, 0x68, 0x14, - 0x69, 0x9d, 0x3b, 0x42, 0x69, 0xa3, 0x69, 0xea, - 0x69, 0xa8, 0x6a, 0xa3, 0x36, 0xdb, 0x6a, 0x18, - 0x3c, 0x21, 0x6b, 0xa7, 0x38, 0x54, 0x6b, 0x4e, - 0x3c, 0x72, 0x6b, 0x9f, 0x6b, 0xba, 0x6b, 0xbb, - 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, 0xfa, 0x3a, 0x4e, - 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, 0xcd, 0x6c, 0x67, - 0x6c, 0x16, 0x6d, 0x3e, 0x6d, 0x77, 0x6d, 0x41, - 0x6d, 0x69, 0x6d, 0x78, 0x6d, 0x85, 0x6d, 0x1e, - 0x3d, 0x34, 0x6d, 0x2f, 0x6e, 0x6e, 0x6e, 0x33, - 0x3d, 0xcb, 0x6e, 0xc7, 0x6e, 0xd1, 0x3e, 0xf9, - 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, 0x8e, 0x3f, 0xc6, - 0x6f, 0x39, 0x70, 0x1e, 0x70, 0x1b, 0x70, 0x96, - 0x3d, 0x4a, 0x70, 0x7d, 0x70, 0x77, 0x70, 0xad, - 0x70, 0x25, 0x05, 0x45, 0x71, 0x63, 0x42, 0x9c, - 0x71, 0xab, 0x43, 0x28, 0x72, 0x35, 0x72, 0x50, - 0x72, 0x08, 0x46, 0x80, 0x72, 0x95, 0x72, 0x35, - 0x47, 0x02, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x02, 0x02, - 0x80, 0x8a, 0x00, 0x00, 0x20, 0x00, 0x08, 0x0a, - 0x00, 0x80, 0x88, 0x80, 0x20, 0x14, 0x48, 0x7a, - 0x73, 0x8b, 0x73, 0xac, 0x3e, 0xa5, 0x73, 0xb8, - 0x3e, 0xb8, 0x3e, 0x47, 0x74, 0x5c, 0x74, 0x71, - 0x74, 0x85, 0x74, 0xca, 0x74, 0x1b, 0x3f, 0x24, - 0x75, 0x36, 0x4c, 0x3e, 0x75, 0x92, 0x4c, 0x70, - 0x75, 0x9f, 0x21, 0x10, 0x76, 0xa1, 0x4f, 0xb8, - 0x4f, 0x44, 0x50, 0xfc, 0x3f, 0x08, 0x40, 0xf4, - 0x76, 0xf3, 0x50, 0xf2, 0x50, 0x19, 0x51, 0x33, - 0x51, 0x1e, 0x77, 0x1f, 0x77, 0x1f, 0x77, 0x4a, - 0x77, 0x39, 0x40, 0x8b, 0x77, 0x46, 0x40, 0x96, - 0x40, 0x1d, 0x54, 0x4e, 0x78, 0x8c, 0x78, 0xcc, - 0x78, 0xe3, 0x40, 0x26, 0x56, 0x56, 0x79, 0x9a, - 0x56, 0xc5, 0x56, 0x8f, 0x79, 0xeb, 0x79, 0x2f, - 0x41, 0x40, 0x7a, 0x4a, 0x7a, 0x4f, 0x7a, 0x7c, - 0x59, 0xa7, 0x5a, 0xa7, 0x5a, 0xee, 0x7a, 0x02, - 0x42, 0xab, 0x5b, 0xc6, 0x7b, 0xc9, 0x7b, 0x27, - 0x42, 0x80, 0x5c, 0xd2, 0x7c, 0xa0, 0x42, 0xe8, - 0x7c, 0xe3, 0x7c, 0x00, 0x7d, 0x86, 0x5f, 0x63, - 0x7d, 0x01, 0x43, 0xc7, 0x7d, 0x02, 0x7e, 0x45, - 0x7e, 0x34, 0x43, 0x28, 0x62, 0x47, 0x62, 0x59, - 0x43, 0xd9, 0x62, 0x7a, 0x7f, 0x3e, 0x63, 0x95, - 0x7f, 0xfa, 0x7f, 0x05, 0x80, 0xda, 0x64, 0x23, - 0x65, 0x60, 0x80, 0xa8, 0x65, 0x70, 0x80, 0x5f, - 0x33, 0xd5, 0x43, 0xb2, 0x80, 0x03, 0x81, 0x0b, - 0x44, 0x3e, 0x81, 0xb5, 0x5a, 0xa7, 0x67, 0xb5, - 0x67, 0x93, 0x33, 0x9c, 0x33, 0x01, 0x82, 0x04, - 0x82, 0x9e, 0x8f, 0x6b, 0x44, 0x91, 0x82, 0x8b, - 0x82, 0x9d, 0x82, 0xb3, 0x52, 0xb1, 0x82, 0xb3, - 0x82, 0xbd, 0x82, 0xe6, 0x82, 0x3c, 0x6b, 0xe5, - 0x82, 0x1d, 0x83, 0x63, 0x83, 0xad, 0x83, 0x23, - 0x83, 0xbd, 0x83, 0xe7, 0x83, 0x57, 0x84, 0x53, - 0x83, 0xca, 0x83, 0xcc, 0x83, 0xdc, 0x83, 0x36, - 0x6c, 0x6b, 0x6d, 0x02, 0x00, 0x00, 0x20, 0x22, - 0x2a, 0xa0, 0x0a, 0x00, 0x20, 0x80, 0x28, 0x00, - 0xa8, 0x20, 0x20, 0x00, 0x02, 0x80, 0x22, 0x02, - 0x8a, 0x08, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x28, 0xd5, 0x6c, 0x2b, 0x45, 0xf1, - 0x84, 0xf3, 0x84, 0x16, 0x85, 0xca, 0x73, 0x64, - 0x85, 0x2c, 0x6f, 0x5d, 0x45, 0x61, 0x45, 0xb1, - 0x6f, 0xd2, 0x70, 0x6b, 0x45, 0x50, 0x86, 0x5c, - 0x86, 0x67, 0x86, 0x69, 0x86, 0xa9, 0x86, 0x88, - 0x86, 0x0e, 0x87, 0xe2, 0x86, 0x79, 0x87, 0x28, - 0x87, 0x6b, 0x87, 0x86, 0x87, 0xd7, 0x45, 0xe1, - 0x87, 0x01, 0x88, 0xf9, 0x45, 0x60, 0x88, 0x63, - 0x88, 0x67, 0x76, 0xd7, 0x88, 0xde, 0x88, 0x35, - 0x46, 0xfa, 0x88, 0xbb, 0x34, 0xae, 0x78, 0x66, - 0x79, 0xbe, 0x46, 0xc7, 0x46, 0xa0, 0x8a, 0xed, - 0x8a, 0x8a, 0x8b, 0x55, 0x8c, 0xa8, 0x7c, 0xab, - 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, 0x77, 0x8d, 0x2f, - 0x7f, 0x04, 0x08, 0xcb, 0x8d, 0xbc, 0x8d, 0xf0, - 0x8d, 0xde, 0x08, 0xd4, 0x8e, 0x38, 0x8f, 0xd2, - 0x85, 0xed, 0x85, 0x94, 0x90, 0xf1, 0x90, 0x11, - 0x91, 0x2e, 0x87, 0x1b, 0x91, 0x38, 0x92, 0xd7, - 0x92, 0xd8, 0x92, 0x7c, 0x92, 0xf9, 0x93, 0x15, - 0x94, 0xfa, 0x8b, 0x8b, 0x95, 0x95, 0x49, 0xb7, - 0x95, 0x77, 0x8d, 0xe6, 0x49, 0xc3, 0x96, 0xb2, - 0x5d, 0x23, 0x97, 0x45, 0x91, 0x1a, 0x92, 0x6e, - 0x4a, 0x76, 0x4a, 0xe0, 0x97, 0x0a, 0x94, 0xb2, - 0x4a, 0x96, 0x94, 0x0b, 0x98, 0x0b, 0x98, 0x29, - 0x98, 0xb6, 0x95, 0xe2, 0x98, 0x33, 0x4b, 0x29, - 0x99, 0xa7, 0x99, 0xc2, 0x99, 0xfe, 0x99, 0xce, - 0x4b, 0x30, 0x9b, 0x12, 0x9b, 0x40, 0x9c, 0xfd, - 0x9c, 0xce, 0x4c, 0xed, 0x4c, 0x67, 0x9d, 0xce, - 0xa0, 0xf8, 0x4c, 0x05, 0xa1, 0x0e, 0xa2, 0x91, - 0xa2, 0xbb, 0x9e, 0x56, 0x4d, 0xf9, 0x9e, 0xfe, - 0x9e, 0x05, 0x9f, 0x0f, 0x9f, 0x16, 0x9f, 0x3b, - 0x9f, 0x00, 0xa6, 0x02, 0x88, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x28, 0x00, 0x08, 0xa0, - 0x80, 0xa0, 0x80, 0x00, 0x80, 0x80, 0x00, 0x0a, - 0x88, 0x80, 0x00, 0x80, 0x00, 0x20, 0x2a, 0x00, - 0x80, + 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d, 0x06, + 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, 0x44, 0x06, + 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, + 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, + 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, + 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f, 0x06, + 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, + 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x06, + 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00, 0x00, + 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, + 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, + 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, + 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27, 0x06, + 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b, 0x06, + 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, + 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, + 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06, 0x2c, + 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, 0x06, 0x32, + 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, 0x2a, + 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, + 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, + 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c, 0x00, + 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14, 0x30, + 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, 0x43, 0x44, + 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d, 0x56, + 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56, 0x57, + 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, 0x52, 0x44, + 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68, 0x4b, + 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30, 0x8c, + 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, 0x59, 0xa4, + 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65, 0x4d, + 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65, 0x1d, + 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, 0x8c, 0xf0, + 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62, 0x55, + 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90, 0xe6, + 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, 0x63, 0x70, + 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a, 0x08, + 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67, 0x33, + 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, 0x91, 0x14, + 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e, 0x8c, + 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62, 0xd7, + 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, 0x5f, 0xef, + 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, 0x00, 0x09, + 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb, 0x4f, + 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, 0xe7, 0x50, + 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d, 0x51, + 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c, 0x05, + 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, 0x4b, 0x05, + 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac, 0x51, + 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03, 0x52, + 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, 0x72, 0x52, + 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20, 0x80, + 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52, 0x00, + 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, 0x82, 0x8a, + 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, 0x0a, + 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63, 0x0b, + 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, 0x9e, 0x54, + 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2, 0x54, + 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, 0x63, 0x55, + 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, 0xab, 0x55, + 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06, 0x56, + 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, 0x07, 0x52, + 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, 0x0d, 0x58, + 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac, 0x58, + 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06, 0x59, + 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, 0xa8, 0x16, + 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27, 0x5a, + 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc, 0x36, + 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, 0xc8, 0x19, + 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, 0x5b, + 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53, 0x5f, + 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, 0x6e, 0x5c, + 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43, 0x5d, + 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, 0x5d, + 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, 0xfd, 0x5d, + 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62, 0x38, + 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3, 0x5e, + 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, 0xfe, 0x5e, + 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22, 0x5f, + 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda, 0x61, + 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, 0x9a, 0x5f, + 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81, 0x60, + 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4, 0x26, + 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, + 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x08, + 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, 0x02, 0x48, + 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46, 0x6a, + 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3, 0x5d, + 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, 0x2b, 0x3d, + 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63, 0xe4, + 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63, 0xa9, + 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, 0x64, 0x9d, + 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65, 0x6c, + 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66, 0x49, + 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, 0x3b, 0xe4, + 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67, 0x9c, + 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67, 0x1b, + 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, 0x67, 0xc3, + 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67, 0x52, + 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68, 0x1f, + 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, 0x69, 0xa3, + 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36, 0xdb, + 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38, 0x54, + 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, 0x6b, 0xba, + 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, 0xfa, + 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, 0xcd, + 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, 0x6d, 0x77, + 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d, 0x85, + 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e, 0x6e, + 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, 0x6e, 0xd1, + 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, 0x8e, + 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70, 0x1b, + 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, 0x70, 0x77, + 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71, 0x63, + 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72, 0x35, + 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, 0x72, 0x95, + 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, + 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, 0x20, 0x00, + 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20, 0x14, + 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e, 0xa5, + 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, 0x74, 0x5c, + 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74, 0x1b, + 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75, 0x92, + 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, 0x76, 0xa1, + 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f, 0x08, + 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50, 0x19, + 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, 0x77, 0x1f, + 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77, 0x46, + 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78, 0x8c, + 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, 0x56, 0x56, + 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79, 0xeb, + 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a, 0x4f, + 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, 0x5a, 0xee, + 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b, 0xc9, + 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c, 0xa0, + 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, 0x7d, 0x86, + 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d, 0x02, + 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62, 0x47, + 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, 0x7f, 0x3e, + 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80, 0xda, + 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65, 0x70, + 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, 0x80, 0x03, + 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a, 0xa7, + 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33, 0x01, + 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, 0x44, 0x91, + 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52, 0xb1, + 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82, 0x3c, + 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, 0x83, 0xad, + 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83, 0x57, + 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83, 0xdc, + 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, 0x00, 0x00, + 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20, 0x80, + 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02, 0x80, + 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c, 0x2b, + 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85, 0xca, + 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, 0x45, 0x61, + 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45, 0x50, + 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86, 0xa9, + 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, 0x86, 0x79, + 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87, 0xd7, + 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45, 0x60, + 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, 0x88, 0xde, + 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34, 0xae, + 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46, 0xa0, + 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, 0x8c, 0xa8, + 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, 0x77, + 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d, 0xbc, + 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, 0x8e, 0x38, + 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90, 0xf1, + 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91, 0x38, + 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, 0x92, 0xf9, + 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95, 0x95, + 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49, 0xc3, + 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, 0x91, 0x1a, + 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97, 0x0a, + 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98, 0x0b, + 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, 0x98, 0x33, + 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99, 0xfe, + 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b, 0x40, + 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, 0x4c, 0x67, + 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1, 0x0e, + 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d, 0xf9, + 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, 0x9f, 0x16, + 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x28, 0x00, + 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, 0x80, 0x80, + 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00, 0x20, + 0x2a, 0x00, 0x80, }; -static const uint16_t unicode_comp_table[945] = { +static const uint16_t unicode_comp_table[965] = { 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, @@ -2247,9 +2352,11 @@ static const uint16_t unicode_comp_table[945] = { 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, - 0x5901, 0x5902, 0x5903, 0x5940, 0x8f40, 0x8f42, 0x8f80, 0x8fc0, - 0x8fc1, 0x9000, 0x9001, 0x9041, 0x9040, 0x9043, 0x9080, 0x9081, - 0x90c0, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8ec0, 0x8f00, 0x8fc0, 0x8fc2, + 0x9000, 0x9040, 0x9041, 0x9080, 0x9081, 0x90c0, 0x90c2, 0x9100, + 0x9140, 0x9182, 0x9180, 0x9183, 0x91c1, 0x91c0, 0x91c3, 0x9200, + 0x9201, 0x9240, 0x9280, 0x9282, 0x9284, 0x9281, 0x9285, 0x9287, + 0x9286, 0x9283, 0x92c1, 0x92c0, 0x92c2, }; typedef enum { @@ -2335,7 +2442,7 @@ static const char unicode_gc_name_table[] = "C,Other" "\0" ; -static const uint8_t unicode_gc_table[3948] = { +static const uint8_t unicode_gc_table[4070] = { 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, @@ -2379,8 +2486,8 @@ static const uint8_t unicode_gc_table[3948] = { 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04, 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5, 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80, - 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0xa0, 0xe6, - 0x00, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6, + 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0x80, 0xe6, + 0x01, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6, 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05, 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6, 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, @@ -2479,82 +2586,82 @@ static const uint8_t unicode_gc_table[3948] = { 0xe9, 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, 0x06, 0x08, 0xe6, 0x08, 0xe0, 0x29, 0x66, 0x07, 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, - 0x06, 0x27, 0xe5, 0x00, 0x40, 0xe9, 0x02, 0xd6, - 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x36, 0x00, + 0x06, 0x27, 0xe5, 0x00, 0x00, 0x36, 0xe9, 0x02, + 0xd6, 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x56, 0x26, 0x07, 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, 0x07, 0x46, 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, 0x07, 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, 0x00, 0x76, 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, 0x27, 0x26, 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, 0xe9, 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, - 0xc0, 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, - 0x00, 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, 0x65, - 0x06, 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, - 0xe2, 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, - 0x1a, 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, 0x0e, - 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, - 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, - 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, - 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, - 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, - 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, - 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, - 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, 0x4e, - 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, - 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, - 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, 0x14, - 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, - 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, - 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, 0x17, - 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, - 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, - 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, 0xe0, - 0x07, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, - 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, - 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, 0x0c, - 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, - 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, - 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, 0x2f, - 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, - 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, - 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, 0x17, - 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, - 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, - 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, 0x49, - 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, - 0xef, 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, - 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, 0x80, - 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, - 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, - 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, + 0x3f, 0x80, 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, + 0xe0, 0x00, 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, + 0x65, 0x06, 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, + 0x80, 0xe2, 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, + 0xe2, 0x1a, 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, + 0x0e, 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, + 0x00, 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, + 0x00, 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, + 0x20, 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, + 0x20, 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, + 0x00, 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, + 0x61, 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, + 0x61, 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, + 0x4e, 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, + 0x22, 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, + 0xb1, 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, + 0x14, 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, + 0x01, 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, + 0x13, 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, + 0x17, 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, + 0xab, 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, + 0x12, 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, + 0xe0, 0x07, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, + 0x04, 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, + 0x02, 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, + 0x0c, 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, + 0x0f, 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, + 0x2f, 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, + 0x2f, 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, + 0x6a, 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, + 0x0c, 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, + 0x17, 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, + 0xec, 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, + 0x13, 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, + 0x49, 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, + 0xac, 0xef, 0x40, 0xe0, 0x0e, 0xef, 0x03, 0xe0, + 0x0d, 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, + 0x80, 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, + 0xec, 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, - 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, + 0xef, 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, - 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, - 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, 0xac, - 0xef, 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, 0x61, - 0xe1, 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, 0xdf, - 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, 0x41, - 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, 0x3f, - 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, 0x02, - 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, 0x16, - 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, 0xc5, - 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, - 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xe6, - 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, 0x14, - 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, 0x36, - 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x96, - 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, 0x12, - 0xf6, 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, 0x13, - 0x12, 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, 0xef, - 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, 0x80, - 0x4e, 0xe0, 0x12, 0xef, 0x04, 0x60, 0x17, 0x56, + 0x13, 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, + 0x12, 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, + 0xec, 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, + 0xac, 0xef, 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, + 0x61, 0xe1, 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, + 0xdf, 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, + 0x41, 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, + 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, + 0x02, 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, + 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, + 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, + 0x36, 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, + 0x12, 0xf6, 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, + 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, + 0x80, 0x4e, 0xe0, 0x12, 0xef, 0x08, 0x17, 0x56, 0x0f, 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, @@ -2563,146 +2670,154 @@ static const uint8_t unicode_gc_table[3948] = { 0xe5, 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, 0xe5, 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, - 0xe5, 0x18, 0xef, 0x1c, 0xe0, 0x04, 0xe5, 0x08, - 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, 0xeb, - 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, 0x02, - 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, 0xe5, - 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, 0x8d, - 0x04, 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, 0xe0, - 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, 0x84, - 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, 0xe0, - 0x0c, 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, 0xe6, - 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, 0xe5, - 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, 0xee, - 0x0f, 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, 0xff, - 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, 0x04, - 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, 0x61, - 0x02, 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, 0x3f, - 0x80, 0x3f, 0x00, 0x02, 0x00, 0x02, 0x7f, 0xe0, - 0x10, 0x44, 0x3f, 0x05, 0x24, 0x02, 0xc5, 0x06, - 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, - 0x07, 0x6f, 0x06, 0x40, 0xab, 0x2f, 0x0d, 0x0f, - 0xa0, 0xe5, 0x2c, 0x76, 0xe0, 0x00, 0x27, 0xe5, - 0x2a, 0xe7, 0x08, 0x26, 0xe0, 0x00, 0x36, 0xe9, - 0x02, 0xa0, 0xe6, 0x0a, 0xa5, 0x56, 0x05, 0x16, - 0x25, 0x06, 0xe9, 0x02, 0xe5, 0x14, 0xe6, 0x00, - 0x36, 0xe5, 0x0f, 0xe6, 0x03, 0x27, 0xe0, 0x03, - 0x16, 0xe5, 0x15, 0x40, 0x46, 0x07, 0xe5, 0x27, - 0x06, 0x27, 0x66, 0x27, 0x26, 0x47, 0xf6, 0x05, - 0x00, 0x04, 0xe9, 0x02, 0x60, 0x36, 0x85, 0x06, - 0x04, 0xe5, 0x01, 0xe9, 0x02, 0x85, 0x00, 0xe5, - 0x21, 0xa6, 0x27, 0x26, 0x27, 0x26, 0xe0, 0x01, - 0x45, 0x06, 0xe5, 0x00, 0x06, 0x07, 0x20, 0xe9, - 0x02, 0x20, 0x76, 0xe5, 0x08, 0x04, 0xa5, 0x4f, - 0x05, 0x07, 0x06, 0x07, 0xe5, 0x2a, 0x06, 0x05, - 0x46, 0x25, 0x26, 0x85, 0x26, 0x05, 0x06, 0x05, - 0xe0, 0x10, 0x25, 0x04, 0x36, 0xe5, 0x03, 0x07, - 0x26, 0x27, 0x36, 0x05, 0x24, 0x07, 0x06, 0xe0, - 0x02, 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0xe0, 0x01, - 0xc5, 0x00, 0xc5, 0x00, 0xe2, 0x23, 0x0e, 0x64, - 0xe2, 0x01, 0x04, 0x2e, 0x60, 0xe2, 0x48, 0xe5, - 0x1b, 0x27, 0x06, 0x27, 0x06, 0x27, 0x16, 0x07, - 0x06, 0x20, 0xe9, 0x02, 0xa0, 0xe5, 0xab, 0x1c, - 0xe0, 0x04, 0xe5, 0x0f, 0x60, 0xe5, 0x29, 0x60, - 0xfc, 0x87, 0x78, 0xfd, 0x98, 0x78, 0xe5, 0x80, - 0xe6, 0x20, 0xe5, 0x62, 0xe0, 0x1e, 0xc2, 0xe0, - 0x04, 0x82, 0x80, 0x05, 0x06, 0xe5, 0x02, 0x0c, - 0xe5, 0x05, 0x00, 0x85, 0x00, 0x05, 0x00, 0x25, - 0x00, 0x25, 0x00, 0xe5, 0x64, 0xee, 0x09, 0xe0, - 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, 0xef, 0x08, - 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, 0x0f, 0xe0, - 0x18, 0xe5, 0x04, 0x0d, 0x4f, 0xe6, 0x08, 0xd6, - 0x12, 0x13, 0x16, 0xa0, 0xe6, 0x08, 0x16, 0x31, - 0x30, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0xe5, 0x18, 0xef, 0x1e, 0xe0, 0x01, 0x0f, 0xe5, + 0x08, 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, + 0xeb, 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, + 0x02, 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, + 0xe5, 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, + 0x8d, 0x04, 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, + 0xe0, 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, + 0x84, 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, + 0xe0, 0x0c, 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, + 0xe6, 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, + 0xe5, 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, + 0xee, 0x0f, 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, + 0xff, 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, + 0x04, 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, + 0x61, 0x02, 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, + 0x5f, 0x3f, 0x20, 0x3f, 0x00, 0x02, 0x00, 0x02, + 0xdf, 0xe0, 0x0d, 0x44, 0x3f, 0x05, 0x24, 0x02, + 0xc5, 0x06, 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, + 0x27, 0x26, 0x07, 0x6f, 0x06, 0x40, 0xab, 0x2f, + 0x0d, 0x0f, 0xa0, 0xe5, 0x2c, 0x76, 0xe0, 0x00, + 0x27, 0xe5, 0x2a, 0xe7, 0x08, 0x26, 0xe0, 0x00, + 0x36, 0xe9, 0x02, 0xa0, 0xe6, 0x0a, 0xa5, 0x56, + 0x05, 0x16, 0x25, 0x06, 0xe9, 0x02, 0xe5, 0x14, + 0xe6, 0x00, 0x36, 0xe5, 0x0f, 0xe6, 0x03, 0x27, + 0xe0, 0x03, 0x16, 0xe5, 0x15, 0x40, 0x46, 0x07, + 0xe5, 0x27, 0x06, 0x27, 0x66, 0x27, 0x26, 0x47, + 0xf6, 0x05, 0x00, 0x04, 0xe9, 0x02, 0x60, 0x36, + 0x85, 0x06, 0x04, 0xe5, 0x01, 0xe9, 0x02, 0x85, + 0x00, 0xe5, 0x21, 0xa6, 0x27, 0x26, 0x27, 0x26, + 0xe0, 0x01, 0x45, 0x06, 0xe5, 0x00, 0x06, 0x07, + 0x20, 0xe9, 0x02, 0x20, 0x76, 0xe5, 0x08, 0x04, + 0xa5, 0x4f, 0x05, 0x07, 0x06, 0x07, 0xe5, 0x2a, + 0x06, 0x05, 0x46, 0x25, 0x26, 0x85, 0x26, 0x05, + 0x06, 0x05, 0xe0, 0x10, 0x25, 0x04, 0x36, 0xe5, + 0x03, 0x07, 0x26, 0x27, 0x36, 0x05, 0x24, 0x07, + 0x06, 0xe0, 0x02, 0xa5, 0x20, 0xa5, 0x20, 0xa5, + 0xe0, 0x01, 0xc5, 0x00, 0xc5, 0x00, 0xe2, 0x23, + 0x0e, 0x64, 0xe2, 0x01, 0x04, 0x2e, 0x60, 0xe2, + 0x48, 0xe5, 0x1b, 0x27, 0x06, 0x27, 0x06, 0x27, + 0x16, 0x07, 0x06, 0x20, 0xe9, 0x02, 0xa0, 0xe5, + 0xab, 0x1c, 0xe0, 0x04, 0xe5, 0x0f, 0x60, 0xe5, + 0x29, 0x60, 0xfc, 0x87, 0x78, 0xfd, 0x98, 0x78, + 0xe5, 0x80, 0xe6, 0x20, 0xe5, 0x62, 0xe0, 0x1e, + 0xc2, 0xe0, 0x04, 0x82, 0x80, 0x05, 0x06, 0xe5, + 0x02, 0x0c, 0xe5, 0x05, 0x00, 0x85, 0x00, 0x05, + 0x00, 0x25, 0x00, 0x25, 0x00, 0xe5, 0x64, 0xee, + 0x09, 0xe0, 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, + 0xef, 0x08, 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, + 0x0f, 0xe0, 0x18, 0xe5, 0x04, 0x0d, 0x4f, 0xe6, + 0x08, 0xd6, 0x12, 0x13, 0x16, 0xa0, 0xe6, 0x08, + 0x16, 0x31, 0x30, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x36, 0x12, 0x13, 0x76, 0x50, 0x56, 0x00, - 0x76, 0x11, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, - 0x56, 0x0c, 0x11, 0x4c, 0x00, 0x16, 0x0d, 0x36, - 0x60, 0x85, 0x00, 0xe5, 0x7f, 0x20, 0x1b, 0x00, - 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, - 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, - 0x12, 0x12, 0x16, 0x13, 0x0e, 0x10, 0x0e, 0xe2, - 0x12, 0x12, 0x0c, 0x13, 0x0c, 0x12, 0x13, 0x16, - 0x12, 0x13, 0x36, 0xe5, 0x02, 0x04, 0xe5, 0x25, - 0x24, 0xe5, 0x17, 0x40, 0xa5, 0x20, 0xa5, 0x20, - 0xa5, 0x20, 0x45, 0x40, 0x2d, 0x0c, 0x0e, 0x0f, - 0x2d, 0x00, 0x0f, 0x6c, 0x2f, 0xe0, 0x02, 0x5b, - 0x2f, 0x20, 0xe5, 0x04, 0x00, 0xe5, 0x12, 0x00, - 0xe5, 0x0b, 0x00, 0x25, 0x00, 0xe5, 0x07, 0x20, - 0xe5, 0x06, 0xe0, 0x1a, 0xe5, 0x73, 0x80, 0x56, - 0x60, 0xeb, 0x25, 0x40, 0xef, 0x01, 0xea, 0x2d, - 0x6b, 0xef, 0x09, 0x2b, 0x4f, 0x00, 0xef, 0x05, - 0x40, 0x0f, 0xe0, 0x27, 0xef, 0x25, 0x06, 0xe0, - 0x7a, 0xe5, 0x15, 0x40, 0xe5, 0x29, 0xe0, 0x07, - 0x06, 0xeb, 0x13, 0x60, 0xe5, 0x18, 0x6b, 0xe0, - 0x01, 0xe5, 0x0c, 0x0a, 0xe5, 0x00, 0x0a, 0x80, - 0xe5, 0x1e, 0x86, 0x80, 0xe5, 0x16, 0x00, 0x16, - 0xe5, 0x1c, 0x60, 0xe5, 0x00, 0x16, 0x8a, 0xe0, - 0x22, 0xe1, 0x20, 0xe2, 0x20, 0xe5, 0x46, 0x20, - 0xe9, 0x02, 0xa0, 0xe1, 0x1c, 0x60, 0xe2, 0x1c, - 0x60, 0xe5, 0x20, 0xe0, 0x00, 0xe5, 0x2c, 0xe0, - 0x03, 0x16, 0xe1, 0x03, 0x00, 0xe1, 0x07, 0x00, - 0xc1, 0x00, 0x21, 0x00, 0xe2, 0x03, 0x00, 0xe2, - 0x07, 0x00, 0xc2, 0x00, 0x22, 0xe0, 0x3b, 0xe5, - 0x80, 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, - 0xe5, 0x00, 0xe0, 0x10, 0xa4, 0x00, 0xe4, 0x22, - 0x00, 0xe4, 0x01, 0xe0, 0x3d, 0xa5, 0x20, 0x05, - 0x00, 0xe5, 0x24, 0x00, 0x25, 0x40, 0x05, 0x20, - 0xe5, 0x0f, 0x00, 0x16, 0xeb, 0x00, 0xe5, 0x0f, - 0x2f, 0xcb, 0xe5, 0x17, 0xe0, 0x00, 0xeb, 0x01, - 0xe0, 0x28, 0xe5, 0x0b, 0x00, 0x25, 0x80, 0x8b, - 0xe5, 0x0e, 0xab, 0x40, 0x16, 0xe5, 0x12, 0x80, - 0x16, 0xe0, 0x38, 0xe5, 0x30, 0x60, 0x2b, 0x25, - 0xeb, 0x08, 0x20, 0xeb, 0x26, 0x05, 0x46, 0x00, - 0x26, 0x80, 0x66, 0x65, 0x00, 0x45, 0x00, 0xe5, - 0x15, 0x20, 0x46, 0x60, 0x06, 0xeb, 0x01, 0xc0, - 0xf6, 0x01, 0xc0, 0xe5, 0x15, 0x2b, 0x16, 0xe5, - 0x15, 0x4b, 0xe0, 0x18, 0xe5, 0x00, 0x0f, 0xe5, - 0x14, 0x26, 0x60, 0x8b, 0xd6, 0xe0, 0x01, 0xe5, - 0x2e, 0x40, 0xd6, 0xe5, 0x0e, 0x20, 0xeb, 0x00, - 0xe5, 0x0b, 0x80, 0xeb, 0x00, 0xe5, 0x0a, 0xc0, - 0x76, 0xe0, 0x04, 0xcb, 0xe0, 0x48, 0xe5, 0x41, - 0xe0, 0x2f, 0xe1, 0x2b, 0xe0, 0x05, 0xe2, 0x2b, - 0xc0, 0xab, 0xe5, 0x1c, 0x66, 0xe0, 0x00, 0xe9, - 0x02, 0xe0, 0x80, 0x9e, 0xeb, 0x17, 0x00, 0xe5, - 0x22, 0x00, 0x26, 0x11, 0x20, 0x25, 0xe0, 0x43, - 0x46, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, - 0xe5, 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x0e, - 0xe5, 0x0a, 0x66, 0x76, 0xe0, 0x1e, 0xe5, 0x0d, - 0xcb, 0xe0, 0x0c, 0xe5, 0x0f, 0xe0, 0x01, 0x07, - 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, 0x60, - 0xeb, 0x0c, 0xe9, 0x02, 0x06, 0x25, 0x26, 0x05, - 0xe0, 0x01, 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, - 0x27, 0x26, 0x36, 0x1b, 0x76, 0x06, 0xe0, 0x02, - 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, - 0x46, 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, - 0xe9, 0x02, 0x76, 0x05, 0x27, 0x05, 0xe0, 0x00, - 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, - 0x07, 0xe5, 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, - 0x76, 0x66, 0x16, 0x07, 0x06, 0xe9, 0x02, 0x05, - 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, - 0xe5, 0x0a, 0x00, 0xe5, 0x11, 0x47, 0x46, 0x27, - 0x06, 0x07, 0x26, 0xb6, 0x06, 0x25, 0x06, 0xe0, - 0x36, 0xc5, 0x00, 0x05, 0x00, 0x65, 0x00, 0xe5, - 0x07, 0x00, 0xe5, 0x02, 0x16, 0xa0, 0xe5, 0x27, - 0x06, 0x47, 0xe6, 0x00, 0x80, 0xe9, 0x02, 0xa0, - 0x26, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, 0x20, - 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x85, - 0x00, 0x26, 0x05, 0x27, 0x06, 0x67, 0x20, 0x27, - 0x20, 0x47, 0x20, 0x05, 0xa0, 0x07, 0x80, 0x85, - 0x27, 0x20, 0xc6, 0x40, 0x86, 0xe0, 0x80, 0x03, - 0xe5, 0x2d, 0x47, 0xe6, 0x00, 0x27, 0x46, 0x07, - 0x06, 0x65, 0x96, 0xe9, 0x02, 0x36, 0x00, 0x16, - 0x06, 0x45, 0xe0, 0x16, 0xe5, 0x28, 0x47, 0xa6, - 0x07, 0x06, 0x67, 0x26, 0x07, 0x26, 0x25, 0x16, - 0x05, 0xe0, 0x00, 0xe9, 0x02, 0xe0, 0x80, 0x1e, - 0xe5, 0x27, 0x47, 0x66, 0x20, 0x67, 0x26, 0x07, - 0x26, 0xf6, 0x0f, 0x65, 0x26, 0xe0, 0x1a, 0xe5, - 0x28, 0x47, 0xe6, 0x00, 0x27, 0x06, 0x07, 0x26, - 0x56, 0x05, 0xe0, 0x03, 0xe9, 0x02, 0xa0, 0xf6, - 0x05, 0xe0, 0x0b, 0xe5, 0x23, 0x06, 0x07, 0x06, - 0x27, 0xa6, 0x07, 0x06, 0x05, 0x16, 0xa0, 0xe9, - 0x02, 0xe0, 0x2e, 0xe5, 0x13, 0x20, 0x46, 0x27, + 0x13, 0x12, 0x13, 0x36, 0x12, 0x13, 0x76, 0x50, + 0x56, 0x00, 0x76, 0x11, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x56, 0x0c, 0x11, 0x4c, 0x00, 0x16, + 0x0d, 0x36, 0x60, 0x85, 0x00, 0xe5, 0x7f, 0x20, + 0x1b, 0x00, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, + 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, + 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, 0x10, + 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, 0x12, + 0x13, 0x16, 0x12, 0x13, 0x36, 0xe5, 0x02, 0x04, + 0xe5, 0x25, 0x24, 0xe5, 0x17, 0x40, 0xa5, 0x20, + 0xa5, 0x20, 0xa5, 0x20, 0x45, 0x40, 0x2d, 0x0c, + 0x0e, 0x0f, 0x2d, 0x00, 0x0f, 0x6c, 0x2f, 0xe0, + 0x02, 0x5b, 0x2f, 0x20, 0xe5, 0x04, 0x00, 0xe5, + 0x12, 0x00, 0xe5, 0x0b, 0x00, 0x25, 0x00, 0xe5, + 0x07, 0x20, 0xe5, 0x06, 0xe0, 0x1a, 0xe5, 0x73, + 0x80, 0x56, 0x60, 0xeb, 0x25, 0x40, 0xef, 0x01, + 0xea, 0x2d, 0x6b, 0xef, 0x09, 0x2b, 0x4f, 0x00, + 0xef, 0x05, 0x40, 0x0f, 0xe0, 0x27, 0xef, 0x25, + 0x06, 0xe0, 0x7a, 0xe5, 0x15, 0x40, 0xe5, 0x29, + 0xe0, 0x07, 0x06, 0xeb, 0x13, 0x60, 0xe5, 0x18, + 0x6b, 0xe0, 0x01, 0xe5, 0x0c, 0x0a, 0xe5, 0x00, + 0x0a, 0x80, 0xe5, 0x1e, 0x86, 0x80, 0xe5, 0x16, + 0x00, 0x16, 0xe5, 0x1c, 0x60, 0xe5, 0x00, 0x16, + 0x8a, 0xe0, 0x22, 0xe1, 0x20, 0xe2, 0x20, 0xe5, + 0x46, 0x20, 0xe9, 0x02, 0xa0, 0xe1, 0x1c, 0x60, + 0xe2, 0x1c, 0x60, 0xe5, 0x20, 0xe0, 0x00, 0xe5, + 0x2c, 0xe0, 0x03, 0x16, 0xe1, 0x03, 0x00, 0xe1, + 0x07, 0x00, 0xc1, 0x00, 0x21, 0x00, 0xe2, 0x03, + 0x00, 0xe2, 0x07, 0x00, 0xc2, 0x00, 0x22, 0x40, + 0xe5, 0x2c, 0xe0, 0x04, 0xe5, 0x80, 0xaf, 0xe0, + 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5, 0x00, 0xe0, + 0x10, 0xa4, 0x00, 0xe4, 0x22, 0x00, 0xe4, 0x01, + 0xe0, 0x3d, 0xa5, 0x20, 0x05, 0x00, 0xe5, 0x24, + 0x00, 0x25, 0x40, 0x05, 0x20, 0xe5, 0x0f, 0x00, + 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f, 0xcb, 0xe5, + 0x17, 0xe0, 0x00, 0xeb, 0x01, 0xe0, 0x28, 0xe5, + 0x0b, 0x00, 0x25, 0x80, 0x8b, 0xe5, 0x0e, 0xab, + 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16, 0xe0, 0x38, + 0xe5, 0x30, 0x60, 0x2b, 0x25, 0xeb, 0x08, 0x20, + 0xeb, 0x26, 0x05, 0x46, 0x00, 0x26, 0x80, 0x66, + 0x65, 0x00, 0x45, 0x00, 0xe5, 0x15, 0x20, 0x46, + 0x60, 0x06, 0xeb, 0x01, 0xc0, 0xf6, 0x01, 0xc0, + 0xe5, 0x15, 0x2b, 0x16, 0xe5, 0x15, 0x4b, 0xe0, + 0x18, 0xe5, 0x00, 0x0f, 0xe5, 0x14, 0x26, 0x60, + 0x8b, 0xd6, 0xe0, 0x01, 0xe5, 0x2e, 0x40, 0xd6, + 0xe5, 0x0e, 0x20, 0xeb, 0x00, 0xe5, 0x0b, 0x80, + 0xeb, 0x00, 0xe5, 0x0a, 0xc0, 0x76, 0xe0, 0x04, + 0xcb, 0xe0, 0x48, 0xe5, 0x41, 0xe0, 0x2f, 0xe1, + 0x2b, 0xe0, 0x05, 0xe2, 0x2b, 0xc0, 0xab, 0xe5, + 0x1c, 0x66, 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xe9, + 0x02, 0x65, 0x04, 0x05, 0xe1, 0x0e, 0x40, 0x86, + 0x11, 0x04, 0xe2, 0x0e, 0xe0, 0x00, 0x2c, 0xe0, + 0x80, 0x48, 0xeb, 0x17, 0x00, 0xe5, 0x22, 0x00, + 0x26, 0x11, 0x20, 0x25, 0xe0, 0x08, 0x45, 0xe0, + 0x2f, 0x66, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, + 0x00, 0xe5, 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, + 0x0e, 0xe5, 0x0a, 0x66, 0x76, 0xe0, 0x1e, 0xe5, + 0x0d, 0xcb, 0xe0, 0x0c, 0xe5, 0x0f, 0xe0, 0x01, + 0x07, 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, + 0x60, 0xeb, 0x0c, 0xe9, 0x02, 0x06, 0x25, 0x26, + 0x05, 0xe0, 0x01, 0x46, 0x07, 0xe5, 0x25, 0x47, + 0x66, 0x27, 0x26, 0x36, 0x1b, 0x76, 0x06, 0xe0, + 0x02, 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, + 0xa0, 0x46, 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, + 0x00, 0xe9, 0x02, 0x76, 0x05, 0x27, 0x05, 0xe0, + 0x00, 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, + 0x26, 0x07, 0xe5, 0x28, 0x47, 0xe6, 0x01, 0x27, + 0x65, 0x76, 0x66, 0x16, 0x07, 0x06, 0xe9, 0x02, + 0x05, 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, + 0x03, 0xe5, 0x0a, 0x00, 0xe5, 0x11, 0x47, 0x46, + 0x27, 0x06, 0x07, 0x26, 0xb6, 0x06, 0x25, 0x06, + 0xe0, 0x36, 0xc5, 0x00, 0x05, 0x00, 0x65, 0x00, + 0xe5, 0x07, 0x00, 0xe5, 0x02, 0x16, 0xa0, 0xe5, + 0x27, 0x06, 0x47, 0xe6, 0x00, 0x80, 0xe9, 0x02, + 0xa0, 0x26, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, + 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x85, 0x00, 0x26, 0x05, 0x27, 0x06, 0x67, 0x20, + 0x27, 0x20, 0x47, 0x20, 0x05, 0xa0, 0x07, 0x80, + 0x85, 0x27, 0x20, 0xc6, 0x40, 0x86, 0xe0, 0x03, + 0xe5, 0x02, 0x00, 0x05, 0x20, 0x05, 0x00, 0xe5, + 0x1e, 0x00, 0x05, 0x47, 0xa6, 0x00, 0x07, 0x20, + 0x07, 0x00, 0x67, 0x00, 0x27, 0x06, 0x07, 0x06, + 0x05, 0x06, 0x05, 0x36, 0x00, 0x36, 0xe0, 0x00, + 0x26, 0xe0, 0x15, 0xe5, 0x2d, 0x47, 0xe6, 0x00, + 0x27, 0x46, 0x07, 0x06, 0x65, 0x96, 0xe9, 0x02, + 0x36, 0x00, 0x16, 0x06, 0x45, 0xe0, 0x16, 0xe5, + 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, 0x26, 0x07, + 0x26, 0x25, 0x16, 0x05, 0xe0, 0x00, 0xe9, 0x02, + 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, 0x66, 0x20, + 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, 0x65, 0x26, + 0xe0, 0x1a, 0xe5, 0x28, 0x47, 0xe6, 0x00, 0x27, + 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, 0x03, 0xe9, + 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, 0xe5, 0x23, + 0x06, 0x07, 0x06, 0x27, 0xa6, 0x07, 0x06, 0x05, + 0x16, 0xa0, 0xe9, 0x02, 0xa0, 0xe9, 0x0c, 0xe0, + 0x14, 0xe5, 0x13, 0x20, 0x06, 0x07, 0x06, 0x27, 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, 0x0f, 0xc5, 0xe0, 0x80, 0x31, 0xe5, 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, 0x5c, 0xe1, @@ -2717,7 +2832,8 @@ static const uint8_t unicode_gc_table[3948] = { 0x05, 0x66, 0xf6, 0x00, 0x06, 0xe0, 0x00, 0x05, 0xa6, 0x27, 0x46, 0xe5, 0x26, 0xe6, 0x05, 0x07, 0x26, 0x56, 0x05, 0x96, 0xe0, 0x05, 0xe5, 0x41, - 0xc0, 0xf6, 0x02, 0xe0, 0x80, 0x6e, 0xe5, 0x01, + 0xc0, 0xf6, 0x02, 0xe0, 0x80, 0x2e, 0xe5, 0x19, + 0x16, 0xe0, 0x06, 0xe9, 0x02, 0xa0, 0xe5, 0x01, 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6, 0x07, 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02, 0xeb, 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6, 0x0e, @@ -2730,106 +2846,112 @@ static const uint8_t unicode_gc_table[3948] = { 0x80, 0xae, 0xe5, 0x0b, 0x26, 0x27, 0x36, 0xc0, 0x26, 0x05, 0x07, 0xe5, 0x05, 0x00, 0xe5, 0x1a, 0x27, 0x86, 0x40, 0x27, 0x06, 0x07, 0x06, 0xf6, - 0x05, 0xe9, 0x02, 0xe0, 0x4e, 0x05, 0xe0, 0x07, - 0xeb, 0x0d, 0xef, 0x00, 0x6d, 0xef, 0x09, 0xe0, - 0x05, 0x16, 0xe5, 0x83, 0x12, 0xe0, 0x5e, 0xea, - 0x67, 0x00, 0x96, 0xe0, 0x03, 0xe5, 0x80, 0x3c, - 0xe0, 0x89, 0xc4, 0xe5, 0x59, 0x36, 0xe0, 0x05, - 0xe5, 0x83, 0xa8, 0xfb, 0x08, 0x06, 0xa5, 0xe6, - 0x07, 0xe0, 0x8f, 0x22, 0xe5, 0x81, 0xbf, 0xe0, - 0xa1, 0x31, 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, - 0x00, 0xe9, 0x02, 0x60, 0x36, 0xe5, 0x47, 0x00, - 0xe9, 0x02, 0xa0, 0xe5, 0x16, 0x20, 0x86, 0x16, - 0xe0, 0x02, 0xe5, 0x28, 0xc6, 0x96, 0x6f, 0x64, - 0x16, 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, 0xcb, - 0x00, 0xe5, 0x0d, 0x80, 0xe5, 0x0b, 0xe0, 0x82, - 0x28, 0xe1, 0x18, 0xe2, 0x18, 0xeb, 0x0f, 0x76, - 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, 0xe7, - 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, 0x24, - 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, 0x06, - 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, 0x4e, - 0xe0, 0x22, 0xe5, 0x01, 0xe0, 0xa2, 0x5f, 0x64, - 0x00, 0xc4, 0x00, 0x24, 0x00, 0xe5, 0x80, 0x9b, - 0xe0, 0x07, 0x05, 0xe0, 0x15, 0x45, 0x20, 0x05, - 0xe0, 0x06, 0x65, 0xe0, 0x00, 0xe5, 0x81, 0x04, - 0xe0, 0x88, 0x7c, 0xe5, 0x63, 0x80, 0xe5, 0x05, - 0x40, 0xe5, 0x01, 0xc0, 0xe5, 0x02, 0x20, 0x0f, - 0x26, 0x16, 0x7b, 0xe0, 0x91, 0xd4, 0xe6, 0x26, - 0x20, 0xe6, 0x0f, 0xe0, 0x01, 0xef, 0x6c, 0xe0, - 0x34, 0xef, 0x80, 0x6e, 0xe0, 0x02, 0xef, 0x1f, - 0x20, 0xef, 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, - 0x00, 0xe6, 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, - 0xef, 0x35, 0xe0, 0x0d, 0xef, 0x3a, 0x46, 0x0f, - 0xe0, 0x72, 0xeb, 0x0c, 0xe0, 0x04, 0xeb, 0x0c, - 0xe0, 0x04, 0xef, 0x4f, 0xe0, 0x01, 0xeb, 0x11, - 0xe0, 0x7f, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, - 0xc2, 0x00, 0xe2, 0x0a, 0xe1, 0x12, 0xe2, 0x12, - 0x01, 0x00, 0x21, 0x20, 0x01, 0x20, 0x21, 0x20, - 0x61, 0x00, 0xe1, 0x00, 0x62, 0x00, 0x02, 0x00, - 0xc2, 0x00, 0xe2, 0x03, 0xe1, 0x12, 0xe2, 0x12, - 0x21, 0x00, 0x61, 0x20, 0xe1, 0x00, 0x00, 0xc1, - 0x00, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x00, 0x81, - 0x00, 0x01, 0x40, 0xc1, 0x00, 0xe2, 0x12, 0xe1, + 0x05, 0xe9, 0x02, 0x06, 0xe0, 0x4d, 0x05, 0xe0, + 0x07, 0xeb, 0x0d, 0xef, 0x00, 0x6d, 0xef, 0x09, + 0xe0, 0x05, 0x16, 0xe5, 0x83, 0x12, 0xe0, 0x5e, + 0xea, 0x67, 0x00, 0x96, 0xe0, 0x03, 0xe5, 0x80, + 0x3c, 0xe0, 0x89, 0xc4, 0xe5, 0x59, 0x36, 0xe0, + 0x05, 0xe5, 0x83, 0xa8, 0xfb, 0x08, 0x06, 0xa5, + 0xe6, 0x07, 0xe0, 0x02, 0xe5, 0x8f, 0x13, 0x80, + 0xe5, 0x81, 0xbf, 0xe0, 0x9a, 0x31, 0xe5, 0x16, + 0xe6, 0x04, 0x47, 0x46, 0xe9, 0x02, 0xe0, 0x86, + 0x3e, 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, 0x00, + 0xe9, 0x02, 0x60, 0x36, 0xe5, 0x47, 0x00, 0xe9, + 0x02, 0xa0, 0xe5, 0x16, 0x20, 0x86, 0x16, 0xe0, + 0x02, 0xe5, 0x28, 0xc6, 0x96, 0x6f, 0x64, 0x16, + 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, 0xcb, 0x00, + 0xe5, 0x0d, 0x80, 0xe5, 0x0b, 0xe0, 0x81, 0x28, + 0x44, 0xe5, 0x20, 0x24, 0x56, 0xe9, 0x02, 0xe0, + 0x80, 0x3e, 0xe1, 0x18, 0xe2, 0x18, 0xeb, 0x0f, + 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, + 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, + 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, + 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, + 0x4e, 0xe0, 0x21, 0xe5, 0x02, 0xe0, 0xa2, 0x5f, + 0x64, 0x00, 0xc4, 0x00, 0x24, 0x00, 0xe5, 0x80, + 0x9b, 0xe0, 0x07, 0x05, 0xe0, 0x15, 0x45, 0x20, + 0x05, 0xe0, 0x06, 0x65, 0xe0, 0x00, 0xe5, 0x81, + 0x04, 0xe0, 0x88, 0x7c, 0xe5, 0x63, 0x80, 0xe5, + 0x05, 0x40, 0xe5, 0x01, 0xc0, 0xe5, 0x02, 0x20, + 0x0f, 0x26, 0x16, 0x7b, 0xe0, 0x8e, 0xd4, 0xef, + 0x80, 0x68, 0xe9, 0x02, 0xa0, 0xef, 0x81, 0x2c, + 0xe0, 0x44, 0xe6, 0x26, 0x20, 0xe6, 0x0f, 0xe0, + 0x01, 0xef, 0x6c, 0xe0, 0x34, 0xef, 0x80, 0x6e, + 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, 0x34, 0x27, + 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6, 0x00, 0x2f, + 0xc6, 0xef, 0x16, 0x66, 0xef, 0x35, 0xe0, 0x0d, + 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x72, 0xeb, 0x0c, + 0xe0, 0x04, 0xeb, 0x0c, 0xe0, 0x04, 0xef, 0x4f, + 0xe0, 0x01, 0xeb, 0x11, 0xe0, 0x7f, 0xe1, 0x12, + 0xe2, 0x12, 0xe1, 0x12, 0xc2, 0x00, 0xe2, 0x0a, + 0xe1, 0x12, 0xe2, 0x12, 0x01, 0x00, 0x21, 0x20, + 0x01, 0x20, 0x21, 0x20, 0x61, 0x00, 0xe1, 0x00, + 0x62, 0x00, 0x02, 0x00, 0xc2, 0x00, 0xe2, 0x03, + 0xe1, 0x12, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x20, + 0xe1, 0x00, 0x00, 0xc1, 0x00, 0xe2, 0x12, 0x21, + 0x00, 0x61, 0x00, 0x81, 0x00, 0x01, 0x40, 0xc1, + 0x00, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, - 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x14, 0x20, - 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, - 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, - 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, - 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, - 0x11, 0x0c, 0xa2, 0x3f, 0x20, 0xe9, 0x2a, 0xef, - 0x81, 0x78, 0xe6, 0x2f, 0x6f, 0xe6, 0x2a, 0xef, - 0x00, 0x06, 0xef, 0x06, 0x06, 0x2f, 0x96, 0xe0, - 0x07, 0x86, 0x00, 0xe6, 0x07, 0xe0, 0x83, 0xc8, - 0xe2, 0x02, 0x05, 0xe2, 0x0c, 0xa0, 0xa2, 0xe0, - 0x80, 0x4d, 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, - 0x00, 0x26, 0x00, 0x86, 0x80, 0xe4, 0x36, 0xe0, - 0x19, 0x06, 0xe0, 0x68, 0xe5, 0x25, 0x40, 0xc6, - 0xc4, 0x20, 0xe9, 0x02, 0x60, 0x05, 0x0f, 0xe0, - 0x80, 0xb8, 0xe5, 0x16, 0x06, 0xe0, 0x09, 0xe5, - 0x24, 0x66, 0xe9, 0x02, 0x80, 0x0d, 0xe0, 0x81, - 0x48, 0xe5, 0x13, 0x04, 0x66, 0xe9, 0x02, 0xe0, - 0x82, 0x5e, 0xc5, 0x00, 0x65, 0x00, 0x25, 0x00, - 0xe5, 0x07, 0x00, 0xe5, 0x80, 0x3d, 0x20, 0xeb, - 0x01, 0xc6, 0xe0, 0x21, 0xe1, 0x1a, 0xe2, 0x1a, - 0xc6, 0x04, 0x60, 0xe9, 0x02, 0x60, 0x36, 0xe0, - 0x82, 0x89, 0xeb, 0x33, 0x0f, 0x4b, 0x0d, 0x6b, - 0xe0, 0x44, 0xeb, 0x25, 0x0f, 0xeb, 0x07, 0xe0, - 0x80, 0x3a, 0x65, 0x00, 0xe5, 0x13, 0x00, 0x25, - 0x00, 0x05, 0x20, 0x05, 0x00, 0xe5, 0x02, 0x00, - 0x65, 0x00, 0x05, 0x00, 0x05, 0xa0, 0x05, 0x60, - 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x45, 0x00, - 0x25, 0x00, 0x05, 0x20, 0x05, 0x00, 0x05, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x25, 0x00, - 0x05, 0x20, 0x65, 0x00, 0xc5, 0x00, 0x65, 0x00, - 0x65, 0x00, 0x05, 0x00, 0xe5, 0x02, 0x00, 0xe5, - 0x09, 0x80, 0x45, 0x00, 0x85, 0x00, 0xe5, 0x09, - 0xe0, 0x2c, 0x2c, 0xe0, 0x80, 0x86, 0xef, 0x24, - 0x60, 0xef, 0x5c, 0xe0, 0x04, 0xef, 0x07, 0x20, - 0xef, 0x07, 0x00, 0xef, 0x07, 0x00, 0xef, 0x1d, - 0xe0, 0x02, 0xeb, 0x05, 0xef, 0x80, 0x19, 0xe0, - 0x30, 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, - 0xef, 0x01, 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, - 0x80, 0x12, 0xef, 0x80, 0x73, 0x8e, 0xef, 0x82, - 0x50, 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, - 0xef, 0x6f, 0x60, 0xef, 0x57, 0xa0, 0xef, 0x04, - 0x60, 0x0f, 0xe0, 0x07, 0xef, 0x04, 0x60, 0xef, - 0x30, 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, - 0xe0, 0x00, 0xef, 0x16, 0x20, 0x2f, 0xe0, 0x46, - 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, 0x06, 0x20, - 0xef, 0x05, 0x40, 0xef, 0x01, 0xc0, 0xef, 0x26, - 0x00, 0xcf, 0xe0, 0x00, 0xef, 0x06, 0x60, 0xef, - 0x01, 0xc0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, - 0x00, 0xef, 0x2f, 0xe0, 0x1d, 0xe9, 0x02, 0xe0, - 0x83, 0x7e, 0xe5, 0xc0, 0x66, 0x58, 0xe0, 0x18, - 0xe5, 0x8f, 0xb2, 0xa0, 0xe5, 0x80, 0x56, 0x20, - 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c, 0xa9, - 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0, 0x85, - 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, 0x8f, 0xd8, - 0xe0, 0xca, 0x9b, 0xc9, 0x1b, 0xe0, 0x16, 0xfb, - 0x58, 0xe0, 0x78, 0xe6, 0x80, 0x68, 0xe0, 0xc0, - 0xbd, 0x88, 0xfd, 0xc0, 0xbf, 0x76, 0x20, 0xfd, - 0xc0, 0xbf, 0x76, 0x20, + 0x12, 0xe2, 0x14, 0x20, 0xe1, 0x11, 0x0c, 0xe2, + 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, + 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, + 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, + 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0x3f, + 0x20, 0xe9, 0x2a, 0xef, 0x81, 0x78, 0xe6, 0x2f, + 0x6f, 0xe6, 0x2a, 0xef, 0x00, 0x06, 0xef, 0x06, + 0x06, 0x2f, 0x96, 0xe0, 0x07, 0x86, 0x00, 0xe6, + 0x07, 0xe0, 0x83, 0xc8, 0xe2, 0x02, 0x05, 0xe2, + 0x0c, 0xa0, 0xa2, 0xe0, 0x80, 0x4d, 0xc6, 0x00, + 0xe6, 0x09, 0x20, 0xc6, 0x00, 0x26, 0x00, 0x86, + 0x80, 0xe4, 0x36, 0xe0, 0x19, 0x06, 0xe0, 0x68, + 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02, + 0x60, 0x05, 0x0f, 0xe0, 0x80, 0xb8, 0xe5, 0x16, + 0x06, 0xe0, 0x09, 0xe5, 0x24, 0x66, 0xe9, 0x02, + 0x80, 0x0d, 0xe0, 0x81, 0x48, 0xe5, 0x13, 0x04, + 0x66, 0xe9, 0x02, 0xe0, 0x80, 0x4e, 0xe5, 0x16, + 0x26, 0x05, 0xe9, 0x02, 0x60, 0x16, 0xe0, 0x81, + 0x58, 0xc5, 0x00, 0x65, 0x00, 0x25, 0x00, 0xe5, + 0x07, 0x00, 0xe5, 0x80, 0x3d, 0x20, 0xeb, 0x01, + 0xc6, 0xe0, 0x21, 0xe1, 0x1a, 0xe2, 0x1a, 0xc6, + 0x04, 0x60, 0xe9, 0x02, 0x60, 0x36, 0xe0, 0x82, + 0x89, 0xeb, 0x33, 0x0f, 0x4b, 0x0d, 0x6b, 0xe0, + 0x44, 0xeb, 0x25, 0x0f, 0xeb, 0x07, 0xe0, 0x80, + 0x3a, 0x65, 0x00, 0xe5, 0x13, 0x00, 0x25, 0x00, + 0x05, 0x20, 0x05, 0x00, 0xe5, 0x02, 0x00, 0x65, + 0x00, 0x05, 0x00, 0x05, 0xa0, 0x05, 0x60, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x45, 0x00, 0x25, + 0x00, 0x05, 0x20, 0x05, 0x00, 0x05, 0x00, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x25, 0x00, 0x05, + 0x20, 0x65, 0x00, 0xc5, 0x00, 0x65, 0x00, 0x65, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x00, 0xe5, 0x09, + 0x80, 0x45, 0x00, 0x85, 0x00, 0xe5, 0x09, 0xe0, + 0x2c, 0x2c, 0xe0, 0x80, 0x86, 0xef, 0x24, 0x60, + 0xef, 0x5c, 0xe0, 0x04, 0xef, 0x07, 0x20, 0xef, + 0x07, 0x00, 0xef, 0x07, 0x00, 0xef, 0x1d, 0xe0, + 0x02, 0xeb, 0x05, 0xef, 0x80, 0x19, 0xe0, 0x30, + 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, 0xef, + 0x01, 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, 0x80, + 0x12, 0xef, 0x80, 0x73, 0x8e, 0xef, 0x82, 0x50, + 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, 0xef, + 0x6f, 0x60, 0xef, 0x57, 0xa0, 0xef, 0x04, 0x60, + 0x0f, 0xe0, 0x07, 0xef, 0x04, 0x60, 0xef, 0x30, + 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, 0xe0, + 0x00, 0xef, 0x16, 0x20, 0xef, 0x04, 0x60, 0x2f, + 0xe0, 0x36, 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, + 0x06, 0x20, 0xef, 0x05, 0x40, 0xef, 0x02, 0x80, + 0xef, 0x30, 0xc0, 0xef, 0x07, 0x20, 0xef, 0x03, + 0xa0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, 0x00, + 0xef, 0x54, 0xe9, 0x02, 0xe0, 0x83, 0x7e, 0xe5, + 0xc0, 0x66, 0x58, 0xe0, 0x18, 0xe5, 0x8f, 0xb2, + 0xa0, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, + 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x07, 0xe5, + 0x81, 0xe6, 0xe0, 0x89, 0x1a, 0xe5, 0x81, 0x96, + 0xe0, 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, + 0x8f, 0xd8, 0xe0, 0xca, 0x9b, 0xc9, 0x1b, 0xe0, + 0x16, 0xfb, 0x58, 0xe0, 0x78, 0xe6, 0x80, 0x68, + 0xe0, 0xc0, 0xbd, 0x88, 0xfd, 0xc0, 0xbf, 0x76, + 0x20, 0xfd, 0xc0, 0xbf, 0x76, 0x20, }; typedef enum { @@ -2873,6 +2995,7 @@ typedef enum { UNICODE_SCRIPT_Elbasan, UNICODE_SCRIPT_Elymaic, UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Garay, UNICODE_SCRIPT_Georgian, UNICODE_SCRIPT_Glagolitic, UNICODE_SCRIPT_Gothic, @@ -2881,6 +3004,7 @@ typedef enum { UNICODE_SCRIPT_Gujarati, UNICODE_SCRIPT_Gunjala_Gondi, UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Gurung_Khema, UNICODE_SCRIPT_Han, UNICODE_SCRIPT_Hangul, UNICODE_SCRIPT_Hanifi_Rohingya, @@ -2903,6 +3027,7 @@ typedef enum { UNICODE_SCRIPT_Khojki, UNICODE_SCRIPT_Khitan_Small_Script, UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Kirat_Rai, UNICODE_SCRIPT_Lao, UNICODE_SCRIPT_Latin, UNICODE_SCRIPT_Lepcha, @@ -2940,6 +3065,7 @@ typedef enum { UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, UNICODE_SCRIPT_Ogham, UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Ol_Onal, UNICODE_SCRIPT_Old_Hungarian, UNICODE_SCRIPT_Old_Italic, UNICODE_SCRIPT_Old_North_Arabian, @@ -2971,6 +3097,7 @@ typedef enum { UNICODE_SCRIPT_Sora_Sompeng, UNICODE_SCRIPT_Soyombo, UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Sunuwar, UNICODE_SCRIPT_Syloti_Nagri, UNICODE_SCRIPT_Syriac, UNICODE_SCRIPT_Tagalog, @@ -2988,7 +3115,9 @@ typedef enum { UNICODE_SCRIPT_Tifinagh, UNICODE_SCRIPT_Tirhuta, UNICODE_SCRIPT_Tangsa, + UNICODE_SCRIPT_Todhri, UNICODE_SCRIPT_Toto, + UNICODE_SCRIPT_Tulu_Tigalari, UNICODE_SCRIPT_Ugaritic, UNICODE_SCRIPT_Vai, UNICODE_SCRIPT_Vithkuqi, @@ -3001,6 +3130,7 @@ typedef enum { } UnicodeScriptEnum; static const char unicode_script_name_table[] = + "Unknown,Zzzz" "\0" "Adlam,Adlm" "\0" "Ahom,Ahom" "\0" "Anatolian_Hieroglyphs,Hluw" "\0" @@ -3040,6 +3170,7 @@ static const char unicode_script_name_table[] = "Elbasan,Elba" "\0" "Elymaic,Elym" "\0" "Ethiopic,Ethi" "\0" + "Garay,Gara" "\0" "Georgian,Geor" "\0" "Glagolitic,Glag" "\0" "Gothic,Goth" "\0" @@ -3048,6 +3179,7 @@ static const char unicode_script_name_table[] = "Gujarati,Gujr" "\0" "Gunjala_Gondi,Gong" "\0" "Gurmukhi,Guru" "\0" + "Gurung_Khema,Gukh" "\0" "Han,Hani" "\0" "Hangul,Hang" "\0" "Hanifi_Rohingya,Rohg" "\0" @@ -3070,6 +3202,7 @@ static const char unicode_script_name_table[] = "Khojki,Khoj" "\0" "Khitan_Small_Script,Kits" "\0" "Khudawadi,Sind" "\0" + "Kirat_Rai,Krai" "\0" "Lao,Laoo" "\0" "Latin,Latn" "\0" "Lepcha,Lepc" "\0" @@ -3107,6 +3240,7 @@ static const char unicode_script_name_table[] = "Nyiakeng_Puachue_Hmong,Hmnp" "\0" "Ogham,Ogam" "\0" "Ol_Chiki,Olck" "\0" + "Ol_Onal,Onao" "\0" "Old_Hungarian,Hung" "\0" "Old_Italic,Ital" "\0" "Old_North_Arabian,Narb" "\0" @@ -3138,6 +3272,7 @@ static const char unicode_script_name_table[] = "Sora_Sompeng,Sora" "\0" "Soyombo,Soyo" "\0" "Sundanese,Sund" "\0" + "Sunuwar,Sunu" "\0" "Syloti_Nagri,Sylo" "\0" "Syriac,Syrc" "\0" "Tagalog,Tglg" "\0" @@ -3155,7 +3290,9 @@ static const char unicode_script_name_table[] = "Tifinagh,Tfng" "\0" "Tirhuta,Tirh" "\0" "Tangsa,Tnsa" "\0" + "Todhri,Todr" "\0" "Toto,Toto" "\0" + "Tulu_Tigalari,Tutg" "\0" "Ugaritic,Ugar" "\0" "Vai,Vaii" "\0" "Vithkuqi,Vith" "\0" @@ -3166,87 +3303,87 @@ static const char unicode_script_name_table[] = "Zanabazar_Square,Zanb" "\0" ; -static const uint8_t unicode_script_table[2720] = { - 0xc0, 0x19, 0x99, 0x47, 0x85, 0x19, 0x99, 0x47, - 0xae, 0x19, 0x80, 0x47, 0x8e, 0x19, 0x80, 0x47, - 0x84, 0x19, 0x96, 0x47, 0x80, 0x19, 0x9e, 0x47, - 0x80, 0x19, 0xe1, 0x60, 0x47, 0xa6, 0x19, 0x84, - 0x47, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, - 0x0f, 0x38, 0x83, 0x2c, 0x80, 0x19, 0x82, 0x2c, - 0x01, 0x83, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x03, - 0x80, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x80, 0x19, - 0x82, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x93, 0x2c, - 0x00, 0xbe, 0x2c, 0x8d, 0x1a, 0x8f, 0x2c, 0xe0, - 0x24, 0x1d, 0x81, 0x38, 0xe0, 0x48, 0x1d, 0x00, +static const uint8_t unicode_script_table[2803] = { + 0xc0, 0x19, 0x99, 0x4a, 0x85, 0x19, 0x99, 0x4a, + 0xae, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x80, 0x4a, + 0x84, 0x19, 0x96, 0x4a, 0x80, 0x19, 0x9e, 0x4a, + 0x80, 0x19, 0xe1, 0x60, 0x4a, 0xa6, 0x19, 0x84, + 0x4a, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, + 0x0f, 0x3a, 0x83, 0x2d, 0x80, 0x19, 0x82, 0x2d, + 0x01, 0x83, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x03, + 0x80, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x80, 0x19, + 0x82, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x93, 0x2d, + 0x00, 0xbe, 0x2d, 0x8d, 0x1a, 0x8f, 0x2d, 0xe0, + 0x24, 0x1d, 0x81, 0x3a, 0xe0, 0x48, 0x1d, 0x00, 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, - 0x00, 0xb6, 0x35, 0x07, 0x9a, 0x35, 0x03, 0x85, - 0x35, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, + 0x00, 0xb6, 0x37, 0x07, 0x9a, 0x37, 0x03, 0x85, + 0x37, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04, 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04, - 0x8a, 0x38, 0x99, 0x04, 0x80, 0x38, 0xe0, 0x0b, - 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x8b, 0x00, - 0xbb, 0x8b, 0x01, 0x82, 0x8b, 0xaf, 0x04, 0xb1, - 0x95, 0x0d, 0xba, 0x66, 0x01, 0x82, 0x66, 0xad, - 0x7f, 0x01, 0x8e, 0x7f, 0x00, 0x9b, 0x52, 0x01, - 0x80, 0x52, 0x00, 0x8a, 0x8b, 0x04, 0x9e, 0x04, - 0x00, 0x81, 0x04, 0x05, 0xc9, 0x04, 0x80, 0x19, - 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x38, 0x8e, 0x20, + 0x8a, 0x3a, 0x99, 0x04, 0x80, 0x3a, 0xe0, 0x0b, + 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x90, 0x00, + 0xbb, 0x90, 0x01, 0x82, 0x90, 0xaf, 0x04, 0xb1, + 0x9a, 0x0d, 0xba, 0x69, 0x01, 0x82, 0x69, 0xad, + 0x83, 0x01, 0x8e, 0x83, 0x00, 0x9b, 0x55, 0x01, + 0x80, 0x55, 0x00, 0x8a, 0x90, 0x04, 0x9e, 0x04, + 0x00, 0x81, 0x04, 0x04, 0xca, 0x04, 0x80, 0x19, + 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x3a, 0x8e, 0x20, 0x81, 0x19, 0x99, 0x20, 0x83, 0x0b, 0x00, 0x87, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00, 0x86, 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b, 0x01, 0x88, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x83, 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00, - 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x2f, - 0x00, 0x85, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x95, - 0x2f, 0x00, 0x86, 0x2f, 0x00, 0x81, 0x2f, 0x00, - 0x81, 0x2f, 0x00, 0x81, 0x2f, 0x01, 0x80, 0x2f, - 0x00, 0x84, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x82, - 0x2f, 0x02, 0x80, 0x2f, 0x06, 0x83, 0x2f, 0x00, - 0x80, 0x2f, 0x06, 0x90, 0x2f, 0x09, 0x82, 0x2d, - 0x00, 0x88, 0x2d, 0x00, 0x82, 0x2d, 0x00, 0x95, - 0x2d, 0x00, 0x86, 0x2d, 0x00, 0x81, 0x2d, 0x00, - 0x84, 0x2d, 0x01, 0x89, 0x2d, 0x00, 0x82, 0x2d, - 0x00, 0x82, 0x2d, 0x01, 0x80, 0x2d, 0x0e, 0x83, - 0x2d, 0x01, 0x8b, 0x2d, 0x06, 0x86, 0x2d, 0x00, - 0x82, 0x74, 0x00, 0x87, 0x74, 0x01, 0x81, 0x74, - 0x01, 0x95, 0x74, 0x00, 0x86, 0x74, 0x00, 0x81, - 0x74, 0x00, 0x84, 0x74, 0x01, 0x88, 0x74, 0x01, - 0x81, 0x74, 0x01, 0x82, 0x74, 0x06, 0x82, 0x74, - 0x03, 0x81, 0x74, 0x00, 0x84, 0x74, 0x01, 0x91, - 0x74, 0x09, 0x81, 0x92, 0x00, 0x85, 0x92, 0x02, - 0x82, 0x92, 0x00, 0x83, 0x92, 0x02, 0x81, 0x92, - 0x00, 0x80, 0x92, 0x00, 0x81, 0x92, 0x02, 0x81, - 0x92, 0x02, 0x82, 0x92, 0x02, 0x8b, 0x92, 0x03, - 0x84, 0x92, 0x02, 0x82, 0x92, 0x00, 0x83, 0x92, - 0x01, 0x80, 0x92, 0x05, 0x80, 0x92, 0x0d, 0x94, - 0x92, 0x04, 0x8c, 0x94, 0x00, 0x82, 0x94, 0x00, - 0x96, 0x94, 0x00, 0x8f, 0x94, 0x01, 0x88, 0x94, - 0x00, 0x82, 0x94, 0x00, 0x83, 0x94, 0x06, 0x81, - 0x94, 0x00, 0x82, 0x94, 0x01, 0x80, 0x94, 0x01, - 0x83, 0x94, 0x01, 0x89, 0x94, 0x06, 0x88, 0x94, - 0x8c, 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x96, 0x3d, - 0x00, 0x89, 0x3d, 0x00, 0x84, 0x3d, 0x01, 0x88, - 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x83, 0x3d, 0x06, - 0x81, 0x3d, 0x05, 0x81, 0x3d, 0x00, 0x83, 0x3d, - 0x01, 0x89, 0x3d, 0x00, 0x82, 0x3d, 0x0b, 0x8c, - 0x51, 0x00, 0x82, 0x51, 0x00, 0xb2, 0x51, 0x00, - 0x82, 0x51, 0x00, 0x85, 0x51, 0x03, 0x8f, 0x51, - 0x01, 0x99, 0x51, 0x00, 0x82, 0x85, 0x00, 0x91, - 0x85, 0x02, 0x97, 0x85, 0x00, 0x88, 0x85, 0x00, - 0x80, 0x85, 0x01, 0x86, 0x85, 0x02, 0x80, 0x85, - 0x03, 0x85, 0x85, 0x00, 0x80, 0x85, 0x00, 0x87, - 0x85, 0x05, 0x89, 0x85, 0x01, 0x82, 0x85, 0x0b, - 0xb9, 0x96, 0x03, 0x80, 0x19, 0x9b, 0x96, 0x24, - 0x81, 0x46, 0x00, 0x80, 0x46, 0x00, 0x84, 0x46, - 0x00, 0x97, 0x46, 0x00, 0x80, 0x46, 0x00, 0x96, - 0x46, 0x01, 0x84, 0x46, 0x00, 0x80, 0x46, 0x00, - 0x86, 0x46, 0x00, 0x89, 0x46, 0x01, 0x83, 0x46, - 0x1f, 0xc7, 0x97, 0x00, 0xa3, 0x97, 0x03, 0xa6, - 0x97, 0x00, 0xa3, 0x97, 0x00, 0x8e, 0x97, 0x00, - 0x86, 0x97, 0x83, 0x19, 0x81, 0x97, 0x24, 0xe0, - 0x3f, 0x60, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, - 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83, - 0x28, 0xe0, 0x9f, 0x31, 0xc8, 0x27, 0x00, 0x83, + 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x30, + 0x00, 0x85, 0x30, 0x03, 0x81, 0x30, 0x01, 0x95, + 0x30, 0x00, 0x86, 0x30, 0x00, 0x81, 0x30, 0x00, + 0x81, 0x30, 0x00, 0x81, 0x30, 0x01, 0x80, 0x30, + 0x00, 0x84, 0x30, 0x03, 0x81, 0x30, 0x01, 0x82, + 0x30, 0x02, 0x80, 0x30, 0x06, 0x83, 0x30, 0x00, + 0x80, 0x30, 0x06, 0x90, 0x30, 0x09, 0x82, 0x2e, + 0x00, 0x88, 0x2e, 0x00, 0x82, 0x2e, 0x00, 0x95, + 0x2e, 0x00, 0x86, 0x2e, 0x00, 0x81, 0x2e, 0x00, + 0x84, 0x2e, 0x01, 0x89, 0x2e, 0x00, 0x82, 0x2e, + 0x00, 0x82, 0x2e, 0x01, 0x80, 0x2e, 0x0e, 0x83, + 0x2e, 0x01, 0x8b, 0x2e, 0x06, 0x86, 0x2e, 0x00, + 0x82, 0x78, 0x00, 0x87, 0x78, 0x01, 0x81, 0x78, + 0x01, 0x95, 0x78, 0x00, 0x86, 0x78, 0x00, 0x81, + 0x78, 0x00, 0x84, 0x78, 0x01, 0x88, 0x78, 0x01, + 0x81, 0x78, 0x01, 0x82, 0x78, 0x06, 0x82, 0x78, + 0x03, 0x81, 0x78, 0x00, 0x84, 0x78, 0x01, 0x91, + 0x78, 0x09, 0x81, 0x97, 0x00, 0x85, 0x97, 0x02, + 0x82, 0x97, 0x00, 0x83, 0x97, 0x02, 0x81, 0x97, + 0x00, 0x80, 0x97, 0x00, 0x81, 0x97, 0x02, 0x81, + 0x97, 0x02, 0x82, 0x97, 0x02, 0x8b, 0x97, 0x03, + 0x84, 0x97, 0x02, 0x82, 0x97, 0x00, 0x83, 0x97, + 0x01, 0x80, 0x97, 0x05, 0x80, 0x97, 0x0d, 0x94, + 0x97, 0x04, 0x8c, 0x99, 0x00, 0x82, 0x99, 0x00, + 0x96, 0x99, 0x00, 0x8f, 0x99, 0x01, 0x88, 0x99, + 0x00, 0x82, 0x99, 0x00, 0x83, 0x99, 0x06, 0x81, + 0x99, 0x00, 0x82, 0x99, 0x01, 0x80, 0x99, 0x01, + 0x83, 0x99, 0x01, 0x89, 0x99, 0x06, 0x88, 0x99, + 0x8c, 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x96, 0x3f, + 0x00, 0x89, 0x3f, 0x00, 0x84, 0x3f, 0x01, 0x88, + 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x83, 0x3f, 0x06, + 0x81, 0x3f, 0x05, 0x81, 0x3f, 0x00, 0x83, 0x3f, + 0x01, 0x89, 0x3f, 0x00, 0x82, 0x3f, 0x0b, 0x8c, + 0x54, 0x00, 0x82, 0x54, 0x00, 0xb2, 0x54, 0x00, + 0x82, 0x54, 0x00, 0x85, 0x54, 0x03, 0x8f, 0x54, + 0x01, 0x99, 0x54, 0x00, 0x82, 0x89, 0x00, 0x91, + 0x89, 0x02, 0x97, 0x89, 0x00, 0x88, 0x89, 0x00, + 0x80, 0x89, 0x01, 0x86, 0x89, 0x02, 0x80, 0x89, + 0x03, 0x85, 0x89, 0x00, 0x80, 0x89, 0x00, 0x87, + 0x89, 0x05, 0x89, 0x89, 0x01, 0x82, 0x89, 0x0b, + 0xb9, 0x9b, 0x03, 0x80, 0x19, 0x9b, 0x9b, 0x24, + 0x81, 0x49, 0x00, 0x80, 0x49, 0x00, 0x84, 0x49, + 0x00, 0x97, 0x49, 0x00, 0x80, 0x49, 0x00, 0x96, + 0x49, 0x01, 0x84, 0x49, 0x00, 0x80, 0x49, 0x00, + 0x86, 0x49, 0x00, 0x89, 0x49, 0x01, 0x83, 0x49, + 0x1f, 0xc7, 0x9c, 0x00, 0xa3, 0x9c, 0x03, 0xa6, + 0x9c, 0x00, 0xa3, 0x9c, 0x00, 0x8e, 0x9c, 0x00, + 0x86, 0x9c, 0x83, 0x19, 0x81, 0x9c, 0x24, 0xe0, + 0x3f, 0x63, 0xa5, 0x29, 0x00, 0x80, 0x29, 0x04, + 0x80, 0x29, 0x01, 0xaa, 0x29, 0x80, 0x19, 0x83, + 0x29, 0xe0, 0x9f, 0x33, 0xc8, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, @@ -3254,366 +3391,430 @@ static const uint8_t unicode_script_table[2720] = { 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27, 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99, 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, - 0xe2, 0x1f, 0x12, 0x9c, 0x69, 0x02, 0xca, 0x7e, - 0x82, 0x19, 0x8a, 0x7e, 0x06, 0x95, 0x8c, 0x08, - 0x80, 0x8c, 0x94, 0x33, 0x81, 0x19, 0x08, 0x93, - 0x11, 0x0b, 0x8c, 0x8d, 0x00, 0x82, 0x8d, 0x00, - 0x81, 0x8d, 0x0b, 0xdd, 0x42, 0x01, 0x89, 0x42, - 0x05, 0x89, 0x42, 0x05, 0x81, 0x5d, 0x81, 0x19, - 0x80, 0x5d, 0x80, 0x19, 0x93, 0x5d, 0x05, 0xd8, - 0x5d, 0x06, 0xaa, 0x5d, 0x04, 0xc5, 0x12, 0x09, - 0x9e, 0x49, 0x00, 0x8b, 0x49, 0x03, 0x8b, 0x49, - 0x03, 0x80, 0x49, 0x02, 0x8b, 0x49, 0x9d, 0x8e, - 0x01, 0x84, 0x8e, 0x0a, 0xab, 0x64, 0x03, 0x99, - 0x64, 0x05, 0x8a, 0x64, 0x02, 0x81, 0x64, 0x9f, - 0x42, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x8f, - 0x00, 0x9c, 0x8f, 0x01, 0x8a, 0x8f, 0x05, 0x89, - 0x8f, 0x05, 0x8d, 0x8f, 0x01, 0x9e, 0x38, 0x30, - 0xcc, 0x07, 0x02, 0xae, 0x07, 0x00, 0xbf, 0x89, - 0xb3, 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x48, 0x02, - 0x8e, 0x48, 0x02, 0x82, 0x48, 0xaf, 0x6a, 0x88, - 0x1d, 0x06, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87, - 0x89, 0x07, 0x82, 0x38, 0x80, 0x19, 0x8c, 0x38, - 0x80, 0x19, 0x86, 0x38, 0x83, 0x19, 0x80, 0x38, - 0x85, 0x19, 0x80, 0x38, 0x82, 0x19, 0x81, 0x38, - 0x80, 0x19, 0x04, 0xa5, 0x47, 0x84, 0x2c, 0x80, - 0x1d, 0xb0, 0x47, 0x84, 0x2c, 0x83, 0x47, 0x84, - 0x2c, 0x8c, 0x47, 0x80, 0x1d, 0xc5, 0x47, 0x80, - 0x2c, 0xbf, 0x38, 0xe0, 0x9f, 0x47, 0x95, 0x2c, - 0x01, 0x85, 0x2c, 0x01, 0xa5, 0x2c, 0x01, 0x85, - 0x2c, 0x01, 0x87, 0x2c, 0x00, 0x80, 0x2c, 0x00, - 0x80, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x9e, 0x2c, - 0x01, 0xb4, 0x2c, 0x00, 0x8e, 0x2c, 0x00, 0x8d, - 0x2c, 0x01, 0x85, 0x2c, 0x00, 0x92, 0x2c, 0x01, - 0x82, 0x2c, 0x00, 0x88, 0x2c, 0x00, 0x8b, 0x19, - 0x81, 0x38, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, - 0x47, 0x01, 0x8a, 0x19, 0x80, 0x47, 0x8e, 0x19, - 0x00, 0x8c, 0x47, 0x02, 0xa0, 0x19, 0x0e, 0xa0, - 0x38, 0x0e, 0xa5, 0x19, 0x80, 0x2c, 0x82, 0x19, - 0x81, 0x47, 0x85, 0x19, 0x80, 0x47, 0x9a, 0x19, - 0x80, 0x47, 0x90, 0x19, 0xa8, 0x47, 0x82, 0x19, - 0x03, 0xe2, 0x36, 0x19, 0x18, 0x8a, 0x19, 0x14, - 0xe3, 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, - 0x19, 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, - 0xdf, 0x29, 0x9f, 0x47, 0xe0, 0x13, 0x1a, 0x04, - 0x86, 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, - 0x80, 0x28, 0x01, 0xb7, 0x98, 0x06, 0x81, 0x98, - 0x0d, 0x80, 0x98, 0x96, 0x27, 0x08, 0x86, 0x27, + 0xe2, 0x1f, 0x12, 0x9c, 0x6c, 0x02, 0xca, 0x82, + 0x82, 0x19, 0x8a, 0x82, 0x06, 0x95, 0x91, 0x08, + 0x80, 0x91, 0x94, 0x35, 0x81, 0x19, 0x08, 0x93, + 0x11, 0x0b, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00, + 0x81, 0x92, 0x0b, 0xdd, 0x44, 0x01, 0x89, 0x44, + 0x05, 0x89, 0x44, 0x05, 0x81, 0x60, 0x81, 0x19, + 0x80, 0x60, 0x80, 0x19, 0x93, 0x60, 0x05, 0xd8, + 0x60, 0x06, 0xaa, 0x60, 0x04, 0xc5, 0x12, 0x09, + 0x9e, 0x4c, 0x00, 0x8b, 0x4c, 0x03, 0x8b, 0x4c, + 0x03, 0x80, 0x4c, 0x02, 0x8b, 0x4c, 0x9d, 0x93, + 0x01, 0x84, 0x93, 0x0a, 0xab, 0x67, 0x03, 0x99, + 0x67, 0x05, 0x8a, 0x67, 0x02, 0x81, 0x67, 0x9f, + 0x44, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x94, + 0x00, 0x9c, 0x94, 0x01, 0x8a, 0x94, 0x05, 0x89, + 0x94, 0x05, 0x8d, 0x94, 0x01, 0x9e, 0x3a, 0x30, + 0xcc, 0x07, 0x00, 0xb1, 0x07, 0xbf, 0x8d, 0xb3, + 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x4b, 0x02, 0x8e, + 0x4b, 0x02, 0x82, 0x4b, 0xaf, 0x6d, 0x8a, 0x1d, + 0x04, 0xaa, 0x29, 0x01, 0x82, 0x29, 0x87, 0x8d, + 0x07, 0x82, 0x3a, 0x80, 0x19, 0x8c, 0x3a, 0x80, + 0x19, 0x86, 0x3a, 0x83, 0x19, 0x80, 0x3a, 0x85, + 0x19, 0x80, 0x3a, 0x82, 0x19, 0x81, 0x3a, 0x80, + 0x19, 0x04, 0xa5, 0x4a, 0x84, 0x2d, 0x80, 0x1d, + 0xb0, 0x4a, 0x84, 0x2d, 0x83, 0x4a, 0x84, 0x2d, + 0x8c, 0x4a, 0x80, 0x1d, 0xc5, 0x4a, 0x80, 0x2d, + 0xbf, 0x3a, 0xe0, 0x9f, 0x4a, 0x95, 0x2d, 0x01, + 0x85, 0x2d, 0x01, 0xa5, 0x2d, 0x01, 0x85, 0x2d, + 0x01, 0x87, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x80, + 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x9e, 0x2d, 0x01, + 0xb4, 0x2d, 0x00, 0x8e, 0x2d, 0x00, 0x8d, 0x2d, + 0x01, 0x85, 0x2d, 0x00, 0x92, 0x2d, 0x01, 0x82, + 0x2d, 0x00, 0x88, 0x2d, 0x00, 0x8b, 0x19, 0x81, + 0x3a, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, 0x4a, + 0x01, 0x8a, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x00, + 0x8c, 0x4a, 0x02, 0xa0, 0x19, 0x0e, 0xa0, 0x3a, + 0x0e, 0xa5, 0x19, 0x80, 0x2d, 0x82, 0x19, 0x81, + 0x4a, 0x85, 0x19, 0x80, 0x4a, 0x9a, 0x19, 0x80, + 0x4a, 0x90, 0x19, 0xa8, 0x4a, 0x82, 0x19, 0x03, + 0xe2, 0x39, 0x19, 0x15, 0x8a, 0x19, 0x14, 0xe3, + 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x19, + 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, 0xdf, + 0x2a, 0x9f, 0x4a, 0xe0, 0x13, 0x1a, 0x04, 0x86, + 0x1a, 0xa5, 0x29, 0x00, 0x80, 0x29, 0x04, 0x80, + 0x29, 0x01, 0xb7, 0x9d, 0x06, 0x81, 0x9d, 0x0d, + 0x80, 0x9d, 0x96, 0x27, 0x08, 0x86, 0x27, 0x00, + 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, - 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, - 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, - 0xdd, 0x19, 0x21, 0x99, 0x30, 0x00, 0xd8, 0x30, - 0x0b, 0xe0, 0x75, 0x30, 0x19, 0x8b, 0x19, 0x03, - 0x84, 0x19, 0x80, 0x30, 0x80, 0x19, 0x80, 0x30, - 0x98, 0x19, 0x88, 0x30, 0x83, 0x38, 0x81, 0x31, - 0x87, 0x19, 0x83, 0x30, 0x83, 0x19, 0x00, 0xd5, - 0x36, 0x01, 0x81, 0x38, 0x81, 0x19, 0x82, 0x36, - 0x80, 0x19, 0xd9, 0x3e, 0x81, 0x19, 0x82, 0x3e, - 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x31, 0x00, 0x8f, - 0x19, 0x9f, 0x0d, 0xa3, 0x19, 0x0b, 0x8f, 0x3e, - 0x9e, 0x31, 0x00, 0xbf, 0x19, 0x9e, 0x31, 0xd0, - 0x19, 0xae, 0x3e, 0x80, 0x19, 0xd7, 0x3e, 0xe0, - 0x47, 0x19, 0xf0, 0x09, 0x5f, 0x30, 0xbf, 0x19, - 0xf0, 0x41, 0x9f, 0x30, 0xe4, 0x2c, 0xa2, 0x02, - 0xb6, 0xa2, 0x08, 0xaf, 0x4c, 0xe0, 0xcb, 0x9d, - 0x13, 0xdf, 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, - 0xe0, 0x05, 0x47, 0x82, 0x19, 0xbf, 0x47, 0x04, - 0x81, 0x47, 0x00, 0x80, 0x47, 0x00, 0x84, 0x47, - 0x17, 0x8d, 0x47, 0xac, 0x8a, 0x02, 0x89, 0x19, - 0x05, 0xb7, 0x7a, 0x07, 0xc5, 0x80, 0x07, 0x8b, - 0x80, 0x05, 0x9f, 0x20, 0xad, 0x40, 0x80, 0x19, - 0x80, 0x40, 0xa3, 0x7d, 0x0a, 0x80, 0x7d, 0x9c, - 0x31, 0x02, 0xcd, 0x3b, 0x00, 0x80, 0x19, 0x89, - 0x3b, 0x03, 0x81, 0x3b, 0x9e, 0x60, 0x00, 0xb6, - 0x16, 0x08, 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, - 0x83, 0x16, 0x9f, 0x60, 0xc2, 0x90, 0x17, 0x84, - 0x90, 0x96, 0x57, 0x09, 0x85, 0x27, 0x01, 0x85, - 0x27, 0x01, 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, - 0x86, 0x27, 0x00, 0xaa, 0x47, 0x80, 0x19, 0x88, - 0x47, 0x80, 0x2c, 0x83, 0x47, 0x81, 0x19, 0x03, - 0xcf, 0x17, 0xad, 0x57, 0x01, 0x89, 0x57, 0x05, - 0xf0, 0x1b, 0x43, 0x31, 0x0b, 0x96, 0x31, 0x03, - 0xb0, 0x31, 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x30, - 0x01, 0xe0, 0x09, 0x30, 0x25, 0x86, 0x47, 0x0b, - 0x84, 0x05, 0x04, 0x99, 0x35, 0x00, 0x84, 0x35, - 0x00, 0x80, 0x35, 0x00, 0x81, 0x35, 0x00, 0x81, - 0x35, 0x00, 0x89, 0x35, 0xe0, 0x12, 0x04, 0x0f, - 0xe1, 0x0a, 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, - 0xb5, 0x04, 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, - 0x8f, 0x38, 0x89, 0x19, 0x05, 0x8d, 0x38, 0x81, - 0x1d, 0xa2, 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, - 0x19, 0x03, 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, - 0x01, 0x80, 0x19, 0x00, 0x9f, 0x19, 0x99, 0x47, - 0x85, 0x19, 0x99, 0x47, 0x8a, 0x19, 0x89, 0x3e, - 0x80, 0x19, 0xac, 0x3e, 0x81, 0x19, 0x9e, 0x31, - 0x02, 0x85, 0x31, 0x01, 0x85, 0x31, 0x01, 0x85, - 0x31, 0x01, 0x82, 0x31, 0x02, 0x86, 0x19, 0x00, - 0x86, 0x19, 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4b, - 0x00, 0x99, 0x4b, 0x00, 0x92, 0x4b, 0x00, 0x81, - 0x4b, 0x00, 0x8e, 0x4b, 0x01, 0x8d, 0x4b, 0x21, - 0xe0, 0x1a, 0x4b, 0x04, 0x82, 0x19, 0x03, 0xac, - 0x19, 0x02, 0x88, 0x19, 0xce, 0x2c, 0x00, 0x8c, - 0x19, 0x02, 0x80, 0x2c, 0x2e, 0xac, 0x19, 0x80, - 0x38, 0x60, 0x21, 0x9c, 0x4d, 0x02, 0xb0, 0x13, - 0x0e, 0x80, 0x38, 0x9a, 0x19, 0x03, 0xa3, 0x6c, - 0x08, 0x82, 0x6c, 0x9a, 0x2a, 0x04, 0xaa, 0x6e, - 0x04, 0x9d, 0x9c, 0x00, 0x80, 0x9c, 0xa3, 0x6f, - 0x03, 0x8d, 0x6f, 0x29, 0xcf, 0x1f, 0xaf, 0x82, - 0x9d, 0x76, 0x01, 0x89, 0x76, 0x05, 0xa3, 0x75, - 0x03, 0xa3, 0x75, 0x03, 0xa7, 0x25, 0x07, 0xb3, - 0x14, 0x0a, 0x80, 0x14, 0x8a, 0x9e, 0x00, 0x8e, - 0x9e, 0x00, 0x86, 0x9e, 0x00, 0x81, 0x9e, 0x00, - 0x8a, 0x9e, 0x00, 0x8e, 0x9e, 0x00, 0x86, 0x9e, - 0x00, 0x81, 0x9e, 0x42, 0xe0, 0xd6, 0x4a, 0x08, - 0x95, 0x4a, 0x09, 0x87, 0x4a, 0x17, 0x85, 0x47, - 0x00, 0xa9, 0x47, 0x00, 0x88, 0x47, 0x44, 0x85, - 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, 0x00, - 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, 0x1c, - 0x95, 0x37, 0x00, 0x88, 0x37, 0x9f, 0x78, 0x9e, - 0x61, 0x07, 0x88, 0x61, 0x2f, 0x92, 0x34, 0x00, - 0x81, 0x34, 0x04, 0x84, 0x34, 0x9b, 0x7b, 0x02, - 0x80, 0x7b, 0x99, 0x4e, 0x04, 0x80, 0x4e, 0x3f, - 0x9f, 0x5a, 0x97, 0x59, 0x03, 0x93, 0x59, 0x01, - 0xad, 0x59, 0x83, 0x41, 0x00, 0x81, 0x41, 0x04, - 0x87, 0x41, 0x00, 0x82, 0x41, 0x00, 0x9c, 0x41, - 0x01, 0x82, 0x41, 0x03, 0x89, 0x41, 0x06, 0x88, - 0x41, 0x06, 0x9f, 0x71, 0x9f, 0x6d, 0x1f, 0xa6, - 0x53, 0x03, 0x8b, 0x53, 0x08, 0xb5, 0x06, 0x02, - 0x86, 0x06, 0x95, 0x3a, 0x01, 0x87, 0x3a, 0x92, - 0x39, 0x04, 0x87, 0x39, 0x91, 0x7c, 0x06, 0x83, - 0x7c, 0x0b, 0x86, 0x7c, 0x4f, 0xc8, 0x72, 0x36, - 0xb2, 0x6b, 0x0c, 0xb2, 0x6b, 0x06, 0x85, 0x6b, - 0xa7, 0x32, 0x07, 0x89, 0x32, 0x60, 0xc5, 0x9e, - 0x04, 0x00, 0xa9, 0xa1, 0x00, 0x82, 0xa1, 0x01, - 0x81, 0xa1, 0x4a, 0x82, 0x04, 0xa7, 0x70, 0x07, - 0xa9, 0x86, 0x15, 0x99, 0x73, 0x25, 0x9b, 0x18, - 0x13, 0x96, 0x26, 0x08, 0xcd, 0x0e, 0x03, 0xa3, - 0x0e, 0x08, 0x80, 0x0e, 0xc2, 0x3c, 0x09, 0x80, - 0x3c, 0x01, 0x98, 0x87, 0x06, 0x89, 0x87, 0x05, - 0xb4, 0x15, 0x00, 0x91, 0x15, 0x07, 0xa6, 0x50, - 0x08, 0xdf, 0x81, 0x00, 0x93, 0x85, 0x0a, 0x91, - 0x43, 0x00, 0xae, 0x43, 0x3d, 0x86, 0x5f, 0x00, - 0x80, 0x5f, 0x00, 0x83, 0x5f, 0x00, 0x8e, 0x5f, - 0x00, 0x8a, 0x5f, 0x05, 0xba, 0x45, 0x04, 0x89, - 0x45, 0x05, 0x83, 0x2b, 0x00, 0x87, 0x2b, 0x01, - 0x81, 0x2b, 0x01, 0x95, 0x2b, 0x00, 0x86, 0x2b, - 0x00, 0x81, 0x2b, 0x00, 0x84, 0x2b, 0x00, 0x80, - 0x38, 0x88, 0x2b, 0x01, 0x81, 0x2b, 0x01, 0x82, - 0x2b, 0x01, 0x80, 0x2b, 0x05, 0x80, 0x2b, 0x04, - 0x86, 0x2b, 0x01, 0x86, 0x2b, 0x02, 0x84, 0x2b, - 0x60, 0x2a, 0xdb, 0x65, 0x00, 0x84, 0x65, 0x1d, - 0xc7, 0x99, 0x07, 0x89, 0x99, 0x60, 0x45, 0xb5, - 0x83, 0x01, 0xa5, 0x83, 0x21, 0xc4, 0x5c, 0x0a, - 0x89, 0x5c, 0x05, 0x8c, 0x5d, 0x12, 0xb9, 0x91, - 0x05, 0x89, 0x91, 0x35, 0x9a, 0x02, 0x01, 0x8e, + 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, 0xdd, + 0x19, 0x21, 0x99, 0x32, 0x00, 0xd8, 0x32, 0x0b, + 0xe0, 0x75, 0x32, 0x19, 0x94, 0x19, 0x80, 0x32, + 0x80, 0x19, 0x80, 0x32, 0x98, 0x19, 0x88, 0x32, + 0x83, 0x3a, 0x81, 0x33, 0x87, 0x19, 0x83, 0x32, + 0x83, 0x19, 0x00, 0xd5, 0x38, 0x01, 0x81, 0x3a, + 0x81, 0x19, 0x82, 0x38, 0x80, 0x19, 0xd9, 0x40, + 0x81, 0x19, 0x82, 0x40, 0x04, 0xaa, 0x0d, 0x00, + 0xdd, 0x33, 0x00, 0x8f, 0x19, 0x9f, 0x0d, 0xa5, + 0x19, 0x08, 0x80, 0x19, 0x8f, 0x40, 0x9e, 0x33, + 0x00, 0xbf, 0x19, 0x9e, 0x33, 0xd0, 0x19, 0xae, + 0x40, 0x80, 0x19, 0xd7, 0x40, 0xe0, 0x47, 0x19, + 0xf0, 0x09, 0x5f, 0x32, 0xbf, 0x19, 0xf0, 0x41, + 0x9f, 0x32, 0xe4, 0x2c, 0xa9, 0x02, 0xb6, 0xa9, + 0x08, 0xaf, 0x4f, 0xe0, 0xcb, 0xa4, 0x13, 0xdf, + 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, 0xe0, 0x05, + 0x4a, 0x82, 0x19, 0xc2, 0x4a, 0x01, 0x81, 0x4a, + 0x00, 0x80, 0x4a, 0x00, 0x87, 0x4a, 0x14, 0x8d, + 0x4a, 0xac, 0x8f, 0x02, 0x89, 0x19, 0x05, 0xb7, + 0x7e, 0x07, 0xc5, 0x84, 0x07, 0x8b, 0x84, 0x05, + 0x9f, 0x20, 0xad, 0x42, 0x80, 0x19, 0x80, 0x42, + 0xa3, 0x81, 0x0a, 0x80, 0x81, 0x9c, 0x33, 0x02, + 0xcd, 0x3d, 0x00, 0x80, 0x19, 0x89, 0x3d, 0x03, + 0x81, 0x3d, 0x9e, 0x63, 0x00, 0xb6, 0x16, 0x08, + 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, 0x16, + 0x9f, 0x63, 0xc2, 0x95, 0x17, 0x84, 0x95, 0x96, + 0x5a, 0x09, 0x85, 0x27, 0x01, 0x85, 0x27, 0x01, + 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, 0x86, 0x27, + 0x00, 0xaa, 0x4a, 0x80, 0x19, 0x88, 0x4a, 0x80, + 0x2d, 0x83, 0x4a, 0x81, 0x19, 0x03, 0xcf, 0x17, + 0xad, 0x5a, 0x01, 0x89, 0x5a, 0x05, 0xf0, 0x1b, + 0x43, 0x33, 0x0b, 0x96, 0x33, 0x03, 0xb0, 0x33, + 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x32, 0x01, 0xe0, + 0x09, 0x32, 0x25, 0x86, 0x4a, 0x0b, 0x84, 0x05, + 0x04, 0x99, 0x37, 0x00, 0x84, 0x37, 0x00, 0x80, + 0x37, 0x00, 0x81, 0x37, 0x00, 0x81, 0x37, 0x00, + 0x89, 0x37, 0xe0, 0x12, 0x04, 0x0f, 0xe1, 0x0a, + 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, 0xb5, 0x04, + 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, 0x8f, 0x3a, + 0x89, 0x19, 0x05, 0x8d, 0x3a, 0x81, 0x1d, 0xa2, + 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, 0x19, 0x03, + 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, + 0x19, 0x00, 0x9f, 0x19, 0x99, 0x4a, 0x85, 0x19, + 0x99, 0x4a, 0x8a, 0x19, 0x89, 0x40, 0x80, 0x19, + 0xac, 0x40, 0x81, 0x19, 0x9e, 0x33, 0x02, 0x85, + 0x33, 0x01, 0x85, 0x33, 0x01, 0x85, 0x33, 0x01, + 0x82, 0x33, 0x02, 0x86, 0x19, 0x00, 0x86, 0x19, + 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4e, 0x00, 0x99, + 0x4e, 0x00, 0x92, 0x4e, 0x00, 0x81, 0x4e, 0x00, + 0x8e, 0x4e, 0x01, 0x8d, 0x4e, 0x21, 0xe0, 0x1a, + 0x4e, 0x04, 0x82, 0x19, 0x03, 0xac, 0x19, 0x02, + 0x88, 0x19, 0xce, 0x2d, 0x00, 0x8c, 0x19, 0x02, + 0x80, 0x2d, 0x2e, 0xac, 0x19, 0x80, 0x3a, 0x60, + 0x21, 0x9c, 0x50, 0x02, 0xb0, 0x13, 0x0e, 0x80, + 0x3a, 0x9a, 0x19, 0x03, 0xa3, 0x70, 0x08, 0x82, + 0x70, 0x9a, 0x2b, 0x04, 0xaa, 0x72, 0x04, 0x9d, + 0xa3, 0x00, 0x80, 0xa3, 0xa3, 0x73, 0x03, 0x8d, + 0x73, 0x29, 0xcf, 0x1f, 0xaf, 0x86, 0x9d, 0x7a, + 0x01, 0x89, 0x7a, 0x05, 0xa3, 0x79, 0x03, 0xa3, + 0x79, 0x03, 0xa7, 0x25, 0x07, 0xb3, 0x14, 0x0a, + 0x80, 0x14, 0x8a, 0xa5, 0x00, 0x8e, 0xa5, 0x00, + 0x86, 0xa5, 0x00, 0x81, 0xa5, 0x00, 0x8a, 0xa5, + 0x00, 0x8e, 0xa5, 0x00, 0x86, 0xa5, 0x00, 0x81, + 0xa5, 0x02, 0xb3, 0xa0, 0x0b, 0xe0, 0xd6, 0x4d, + 0x08, 0x95, 0x4d, 0x09, 0x87, 0x4d, 0x17, 0x85, + 0x4a, 0x00, 0xa9, 0x4a, 0x00, 0x88, 0x4a, 0x44, + 0x85, 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, + 0x00, 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, + 0x1c, 0x95, 0x39, 0x00, 0x88, 0x39, 0x9f, 0x7c, + 0x9e, 0x64, 0x07, 0x88, 0x64, 0x2f, 0x92, 0x36, + 0x00, 0x81, 0x36, 0x04, 0x84, 0x36, 0x9b, 0x7f, + 0x02, 0x80, 0x7f, 0x99, 0x51, 0x04, 0x80, 0x51, + 0x3f, 0x9f, 0x5d, 0x97, 0x5c, 0x03, 0x93, 0x5c, + 0x01, 0xad, 0x5c, 0x83, 0x43, 0x00, 0x81, 0x43, + 0x04, 0x87, 0x43, 0x00, 0x82, 0x43, 0x00, 0x9c, + 0x43, 0x01, 0x82, 0x43, 0x03, 0x89, 0x43, 0x06, + 0x88, 0x43, 0x06, 0x9f, 0x75, 0x9f, 0x71, 0x1f, + 0xa6, 0x56, 0x03, 0x8b, 0x56, 0x08, 0xb5, 0x06, + 0x02, 0x86, 0x06, 0x95, 0x3c, 0x01, 0x87, 0x3c, + 0x92, 0x3b, 0x04, 0x87, 0x3b, 0x91, 0x80, 0x06, + 0x83, 0x80, 0x0b, 0x86, 0x80, 0x4f, 0xc8, 0x76, + 0x36, 0xb2, 0x6f, 0x0c, 0xb2, 0x6f, 0x06, 0x85, + 0x6f, 0xa7, 0x34, 0x07, 0x89, 0x34, 0x05, 0xa5, + 0x28, 0x02, 0x9c, 0x28, 0x07, 0x81, 0x28, 0x60, + 0x6f, 0x9e, 0x04, 0x00, 0xa9, 0xa8, 0x00, 0x82, + 0xa8, 0x01, 0x81, 0xa8, 0x0f, 0x82, 0x04, 0x36, + 0x83, 0x04, 0xa7, 0x74, 0x07, 0xa9, 0x8a, 0x15, + 0x99, 0x77, 0x25, 0x9b, 0x18, 0x13, 0x96, 0x26, + 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08, 0x80, + 0x0e, 0xc2, 0x3e, 0x09, 0x80, 0x3e, 0x01, 0x98, + 0x8b, 0x06, 0x89, 0x8b, 0x05, 0xb4, 0x15, 0x00, + 0x91, 0x15, 0x07, 0xa6, 0x53, 0x08, 0xdf, 0x85, + 0x00, 0x93, 0x89, 0x0a, 0x91, 0x45, 0x00, 0xae, + 0x45, 0x3d, 0x86, 0x62, 0x00, 0x80, 0x62, 0x00, + 0x83, 0x62, 0x00, 0x8e, 0x62, 0x00, 0x8a, 0x62, + 0x05, 0xba, 0x47, 0x04, 0x89, 0x47, 0x05, 0x83, + 0x2c, 0x00, 0x87, 0x2c, 0x01, 0x81, 0x2c, 0x01, + 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, + 0x00, 0x84, 0x2c, 0x00, 0x80, 0x3a, 0x88, 0x2c, + 0x01, 0x81, 0x2c, 0x01, 0x82, 0x2c, 0x01, 0x80, + 0x2c, 0x05, 0x80, 0x2c, 0x04, 0x86, 0x2c, 0x01, + 0x86, 0x2c, 0x02, 0x84, 0x2c, 0x0a, 0x89, 0xa2, + 0x00, 0x80, 0xa2, 0x01, 0x80, 0xa2, 0x00, 0xa5, + 0xa2, 0x00, 0x89, 0xa2, 0x00, 0x80, 0xa2, 0x01, + 0x80, 0xa2, 0x00, 0x83, 0xa2, 0x00, 0x89, 0xa2, + 0x00, 0x81, 0xa2, 0x07, 0x81, 0xa2, 0x1c, 0xdb, + 0x68, 0x00, 0x84, 0x68, 0x1d, 0xc7, 0x9e, 0x07, + 0x89, 0x9e, 0x60, 0x45, 0xb5, 0x87, 0x01, 0xa5, + 0x87, 0x21, 0xc4, 0x5f, 0x0a, 0x89, 0x5f, 0x05, + 0x8c, 0x60, 0x12, 0xb9, 0x96, 0x05, 0x89, 0x96, + 0x05, 0x93, 0x63, 0x1b, 0x9a, 0x02, 0x01, 0x8e, 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, - 0x60, 0x03, 0xd2, 0xa0, 0x0b, 0x80, 0xa0, 0x86, + 0x60, 0x03, 0xd2, 0xa7, 0x0b, 0x80, 0xa7, 0x86, 0x21, 0x01, 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, 0x81, 0x21, 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, 0x01, 0x8b, 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, - 0x63, 0x01, 0xad, 0x63, 0x01, 0x8a, 0x63, 0x1a, - 0xc7, 0xa3, 0x07, 0xd2, 0x88, 0x0c, 0x8f, 0x12, - 0xb8, 0x79, 0x06, 0x89, 0x20, 0x60, 0x95, 0x88, - 0x0c, 0x00, 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, - 0x9c, 0x0c, 0x02, 0x9f, 0x54, 0x01, 0x95, 0x54, - 0x00, 0x8d, 0x54, 0x48, 0x86, 0x55, 0x00, 0x81, - 0x55, 0x00, 0xab, 0x55, 0x02, 0x80, 0x55, 0x00, - 0x81, 0x55, 0x00, 0x88, 0x55, 0x07, 0x89, 0x55, - 0x05, 0x85, 0x2e, 0x00, 0x81, 0x2e, 0x00, 0xa4, - 0x2e, 0x00, 0x81, 0x2e, 0x00, 0x85, 0x2e, 0x06, - 0x89, 0x2e, 0x60, 0xd5, 0x98, 0x4f, 0x06, 0x90, - 0x3f, 0x00, 0xa8, 0x3f, 0x02, 0x9b, 0x3f, 0x55, - 0x80, 0x4c, 0x0e, 0xb1, 0x92, 0x0c, 0x80, 0x92, - 0xe3, 0x39, 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, - 0x00, 0x84, 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, - 0xeb, 0xe0, 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, - 0x6f, 0x49, 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, - 0xe1, 0xd8, 0x08, 0x06, 0x9e, 0x5e, 0x00, 0x89, - 0x5e, 0x03, 0x81, 0x5e, 0xce, 0x9a, 0x00, 0x89, - 0x9a, 0x05, 0x9d, 0x09, 0x01, 0x85, 0x09, 0x09, - 0xc5, 0x77, 0x09, 0x89, 0x77, 0x00, 0x86, 0x77, - 0x00, 0x94, 0x77, 0x04, 0x92, 0x77, 0x62, 0x4f, - 0xda, 0x56, 0x60, 0x04, 0xca, 0x5b, 0x03, 0xb8, - 0x5b, 0x06, 0x90, 0x5b, 0x3f, 0x80, 0x93, 0x80, - 0x67, 0x81, 0x30, 0x80, 0x44, 0x0a, 0x81, 0x30, - 0x0d, 0xf0, 0x07, 0x97, 0x93, 0x07, 0xe2, 0x9f, - 0x93, 0xe1, 0x75, 0x44, 0x29, 0x88, 0x93, 0x70, - 0x12, 0x86, 0x83, 0x3e, 0x00, 0x86, 0x3e, 0x00, - 0x81, 0x3e, 0x00, 0x80, 0x3e, 0xe0, 0xbe, 0x36, - 0x82, 0x3e, 0x0e, 0x80, 0x36, 0x1c, 0x82, 0x36, - 0x01, 0x80, 0x3e, 0x0d, 0x83, 0x3e, 0x07, 0xe1, - 0x2b, 0x67, 0x68, 0xa3, 0xe0, 0x0a, 0x23, 0x04, - 0x8c, 0x23, 0x02, 0x88, 0x23, 0x06, 0x89, 0x23, - 0x01, 0x83, 0x23, 0x83, 0x19, 0x70, 0x01, 0xfb, - 0xad, 0x38, 0x01, 0x96, 0x38, 0x08, 0xe0, 0x13, - 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, 0xa6, 0x19, - 0x01, 0xbd, 0x19, 0x82, 0x38, 0x90, 0x19, 0x87, - 0x38, 0x81, 0x19, 0x86, 0x38, 0x9d, 0x19, 0x83, - 0x38, 0xbc, 0x19, 0x14, 0xc5, 0x2c, 0x60, 0x19, - 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, 0xd6, 0x19, - 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, 0x19, 0x00, - 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, 0x80, 0x19, - 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, 0x00, 0x8b, - 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, 0x19, 0x00, - 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, 0x87, 0x19, - 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, 0x00, 0x83, - 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, 0x19, 0x02, - 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, 0x01, 0xe0, - 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, 0x2b, 0x84, - 0x0e, 0x84, 0x84, 0x00, 0x8e, 0x84, 0x63, 0xef, - 0x9e, 0x47, 0x05, 0x85, 0x47, 0x60, 0x74, 0x86, - 0x29, 0x00, 0x90, 0x29, 0x01, 0x86, 0x29, 0x00, - 0x81, 0x29, 0x00, 0x84, 0x29, 0x04, 0xbd, 0x1d, - 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, 0x68, 0x02, - 0x8d, 0x68, 0x01, 0x89, 0x68, 0x03, 0x81, 0x68, - 0x60, 0xdf, 0x9e, 0x9b, 0x10, 0xb9, 0x9f, 0x04, - 0x80, 0x9f, 0x61, 0x6f, 0xa9, 0x62, 0x62, 0x85, - 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, 0x27, - 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x58, 0x01, - 0x8f, 0x58, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, - 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, 0x4b, - 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, 0x9a, - 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01, - 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, 0x04, - 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, 0x80, - 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, - 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, 0x04, - 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x80, - 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, - 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, - 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, 0x83, - 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00, - 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, 0x04, - 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, 0x81, - 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, 0x03, - 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, 0x00, - 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, 0x4d, - 0x19, 0x37, 0x99, 0x19, 0x80, 0x36, 0x81, 0x19, - 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, 0x81, - 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, 0x77, - 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, 0x02, - 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, 0x8b, - 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, 0x03, - 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, 0x19, - 0x07, 0x9d, 0x19, 0x01, 0x81, 0x19, 0x4d, 0xe0, - 0xf3, 0x19, 0x0b, 0x8d, 0x19, 0x01, 0x8c, 0x19, - 0x02, 0x88, 0x19, 0x06, 0xad, 0x19, 0x00, 0x86, - 0x19, 0x07, 0x8d, 0x19, 0x03, 0x88, 0x19, 0x06, - 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, 0xb6, - 0x19, 0x24, 0x89, 0x19, 0x63, 0xa5, 0xf0, 0x96, - 0x7f, 0x30, 0x1f, 0xef, 0xd9, 0x30, 0x05, 0xe0, - 0x7d, 0x30, 0x01, 0xf0, 0x06, 0x21, 0x30, 0x0d, - 0xf0, 0x0c, 0xd0, 0x30, 0x6b, 0xbe, 0xe1, 0xbd, - 0x30, 0x65, 0x81, 0xf0, 0x02, 0xea, 0x30, 0x04, - 0xef, 0xff, 0x30, 0x7a, 0xcb, 0xf0, 0x80, 0x19, - 0x1d, 0xdf, 0x19, 0x60, 0x1f, 0xe0, 0x8f, 0x38, + 0x66, 0x01, 0xad, 0x66, 0x01, 0x8a, 0x66, 0x1a, + 0xc7, 0xaa, 0x07, 0xd2, 0x8c, 0x0c, 0x8f, 0x12, + 0xb8, 0x7d, 0x06, 0x89, 0x20, 0x60, 0x55, 0xa1, + 0x8e, 0x0d, 0x89, 0x8e, 0x05, 0x88, 0x0c, 0x00, + 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, + 0x02, 0x9f, 0x57, 0x01, 0x95, 0x57, 0x00, 0x8d, + 0x57, 0x48, 0x86, 0x58, 0x00, 0x81, 0x58, 0x00, + 0xab, 0x58, 0x02, 0x80, 0x58, 0x00, 0x81, 0x58, + 0x00, 0x88, 0x58, 0x07, 0x89, 0x58, 0x05, 0x85, + 0x2f, 0x00, 0x81, 0x2f, 0x00, 0xa4, 0x2f, 0x00, + 0x81, 0x2f, 0x00, 0x85, 0x2f, 0x06, 0x89, 0x2f, + 0x60, 0xd5, 0x98, 0x52, 0x06, 0x90, 0x41, 0x00, + 0xa8, 0x41, 0x02, 0x9c, 0x41, 0x54, 0x80, 0x4f, + 0x0e, 0xb1, 0x97, 0x0c, 0x80, 0x97, 0xe3, 0x39, + 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, 0x00, 0x84, + 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, 0xeb, 0xe0, + 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, 0x09, 0xef, + 0x3a, 0x24, 0x04, 0xe1, 0xe6, 0x03, 0x70, 0x0a, + 0x58, 0xb9, 0x31, 0x66, 0x65, 0xe1, 0xd8, 0x08, + 0x06, 0x9e, 0x61, 0x00, 0x89, 0x61, 0x03, 0x81, + 0x61, 0xce, 0x9f, 0x00, 0x89, 0x9f, 0x05, 0x9d, + 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x7b, 0x09, + 0x89, 0x7b, 0x00, 0x86, 0x7b, 0x00, 0x94, 0x7b, + 0x04, 0x92, 0x7b, 0x61, 0x4f, 0xb9, 0x48, 0x60, + 0x65, 0xda, 0x59, 0x60, 0x04, 0xca, 0x5e, 0x03, + 0xb8, 0x5e, 0x06, 0x90, 0x5e, 0x3f, 0x80, 0x98, + 0x80, 0x6a, 0x81, 0x32, 0x80, 0x46, 0x0a, 0x81, + 0x32, 0x0d, 0xf0, 0x07, 0x97, 0x98, 0x07, 0xe2, + 0x9f, 0x98, 0xe1, 0x75, 0x46, 0x28, 0x80, 0x46, + 0x88, 0x98, 0x70, 0x12, 0x86, 0x83, 0x40, 0x00, + 0x86, 0x40, 0x00, 0x81, 0x40, 0x00, 0x80, 0x40, + 0xe0, 0xbe, 0x38, 0x82, 0x40, 0x0e, 0x80, 0x38, + 0x1c, 0x82, 0x38, 0x01, 0x80, 0x40, 0x0d, 0x83, + 0x40, 0x07, 0xe1, 0x2b, 0x6a, 0x68, 0xa3, 0xe0, + 0x0a, 0x23, 0x04, 0x8c, 0x23, 0x02, 0x88, 0x23, + 0x06, 0x89, 0x23, 0x01, 0x83, 0x23, 0x83, 0x19, + 0x6e, 0xfb, 0xe0, 0x99, 0x19, 0x05, 0xe1, 0x53, + 0x19, 0x4b, 0xad, 0x3a, 0x01, 0x96, 0x3a, 0x08, + 0xe0, 0x13, 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, + 0xa6, 0x19, 0x01, 0xbd, 0x19, 0x82, 0x3a, 0x90, + 0x19, 0x87, 0x3a, 0x81, 0x19, 0x86, 0x3a, 0x9d, + 0x19, 0x83, 0x3a, 0xbc, 0x19, 0x14, 0xc5, 0x2d, + 0x60, 0x19, 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, + 0xd6, 0x19, 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, + 0x19, 0x00, 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, + 0x80, 0x19, 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, + 0x00, 0x8b, 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, + 0x19, 0x00, 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, + 0x87, 0x19, 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, + 0x00, 0x83, 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, + 0x19, 0x02, 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, + 0x01, 0xe0, 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, + 0x2b, 0x88, 0x0e, 0x84, 0x88, 0x00, 0x8e, 0x88, + 0x63, 0xef, 0x9e, 0x4a, 0x05, 0x85, 0x4a, 0x60, + 0x74, 0x86, 0x2a, 0x00, 0x90, 0x2a, 0x01, 0x86, + 0x2a, 0x00, 0x81, 0x2a, 0x00, 0x84, 0x2a, 0x04, + 0xbd, 0x1d, 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, + 0x6b, 0x02, 0x8d, 0x6b, 0x01, 0x89, 0x6b, 0x03, + 0x81, 0x6b, 0x60, 0xdf, 0x9e, 0xa1, 0x10, 0xb9, + 0xa6, 0x04, 0x80, 0xa6, 0x61, 0x6f, 0xa9, 0x65, + 0x60, 0x75, 0xaa, 0x6e, 0x03, 0x80, 0x6e, 0x61, + 0x7f, 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, + 0x27, 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x5b, + 0x01, 0x8f, 0x5b, 0x28, 0xcb, 0x01, 0x03, 0x89, + 0x01, 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, + 0x4b, 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, + 0x9a, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, + 0x01, 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, + 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, + 0x80, 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, + 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, + 0x04, 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, + 0x83, 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, + 0x04, 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, + 0x81, 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, + 0x03, 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, + 0x00, 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, + 0x4d, 0x19, 0x37, 0x99, 0x19, 0x80, 0x38, 0x81, + 0x19, 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, + 0x81, 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, + 0x77, 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, + 0x02, 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, + 0x8b, 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, + 0x03, 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, + 0x19, 0x07, 0x9d, 0x19, 0x01, 0x8b, 0x19, 0x03, + 0x81, 0x19, 0x3d, 0xe0, 0xf3, 0x19, 0x0b, 0x8d, + 0x19, 0x01, 0x8c, 0x19, 0x02, 0x89, 0x19, 0x04, + 0xb7, 0x19, 0x06, 0x8e, 0x19, 0x01, 0x8a, 0x19, + 0x05, 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, + 0xe0, 0x05, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, + 0x32, 0x1f, 0xef, 0xd9, 0x32, 0x05, 0xe0, 0x7d, + 0x32, 0x01, 0xf0, 0x06, 0x21, 0x32, 0x0d, 0xf0, + 0x0c, 0xd0, 0x32, 0x0e, 0xe2, 0x0d, 0x32, 0x69, + 0x41, 0xe1, 0xbd, 0x32, 0x65, 0x81, 0xf0, 0x02, + 0xea, 0x32, 0x04, 0xef, 0xff, 0x32, 0x7a, 0xcb, + 0xf0, 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, + 0xe0, 0x8f, 0x3a, }; -static const uint8_t unicode_script_ext_table[828] = { - 0x82, 0xc1, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x00, - 0x00, 0x01, 0x2c, 0x1c, 0x00, 0x0c, 0x01, 0x47, - 0x80, 0x92, 0x00, 0x00, 0x02, 0x1d, 0x6e, 0x00, - 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x47, 0x00, - 0x02, 0x1d, 0x29, 0x81, 0x03, 0x00, 0x00, 0x06, - 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, 0x0d, 0x00, - 0x00, 0x06, 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, - 0x00, 0x03, 0x04, 0x8b, 0x95, 0x01, 0x00, 0x00, - 0x07, 0x01, 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, - 0x1f, 0x00, 0x00, 0x09, 0x01, 0x04, 0x52, 0x53, - 0x73, 0x7c, 0x32, 0x86, 0x8b, 0x09, 0x00, 0x0a, - 0x02, 0x04, 0x8b, 0x09, 0x00, 0x09, 0x03, 0x04, - 0x95, 0xa1, 0x05, 0x00, 0x00, 0x02, 0x04, 0x8b, - 0x62, 0x00, 0x00, 0x02, 0x04, 0x32, 0x81, 0xfb, - 0x00, 0x00, 0x0d, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, - 0x3d, 0x47, 0x51, 0x74, 0x81, 0x92, 0x94, 0x99, - 0x00, 0x0c, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, 0x3d, - 0x47, 0x51, 0x74, 0x92, 0x94, 0x99, 0x10, 0x00, - 0x00, 0x14, 0x0b, 0x20, 0x22, 0x2e, 0x55, 0x2b, - 0x2d, 0x2f, 0x3d, 0x50, 0x51, 0x63, 0x74, 0x45, - 0x85, 0x8a, 0x91, 0x92, 0x94, 0x99, 0x00, 0x15, - 0x0b, 0x20, 0x22, 0x2e, 0x55, 0x2b, 0x2d, 0x2f, - 0x3d, 0x49, 0x50, 0x51, 0x63, 0x74, 0x45, 0x85, - 0x8a, 0x91, 0x92, 0x94, 0x99, 0x09, 0x04, 0x20, - 0x22, 0x3c, 0x50, 0x75, 0x00, 0x09, 0x03, 0x0b, - 0x15, 0x8a, 0x75, 0x00, 0x09, 0x02, 0x2f, 0x5f, - 0x75, 0x00, 0x09, 0x02, 0x2d, 0x43, 0x80, 0x75, - 0x00, 0x0d, 0x02, 0x2b, 0x92, 0x80, 0x71, 0x00, - 0x09, 0x02, 0x3d, 0x63, 0x82, 0xcf, 0x00, 0x09, - 0x03, 0x15, 0x60, 0x8e, 0x80, 0x30, 0x00, 0x00, - 0x02, 0x28, 0x47, 0x85, 0xb8, 0x00, 0x01, 0x04, - 0x11, 0x33, 0x8d, 0x8c, 0x80, 0x4a, 0x00, 0x01, - 0x02, 0x5d, 0x7a, 0x00, 0x00, 0x00, 0x02, 0x5d, - 0x7a, 0x84, 0x49, 0x00, 0x00, 0x04, 0x0b, 0x20, - 0x2b, 0x3d, 0x00, 0x01, 0x20, 0x00, 0x04, 0x0b, - 0x20, 0x2b, 0x3d, 0x00, 0x02, 0x20, 0x2b, 0x00, - 0x01, 0x20, 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, - 0x20, 0x81, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, - 0x20, 0x81, 0x00, 0x06, 0x20, 0x3d, 0x51, 0x74, - 0x92, 0x94, 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, - 0x81, 0x01, 0x01, 0x20, 0x00, 0x02, 0x20, 0x81, - 0x00, 0x02, 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, - 0x02, 0x20, 0x63, 0x00, 0x02, 0x0b, 0x20, 0x01, - 0x01, 0x20, 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, - 0x20, 0x00, 0x08, 0x0b, 0x20, 0x2b, 0x3d, 0x63, - 0x74, 0x94, 0x99, 0x00, 0x02, 0x20, 0x2b, 0x00, - 0x03, 0x20, 0x2b, 0x3d, 0x01, 0x02, 0x0b, 0x20, - 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, 0x2b, 0x00, - 0x01, 0x63, 0x80, 0x44, 0x00, 0x01, 0x01, 0x2c, - 0x35, 0x00, 0x00, 0x02, 0x1d, 0x8b, 0x00, 0x00, - 0x00, 0x01, 0x8b, 0x81, 0xb3, 0x00, 0x00, 0x02, - 0x47, 0x5d, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x20, - 0x2b, 0x47, 0x8c, 0xd1, 0x00, 0x00, 0x02, 0x1d, - 0x29, 0x81, 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x31, - 0x30, 0x36, 0x3e, 0xa2, 0x00, 0x05, 0x0d, 0x31, - 0x30, 0x36, 0x3e, 0x01, 0x00, 0x00, 0x01, 0x30, - 0x00, 0x00, 0x09, 0x06, 0x0d, 0x31, 0x30, 0x36, - 0x3e, 0xa2, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x31, - 0x30, 0x36, 0x3e, 0x07, 0x06, 0x0d, 0x31, 0x30, - 0x36, 0x3e, 0xa2, 0x03, 0x05, 0x0d, 0x31, 0x30, - 0x36, 0x3e, 0x09, 0x00, 0x03, 0x02, 0x0d, 0x30, - 0x01, 0x00, 0x00, 0x05, 0x0d, 0x31, 0x30, 0x36, - 0x3e, 0x04, 0x02, 0x36, 0x3e, 0x00, 0x00, 0x00, - 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x03, 0x00, - 0x01, 0x03, 0x30, 0x36, 0x3e, 0x01, 0x01, 0x30, - 0x58, 0x00, 0x03, 0x02, 0x36, 0x3e, 0x02, 0x00, - 0x00, 0x02, 0x36, 0x3e, 0x59, 0x00, 0x00, 0x06, - 0x0d, 0x31, 0x30, 0x36, 0x3e, 0xa2, 0x00, 0x02, - 0x36, 0x3e, 0x80, 0x12, 0x00, 0x0f, 0x01, 0x30, - 0x1f, 0x00, 0x23, 0x01, 0x30, 0x3b, 0x00, 0x27, - 0x01, 0x30, 0x37, 0x00, 0x30, 0x01, 0x30, 0x0e, - 0x00, 0x0b, 0x01, 0x30, 0x32, 0x00, 0x00, 0x01, - 0x30, 0x57, 0x00, 0x18, 0x01, 0x30, 0x09, 0x00, - 0x04, 0x01, 0x30, 0x5f, 0x00, 0x1e, 0x01, 0x30, - 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, 0x29, - 0x80, 0x0f, 0x00, 0x07, 0x02, 0x30, 0x47, 0x80, - 0xa7, 0x00, 0x02, 0x0e, 0x20, 0x22, 0x2d, 0x2f, - 0x43, 0x3d, 0x3c, 0x50, 0x51, 0x5c, 0x63, 0x45, - 0x91, 0x99, 0x02, 0x0d, 0x20, 0x22, 0x2d, 0x2f, - 0x43, 0x3d, 0x3c, 0x50, 0x5c, 0x63, 0x45, 0x91, - 0x99, 0x03, 0x0b, 0x20, 0x22, 0x2d, 0x2f, 0x43, - 0x3c, 0x50, 0x5c, 0x45, 0x91, 0x99, 0x80, 0x36, - 0x00, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x00, 0x00, - 0x02, 0x20, 0x92, 0x39, 0x00, 0x00, 0x03, 0x40, - 0x47, 0x60, 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10, - 0x3b, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, 0x04, - 0x66, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, 0x95, - 0x09, 0x00, 0x00, 0x02, 0x04, 0x95, 0x46, 0x00, - 0x01, 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x80, - 0x99, 0x00, 0x04, 0x06, 0x0d, 0x31, 0x30, 0x36, - 0x3e, 0xa2, 0x09, 0x00, 0x00, 0x02, 0x36, 0x3e, - 0x2c, 0x00, 0x01, 0x02, 0x36, 0x3e, 0x80, 0xdf, - 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4b, 0x00, 0x02, - 0x1c, 0x4b, 0x03, 0x00, 0x2c, 0x03, 0x1c, 0x4a, - 0x4b, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4b, 0x81, - 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, 0x75, - 0x00, 0x00, 0x02, 0x53, 0x73, 0x87, 0x8d, 0x00, - 0x00, 0x02, 0x2b, 0x92, 0x00, 0x00, 0x00, 0x02, - 0x2b, 0x92, 0x36, 0x00, 0x01, 0x02, 0x2b, 0x92, - 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2b, 0x92, 0x00, - 0x00, 0x00, 0x02, 0x2b, 0x92, 0xc0, 0x5c, 0x4b, - 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, 0x11, - 0x01, 0x30, 0x9e, 0x5d, 0x00, 0x01, 0x01, 0x30, - 0xce, 0xcd, 0x2d, 0x00, +static const uint8_t unicode_script_ext_table[1253] = { + 0x80, 0x36, 0x00, 0x00, 0x10, 0x06, 0x13, 0x1a, + 0x23, 0x25, 0x29, 0x2a, 0x2f, 0x2b, 0x2d, 0x32, + 0x4a, 0x51, 0x53, 0x72, 0x86, 0x81, 0x83, 0x00, + 0x00, 0x07, 0x0b, 0x1d, 0x20, 0x4a, 0x4f, 0x9b, + 0xa1, 0x09, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x00, + 0x00, 0x02, 0x02, 0x0d, 0x4a, 0x00, 0x00, 0x00, + 0x02, 0x4a, 0x4f, 0x08, 0x00, 0x00, 0x02, 0x4a, + 0x9b, 0x00, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x25, + 0x00, 0x00, 0x08, 0x17, 0x1a, 0x1d, 0x2d, 0x4a, + 0x72, 0x8e, 0x93, 0x00, 0x08, 0x17, 0x1d, 0x2d, + 0x4a, 0x79, 0x8e, 0x93, 0xa0, 0x00, 0x04, 0x17, + 0x1d, 0x4a, 0x9d, 0x00, 0x05, 0x2a, 0x4a, 0x8e, + 0x90, 0x9b, 0x00, 0x0b, 0x14, 0x17, 0x1a, 0x1d, + 0x2b, 0x2d, 0x4a, 0x79, 0x90, 0x9d, 0xa0, 0x00, + 0x06, 0x1a, 0x25, 0x2a, 0x2b, 0x40, 0x4a, 0x00, + 0x04, 0x1d, 0x2d, 0x4a, 0x72, 0x00, 0x09, 0x1a, + 0x23, 0x37, 0x4a, 0x72, 0x90, 0x93, 0x9d, 0xa0, + 0x00, 0x0a, 0x05, 0x1d, 0x23, 0x2b, 0x2d, 0x37, + 0x4a, 0x72, 0x90, 0x93, 0x00, 0x02, 0x4a, 0x9d, + 0x00, 0x03, 0x23, 0x4a, 0x90, 0x00, 0x04, 0x17, + 0x1d, 0x4a, 0x79, 0x00, 0x03, 0x17, 0x4a, 0x93, + 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x02, 0x27, 0x4a, + 0x00, 0x00, 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x03, + 0x1d, 0x4a, 0xa0, 0x00, 0x00, 0x00, 0x04, 0x2d, + 0x4a, 0x72, 0xa0, 0x0b, 0x00, 0x00, 0x02, 0x4a, + 0x90, 0x01, 0x00, 0x00, 0x05, 0x17, 0x23, 0x40, + 0x4a, 0x90, 0x00, 0x04, 0x17, 0x23, 0x4a, 0x90, + 0x00, 0x02, 0x4a, 0x90, 0x06, 0x00, 0x00, 0x03, + 0x4a, 0x8e, 0x90, 0x00, 0x02, 0x4a, 0x90, 0x00, + 0x00, 0x00, 0x03, 0x17, 0x4a, 0x90, 0x00, 0x06, + 0x14, 0x17, 0x2b, 0x4a, 0x8e, 0x9b, 0x0f, 0x00, + 0x00, 0x01, 0x2d, 0x01, 0x00, 0x00, 0x01, 0x2d, + 0x11, 0x00, 0x00, 0x02, 0x4a, 0x79, 0x04, 0x00, + 0x00, 0x03, 0x14, 0x4a, 0xa0, 0x03, 0x00, 0x0c, + 0x01, 0x4a, 0x03, 0x00, 0x01, 0x02, 0x1a, 0x2d, + 0x80, 0x8c, 0x00, 0x00, 0x02, 0x1d, 0x72, 0x00, + 0x02, 0x1d, 0x2a, 0x01, 0x02, 0x1d, 0x4a, 0x00, + 0x02, 0x1d, 0x2a, 0x80, 0x80, 0x00, 0x00, 0x03, + 0x05, 0x29, 0x2a, 0x80, 0x01, 0x00, 0x00, 0x07, + 0x04, 0x28, 0x69, 0x34, 0x90, 0x9a, 0xa8, 0x0d, + 0x00, 0x00, 0x07, 0x04, 0x28, 0x69, 0x34, 0x90, + 0x9a, 0xa8, 0x00, 0x03, 0x04, 0x90, 0x9a, 0x01, + 0x00, 0x00, 0x08, 0x01, 0x04, 0x28, 0x69, 0x34, + 0x90, 0x9a, 0xa8, 0x1f, 0x00, 0x00, 0x09, 0x01, + 0x04, 0x55, 0x56, 0x77, 0x80, 0x34, 0x8a, 0x90, + 0x09, 0x00, 0x0a, 0x02, 0x04, 0x90, 0x09, 0x00, + 0x09, 0x03, 0x04, 0x9a, 0xa8, 0x05, 0x00, 0x00, + 0x02, 0x04, 0x90, 0x62, 0x00, 0x00, 0x02, 0x04, + 0x34, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x20, + 0x2c, 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x85, + 0x97, 0x99, 0x9e, 0x00, 0x0c, 0x0b, 0x20, 0x2c, + 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x97, 0x99, + 0x9e, 0x10, 0x00, 0x00, 0x15, 0x0b, 0x20, 0x22, + 0x2f, 0x58, 0x2c, 0x2e, 0x30, 0x3f, 0x53, 0x54, + 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, 0x97, + 0x99, 0x9e, 0x00, 0x17, 0x0b, 0x20, 0x22, 0x2f, + 0x58, 0x2c, 0x2e, 0x31, 0x30, 0x3f, 0x4c, 0x53, + 0x54, 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, + 0x97, 0x99, 0x9e, 0x09, 0x04, 0x20, 0x22, 0x3e, + 0x53, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x8f, + 0x75, 0x00, 0x09, 0x02, 0x30, 0x62, 0x75, 0x00, + 0x09, 0x02, 0x2e, 0x45, 0x80, 0x75, 0x00, 0x0d, + 0x02, 0x2c, 0x97, 0x80, 0x71, 0x00, 0x09, 0x03, + 0x3f, 0x66, 0xa2, 0x82, 0xcf, 0x00, 0x09, 0x03, + 0x15, 0x63, 0x93, 0x80, 0x30, 0x00, 0x00, 0x03, + 0x29, 0x2a, 0x4a, 0x85, 0x6e, 0x00, 0x02, 0x01, + 0x82, 0x46, 0x00, 0x01, 0x04, 0x11, 0x35, 0x92, + 0x91, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x60, 0x7e, + 0x00, 0x00, 0x00, 0x02, 0x60, 0x7e, 0x84, 0x49, + 0x00, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, 0x00, + 0x01, 0x20, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, + 0x00, 0x03, 0x20, 0x2c, 0x3f, 0x00, 0x01, 0x20, + 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, + 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, + 0x00, 0x06, 0x20, 0x3f, 0x54, 0x78, 0x97, 0x99, + 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, 0x85, 0x01, + 0x01, 0x20, 0x00, 0x02, 0x20, 0x85, 0x00, 0x02, + 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, 0x02, 0x20, + 0x66, 0x00, 0x02, 0x0b, 0x20, 0x01, 0x01, 0x20, + 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, 0x20, 0x00, + 0x0b, 0x0b, 0x20, 0x2c, 0x3f, 0x54, 0x66, 0x78, + 0x89, 0x99, 0x9e, 0xa2, 0x00, 0x02, 0x20, 0x2c, + 0x00, 0x04, 0x20, 0x2c, 0x3f, 0xa2, 0x01, 0x02, + 0x0b, 0x20, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, + 0x2c, 0x00, 0x01, 0x66, 0x80, 0x44, 0x00, 0x01, + 0x01, 0x2d, 0x35, 0x00, 0x00, 0x03, 0x1d, 0x4a, + 0x90, 0x00, 0x00, 0x00, 0x01, 0x90, 0x81, 0xb3, + 0x00, 0x00, 0x03, 0x4a, 0x60, 0x7e, 0x1e, 0x00, + 0x00, 0x02, 0x01, 0x04, 0x09, 0x00, 0x00, 0x06, + 0x13, 0x29, 0x2a, 0x6f, 0x50, 0x76, 0x01, 0x00, + 0x00, 0x04, 0x13, 0x2d, 0x6f, 0x5d, 0x80, 0x11, + 0x00, 0x00, 0x03, 0x20, 0x2c, 0x4a, 0x8c, 0xa5, + 0x00, 0x00, 0x02, 0x1a, 0x4a, 0x17, 0x00, 0x00, + 0x02, 0x06, 0x76, 0x00, 0x07, 0x06, 0x13, 0x29, + 0x6f, 0x3e, 0x51, 0x83, 0x09, 0x00, 0x00, 0x01, + 0x23, 0x03, 0x00, 0x00, 0x03, 0x01, 0x04, 0x6f, + 0x00, 0x00, 0x00, 0x02, 0x1d, 0x2a, 0x81, 0x2b, + 0x00, 0x0f, 0x02, 0x32, 0x98, 0x00, 0x00, 0x00, + 0x07, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, 0xa9, + 0x00, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, + 0x7e, 0xa9, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x01, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, + 0x01, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, + 0x9c, 0xa9, 0x01, 0x09, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x4f, 0x60, 0x9c, 0xa9, 0x05, 0x06, 0x0d, + 0x33, 0x32, 0x38, 0x40, 0xa9, 0x00, 0x00, 0x00, + 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x07, 0x06, + 0x0d, 0x33, 0x32, 0x38, 0x40, 0xa9, 0x03, 0x05, + 0x0d, 0x33, 0x32, 0x38, 0x40, 0x09, 0x00, 0x03, + 0x02, 0x0d, 0x32, 0x01, 0x00, 0x00, 0x05, 0x0d, + 0x33, 0x32, 0x38, 0x40, 0x04, 0x02, 0x38, 0x40, + 0x00, 0x00, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x03, 0x00, 0x01, 0x03, 0x32, 0x38, 0x40, + 0x01, 0x01, 0x32, 0x58, 0x00, 0x03, 0x02, 0x38, + 0x40, 0x02, 0x00, 0x00, 0x02, 0x38, 0x40, 0x59, + 0x00, 0x00, 0x06, 0x0d, 0x33, 0x32, 0x38, 0x40, + 0xa9, 0x00, 0x02, 0x38, 0x40, 0x80, 0x12, 0x00, + 0x0f, 0x01, 0x32, 0x1f, 0x00, 0x25, 0x01, 0x32, + 0x08, 0x00, 0x00, 0x02, 0x32, 0x98, 0x2f, 0x00, + 0x27, 0x01, 0x32, 0x37, 0x00, 0x30, 0x01, 0x32, + 0x0e, 0x00, 0x0b, 0x01, 0x32, 0x32, 0x00, 0x00, + 0x01, 0x32, 0x57, 0x00, 0x18, 0x01, 0x32, 0x09, + 0x00, 0x04, 0x01, 0x32, 0x5f, 0x00, 0x1e, 0x01, + 0x32, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, + 0x2a, 0x80, 0x0f, 0x00, 0x07, 0x02, 0x32, 0x4a, + 0x80, 0xa7, 0x00, 0x02, 0x10, 0x20, 0x22, 0x2e, + 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x54, 0x5f, 0x66, + 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x02, 0x0f, 0x20, + 0x22, 0x2e, 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x5f, + 0x66, 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x01, 0x0b, + 0x20, 0x22, 0x2e, 0x30, 0x45, 0x3e, 0x53, 0x5f, + 0x47, 0x96, 0x9e, 0x00, 0x0c, 0x20, 0x22, 0x2e, + 0x30, 0x45, 0x3e, 0x53, 0x5f, 0x85, 0x47, 0x96, + 0x9e, 0x00, 0x0b, 0x20, 0x22, 0x2e, 0x30, 0x45, + 0x3e, 0x53, 0x5f, 0x47, 0x96, 0x9e, 0x80, 0x36, + 0x00, 0x00, 0x03, 0x0b, 0x20, 0xa2, 0x00, 0x00, + 0x00, 0x02, 0x20, 0x97, 0x39, 0x00, 0x00, 0x03, + 0x42, 0x4a, 0x63, 0x80, 0x1f, 0x00, 0x00, 0x02, + 0x10, 0x3d, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, + 0x04, 0x69, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, + 0x9a, 0x09, 0x00, 0x00, 0x02, 0x04, 0x9a, 0x46, + 0x00, 0x01, 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, + 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x33, 0x32, + 0x38, 0x40, 0xa9, 0x09, 0x00, 0x00, 0x02, 0x38, + 0x40, 0x2c, 0x00, 0x01, 0x02, 0x38, 0x40, 0x80, + 0xdf, 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4e, 0x00, + 0x02, 0x1c, 0x4e, 0x03, 0x00, 0x2c, 0x03, 0x1c, + 0x4d, 0x4e, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4e, + 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, + 0x75, 0x00, 0x00, 0x02, 0x56, 0x77, 0x87, 0x8d, + 0x00, 0x00, 0x02, 0x2c, 0x97, 0x00, 0x00, 0x00, + 0x02, 0x2c, 0x97, 0x36, 0x00, 0x01, 0x02, 0x2c, + 0x97, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2c, 0x97, + 0x00, 0x00, 0x00, 0x02, 0x2c, 0x97, 0xc0, 0x5c, + 0x4b, 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, + 0x11, 0x01, 0x32, 0x9e, 0x5d, 0x00, 0x01, 0x01, + 0x32, 0xce, 0xcd, 0x2d, 0x00, }; static const uint8_t unicode_prop_Hyphen_table[28] = { @@ -3651,61 +3852,63 @@ static const uint8_t unicode_prop_Other_Math_table[200] = { 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, }; -static const uint8_t unicode_prop_Other_Alphabetic_table[428] = { - 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01, - 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, 0x06, 0x8f, - 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80, - 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02, - 0x03, 0x40, 0xa6, 0x8b, 0x16, 0x85, 0x93, 0xb5, - 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82, - 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, - 0x9c, 0x82, 0xb9, 0x23, 0x09, 0x0b, 0x80, 0x9d, - 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81, - 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09, - 0x81, 0x88, 0x81, 0x89, 0x81, 0x9d, 0x80, 0xba, - 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x84, 0xb8, - 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, - 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x8e, - 0x80, 0x8b, 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, - 0x80, 0x89, 0x81, 0x9c, 0x82, 0xca, 0x28, 0x00, - 0x87, 0x91, 0x81, 0xbc, 0x01, 0x86, 0x91, 0x80, - 0xe2, 0x01, 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, - 0x92, 0x88, 0x8a, 0x80, 0xa3, 0xed, 0x8b, 0x00, - 0x0b, 0x96, 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, - 0x8b, 0x00, 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, - 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, - 0xbb, 0x81, 0xa1, 0x80, 0xf5, 0x8b, 0x83, 0x88, - 0x40, 0xdd, 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, - 0x81, 0x8a, 0x82, 0xb0, 0x84, 0xaf, 0x8e, 0xbb, - 0x82, 0x9d, 0x88, 0x09, 0xb8, 0x8a, 0xb1, 0x92, - 0x41, 0xaf, 0x8d, 0x46, 0xc0, 0xb3, 0x48, 0xf5, - 0x9f, 0x60, 0x78, 0x73, 0x87, 0xa1, 0x81, 0x41, - 0x61, 0x07, 0x80, 0x96, 0x84, 0xd7, 0x81, 0xb1, - 0x8f, 0x00, 0xb8, 0x80, 0xa5, 0x84, 0x9b, 0x8b, - 0xac, 0x83, 0xaf, 0x8b, 0xa4, 0x80, 0xc2, 0x8d, - 0x8b, 0x07, 0x81, 0xac, 0x82, 0xb1, 0x00, 0x11, - 0x0c, 0x80, 0xab, 0x24, 0x80, 0x40, 0xec, 0x87, - 0x60, 0x4f, 0x32, 0x80, 0x48, 0x56, 0x84, 0x46, - 0x85, 0x10, 0x0c, 0x83, 0x43, 0x13, 0x83, 0x41, - 0x82, 0x81, 0x41, 0x52, 0x82, 0xb4, 0x8d, 0xac, - 0x81, 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, - 0x82, 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, - 0x8c, 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, - 0x40, 0x9c, 0x89, 0x96, 0x83, 0xb9, 0x31, 0x09, - 0x81, 0x89, 0x80, 0x89, 0x81, 0x40, 0xd0, 0x8c, - 0x02, 0xe9, 0x91, 0x40, 0xec, 0x31, 0x86, 0x9c, - 0x81, 0xd1, 0x8e, 0x00, 0xe9, 0x8a, 0xe6, 0x8d, - 0x41, 0x00, 0x8c, 0x40, 0xf6, 0x28, 0x09, 0x0a, - 0x00, 0x80, 0x40, 0x8d, 0x31, 0x2b, 0x80, 0x9b, - 0x89, 0xa9, 0x20, 0x83, 0x91, 0x8a, 0xad, 0x8d, - 0x41, 0x96, 0x38, 0x86, 0xd2, 0x95, 0x80, 0x8d, - 0xf9, 0x2a, 0x00, 0x08, 0x10, 0x02, 0x80, 0xc1, - 0x20, 0x08, 0x83, 0x41, 0x5b, 0x83, 0x88, 0x08, - 0x80, 0xaf, 0x32, 0x82, 0x60, 0x50, 0x0d, 0x00, - 0xb6, 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, - 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, - 0xe3, 0x80, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, - 0x85, 0x99, 0x85, 0x99, +static const uint8_t unicode_prop_Other_Alphabetic_table[443] = { + 0x43, 0x44, 0x80, 0x9c, 0x8c, 0x42, 0x3f, 0x8d, + 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, + 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, + 0xa2, 0x80, 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, + 0x88, 0x02, 0x03, 0xe9, 0x80, 0xbb, 0x8b, 0x16, + 0x85, 0x93, 0xb5, 0x09, 0x8e, 0x01, 0x22, 0x89, + 0x81, 0x9c, 0x82, 0xb9, 0x31, 0x09, 0x81, 0x89, + 0x80, 0x89, 0x81, 0x9c, 0x82, 0xb9, 0x23, 0x09, + 0x0b, 0x80, 0x9d, 0x0a, 0x80, 0x8a, 0x82, 0xb9, + 0x38, 0x10, 0x81, 0x94, 0x81, 0x95, 0x13, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x88, 0x81, 0x89, 0x81, + 0x9d, 0x80, 0xba, 0x22, 0x10, 0x82, 0x89, 0x80, + 0xa7, 0x84, 0xb8, 0x30, 0x10, 0x17, 0x81, 0x8a, + 0x81, 0x9c, 0x82, 0xb9, 0x30, 0x10, 0x17, 0x81, + 0x8a, 0x81, 0x8e, 0x80, 0x8b, 0x83, 0xb9, 0x30, + 0x10, 0x82, 0x89, 0x80, 0x89, 0x81, 0x9c, 0x82, + 0xca, 0x28, 0x00, 0x87, 0x91, 0x81, 0xbc, 0x01, + 0x86, 0x91, 0x80, 0xe2, 0x01, 0x28, 0x81, 0x8f, + 0x80, 0x40, 0xa2, 0x92, 0x88, 0x8a, 0x80, 0xa3, + 0xed, 0x8b, 0x00, 0x0b, 0x96, 0x1b, 0x10, 0x11, + 0x32, 0x83, 0x8c, 0x8b, 0x00, 0x89, 0x83, 0x46, + 0x73, 0x81, 0x9d, 0x81, 0x9d, 0x81, 0x9d, 0x81, + 0xc1, 0x92, 0x40, 0xbb, 0x81, 0xa1, 0x80, 0xf5, + 0x8b, 0x83, 0x88, 0x40, 0xdd, 0x84, 0xb8, 0x89, + 0x81, 0x93, 0xc9, 0x81, 0x8a, 0x82, 0xb0, 0x84, + 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, 0xb8, + 0x8a, 0xb1, 0x92, 0x41, 0x9b, 0xa1, 0x46, 0xc0, + 0xb3, 0x48, 0xf5, 0x9f, 0x60, 0x78, 0x73, 0x87, + 0xa1, 0x81, 0x41, 0x61, 0x07, 0x80, 0x96, 0x84, + 0xd7, 0x81, 0xb1, 0x8f, 0x00, 0xb8, 0x80, 0xa5, + 0x84, 0x9b, 0x8b, 0xac, 0x83, 0xaf, 0x8b, 0xa4, + 0x80, 0xc2, 0x8d, 0x8b, 0x07, 0x81, 0xac, 0x82, + 0xb1, 0x00, 0x11, 0x0c, 0x80, 0xab, 0x24, 0x80, + 0x40, 0xec, 0x87, 0x60, 0x4f, 0x32, 0x80, 0x48, + 0x56, 0x84, 0x46, 0x85, 0x10, 0x0c, 0x83, 0x43, + 0x13, 0x83, 0xc0, 0x80, 0x41, 0x40, 0x81, 0xce, + 0x80, 0x41, 0x02, 0x82, 0xb4, 0x8d, 0xac, 0x81, + 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, 0x82, + 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, 0x8c, + 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, 0x40, + 0x9c, 0x89, 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, + 0x89, 0x80, 0x89, 0x81, 0xd3, 0x88, 0x00, 0x08, + 0x03, 0x01, 0xe6, 0x8c, 0x02, 0xe9, 0x91, 0x40, + 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e, 0x00, + 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c, 0x40, + 0xf6, 0x28, 0x09, 0x0a, 0x00, 0x80, 0x40, 0x8d, + 0x31, 0x2b, 0x80, 0x9b, 0x89, 0xa9, 0x20, 0x83, + 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, 0x38, 0x86, + 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, + 0x10, 0x02, 0x80, 0xc1, 0x20, 0x08, 0x83, 0x41, + 0x5b, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, 0x82, + 0x60, 0x41, 0xdc, 0x90, 0x4e, 0x1f, 0x00, 0xb6, + 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, 0x60, + 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, + 0x80, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, 0x85, + 0x99, 0x85, 0x99, }; static const uint8_t unicode_prop_Other_Lowercase_table[69] = { @@ -3725,16 +3928,21 @@ static const uint8_t unicode_prop_Other_Uppercase_table[15] = { 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, }; -static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = { +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[112] = { 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, - 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9, - 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6, - 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, - 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, - 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80, - 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80, - 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac, - 0xdf, + 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe7, + 0x00, 0x03, 0x08, 0x81, 0x88, 0x81, 0xe6, 0x80, + 0x97, 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x49, 0x34, + 0x80, 0x9d, 0x80, 0x43, 0xff, 0x04, 0x00, 0x04, + 0x81, 0xe4, 0x80, 0xc6, 0x81, 0x44, 0x17, 0x80, + 0x50, 0x20, 0x81, 0x60, 0x79, 0x22, 0x80, 0xeb, + 0x80, 0x60, 0x55, 0xdc, 0x81, 0x52, 0x1f, 0x80, + 0xf3, 0x80, 0x41, 0x07, 0x80, 0x8d, 0x80, 0x88, + 0x80, 0xdf, 0x80, 0x88, 0x01, 0x00, 0x14, 0x80, + 0x40, 0xdf, 0x80, 0x8b, 0x80, 0x40, 0xf0, 0x80, + 0x41, 0x05, 0x80, 0x42, 0x78, 0x80, 0x8b, 0x80, + 0x46, 0x02, 0x80, 0x60, 0x50, 0xad, 0x81, 0x60, + 0x61, 0x72, 0x0d, 0x85, 0x6c, 0x2e, 0xac, 0xdf, }; static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { @@ -3749,9 +3957,10 @@ static const uint8_t unicode_prop_Other_ID_Start_table[11] = { 0x4f, 0x6b, 0x81, }; -static const uint8_t unicode_prop_Other_ID_Continue_table[12] = { +static const uint8_t unicode_prop_Other_ID_Continue_table[22] = { 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, - 0x88, 0x46, 0x67, 0x80, + 0x88, 0x46, 0x67, 0x80, 0x46, 0x30, 0x81, 0x50, + 0xec, 0x80, 0x60, 0xce, 0x68, 0x80, }; static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = { @@ -3779,72 +3988,154 @@ static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, }; -static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = { - 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, - 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f, - 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80, - 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, - 0x84, +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[29] = { + 0x41, 0xef, 0x80, 0x41, 0x9e, 0x80, 0x9e, 0x80, + 0x5a, 0xe4, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, + 0x80, 0xde, 0x06, 0x06, 0x80, 0x8a, 0x09, 0x81, + 0x89, 0x10, 0x81, 0x8d, 0x80, }; -static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[451] = { +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[450] = { 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, - 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b, - 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80, - 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, - 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, - 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, - 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, - 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, - 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, - 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, - 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, - 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, - 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, - 0x46, 0x52, 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, - 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, - 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87, - 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, - 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08, - 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, - 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, - 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, - 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, - 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, - 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, - 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, - 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, - 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, - 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, - 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, - 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, - 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, - 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, - 0x60, 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, - 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, - 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00, - 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91, - 0xbf, 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, 0x95, - 0x94, 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00, - 0x80, 0x40, 0x86, 0x08, 0x80, 0x9f, 0x99, 0x40, - 0x83, 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, 0x80, - 0x88, 0x47, 0x87, 0x20, 0xa9, 0x80, 0x88, 0x60, - 0xb4, 0xe4, 0x83, 0x54, 0xb9, 0x86, 0x8d, 0x87, - 0xbf, 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, - 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, - 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, - 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, - 0x81, 0xb1, 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18, - 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, - 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, - 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, - 0x22, 0x04, 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84, - 0x9e, 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, - 0x82, 0x8c, 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d, - 0x89, 0x60, 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, - 0xe1, 0x4f, 0xff, + 0x10, 0x82, 0xf3, 0x80, 0x8b, 0x80, 0x40, 0x84, + 0x01, 0x01, 0x80, 0xa2, 0x01, 0x80, 0x40, 0xbb, + 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, + 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, 0x07, 0x80, + 0x9e, 0x80, 0xa0, 0x82, 0x9c, 0x80, 0x42, 0x28, + 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, 0xfb, 0x08, + 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, 0x80, 0x40, + 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, 0x80, 0xa7, + 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, 0x03, 0x03, + 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, 0x26, 0x80, + 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, 0x80, 0x8b, + 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, 0x46, 0x52, + 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, 0x8a, 0x80, + 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, 0xa4, 0x40, + 0xd5, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, 0x80, + 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xb7, 0x05, 0x00, 0x13, 0x05, 0x11, 0x02, 0x0c, + 0x11, 0x00, 0x00, 0x0c, 0x15, 0x05, 0x08, 0x8f, + 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, 0x00, + 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, 0x80, + 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, 0x01, + 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, 0x05, + 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, 0x40, + 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, 0x34, + 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, 0x82, + 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, 0x80, + 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, 0xd5, + 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, 0x80, + 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, 0x9e, + 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, 0x60, + 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x80, + 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, 0x60, + 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xc2, + 0x00, 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, + 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, + 0x8c, 0x82, 0x99, 0x95, 0x94, 0x81, 0x8b, 0x80, + 0x92, 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, 0x08, + 0x80, 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, 0x0d, + 0x0a, 0x16, 0x06, 0x80, 0x88, 0x47, 0x87, 0x20, + 0xa9, 0x80, 0x88, 0x60, 0xb4, 0xe4, 0x83, 0x50, + 0x31, 0xa3, 0x44, 0x63, 0x86, 0x8d, 0x87, 0xbf, + 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, 0x81, + 0xb1, 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18, 0x9a, + 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, + 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, + 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, + 0x04, 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84, 0x9e, + 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, 0x82, + 0x8c, 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d, 0x89, + 0x60, 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, + 0x4f, 0xff, +}; + +static const uint8_t unicode_prop_Basic_Emoji1_table[143] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, + 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, + 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 0xe5, 0x80, 0x97, 0x80, 0x93, 0x01, + 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, 0x8b, + 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, 0x1c, + 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, 0x40, + 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, 0x80, + 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, 0xc5, + 0x28, 0x12, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, 0x40, + 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, + 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, + 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, +}; + +static const uint8_t unicode_prop_Basic_Emoji2_table[183] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, + 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, + 0x8e, 0x81, 0x41, 0x7c, 0x80, 0x40, 0xa5, 0x80, + 0x9c, 0x10, 0x0c, 0x82, 0x40, 0xc6, 0x80, 0x40, + 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, 0xb9, 0x0a, + 0x84, 0x88, 0x01, 0x05, 0x03, 0x01, 0x00, 0x09, + 0x02, 0x02, 0x0f, 0x14, 0x00, 0x80, 0x9b, 0x09, + 0x00, 0x08, 0x80, 0x91, 0x01, 0x80, 0x92, 0x00, + 0x18, 0x00, 0x0a, 0x05, 0x07, 0x81, 0x95, 0x05, + 0x00, 0x00, 0x80, 0x94, 0x05, 0x09, 0x01, 0x17, + 0x04, 0x09, 0x08, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x80, 0x90, 0x81, 0x8e, 0x01, 0x80, 0x9a, 0x81, + 0xbb, 0x80, 0x41, 0x91, 0x81, 0x41, 0xce, 0x82, + 0x45, 0x27, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbe, 0xd5, 0x81, 0x8b, 0x81, 0x40, + 0x81, 0x80, 0xb3, 0x80, 0x40, 0xe8, 0x01, 0x88, + 0x88, 0x80, 0xc5, 0x80, 0x97, 0x08, 0x11, 0x81, + 0xaa, 0x1c, 0x8b, 0x92, 0x00, 0x00, 0x80, 0xc6, + 0x00, 0x80, 0x40, 0xba, 0x80, 0xca, 0x81, 0xa3, + 0x09, 0x86, 0x8c, 0x01, 0x19, 0x80, 0x93, 0x01, + 0x07, 0x81, 0x88, 0x04, 0x82, 0x8b, 0x17, 0x11, + 0x00, 0x03, 0x05, 0x02, 0x05, 0x80, 0x40, 0xcf, + 0x00, 0x82, 0x8f, 0x2a, 0x05, 0x01, 0x80, +}; + +static const uint8_t unicode_prop_RGI_Emoji_Modifier_Sequence_table[73] = { + 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, + 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, + 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x18, 0x18, 0x88, + 0x10, 0x1a, 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, + 0xc8, 0x0b, 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, + 0x12, 0x84, 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, + 0x8a, 0x80, 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, + 0x88, 0x89, 0x11, 0xb7, 0x80, 0xbc, 0x08, 0x08, + 0x80, 0x90, 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, + 0x88, +}; + +static const uint8_t unicode_prop_RGI_Emoji_Flag_Sequence_table[128] = { + 0x0c, 0x00, 0x09, 0x00, 0x04, 0x01, 0x02, 0x06, + 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x07, 0x0d, + 0x18, 0x00, 0x09, 0x00, 0x00, 0x89, 0x08, 0x00, + 0x00, 0x81, 0x88, 0x83, 0x8c, 0x10, 0x00, 0x01, + 0x07, 0x08, 0x29, 0x10, 0x28, 0x00, 0x80, 0x8a, + 0x00, 0x0a, 0x00, 0x0e, 0x15, 0x18, 0x83, 0x89, + 0x06, 0x00, 0x81, 0x8d, 0x00, 0x12, 0x08, 0x00, + 0x03, 0x00, 0x24, 0x00, 0x05, 0x21, 0x00, 0x00, + 0x29, 0x90, 0x00, 0x02, 0x00, 0x08, 0x09, 0x00, + 0x08, 0x18, 0x8b, 0x80, 0x8c, 0x02, 0x19, 0x1a, + 0x11, 0x00, 0x00, 0x80, 0x9c, 0x80, 0x88, 0x02, + 0x00, 0x00, 0x02, 0x20, 0x88, 0x0a, 0x00, 0x03, + 0x01, 0x02, 0x05, 0x08, 0x00, 0x01, 0x09, 0x20, + 0x21, 0x18, 0x22, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x28, 0x89, 0x80, 0x8b, 0x80, 0x90, 0x80, 0x92, + 0x80, 0x8d, 0x05, 0x80, 0x8a, 0x80, 0x88, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_Keycap_Sequence_table[4] = { + 0xa2, 0x05, 0x04, 0x89, }; static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { @@ -3856,14 +4147,15 @@ static const uint8_t unicode_prop_Bidi_Control_table[10] = { 0xb6, 0x83, }; -static const uint8_t unicode_prop_Dash_table[55] = { +static const uint8_t unicode_prop_Dash_table[58] = { 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e, 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85, 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85, 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80, 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee, 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, - 0x80, 0x40, 0xa8, 0x80, 0x4f, 0x9e, 0x80, + 0x80, 0x40, 0xa8, 0x80, 0x4e, 0x5f, 0x80, 0x41, + 0x3d, 0x80, }; static const uint8_t unicode_prop_Deprecated_table[23] = { @@ -3872,7 +4164,7 @@ static const uint8_t unicode_prop_Deprecated_table[23] = { 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, }; -static const uint8_t unicode_prop_Diacritic_table[399] = { +static const uint8_t unicode_prop_Diacritic_table[438] = { 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, @@ -3885,59 +4177,66 @@ static const uint8_t unicode_prop_Diacritic_table[399] = { 0x8f, 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06, 0x80, 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, - 0x80, 0xfb, 0x28, 0x80, 0xea, 0x80, 0x8c, 0x84, - 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, 0x81, 0xc1, - 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, 0x81, 0xa7, - 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, 0x81, 0x42, - 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x40, 0xb2, 0x8a, - 0x88, 0x80, 0x41, 0x5a, 0x82, 0x41, 0x38, 0x39, - 0x80, 0xaf, 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e, - 0x80, 0xa5, 0x88, 0xb5, 0x81, 0x40, 0x89, 0x81, - 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, 0xb1, - 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, 0x00, - 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c, - 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, 0x41, - 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, 0x75, - 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, 0xd1, - 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, 0x40, - 0xc9, 0x80, 0x9a, 0x91, 0xb8, 0x83, 0xa3, 0x80, - 0xde, 0x80, 0x8b, 0x80, 0xa3, 0x80, 0x40, 0x94, - 0x82, 0xc0, 0x83, 0xb2, 0x80, 0xe3, 0x84, 0x88, - 0x82, 0xff, 0x81, 0x60, 0x4f, 0x2f, 0x80, 0x43, - 0x00, 0x8f, 0x41, 0x0d, 0x00, 0x80, 0xae, 0x80, - 0xac, 0x81, 0xc2, 0x80, 0x42, 0xfb, 0x80, 0x44, - 0x9e, 0x28, 0xa9, 0x80, 0x88, 0x43, 0x29, 0x81, - 0x42, 0x3a, 0x85, 0x41, 0xd4, 0x82, 0xc5, 0x8a, - 0xb0, 0x83, 0x40, 0xbf, 0x80, 0xa8, 0x80, 0xc7, - 0x81, 0xf7, 0x81, 0xbd, 0x80, 0xcb, 0x80, 0x88, - 0x82, 0xe7, 0x81, 0x40, 0xb1, 0x81, 0xd0, 0x80, - 0x8f, 0x80, 0x97, 0x32, 0x84, 0x40, 0xcc, 0x02, - 0x80, 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, - 0xf5, 0x81, 0xf2, 0x80, 0x41, 0x0c, 0x81, 0x41, - 0x01, 0x0b, 0x80, 0x40, 0x9b, 0x80, 0xd2, 0x80, - 0x91, 0x80, 0xd0, 0x80, 0x41, 0xa4, 0x80, 0x41, - 0x01, 0x00, 0x81, 0xd0, 0x80, 0x56, 0xae, 0x8e, - 0x60, 0x36, 0x99, 0x84, 0xba, 0x86, 0x44, 0x57, - 0x90, 0xcf, 0x81, 0x60, 0x3f, 0xfd, 0x18, 0x30, - 0x81, 0x5f, 0x00, 0xad, 0x81, 0x96, 0x42, 0x1f, - 0x12, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x4e, 0x81, - 0xbd, 0x40, 0xc1, 0x86, 0x41, 0x76, 0x80, 0xbc, - 0x83, 0x45, 0xdf, 0x86, 0xec, 0x10, 0x82, + 0x80, 0xee, 0x80, 0x8b, 0x28, 0x80, 0xea, 0x80, + 0x8c, 0x84, 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, + 0x81, 0xc1, 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, + 0x81, 0xa7, 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, + 0x81, 0x42, 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x9d, + 0x80, 0x40, 0x93, 0x8a, 0x88, 0x80, 0x41, 0x5a, + 0x82, 0x41, 0x23, 0x80, 0x93, 0x39, 0x80, 0xaf, + 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e, 0x80, 0xa5, + 0x88, 0xb5, 0x81, 0xb9, 0x80, 0x8a, 0x81, 0xc1, + 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, + 0xb1, 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, + 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, + 0x8c, 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, + 0x41, 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, + 0x75, 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, + 0xd1, 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, + 0x8b, 0x80, 0xa4, 0x80, 0x40, 0x96, 0x80, 0x9a, + 0x91, 0xb8, 0x83, 0xa3, 0x80, 0xde, 0x80, 0x8b, + 0x80, 0xa3, 0x80, 0x40, 0x94, 0x82, 0xc0, 0x83, + 0xb2, 0x80, 0xe3, 0x84, 0x88, 0x82, 0xff, 0x81, + 0x60, 0x4f, 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, + 0x0d, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x42, 0xfb, 0x80, 0x44, 0x9e, 0x28, 0xa9, + 0x80, 0x88, 0x42, 0x7c, 0x13, 0x80, 0x40, 0xa4, + 0x81, 0x42, 0x3a, 0x85, 0xa5, 0x80, 0x99, 0x84, + 0x41, 0x8e, 0x82, 0xc5, 0x8a, 0xb0, 0x83, 0x40, + 0xbf, 0x80, 0xa8, 0x80, 0xc7, 0x81, 0xf7, 0x81, + 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7, 0x81, + 0x40, 0xb1, 0x81, 0xcf, 0x81, 0x8f, 0x80, 0x97, + 0x32, 0x84, 0xd8, 0x10, 0x81, 0x8c, 0x81, 0xde, + 0x02, 0x80, 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, + 0x80, 0xf5, 0x81, 0xf2, 0x80, 0x41, 0x0c, 0x81, + 0x41, 0x01, 0x0b, 0x80, 0x40, 0x9b, 0x80, 0xd2, + 0x80, 0x91, 0x80, 0xd0, 0x80, 0x41, 0xa4, 0x80, + 0x41, 0x01, 0x00, 0x81, 0xd0, 0x80, 0x41, 0xa8, + 0x81, 0x96, 0x80, 0x54, 0xeb, 0x8e, 0x60, 0x2c, + 0xd8, 0x80, 0x49, 0xbf, 0x84, 0xba, 0x86, 0x42, + 0x33, 0x81, 0x42, 0x21, 0x90, 0xcf, 0x81, 0x60, + 0x3f, 0xfd, 0x18, 0x30, 0x81, 0x5f, 0x00, 0xad, + 0x81, 0x96, 0x42, 0x1f, 0x12, 0x2f, 0x39, 0x86, + 0x9d, 0x83, 0x4e, 0x81, 0xbd, 0x40, 0xc1, 0x86, + 0x41, 0x76, 0x80, 0xbc, 0x83, 0x42, 0xfd, 0x81, + 0x42, 0xdf, 0x86, 0xec, 0x10, 0x82, }; -static const uint8_t unicode_prop_Extender_table[92] = { +static const uint8_t unicode_prop_Extender_table[111] = { 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, - 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42, - 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7, - 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3, - 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81, - 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5, - 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88, - 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a, - 0x80, 0x48, 0x0f, 0x81, 0x4b, 0xd9, 0x80, 0x42, - 0x67, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, + 0x80, 0x41, 0xb8, 0x80, 0x42, 0x75, 0x80, 0x40, + 0x88, 0x80, 0xd8, 0x80, 0x42, 0xef, 0x80, 0xfe, + 0x80, 0x49, 0x42, 0x80, 0xb7, 0x80, 0x42, 0x62, + 0x80, 0x41, 0x8d, 0x80, 0xc3, 0x80, 0x53, 0x88, + 0x80, 0xaa, 0x84, 0xe6, 0x81, 0xdc, 0x82, 0x60, + 0x6f, 0x15, 0x80, 0x45, 0xf5, 0x80, 0x43, 0xc1, + 0x80, 0x95, 0x80, 0x40, 0x88, 0x80, 0xeb, 0x80, + 0x94, 0x81, 0x60, 0x54, 0x7a, 0x80, 0x48, 0x0f, + 0x81, 0x45, 0xca, 0x80, 0x9a, 0x03, 0x80, 0x44, + 0xc6, 0x80, 0x41, 0x24, 0x80, 0xf3, 0x81, 0x41, + 0xf1, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, - 0x81, 0x48, 0x05, 0x82, + 0x81, 0x44, 0xb0, 0x80, 0x43, 0x53, 0x82, }; static const uint8_t unicode_prop_Hex_Digit_table[12] = { @@ -3945,24 +4244,28 @@ static const uint8_t unicode_prop_Hex_Digit_table[12] = { 0x89, 0x35, 0x99, 0x85, }; -static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = { - 0x60, 0x2f, 0xef, 0x09, 0x87, +static const uint8_t unicode_prop_IDS_Unary_Operator_table[4] = { + 0x60, 0x2f, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[8] = { + 0x60, 0x2f, 0xef, 0x09, 0x89, 0x41, 0xf0, 0x80, }; static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { 0x60, 0x2f, 0xf1, 0x81, }; -static const uint8_t unicode_prop_Ideographic_table[69] = { +static const uint8_t unicode_prop_Ideographic_table[72] = { 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff, 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60, 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44, - 0xd5, 0xa9, 0x88, 0x60, 0x24, 0x66, 0x41, 0x8b, + 0xd5, 0xa8, 0x89, 0x60, 0x24, 0x66, 0x41, 0x8b, 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, - 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, - 0x53, 0x4a, 0x84, 0x50, 0x5f, + 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x49, 0xa1, 0x42, + 0x1d, 0x45, 0xe1, 0x53, 0x4a, 0x84, 0x50, 0x5f, }; static const uint8_t unicode_prop_Join_Control_table[4] = { @@ -3974,6 +4277,11 @@ static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, }; +static const uint8_t unicode_prop_Modifier_Combining_Mark_table[16] = { + 0x46, 0x53, 0x09, 0x80, 0x40, 0x82, 0x05, 0x02, + 0x81, 0x41, 0xe0, 0x08, 0x12, 0x80, 0x9e, 0x80, +}; + static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, @@ -4018,32 +4326,34 @@ static const uint8_t unicode_prop_Regional_Indicator_table[4] = { 0x61, 0xf1, 0xe5, 0x99, }; -static const uint8_t unicode_prop_Sentence_Terminal_table[196] = { +static const uint8_t unicode_prop_Sentence_Terminal_table[213] = { 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa, 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, - 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41, - 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x9c, 0x81, - 0x40, 0xbb, 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81, - 0x88, 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x95, - 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, 0x80, - 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, 0x41, - 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x97, - 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, 0x40, - 0xf8, 0x80, 0x60, 0x52, 0x65, 0x02, 0x81, 0x40, - 0xa8, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0xc0, 0x80, - 0x4a, 0xf3, 0x81, 0x44, 0xfc, 0x84, 0xab, 0x83, - 0x40, 0xbc, 0x81, 0xf4, 0x83, 0xfe, 0x82, 0x40, - 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x08, 0x81, - 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, 0x74, 0x0c, - 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, - 0x00, 0x80, 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41, - 0xa3, 0x81, 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, - 0x4b, 0x28, 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, - 0x8a, 0x80, 0x43, 0x52, 0x80, 0x60, 0x4e, 0x05, - 0x80, 0x5d, 0xe7, 0x80, + 0x40, 0x9c, 0x81, 0xac, 0x04, 0x80, 0x41, 0x39, + 0x81, 0x41, 0x61, 0x83, 0x40, 0xa1, 0x81, 0x89, + 0x09, 0x81, 0x9c, 0x82, 0x40, 0xba, 0x81, 0xc0, + 0x81, 0x43, 0xa3, 0x80, 0x96, 0x81, 0x88, 0x82, + 0x4c, 0xae, 0x82, 0x41, 0x31, 0x80, 0x8c, 0x80, + 0x95, 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, + 0x80, 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, + 0x41, 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, + 0x97, 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, + 0x40, 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, + 0xba, 0x02, 0x81, 0x40, 0xa8, 0x80, 0x8b, 0x80, + 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, + 0xfc, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x81, 0xf4, + 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, + 0x81, 0xd7, 0x08, 0x81, 0xeb, 0x80, 0x41, 0x29, + 0x81, 0xf4, 0x81, 0x41, 0x74, 0x0c, 0x8e, 0xe8, + 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80, + 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41, 0xa3, 0x81, + 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, 0x4b, 0x28, + 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80, + 0x42, 0x28, 0x81, 0x41, 0x27, 0x80, 0x60, 0x4e, + 0x05, 0x80, 0x5d, 0xe7, 0x80, }; static const uint8_t unicode_prop_Soft_Dotted_table[79] = { @@ -4059,47 +4369,49 @@ static const uint8_t unicode_prop_Soft_Dotted_table[79] = { 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, }; -static const uint8_t unicode_prop_Terminal_Punctuation_table[248] = { +static const uint8_t unicode_prop_Terminal_Punctuation_table[264] = { 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3, 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, - 0x8e, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, 0xf3, - 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81, - 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82, - 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, 0x19, - 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40, - 0xad, 0x08, 0x82, 0x9c, 0x81, 0x40, 0xbb, 0x84, - 0xbd, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d, - 0xe3, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a, + 0x28, 0x87, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, + 0xf3, 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, + 0x81, 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, + 0x82, 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, + 0x19, 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, + 0x40, 0xa1, 0x81, 0x89, 0x08, 0x82, 0x9c, 0x82, + 0x40, 0xba, 0x84, 0xbd, 0x81, 0x43, 0xa3, 0x80, + 0x96, 0x81, 0x88, 0x82, 0x4c, 0xae, 0x82, 0x41, + 0x31, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a, 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81, 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82, 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40, - 0xf8, 0x80, 0x60, 0x52, 0x63, 0x10, 0x83, 0x40, - 0xa8, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, - 0xc0, 0x01, 0x80, 0x44, 0x39, 0x80, 0xaf, 0x80, - 0x44, 0x85, 0x80, 0x40, 0xc6, 0x80, 0x41, 0x35, - 0x81, 0x40, 0x97, 0x85, 0xc3, 0x85, 0xd8, 0x83, - 0x43, 0xb7, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x86, - 0xef, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, - 0x8f, 0x81, 0xd7, 0x84, 0xeb, 0x80, 0x41, 0xa0, - 0x82, 0x8b, 0x81, 0x41, 0x65, 0x1a, 0x8e, 0xe8, - 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80, - 0x40, 0xfa, 0x81, 0xd6, 0x0b, 0x81, 0x41, 0x9d, - 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0xc9, 0x81, - 0x45, 0x2a, 0x84, 0x60, 0x45, 0xf8, 0x81, 0x40, - 0x84, 0x80, 0xc0, 0x82, 0x89, 0x80, 0x43, 0x51, + 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, 0xb8, + 0x10, 0x83, 0x40, 0xa8, 0x80, 0x89, 0x00, 0x80, + 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, 0x44, 0x39, + 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, 0x40, 0xc6, + 0x80, 0x41, 0x35, 0x81, 0x40, 0x97, 0x85, 0xc3, + 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, 0xab, 0x83, + 0x40, 0xbc, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, + 0x80, 0x41, 0x29, 0x81, 0xf4, 0x82, 0x8b, 0x81, + 0x41, 0x65, 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, + 0x82, 0x42, 0x04, 0x00, 0x80, 0x40, 0xfa, 0x81, + 0xd6, 0x0b, 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, + 0x42, 0x84, 0x81, 0xc9, 0x81, 0x45, 0x2a, 0x84, + 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80, 0xc0, + 0x82, 0x89, 0x80, 0x42, 0x28, 0x81, 0x41, 0x26, 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, }; -static const uint8_t unicode_prop_Unified_Ideograph_table[45] = { +static const uint8_t unicode_prop_Unified_Ideograph_table[48] = { 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89, 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, 0xdd, - 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x54, 0x1e, - 0x53, 0x4a, 0x84, 0x50, 0x5f, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, 0x42, + 0x6d, 0x51, 0xa1, 0x53, 0x4a, 0x84, 0x50, 0x5f, }; static const uint8_t unicode_prop_Variation_Selector_table[13] = { @@ -4120,7 +4432,7 @@ static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, - 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88, + 0x88, 0x08, 0x00, 0x38, 0x9f, 0x0b, 0x20, 0x88, 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, @@ -4138,7 +4450,7 @@ static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { 0x80, 0xb8, 0x80, 0xb8, 0x80, }; -static const uint8_t unicode_prop_Emoji_table[239] = { +static const uint8_t unicode_prop_Emoji_table[238] = { 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, @@ -4167,8 +4479,8 @@ static const uint8_t unicode_prop_Emoji_table[239] = { 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x83, 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, - 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x88, 0x86, 0xad, - 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, 0x88, + 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, 0xb7, + 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, }; static const uint8_t unicode_prop_Emoji_Component_table[28] = { @@ -4194,7 +4506,7 @@ static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, }; -static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { +static const uint8_t unicode_prop_Emoji_Presentation_table[144] = { 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, @@ -4211,9 +4523,8 @@ static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, 0xc5, 0x28, 0x12, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, - 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x88, - 0x86, 0xad, 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, - 0x88, + 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, + 0x84, 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, }; static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { @@ -4266,6 +4577,11 @@ typedef enum { UNICODE_PROP_Changes_When_Titlecased1, UNICODE_PROP_Changes_When_Casefolded1, UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_Basic_Emoji1, + UNICODE_PROP_Basic_Emoji2, + UNICODE_PROP_RGI_Emoji_Modifier_Sequence, + UNICODE_PROP_RGI_Emoji_Flag_Sequence, + UNICODE_PROP_Emoji_Keycap_Sequence, UNICODE_PROP_ASCII_Hex_Digit, UNICODE_PROP_Bidi_Control, UNICODE_PROP_Dash, @@ -4273,11 +4589,13 @@ typedef enum { UNICODE_PROP_Diacritic, UNICODE_PROP_Extender, UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Unary_Operator, UNICODE_PROP_IDS_Binary_Operator, UNICODE_PROP_IDS_Trinary_Operator, UNICODE_PROP_Ideographic, UNICODE_PROP_Join_Control, UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Modifier_Combining_Mark, UNICODE_PROP_Noncharacter_Code_Point, UNICODE_PROP_Pattern_Syntax, UNICODE_PROP_Pattern_White_Space, @@ -4314,6 +4632,9 @@ typedef enum { UNICODE_PROP_Grapheme_Base, UNICODE_PROP_Grapheme_Extend, UNICODE_PROP_ID_Continue, + UNICODE_PROP_ID_Compat_Math_Start, + UNICODE_PROP_ID_Compat_Math_Continue, + UNICODE_PROP_InCB, UNICODE_PROP_Lowercase, UNICODE_PROP_Math, UNICODE_PROP_Uppercase, @@ -4331,11 +4652,13 @@ static const char unicode_prop_name_table[] = "Diacritic,Dia" "\0" "Extender,Ext" "\0" "Hex_Digit,Hex" "\0" + "IDS_Unary_Operator,IDSU" "\0" "IDS_Binary_Operator,IDSB" "\0" "IDS_Trinary_Operator,IDST" "\0" "Ideographic,Ideo" "\0" "Join_Control,Join_C" "\0" "Logical_Order_Exception,LOE" "\0" + "Modifier_Combining_Mark,MCM" "\0" "Noncharacter_Code_Point,NChar" "\0" "Pattern_Syntax,Pat_Syn" "\0" "Pattern_White_Space,Pat_WS" "\0" @@ -4372,6 +4695,9 @@ static const char unicode_prop_name_table[] = "Grapheme_Base,Gr_Base" "\0" "Grapheme_Extend,Gr_Ext" "\0" "ID_Continue,IDC" "\0" + "ID_Compat_Math_Start" "\0" + "ID_Compat_Math_Continue" "\0" + "InCB" "\0" "Lowercase,Lower" "\0" "Math" "\0" "Uppercase,Upper" "\0" @@ -4396,6 +4722,11 @@ static const uint8_t * const unicode_prop_table[] = { unicode_prop_Changes_When_Titlecased1_table, unicode_prop_Changes_When_Casefolded1_table, unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_Basic_Emoji1_table, + unicode_prop_Basic_Emoji2_table, + unicode_prop_RGI_Emoji_Modifier_Sequence_table, + unicode_prop_RGI_Emoji_Flag_Sequence_table, + unicode_prop_Emoji_Keycap_Sequence_table, unicode_prop_ASCII_Hex_Digit_table, unicode_prop_Bidi_Control_table, unicode_prop_Dash_table, @@ -4403,11 +4734,13 @@ static const uint8_t * const unicode_prop_table[] = { unicode_prop_Diacritic_table, unicode_prop_Extender_table, unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Unary_Operator_table, unicode_prop_IDS_Binary_Operator_table, unicode_prop_IDS_Trinary_Operator_table, unicode_prop_Ideographic_table, unicode_prop_Join_Control_table, unicode_prop_Logical_Order_Exception_table, + unicode_prop_Modifier_Combining_Mark_table, unicode_prop_Noncharacter_Code_Point_table, unicode_prop_Pattern_Syntax_table, unicode_prop_Pattern_White_Space_table, @@ -4449,6 +4782,11 @@ static const uint16_t unicode_prop_len_table[] = { countof(unicode_prop_Changes_When_Titlecased1_table), countof(unicode_prop_Changes_When_Casefolded1_table), countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_Basic_Emoji1_table), + countof(unicode_prop_Basic_Emoji2_table), + countof(unicode_prop_RGI_Emoji_Modifier_Sequence_table), + countof(unicode_prop_RGI_Emoji_Flag_Sequence_table), + countof(unicode_prop_Emoji_Keycap_Sequence_table), countof(unicode_prop_ASCII_Hex_Digit_table), countof(unicode_prop_Bidi_Control_table), countof(unicode_prop_Dash_table), @@ -4456,11 +4794,13 @@ static const uint16_t unicode_prop_len_table[] = { countof(unicode_prop_Diacritic_table), countof(unicode_prop_Extender_table), countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Unary_Operator_table), countof(unicode_prop_IDS_Binary_Operator_table), countof(unicode_prop_IDS_Trinary_Operator_table), countof(unicode_prop_Ideographic_table), countof(unicode_prop_Join_Control_table), countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Modifier_Combining_Mark_table), countof(unicode_prop_Noncharacter_Code_Point_table), countof(unicode_prop_Pattern_Syntax_table), countof(unicode_prop_Pattern_White_Space_table), @@ -4485,4 +4825,325 @@ static const uint16_t unicode_prop_len_table[] = { countof(unicode_prop_Case_Ignorable_table), }; +typedef enum { + UNICODE_SEQUENCE_PROP_Basic_Emoji, + UNICODE_SEQUENCE_PROP_Emoji_Keycap_Sequence, + UNICODE_SEQUENCE_PROP_RGI_Emoji_Modifier_Sequence, + UNICODE_SEQUENCE_PROP_RGI_Emoji_Flag_Sequence, + UNICODE_SEQUENCE_PROP_RGI_Emoji_Tag_Sequence, + UNICODE_SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence, + UNICODE_SEQUENCE_PROP_RGI_Emoji, + UNICODE_SEQUENCE_PROP_COUNT, +} UnicodeSequencePropertyEnum; + +static const char unicode_sequence_prop_name_table[] = + "Basic_Emoji" "\0" + "Emoji_Keycap_Sequence" "\0" + "RGI_Emoji_Modifier_Sequence" "\0" + "RGI_Emoji_Flag_Sequence" "\0" + "RGI_Emoji_Tag_Sequence" "\0" + "RGI_Emoji_ZWJ_Sequence" "\0" + "RGI_Emoji" "\0" +; + +static const uint8_t unicode_rgi_emoji_tag_sequence[18] = { + 0x67, 0x62, 0x65, 0x6e, 0x67, 0x00, 0x67, 0x62, + 0x73, 0x63, 0x74, 0x00, 0x67, 0x62, 0x77, 0x6c, + 0x73, 0x00, +}; + +static const uint8_t unicode_rgi_emoji_zwj_sequence[2320] = { + 0x02, 0xb8, 0x19, 0x40, 0x86, 0x02, 0xd1, 0x39, + 0xb0, 0x19, 0x02, 0x26, 0x39, 0x42, 0x86, 0x02, + 0xb4, 0x36, 0x42, 0x86, 0x03, 0x68, 0x54, 0x64, + 0x87, 0x68, 0x54, 0x02, 0xdc, 0x39, 0x42, 0x86, + 0x02, 0xd1, 0x39, 0x73, 0x13, 0x02, 0x39, 0x39, + 0x40, 0x86, 0x02, 0x69, 0x34, 0xbd, 0x19, 0x03, + 0xb6, 0x36, 0x40, 0x86, 0xa1, 0x87, 0x03, 0x68, + 0x74, 0x1d, 0x19, 0x68, 0x74, 0x03, 0x68, 0x34, + 0xbd, 0x19, 0xa1, 0x87, 0x02, 0xf1, 0x7a, 0xf2, + 0x7a, 0x02, 0xca, 0x33, 0x42, 0x86, 0x02, 0x69, + 0x34, 0xb0, 0x19, 0x04, 0x68, 0x14, 0x68, 0x14, + 0x67, 0x14, 0x66, 0x14, 0x02, 0xf9, 0x26, 0x42, + 0x86, 0x03, 0x69, 0x74, 0x1d, 0x19, 0x69, 0x74, + 0x03, 0xd1, 0x19, 0xbc, 0x19, 0xa1, 0x87, 0x02, + 0x3c, 0x19, 0x40, 0x86, 0x02, 0x68, 0x34, 0xeb, + 0x13, 0x02, 0xc3, 0x33, 0xa1, 0x87, 0x02, 0x70, + 0x34, 0x40, 0x86, 0x02, 0xd4, 0x39, 0x42, 0x86, + 0x02, 0xcf, 0x39, 0x42, 0x86, 0x02, 0x47, 0x36, + 0x40, 0x86, 0x02, 0x39, 0x39, 0x42, 0x86, 0x04, + 0xd1, 0x79, 0x64, 0x87, 0x8b, 0x14, 0xd1, 0x79, + 0x02, 0xd1, 0x39, 0x95, 0x86, 0x02, 0x68, 0x34, + 0x93, 0x13, 0x02, 0x69, 0x34, 0xed, 0x13, 0x02, + 0xda, 0x39, 0x40, 0x86, 0x03, 0x69, 0x34, 0xaf, + 0x19, 0xa1, 0x87, 0x02, 0xd1, 0x39, 0x93, 0x13, + 0x03, 0xce, 0x39, 0x42, 0x86, 0xa1, 0x87, 0x03, + 0xd1, 0x79, 0x64, 0x87, 0xd1, 0x79, 0x03, 0xc3, + 0x33, 0x42, 0x86, 0xa1, 0x87, 0x03, 0x69, 0x74, + 0x1d, 0x19, 0x68, 0x74, 0x02, 0x69, 0x34, 0x92, + 0x16, 0x02, 0xd1, 0x39, 0x96, 0x86, 0x04, 0x69, + 0x14, 0x64, 0x87, 0x8b, 0x14, 0x68, 0x14, 0x02, + 0x68, 0x34, 0x7c, 0x13, 0x02, 0x47, 0x36, 0x42, + 0x86, 0x02, 0x86, 0x34, 0x42, 0x86, 0x02, 0xd1, + 0x39, 0x7c, 0x13, 0x02, 0x69, 0x14, 0xa4, 0x13, + 0x02, 0xda, 0x39, 0x42, 0x86, 0x02, 0x37, 0x39, + 0x40, 0x86, 0x02, 0xd1, 0x39, 0x08, 0x87, 0x04, + 0x68, 0x54, 0x64, 0x87, 0x8b, 0x14, 0x68, 0x54, + 0x02, 0x4d, 0x36, 0x40, 0x86, 0x02, 0x68, 0x34, + 0x2c, 0x15, 0x02, 0x69, 0x34, 0xaf, 0x19, 0x02, + 0x6e, 0x34, 0x40, 0x86, 0x02, 0xcd, 0x39, 0x42, + 0x86, 0x02, 0xd1, 0x39, 0x2c, 0x15, 0x02, 0x6f, + 0x14, 0x40, 0x86, 0x03, 0xd1, 0x39, 0xbc, 0x19, + 0xa1, 0x87, 0x02, 0x68, 0x34, 0xa8, 0x13, 0x02, + 0x69, 0x34, 0x73, 0x13, 0x04, 0x69, 0x54, 0x64, + 0x87, 0x8b, 0x14, 0x68, 0x54, 0x02, 0x71, 0x34, + 0x42, 0x86, 0x02, 0xd1, 0x39, 0xa8, 0x13, 0x02, + 0x45, 0x36, 0x40, 0x86, 0x03, 0x69, 0x54, 0x64, + 0x87, 0x68, 0x54, 0x03, 0x69, 0x54, 0x64, 0x87, + 0x69, 0x54, 0x03, 0xce, 0x39, 0x40, 0x86, 0xa1, + 0x87, 0x02, 0xd8, 0x39, 0x40, 0x86, 0x03, 0xc3, + 0x33, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x4d, 0x36, + 0x42, 0x86, 0x02, 0xd1, 0x19, 0x92, 0x16, 0x02, + 0xd1, 0x39, 0xeb, 0x13, 0x02, 0x68, 0x34, 0xbc, + 0x14, 0x02, 0xd1, 0x39, 0xbc, 0x14, 0x02, 0x3d, + 0x39, 0x40, 0x86, 0x02, 0xb8, 0x39, 0x42, 0x86, + 0x02, 0xa3, 0x36, 0x40, 0x86, 0x02, 0x75, 0x35, + 0x40, 0x86, 0x02, 0xd8, 0x39, 0x42, 0x86, 0x02, + 0x69, 0x34, 0x93, 0x13, 0x02, 0x35, 0x39, 0x40, + 0x86, 0x02, 0x4b, 0x36, 0x40, 0x86, 0x02, 0x3d, + 0x39, 0x42, 0x86, 0x02, 0x38, 0x39, 0x42, 0x86, + 0x02, 0xa3, 0x36, 0x42, 0x86, 0x03, 0x69, 0x14, + 0x67, 0x14, 0x67, 0x14, 0x02, 0xb6, 0x36, 0x40, + 0x86, 0x02, 0x69, 0x34, 0x7c, 0x13, 0x02, 0x75, + 0x35, 0x42, 0x86, 0x02, 0xcc, 0x93, 0x40, 0x86, + 0x02, 0xcc, 0x33, 0x40, 0x86, 0x03, 0xd1, 0x39, + 0xbd, 0x19, 0xa1, 0x87, 0x02, 0x82, 0x34, 0x40, + 0x86, 0x02, 0x87, 0x34, 0x40, 0x86, 0x02, 0x69, + 0x14, 0x3e, 0x13, 0x02, 0xd6, 0x39, 0x40, 0x86, + 0x02, 0x68, 0x14, 0xbd, 0x19, 0x02, 0x46, 0x36, + 0x42, 0x86, 0x02, 0x4b, 0x36, 0x42, 0x86, 0x02, + 0x69, 0x34, 0x2c, 0x15, 0x03, 0xb6, 0x36, 0x42, + 0x86, 0xa1, 0x87, 0x02, 0xc4, 0x33, 0x40, 0x86, + 0x02, 0x26, 0x19, 0x40, 0x86, 0x02, 0x69, 0x14, + 0xb0, 0x19, 0x02, 0xde, 0x19, 0x42, 0x86, 0x02, + 0x69, 0x34, 0xa8, 0x13, 0x02, 0xcc, 0x33, 0x42, + 0x86, 0x02, 0x82, 0x34, 0x42, 0x86, 0x02, 0xd1, + 0x19, 0x93, 0x13, 0x02, 0x81, 0x14, 0x42, 0x86, + 0x02, 0x69, 0x34, 0x95, 0x86, 0x02, 0x68, 0x34, + 0xbb, 0x14, 0x02, 0xd1, 0x39, 0xbb, 0x14, 0x02, + 0x69, 0x34, 0xeb, 0x13, 0x02, 0xd1, 0x39, 0x84, + 0x13, 0x02, 0x69, 0x34, 0xbc, 0x14, 0x04, 0x69, + 0x54, 0x64, 0x87, 0x8b, 0x14, 0x69, 0x54, 0x02, + 0x26, 0x39, 0x40, 0x86, 0x02, 0xb4, 0x36, 0x40, + 0x86, 0x02, 0x47, 0x16, 0x42, 0x86, 0x02, 0xdc, + 0x39, 0x40, 0x86, 0x02, 0xca, 0x33, 0x40, 0x86, + 0x02, 0xf9, 0x26, 0x40, 0x86, 0x02, 0x69, 0x34, + 0x08, 0x87, 0x03, 0x69, 0x14, 0x69, 0x14, 0x66, + 0x14, 0x03, 0xd1, 0x59, 0x1d, 0x19, 0xd1, 0x59, + 0x02, 0xd4, 0x39, 0x40, 0x86, 0x02, 0xcf, 0x39, + 0x40, 0x86, 0x02, 0x68, 0x34, 0xa4, 0x13, 0x02, + 0xd1, 0x39, 0xa4, 0x13, 0x02, 0xd1, 0x19, 0xa8, + 0x13, 0x02, 0xd7, 0x39, 0x42, 0x86, 0x03, 0x69, + 0x34, 0xbc, 0x19, 0xa1, 0x87, 0x02, 0x68, 0x14, + 0xb0, 0x19, 0x02, 0x68, 0x14, 0x73, 0x13, 0x04, + 0x69, 0x14, 0x69, 0x14, 0x66, 0x14, 0x66, 0x14, + 0x03, 0x68, 0x34, 0xaf, 0x19, 0xa1, 0x87, 0x02, + 0x68, 0x34, 0x80, 0x16, 0x02, 0x73, 0x34, 0x42, + 0x86, 0x02, 0xd1, 0x39, 0x80, 0x16, 0x02, 0x68, + 0x34, 0xb0, 0x19, 0x02, 0x86, 0x34, 0x40, 0x86, + 0x02, 0x38, 0x19, 0x42, 0x86, 0x02, 0x69, 0x34, + 0xbb, 0x14, 0x02, 0xb5, 0x36, 0x42, 0x86, 0x02, + 0xcd, 0x39, 0x40, 0x86, 0x02, 0x68, 0x34, 0x95, + 0x86, 0x02, 0x68, 0x34, 0x27, 0x15, 0x03, 0x68, + 0x14, 0x68, 0x14, 0x66, 0x14, 0x02, 0x71, 0x34, + 0x40, 0x86, 0x02, 0xd1, 0x39, 0x27, 0x15, 0x02, + 0x2e, 0x16, 0xa8, 0x14, 0x02, 0xc3, 0x33, 0x42, + 0x86, 0x02, 0x69, 0x14, 0x66, 0x14, 0x02, 0x68, + 0x34, 0x96, 0x86, 0x02, 0x69, 0x34, 0xa4, 0x13, + 0x03, 0x69, 0x14, 0x64, 0x87, 0x68, 0x14, 0x02, + 0xb8, 0x39, 0x40, 0x86, 0x02, 0x68, 0x34, 0x3e, + 0x13, 0x03, 0xd1, 0x19, 0xaf, 0x19, 0xa1, 0x87, + 0x02, 0xd1, 0x39, 0x3e, 0x13, 0x02, 0x68, 0x34, + 0xbd, 0x19, 0x02, 0xd1, 0x19, 0xbb, 0x14, 0x02, + 0xd1, 0x19, 0x95, 0x86, 0x02, 0xdb, 0x39, 0x42, + 0x86, 0x02, 0x38, 0x39, 0x40, 0x86, 0x02, 0x69, + 0x34, 0x80, 0x16, 0x02, 0x69, 0x14, 0xeb, 0x13, + 0x04, 0x68, 0x14, 0x69, 0x14, 0x67, 0x14, 0x67, + 0x14, 0x02, 0x77, 0x34, 0x42, 0x86, 0x02, 0x46, + 0x36, 0x40, 0x86, 0x02, 0x68, 0x34, 0x92, 0x16, + 0x02, 0x4e, 0x36, 0x42, 0x86, 0x03, 0x69, 0x14, + 0xbd, 0x19, 0xa1, 0x87, 0x02, 0xde, 0x19, 0x40, + 0x86, 0x02, 0x69, 0x34, 0x27, 0x15, 0x03, 0xc3, + 0x13, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x81, 0x14, + 0x40, 0x86, 0x03, 0xd1, 0x39, 0xaf, 0x19, 0xa1, + 0x87, 0x02, 0x68, 0x34, 0xbc, 0x19, 0x02, 0xd1, + 0x19, 0x80, 0x16, 0x02, 0xd9, 0x39, 0x42, 0x86, + 0x02, 0xd1, 0x39, 0xbc, 0x19, 0x02, 0xdc, 0x19, + 0x42, 0x86, 0x02, 0x68, 0x34, 0x73, 0x13, 0x02, + 0x69, 0x34, 0x3e, 0x13, 0x02, 0x47, 0x16, 0x40, + 0x86, 0x02, 0xd1, 0x39, 0xbd, 0x19, 0x02, 0x3e, + 0x39, 0x42, 0x86, 0x02, 0x69, 0x14, 0x95, 0x86, + 0x02, 0x68, 0x14, 0x96, 0x86, 0x03, 0x69, 0x34, + 0xbd, 0x19, 0xa1, 0x87, 0x02, 0xd7, 0x39, 0x40, + 0x86, 0x02, 0x45, 0x16, 0x42, 0x86, 0x02, 0x68, + 0x34, 0xed, 0x13, 0x03, 0x68, 0x34, 0xbc, 0x19, + 0xa1, 0x87, 0x02, 0xd1, 0x39, 0xed, 0x13, 0x02, + 0xd1, 0x39, 0x92, 0x16, 0x02, 0x73, 0x34, 0x40, + 0x86, 0x02, 0x38, 0x19, 0x40, 0x86, 0x02, 0xb5, + 0x36, 0x40, 0x86, 0x02, 0x68, 0x34, 0xaf, 0x19, + 0x02, 0xd1, 0x39, 0xaf, 0x19, 0x02, 0x69, 0x34, + 0xbc, 0x19, 0x02, 0xb6, 0x16, 0x42, 0x86, 0x02, + 0x26, 0x14, 0x25, 0x15, 0x02, 0xc3, 0x33, 0x40, + 0x86, 0x02, 0xdd, 0x39, 0x42, 0x86, 0x02, 0xcb, + 0x93, 0x42, 0x86, 0x02, 0xcb, 0x33, 0x42, 0x86, + 0x02, 0x81, 0x34, 0x42, 0x86, 0x02, 0xce, 0x39, + 0xa1, 0x87, 0x02, 0xdb, 0x39, 0x40, 0x86, 0x02, + 0x68, 0x34, 0x08, 0x87, 0x02, 0xd1, 0x19, 0xb0, + 0x19, 0x02, 0x77, 0x34, 0x40, 0x86, 0x02, 0x4e, + 0x36, 0x40, 0x86, 0x02, 0xce, 0x39, 0x42, 0x86, + 0x02, 0x4e, 0x16, 0x42, 0x86, 0x02, 0xd9, 0x39, + 0x40, 0x86, 0x02, 0xdc, 0x19, 0x40, 0x86, 0x02, + 0x3e, 0x39, 0x40, 0x86, 0x02, 0xb9, 0x39, 0x42, + 0x86, 0x02, 0xda, 0x19, 0x42, 0x86, 0x02, 0x42, + 0x16, 0x94, 0x81, 0x02, 0x45, 0x16, 0x40, 0x86, + 0x02, 0x69, 0x14, 0xbd, 0x19, 0x02, 0x70, 0x34, + 0x42, 0x86, 0x02, 0xce, 0x19, 0xa1, 0x87, 0x02, + 0xc3, 0x13, 0x42, 0x86, 0x02, 0x68, 0x14, 0x08, + 0x87, 0x02, 0xd1, 0x19, 0x7c, 0x13, 0x02, 0x68, + 0x14, 0x92, 0x16, 0x02, 0xb6, 0x16, 0x40, 0x86, + 0x02, 0x37, 0x39, 0x42, 0x86, 0x03, 0xce, 0x19, + 0x42, 0x86, 0xa1, 0x87, 0x03, 0x68, 0x14, 0x67, + 0x14, 0x67, 0x14, 0x02, 0xdd, 0x39, 0x40, 0x86, + 0x02, 0xcf, 0x19, 0x42, 0x86, 0x02, 0xd1, 0x19, + 0x2c, 0x15, 0x02, 0x4b, 0x13, 0xe9, 0x17, 0x02, + 0x68, 0x14, 0x67, 0x14, 0x02, 0xcb, 0x93, 0x40, + 0x86, 0x02, 0x6e, 0x34, 0x42, 0x86, 0x02, 0xcb, + 0x33, 0x40, 0x86, 0x02, 0x81, 0x34, 0x40, 0x86, + 0x02, 0xb6, 0x36, 0xa1, 0x87, 0x02, 0x45, 0x36, + 0x42, 0x86, 0x02, 0xb4, 0x16, 0x42, 0x86, 0x02, + 0x69, 0x14, 0x73, 0x13, 0x04, 0x69, 0x14, 0x69, + 0x14, 0x67, 0x14, 0x66, 0x14, 0x02, 0x35, 0x39, + 0x42, 0x86, 0x02, 0x68, 0x14, 0x93, 0x13, 0x02, + 0xb6, 0x36, 0x42, 0x86, 0x03, 0x68, 0x14, 0x69, + 0x14, 0x66, 0x14, 0x02, 0xce, 0x39, 0x40, 0x86, + 0x02, 0x4e, 0x16, 0x40, 0x86, 0x02, 0x87, 0x34, + 0x42, 0x86, 0x02, 0x86, 0x14, 0x42, 0x86, 0x02, + 0xd6, 0x39, 0x42, 0x86, 0x02, 0xc4, 0x33, 0x42, + 0x86, 0x02, 0x69, 0x34, 0x96, 0x86, 0x02, 0xb9, + 0x39, 0x40, 0x86, 0x02, 0x68, 0x14, 0xa8, 0x13, + 0x02, 0xd1, 0x19, 0x84, 0x13, 0x02, 0xda, 0x19, + 0x40, 0x86, 0x02, 0xd8, 0x19, 0x42, 0x86, 0x02, + 0xc3, 0x13, 0x40, 0x86, 0x02, 0xb9, 0x19, 0x42, + 0x86, 0x02, 0x3d, 0x19, 0x42, 0x86, 0x02, 0xcf, + 0x19, 0x40, 0x86, 0x04, 0x68, 0x14, 0x68, 0x14, + 0x67, 0x14, 0x67, 0x14, 0x03, 0xd1, 0x19, 0xd1, + 0x19, 0xd2, 0x19, 0x02, 0x68, 0x14, 0xbb, 0x14, + 0x02, 0x3b, 0x14, 0x44, 0x87, 0x02, 0xd1, 0x19, + 0x27, 0x15, 0x02, 0xb4, 0x16, 0x40, 0x86, 0x02, + 0xcd, 0x19, 0x42, 0x86, 0x02, 0xd3, 0x86, 0xa5, + 0x14, 0x02, 0x70, 0x14, 0x42, 0x86, 0x03, 0xb6, + 0x16, 0x42, 0x86, 0xa1, 0x87, 0x04, 0x69, 0x14, + 0x64, 0x87, 0x8b, 0x14, 0x69, 0x14, 0x02, 0x36, + 0x16, 0x2b, 0x93, 0x02, 0x68, 0x14, 0x80, 0x16, + 0x02, 0x86, 0x14, 0x40, 0x86, 0x02, 0x08, 0x14, + 0x1b, 0x0b, 0x02, 0xd1, 0x19, 0xbc, 0x19, 0x02, + 0xca, 0x13, 0x42, 0x86, 0x02, 0x41, 0x94, 0xe8, + 0x95, 0x02, 0xd8, 0x19, 0x40, 0x86, 0x02, 0xb9, + 0x19, 0x40, 0x86, 0x02, 0xd1, 0x19, 0xed, 0x13, + 0x02, 0xf9, 0x86, 0x42, 0x86, 0x03, 0xd1, 0x19, + 0xbd, 0x19, 0xa1, 0x87, 0x02, 0x3d, 0x19, 0x40, + 0x86, 0x02, 0xd6, 0x19, 0x42, 0x86, 0x03, 0x69, + 0x14, 0x66, 0x14, 0x66, 0x14, 0x02, 0xd1, 0x19, + 0xaf, 0x19, 0x03, 0x69, 0x14, 0x69, 0x14, 0x67, + 0x14, 0x02, 0xcd, 0x19, 0x40, 0x86, 0x02, 0x70, + 0x14, 0x40, 0x86, 0x03, 0x68, 0x14, 0xbc, 0x19, + 0xa1, 0x87, 0x02, 0x6e, 0x14, 0x42, 0x86, 0x02, + 0x69, 0x14, 0x92, 0x16, 0x03, 0x68, 0x14, 0x68, + 0x14, 0x67, 0x14, 0x02, 0x69, 0x14, 0x67, 0x14, + 0x02, 0x75, 0x95, 0x42, 0x86, 0x03, 0x69, 0x14, + 0x64, 0x87, 0x69, 0x14, 0x02, 0xd1, 0x19, 0xbc, + 0x14, 0x02, 0xdf, 0x19, 0x42, 0x86, 0x02, 0xca, + 0x13, 0x40, 0x86, 0x02, 0x82, 0x14, 0x42, 0x86, + 0x02, 0x69, 0x14, 0x93, 0x13, 0x02, 0x68, 0x14, + 0x7c, 0x13, 0x02, 0xf9, 0x86, 0x40, 0x86, 0x02, + 0xd6, 0x19, 0x40, 0x86, 0x02, 0x68, 0x14, 0x2c, + 0x15, 0x02, 0x69, 0x14, 0xa8, 0x13, 0x02, 0xd4, + 0x19, 0x42, 0x86, 0x04, 0x68, 0x14, 0x69, 0x14, + 0x66, 0x14, 0x66, 0x14, 0x02, 0x77, 0x14, 0x42, + 0x86, 0x02, 0x39, 0x19, 0x42, 0x86, 0x02, 0xd1, + 0x19, 0xa4, 0x13, 0x02, 0x6e, 0x14, 0x40, 0x86, + 0x03, 0xd1, 0x19, 0xd2, 0x19, 0xd2, 0x19, 0x02, + 0x69, 0x14, 0xbb, 0x14, 0x02, 0xd1, 0x19, 0x96, + 0x86, 0x02, 0x75, 0x95, 0x40, 0x86, 0x04, 0x68, + 0x14, 0x64, 0x87, 0x8b, 0x14, 0x68, 0x14, 0x02, + 0xd1, 0x19, 0x3e, 0x13, 0x02, 0xdf, 0x19, 0x40, + 0x86, 0x02, 0x82, 0x14, 0x40, 0x86, 0x02, 0x44, + 0x13, 0xeb, 0x17, 0x02, 0xdd, 0x19, 0x42, 0x86, + 0x02, 0x69, 0x14, 0x80, 0x16, 0x03, 0x68, 0x14, + 0xaf, 0x19, 0xa1, 0x87, 0x02, 0xa3, 0x16, 0x42, + 0x86, 0x02, 0x69, 0x14, 0x96, 0x86, 0x02, 0x46, + 0x16, 0x42, 0x86, 0x02, 0xb6, 0x16, 0xa1, 0x87, + 0x02, 0x68, 0x14, 0x27, 0x15, 0x02, 0x26, 0x14, + 0x1b, 0x0b, 0x02, 0xd4, 0x19, 0x40, 0x86, 0x02, + 0x77, 0x14, 0x40, 0x86, 0x02, 0x39, 0x19, 0x40, + 0x86, 0x02, 0x37, 0x19, 0x42, 0x86, 0x03, 0x69, + 0x14, 0x67, 0x14, 0x66, 0x14, 0x03, 0xc3, 0x13, + 0x42, 0x86, 0xa1, 0x87, 0x02, 0x68, 0x14, 0xbc, + 0x19, 0x02, 0xd1, 0x19, 0xeb, 0x13, 0x04, 0x69, + 0x14, 0x69, 0x14, 0x67, 0x14, 0x67, 0x14, 0x02, + 0xd1, 0x19, 0x08, 0x87, 0x02, 0x68, 0x14, 0xed, + 0x13, 0x03, 0x69, 0x14, 0xbc, 0x19, 0xa1, 0x87, + 0x02, 0xdd, 0x19, 0x40, 0x86, 0x02, 0xc3, 0x13, + 0xa1, 0x87, 0x03, 0x68, 0x14, 0x66, 0x14, 0x66, + 0x14, 0x03, 0x68, 0x14, 0x69, 0x14, 0x67, 0x14, + 0x02, 0xa3, 0x16, 0x40, 0x86, 0x02, 0xdb, 0x19, + 0x42, 0x86, 0x02, 0x68, 0x14, 0xaf, 0x19, 0x02, + 0x46, 0x16, 0x40, 0x86, 0x02, 0x35, 0x16, 0xab, + 0x14, 0x02, 0x68, 0x14, 0x95, 0x86, 0x02, 0x42, + 0x16, 0x95, 0x81, 0x02, 0xc4, 0x13, 0x42, 0x86, + 0x02, 0x15, 0x14, 0xba, 0x19, 0x02, 0x69, 0x14, + 0x08, 0x87, 0x03, 0xd1, 0x19, 0x1d, 0x19, 0xd1, + 0x19, 0x02, 0x69, 0x14, 0x7c, 0x13, 0x02, 0x37, + 0x19, 0x40, 0x86, 0x02, 0x73, 0x14, 0x42, 0x86, + 0x02, 0x69, 0x14, 0x2c, 0x15, 0x02, 0xb5, 0x16, + 0x42, 0x86, 0x02, 0x35, 0x19, 0x42, 0x86, 0x04, + 0x68, 0x14, 0x69, 0x14, 0x67, 0x14, 0x66, 0x14, + 0x02, 0x64, 0x87, 0x25, 0x15, 0x02, 0x64, 0x87, + 0x79, 0x1a, 0x02, 0x68, 0x14, 0xbc, 0x14, 0x03, + 0xce, 0x19, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x87, + 0x14, 0x42, 0x86, 0x02, 0x4d, 0x16, 0x42, 0x86, + 0x04, 0x68, 0x14, 0x68, 0x14, 0x66, 0x14, 0x66, + 0x14, 0x02, 0xdb, 0x19, 0x40, 0x86, 0x02, 0xd9, + 0x19, 0x42, 0x86, 0x02, 0xc4, 0x13, 0x40, 0x86, + 0x02, 0xd1, 0x19, 0xbd, 0x19, 0x02, 0x68, 0x14, + 0xa4, 0x13, 0x02, 0x3e, 0x19, 0x42, 0x86, 0x02, + 0xf3, 0x93, 0xa7, 0x86, 0x03, 0x69, 0x14, 0xaf, + 0x19, 0xa1, 0x87, 0x02, 0xf3, 0x93, 0x08, 0x13, + 0x02, 0xd1, 0x19, 0xd2, 0x19, 0x02, 0x73, 0x14, + 0x40, 0x86, 0x02, 0xb5, 0x16, 0x40, 0x86, 0x02, + 0x35, 0x19, 0x40, 0x86, 0x02, 0x69, 0x14, 0x27, + 0x15, 0x02, 0xce, 0x19, 0x42, 0x86, 0x02, 0x71, + 0x14, 0x42, 0x86, 0x02, 0xd1, 0x19, 0x73, 0x13, + 0x02, 0x68, 0x14, 0x3e, 0x13, 0x02, 0xf4, 0x13, + 0x20, 0x86, 0x02, 0x87, 0x14, 0x40, 0x86, 0x03, + 0xb6, 0x16, 0x40, 0x86, 0xa1, 0x87, 0x02, 0x4d, + 0x16, 0x40, 0x86, 0x02, 0x69, 0x14, 0xbc, 0x19, + 0x02, 0x4b, 0x16, 0x42, 0x86, 0x02, 0xd9, 0x19, + 0x40, 0x86, 0x02, 0x3e, 0x19, 0x40, 0x86, 0x02, + 0x69, 0x14, 0xed, 0x13, 0x02, 0xd7, 0x19, 0x42, + 0x86, 0x02, 0xb8, 0x19, 0x42, 0x86, 0x03, 0x68, + 0x14, 0x67, 0x14, 0x66, 0x14, 0x02, 0x3c, 0x19, + 0x42, 0x86, 0x02, 0x68, 0x14, 0x66, 0x14, 0x03, + 0x68, 0x14, 0x64, 0x87, 0x68, 0x14, 0x02, 0x69, + 0x14, 0xaf, 0x19, 0x02, 0xce, 0x19, 0x40, 0x86, + 0x02, 0x71, 0x14, 0x40, 0x86, 0x02, 0x68, 0x14, + 0xeb, 0x13, 0x03, 0x68, 0x14, 0xbd, 0x19, 0xa1, + 0x87, 0x02, 0x6f, 0x14, 0x42, 0x86, 0x04, 0xd1, + 0x19, 0xd1, 0x19, 0xd2, 0x19, 0xd2, 0x19, 0x02, + 0x69, 0x14, 0xbc, 0x14, 0x02, 0xcc, 0x93, 0x42, + 0x86, 0x02, 0x4b, 0x16, 0x40, 0x86, 0x02, 0x26, + 0x19, 0x42, 0x86, 0x02, 0xd7, 0x19, 0x40, 0x86, +}; + #endif /* CONFIG_ALL_UNICODE */ +/* 71 tables / 36311 bytes, 5 index / 351 bytes */ diff --git a/quickjs/libunicode.c b/quickjs/libunicode.c index 63c12a0771..3791523d6a 100644 --- a/quickjs/libunicode.c +++ b/quickjs/libunicode.c @@ -1,6 +1,6 @@ /* * Unicode utilities - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -43,15 +43,115 @@ enum { RUN_TYPE_UF_D1_EXT, RUN_TYPE_U_EXT, RUN_TYPE_LF_EXT, - RUN_TYPE_U_EXT2, - RUN_TYPE_L_EXT2, - RUN_TYPE_U_EXT3, + RUN_TYPE_UF_EXT2, + RUN_TYPE_LF_EXT2, + RUN_TYPE_UF_EXT3, }; +static int lre_case_conv1(uint32_t c, int conv_type) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + lre_case_conv(res, c, conv_type); + return res[0]; +} + +/* case conversion using the table entry 'idx' with value 'v' */ +static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v) +{ + uint32_t code, data, type, a, is_lower; + is_lower = (conv_type != 0); + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + data = ((v & 0xf) << 8) | case_conv_table2[idx]; + code = v >> (32 - 17); + switch(type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + if (conv_type == (type & 1) || + (type >= RUN_TYPE_UF && conv_type == 2)) { + c = c - code + (case_conv_table1[data] >> (32 - 17)); + } + break; + case RUN_TYPE_UL: + a = c - code; + if ((a & 1) != (1 - is_lower)) + break; + c = (a ^ 1) + code; + break; + case RUN_TYPE_LSU: + a = c - code; + if (a == 1) { + c += 2 * is_lower - 1; + } else if (a == (1 - is_lower) * 2) { + c += (2 * is_lower - 1) * 2; + } + break; + case RUN_TYPE_U2L_399_EXT2: + if (!is_lower) { + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = 0x399; + return 2; + } else { + c = c - code + case_conv_ext[data & 0x3f]; + } + break; + case RUN_TYPE_UF_D20: + if (conv_type == 1) + break; + c = data + (conv_type == 2) * 0x20; + break; + case RUN_TYPE_UF_D1_EXT: + if (conv_type == 1) + break; + c = case_conv_ext[data] + (conv_type == 2); + break; + case RUN_TYPE_U_EXT: + case RUN_TYPE_LF_EXT: + if (is_lower != (type - RUN_TYPE_U_EXT)) + break; + c = case_conv_ext[data]; + break; + case RUN_TYPE_LF_EXT2: + if (!is_lower) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + return 2; + case RUN_TYPE_UF_EXT2: + if (conv_type == 1) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + } + return 2; + default: + case RUN_TYPE_UF_EXT3: + if (conv_type == 1) + break; + res[0] = case_conv_ext[data >> 8]; + res[1] = case_conv_ext[(data >> 4) & 0xf]; + res[2] = case_conv_ext[data & 0xf]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + res[2] = lre_case_conv1(res[2], 1); + } + return 3; + } + res[0] = c; + return 1; +} + /* conv_type: - 0 = to upper + 0 = to upper 1 = to lower - 2 = case folding (= to lower with modifications) + 2 = case folding (= to lower with modifications) */ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) { @@ -66,10 +166,9 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) } } } else { - uint32_t v, code, data, type, len, a, is_lower; + uint32_t v, code, len; int idx, idx_min, idx_max; - - is_lower = (conv_type != 0); + idx_min = 0; idx_max = countof(case_conv_table1) - 1; while (idx_min <= idx_max) { @@ -82,74 +181,7 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) } else if (c >= code + len) { idx_min = idx + 1; } else { - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - data = ((v & 0xf) << 8) | case_conv_table2[idx]; - switch(type) { - case RUN_TYPE_U: - case RUN_TYPE_L: - case RUN_TYPE_UF: - case RUN_TYPE_LF: - if (conv_type == (type & 1) || - (type >= RUN_TYPE_UF && conv_type == 2)) { - c = c - code + (case_conv_table1[data] >> (32 - 17)); - } - break; - case RUN_TYPE_UL: - a = c - code; - if ((a & 1) != (1 - is_lower)) - break; - c = (a ^ 1) + code; - break; - case RUN_TYPE_LSU: - a = c - code; - if (a == 1) { - c += 2 * is_lower - 1; - } else if (a == (1 - is_lower) * 2) { - c += (2 * is_lower - 1) * 2; - } - break; - case RUN_TYPE_U2L_399_EXT2: - if (!is_lower) { - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = 0x399; - return 2; - } else { - c = c - code + case_conv_ext[data & 0x3f]; - } - break; - case RUN_TYPE_UF_D20: - if (conv_type == 1) - break; - c = data + (conv_type == 2) * 0x20; - break; - case RUN_TYPE_UF_D1_EXT: - if (conv_type == 1) - break; - c = case_conv_ext[data] + (conv_type == 2); - break; - case RUN_TYPE_U_EXT: - case RUN_TYPE_LF_EXT: - if (is_lower != (type - RUN_TYPE_U_EXT)) - break; - c = case_conv_ext[data]; - break; - case RUN_TYPE_U_EXT2: - case RUN_TYPE_L_EXT2: - if (conv_type != (type - RUN_TYPE_U_EXT2)) - break; - res[0] = c - code + case_conv_ext[data >> 6]; - res[1] = case_conv_ext[data & 0x3f]; - return 2; - default: - case RUN_TYPE_U_EXT3: - if (conv_type != 0) - break; - res[0] = case_conv_ext[data >> 8]; - res[1] = case_conv_ext[(data >> 4) & 0xf]; - res[2] = case_conv_ext[data & 0xf]; - return 3; - } - break; + return lre_case_conv_entry(res, c, conv_type, idx, v); } } } @@ -157,13 +189,80 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) return 1; } +static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, BOOL is_unicode) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + int len; + + if (is_unicode) { + len = lre_case_conv_entry(res, c, 2, idx, v); + if (len == 1) { + c = res[0]; + } else { + /* handle the few specific multi-character cases (see + unicode_gen.c:dump_case_folding_special_cases()) */ + if (c == 0xfb06) { + c = 0xfb05; + } else if (c == 0x01fd3) { + c = 0x390; + } else if (c == 0x01fe3) { + c = 0x3b0; + } + } + } else { + if (likely(c < 128)) { + if (c >= 'a' && c <= 'z') + c = c - 'a' + 'A'; + } else { + /* legacy regexp: to upper case if single char >= 128 */ + len = lre_case_conv_entry(res, c, FALSE, idx, v); + if (len == 1 && res[0] >= 128) + c = res[0]; + } + } + return c; +} + +/* JS regexp specific rules for case folding */ +int lre_canonicalize(uint32_t c, BOOL is_unicode) +{ + if (c < 128) { + /* fast case */ + if (is_unicode) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return lre_case_folding_entry(c, idx, v, is_unicode); + } + } + } + return c; +} + static uint32_t get_le24(const uint8_t *ptr) { -#if defined(__x86__) || defined(__x86_64__) - return *(uint16_t *)ptr | (ptr[2] << 16); -#else return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); -#endif } #define UNICODE_INDEX_BLOCK_LEN 32 @@ -208,12 +307,20 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, uint32_t code, b, bit; int pos; const uint8_t *p; - + pos = get_index_pos(&code, c, index_table, index_table_len); if (pos < 0) return FALSE; /* outside the table */ p = table + pos; bit = 0; + /* Compressed run length encoding: + 00..3F: 2 packed lengths: 3-bit + 3-bit + 40..5F: 5-bits plus extra byte for length + 60..7F: 5-bits plus 2 extra bytes for length + 80..FF: 7-bit length + lengths must be incremented to get character count + Ranges alternate between false and true return value. + */ for(;;) { b = *p++; if (b < 64) { @@ -241,7 +348,7 @@ BOOL lre_is_cased(uint32_t c) { uint32_t v, code, len; int idx, idx_min, idx_max; - + idx_min = 0; idx_max = countof(case_conv_table1) - 1; while (idx_min <= idx_max) { @@ -300,7 +407,7 @@ int cr_realloc(CharRange *cr, int size) { int new_size; uint32_t *new_buf; - + if (size > cr->size) { new_size = max_int(size, cr->size * 3 / 2); new_buf = cr->realloc_func(cr->mem_opaque, cr->points, @@ -327,7 +434,7 @@ static void cr_compress(CharRange *cr) { int i, j, k, len; uint32_t *pt; - + pt = cr->points; len = cr->len; i = 0; @@ -357,7 +464,7 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, { int a_idx, b_idx, is_in; uint32_t v; - + a_idx = 0; b_idx = 0; for(;;) { @@ -392,6 +499,9 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, case CR_OP_XOR: is_in = (a_idx & 1) ^ (b_idx & 1); break; + case CR_OP_SUB: + is_in = (a_idx & 1) & ((b_idx & 1) ^ 1); + break; default: abort(); } @@ -404,14 +514,14 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, return 0; } -int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) +int cr_op1(CharRange *cr, const uint32_t *b_pt, int b_len, int op) { CharRange a = *cr; int ret; cr->len = 0; cr->size = 0; cr->points = NULL; - ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); + ret = cr_op(cr, a.points, a.len, b_pt, b_len, op); cr_free(&a); return ret; } @@ -430,6 +540,207 @@ int cr_invert(CharRange *cr) return 0; } +#define CASE_U (1 << 0) +#define CASE_L (1 << 1) +#define CASE_F (1 << 2) + +/* use the case conversion table to generate range of characters. + CASE_U: set char if modified by uppercasing, + CASE_L: set char if modified by lowercasing, + CASE_F: set char if modified by case folding, + */ +static int unicode_case1(CharRange *cr, int case_mask) +{ +#define MR(x) (1 << RUN_TYPE_ ## x) + const uint32_t tab_run_mask[3] = { + MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | + MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3), + + MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2), + + MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3), + }; +#undef MR + uint32_t mask, v, code, type, len, i, idx; + + if (case_mask == 0) + return 0; + mask = 0; + for(i = 0; i < 3; i++) { + if ((case_mask >> i) & 1) + mask |= tab_run_mask[i]; + } + for(idx = 0; idx < countof(case_conv_table1); idx++) { + v = case_conv_table1[idx]; + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if ((mask >> type) & 1) { + // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); + switch(type) { + case RUN_TYPE_UL: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + code += ((case_mask & CASE_U) != 0); + for(i = 0; i < len; i += 2) { + if (cr_add_interval(cr, code + i, code + i + 1)) + return -1; + } + break; + case RUN_TYPE_LSU: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + if (!(case_mask & CASE_U)) { + if (cr_add_interval(cr, code, code + 1)) + return -1; + } + if (cr_add_interval(cr, code + 1, code + 2)) + return -1; + if (case_mask & CASE_U) { + if (cr_add_interval(cr, code + 2, code + 3)) + return -1; + } + break; + default: + def_case: + if (cr_add_interval(cr, code, code + len)) + return -1; + break; + } + } + } + return 0; +} + +static int point_cmp(const void *p1, const void *p2, void *arg) +{ + uint32_t v1 = *(uint32_t *)p1; + uint32_t v2 = *(uint32_t *)p2; + return (v1 > v2) - (v1 < v2); +} + +static void cr_sort_and_remove_overlap(CharRange *cr) +{ + uint32_t start, end, start1, end1, i, j; + + /* the resulting ranges are not necessarily sorted and may overlap */ + rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); + j = 0; + for(i = 0; i < cr->len; ) { + start = cr->points[i]; + end = cr->points[i + 1]; + i += 2; + while (i < cr->len) { + start1 = cr->points[i]; + end1 = cr->points[i + 1]; + if (start1 > end) { + /* |------| + * |-------| */ + break; + } else if (end1 <= end) { + /* |------| + * |--| */ + i += 2; + } else { + /* |------| + * |-------| */ + end = end1; + i += 2; + } + } + cr->points[j] = start; + cr->points[j + 1] = end; + j += 2; + } + cr->len = j; +} + +/* canonicalize a character set using the JS regex case folding rules + (see lre_canonicalize()) */ +int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode) +{ + CharRange cr_inter, cr_mask, cr_result, cr_sub; + uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; + + cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_result, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func); + + if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U)) + goto fail; + if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + if (cr_invert(&cr_mask)) + goto fail; + if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + /* cr_inter = cr & cr_mask */ + /* cr_sub = cr & ~cr_mask */ + + /* use the case conversion table to compute the result */ + d_start = -1; + d_end = -1; + idx = 0; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + for(i = 0; i < cr_inter.len; i += 2) { + start = cr_inter.points[i]; + end = cr_inter.points[i + 1]; + + for(c = start; c < end; c++) { + for(;;) { + if (c >= code && c < code + len) + break; + idx++; + assert(idx < countof(case_conv_table1)); + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + } + d = lre_case_folding_entry(c, idx, v, is_unicode); + /* try to merge with the current interval */ + if (d_start == -1) { + d_start = d; + d_end = d + 1; + } else if (d_end == d) { + d_end++; + } else { + cr_add_interval(&cr_result, d_start, d_end); + d_start = d; + d_end = d + 1; + } + } + } + if (d_start != -1) { + if (cr_add_interval(&cr_result, d_start, d_end)) + goto fail; + } + + /* the resulting ranges are not necessarily sorted and may overlap */ + cr_sort_and_remove_overlap(&cr_result); + + /* or with the character not affected by the case folding */ + cr->len = 0; + if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION)) + goto fail; + + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return 0; + fail: + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return -1; +} + #ifdef CONFIG_ALL_UNICODE BOOL lre_is_id_start(uint32_t c) @@ -658,7 +969,7 @@ static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) { uint32_t v, type, is_compat, code, len; int idx_min, idx_max, idx; - + idx_min = 0; idx_max = countof(unicode_decomp_table1) - 1; while (idx_min <= idx_max) { @@ -688,7 +999,7 @@ static int unicode_compose_pair(uint32_t c0, uint32_t c1) uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; int idx_min, idx_max, idx, d; uint32_t pair[2]; - + idx_min = 0; idx_max = countof(unicode_comp_table) - 1; while (idx_min <= idx_max) { @@ -724,12 +1035,19 @@ static int unicode_get_cc(uint32_t c) uint32_t code, n, type, cc, c1, b; int pos; const uint8_t *p; - + pos = get_index_pos(&code, c, unicode_cc_index, sizeof(unicode_cc_index) / 3); if (pos < 0) return 0; p = unicode_cc_table + pos; + /* Compressed run length encoding: + - 2 high order bits are combining class type + - 0:0, 1:230, 2:extra byte linear progression, 3:extra byte + - 00..2F: range length (add 1) + - 30..37: 3-bit range-length + 1 extra byte + - 38..3F: 3-bit range-length + 2 extra byte + */ for(;;) { b = *p++; type = b >> 6; @@ -773,7 +1091,7 @@ static int unicode_get_cc(uint32_t c) static void sort_cc(int *buf, int len) { int i, j, k, cc, cc1, start, ch1; - + for(i = 0; i < len; i++) { cc = unicode_get_cc(buf[i]); if (cc != 0) { @@ -812,7 +1130,7 @@ static void to_nfd_rec(DynBuf *dbuf, uint32_t c, v; int i, l; uint32_t res[UNICODE_DECOMP_LEN_MAX]; - + for(i = 0; i < src_len; i++) { c = src[i]; if (c >= 0xac00 && c < 0xd7a4) { @@ -857,7 +1175,7 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; BOOL is_compat; DynBuf dbuf_s, *dbuf = &dbuf_s; - + is_compat = n_type >> 1; dbuf_init2(dbuf, opaque, realloc_func); @@ -885,15 +1203,15 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, } buf = (int *)dbuf->buf; buf_len = dbuf->size / sizeof(int); - + sort_cc(buf, buf_len); - + if (buf_len <= 1 || (n_type & 1) != 0) { /* NFD / NFKD */ *pdst = (uint32_t *)buf; return buf_len; } - + i = 1; out_len = 1; while (i < buf_len) { @@ -930,7 +1248,7 @@ static int unicode_find_name(const char *name_table, const char *name) const char *p, *r; int pos; size_t name_len, len; - + p = name_table; pos = 0; name_len = strlen(name); @@ -963,13 +1281,11 @@ int unicode_script(CharRange *cr, CharRange cr1_s, *cr1; CharRange cr2_s, *cr2 = &cr2_s; BOOL is_common; - + script_idx = unicode_find_name(unicode_script_name_table, script_name); if (script_idx < 0) return -2; - /* Note: we remove the "Unknown" Script */ - script_idx += UNICODE_SCRIPT_Unknown + 1; - + is_common = (script_idx == UNICODE_SCRIPT_Common || script_idx == UNICODE_SCRIPT_Inherited); if (is_ext) { @@ -998,17 +1314,21 @@ int unicode_script(CharRange *cr, n |= *p++; n += 96 + (1 << 12); } - if (type == 0) - v = 0; - else - v = *p++; c1 = c + n + 1; - if (v == script_idx) { - if (cr_add_interval(cr1, c, c1)) - goto fail; + if (type != 0) { + v = *p++; + if (v == script_idx || script_idx == UNICODE_SCRIPT_Unknown) { + if (cr_add_interval(cr1, c, c1)) + goto fail; + } } c = c1; } + if (script_idx == UNICODE_SCRIPT_Unknown) { + /* Unknown is all the characters outside scripts */ + if (cr_invert(cr1)) + goto fail; + } if (is_ext) { /* add the script extensions */ @@ -1082,6 +1402,15 @@ static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) p = unicode_gc_table; p_end = unicode_gc_table + countof(unicode_gc_table); c = 0; + /* Compressed range encoding: + initial byte: + bits 0..4: category number (special case 31) + bits 5..7: range length (add 1) + special case bits 5..7 == 7: read an extra byte + - 00..7F: range length (add 7 + 1) + - 80..BF: 6-bits plus extra byte for range length (add 7 + 128) + - C0..FF: 6-bits plus 2 extra bytes for range length (add 7 + 128 + 16384) + */ while (p < p_end) { b = *p++; n = b >> 5; @@ -1135,6 +1464,14 @@ static int unicode_prop1(CharRange *cr, int prop_idx) p_end = p + unicode_prop_len_table[prop_idx]; c = 0; bit = 0; + /* Compressed range encoding: + 00..3F: 2 packed lengths: 3-bit + 3-bit + 40..5F: 5-bits plus extra byte for length + 60..7F: 5-bits plus 2 extra bytes for length + 80..FF: 7-bit length + lengths must be incremented to get character count + Ranges alternate between false and true return value. + */ while (p < p_end) { c0 = c; b = *p++; @@ -1165,78 +1502,6 @@ static int unicode_prop1(CharRange *cr, int prop_idx) return 0; } -#define CASE_U (1 << 0) -#define CASE_L (1 << 1) -#define CASE_F (1 << 2) - -/* use the case conversion table to generate range of characters. - CASE_U: set char if modified by uppercasing, - CASE_L: set char if modified by lowercasing, - CASE_F: set char if modified by case folding, - */ -static int unicode_case1(CharRange *cr, int case_mask) -{ -#define MR(x) (1 << RUN_TYPE_ ## x) - const uint32_t tab_run_mask[3] = { - MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | - MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3), - - MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2), - - MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT), - }; -#undef MR - uint32_t mask, v, code, type, len, i, idx; - - if (case_mask == 0) - return 0; - mask = 0; - for(i = 0; i < 3; i++) { - if ((case_mask >> i) & 1) - mask |= tab_run_mask[i]; - } - for(idx = 0; idx < countof(case_conv_table1); idx++) { - v = case_conv_table1[idx]; - type = (v >> (32 - 17 - 7 - 4)) & 0xf; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if ((mask >> type) & 1) { - // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); - switch(type) { - case RUN_TYPE_UL: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - code += ((case_mask & CASE_U) != 0); - for(i = 0; i < len; i += 2) { - if (cr_add_interval(cr, code + i, code + i + 1)) - return -1; - } - break; - case RUN_TYPE_LSU: - if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) - goto def_case; - if (!(case_mask & CASE_U)) { - if (cr_add_interval(cr, code, code + 1)) - return -1; - } - if (cr_add_interval(cr, code + 1, code + 2)) - return -1; - if (case_mask & CASE_U) { - if (cr_add_interval(cr, code + 2, code + 3)) - return -1; - } - break; - default: - def_case: - if (cr_add_interval(cr, code, code + len)) - return -1; - break; - } - } - } - return 0; -} - typedef enum { POP_GC, POP_PROP, @@ -1256,7 +1521,7 @@ static int unicode_prop_ops(CharRange *cr, ...) CharRange stack[POP_STACK_LEN_MAX]; int stack_len, op, ret, i; uint32_t a; - + va_start(ap, cr); stack_len = 0; for(;;) { @@ -1294,6 +1559,7 @@ static int unicode_prop_ops(CharRange *cr, ...) cr2 = &stack[stack_len - 1]; cr3 = &stack[stack_len++]; cr_init(cr3, cr->mem_opaque, cr->realloc_func); + /* CR_OP_XOR may be used here */ if (cr_op(cr3, cr1->points, cr1->len, cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) goto fail; @@ -1342,7 +1608,7 @@ int unicode_general_category(CharRange *cr, const char *gc_name) { int gc_idx; uint32_t gc_mask; - + gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); if (gc_idx < 0) return -2; @@ -1360,7 +1626,7 @@ int unicode_general_category(CharRange *cr, const char *gc_name) int unicode_prop(CharRange *cr, const char *prop_name) { int prop_idx, ret; - + prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); if (prop_idx < 0) return -2; @@ -1554,3 +1820,304 @@ int unicode_prop(CharRange *cr, const char *prop_name) } #endif /* CONFIG_ALL_UNICODE */ + +/*---- lre codepoint categorizing functions ----*/ + +#define S UNICODE_C_SPACE +#define D UNICODE_C_DIGIT +#define X UNICODE_C_XDIGIT +#define U UNICODE_C_UPPER +#define L UNICODE_C_LOWER +#define _ UNICODE_C_UNDER +#define d UNICODE_C_DOLLAR + +uint8_t const lre_ctype_bits[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, S, S, S, S, S, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + S, 0, 0, 0, d, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + X|D, X|D, X|D, X|D, X|D, X|D, X|D, X|D, + X|D, X|D, 0, 0, 0, 0, 0, 0, + + 0, X|U, X|U, X|U, X|U, X|U, X|U, U, + U, U, U, U, U, U, U, U, + U, U, U, U, U, U, U, U, + U, U, U, 0, 0, 0, 0, _, + + 0, X|L, X|L, X|L, X|L, X|L, X|L, L, + L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, L, + L, L, L, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + S, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#undef S +#undef D +#undef X +#undef U +#undef L +#undef _ +#undef d + +/* code point ranges for Zs,Zl or Zp property */ +static const uint16_t char_range_s[] = { + 10, + 0x0009, 0x000D + 1, + 0x0020, 0x0020 + 1, + 0x00A0, 0x00A0 + 1, + 0x1680, 0x1680 + 1, + 0x2000, 0x200A + 1, + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + 0x2028, 0x2029 + 1, + 0x202F, 0x202F + 1, + 0x205F, 0x205F + 1, + 0x3000, 0x3000 + 1, + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + 0xFEFF, 0xFEFF + 1, +}; + +BOOL lre_is_space_non_ascii(uint32_t c) +{ + size_t i, n; + + n = countof(char_range_s); + for(i = 5; i < n; i += 2) { + uint32_t low = char_range_s[i]; + uint32_t high = char_range_s[i + 1]; + if (c < low) + return FALSE; + if (c < high) + return TRUE; + } + return FALSE; +} + +#define SEQ_MAX_LEN 16 + +static int unicode_sequence_prop1(int seq_prop_idx, UnicodeSequencePropCB *cb, void *opaque, + CharRange *cr) +{ + int i, c, j; + uint32_t seq[SEQ_MAX_LEN]; + + switch(seq_prop_idx) { + case UNICODE_SEQUENCE_PROP_Basic_Emoji: + if (unicode_prop1(cr, UNICODE_PROP_Basic_Emoji1) < 0) + return -1; + for(i = 0; i < cr->len; i += 2) { + for(c = cr->points[i]; c < cr->points[i + 1]; c++) { + seq[0] = c; + cb(opaque, seq, 1); + } + } + + cr->len = 0; + + if (unicode_prop1(cr, UNICODE_PROP_Basic_Emoji2) < 0) + return -1; + for(i = 0; i < cr->len; i += 2) { + for(c = cr->points[i]; c < cr->points[i + 1]; c++) { + seq[0] = c; + seq[1] = 0xfe0f; + cb(opaque, seq, 2); + } + } + + break; + case UNICODE_SEQUENCE_PROP_RGI_Emoji_Modifier_Sequence: + if (unicode_prop1(cr, UNICODE_PROP_Emoji_Modifier_Base) < 0) + return -1; + for(i = 0; i < cr->len; i += 2) { + for(c = cr->points[i]; c < cr->points[i + 1]; c++) { + for(j = 0; j < 5; j++) { + seq[0] = c; + seq[1] = 0x1f3fb + j; + cb(opaque, seq, 2); + } + } + } + break; + case UNICODE_SEQUENCE_PROP_RGI_Emoji_Flag_Sequence: + if (unicode_prop1(cr, UNICODE_PROP_RGI_Emoji_Flag_Sequence) < 0) + return -1; + for(i = 0; i < cr->len; i += 2) { + for(c = cr->points[i]; c < cr->points[i + 1]; c++) { + int c0, c1; + c0 = c / 26; + c1 = c % 26; + seq[0] = 0x1F1E6 + c0; + seq[1] = 0x1F1E6 + c1; + cb(opaque, seq, 2); + } + } + break; + case UNICODE_SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence: + { + int len, code, pres, k, mod, mod_count, mod_pos[2], hc_pos, n_mod, n_hc, mod1; + int mod_idx, hc_idx, i0, i1; + const uint8_t *tab = unicode_rgi_emoji_zwj_sequence; + + for(i = 0; i < countof(unicode_rgi_emoji_zwj_sequence);) { + len = tab[i++]; + k = 0; + mod = 0; + mod_count = 0; + hc_pos = -1; + for(j = 0; j < len; j++) { + code = tab[i++]; + code |= tab[i++] << 8; + pres = code >> 15; + mod1 = (code >> 13) & 3; + code &= 0x1fff; + if (code < 0x1000) { + c = code + 0x2000; + } else { + c = 0x1f000 + (code - 0x1000); + } + if (c == 0x1f9b0) + hc_pos = k; + seq[k++] = c; + if (mod1 != 0) { + assert(mod_count < 2); + mod = mod1; + mod_pos[mod_count++] = k; + seq[k++] = 0; /* will be filled later */ + } + if (pres) { + seq[k++] = 0xfe0f; + } + if (j < len - 1) { + seq[k++] = 0x200d; + } + } + + /* genrate all the variants */ + switch(mod) { + case 1: + n_mod = 5; + break; + case 2: + n_mod = 25; + break; + case 3: + n_mod = 20; + break; + default: + n_mod = 1; + break; + } + if (hc_pos >= 0) + n_hc = 4; + else + n_hc = 1; + for(hc_idx = 0; hc_idx < n_hc; hc_idx++) { + for(mod_idx = 0; mod_idx < n_mod; mod_idx++) { + if (hc_pos >= 0) + seq[hc_pos] = 0x1f9b0 + hc_idx; + + switch(mod) { + case 1: + seq[mod_pos[0]] = 0x1f3fb + mod_idx; + break; + case 2: + case 3: + i0 = mod_idx / 5; + i1 = mod_idx % 5; + /* avoid identical values */ + if (mod == 3 && i0 >= i1) + i0++; + seq[mod_pos[0]] = 0x1f3fb + i0; + seq[mod_pos[1]] = 0x1f3fb + i1; + break; + default: + break; + } +#if 0 + for(j = 0; j < k; j++) + printf(" %04x", seq[j]); + printf("\n"); +#endif + cb(opaque, seq, k); + } + } + } + } + break; + case UNICODE_SEQUENCE_PROP_RGI_Emoji_Tag_Sequence: + { + for(i = 0; i < countof(unicode_rgi_emoji_tag_sequence);) { + j = 0; + seq[j++] = 0x1F3F4; + for(;;) { + c = unicode_rgi_emoji_tag_sequence[i++]; + if (c == 0x00) + break; + seq[j++] = 0xe0000 + c; + } + seq[j++] = 0xe007f; + cb(opaque, seq, j); + } + } + break; + case UNICODE_SEQUENCE_PROP_Emoji_Keycap_Sequence: + if (unicode_prop1(cr, UNICODE_PROP_Emoji_Keycap_Sequence) < 0) + return -1; + for(i = 0; i < cr->len; i += 2) { + for(c = cr->points[i]; c < cr->points[i + 1]; c++) { + seq[0] = c; + seq[1] = 0xfe0f; + seq[2] = 0x20e3; + cb(opaque, seq, 3); + } + } + break; + case UNICODE_SEQUENCE_PROP_RGI_Emoji: + /* all prevous sequences */ + for(i = UNICODE_SEQUENCE_PROP_Basic_Emoji; i <= UNICODE_SEQUENCE_PROP_RGI_Emoji_ZWJ_Sequence; i++) { + int ret; + ret = unicode_sequence_prop1(i, cb, opaque, cr); + if (ret < 0) + return ret; + cr->len = 0; + } + break; + default: + return -2; + } + return 0; +} + +/* build a unicode sequence property */ +/* return -2 if not found, -1 if other error. 'cr' is used as temporary memory. */ +int unicode_sequence_prop(const char *prop_name, UnicodeSequencePropCB *cb, void *opaque, + CharRange *cr) +{ + int seq_prop_idx; + seq_prop_idx = unicode_find_name(unicode_sequence_prop_name_table, prop_name); + if (seq_prop_idx < 0) + return -2; + return unicode_sequence_prop1(seq_prop_idx, cb, opaque, cr); +} diff --git a/quickjs/libunicode.h b/quickjs/libunicode.h index cfa600a50d..5d964e40f7 100644 --- a/quickjs/libunicode.h +++ b/quickjs/libunicode.h @@ -1,6 +1,6 @@ /* * Unicode utilities - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,26 +24,13 @@ #ifndef LIBUNICODE_H #define LIBUNICODE_H -#include - -#define LRE_BOOL int /* for documentation purposes */ +#include /* define it to include all the unicode tables (40KB larger) */ #define CONFIG_ALL_UNICODE #define LRE_CC_RES_LEN_MAX 3 -typedef enum { - UNICODE_NFC, - UNICODE_NFD, - UNICODE_NFKC, - UNICODE_NFKD, -} UnicodeNormalizationEnum; - -int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); -LRE_BOOL lre_is_cased(uint32_t c); -LRE_BOOL lre_is_case_ignorable(uint32_t c); - /* char ranges */ typedef struct { @@ -58,6 +45,7 @@ typedef enum { CR_OP_UNION, CR_OP_INTER, CR_OP_XOR, + CR_OP_SUB, } CharRangeOpEnum; void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); @@ -86,25 +74,28 @@ static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) return 0; } -int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); +int cr_op1(CharRange *cr, const uint32_t *b_pt, int b_len, int op); static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) { uint32_t b_pt[2]; b_pt[0] = c1; b_pt[1] = c2 + 1; - return cr_union1(cr, b_pt, 2); + return cr_op1(cr, b_pt, 2, CR_OP_UNION); } -int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, - const uint32_t *b_pt, int b_len, int op); - int cr_invert(CharRange *cr); -#ifdef CONFIG_ALL_UNICODE +int cr_regexp_canonicalize(CharRange *cr, int is_unicode); -LRE_BOOL lre_is_id_start(uint32_t c); -LRE_BOOL lre_is_id_continue(uint32_t c); +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, UnicodeNormalizationEnum n_type, @@ -112,13 +103,84 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, /* Unicode character range functions */ -int unicode_script(CharRange *cr, - const char *script_name, LRE_BOOL is_ext); +int unicode_script(CharRange *cr, const char *script_name, int is_ext); int unicode_general_category(CharRange *cr, const char *gc_name); int unicode_prop(CharRange *cr, const char *prop_name); -#endif /* CONFIG_ALL_UNICODE */ +typedef void UnicodeSequencePropCB(void *opaque, const uint32_t *buf, int len); +int unicode_sequence_prop(const char *prop_name, UnicodeSequencePropCB *cb, void *opaque, + CharRange *cr); + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +int lre_canonicalize(uint32_t c, int is_unicode); + +/* Code point type categories */ +enum { + UNICODE_C_SPACE = (1 << 0), + UNICODE_C_DIGIT = (1 << 1), + UNICODE_C_UPPER = (1 << 2), + UNICODE_C_LOWER = (1 << 3), + UNICODE_C_UNDER = (1 << 4), + UNICODE_C_DOLLAR = (1 << 5), + UNICODE_C_XDIGIT = (1 << 6), +}; +extern uint8_t const lre_ctype_bits[256]; + +/* zero or non-zero return value */ +int lre_is_cased(uint32_t c); +int lre_is_case_ignorable(uint32_t c); +int lre_is_id_start(uint32_t c); +int lre_is_id_continue(uint32_t c); + +static inline int lre_is_space_byte(uint8_t c) { + return lre_ctype_bits[c] & UNICODE_C_SPACE; +} + +static inline int lre_is_id_start_byte(uint8_t c) { + return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER | + UNICODE_C_UNDER | UNICODE_C_DOLLAR); +} + +static inline int lre_is_id_continue_byte(uint8_t c) { + return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER | + UNICODE_C_UNDER | UNICODE_C_DOLLAR | + UNICODE_C_DIGIT); +} + +int lre_is_space_non_ascii(uint32_t c); + +static inline int lre_is_space(uint32_t c) { + if (c < 256) + return lre_is_space_byte(c); + else + return lre_is_space_non_ascii(c); +} + +static inline int lre_js_is_ident_first(uint32_t c) { + if (c < 128) { + return lre_is_id_start_byte(c); + } else { +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_start(c); +#else + return !lre_is_space_non_ascii(c); +#endif + } +} -#undef LRE_BOOL +static inline int lre_js_is_ident_next(uint32_t c) { + if (c < 128) { + return lre_is_id_continue_byte(c); + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ + if (c >= 0x200C && c <= 0x200D) + return TRUE; +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_continue(c); +#else + return !lre_is_space_non_ascii(c); +#endif + } +} #endif /* LIBUNICODE_H */ diff --git a/quickjs/list.h b/quickjs/list.h index 0a1bc5a493..809831115f 100644 --- a/quickjs/list.h +++ b/quickjs/list.h @@ -1,6 +1,6 @@ /* * Linux klist like system - * + * * Copyright (c) 2016-2017 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -36,8 +36,7 @@ struct list_head { #define LIST_HEAD_INIT(el) { &(el), &(el) } /* return the pointer of type 'type *' containing 'el' as field 'member' */ -#define list_entry(el, type, member) \ - ((type *)((uint8_t *)(el) - offsetof(type, member))) +#define list_entry(el, type, member) container_of(el, type, member) static inline void init_list_head(struct list_head *head) { @@ -46,7 +45,7 @@ static inline void init_list_head(struct list_head *head) } /* insert 'el' between 'prev' and 'next' */ -static inline void __list_add(struct list_head *el, +static inline void __list_add(struct list_head *el, struct list_head *prev, struct list_head *next) { prev->next = el; diff --git a/quickjs/quickjs-atom.h b/quickjs/quickjs-atom.h index 81c65b99d8..425c2e909a 100644 --- a/quickjs/quickjs-atom.h +++ b/quickjs/quickjs-atom.h @@ -1,6 +1,6 @@ /* * QuickJS atom definitions - * + * * Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Charlie Gordon * @@ -81,7 +81,9 @@ DEF(empty_string, "") DEF(length, "length") DEF(fileName, "fileName") DEF(lineNumber, "lineNumber") +DEF(columnNumber, "columnNumber") DEF(message, "message") +DEF(cause, "cause") DEF(errors, "errors") DEF(stack, "stack") DEF(name, "name") @@ -166,22 +168,26 @@ DEF(revoke, "revoke") DEF(async, "async") DEF(exec, "exec") DEF(groups, "groups") +DEF(indices, "indices") DEF(status, "status") DEF(reason, "reason") DEF(globalThis, "globalThis") DEF(bigint, "bigint") -#ifdef CONFIG_BIGNUM -DEF(bigfloat, "bigfloat") -DEF(bigdecimal, "bigdecimal") -DEF(roundingMode, "roundingMode") -DEF(maximumSignificantDigits, "maximumSignificantDigits") -DEF(maximumFractionDigits, "maximumFractionDigits") -#endif -#ifdef CONFIG_ATOMICS +DEF(minus_zero, "-0") +DEF(Infinity, "Infinity") +DEF(minus_Infinity, "-Infinity") +DEF(NaN, "NaN") +DEF(hasIndices, "hasIndices") +DEF(ignoreCase, "ignoreCase") +DEF(multiline, "multiline") +DEF(dotAll, "dotAll") +DEF(sticky, "sticky") +DEF(unicodeSets, "unicodeSets") +/* the following 3 atoms are only used with CONFIG_ATOMICS */ DEF(not_equal, "not-equal") DEF(timed_out, "timed-out") DEF(ok, "ok") -#endif +/* */ DEF(toJSON, "toJSON") /* class names */ DEF(Object, "Object") @@ -202,7 +208,7 @@ DEF(RegExp, "RegExp") DEF(ArrayBuffer, "ArrayBuffer") DEF(SharedArrayBuffer, "SharedArrayBuffer") /* must keep same order as class IDs for typed arrays */ -DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Uint8ClampedArray, "Uint8ClampedArray") DEF(Int8Array, "Int8Array") DEF(Uint8Array, "Uint8Array") DEF(Int16Array, "Int16Array") @@ -211,17 +217,13 @@ DEF(Int32Array, "Int32Array") DEF(Uint32Array, "Uint32Array") DEF(BigInt64Array, "BigInt64Array") DEF(BigUint64Array, "BigUint64Array") +DEF(Float16Array, "Float16Array") DEF(Float32Array, "Float32Array") DEF(Float64Array, "Float64Array") DEF(DataView, "DataView") DEF(BigInt, "BigInt") -#ifdef CONFIG_BIGNUM -DEF(BigFloat, "BigFloat") -DEF(BigFloatEnv, "BigFloatEnv") -DEF(BigDecimal, "BigDecimal") -DEF(OperatorSet, "OperatorSet") -DEF(Operators, "Operators") -#endif +DEF(WeakRef, "WeakRef") +DEF(FinalizationRegistry, "FinalizationRegistry") DEF(Map, "Map") DEF(Set, "Set") /* Map + 1 */ DEF(WeakMap, "WeakMap") /* Map + 2 */ @@ -264,8 +266,5 @@ DEF(Symbol_hasInstance, "Symbol.hasInstance") DEF(Symbol_species, "Symbol.species") DEF(Symbol_unscopables, "Symbol.unscopables") DEF(Symbol_asyncIterator, "Symbol.asyncIterator") -#ifdef CONFIG_BIGNUM -DEF(Symbol_operatorSet, "Symbol.operatorSet") -#endif - + #endif /* DEF */ diff --git a/quickjs/quickjs-libc.c b/quickjs/quickjs-libc.c index f0c61be66f..5af9f2a922 100644 --- a/quickjs/quickjs-libc.c +++ b/quickjs/quickjs-libc.c @@ -1,6 +1,6 @@ /* * QuickJS C library - * + * * Copyright (c) 2017-2021 Fabrice Bellard * Copyright (c) 2017-2021 Charlie Gordon * @@ -22,26 +22,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE #include #include #include #include #include #include -#include #include #include -#include #include #include #include #include -#include #if defined(_WIN32) #include #include -#include +#include +#include +#include +#include +#include +#include "win/dirent.h" +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +#define popen _popen +#define pclose _pclose #else +#include +#include +#include #include #include #include @@ -55,13 +67,6 @@ typedef sig_t sighandler_t; #endif #endif /* __APPLE__ */ -#if defined(__FreeBSD__) -typedef sig_t sighandler_t; -__BEGIN_DECLS -extern char **environ; -__END_DECLS -#endif /* __FreeBSD__ */ - #endif #if !defined(_WIN32) @@ -78,6 +83,10 @@ __END_DECLS #include "list.h" #include "quickjs-libc.h" +#if !defined(PATH_MAX) +#define PATH_MAX 4096 +#endif + /* TODO: - add socket calls */ @@ -96,7 +105,7 @@ typedef struct { typedef struct { struct list_head link; - BOOL has_object; + int timer_id; int64_t timeout; JSValue func; } JSOSTimer; @@ -110,14 +119,22 @@ typedef struct { size_t sab_tab_len; } JSWorkerMessage; +typedef struct JSWaker { +#ifdef _WIN32 + HANDLE handle; +#else + int read_fd; + int write_fd; +#endif +} JSWaker; + typedef struct { int ref_count; #ifdef USE_WORKER pthread_mutex_t mutex; #endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ - int read_fd; - int write_fd; + JSWaker waker; } JSWorkerMessagePipe; typedef struct { @@ -126,12 +143,20 @@ typedef struct { JSValue on_message_func; } JSWorkerMessageHandler; +typedef struct { + struct list_head link; + JSValue promise; + JSValue reason; +} JSRejectedPromiseEntry; + typedef struct JSThreadState { struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */ + struct list_head rejected_promise_list; /* list of JSRejectedPromiseEntry.link */ int eval_script_recurse; /* only used in the main thread */ + int next_timer_id; /* for setTimeout() */ /* not used in the main thread */ JSWorkerMessagePipe *recv_pipe, *send_pipe; } JSThreadState; @@ -149,6 +174,7 @@ static BOOL my_isdigit(int c) return (c >= '0' && c <= '9'); } +/* XXX: use 'o' and 'O' for object using JS_PrintValue() ? */ static JSValue js_printf_internal(JSContext *ctx, int argc, JSValueConst *argv, FILE *fp) { @@ -156,7 +182,7 @@ static JSValue js_printf_internal(JSContext *ctx, uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; JSValue res; DynBuf dbuf; - const char *fmt_str; + const char *fmt_str = NULL; const uint8_t *fmt, *fmt_end; const uint8_t *p; char *q; @@ -187,7 +213,7 @@ static JSValue js_printf_internal(JSContext *ctx, break; q = fmtbuf; *q++ = *fmt++; /* copy '%' */ - + /* flags */ for(;;) { c = *fmt; @@ -241,14 +267,14 @@ static JSValue js_printf_internal(JSContext *ctx, if (*fmt == 'l') { mod = *fmt++; } - + /* type */ c = *fmt++; if (q >= fmtbuf + sizeof(fmtbuf) - 1) goto invalid; *q++ = c; *q = '\0'; - + switch (c) { case 'c': if (i >= argc) @@ -257,7 +283,7 @@ static JSValue js_printf_internal(JSContext *ctx, string_arg = JS_ToCString(ctx, argv[i++]); if (!string_arg) goto fail; - int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); + int32_arg = unicode_from_utf8((const uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); JS_FreeCString(ctx, string_arg); } else { if (JS_ToInt32(ctx, &int32_arg, argv[i++])) @@ -270,7 +296,7 @@ static JSValue js_printf_internal(JSContext *ctx, len = unicode_to_utf8(cbuf, int32_arg); dbuf_put(&dbuf, cbuf, len); break; - + case 'd': case 'i': case 'o': @@ -315,7 +341,7 @@ static JSValue js_printf_internal(JSContext *ctx, dbuf_printf_fun(&dbuf, fmtbuf, string_arg); JS_FreeCString(ctx, string_arg); break; - + case 'e': case 'f': case 'g': @@ -330,11 +356,11 @@ static JSValue js_printf_internal(JSContext *ctx, goto fail; dbuf_printf_fun(&dbuf, fmtbuf, double_arg); break; - + case '%': dbuf_putc(&dbuf, '%'); break; - + default: /* XXX: should support an extension mechanism */ invalid: @@ -361,6 +387,7 @@ static JSValue js_printf_internal(JSContext *ctx, return res; fail: + JS_FreeCString(ctx, fmt_str); dbuf_free(&dbuf); return JS_EXCEPTION; } @@ -371,7 +398,7 @@ uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) uint8_t *buf; size_t buf_len; long lret; - + f = fopen(filename, "rb"); if (!f) return NULL; @@ -418,7 +445,7 @@ static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, const char *filename; JSValue ret; size_t buf_len; - + filename = JS_ToCString(ctx, argv[0]); if (!filename) return JS_EXCEPTION; @@ -443,7 +470,7 @@ static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val, const char *filename; JSValue ret; size_t buf_len; - + filename = JS_ToCString(ctx, argv[0]); if (!filename) return JS_EXCEPTION; @@ -475,7 +502,7 @@ static JSModuleDef *js_module_loader_so(JSContext *ctx, void *hd; JSInitModuleFunc *init; char *filename; - + if (!strchr(module_name, '/')) { /* must add a '/' so that the DLL is not searched in the system library paths */ @@ -487,7 +514,7 @@ static JSModuleDef *js_module_loader_so(JSContext *ctx, } else { filename = (char *)module_name; } - + /* C module */ hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); if (filename != module_name) @@ -526,7 +553,7 @@ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, JSValue meta_obj; JSAtom module_name_atom; const char *module_name; - + assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); m = JS_VALUE_GET_PTR(func_val); @@ -557,7 +584,7 @@ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, pstrcpy(buf, sizeof(buf), module_name); } JS_FreeCString(ctx, module_name); - + meta_obj = JS_GetImportMeta(ctx, m); if (JS_IsException(meta_obj)) return -1; @@ -571,36 +598,138 @@ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, return 0; } -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque) +static int json_module_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue val; + val = JS_GetModulePrivateValue(ctx, m); + JS_SetModuleExport(ctx, m, "default", val); + return 0; +} + +static JSModuleDef *create_json_module(JSContext *ctx, const char *module_name, JSValue val) { JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, json_module_init); + if (!m) { + JS_FreeValue(ctx, val); + return NULL; + } + /* only export the "default" symbol which will contain the JSON object */ + JS_AddModuleExport(ctx, m, "default"); + JS_SetModulePrivateValue(ctx, m, val); + return m; +} + +/* in order to conform with the specification, only the keys should be + tested and not the associated values. */ +int js_module_check_attributes(JSContext *ctx, void *opaque, + JSValueConst attributes) +{ + JSPropertyEnum *tab; + uint32_t i, len; + int ret; + const char *cstr; + size_t cstr_len; + + if (JS_GetOwnPropertyNames(ctx, &tab, &len, attributes, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK)) + return -1; + ret = 0; + for(i = 0; i < len; i++) { + cstr = JS_AtomToCStringLen(ctx, &cstr_len, tab[i].atom); + if (!cstr) { + ret = -1; + break; + } + if (!(cstr_len == 4 && !memcmp(cstr, "type", cstr_len))) { + JS_ThrowTypeError(ctx, "import attribute '%s' is not supported", cstr); + ret = -1; + } + JS_FreeCString(ctx, cstr); + if (ret) + break; + } + JS_FreePropertyEnum(ctx, tab, len); + return ret; +} +/* return > 0 if the attributes indicate a JSON module */ +int js_module_test_json(JSContext *ctx, JSValueConst attributes) +{ + JSValue str; + const char *cstr; + size_t len; + BOOL res; + + if (JS_IsUndefined(attributes)) + return FALSE; + str = JS_GetPropertyStr(ctx, attributes, "type"); + if (!JS_IsString(str)) + return FALSE; + cstr = JS_ToCStringLen(ctx, &len, str); + JS_FreeValue(ctx, str); + if (!cstr) + return FALSE; + /* XXX: raise an error if unknown type ? */ + if (len == 4 && !memcmp(cstr, "json", len)) { + res = 1; + } else if (len == 5 && !memcmp(cstr, "json5", len)) { + res = 2; + } else { + res = 0; + } + JS_FreeCString(ctx, cstr); + return res; +} + +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque, + JSValueConst attributes) +{ + JSModuleDef *m; + int res; + if (has_suffix(module_name, ".so")) { m = js_module_loader_so(ctx, module_name); } else { size_t buf_len; uint8_t *buf; - JSValue func_val; - + buf = js_load_file(ctx, &buf_len, module_name); if (!buf) { JS_ThrowReferenceError(ctx, "could not load module filename '%s'", module_name); return NULL; } - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - /* XXX: could propagate the exception */ - js_module_set_import_meta(ctx, func_val, TRUE, FALSE); - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); + res = js_module_test_json(ctx, attributes); + if (has_suffix(module_name, ".json") || res > 0) { + /* compile as JSON or JSON5 depending on "type" */ + JSValue val; + int flags; + if (res == 2) + flags = JS_PARSE_JSON_EXT; + else + flags = 0; + val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags); + js_free(ctx, buf); + if (JS_IsException(val)) + return NULL; + m = create_json_module(ctx, module_name, val); + if (!m) + return NULL; + } else { + JSValue func_val; + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* XXX: could propagate the exception */ + js_module_set_import_meta(ctx, func_val, TRUE, FALSE); + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } } return m; } @@ -758,13 +887,17 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, JSValue ret; JSValueConst options_obj; BOOL backtrace_barrier = FALSE; + BOOL is_async = FALSE; int flags; - + if (argc >= 2) { options_obj = argv[1]; if (get_bool_option(ctx, &backtrace_barrier, options_obj, "backtrace_barrier")) return JS_EXCEPTION; + if (get_bool_option(ctx, &is_async, options_obj, + "async")) + return JS_EXCEPTION; } str = JS_ToCStringLen(ctx, &len, argv[0]); @@ -774,9 +907,11 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, /* install the interrupt handler */ JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); } - flags = JS_EVAL_TYPE_GLOBAL; + flags = JS_EVAL_TYPE_GLOBAL; if (backtrace_barrier) flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + if (is_async) + flags |= JS_EVAL_FLAG_ASYNC; ret = JS_Eval(ctx, str, len, "", flags); JS_FreeCString(ctx, str); if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { @@ -786,7 +921,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, /* convert the uncatchable "interrupted" error into a normal error so that it can be caught by the REPL */ if (JS_IsException(ret)) - JS_ResetUncatchableError(ctx); + JS_SetUncatchableException(ctx, FALSE); } return ret; } @@ -878,7 +1013,7 @@ static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, const char *filename, *mode = NULL; FILE *f; int err; - + filename = JS_ToCString(ctx, argv[0]); if (!filename) goto fail; @@ -914,7 +1049,7 @@ static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, const char *filename, *mode = NULL; FILE *f; int err; - + filename = JS_ToCString(ctx, argv[0]); if (!filename) goto fail; @@ -1028,7 +1163,7 @@ static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, if (!f) return JS_EXCEPTION; } - + for(i = 0; i < argc; i++) { str = JS_ToCStringLen(ctx, &len, argv[i]); if (!str) @@ -1065,6 +1200,19 @@ static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, return js_printf_internal(ctx, argc, argv, f); } +static void js_print_value_write(void *opaque, const char *buf, size_t len) +{ + FILE *fo = opaque; + fwrite(buf, 1, len, fo); +} + +static JSValue js_std_file_printObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JS_PrintValue(ctx, js_print_value_write, stdout, argv[0], NULL); + return JS_UNDEFINED; +} + static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1082,7 +1230,7 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, int64_t pos; if (!f) return JS_EXCEPTION; -#if defined(__linux__) +#if defined(__linux__) || defined(__GLIBC__) pos = ftello(f); #else pos = ftell(f); @@ -1105,7 +1253,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (JS_ToInt32(ctx, &whence, argv[1])) return JS_EXCEPTION; -#if defined(__linux__) +#if defined(__linux__) || defined(__GLIBC__) ret = fseeko(f, pos, whence); #else ret = fseek(f, pos, whence); @@ -1159,7 +1307,7 @@ static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, uint64_t pos, len; size_t size, ret; uint8_t *buf; - + if (!f) return JS_EXCEPTION; if (JS_ToIndex(ctx, &pos, argv[1])) @@ -1186,7 +1334,7 @@ static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, int c; DynBuf dbuf; JSValue obj; - + if (!f) return JS_EXCEPTION; @@ -1225,7 +1373,7 @@ static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val, uint64_t max_size64; size_t max_size; JSValueConst max_size_val; - + if (!f) return JS_EXCEPTION; @@ -1281,7 +1429,7 @@ static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, /* urlGet */ -#define URL_GET_PROGRAM "curl -s -i" +#define URL_GET_PROGRAM "curl -s -i --" #define URL_GET_BUF_SIZE 4096 static int http_get_header_line(FILE *f, char *buf, size_t buf_size, @@ -1289,7 +1437,7 @@ static int http_get_header_line(FILE *f, char *buf, size_t buf_size, { int c; char *p; - + p = buf; for(;;) { c = fgetc(f); @@ -1325,21 +1473,21 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, DynBuf cmd_buf; DynBuf data_buf_s, *data_buf = &data_buf_s; DynBuf header_buf_s, *header_buf = &header_buf_s; - char *buf; + char *buf; size_t i, len; - int c, status; + int status; JSValue response = JS_UNDEFINED, ret_obj; JSValueConst options_obj; FILE *f; BOOL binary_flag, full_flag; - + url = JS_ToCString(ctx, argv[0]); if (!url) return JS_EXCEPTION; - + binary_flag = FALSE; full_flag = FALSE; - + if (argc >= 2) { options_obj = argv[1]; @@ -1352,18 +1500,27 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } } - + js_std_dbuf_init(ctx, &cmd_buf); - dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM); - len = strlen(url); - for(i = 0; i < len; i++) { - c = url[i]; - if (c == '\'' || c == '\\') + dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); + for(i = 0; url[i] != '\0'; i++) { + unsigned char c = url[i]; + switch (c) { + case '\'': + /* shell single quoted string does not support \' */ + dbuf_putstr(&cmd_buf, "'\\''"); + break; + case '[': case ']': case '{': case '}': case '\\': + /* prevent interpretation by curl as range or set specification */ dbuf_putc(&cmd_buf, '\\'); - dbuf_putc(&cmd_buf, c); + /* FALLTHROUGH */ + default: + dbuf_putc(&cmd_buf, c); + break; + } } JS_FreeCString(ctx, url); - dbuf_putstr(&cmd_buf, "''"); + dbuf_putstr(&cmd_buf, "'"); dbuf_putc(&cmd_buf, '\0'); if (dbuf_error(&cmd_buf)) { dbuf_free(&cmd_buf); @@ -1378,7 +1535,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, js_std_dbuf_init(ctx, data_buf); js_std_dbuf_init(ctx, header_buf); - + buf = js_malloc(ctx, URL_GET_BUF_SIZE); if (!buf) goto fail; @@ -1392,7 +1549,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, if (!full_flag && !(status >= 200 && status <= 299)) { goto bad_header; } - + /* wait until there is an empty line */ for(;;) { if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { @@ -1468,7 +1625,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, static JSClassDef js_std_file_class = { "FILE", .finalizer = js_std_file_finalizer, -}; +}; static const JSCFunctionListEntry js_std_error_props[] = { /* various errno values */ @@ -1500,7 +1657,7 @@ static const JSCFunctionListEntry js_std_funcs[] = { JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), JS_CFUNC_DEF("strerror", 1, js_std_strerror ), JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ), - + /* FILE I/O */ JS_CFUNC_DEF("open", 2, js_std_open ), JS_CFUNC_DEF("popen", 2, js_std_popen ), @@ -1513,8 +1670,9 @@ static const JSCFunctionListEntry js_std_funcs[] = { JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), + JS_CFUNC_DEF("__printObject", 1, js_std_file_printObject ), }; - + static const JSCFunctionListEntry js_std_file_proto_funcs[] = { JS_CFUNC_DEF("close", 0, js_std_file_close ), JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), @@ -1539,7 +1697,7 @@ static const JSCFunctionListEntry js_std_file_proto_funcs[] = { static int js_std_init(JSContext *ctx, JSModuleDef *m) { JSValue proto; - + /* FILE class */ /* the class ID is created once */ JS_NewClassID(&js_std_file_class_id); @@ -1620,7 +1778,7 @@ static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, int fd, whence; int64_t pos, ret; BOOL is_bigint; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; is_bigint = JS_IsBigInt(ctx, argv[1]); @@ -1638,14 +1796,14 @@ static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, } static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) + int argc, JSValueConst *argv, int magic) { int fd; uint64_t pos, len; size_t size; ssize_t ret; uint8_t *buf; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; if (JS_ToIndex(ctx, &pos, argv[2])) @@ -1670,7 +1828,7 @@ static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, int fd; if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - return JS_NewBool(ctx, (isatty(fd) != 0)); + return JS_NewBool(ctx, isatty(fd)); } #if defined(_WIN32) @@ -1685,7 +1843,7 @@ static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; handle = (HANDLE)_get_osfhandle(fd); - + if (!GetConsoleScreenBufferInfo(handle, &info)) return JS_NULL; obj = JS_NewArray(ctx); @@ -1724,7 +1882,7 @@ static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, int fd; struct winsize ws; JSValue obj; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && @@ -1753,10 +1911,10 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, { struct termios tty; int fd; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - + memset(&tty, 0, sizeof(tty)); tcgetattr(fd, &tty); oldtty = tty; @@ -1779,11 +1937,11 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, #endif /* !_WIN32 */ static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv) { const char *filename; int ret; - + filename = JS_ToCString(ctx, argv[0]); if (!filename) return JS_EXCEPTION; @@ -1809,7 +1967,7 @@ static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, { const char *oldpath, *newpath; int ret; - + oldpath = JS_ToCString(ctx, argv[0]); if (!oldpath) return JS_EXCEPTION; @@ -1861,7 +2019,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, JSOSRWHandler *rh; int fd; JSValueConst func; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; func = argv[1]; @@ -1935,7 +2093,7 @@ static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, if (!is_main_thread(rt)) return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); - + if (JS_ToUint32(ctx, &sig_num, argv[0])) return JS_EXCEPTION; if (sig_num >= 64) @@ -1977,6 +2135,13 @@ static int64_t get_time_ms(void) clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); } + +static int64_t get_time_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; +} #else /* more portable, but does not work if the date is updated */ static int64_t get_time_ms(void) @@ -1985,43 +2150,28 @@ static int64_t get_time_ms(void) gettimeofday(&tv, NULL); return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); } + +static int64_t get_time_ns(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000000000 + (tv.tv_usec * 1000); +} #endif -static void unlink_timer(JSRuntime *rt, JSOSTimer *th) +static JSValue js_os_now(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - if (th->link.prev) { - list_del(&th->link); - th->link.prev = th->link.next = NULL; - } + return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6); } static void free_timer(JSRuntime *rt, JSOSTimer *th) { + list_del(&th->link); JS_FreeValueRT(rt, th->func); js_free_rt(rt, th); } -static JSClassID js_os_timer_class_id; - -static void js_os_timer_finalizer(JSRuntime *rt, JSValue val) -{ - JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); - if (th) { - th->has_object = FALSE; - if (!th->link.prev) - free_timer(rt, th); - } -} - -static void js_os_timer_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); - if (th) { - JS_MarkValue(rt, th->func, mark_func); - } -} - static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -2030,44 +2180,87 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, int64_t delay; JSValueConst func; JSOSTimer *th; - JSValue obj; func = argv[0]; if (!JS_IsFunction(ctx, func)) return JS_ThrowTypeError(ctx, "not a function"); if (JS_ToInt64(ctx, &delay, argv[1])) return JS_EXCEPTION; - obj = JS_NewObjectClass(ctx, js_os_timer_class_id); - if (JS_IsException(obj)) - return obj; th = js_mallocz(ctx, sizeof(*th)); - if (!th) { - JS_FreeValue(ctx, obj); + if (!th) return JS_EXCEPTION; - } - th->has_object = TRUE; + th->timer_id = ts->next_timer_id; + if (ts->next_timer_id == INT32_MAX) + ts->next_timer_id = 1; + else + ts->next_timer_id++; th->timeout = get_time_ms() + delay; th->func = JS_DupValue(ctx, func); list_add_tail(&th->link, &ts->os_timers); - JS_SetOpaque(obj, th); - return obj; + return JS_NewInt32(ctx, th->timer_id); +} + +static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) +{ + struct list_head *el; + if (timer_id <= 0) + return NULL; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + if (th->timer_id == timer_id) + return th; + } + return NULL; } static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); - if (!th) + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSOSTimer *th; + int timer_id; + + if (JS_ToInt32(ctx, &timer_id, argv[0])) return JS_EXCEPTION; - unlink_timer(JS_GetRuntime(ctx), th); + th = find_timer_by_id(ts, timer_id); + if (!th) + return JS_UNDEFINED; + free_timer(rt, th); return JS_UNDEFINED; } -static JSClassDef js_os_timer_class = { - "OSTimer", - .finalizer = js_os_timer_finalizer, - .gc_mark = js_os_timer_mark, -}; +/* return a promise */ +static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int64_t delay; + JSOSTimer *th; + JSValue promise, resolving_funcs[2]; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return JS_EXCEPTION; + } + th->timer_id = -1; + th->timeout = get_time_ms() + delay; + th->func = JS_DupValue(ctx, resolving_funcs[0]); + list_add_tail(&th->link, &ts->os_timers); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} static void call_handler(JSContext *ctx, JSValueConst func) { @@ -2082,84 +2275,81 @@ static void call_handler(JSContext *ctx, JSValueConst func) JS_FreeValue(ctx, ret); } -#if defined(_WIN32) +#ifdef USE_WORKER -static int js_os_poll(JSContext *ctx) +#ifdef _WIN32 + +static int js_waker_init(JSWaker *w) { - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int min_delay, console_fd; - int64_t cur_time, delay; - JSOSRWHandler *rh; - struct list_head *el; - - /* XXX: handle signals if useful */ + w->handle = CreateEvent(NULL, TRUE, FALSE, NULL); + return w->handle ? 0 : -1; +} - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers)) - return -1; /* no more events */ - - /* XXX: only timers and basic console input are supported */ - if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); - min_delay = 10000; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - delay = th->timeout - cur_time; - if (delay <= 0) { - JSValue func; - /* the timer expired */ - func = th->func; - th->func = JS_UNDEFINED; - unlink_timer(rt, th); - if (!th->has_object) - free_timer(rt, th); - call_handler(ctx, func); - JS_FreeValue(ctx, func); - return 0; - } else if (delay < min_delay) { - min_delay = delay; - } - } - } else { - min_delay = -1; - } +static void js_waker_signal(JSWaker *w) +{ + SetEvent(w->handle); +} - console_fd = -1; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - console_fd = rh->fd; +static void js_waker_clear(JSWaker *w) +{ + ResetEvent(w->handle); +} + +static void js_waker_close(JSWaker *w) +{ + CloseHandle(w->handle); + w->handle = INVALID_HANDLE_VALUE; +} + +#else // !_WIN32 + +static int js_waker_init(JSWaker *w) +{ + int fds[2]; + + if (pipe(fds) < 0) + return -1; + w->read_fd = fds[0]; + w->write_fd = fds[1]; + return 0; +} + +static void js_waker_signal(JSWaker *w) +{ + int ret; + + for(;;) { + ret = write(w->write_fd, "", 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) break; - } } +} - if (console_fd >= 0) { - DWORD ti, ret; - HANDLE handle; - if (min_delay == -1) - ti = INFINITE; - else - ti = min_delay; - handle = (HANDLE)_get_osfhandle(console_fd); - ret = WaitForSingleObject(handle, ti); - if (ret == WAIT_OBJECT_0) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { - call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - break; - } - } - } - } else { - Sleep(min_delay); +static void js_waker_clear(JSWaker *w) +{ + uint8_t buf[16]; + int ret; + + for(;;) { + ret = read(w->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; } - return 0; } -#else -#ifdef USE_WORKER +static void js_waker_close(JSWaker *w) +{ + close(w->read_fd); + close(w->write_fd); + w->read_fd = -1; + w->write_fd = -1; +} + +#endif // _WIN32 static void js_free_message(JSWorkerMessage *msg); @@ -2172,7 +2362,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, struct list_head *el; JSWorkerMessage *msg; JSValue obj, data_obj, func, retval; - + pthread_mutex_lock(&ps->mutex); if (!list_empty(&ps->msg_queue)) { el = ps->msg_queue.next; @@ -2181,17 +2371,8 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, /* remove the message from the queue */ list_del(&msg->link); - if (list_empty(&ps->msg_queue)) { - uint8_t buf[16]; - int ret; - for(;;) { - ret = read(ps->read_fd, buf, sizeof(buf)); - if (ret >= 0) - break; - if (errno != EAGAIN && errno != EINTR) - break; - } - } + if (list_empty(&ps->msg_queue)) + js_waker_clear(&ps->waker); pthread_mutex_unlock(&ps->mutex); @@ -2199,7 +2380,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); js_free_message(msg); - + if (JS_IsException(data_obj)) goto fail; obj = JS_NewObject(ctx); @@ -2234,7 +2415,104 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, { return 0; } -#endif +#endif /* !USE_WORKER */ + +#if defined(_WIN32) + +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int min_delay, count; + int64_t cur_time, delay; + JSOSRWHandler *rh; + struct list_head *el; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 + + /* XXX: handle signals if useful */ + + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && + list_empty(&ts->port_list)) { + return -1; /* no more events */ + } + + if (!list_empty(&ts->os_timers)) { + cur_time = get_time_ms(); + min_delay = 10000; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay <= 0) { + JSValue func; + /* the timer expired */ + func = th->func; + th->func = JS_UNDEFINED; + free_timer(rt, th); + call_handler(ctx, func); + JS_FreeValue(ctx, func); + return 0; + } else if (delay < min_delay) { + min_delay = delay; + } + } + } else { + min_delay = -1; + } + + count = 0; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin + if (count == (int)countof(handles)) + break; + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (JS_IsNull(port->on_message_func)) + continue; + handles[count++] = port->recv_pipe->waker.handle; + if (count == (int)countof(handles)) + break; + } + + if (count > 0) { + DWORD ret, timeout = INFINITE; + if (min_delay != -1) + timeout = min_delay; + ret = WaitForMultipleObjects(count, handles, FALSE, timeout); + + if (ret < count) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + goto done; + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (ps->waker.handle == handles[ret]) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } + } + } + } + } else { + Sleep(min_delay); + } + done: + return 0; +} + +#else static int js_os_poll(JSContext *ctx) { @@ -2252,7 +2530,7 @@ static int js_os_poll(JSContext *ctx) unlikely(os_pending_signals != 0)) { JSOSSignalHandler *sh; uint64_t mask; - + list_for_each(el, &ts->os_signal_handlers) { sh = list_entry(el, JSOSSignalHandler, link); mask = (uint64_t)1 << sh->sig_num; @@ -2267,7 +2545,7 @@ static int js_os_poll(JSContext *ctx) if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && list_empty(&ts->port_list)) return -1; /* no more events */ - + if (!list_empty(&ts->os_timers)) { cur_time = get_time_ms(); min_delay = 10000; @@ -2279,9 +2557,7 @@ static int js_os_poll(JSContext *ctx) /* the timer expired */ func = th->func; th->func = JS_UNDEFINED; - unlink_timer(rt, th); - if (!th->has_object) - free_timer(rt, th); + free_timer(rt, th); call_handler(ctx, func); JS_FreeValue(ctx, func); return 0; @@ -2295,7 +2571,7 @@ static int js_os_poll(JSContext *ctx) } else { tvp = NULL; } - + FD_ZERO(&rfds); FD_ZERO(&wfds); fd_max = -1; @@ -2312,8 +2588,8 @@ static int js_os_poll(JSContext *ctx) JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { JSWorkerMessagePipe *ps = port->recv_pipe; - fd_max = max_int(fd_max, ps->read_fd); - FD_SET(ps->read_fd, &rfds); + fd_max = max_int(fd_max, ps->waker.read_fd); + FD_SET(ps->waker.read_fd, &rfds); } } @@ -2339,14 +2615,14 @@ static int js_os_poll(JSContext *ctx) JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { JSWorkerMessagePipe *ps = port->recv_pipe; - if (FD_ISSET(ps->read_fd, &rfds)) { + if (FD_ISSET(ps->waker.read_fd, &rfds)) { if (handle_posted_message(rt, ctx, port)) goto done; } } } } - done: + done: return 0; } #endif /* !_WIN32 */ @@ -2381,7 +2657,7 @@ static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, { char buf[PATH_MAX]; int err; - + if (!getcwd(buf, sizeof(buf))) { buf[0] = '\0'; err = errno; @@ -2410,7 +2686,7 @@ static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, { int mode, ret; const char *path; - + if (argc >= 2) { if (JS_ToInt32(ctx, &mode, argv[1])) return JS_EXCEPTION; @@ -2440,7 +2716,7 @@ static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, JSValue obj; int err; uint32_t len; - + path = JS_ToCString(ctx, argv[0]); if (!path) return JS_EXCEPTION; @@ -2501,12 +2777,14 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, else res = stat(path, &st); #endif + if (res < 0) + err = errno; + else + err = 0; JS_FreeCString(ctx, path); if (res < 0) { - err = errno; obj = JS_NULL; } else { - err = 0; obj = JS_NewObject(ctx); if (JS_IsException(obj)) return JS_EXCEPTION; @@ -2588,7 +2866,7 @@ static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, const char *path; int64_t atime, mtime; int ret; - + if (JS_ToInt64(ctx, &atime, argv[1])) return JS_EXCEPTION; if (JS_ToInt64(ctx, &mtime, argv[2])) @@ -2617,11 +2895,11 @@ static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, /* sleep(delay_ms) */ static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv) { int64_t delay; int ret; - + if (JS_ToInt64(ctx, &delay, argv[0])) return JS_EXCEPTION; if (delay < 0) @@ -2681,11 +2959,11 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, #if !defined(_WIN32) static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv) { const char *target, *linkpath; int err; - + target = JS_ToCString(ctx, argv[0]); if (!target) return JS_EXCEPTION; @@ -2708,7 +2986,7 @@ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, char buf[PATH_MAX]; int err; ssize_t res; - + path = JS_ToCString(ctx, argv[0]); if (!path) return JS_EXCEPTION; @@ -2732,7 +3010,7 @@ static char **build_envp(JSContext *ctx, JSValueConst obj) const char *key, *str; JSValue val; size_t key_len, str_len; - + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) return NULL; @@ -2769,9 +3047,7 @@ static char **build_envp(JSContext *ctx, JSValueConst obj) JS_FreeCString(ctx, str); } done: - for(i = 0; i < len; i++) - JS_FreeAtom(ctx, tab[i].atom); - js_free(ctx, tab); + JS_FreePropertyEnum(ctx, tab, len); return envp; fail: if (envp) { @@ -2790,7 +3066,7 @@ static int my_execvpe(const char *filename, char **argv, char **envp) char buf[PATH_MAX]; size_t filename_len, path_len; BOOL eacces_error; - + filename_len = strlen(filename); if (filename_len == 0) { errno = ENOENT; @@ -2798,7 +3074,7 @@ static int my_execvpe(const char *filename, char **argv, char **envp) } if (strchr(filename, '/')) return execve(filename, argv, envp); - + path = getenv("PATH"); if (!path) path = (char *)"/bin:/usr/bin"; @@ -2820,7 +3096,7 @@ static int my_execvpe(const char *filename, char **argv, char **envp) buf[path_len] = '/'; memcpy(buf + path_len + 1, filename, filename_len); buf[path_len + 1 + filename_len] = '\0'; - + execve(buf, argv, envp); switch(errno) { @@ -2853,7 +3129,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, static const char *std_name[3] = { "stdin", "stdout", "stderr" }; int std_fds[3]; uint32_t uid = -1, gid = -1; - + val = JS_GetPropertyStr(ctx, args, "length"); if (JS_IsException(val)) return JS_EXCEPTION; @@ -2882,7 +3158,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, for(i = 0; i < 3; i++) std_fds[i] = i; - + /* get the options, if any */ if (argc >= 2) { options = argv[1]; @@ -2891,7 +3167,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, goto exception; if (get_bool_option(ctx, &use_path, options, "usePath")) goto exception; - + val = JS_GetPropertyStr(ctx, options, "file"); if (JS_IsException(val)) goto exception; @@ -2936,7 +3212,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (!envp) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "uid"); if (JS_IsException(val)) goto exception; @@ -2965,7 +3241,6 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, } if (pid == 0) { /* child */ - int fd_max = sysconf(_SC_OPEN_MAX); /* remap the stdin/stdout/stderr handles if necessary */ for(i = 0; i < 3; i++) { @@ -2974,9 +3249,28 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, _exit(127); } } - - for(i = 3; i < fd_max; i++) - close(i); +#if defined(HAVE_CLOSEFROM) + /* closefrom() is available on many recent unix systems: + Linux with glibc 2.34+, Solaris 9+, FreeBSD 7.3+, + NetBSD 3.0+, OpenBSD 3.5+. + Linux with the musl libc and macOS don't have it. + */ + + closefrom(3); +#else + { + /* Close the file handles manually, limit to 1024 to avoid + costly loop on linux Alpine where sysconf(_SC_OPEN_MAX) + returns a huge value 1048576. + Patch inspired by nicolas-duteil-nova. See also: + https://stackoverflow.com/questions/73229353/ + https://stackoverflow.com/questions/899038/#918469 + */ + int fd_max = min_int(sysconf(_SC_OPEN_MAX), 1024); + for(i = 3; i < fd_max; i++) + close(i); + } +#endif if (cwd) { if (chdir(cwd) < 0) _exit(127); @@ -3037,13 +3331,20 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, goto done; } +/* getpid() -> pid */ +static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewInt32(ctx, getpid()); +} + /* waitpid(pid, block) -> [pid, status] */ static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int pid, status, options, ret; JSValue obj; - + if (JS_ToInt32(ctx, &pid, argv[0])) return JS_EXCEPTION; if (JS_ToInt32(ctx, &options, argv[1])) @@ -3063,7 +3364,7 @@ static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), JS_PROP_C_W_E); return obj; -} +} /* pipe() -> [read_fd, write_fd] or null if error */ static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, @@ -3071,7 +3372,7 @@ static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, { int pipe_fds[2], ret; JSValue obj; - + ret = pipe(pipe_fds); if (ret < 0) return JS_NULL; @@ -3090,7 +3391,7 @@ static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int pid, sig, ret; - + if (JS_ToInt32(ctx, &pid, argv[0])) return JS_EXCEPTION; if (JS_ToInt32(ctx, &sig, argv[1])) @@ -3104,7 +3405,7 @@ static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int fd, ret; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; ret = js_get_errno(dup(fd)); @@ -3116,7 +3417,7 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int fd, fd2, ret; - + if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; if (JS_ToInt32(ctx, &fd2, argv[1])) @@ -3141,6 +3442,7 @@ typedef struct { char *filename; /* module filename */ char *basename; /* module base name */ JSWorkerMessagePipe *recv_pipe, *send_pipe; + int strip_flags; } WorkerFuncArgs; typedef struct { @@ -3189,22 +3491,17 @@ static void js_sab_dup(void *opaque, void *ptr) static JSWorkerMessagePipe *js_new_message_pipe(void) { JSWorkerMessagePipe *ps; - int pipe_fds[2]; - - if (pipe(pipe_fds) < 0) - return NULL; ps = malloc(sizeof(*ps)); - if (!ps) { - close(pipe_fds[0]); - close(pipe_fds[1]); + if (!ps) + return NULL; + if (js_waker_init(&ps->waker)) { + free(ps); return NULL; } ps->ref_count = 1; init_list_head(&ps->msg_queue); pthread_mutex_init(&ps->mutex, NULL); - ps->read_fd = pipe_fds[0]; - ps->write_fd = pipe_fds[1]; return ps; } @@ -3231,10 +3528,10 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps) struct list_head *el, *el1; JSWorkerMessage *msg; int ref_count; - + if (!ps) return; - + ref_count = atomic_add_int(&ps->ref_count, -1); assert(ref_count >= 0); if (ref_count == 0) { @@ -3243,8 +3540,7 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps) js_free_message(msg); } pthread_mutex_destroy(&ps->mutex); - close(ps->read_fd); - close(ps->write_fd); + js_waker_close(&ps->waker); free(ps); } } @@ -3273,7 +3569,7 @@ static void js_worker_finalizer(JSRuntime *rt, JSValue val) static JSClassDef js_worker_class = { "Worker", .finalizer = js_worker_finalizer, -}; +}; static void *worker_func(void *opaque) { @@ -3281,21 +3577,23 @@ static void *worker_func(void *opaque) JSRuntime *rt; JSThreadState *ts; JSContext *ctx; - + JSValue val; + rt = JS_NewRuntime(); if (rt == NULL) { fprintf(stderr, "JS_NewRuntime failure"); exit(1); - } + } + JS_SetStripInfo(rt, args->strip_flags); js_std_init_handlers(rt); - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + JS_SetModuleLoaderFunc2(rt, NULL, js_module_loader, js_module_check_attributes, NULL); /* set the pipe to communicate with the parent */ ts = JS_GetRuntimeOpaque(rt); ts->recv_pipe = args->recv_pipe; ts->send_pipe = args->send_pipe; - + /* function pointer to avoid linking the whole JS_NewContext() if not needed */ ctx = js_worker_new_context_func(rt); @@ -3307,11 +3605,14 @@ static void *worker_func(void *opaque) js_std_add_helpers(ctx, -1, NULL); - if (!JS_RunModule(ctx, args->basename, args->filename)) - js_std_dump_error(ctx); + val = JS_LoadModule(ctx, args->basename, args->filename); free(args->filename); free(args->basename); free(args); + val = js_std_await(ctx, val); + if (JS_IsException(val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, val); js_std_loop(ctx); @@ -3327,7 +3628,7 @@ static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, { JSValue obj = JS_UNDEFINED, proto; JSWorkerData *s; - + /* create the object */ if (JS_IsUndefined(new_target)) { proto = JS_GetClassProto(ctx, js_worker_class_id); @@ -3364,7 +3665,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, int ret; const char *filename = NULL, *basename; JSAtom basename_atom; - + /* XXX: in order to avoid problems with resource liberation, we don't support creating workers inside workers */ if (!is_main_thread(rt)) @@ -3380,7 +3681,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, JS_FreeAtom(ctx, basename_atom); if (!basename) goto fail; - + /* module name */ filename = JS_ToCString(ctx, argv[0]); if (!filename) @@ -3401,11 +3702,13 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, if (!args->send_pipe) goto oom_fail; + args->strip_flags = JS_GetStripInfo(rt); + obj = js_worker_ctor_internal(ctx, new_target, args->send_pipe, args->recv_pipe); if (JS_IsException(obj)) goto fail; - + pthread_attr_init(&attr); /* no join at the end */ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); @@ -3443,10 +3746,10 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, uint8_t *data; JSWorkerMessage *msg; uint8_t **sab_tab; - + if (!worker) return JS_EXCEPTION; - + data = JS_WriteObject2(ctx, &data_len, argv[0], JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, &sab_tab, &sab_tab_len); @@ -3466,15 +3769,17 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, memcpy(msg->data, data, data_len); msg->data_len = data_len; - msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len); - if (!msg->sab_tab) - goto fail; - memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len); + if (sab_tab_len > 0) { + msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len); + if (!msg->sab_tab) + goto fail; + memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len); + } msg->sab_tab_len = sab_tab_len; js_free(ctx, data); js_free(ctx, sab_tab); - + /* increment the SAB reference counts */ for(i = 0; i < msg->sab_tab_len; i++) { js_sab_dup(NULL, msg->sab_tab[i]); @@ -3483,17 +3788,8 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, ps = worker->send_pipe; pthread_mutex_lock(&ps->mutex); /* indicate that data is present */ - if (list_empty(&ps->msg_queue)) { - uint8_t ch = '\0'; - int ret; - for(;;) { - ret = write(ps->write_fd, &ch, 1); - if (ret == 1) - break; - if (ret < 0 && (errno != EAGAIN || errno != EINTR)) - break; - } - } + if (list_empty(&ps->msg_queue)) + js_waker_signal(&ps->waker); list_add_tail(&msg->link, &ps->msg_queue); pthread_mutex_unlock(&ps->mutex); return JS_UNDEFINED; @@ -3506,7 +3802,7 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, js_free(ctx, data); js_free(ctx, sab_tab); return JS_EXCEPTION; - + } static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, @@ -3516,7 +3812,7 @@ static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); JSWorkerMessageHandler *port; - + if (!worker) return JS_EXCEPTION; @@ -3628,8 +3924,10 @@ static const JSCFunctionListEntry js_os_funcs[] = { OS_FLAG(SIGTTIN), OS_FLAG(SIGTTOU), #endif + JS_CFUNC_DEF("now", 0, js_os_now ), JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ), JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ), JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), JS_CFUNC_DEF("chdir", 0, js_os_chdir ), @@ -3657,6 +3955,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("getpid", 0, js_os_getpid ), JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), OS_FLAG(WNOHANG), JS_CFUNC_DEF("pipe", 0, js_os_pipe ), @@ -3669,10 +3968,6 @@ static const JSCFunctionListEntry js_os_funcs[] = { static int js_os_init(JSContext *ctx, JSModuleDef *m) { os_poll_func = js_os_poll; - - /* OSTimer class */ - JS_NewClassID(&js_os_timer_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class); #ifdef USE_WORKER { @@ -3684,20 +3979,20 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m) JS_NewClass(JS_GetRuntime(ctx), js_worker_class_id, &js_worker_class); proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); - + obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, JS_CFUNC_constructor, 0); JS_SetConstructor(ctx, obj, proto); - + JS_SetClassProto(ctx, js_worker_class_id, proto); - + /* set 'Worker.parent' if necessary */ if (ts->recv_pipe && ts->send_pipe) { JS_DefinePropertyValueStr(ctx, obj, "parent", js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), JS_PROP_C_W_E); } - + JS_SetModuleExport(ctx, m, "Worker", obj); } #endif /* USE_WORKER */ @@ -3722,28 +4017,43 @@ JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) /**********************************************************/ static JSValue js_print(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv) { int i; - const char *str; - size_t len; - + JSValueConst v; + for(i = 0; i < argc; i++) { if (i != 0) putchar(' '); - str = JS_ToCStringLen(ctx, &len, argv[i]); - if (!str) - return JS_EXCEPTION; - fwrite(str, 1, len, stdout); - JS_FreeCString(ctx, str); + v = argv[i]; + if (JS_IsString(v)) { + const char *str; + size_t len; + str = JS_ToCStringLen(ctx, &len, v); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, stdout); + JS_FreeCString(ctx, str); + } else { + JS_PrintValue(ctx, js_print_value_write, stdout, v, NULL); + } } putchar('\n'); return JS_UNDEFINED; } +static JSValue js_console_log(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue ret; + ret = js_print(ctx, this_val, argc, argv); + fflush(stdout); + return ret; +} + void js_std_add_helpers(JSContext *ctx, int argc, char **argv) { - JSValue global_obj, console, args; + JSValue global_obj, console, args, performance; int i; /* XXX: should these global definitions be enumerable? */ @@ -3751,9 +4061,14 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv) console = JS_NewObject(ctx); JS_SetPropertyStr(ctx, console, "log", - JS_NewCFunction(ctx, js_print, "log", 1)); + JS_NewCFunction(ctx, js_console_log, "log", 1)); JS_SetPropertyStr(ctx, global_obj, "console", console); + performance = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, performance, "now", + JS_NewCFunction(ctx, js_os_now, "now", 0)); + JS_SetPropertyStr(ctx, global_obj, "performance", performance); + /* same methods as the mozilla JS shell */ if (argc >= 0) { args = JS_NewArray(ctx); @@ -3762,12 +4077,12 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv) } JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); } - + JS_SetPropertyStr(ctx, global_obj, "print", JS_NewCFunction(ctx, js_print, "print", 1)); JS_SetPropertyStr(ctx, global_obj, "__loadScript", JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1)); - + JS_FreeValue(ctx, global_obj); } @@ -3785,6 +4100,8 @@ void js_std_init_handlers(JSRuntime *rt) init_list_head(&ts->os_signal_handlers); init_list_head(&ts->os_timers); init_list_head(&ts->port_list); + init_list_head(&ts->rejected_promise_list); + ts->next_timer_id = 1; JS_SetRuntimeOpaque(rt, ts); @@ -3815,12 +4132,17 @@ void js_std_free_handlers(JSRuntime *rt) JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); free_sh(rt, sh); } - + list_for_each_safe(el, el1, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); - unlink_timer(rt, th); - if (!th->has_object) - free_timer(rt, th); + free_timer(rt, th); + } + + list_for_each_safe(el, el1, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + JS_FreeValueRT(rt, rp->promise); + JS_FreeValueRT(rt, rp->reason); + free(rp); } #ifdef USE_WORKER @@ -3833,51 +4155,82 @@ void js_std_free_handlers(JSRuntime *rt) JS_SetRuntimeOpaque(rt, NULL); /* fail safe */ } -static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val) -{ - const char *str; - - str = JS_ToCString(ctx, val); - if (str) { - fprintf(f, "%s\n", str); - JS_FreeCString(ctx, str); - } else { - fprintf(f, "[exception]\n"); - } -} - static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val) { - JSValue val; - BOOL is_error; - - is_error = JS_IsError(ctx, exception_val); - js_dump_obj(ctx, stderr, exception_val); - if (is_error) { - val = JS_GetPropertyStr(ctx, exception_val, "stack"); - if (!JS_IsUndefined(val)) { - js_dump_obj(ctx, stderr, val); - } - JS_FreeValue(ctx, val); - } + JS_PrintValue(ctx, js_print_value_write, stderr, exception_val, NULL); + fputc('\n', stderr); } void js_std_dump_error(JSContext *ctx) { JSValue exception_val; - + exception_val = JS_GetException(ctx); js_std_dump_error1(ctx, exception_val); JS_FreeValue(ctx, exception_val); } +static JSRejectedPromiseEntry *find_rejected_promise(JSContext *ctx, JSThreadState *ts, + JSValueConst promise) +{ + struct list_head *el; + + list_for_each(el, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + if (JS_SameValue(ctx, rp->promise, promise)) + return rp; + } + return NULL; +} + void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, BOOL is_handled, void *opaque) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSRejectedPromiseEntry *rp; + if (!is_handled) { - fprintf(stderr, "Possibly unhandled promise rejection: "); - js_std_dump_error1(ctx, reason); + /* add a new entry if needed */ + rp = find_rejected_promise(ctx, ts, promise); + if (!rp) { + rp = malloc(sizeof(*rp)); + if (rp) { + rp->promise = JS_DupValue(ctx, promise); + rp->reason = JS_DupValue(ctx, reason); + list_add_tail(&rp->link, &ts->rejected_promise_list); + } + } + } else { + /* the rejection is handled, so the entry can be removed if present */ + rp = find_rejected_promise(ctx, ts, promise); + if (rp) { + JS_FreeValue(ctx, rp->promise); + JS_FreeValue(ctx, rp->reason); + list_del(&rp->link); + free(rp); + } + } +} + +/* check if there are pending promise rejections. It must be done + asynchrously in case a rejected promise is handled later. Currently + we do it once the application is about to sleep. It could be done + more often if needed. */ +static void js_std_promise_rejection_check(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + struct list_head *el; + + if (unlikely(!list_empty(&ts->rejected_promise_list))) { + list_for_each(el, &ts->rejected_promise_list) { + JSRejectedPromiseEntry *rp = list_entry(el, JSRejectedPromiseEntry, link); + fprintf(stderr, "Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, rp->reason); + } + exit(1); } } @@ -3899,11 +4252,53 @@ void js_std_loop(JSContext *ctx) } } + js_std_promise_rejection_check(ctx); + if (!os_poll_func || os_poll_func(ctx)) break; } } +/* Wait for a promise and execute pending jobs while waiting for + it. Return the promise result or JS_EXCEPTION in case of promise + rejection. */ +JSValue js_std_await(JSContext *ctx, JSValue obj) +{ + JSValue ret; + int state; + + for(;;) { + state = JS_PromiseState(ctx, obj); + if (state == JS_PROMISE_FULFILLED) { + ret = JS_PromiseResult(ctx, obj); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_REJECTED) { + ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_PENDING) { + JSContext *ctx1; + int err; + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err < 0) { + js_std_dump_error(ctx1); + } + if (err == 0) { + js_std_promise_rejection_check(ctx); + + if (os_poll_func) + os_poll_func(ctx); + } + } else { + /* not a promise */ + ret = obj; + break; + } + } + return ret; +} + void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, int load_only) { @@ -3922,8 +4317,11 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, goto exception; } js_module_set_import_meta(ctx, obj, FALSE, TRUE); + val = JS_EvalFunction(ctx, obj); + val = js_std_await(ctx, val); + } else { + val = JS_EvalFunction(ctx, obj); } - val = JS_EvalFunction(ctx, obj); if (JS_IsException(val)) { exception: js_std_dump_error(ctx); @@ -3932,3 +4330,22 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, JS_FreeValue(ctx, val); } } + +void js_std_eval_binary_json_module(JSContext *ctx, + const uint8_t *buf, size_t buf_len, + const char *module_name) +{ + JSValue obj; + JSModuleDef *m; + + obj = JS_ReadObject(ctx, buf, buf_len, 0); + if (JS_IsException(obj)) + goto exception; + m = create_json_module(ctx, module_name, obj); + if (!m) { + exception: + js_std_dump_error(ctx); + exit(1); + } +} + diff --git a/quickjs/quickjs-libc.h b/quickjs/quickjs-libc.h index fbbe5b0166..5c8301b717 100644 --- a/quickjs/quickjs-libc.h +++ b/quickjs/quickjs-libc.h @@ -1,6 +1,6 @@ /* * QuickJS C library - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -37,21 +37,28 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); void js_std_add_helpers(JSContext *ctx, int argc, char **argv); void js_std_loop(JSContext *ctx); +JSValue js_std_await(JSContext *ctx, JSValue obj); void js_std_init_handlers(JSRuntime *rt); void js_std_free_handlers(JSRuntime *rt); void js_std_dump_error(JSContext *ctx); uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, JS_BOOL use_realpath, JS_BOOL is_main); +int js_module_test_json(JSContext *ctx, JSValueConst attributes); +int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes); JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque); + const char *module_name, void *opaque, + JSValueConst attributes); void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); +void js_std_eval_binary_json_module(JSContext *ctx, + const uint8_t *buf, size_t buf_len, + const char *module_name); void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque); void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); - + #ifdef __cplusplus } /* extern "C" { */ #endif diff --git a/quickjs/quickjs-opcode.h b/quickjs/quickjs-opcode.h index 15a9fce6a3..814a7cbaaa 100644 --- a/quickjs/quickjs-opcode.h +++ b/quickjs/quickjs-opcode.h @@ -1,6 +1,6 @@ /* * QuickJS opcode definitions - * + * * Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Charlie Gordon * @@ -110,6 +110,7 @@ DEF( return, 1, 1, 0, none) DEF( return_undef, 1, 0, 0, none) DEF(check_ctor_return, 1, 1, 2, none) DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ DEF( return_async, 1, 1, 0, none) @@ -120,7 +121,7 @@ DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a bytecode string */ DEF( get_super, 1, 1, 1, none) -DEF( import, 1, 1, 1, none) /* dynamic module import */ +DEF( import, 1, 2, 1, none) /* dynamic module import */ DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ @@ -143,6 +144,7 @@ DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ DEF( get_array_el, 1, 2, 1, none) DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( get_array_el3, 1, 2, 3, none) /* obj prop -> obj prop1 value */ DEF( put_array_el, 1, 3, 0, none) DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ @@ -165,14 +167,15 @@ DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ DEF( get_arg, 3, 0, 1, arg) DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ -DEF( get_var_ref, 3, 0, 1, var_ref) +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( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ DEF(set_loc_uninitialized, 3, 0, 0, loc) DEF( get_loc_check, 3, 0, 1, loc) DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ DEF( put_loc_check_init, 3, 1, 0, loc) -DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(get_loc_checkthis, 3, 0, 1, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ DEF(put_var_ref_check_init, 3, 1, 0, var_ref) DEF( close_loc, 3, 0, 0, loc) @@ -182,18 +185,17 @@ 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( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ DEF( to_object, 1, 1, 1, none) //DEF( to_string, 1, 1, 1, none) DEF( to_propkey, 1, 1, 1, none) -DEF( to_propkey2, 1, 2, 2, none) DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ -DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) DEF( make_loc_ref, 7, 0, 2, atom_u16) DEF( make_arg_ref, 7, 0, 2, atom_u16) @@ -205,10 +207,10 @@ DEF( for_of_start, 1, 1, 3, none) DEF(for_await_of_start, 1, 1, 3, none) DEF( for_in_next, 1, 1, 3, none) DEF( for_of_next, 2, 3, 5, u8) +DEF(for_await_of_next, 1, 3, 4, none) /* iter next catch_offset -> iter next catch_offset obj */ DEF(iterator_check_object, 1, 1, 1, none) -DEF(iterator_get_value_done, 1, 1, 2, none) +DEF(iterator_get_value_done, 1, 2, 3, none) /* catch_offset obj -> catch_offset value done */ DEF( iterator_close, 1, 3, 0, none) -DEF(iterator_close_return, 1, 4, 4, none) DEF( iterator_next, 1, 4, 4, none) DEF( iterator_call, 2, 4, 5, u8) DEF( initial_yield, 1, 0, 0, none) @@ -256,12 +258,10 @@ DEF( and, 1, 2, 1, none) DEF( xor, 1, 2, 1, none) DEF( or, 1, 2, 1, none) DEF(is_undefined_or_null, 1, 1, 1, none) -#ifdef CONFIG_BIGNUM -DEF( mul_pow10, 1, 2, 1, none) -DEF( math_mod, 1, 2, 1, none) -#endif +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) /* must be the last non short and non temporary opcode */ -DEF( nop, 1, 0, 0, none) +DEF( nop, 1, 0, 0, none) /* temporary opcodes: never emitted in the final bytecode */ @@ -270,6 +270,8 @@ def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ +/* the following opcodes must be in the same order as the 'with_x' and + get_var_undef, get_var and put_var opcodes */ def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ @@ -277,12 +279,15 @@ def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */ def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ - +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ - + def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ #if SHORT_OPCODES diff --git a/quickjs/quickjs.c b/quickjs/quickjs.c index f9fd091ac0..ac8401f7e3 100644 --- a/quickjs/quickjs.c +++ b/quickjs/quickjs.c @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon + * + * 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 @@ -22,13 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_NONSTDC_NO_DEPRECATE #include #include #include #include #include #include -#include #include #include #include @@ -44,11 +45,14 @@ #include "list.h" #include "quickjs.h" #include "libregexp.h" -#include "libbf.h" +#include "libunicode.h" +#include "dtoa.h" + +#pragma warning(disable:4146) #define OPTIMIZE 1 #define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) +#if defined(EMSCRIPTEN) || defined(_MSC_VER) #define DIRECT_DISPATCH 0 #else #define DIRECT_DISPATCH 1 @@ -67,11 +71,11 @@ /* define to include Atomics.* operations which depend on the OS threads */ -#if !defined(EMSCRIPTEN) +#if !defined(EMSCRIPTEN) && !defined(_MSC_VER) #define CONFIG_ATOMICS #endif -#if !defined(EMSCRIPTEN) +#if !defined(EMSCRIPTEN) && !defined(_MSC_VER) /* enable stack limitation */ #define CONFIG_STACK_CHECK #endif @@ -87,6 +91,7 @@ 8: dump stdlib functions 16: dump bytecode in hex 32: dump line number table + 64: dump compute_stack_size */ //#define DUMP_BYTECODE (1) /* dump the occurence of the automatic GC */ @@ -103,6 +108,7 @@ //#define DUMP_MODULE_RESOLVE //#define DUMP_PROMISE //#define DUMP_READ_OBJECT +//#define DUMP_ROPE_REBALANCE /* test the GC by forcing it before each object allocation */ //#define FORCE_GC_AT_MALLOC @@ -144,16 +150,11 @@ enum { JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */ JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ JS_CLASS_DATAVIEW, /* u.typed_array */ JS_CLASS_BIG_INT, /* u.object_data */ -#ifdef CONFIG_BIGNUM - JS_CLASS_BIG_FLOAT, /* u.object_data */ - JS_CLASS_FLOAT_ENV, /* u.float_env */ - JS_CLASS_BIG_DECIMAL, /* u.object_data */ - JS_CLASS_OPERATOR_SET, /* u.operator_set */ -#endif JS_CLASS_MAP, /* u.map_state */ JS_CLASS_SET, /* u.map_state */ JS_CLASS_WEAKMAP, /* u.map_state */ @@ -174,7 +175,9 @@ enum { JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ - + JS_CLASS_WEAK_REF, + JS_CLASS_FINALIZATION_REGISTRY, + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ }; @@ -192,19 +195,34 @@ typedef enum JSErrorEnum { JS_URI_ERROR, JS_INTERNAL_ERROR, JS_AGGREGATE_ERROR, - + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ } JSErrorEnum; -#define JS_MAX_LOCAL_VARS 65536 +/* the variable and scope indexes must fit on 16 bits. The (-1) and + ARG_SCOPE_END values are reserved. */ +#define JS_MAX_LOCAL_VARS 65534 #define JS_STACK_SIZE_MAX 65534 #define JS_STRING_LEN_MAX ((1 << 30) - 1) +/* strings <= this length are not concatenated using ropes. if too + small, the rope memory overhead becomes high. */ +#define JS_STRING_ROPE_SHORT_LEN 512 +/* specific threshold for initial rope use */ +#define JS_STRING_ROPE_SHORT2_LEN 8192 +/* rope depth at which we rebalance */ +#define JS_STRING_ROPE_MAX_DEPTH 60 + #define __exception __attribute__((warn_unused_result)) typedef struct JSShape JSShape; typedef struct JSString JSString; typedef struct JSString JSAtomStruct; +typedef struct JSObject JSObject; + +#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR(v)) typedef enum { JS_GC_PHASE_NONE, @@ -214,24 +232,6 @@ typedef enum { typedef enum OPCodeEnum OPCodeEnum; -/* function pointers are used for numeric operations so that it is - possible to remove some numeric types */ -typedef struct { - JSValue (*to_string)(JSContext *ctx, JSValueConst val); - JSValue (*from_string)(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent); - int (*unary_arith)(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1); - int (*binary_arith)(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2); - int (*compare)(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2); - /* only for bigfloat: */ - JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a, - int64_t exponent); - int (*mul_pow10)(JSContext *ctx, JSValue *sp); -} JSNumericOperations; - struct JSRuntime { JSMallocFunctions mf; JSMallocState malloc_state; @@ -253,10 +253,11 @@ struct JSRuntime { by the garbage collector) */ struct list_head gc_obj_list; /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ - struct list_head gc_zero_ref_count_list; + struct list_head gc_zero_ref_count_list; struct list_head tmp_obj_list; /* used during GC */ JSGCPhaseEnum gc_phase : 8; size_t malloc_gc_threshold; + struct list_head weakref_list; /* list of JSWeakRefHeader.link */ #ifdef DUMP_LEAKS struct list_head string_list; /* list of JSString.link */ #endif @@ -264,8 +265,10 @@ struct JSRuntime { uintptr_t stack_size; /* in bytes, 0 if no limit */ uintptr_t stack_top; uintptr_t stack_limit; /* lower stack limit */ - + JSValue current_exception; + /* true if the current exception cannot be catched */ + BOOL current_exception_is_uncatchable : 8; /* true if inside an out of memory error, to avoid recursing */ BOOL in_out_of_memory : 8; @@ -279,29 +282,31 @@ struct JSRuntime { JSHostPromiseRejectionTracker *host_unhandled_promise_rejection_tracker; void *host_unhandled_promise_rejection_tracker_opaque; - + struct list_head job_list; /* list of JSJobEntry.link */ JSModuleNormalizeFunc *module_normalize_func; - JSModuleLoaderFunc *module_loader_func; + BOOL module_loader_has_attr; + union { + JSModuleLoaderFunc *module_loader_func; + JSModuleLoaderFunc2 *module_loader_func2; + } u; + JSModuleCheckSupportedImportAttributes *module_check_attrs; void *module_loader_opaque; + /* timestamp for internal use in module evaluation */ + int64_t module_async_evaluation_next_timestamp; BOOL can_block : 8; /* TRUE if Atomics.wait can block */ /* used to allocate, free and clone SharedArrayBuffers */ JSSharedArrayBufferFunctions sab_funcs; + /* see JS_SetStripInfo() */ + uint8_t strip_flags; /* Shape hash table */ int shape_hash_bits; int shape_hash_size; int shape_hash_count; /* number of hashed shapes */ JSShape **shape_hash; - bf_context_t bf_ctx; - JSNumericOperations bigint_ops; -#ifdef CONFIG_BIGNUM - JSNumericOperations bigfloat_ops; - JSNumericOperations bigdecimal_ops; - uint32_t operator_count; -#endif void *user_opaque; }; @@ -316,21 +321,21 @@ struct JSClass { }; #define JS_MODE_STRICT (1 << 0) -#define JS_MODE_STRIP (1 << 1) -#define JS_MODE_MATH (1 << 2) +#define JS_MODE_ASYNC (1 << 2) /* async function */ +#define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */ typedef struct JSStackFrame { struct JSStackFrame *prev_frame; /* NULL if first stack frame */ JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ JSValue *arg_buf; /* arguments */ JSValue *var_buf; /* variables */ - struct list_head var_ref_list; /* list of JSVarRef.link */ + struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */ const uint8_t *cur_pc; /* only used in bytecode functions : PC of the instruction after the call */ int arg_count; - int js_mode; /* 0 or JS_MODE_MATH for C functions */ + int js_mode; /* not supported for C functions */ /* only used in generators. Current stack pointer value. NULL if - the function is running. */ + the function is running. */ JSValue *cur_sp; } JSStackFrame; @@ -355,48 +360,76 @@ struct JSGCObjectHeader { struct list_head link; }; +typedef enum { + JS_WEAKREF_TYPE_MAP, + JS_WEAKREF_TYPE_WEAKREF, + JS_WEAKREF_TYPE_FINREC, +} JSWeakRefHeaderTypeEnum; + +typedef struct { + struct list_head link; + JSWeakRefHeaderTypeEnum weakref_type; +} JSWeakRefHeader; + typedef struct JSVarRef { union { JSGCObjectHeader header; /* must come first */ struct { int __gc_ref_count; /* corresponds to header.ref_count */ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - - /* 0 : the JSVarRef is on the stack. header.link is an element - of JSStackFrame.var_ref_list. - 1 : the JSVarRef is detached. header.link has the normal meanning - */ - uint8_t is_detached : 1; - uint8_t is_arg : 1; - uint16_t var_idx; /* index of the corresponding function variable on - the stack */ + uint8_t is_detached; }; }; JSValue *pvalue; /* pointer to the value, either on the stack or to 'value' */ - JSValue value; /* used when the variable is no longer on the stack */ + union { + JSValue value; /* used when is_detached = TRUE */ + struct { + struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */ + struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */ + }; /* used when is_detached = FALSE */ + }; } JSVarRef; -/* the same structure is used for big integers and big floats. Big - integers are never infinite or NaNs */ -typedef struct JSBigFloat { - JSRefCountHeader header; /* must come first, 32-bit */ - bf_t num; -} JSBigFloat; +/* bigint */ -#ifdef CONFIG_BIGNUM -typedef struct JSFloatEnv { - limb_t prec; - bf_flags_t flags; - unsigned int status; -} JSFloatEnv; +#if JS_LIMB_BITS == 32 + +typedef int32_t js_slimb_t; +typedef uint32_t js_limb_t; +typedef int64_t js_sdlimb_t; +typedef uint64_t js_dlimb_t; + +#define JS_LIMB_DIGITS 9 + +#else + +typedef __int128 int128_t; +typedef unsigned __int128 uint128_t; +typedef int64_t js_slimb_t; +typedef uint64_t js_limb_t; +typedef int128_t js_sdlimb_t; +typedef uint128_t js_dlimb_t; + +#define JS_LIMB_DIGITS 19 -typedef struct JSBigDecimal { - JSRefCountHeader header; /* must come first, 32-bit */ - bfdec_t num; -} JSBigDecimal; #endif +typedef struct JSBigInt { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len; /* number of limbs, >= 1 */ + js_limb_t tab[]; /* two's complement representation, always + normalized so that 'len' is the minimum + possible length >= 1 */ +} JSBigInt; + +/* this bigint structure can hold a 64 bit integer */ +typedef struct { + js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */ + /* must come just after */ + js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS]; +} JSBigIntBuf; + typedef enum { JS_AUTOINIT_ID_PROTOTYPE, JS_AUTOINIT_ID_MODULE_NS, @@ -434,15 +467,9 @@ struct JSContext { JSValue global_var_obj; /* contains the global let/const definitions */ uint64_t random_state; - bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ -#ifdef CONFIG_BIGNUM - JSFloatEnv fp_env; /* global FP environment */ - BOOL bignum_ext : 8; /* enable math mode */ - BOOL allow_operator_overloading : 8; -#endif + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; - BOOL is_error_property_enabled; struct list_head loaded_modules; /* list of JSModuleDef.link */ @@ -469,11 +496,6 @@ enum { JS_ATOM_TYPE_PRIVATE, }; -enum { - JS_ATOM_HASH_SYMBOL, - JS_ATOM_HASH_PRIVATE, -}; - typedef enum { JS_ATOM_KIND_STRING, JS_ATOM_KIND_SYMBOL, @@ -481,13 +503,14 @@ typedef enum { } JSAtomKindEnum; #define JS_ATOM_HASH_MASK ((1 << 30) - 1) +#define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK struct JSString { JSRefCountHeader header; /* must come first, 32-bit */ uint32_t len : 31; uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ - /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, - for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + /* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = JS_ATOM_HASH_PRIVATE, atom_type = 3 XXX: could change encoding to have one more bit in hash */ uint32_t hash : 30; uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ @@ -501,6 +524,17 @@ struct JSString { } u; }; +typedef struct JSStringRope { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len; + uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */ + uint8_t depth; /* max depth of the rope tree */ + /* XXX: could reduce memory usage by using a direct pointer with + bit 0 to select rope or string */ + JSValue left; + JSValue right; /* might be the empty string */ +} JSStringRope; + typedef struct JSClosureVar { uint8_t is_local : 1; uint8_t is_arg : 1; @@ -543,7 +577,7 @@ typedef struct JSVarDef { JSAtom var_name; /* index into fd->scopes of this variable lexical scope */ int scope_level; - /* during compilation: + /* during compilation: - if scope_level = 0: scope in which the variable is defined - if scope_level != 0: index into fd->vars of the next variable in the same or enclosing lexical scope @@ -551,7 +585,7 @@ typedef struct JSVarDef { index into fd->vars of the next variable in the same or enclosing lexical scope */ - int scope_next; + int scope_next; uint8_t is_const : 1; uint8_t is_lexical : 1; uint8_t is_captured : 1; @@ -594,9 +628,9 @@ typedef struct JSFunctionBytecode { uint8_t super_allowed : 1; uint8_t arguments_allowed : 1; uint8_t has_debug : 1; - uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ uint8_t read_only_bytecode : 1; - /* XXX: 4 bits available */ + uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */ + /* XXX: 10 bits available */ uint8_t *byte_code_buf; /* (self pointer) */ int byte_code_len; JSAtom func_name; @@ -613,8 +647,7 @@ typedef struct JSFunctionBytecode { struct { /* debug info, move to separate structure to save memory? */ JSAtom filename; - int line_num; - int source_len; + int source_len; int pc2line_len; uint8_t *pc2line_buf; char *source; @@ -636,9 +669,11 @@ typedef enum JSIteratorKindEnum { typedef struct JSForInIterator { JSValue obj; - BOOL is_array; - uint32_t array_length; uint32_t idx; + uint32_t atom_count; + uint8_t in_prototype_chain; + uint8_t is_array; + JSPropertyEnum *tab_atom; /* is_array = FALSE */ } JSForInIterator; typedef struct JSRegExp { @@ -672,21 +707,16 @@ typedef struct JSTypedArray { } JSTypedArray; typedef struct JSAsyncFunctionState { - JSValue this_val; /* 'this' generator argument */ + JSGCObjectHeader header; + JSValue this_val; /* 'this' argument */ int argc; /* number of function arguments */ BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + BOOL is_completed; /* TRUE if the function has returned. The stack + frame is no longer valid */ + JSValue resolving_funcs[2]; /* only used in JS async functions */ JSStackFrame frame; } JSAsyncFunctionState; -/* XXX: could use an object instead to avoid the - JS_TAG_ASYNC_FUNCTION tag for the GC */ -typedef struct JSAsyncFunctionData { - JSGCObjectHeader header; /* must come first */ - JSValue resolving_funcs[2]; - BOOL is_active; /* true if the async function state is valid */ - JSAsyncFunctionState func_state; -} JSAsyncFunctionData; - typedef enum { /* binary operators */ JS_OVOP_ADD, @@ -737,6 +767,7 @@ typedef struct { typedef struct JSReqModuleEntry { JSAtom module_name; JSModuleDef *module; /* used using resolution */ + JSValue attributes; /* JS_UNDEFINED or an object contains the attributes as key/value */ } JSReqModuleEntry; typedef enum JSExportTypeEnum { @@ -764,10 +795,20 @@ typedef struct JSStarExportEntry { typedef struct JSImportEntry { int var_idx; /* closure variable index */ + BOOL is_star; /* import_name = '*' is a valid import name, so need a flag */ JSAtom import_name; int req_module_idx; /* in req_module_entries */ } JSImportEntry; +typedef enum { + JS_MODULE_STATUS_UNLINKED, + JS_MODULE_STATUS_LINKING, + JS_MODULE_STATUS_LINKED, + JS_MODULE_STATUS_EVALUATING, + JS_MODULE_STATUS_EVALUATING_ASYNC, + JS_MODULE_STATUS_EVALUATED, +} JSModuleStatus; + struct JSModuleDef { JSRefCountHeader header; /* must come first, 32-bit */ JSAtom module_name; @@ -792,16 +833,30 @@ struct JSModuleDef { JSValue module_ns; JSValue func_obj; /* only used for JS modules */ JSModuleInitFunc *init_func; /* only used for C modules */ + BOOL has_tla : 8; /* true if func_obj contains await */ BOOL resolved : 8; BOOL func_created : 8; - BOOL instantiated : 8; - BOOL evaluated : 8; - BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ + JSModuleStatus status : 8; + /* temp use during js_module_link() & js_module_evaluate() */ + int dfs_index, dfs_ancestor_index; + JSModuleDef *stack_prev; + /* temp use during js_module_evaluate() */ + JSModuleDef **async_parent_modules; + int async_parent_modules_count; + int async_parent_modules_size; + int pending_async_dependencies; + BOOL async_evaluation; + int64_t async_evaluation_timestamp; + JSModuleDef *cycle_root; + JSValue promise; /* corresponds to spec field: capability */ + JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ + /* true if evaluation yielded an exception. It is saved in eval_exception */ - BOOL eval_has_exception : 8; + BOOL eval_has_exception : 8; JSValue eval_exception; JSValue meta_obj; /* for import.meta */ + JSValue private_value; /* private value for C modules */ }; typedef struct JSJobEntry { @@ -867,24 +922,24 @@ struct JSObject { struct { int __gc_ref_count; /* corresponds to header.ref_count */ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - + uint8_t extensible : 1; uint8_t free_mark : 1; /* only used when freeing objects with cycles */ uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ - uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t has_immutable_prototype : 1; /* cannot modify the prototype */ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ uint16_t class_id; /* see JS_CLASS_x */ }; }; - /* byte offsets: 16/24 */ + /* count the number of weak references to this object. The object + structure is freed only if header.ref_count = 0 and + weakref_count = 0 */ + uint32_t weakref_count; JSShape *shape; /* prototype and property names + flag */ JSProperty *prop; /* array of properties */ - /* byte offsets: 24/40 */ - struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ - /* byte offsets: 28/48 */ union { void *opaque; struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ @@ -892,10 +947,6 @@ struct JSObject { struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ -#ifdef CONFIG_BIGNUM - struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ - struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ -#endif struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ @@ -904,7 +955,7 @@ struct JSObject { struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ - struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ @@ -927,7 +978,7 @@ struct JSObject { struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ } u1; union { - JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ @@ -937,6 +988,7 @@ struct JSObject { uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */ float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ } u; @@ -945,8 +997,29 @@ struct JSObject { JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ } u; - /* byte sizes: 40/48/72 */ }; + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + BOOL empty : 8; /* TRUE if the record is deleted */ + struct list_head link; + struct JSMapRecord *hash_next; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + BOOL is_weak; /* TRUE if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + JSMapRecord **hash_table; + int hash_bits; + uint32_t hash_size; /* = 2 ^ hash_bits */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ + JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */ +} JSMapState; + enum { __JS_ATOM_NULL = JS_ATOM_NULL, #define DEF(name, str) JS_ATOM_ ## name, @@ -1021,29 +1094,22 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, JSValueConst val, int flags, int scope_idx); JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); -static __maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p); +static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); -static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val); -static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); -static __maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val); +static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val); +static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val); static __maybe_unused void JS_DumpShapes(JSRuntime *rt); +static void js_dump_value_write(void *opaque, const char *buf, size_t len); static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static void js_array_finalizer(JSRuntime *rt, JSValue val); -static void js_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); +static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void js_object_data_finalizer(JSRuntime *rt, JSValue val); -static void js_object_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void js_c_function_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); @@ -1082,16 +1148,18 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val, static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -#ifdef CONFIG_BIGNUM -static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); -static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -#endif + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); static int JS_ToBoolFree(JSContext *ctx, JSValue val); static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, JSValueConst flags); static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, @@ -1108,58 +1176,24 @@ typedef enum JSStrictEqModeEnum { static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode); -static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); +static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2); static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); static JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags); -static JSValue JS_NewBigInt(JSContext *ctx); -static inline bf_t *JS_GetBigInt(JSValueConst val) -{ - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, - BOOL convert_to_safe_integer); -static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); -#ifdef CONFIG_BIGNUM -static void js_float_env_finalizer(JSRuntime *rt, JSValue val); -static JSValue JS_NewBigFloat(JSContext *ctx); -static inline bf_t *JS_GetBigFloat(JSValueConst val) -{ - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static JSValue JS_NewBigDecimal(JSContext *ctx); -static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) -{ - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); -static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, - BOOL allow_null_or_undefined); -static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); -#endif JSValue JS_ThrowOutOfMemory(JSContext *ctx); static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); -static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); -static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, BOOL throw_flag); -static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); -static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); -static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); + +static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); static int JS_CreateProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); -static void reset_weak_ref(JSRuntime *rt, JSObject *p); +static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2, + int pos2, int len); static JSValue js_array_buffer_constructor3(JSContext *ctx, JSValueConst new_target, uint64_t len, JSClassID class_id, @@ -1171,11 +1205,17 @@ static JSValue js_typed_array_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int classid); +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValueConst new_target, + JSValueConst src_obj, + int classid); static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, BOOL is_arg); +static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst *argv, @@ -1190,7 +1230,7 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m); static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, JS_MarkFunc *mark_func); static JSValue js_import_meta(JSContext *ctx); -static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options); static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); static JSValue js_new_promise_capability(JSContext *ctx, JSValue *resolving_funcs, @@ -1201,6 +1241,8 @@ static __exception int perform_promise_then(JSContext *ctx, JSValueConst *cap_resolving_funcs); static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); static int js_string_compare(JSContext *ctx, const JSString *p1, const JSString *p2); static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); @@ -1212,8 +1254,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, JSObject *p, JSAtom prop); static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); -static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, - JS_MarkFunc *mark_func); static void JS_AddIntrinsicBasicObjects(JSContext *ctx); static void js_free_shape(JSRuntime *rt, JSShape *sh); static void js_free_shape_null(JSRuntime *rt, JSShape *sh); @@ -1241,13 +1281,26 @@ static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type); static void remove_gc_object(JSGCObjectHeader *h); -static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag); +static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_map); +static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh); +static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh); +static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh); +static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects); +static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, + JSValueConst obj, JSValueConst method); +static int js_string_find_invalid_codepoint(JSString *p); +static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); static const JSClassExoticMethods js_arguments_exotic_methods; static const JSClassExoticMethods js_string_exotic_methods; @@ -1309,13 +1362,6 @@ void *js_mallocz_rt(JSRuntime *rt, size_t size) return memset(ptr, 0, size); } -/* called by libbf */ -static void *js_bf_realloc(void *opaque, void *ptr, size_t size) -{ - JSRuntime *rt = opaque; - return js_realloc_rt(rt, ptr, size); -} - /* Throw out of memory in case of error */ void *js_malloc(JSContext *ctx, size_t size) { @@ -1431,6 +1477,10 @@ static inline int is_digit(int c) { return c >= '0' && c <= '9'; } +static inline int string_get(const JSString *p, int idx) { + return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +} + typedef struct JSClassShortDef { JSAtom class_name; JSClassFinalizer *finalizer; @@ -1467,16 +1517,11 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ + { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */ { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ -#ifdef CONFIG_BIGNUM - { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */ - { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */ - { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */ - { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */ -#endif { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ @@ -1506,61 +1551,6 @@ static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, return 0; } -static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "unsupported operation"); -} - -static JSValue invalid_to_string(JSContext *ctx, JSValueConst val) -{ - return JS_ThrowUnsupportedOperation(ctx); -} - -static JSValue invalid_from_string(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - return JS_NAN; -} - -static int invalid_unary_arith(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - JS_FreeValue(ctx, op1); - JS_ThrowUnsupportedOperation(ctx); - return -1; -} - -static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowUnsupportedOperation(ctx); - return -1; -} - -static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, - int64_t exponent) -{ - return JS_ThrowUnsupportedOperation(ctx); -} - -static int invalid_mul_pow10(JSContext *ctx, JSValue *sp) -{ - JS_ThrowUnsupportedOperation(ctx); - return -1; -} - -static void set_dummy_numeric_ops(JSNumericOperations *ops) -{ - ops->to_string = invalid_to_string; - ops->from_string = invalid_from_string; - ops->unary_arith = invalid_unary_arith; - ops->binary_arith = invalid_binary_arith; - ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64; - ops->mul_pow10 = invalid_mul_pow10; -} - #if !defined(CONFIG_STACK_CHECK) /* no stack limitation */ static inline uintptr_t js_get_stack_pointer(void) @@ -1608,18 +1598,12 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->malloc_state = ms; rt->malloc_gc_threshold = 256 * 1024; - bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); - set_dummy_numeric_ops(&rt->bigint_ops); -#ifdef CONFIG_BIGNUM - set_dummy_numeric_ops(&rt->bigfloat_ops); - set_dummy_numeric_ops(&rt->bigdecimal_ops); -#endif - init_list_head(&rt->context_list); init_list_head(&rt->gc_obj_list); init_list_head(&rt->gc_zero_ref_count_list); rt->gc_phase = JS_GC_PHASE_NONE; - + init_list_head(&rt->weakref_list); + #ifdef DUMP_LEAKS init_list_head(&rt->string_list); #endif @@ -1646,7 +1630,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->stack_size = JS_DEFAULT_STACK_SIZE; JS_UpdateStackTop(rt); - rt->current_exception = JS_NULL; + rt->current_exception = JS_UNINITIALIZED; return rt; fail: @@ -1665,19 +1649,19 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) } /* default memory allocation functions with memory limitation */ -static inline size_t js_def_malloc_usable_size(void *ptr) +static size_t js_def_malloc_usable_size(const void *ptr) { #if defined(__APPLE__) return malloc_size(ptr); #elif defined(_WIN32) - return _msize(ptr); + return _msize((void *)ptr); #elif defined(EMSCRIPTEN) return 0; -#elif defined(__linux__) - return malloc_usable_size(ptr); +#elif defined(__linux__) || defined(__GLIBC__) + return malloc_usable_size((void *)ptr); #else /* change this to `return 0;` if compilation fails */ - return malloc_usable_size(ptr); + return malloc_usable_size((void *)ptr); #endif } @@ -1741,18 +1725,7 @@ static const JSMallocFunctions def_malloc_funcs = { js_def_malloc, js_def_free, js_def_realloc, -#if defined(__APPLE__) - malloc_size, -#elif defined(_WIN32) - (size_t (*)(const void *))_msize, -#elif defined(EMSCRIPTEN) - NULL, -#elif defined(__linux__) - (size_t (*)(const void *))malloc_usable_size, -#else - /* change this to `NULL,` if compilation fails */ - malloc_usable_size, -#endif + js_def_malloc_usable_size, }; JSRuntime *JS_NewRuntime(void) @@ -1792,6 +1765,16 @@ void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, rt->sab_funcs = *sf; } +void JS_SetStripInfo(JSRuntime *rt, int flags) +{ + rt->strip_flags = flags; +} + +int JS_GetStripInfo(JSRuntime *rt) +{ + return rt->strip_flags; +} + /* return 0 if OK, < 0 if exception */ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv) @@ -1930,7 +1913,9 @@ void JS_FreeRuntime(JSRuntime *rt) } init_list_head(&rt->job_list); - JS_RunGC(rt); + /* don't remove the weak objects to avoid create new jobs with + FinalizationRegistry */ + JS_RunGCInternal(rt, FALSE); #ifdef DUMP_LEAKS /* leaking objects */ @@ -1972,6 +1957,7 @@ void JS_FreeRuntime(JSRuntime *rt) } #endif assert(list_empty(&rt->gc_obj_list)); + assert(list_empty(&rt->weakref_list)); /* free the classes */ for(i = 0; i < rt->class_count; i++) { @@ -1982,8 +1968,6 @@ void JS_FreeRuntime(JSRuntime *rt) } js_free_rt(rt, rt->class_array); - bf_context_end(&rt->bf_ctx); - #ifdef DUMP_LEAKS /* only the atoms defined in JS_InitAtoms() should be left */ { @@ -2018,7 +2002,7 @@ void JS_FreeRuntime(JSRuntime *rt) printf(")"); break; case JS_ATOM_TYPE_SYMBOL: - if (p->hash == JS_ATOM_HASH_SYMBOL) { + if (p->hash != JS_ATOM_HASH_PRIVATE) { printf("Symbol("); JS_DumpString(rt, p); printf(")"); @@ -2120,11 +2104,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) } ctx->rt = rt; list_add_tail(&ctx->link, &rt->context_list); - ctx->bf_ctx = &rt->bf_ctx; -#ifdef CONFIG_BIGNUM - ctx->fp_env.prec = 113; - ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL; -#endif for(i = 0; i < rt->class_count; i++) ctx->class_proto[i] = JS_NULL; ctx->array_ctor = JS_NULL; @@ -2154,7 +2133,7 @@ JSContext *JS_NewContext(JSRuntime *rt) JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicPromise(ctx); - JS_AddIntrinsicBigInt(ctx); + JS_AddIntrinsicWeakRef(ctx); return ctx; } @@ -2195,7 +2174,6 @@ JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) typedef enum JSFreeModuleEnum { JS_FREE_MODULE_ALL, JS_FREE_MODULE_NOT_RESOLVED, - JS_FREE_MODULE_NOT_EVALUATED, } JSFreeModuleEnum; /* XXX: would be more efficient with separate module lists */ @@ -2205,8 +2183,7 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) list_for_each_safe(el, el1, &ctx->loaded_modules) { JSModuleDef *m = list_entry(el, JSModuleDef, link); if (flag == JS_FREE_MODULE_ALL || - (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) || - (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) { + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { js_free_module_def(ctx, m); } } @@ -2265,7 +2242,7 @@ void JS_FreeContext(JSContext *ctx) if (--ctx->header.ref_count > 0) return; assert(ctx->header.ref_count == 0); - + #ifdef DUMP_ATOMS JS_DumpAtoms(ctx->rt); #endif @@ -2356,19 +2333,6 @@ static inline BOOL is_strict_mode(JSContext *ctx) return (sf && (sf->js_mode & JS_MODE_STRICT)); } -#ifdef CONFIG_BIGNUM -static inline BOOL is_math_mode(JSContext *ctx) -{ - JSStackFrame *sf = ctx->rt->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_MATH)); -} -#else -static inline BOOL is_math_mode(JSContext *ctx) -{ - return FALSE; -} -#endif - /* JSAtom support */ #define JS_ATOM_TAG_INT (1U << 31) @@ -2417,10 +2381,7 @@ static inline BOOL is_num_string(uint32_t *pval, const JSString *p) len = p->len; if (len == 0 || len > 10) return FALSE; - if (p->is_wide_char) - c = p->u.str16[0]; - else - c = p->u.str8[0]; + c = string_get(p, 0); if (is_num(c)) { if (c == '0') { if (len != 1) @@ -2429,10 +2390,7 @@ static inline BOOL is_num_string(uint32_t *pval, const JSString *p) } else { n = c - '0'; for(i = 1; i < len; i++) { - if (p->is_wide_char) - c = p->u.str16[i]; - else - c = p->u.str8[i]; + c = string_get(p, i); if (!is_num(c)) return FALSE; n64 = (uint64_t)n * 10 + (c - '0'); @@ -2477,10 +2435,35 @@ static uint32_t hash_string(const JSString *str, uint32_t h) return h; } -static __maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p) +static uint32_t hash_string_rope(JSValueConst val, uint32_t h) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + return hash_string(JS_VALUE_GET_STRING(val), h); + } else { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + h = hash_string_rope(r->left, h); + return hash_string_rope(r->right, h); + } +} + +static __maybe_unused void JS_DumpChar(FILE *fo, int c, int sep) +{ + if (c == sep || c == '\\') { + fputc('\\', fo); + fputc(c, fo); + } else if (c >= ' ' && c <= 126) { + fputc(c, fo); + } else if (c == '\n') { + fputc('\\', fo); + fputc('n', fo); + } else { + fprintf(fo, "\\u%04x", c); + } +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) { - int i, c, sep; + int i, sep; if (p == NULL) { printf(""); @@ -2490,21 +2473,7 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt, sep = (p->header.ref_count == 1) ? '\"' : '\''; putchar(sep); for(i = 0; i < p->len; i++) { - if (p->is_wide_char) - c = p->u.str16[i]; - else - c = p->u.str8[i]; - if (c == sep || c == '\\') { - putchar('\\'); - putchar(c); - } else if (c >= ' ' && c <= 126) { - putchar(c); - } else if (c == '\n') { - putchar('\\'); - putchar('n'); - } else { - printf("\\u%04x", c); - } + JS_DumpChar(stdout, string_get(p, i), sep); } putchar(sep); } @@ -2642,14 +2611,10 @@ static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) case JS_ATOM_TYPE_GLOBAL_SYMBOL: return JS_ATOM_KIND_SYMBOL; case JS_ATOM_TYPE_SYMBOL: - switch(p->hash) { - case JS_ATOM_HASH_SYMBOL: - return JS_ATOM_KIND_SYMBOL; - case JS_ATOM_HASH_PRIVATE: + if (p->hash == JS_ATOM_HASH_PRIVATE) return JS_ATOM_KIND_PRIVATE; - default: - abort(); - } + else + return JS_ATOM_KIND_SYMBOL; default: abort(); } @@ -2709,7 +2674,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) if (p->hash == h && p->atom_type == atom_type && p->len == len && - js_string_memcmp(p, str, len) == 0) { + js_string_memcmp(p, 0, str, 0, len) == 0) { if (!__JS_AtomIsConst(i)) p->header.ref_count++; goto done; @@ -2719,7 +2684,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) } else { h1 = 0; /* avoid warning */ if (atom_type == JS_ATOM_TYPE_SYMBOL) { - h = JS_ATOM_HASH_SYMBOL; + h = 0; } else { h = JS_ATOM_HASH_PRIVATE; atom_type = JS_ATOM_TYPE_SYMBOL; @@ -2847,6 +2812,7 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, return __JS_NewAtom(rt, p, atom_type); } +/* Warning: str must be ASCII only */ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, int atom_type) { @@ -2911,7 +2877,13 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) #ifdef DUMP_LEAKS list_del(&p->link); #endif - js_free_rt(rt, p); + if (p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash != JS_ATOM_HASH_PRIVATE && p->hash != 0) { + /* live weak references are still present on this object: keep + it */ + } else { + js_free_rt(rt, p); + } rt->atom_count--; assert(rt->atom_count >= 0); } @@ -2941,11 +2913,25 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); } +/* XXX: optimize */ +static size_t count_ascii(const uint8_t *buf, size_t len) +{ + const uint8_t *p, *p_end; + p = buf; + p_end = buf + len; + while (p < p_end && *p < 128) + p++; + return p - buf; +} + +/* str is UTF-8 encoded */ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) { JSValue val; - if (len == 0 || !is_digit(*str)) { + if (len == 0 || + (!is_digit(*str) && + count_ascii((const uint8_t *)str, len) == len)) { JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); if (atom) return atom; @@ -2968,8 +2954,9 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) } else { char buf[11]; JSValue val; - snprintf(buf, sizeof(buf), "%u", n); - val = JS_NewString(ctx, buf); + size_t len; + len = u32toa(buf, n); + val = js_new_string8_len(ctx, buf, len); if (JS_IsException(val)) return JS_ATOM_NULL; return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), @@ -2984,8 +2971,9 @@ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) } else { char buf[24]; JSValue val; - snprintf(buf, sizeof(buf), "%" PRId64 , n); - val = JS_NewString(ctx, buf); + size_t len; + len = i64toa(buf, n); + val = js_new_string8_len(ctx, buf, len); if (JS_IsException(val)) return JS_ATOM_NULL; return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), @@ -3051,10 +3039,7 @@ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, return (const char *)str->u.str8; } for(i = 0; i < str->len; i++) { - if (str->is_wide_char) - c = str->u.str16[i]; - else - c = str->u.str8[i]; + c = string_get(str, i); if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) break; if (c < 128) { @@ -3080,8 +3065,8 @@ static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) char buf[ATOM_GET_STR_BUF_SIZE]; if (__JS_AtomIsTaggedInt(atom)) { - snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); - return JS_NewString(ctx, buf); + size_t len = u32toa(buf, __JS_AtomToUInt32(atom)); + return js_new_string8_len(ctx, buf, len); } else { JSRuntime *rt = ctx->rt; JSAtomStruct *p; @@ -3145,7 +3130,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) JSRuntime *rt = ctx->rt; JSAtomStruct *p1; JSString *p; - int c, len, ret; + int c, ret; JSValue num, str; if (__JS_AtomIsTaggedInt(atom)) @@ -3154,54 +3139,24 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) p1 = rt->atom_array[atom]; if (p1->atom_type != JS_ATOM_TYPE_STRING) return JS_UNDEFINED; + switch(atom) { + case JS_ATOM_minus_zero: + return __JS_NewFloat64(ctx, -0.0); + case JS_ATOM_Infinity: + return __JS_NewFloat64(ctx, INFINITY); + case JS_ATOM_minus_Infinity: + return __JS_NewFloat64(ctx, -INFINITY); + case JS_ATOM_NaN: + return __JS_NewFloat64(ctx, NAN); + default: + break; + } p = p1; - len = p->len; - if (p->is_wide_char) { - const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; - if (r >= r_end) - return JS_UNDEFINED; - c = *r; - if (c == '-') { - if (r >= r_end) - return JS_UNDEFINED; - r++; - c = *r; - /* -0 case is specific */ - if (c == '0' && len == 2) - goto minus_zero; - } - /* XXX: should test NaN, but the tests do not check it */ - if (!is_num(c)) { - /* XXX: String should be normalized, therefore 8-bit only */ - const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; - if (!(c =='I' && (r_end - r) == 8 && - !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) - return JS_UNDEFINED; - } - } else { - const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; - if (r >= r_end) - return JS_UNDEFINED; - c = *r; - if (c == '-') { - if (r >= r_end) - return JS_UNDEFINED; - r++; - c = *r; - /* -0 case is specific */ - if (c == '0' && len == 2) { - minus_zero: - return __JS_NewFloat64(ctx, -0.0); - } - } - if (!is_num(c)) { - if (!(c =='I' && (r_end - r) == 8 && - !memcmp(r + 1, "nfinity", 7))) - return JS_UNDEFINED; - } - } - /* XXX: bignum: would be better to only accept integer to avoid - relying on current floating point precision */ + if (p->len == 0) + return JS_UNDEFINED; + c = string_get(p, 0); + if (!is_num(c) && c != '-') + return JS_UNDEFINED; /* this is ECMA CanonicalNumericIndexString primitive */ num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); if (JS_IsException(num)) @@ -3257,59 +3212,24 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) return FALSE; p = rt->atom_array[v]; return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && - p->hash == JS_ATOM_HASH_SYMBOL) || + p->hash != JS_ATOM_HASH_PRIVATE) || p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && !(p->len == 0 && p->is_wide_char != 0)); } -static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - const char *p; - int i; - - /* XXX: should handle embedded null characters */ - /* XXX: should move encoding code to JS_AtomGetStr */ - p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); - for (i = 0; p[i]; i++) { - int c = (unsigned char)p[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) - break; - } - if (i > 0 && p[i] == '\0') { - printf("%s", p); - } else { - putchar('"'); - printf("%.*s", i, p); - for (; p[i]; i++) { - int c = (unsigned char)p[i]; - if (c == '\"' || c == '\\') { - putchar('\\'); - putchar(c); - } else if (c >= ' ' && c <= 126) { - putchar(c); - } else if (c == '\n') { - putchar('\\'); - putchar('n'); - } else { - printf("\\u%04x", c); - } - } - putchar('\"'); - } -} - /* free with JS_FreeCString() */ -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom) { JSValue str; const char *cstr; str = JS_AtomToString(ctx, atom); - if (JS_IsException(str)) + if (JS_IsException(str)) { + if (plen) + *plen = 0; return NULL; - cstr = JS_ToCString(ctx, str); + } + cstr = JS_ToCStringLen(ctx, plen, str); JS_FreeValue(ctx, str); return cstr; } @@ -3322,7 +3242,7 @@ static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) const char *cstr; char *cstr2; size_t len, len1; - + str = JS_AtomToString(ctx, name); if (JS_IsException(str)) return JS_ATOM_NULL; @@ -3350,7 +3270,9 @@ static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) { char buf[16]; - snprintf(buf, sizeof(buf), "%u", n); + size_t len; + len = u32toa(buf, n); + buf[len] = '\0'; return js_atom_concat_str(ctx, name, buf); } @@ -3361,19 +3283,37 @@ static inline BOOL JS_IsEmptyString(JSValueConst v) /* JSClass support */ +#ifdef CONFIG_ATOMICS +static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + /* a new class ID is allocated if *pclass_id != 0 */ JSClassID JS_NewClassID(JSClassID *pclass_id) { JSClassID class_id; - /* XXX: make it thread safe */ +#ifdef CONFIG_ATOMICS + pthread_mutex_lock(&js_class_id_mutex); +#endif class_id = *pclass_id; if (class_id == 0) { class_id = js_class_id_alloc++; *pclass_id = class_id; } +#ifdef CONFIG_ATOMICS + pthread_mutex_unlock(&js_class_id_mutex); +#endif return class_id; } +JSClassID JS_GetClassID(JSValue v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return JS_INVALID_CLASS_ID; + p = JS_VALUE_GET_OBJ(v); + return p->class_id; +} + BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) { return (class_id < rt->class_count && @@ -3448,7 +3388,7 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) return ret; } -static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) { JSString *str; @@ -3463,7 +3403,12 @@ static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) return JS_MKPTR(JS_TAG_STRING, str); } -static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len) +static JSValue js_new_string8(JSContext *ctx, const char *buf) +{ + return js_new_string8_len(ctx, buf, strlen(buf)); +} + +static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) { JSString *str; str = js_alloc_string(ctx, len, 1); @@ -3477,10 +3422,10 @@ static JSValue js_new_string_char(JSContext *ctx, uint16_t c) { if (c < 0x100) { uint8_t ch8 = c; - return js_new_string8(ctx, &ch8, 1); + return js_new_string8_len(ctx, (const char *)&ch8, 1); } else { uint16_t ch16 = c; - return js_new_string16(ctx, &ch16, 1); + return js_new_string16_len(ctx, &ch16, 1); } } @@ -3498,7 +3443,7 @@ static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) c |= p->u.str16[i]; } if (c > 0xFF) - return js_new_string16(ctx, p->u.str16 + start, len); + return js_new_string16_len(ctx, p->u.str16 + start, len); str = js_alloc_string(ctx, len, 0); if (!str) @@ -3509,7 +3454,7 @@ static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) str->u.str8[len] = '\0'; return JS_MKPTR(JS_TAG_STRING, str); } else { - return js_new_string8(ctx, p->u.str8 + start, len); + return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len); } } @@ -3668,28 +3613,23 @@ static int string_buffer_putc(StringBuffer *s, uint32_t c) { if (unlikely(c >= 0x10000)) { /* surrogate pair */ - c -= 0x10000; - if (string_buffer_putc16(s, (c >> 10) + 0xd800)) + if (string_buffer_putc16(s, get_hi_surrogate(c))) return -1; - c = (c & 0x3ff) + 0xdc00; + c = get_lo_surrogate(c); } return string_buffer_putc16(s, c); } -static int string_get(const JSString *p, int idx) { - return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; -} - static int string_getc(const JSString *p, int *pidx) { int idx, c, c1; idx = *pidx; if (p->is_wide_char) { c = p->u.str16[idx++]; - if (c >= 0xd800 && c < 0xdc00 && idx < p->len) { + if (is_hi_surrogate(c) && idx < p->len) { c1 = p->u.str16[idx]; - if (c1 >= 0xdc00 && c1 < 0xe000) { - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + if (is_lo_surrogate(c1)) { + c = from_surrogate(c, c1); idx++; } } @@ -3774,13 +3714,21 @@ static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) return -1; } if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { - v1 = JS_ToString(s->ctx, v); - if (JS_IsException(v1)) - return string_buffer_set_error(s); - p = JS_VALUE_GET_STRING(v1); - res = string_buffer_concat(s, p, 0, p->len); - JS_FreeValue(s->ctx, v1); - return res; + if (JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE) { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v); + /* recursion is acceptable because the rope depth is bounded */ + if (string_buffer_concat_value(s, r->left)) + return -1; + return string_buffer_concat_value(s, r->right); + } else { + v1 = JS_ToString(s->ctx, v); + if (JS_IsException(v1)) + return string_buffer_set_error(s); + p = JS_VALUE_GET_STRING(v1); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v1); + return res; + } } p = JS_VALUE_GET_STRING(v); return string_buffer_concat(s, p, 0, p->len); @@ -3860,18 +3808,16 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) uint32_t c; StringBuffer b_s, *b = &b_s; size_t len1; - + p_start = (const uint8_t *)buf; p_end = p_start + buf_len; - p = p_start; - while (p < p_end && *p < 128) - p++; - len1 = p - p_start; + len1 = count_ascii(p_start, buf_len); + p = p_start + len1; if (len1 > JS_STRING_LEN_MAX) return JS_ThrowInternalError(ctx, "string too long"); if (p == p_end) { /* ASCII string */ - return js_new_string8(ctx, (const uint8_t *)buf, buf_len); + return js_new_string8_len(ctx, buf, buf_len); } else { if (string_buffer_init(ctx, b, buf_len)) goto fail; @@ -3887,9 +3833,8 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) } else if (c <= 0x10FFFF) { p = p_next; /* surrogate pair */ - c -= 0x10000; - string_buffer_putc16(b, (c >> 10) + 0xd800); - c = (c & 0x3ff) + 0xdc00; + string_buffer_putc16(b, get_hi_surrogate(c)); + c = get_lo_surrogate(c); } else { /* invalid char */ c = 0xfffd; @@ -3945,11 +3890,6 @@ static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, return JS_EXCEPTION; } -JSValue JS_NewString(JSContext *ctx, const char *str) -{ - return JS_NewStringLen(ctx, str, strlen(str)); -} - JSValue JS_NewAtomString(JSContext *ctx, const char *str) { JSAtom atom = JS_NewAtom(ctx, str); @@ -4027,13 +3967,12 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BO if (c < 0x80) { *q++ = c; } else { - if (c >= 0xd800 && c < 0xdc00) { + if (is_hi_surrogate(c)) { if (pos < len && !cesu8) { c1 = src[pos]; - if (c1 >= 0xdc00 && c1 < 0xe000) { + if (is_lo_surrogate(c1)) { pos++; - /* surrogate pair */ - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + c = from_surrogate(c, c1); } else { /* Keep unmatched surrogate code points */ /* c = 0xfffd; */ /* error */ @@ -4066,7 +4005,7 @@ void JS_FreeCString(JSContext *ctx, const char *ptr) if (!ptr) return; /* purposely removing constness */ - p = (JSString *)(void *)(ptr - offsetof(JSString, u)); + p = container_of(ptr, JSString, u); JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); } @@ -4092,20 +4031,21 @@ static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) return 0; } -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) +static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2, + int pos2, int len) { int res; if (likely(!p1->is_wide_char)) { if (likely(!p2->is_wide_char)) - res = memcmp(p1->u.str8, p2->u.str8, len); + res = memcmp(p1->u.str8 + pos1, p2->u.str8 + pos2, len); else - res = -memcmp16_8(p2->u.str16, p1->u.str8, len); + res = -memcmp16_8(p2->u.str16 + pos2, p1->u.str8 + pos1, len); } else { if (!p2->is_wide_char) - res = memcmp16_8(p1->u.str16, p2->u.str8, len); + res = memcmp16_8(p1->u.str16 + pos1, p2->u.str8 + pos2, len); else - res = memcmp16(p1->u.str16, p2->u.str16, len); + res = memcmp16(p1->u.str16 + pos1, p2->u.str16 + pos2, len); } return res; } @@ -4116,7 +4056,7 @@ static int js_string_compare(JSContext *ctx, { int res, len; len = min_int(p1->len, p2->len); - res = js_string_memcmp(p1, p2, len); + res = js_string_memcmp(p1, 0, p2, 0, len); if (res == 0) { if (p1->len == p2->len) res = 0; @@ -4166,53 +4106,452 @@ static JSValue JS_ConcatString1(JSContext *ctx, return JS_MKPTR(JS_TAG_STRING, p); } -/* op1 and op2 are converted to strings. For convience, op1 or op2 = +static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) { + if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + JSString *p2 = JS_VALUE_GET_STRING(op2); + size_t size1; + + if (p2->len == 0) + return TRUE; + if (p1->header.ref_count != 1) + return FALSE; + size1 = js_malloc_usable_size(ctx, p1); + if (p1->is_wide_char) { + if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) { + if (p2->is_wide_char) { + memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); + p1->len += p2->len; + return TRUE; + } else { + size_t i; + for (i = 0; i < p2->len; i++) { + p1->u.str16[p1->len++] = p2->u.str8[i]; + } + return TRUE; + } + } + } else if (!p2->is_wide_char) { + if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) { + memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); + p1->len += p2->len; + p1->u.str8[p1->len] = '\0'; + return TRUE; + } + } + } + return FALSE; +} + +static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue ret; + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + if (JS_ConcatStringInPlace(ctx, p1, op2)) { + JS_FreeValue(ctx, op2); + return op1; + } + p2 = JS_VALUE_GET_STRING(op2); + ret = JS_ConcatString1(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return ret; +} + +/* Return the character at position 'idx'. 'val' must be a string or rope */ +static int string_rope_get(JSValueConst val, uint32_t idx) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + return string_get(JS_VALUE_GET_STRING(val), idx); + } else { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + uint32_t len; + if (JS_VALUE_GET_TAG(r->left) == JS_TAG_STRING) + len = JS_VALUE_GET_STRING(r->left)->len; + else + len = JS_VALUE_GET_STRING_ROPE(r->left)->len; + if (idx < len) + return string_rope_get(r->left, idx); + else + return string_rope_get(r->right, idx - len); + } +} + +typedef struct { + JSValueConst stack[JS_STRING_ROPE_MAX_DEPTH]; + int stack_len; +} JSStringRopeIter; + +static void string_rope_iter_init(JSStringRopeIter *s, JSValueConst val) +{ + s->stack_len = 0; + s->stack[s->stack_len++] = val; +} + +/* iterate thru a rope and return the strings in order */ +static JSString *string_rope_iter_next(JSStringRopeIter *s) +{ + JSValueConst val; + JSStringRope *r; + + if (s->stack_len == 0) + return NULL; + val = s->stack[--s->stack_len]; + for(;;) { + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) + return JS_VALUE_GET_STRING(val); + r = JS_VALUE_GET_STRING_ROPE(val); + assert(s->stack_len < JS_STRING_ROPE_MAX_DEPTH); + s->stack[s->stack_len++] = r->right; + val = r->left; + } +} + +static uint32_t string_rope_get_len(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) + return JS_VALUE_GET_STRING(val)->len; + else + return JS_VALUE_GET_STRING_ROPE(val)->len; +} + +static int js_string_rope_compare(JSContext *ctx, JSValueConst op1, + JSValueConst op2, BOOL eq_only) +{ + uint32_t len1, len2, len, pos1, pos2, l; + int res; + JSStringRopeIter it1, it2; + JSString *p1, *p2; + + len1 = string_rope_get_len(op1); + len2 = string_rope_get_len(op2); + /* no need to go further for equality test if + different length */ + if (eq_only && len1 != len2) + return 1; + len = min_uint32(len1, len2); + string_rope_iter_init(&it1, op1); + string_rope_iter_init(&it2, op2); + p1 = string_rope_iter_next(&it1); + p2 = string_rope_iter_next(&it2); + pos1 = 0; + pos2 = 0; + while (len != 0) { + l = min_uint32(p1->len - pos1, p2->len - pos2); + l = min_uint32(l, len); + res = js_string_memcmp(p1, pos1, p2, pos2, l); + if (res != 0) + return res; + len -= l; + pos1 += l; + if (pos1 >= p1->len) { + p1 = string_rope_iter_next(&it1); + pos1 = 0; + } + pos2 += l; + if (pos2 >= p2->len) { + p2 = string_rope_iter_next(&it2); + pos2 = 0; + } + } + + if (len1 == len2) + res = 0; + else if (len1 < len2) + res = -1; + else + res = 1; + return res; +} + +/* 'rope' must be a rope. return a string and modify the rope so that + it won't need to be linearized again. */ +static JSValue js_linearize_string_rope(JSContext *ctx, JSValue rope) +{ + StringBuffer b_s, *b = &b_s; + JSStringRope *r; + JSValue ret; + + r = JS_VALUE_GET_STRING_ROPE(rope); + + /* check whether it is already linearized */ + if (JS_VALUE_GET_TAG(r->right) == JS_TAG_STRING && + JS_VALUE_GET_STRING(r->right)->len == 0) { + ret = JS_DupValue(ctx, r->left); + JS_FreeValue(ctx, rope); + return ret; + } + if (string_buffer_init2(ctx, b, r->len, r->is_wide_char)) + goto fail; + if (string_buffer_concat_value(b, rope)) + goto fail; + ret = string_buffer_end(b); + if (r->header.ref_count > 1) { + /* update the rope so that it won't need to be linearized again */ + JS_FreeValue(ctx, r->left); + JS_FreeValue(ctx, r->right); + r->left = JS_DupValue(ctx, ret); + r->right = JS_AtomToString(ctx, JS_ATOM_empty_string); + } + JS_FreeValue(ctx, rope); + return ret; + fail: + JS_FreeValue(ctx, rope); + return JS_EXCEPTION; +} + +static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope); + +/* op1 and op2 must be strings or string ropes */ +static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2) +{ + uint32_t len; + int is_wide_char, depth; + JSStringRope *r; + JSValue res; + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { + JSString *p1 = JS_VALUE_GET_STRING(op1); + len = p1->len; + is_wide_char = p1->is_wide_char; + depth = 0; + } else { + JSStringRope *r1 = JS_VALUE_GET_STRING_ROPE(op1); + len = r1->len; + is_wide_char = r1->is_wide_char; + depth = r1->depth; + } + + if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + JSString *p2 = JS_VALUE_GET_STRING(op2); + len += p2->len; + is_wide_char |= p2->is_wide_char; + } else { + JSStringRope *r2 = JS_VALUE_GET_STRING_ROPE(op2); + len += r2->len; + is_wide_char |= r2->is_wide_char; + depth = max_int(depth, r2->depth); + } + if (len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail; + } + r = js_malloc(ctx, sizeof(*r)); + if (!r) + goto fail; + r->header.ref_count = 1; + r->len = len; + r->is_wide_char = is_wide_char; + r->depth = depth + 1; + r->left = op1; + r->right = op2; + res = JS_MKPTR(JS_TAG_STRING_ROPE, r); + if (r->depth > JS_STRING_ROPE_MAX_DEPTH) { + JSValue res2; +#ifdef DUMP_ROPE_REBALANCE + printf("rebalance: initial depth=%d\n", r->depth); +#endif + res2 = js_rebalancee_string_rope(ctx, res); +#ifdef DUMP_ROPE_REBALANCE + if (JS_VALUE_GET_TAG(res2) == JS_TAG_STRING_ROPE) + printf("rebalance: final depth=%d\n", JS_VALUE_GET_STRING_ROPE(res2)->depth); +#endif + JS_FreeValue(ctx, res); + return res2; + } else { + return res; + } + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; +} + +#define ROPE_N_BUCKETS 44 + +/* Fibonacii numbers starting from F_2 */ +static const uint32_t rope_bucket_len[ROPE_N_BUCKETS] = { + 1, 2, 3, 5, + 8, 13, 21, 34, + 55, 89, 144, 233, + 377, 610, 987, 1597, + 2584, 4181, 6765, 10946, + 17711, 28657, 46368, 75025, + 121393, 196418, 317811, 514229, + 832040, 1346269, 2178309, 3524578, + 5702887, 9227465, 14930352, 24157817, + 39088169, 63245986, 102334155, 165580141, + 267914296, 433494437, 701408733, 1134903170, /* > JS_STRING_LEN_MAX */ +}; + +static int js_rebalancee_string_rope_rec(JSContext *ctx, JSValue *buckets, + JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + JSString *p = JS_VALUE_GET_STRING(val); + uint32_t len, i; + JSValue a, b; + + len = p->len; + if (len == 0) + return 0; /* nothing to do */ + /* find the bucket i so that rope_bucket_len[i] <= len < + rope_bucket_len[i + 1] and concatenate the ropes in the + buckets before */ + a = JS_NULL; + i = 0; + while (len >= rope_bucket_len[i + 1]) { + b = buckets[i]; + if (!JS_IsNull(b)) { + buckets[i] = JS_NULL; + if (JS_IsNull(a)) { + a = b; + } else { + a = js_new_string_rope(ctx, b, a); + if (JS_IsException(a)) + return -1; + } + } + i++; + } + if (!JS_IsNull(a)) { + a = js_new_string_rope(ctx, a, JS_DupValue(ctx, val)); + if (JS_IsException(a)) + return -1; + } else { + a = JS_DupValue(ctx, val); + } + while (!JS_IsNull(buckets[i])) { + a = js_new_string_rope(ctx, buckets[i], a); + buckets[i] = JS_NULL; + if (JS_IsException(a)) + return -1; + i++; + } + buckets[i] = a; + } else { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + js_rebalancee_string_rope_rec(ctx, buckets, r->left); + js_rebalancee_string_rope_rec(ctx, buckets, r->right); + } + return 0; +} + +/* Return a new rope which is balanced. Algorithm from "Ropes: an + Alternative to Strings", Hans-J. Boehm, Russ Atkinson and Michael + Plass. */ +static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope) +{ + JSValue buckets[ROPE_N_BUCKETS], a, b; + int i; + + for(i = 0; i < ROPE_N_BUCKETS; i++) + buckets[i] = JS_NULL; + if (js_rebalancee_string_rope_rec(ctx, buckets, rope)) + goto fail; + a = JS_NULL; + for(i = 0; i < ROPE_N_BUCKETS; i++) { + b = buckets[i]; + if (!JS_IsNull(b)) { + buckets[i] = JS_NULL; + if (JS_IsNull(a)) { + a = b; + } else { + a = js_new_string_rope(ctx, b, a); + if (JS_IsException(a)) + goto fail; + } + } + } + /* fail safe */ + if (JS_IsNull(a)) + return JS_AtomToString(ctx, JS_ATOM_empty_string); + else + return a; + fail: + for(i = 0; i < ROPE_N_BUCKETS; i++) { + JS_FreeValue(ctx, buckets[i]); + } + return JS_EXCEPTION; +} + +/* op1 and op2 are converted to strings. For convenience, op1 or op2 = JS_EXCEPTION are accepted and return JS_EXCEPTION. */ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) { - JSValue ret; JSString *p1, *p2; - if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING && + JS_VALUE_GET_TAG(op1) != JS_TAG_STRING_ROPE)) { op1 = JS_ToStringFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); return JS_EXCEPTION; } } - if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { + if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING && + JS_VALUE_GET_TAG(op2) != JS_TAG_STRING_ROPE)) { op2 = JS_ToStringFree(ctx, op2); if (JS_IsException(op2)) { JS_FreeValue(ctx, op1); return JS_EXCEPTION; } } - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - /* XXX: could also check if p1 is empty */ - if (p2->len == 0) { - goto ret_op1; - } - if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char - && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { - /* Concatenate in place in available space at the end of p1 */ - if (p1->is_wide_char) { - memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); - p1->len += p2->len; - } else { - memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); - p1->len += p2->len; - p1->u.str8[p1->len] = '\0'; + /* normal concatenation for short strings */ + if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + p2 = JS_VALUE_GET_STRING(op2); + if (p2->len == 0) { + JS_FreeValue(ctx, op2); + return op1; + } + if (p2->len <= JS_STRING_ROPE_SHORT_LEN) { + if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(op1); + if (p1->len <= JS_STRING_ROPE_SHORT2_LEN) { + return JS_ConcatString2(ctx, op1, op2); + } else { + return js_new_string_rope(ctx, op1, op2); + } + } else { + JSStringRope *r1; + r1 = JS_VALUE_GET_STRING_ROPE(op1); + if (JS_VALUE_GET_TAG(r1->right) == JS_TAG_STRING && + JS_VALUE_GET_STRING(r1->right)->len <= JS_STRING_ROPE_SHORT_LEN) { + JSValue val, ret; + val = JS_ConcatString2(ctx, JS_DupValue(ctx, r1->right), op2); + if (JS_IsException(val)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + ret = js_new_string_rope(ctx, JS_DupValue(ctx, r1->left), val); + JS_FreeValue(ctx, op1); + return ret; + } + } + } + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { + JSStringRope *r2; + p1 = JS_VALUE_GET_STRING(op1); + if (p1->len == 0) { + JS_FreeValue(ctx, op1); + return op2; + } + r2 = JS_VALUE_GET_STRING_ROPE(op2); + if (JS_VALUE_GET_TAG(r2->left) == JS_TAG_STRING && + JS_VALUE_GET_STRING(r2->left)->len <= JS_STRING_ROPE_SHORT_LEN) { + JSValue val, ret; + val = JS_ConcatString2(ctx, op1, JS_DupValue(ctx, r2->left)); + if (JS_IsException(val)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + ret = js_new_string_rope(ctx, val, JS_DupValue(ctx, r2->right)); + JS_FreeValue(ctx, op2); + return ret; } - ret_op1: - JS_FreeValue(ctx, op2); - return op1; } - ret = JS_ConcatString1(ctx, p1, p2); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return ret; + return js_new_string_rope(ctx, op1, op2); } /* Shape support */ @@ -4352,7 +4691,7 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, sh->prop_size = prop_size; sh->prop_count = 0; sh->deleted_prop_count = 0; - + /* insert in the hash table */ sh->hash = shape_initial_hash(proto); sh->is_hashed = TRUE; @@ -4445,6 +4784,7 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, JSShapeProperty *pr; void *sh_alloc; intptr_t h; + JSShape *old_sh; sh = *psh; new_size = max_int(count, sh->prop_size * 3 / 2); @@ -4460,19 +4800,21 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, new_hash_size = sh->prop_hash_mask + 1; while (new_hash_size < new_size) new_hash_size = 2 * new_hash_size; + /* resize the property shapes. Using js_realloc() is not possible in + case the GC runs during the allocation */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + /* copy all the shape properties */ + memcpy(sh, old_sh, + sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + if (new_hash_size != (sh->prop_hash_mask + 1)) { - JSShape *old_sh; /* resize the hash table and the properties */ - old_sh = sh; - sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); - if (!sh_alloc) - return -1; - sh = get_shape_from_alloc(sh_alloc, new_hash_size); - list_del(&old_sh->header.link); - /* copy all the fields and the properties */ - memcpy(sh, old_sh, - sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); - list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); new_hash_mask = new_hash_size - 1; sh->prop_hash_mask = new_hash_mask; memset(prop_hash_end(sh) - new_hash_size, 0, @@ -4484,20 +4826,12 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, prop_hash_end(sh)[-h - 1] = i + 1; } } - js_free(ctx, get_alloc_from_shape(old_sh)); } else { - /* only resize the properties */ - list_del(&sh->header.link); - sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), - get_shape_size(new_hash_size, new_size)); - if (unlikely(!sh_alloc)) { - /* insert again in the GC list */ - list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - return -1; - } - sh = get_shape_from_alloc(sh_alloc, new_hash_size); - list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + /* just copy the previous hash table */ + memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); } + js_free(ctx, get_alloc_from_shape(old_sh)); *psh = sh; sh->prop_size = new_size; return 0; @@ -4512,7 +4846,7 @@ static int compact_properties(JSContext *ctx, JSObject *p) uint32_t new_hash_size, i, j, new_hash_mask, new_size; JSShapeProperty *old_pr, *pr; JSProperty *prop, *new_prop; - + sh = p->shape; assert(!sh->is_hashed); @@ -4534,7 +4868,7 @@ static int compact_properties(JSContext *ctx, JSObject *p) list_del(&old_sh->header.link); memcpy(sh, old_sh, sizeof(JSShape)); list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - + memset(prop_hash_end(sh) - new_hash_size, 0, sizeof(prop_hash_end(sh)[0]) * new_hash_size); @@ -4563,7 +4897,7 @@ static int compact_properties(JSContext *ctx, JSObject *p) p->shape = sh; js_free(ctx, get_alloc_from_shape(old_sh)); - + /* reduce the size of the object properties */ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); if (new_prop) @@ -4690,7 +5024,7 @@ static __maybe_unused void JS_DumpShapes(JSRuntime *rt) struct list_head *el; JSObject *p; JSGCObjectHeader *gp; - + printf("JSShapes: {\n"); printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); for(i = 0; i < rt->shape_hash_size; i++) { @@ -4726,10 +5060,10 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->is_exotic = 0; p->fast_array = 0; p->is_constructor = 0; - p->is_uncatchable_error = 0; + p->has_immutable_prototype = 0; p->tmp_mark = 0; p->is_HTMLDDA = 0; - p->first_weak_ref = NULL; + p->weakref_count = 0; p->u.opaque = NULL; p->shape = sh; p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); @@ -4776,6 +5110,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas case JS_CLASS_UINT32_ARRAY: case JS_CLASS_BIG_INT64_ARRAY: case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: case JS_CLASS_FLOAT32_ARRAY: case JS_CLASS_FLOAT64_ARRAY: p->is_exotic = 1; @@ -4793,10 +5128,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas case JS_CLASS_SYMBOL: case JS_CLASS_DATE: case JS_CLASS_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_FLOAT: - case JS_CLASS_BIG_DECIMAL: -#endif p->u.object_data = JS_UNDEFINED; goto set_exotic; case JS_CLASS_REGEXP: @@ -4856,10 +5187,6 @@ static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) case JS_CLASS_SYMBOL: case JS_CLASS_DATE: case JS_CLASS_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_FLOAT: - case JS_CLASS_BIG_DECIMAL: -#endif return JS_DupValue(ctx, p->u.object_data); } } @@ -4880,12 +5207,10 @@ static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) case JS_CLASS_SYMBOL: case JS_CLASS_DATE: case JS_CLASS_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_FLOAT: - case JS_CLASS_BIG_DECIMAL: -#endif JS_FreeValue(ctx, p->u.object_data); - p->u.object_data = val; + p->u.object_data = val; /* for JS_CLASS_STRING, 'val' must + be JS_TAG_STRING (and not a + rope) */ return 0; } } @@ -5016,7 +5341,7 @@ static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, JSValue func_obj; JSObject *p; JSAtom name_atom; - + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); if (JS_IsException(func_obj)) return func_obj; @@ -5033,6 +5358,10 @@ static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, if (!name) name = ""; name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } js_function_set_properties(ctx, func_obj, name_atom, length); JS_FreeAtom(ctx, name_atom); return func_obj; @@ -5227,10 +5556,12 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) if (--var_ref->header.ref_count == 0) { if (var_ref->is_detached) { JS_FreeValueRT(rt, var_ref->value); - remove_gc_object(&var_ref->header); } else { - list_del(&var_ref->header.link); /* still on the stack */ + list_del(&var_ref->var_ref_link); /* still on the stack */ + if (var_ref->async_func) + async_func_free(rt, var_ref->async_func); } + remove_gc_object(&var_ref->header); js_free_rt(rt, var_ref); } } @@ -5328,7 +5659,7 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, if (var_refs) { for(i = 0; i < b->closure_var_count; i++) { JSVarRef *var_ref = var_refs[i]; - if (var_ref && var_ref->is_detached) { + if (var_ref) { mark_func(rt, &var_ref->header); } } @@ -5370,7 +5701,15 @@ static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSForInIterator *it = p->u.for_in_iterator; + int i; + JS_FreeValueRT(rt, it->obj); + if (!it->is_array) { + for(i = 0; i < it->atom_count; i++) { + JS_FreeAtomRT(rt, it->tab_atom[i].atom); + } + js_free_rt(rt, it->tab_atom); + } js_free_rt(rt, it); } @@ -5407,10 +5746,6 @@ static void free_object(JSRuntime *rt, JSObject *p) p->shape = NULL; p->prop = NULL; - if (unlikely(p->first_weak_ref)) { - reset_weak_ref(rt, p); - } - finalizer = rt->class_array[p->class_id].finalizer; if (finalizer) (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); @@ -5422,10 +5757,21 @@ static void free_object(JSRuntime *rt, JSObject *p) p->u.func.home_object = NULL; remove_gc_object(&p->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { - list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) { + if (p->header.ref_count == 0 && p->weakref_count == 0) { + js_free_rt(rt, p); + } else { + /* keep the object structure because there are may be + references to it */ + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } } else { - js_free_rt(rt, p); + /* keep the object structure in case there are weak references to it */ + if (p->weakref_count == 0) { + js_free_rt(rt, p); + } else { + p->header.mark = 0; /* reset the mark so that the weakref can be freed */ + } } } @@ -5438,6 +5784,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: free_function_bytecode(rt, (JSFunctionBytecode *)gp); break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + __async_func_free(rt, (JSAsyncFunctionState *)gp); + break; default: abort(); } @@ -5447,7 +5796,7 @@ static void free_zero_refcount(JSRuntime *rt) { struct list_head *el; JSGCObjectHeader *p; - + rt->gc_phase = JS_GC_PHASE_DECREF; for(;;) { el = rt->gc_zero_ref_count_list.next; @@ -5491,6 +5840,15 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) } } break; + case JS_TAG_STRING_ROPE: + /* Note: recursion is acceptable because the rope depth is bounded */ + { + JSStringRope *p = JS_VALUE_GET_STRING_ROPE(v); + JS_FreeValueRT(rt, p->left); + JS_FreeValueRT(rt, p->right); + js_free_rt(rt, p); + } + break; case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: { @@ -5498,6 +5856,7 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { list_del(&p->link); list_add(&p->link, &rt->gc_zero_ref_count_list); + p->mark = 1; /* indicate that the object is about to be freed */ if (rt->gc_phase == JS_GC_PHASE_NONE) { free_zero_refcount(rt); } @@ -5508,24 +5867,11 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) abort(); /* never freed here */ break; case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif { - JSBigFloat *bf = JS_VALUE_GET_PTR(v); - bf_delete(&bf->num); - js_free_rt(rt, bf); - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *bf = JS_VALUE_GET_PTR(v); - bfdec_delete(&bf->num); - js_free_rt(rt, bf); + JSBigInt *p = JS_VALUE_GET_PTR(v); + js_free_rt(rt, p); } break; -#endif case JS_TAG_SYMBOL: { JSAtomStruct *p = JS_VALUE_GET_PTR(v); @@ -5533,7 +5879,6 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) } break; default: - printf("__JS_FreeValue: unknown tag=%d\n", tag); abort(); } } @@ -5545,6 +5890,36 @@ void __JS_FreeValue(JSContext *ctx, JSValue v) /* garbage collection */ +static void gc_remove_weak_objects(JSRuntime *rt) +{ + struct list_head *el; + + /* add the freed objects to rt->gc_zero_ref_count_list so that + rt->weakref_list is not modified while we traverse it */ + rt->gc_phase = JS_GC_PHASE_DECREF; + + list_for_each(el, &rt->weakref_list) { + JSWeakRefHeader *wh = list_entry(el, JSWeakRefHeader, link); + switch(wh->weakref_type) { + case JS_WEAKREF_TYPE_MAP: + map_delete_weakrefs(rt, wh); + break; + case JS_WEAKREF_TYPE_WEAKREF: + weakref_delete_weakref(rt, wh); + break; + case JS_WEAKREF_TYPE_FINREC: + finrec_delete_weakref(rt, wh); + break; + default: + abort(); + } + } + + rt->gc_phase = JS_GC_PHASE_NONE; + /* free the freed objects here. */ + free_zero_refcount(rt); +} + static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type) { @@ -5596,11 +5971,9 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, if (pr->u.getset.setter) mark_func(rt, &pr->u.getset.setter->header); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - if (pr->u.var_ref->is_detached) { - /* Note: the tag does not matter - provided it is a GC object */ - mark_func(rt, &pr->u.var_ref->header); - } + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { js_autoinit_mark(rt, pr, mark_func); } @@ -5634,16 +6007,32 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, case JS_GC_OBJ_TYPE_VAR_REF: { JSVarRef *var_ref = (JSVarRef *)gp; - /* only detached variable referenced are taken into account */ - assert(var_ref->is_detached); - JS_MarkValue(rt, *var_ref->pvalue, mark_func); + if (var_ref->is_detached) { + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } else if (var_ref->async_func) { + mark_func(rt, &var_ref->async_func->header); + } } break; case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: { - JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp; - if (s->is_active) - async_func_mark(rt, &s->func_state, mark_func); + JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp; + JSStackFrame *sf = &s->frame; + JSValue *sp; + + if (!s->is_completed) { + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + /* sf->cur_sp = NULL if the function is running */ + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } + } JS_MarkValue(rt, s->resolving_funcs[0], mark_func); JS_MarkValue(rt, s->resolving_funcs[1], mark_func); } @@ -5681,7 +6070,7 @@ static void gc_decref(JSRuntime *rt) { struct list_head *el, *el1; JSGCObjectHeader *p; - + init_list_head(&rt->tmp_obj_list); /* decrement the refcount of all the children of all the GC @@ -5728,7 +6117,7 @@ static void gc_scan(JSRuntime *rt) p->mark = 0; /* reset the mark for the next GC call */ mark_children(rt, p, gc_scan_incref_child); } - + /* restore the refcount of the objects to be deleted. */ list_for_each(el, &rt->tmp_obj_list) { p = list_entry(el, JSGCObjectHeader, link); @@ -5751,12 +6140,13 @@ static void gc_free_cycles(JSRuntime *rt) if (el == &rt->tmp_obj_list) break; p = list_entry(el, JSGCObjectHeader, link); - /* Only need to free the GC object associated with JS - values. The rest will be automatically removed because they - must be referenced by them. */ + /* Only need to free the GC object associated with JS values + or async functions. The rest will be automatically removed + because they must be referenced by them. */ switch(p->gc_obj_type) { case JS_GC_OBJ_TYPE_JS_OBJECT: case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: #ifdef DUMP_GC_FREE if (!header_done) { printf("Freeing cycles:\n"); @@ -5774,19 +6164,33 @@ static void gc_free_cycles(JSRuntime *rt) } } rt->gc_phase = JS_GC_PHASE_NONE; - + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { p = list_entry(el, JSGCObjectHeader, link); assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || - p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - js_free_rt(rt, p); + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || + p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT && + ((JSObject *)p)->weakref_count != 0) { + /* keep the object because there are weak references to it */ + p->mark = 0; + } else { + js_free_rt(rt, p); + } } init_list_head(&rt->gc_zero_ref_count_list); } -void JS_RunGC(JSRuntime *rt) +static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects) { + if (remove_weak_objects) { + /* free the weakly referenced object or symbol structures, delete + the associated Map/Set entries and queue the finalization + registry callbacks. */ + gc_remove_weak_objects(rt); + } + /* decrement the reference of the children of each object. mark = 1 after this pass. */ gc_decref(rt); @@ -5798,6 +6202,11 @@ void JS_RunGC(JSRuntime *rt) gc_free_cycles(rt); } +void JS_RunGC(JSRuntime *rt) +{ + JS_RunGCInternal(rt, TRUE); +} + /* Return false if not an object or if the object has already been freed (zombie objects are visible in finalizers when freeing cycles). */ @@ -5881,11 +6290,7 @@ static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); break; case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: -#endif - /* should track JSBigFloat usage */ + /* should track JSBigInt usage */ break; } } @@ -6011,10 +6416,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) case JS_CLASS_SYMBOL: /* u.object_data */ case JS_CLASS_DATE: /* u.object_data */ case JS_CLASS_BIG_INT: /* u.object_data */ -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_FLOAT: /* u.object_data */ - case JS_CLASS_BIG_DECIMAL: /* u.object_data */ -#endif compute_value_size(p->u.object_data, hp); break; case JS_CLASS_C_FUNCTION: /* u.cfunc */ @@ -6105,12 +6506,10 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ case JS_CLASS_DATAVIEW: /* u.typed_array */ -#ifdef CONFIG_BIGNUM - case JS_CLASS_FLOAT_ENV: /* u.float_env */ -#endif case JS_CLASS_MAP: /* u.map_state */ case JS_CLASS_SET: /* u.map_state */ case JS_CLASS_WEAKMAP: /* u.map_state */ @@ -6180,12 +6579,8 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) { - fprintf(fp, "QuickJS memory usage -- " -#ifdef CONFIG_BIGNUM - "BigNum " -#endif - CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", - (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit); + fprintf(fp, "QuickJS memory usage -- %d-bit, malloc limit: %"PRId64"\n\n", + (int)sizeof(void *) * 8, s->malloc_limit); #if 1 if (rt) { static const struct { @@ -6231,10 +6626,10 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) if (obj_classes[0]) fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { - if (obj_classes[class_id]) { + if (obj_classes[class_id] && class_id < rt->class_count) { char buf[ATOM_GET_STR_BUF_SIZE]; fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, - JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); + JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); } } if (obj_classes[JS_CLASS_INIT_COUNT]) @@ -6318,6 +6713,7 @@ JSValue JS_Throw(JSContext *ctx, JSValue obj) JSRuntime *rt = ctx->rt; JS_FreeValue(ctx, rt->current_exception); rt->current_exception = obj; + rt->current_exception_is_uncatchable = FALSE; return JS_EXCEPTION; } @@ -6327,10 +6723,15 @@ JSValue JS_GetException(JSContext *ctx) JSValue val; JSRuntime *rt = ctx->rt; val = rt->current_exception; - rt->current_exception = JS_NULL; + rt->current_exception = JS_UNINITIALIZED; return val; } +JS_BOOL JS_HasException(JSContext *ctx) +{ + return !JS_IsUninitialized(ctx->rt->current_exception); +} + static void dbuf_put_leb128(DynBuf *s, uint32_t v) { uint32_t a; @@ -6386,49 +6787,72 @@ static int get_sleb128(int32_t *pval, const uint8_t *buf, return ret; } +/* use pc_value = -1 to get the position of the function definition */ static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, - uint32_t pc_value) + uint32_t pc_value, int *pcol_num) { const uint8_t *p_end, *p; - int new_line_num, line_num, pc, v, ret; + int new_line_num, line_num, pc, v, ret, new_col_num, col_num; + uint32_t val; unsigned int op; - if (!b->has_debug || !b->debug.pc2line_buf) { - /* function was stripped */ - return -1; - } + if (!b->has_debug || !b->debug.pc2line_buf) + goto fail; /* function was stripped */ p = b->debug.pc2line_buf; p_end = p + b->debug.pc2line_len; - pc = 0; - line_num = b->debug.line_num; - while (p < p_end) { - op = *p++; - if (op == 0) { - uint32_t val; - ret = get_leb128(&val, p, p_end); + + /* get the function line and column numbers */ + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + p += ret; + line_num = val + 1; + + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + p += ret; + col_num = val + 1; + + if (pc_value != -1) { + pc = 0; + while (p < p_end) { + op = *p++; + if (op == 0) { + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_line_num = line_num + v; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + ret = get_sleb128(&v, p, p_end); if (ret < 0) goto fail; - pc += val; p += ret; - ret = get_sleb128(&v, p, p_end); - if (ret < 0) { - fail: - /* should never happen */ - return b->debug.line_num; - } - p += ret; - new_line_num = line_num + v; - } else { - op -= PC2LINE_OP_FIRST; - pc += (op / PC2LINE_RANGE); - new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + new_col_num = col_num + v; + + if (pc_value < pc) + goto done; + line_num = new_line_num; + col_num = new_col_num; } - if (pc_value < pc) - return line_num; - line_num = new_line_num; } + done: + *pcol_num = col_num; return line_num; + fail: + *pcol_num = 0; + return 0; } /* in order to avoid executing arbitrary code during the stack trace @@ -6439,7 +6863,7 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func) JSProperty *pr; JSShapeProperty *prs; JSValueConst val; - + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) return NULL; prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); @@ -6454,13 +6878,11 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func) } #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) -/* only taken into account if filename is provided */ -#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) /* if filename != NULL, an additional level is added with the filename and line number information (used for parse error). */ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, - const char *filename, int line_num, + const char *filename, int line_num, int col_num, int backtrace_flags) { JSStackFrame *sf; @@ -6469,23 +6891,32 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, const char *func_name_str; const char *str1; JSObject *p; - BOOL backtrace_barrier; + + if (!JS_IsObject(error_obj)) + return; /* protection in the out of memory case */ js_dbuf_init(ctx, &dbuf); if (filename) { dbuf_printf(&dbuf, " at %s", filename); if (line_num != -1) - dbuf_printf(&dbuf, ":%d", line_num); + dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); dbuf_putc(&dbuf, '\n'); str = JS_NewString(ctx, filename); - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL) - goto done; + if (JS_IsException(str)) + return; + /* Note: SpiderMonkey does that, could update once there is a standard */ + if (JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_columnNumber, JS_NewInt32(ctx, col_num), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) { + return; + } } for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { + if (sf->js_mode & JS_MODE_BACKTRACE_BARRIER) + break; if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; continue; @@ -6499,34 +6930,28 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, JS_FreeCString(ctx, func_name_str); p = JS_VALUE_GET_OBJ(sf->cur_func); - backtrace_barrier = FALSE; if (js_class_has_bytecode(p->class_id)) { JSFunctionBytecode *b; const char *atom_str; - int line_num1; + int line_num1, col_num1; b = p->u.func.function_bytecode; - backtrace_barrier = b->backtrace_barrier; if (b->has_debug) { line_num1 = find_line_num(ctx, b, - sf->cur_pc - b->byte_code_buf - 1); + sf->cur_pc - b->byte_code_buf - 1, &col_num1); atom_str = JS_AtomToCString(ctx, b->debug.filename); dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : ""); JS_FreeCString(ctx, atom_str); - if (line_num1 != -1) - dbuf_printf(&dbuf, ":%d", line_num1); + if (line_num1 != 0) + dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); dbuf_putc(&dbuf, ')'); } } else { dbuf_printf(&dbuf, " (native)"); } dbuf_putc(&dbuf, '\n'); - /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ - if (backtrace_barrier) - break; } - done: dbuf_putc(&dbuf, '\0'); if (dbuf_error(&dbuf)) str = JS_NULL; @@ -6572,9 +6997,9 @@ static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, JS_NewString(ctx, buf), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - } - if (add_backtrace) { - build_backtrace(ctx, obj, NULL, 0, 0); + if (add_backtrace) { + build_backtrace(ctx, obj, NULL, 0, 0, 0); + } } ret = JS_Throw(ctx, obj); return ret; @@ -6760,15 +7185,19 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); } +static void JS_ThrowInterrupted(JSContext *ctx) +{ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableException(ctx, TRUE); +} + static no_inline __exception int __js_poll_interrupts(JSContext *ctx) { JSRuntime *rt = ctx->rt; ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; if (rt->interrupt_handler) { if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { - /* XXX: should set a specific flag to avoid catching */ - JS_ThrowInternalError(ctx, "interrupted"); - JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); + JS_ThrowInterrupted(ctx); return -1; } } @@ -6784,7 +7213,17 @@ static inline __exception int js_poll_interrupts(JSContext *ctx) } } -/* return -1 (exception) or TRUE/FALSE */ +static void JS_SetImmutablePrototype(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->has_immutable_prototype = TRUE; +} + +/* Return -1 (exception) or TRUE/FALSE. 'throw_flag' = FALSE indicates + that it is called from Reflect.setPrototypeOf(). */ static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, JSValueConst proto_val, BOOL throw_flag) @@ -6815,12 +7254,32 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) return TRUE; - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); + if (unlikely(p->is_exotic)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + int ret; + if (em && em->set_prototype) { + ret = em->set_prototype(ctx, obj, proto_val); + if (ret == 0 && throw_flag) { + JS_ThrowTypeError(ctx, "proxy: bad prototype"); + return -1; + } else { + return ret; + } + } + } + sh = p->shape; if (sh->proto == proto) return TRUE; - if (!p->extensible) { + if (unlikely(p->has_immutable_prototype)) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "prototype is immutable"); + return -1; + } else { + return FALSE; + } + } + if (unlikely(!p->extensible)) { if (throw_flag) { JS_ThrowTypeError(ctx, "object is not extensible"); return -1; @@ -6865,17 +7324,10 @@ int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) { switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: val = ctx->class_proto[JS_CLASS_BIG_INT]; break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - val = ctx->class_proto[JS_CLASS_BIG_FLOAT]; - break; - case JS_TAG_BIG_DECIMAL: - val = ctx->class_proto[JS_CLASS_BIG_DECIMAL]; - break; -#endif case JS_TAG_INT: case JS_TAG_FLOAT64: val = ctx->class_proto[JS_CLASS_NUMBER]; @@ -6884,6 +7336,7 @@ static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) val = ctx->class_proto[JS_CLASS_BOOLEAN]; break; case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: val = ctx->class_proto[JS_CLASS_STRING]; break; case JS_TAG_SYMBOL: @@ -6899,22 +7352,24 @@ static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) return val; } -/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +/* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic object. */ JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) { JSValue val; if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { JSObject *p; p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) { - val = js_proxy_getPrototypeOf(ctx, obj); - } else { - p = p->shape->proto; - if (!p) - val = JS_NULL; - else - val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (unlikely(p->is_exotic)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_prototype) { + return em->get_prototype(ctx, obj); + } } + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); } else { val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj)); } @@ -6961,8 +7416,8 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, for(;;) { proto1 = p->shape->proto; if (!proto1) { - /* slow case if proxy in the prototype chain */ - if (unlikely(p->class_id == JS_CLASS_PROXY)) { + /* slow case if exotic object in the prototype chain */ + if (unlikely(p->is_exotic && !p->fast_array)) { JSValue obj1; obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); for(;;) { @@ -7044,12 +7499,14 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSValue val; JSContext *realm; JSAutoInitFunc *func; - + JSAutoInitIDEnum id; + if (js_shape_prepare_update(ctx, p, &prs)) return -1; realm = js_autoinit_get_realm(pr); - func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + id = js_autoinit_get_id(pr); + func = js_autoinit_func_table[id]; /* 'func' shall not modify the object properties 'pr' */ val = func(realm, p, prop, pr->u.init.opaque); js_autoinit_free(ctx->rt, pr); @@ -7057,7 +7514,15 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, pr->u.value = JS_UNDEFINED; if (JS_IsException(val)) return -1; - pr->u.value = val; + if (id == JS_AUTOINIT_ID_MODULE_NS && + JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + /* WARNING: a varref is returned as a string ! */ + prs->flags |= JS_PROP_VARREF; + pr->u.var_ref = JS_VALUE_GET_PTR(val); + pr->u.var_ref->header.ref_count++; + } else { + pr->u.value = val; + } return 0; } @@ -7083,14 +7548,24 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, { JSString *p1 = JS_VALUE_GET_STRING(obj); if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx, ch; + uint32_t idx; idx = __JS_AtomToUInt32(prop); if (idx < p1->len) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; - else - ch = p1->u.str8[idx]; - return js_new_string_char(ctx, ch); + return js_new_string_char(ctx, string_get(p1, idx)); + } + } else if (prop == JS_ATOM_length) { + return JS_NewInt32(ctx, p1->len); + } + } + break; + case JS_TAG_STRING_ROPE: + { + JSStringRope *p1 = JS_VALUE_GET_STRING_ROPE(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + return js_new_string_char(ctx, string_rope_get(obj, idx)); } } else if (prop == JS_ATOM_length) { return JS_NewInt32(ctx, p1->len); @@ -7232,7 +7707,7 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, JS_ThrowTypeErrorNotASymbol(ctx); goto fail; } - prop = js_symbol_to_atom(ctx, (JSValue)name); + prop = js_symbol_to_atom(ctx, name); p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (prs) { @@ -7263,7 +7738,7 @@ static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, /* safety check */ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) return JS_ThrowTypeErrorNotASymbol(ctx); - prop = js_symbol_to_atom(ctx, (JSValue)name); + prop = js_symbol_to_atom(ctx, name); p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (!prs) { @@ -7290,7 +7765,7 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, JS_ThrowTypeErrorNotASymbol(ctx); goto fail; } - prop = js_symbol_to_atom(ctx, (JSValue)name); + prop = js_symbol_to_atom(ctx, name); p = JS_VALUE_GET_OBJ(obj); prs = find_own_property(&pr, p, prop); if (!prs) { @@ -7303,6 +7778,8 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, return 0; } +/* add a private brand field to 'home_obj' if not already present and + if obj is != null add a private brand to it */ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) { JSObject *p, *p1; @@ -7310,7 +7787,7 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) JSProperty *pr; JSValue brand; JSAtom brand_atom; - + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { JS_ThrowTypeErrorNotAnObject(ctx); return -1; @@ -7318,10 +7795,10 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) p = JS_VALUE_GET_OBJ(home_obj); prs = find_own_property(&pr, p, JS_ATOM_Private_brand); if (!prs) { + /* if the brand is not present, add it */ brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); if (JS_IsException(brand)) return -1; - /* if the brand is not present, add it */ pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); if (!pr) { JS_FreeValue(ctx, brand); @@ -7332,34 +7809,38 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) brand = JS_DupValue(ctx, pr->u.value); } brand_atom = js_symbol_to_atom(ctx, brand); - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); + + if (JS_IsObject(obj)) { + p1 = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p1, brand_atom); + if (unlikely(prs)) { + JS_FreeAtom(ctx, brand_atom); + JS_ThrowTypeError(ctx, "private method is already present"); + return -1; + } + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + } else { JS_FreeAtom(ctx, brand_atom); - return -1; } - p1 = JS_VALUE_GET_OBJ(obj); - pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); - JS_FreeAtom(ctx, brand_atom); - if (!pr) - return -1; - pr->u.value = JS_UNDEFINED; return 0; } +/* return a boolean telling if the brand of the home object of 'func' + is present on 'obj' or -1 in case of exception */ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) { JSObject *p, *p1, *home_obj; JSShapeProperty *prs; JSProperty *pr; JSValueConst brand; - + /* get the home object of 'func' */ - if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { - not_obj: - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) + goto not_obj; p1 = JS_VALUE_GET_OBJ(func); if (!js_class_has_bytecode(p1->class_id)) goto not_obj; @@ -7375,30 +7856,28 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) /* safety check */ if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) goto not_obj; - + /* get the brand array of 'obj' */ - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - goto not_obj; - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); - if (!prs) { - JS_ThrowTypeError(ctx, "invalid brand on object"); + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); return -1; } - return 0; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); + return (prs != NULL); } static uint32_t js_string_obj_get_length(JSContext *ctx, JSValueConst obj) { JSObject *p; - JSString *p1; uint32_t len = 0; /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ p = JS_VALUE_GET_OBJ(obj); if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { - p1 = JS_VALUE_GET_STRING(p->u.object_data); + JSString *p1 = JS_VALUE_GET_STRING(p->u.object_data); len = p1->len; } return len; @@ -7423,7 +7902,7 @@ static int num_keys_cmp(const void *p1, const void *p2, void *opaque) return 1; } -static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) { uint32_t i; if (tab) { @@ -7450,7 +7929,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, BOOL is_enumerable, num_sorted; uint32_t num_key; JSAtomKindEnum kind; - + /* clear pointer for consistency in case of failure */ *ptab = NULL; *plen = 0; @@ -7517,7 +7996,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, /* set the "is_enumerable" field if necessary */ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); if (res < 0) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); + JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); return -1; } if (res) { @@ -7538,11 +8017,25 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, /* fill them */ - atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; + atom_count = num_keys_count + str_keys_count; + if (atom_count < str_keys_count) + goto add_overflow; + atom_count += sym_keys_count; + if (atom_count < sym_keys_count) + goto add_overflow; + atom_count += exotic_keys_count; + if (atom_count < exotic_keys_count || atom_count > INT32_MAX) { + add_overflow: + JS_ThrowOutOfMemory(ctx); + JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); + return -1; + } + /* XXX: need generic way to test for js_malloc(ctx, a * b) overflow */ + /* avoid allocating 0 bytes */ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); if (!tab_atom) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); + JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); return -1; } @@ -7587,7 +8080,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, for(i = 0; i < len; i++) { tab_atom[num_index].atom = __JS_AtomFromUInt32(i); if (tab_atom[num_index].atom == JS_ATOM_NULL) { - js_free_prop_enum(ctx, tab_atom, num_index); + JS_FreePropertyEnum(ctx, tab_atom, num_index); return -1; } tab_atom[num_index].is_enumerable = TRUE; @@ -7728,7 +8221,7 @@ int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); } -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +/* return -1 if exception (exotic object only) or TRUE/FALSE */ int JS_IsExtensible(JSContext *ctx, JSValueConst obj) { JSObject *p; @@ -7736,13 +8229,16 @@ int JS_IsExtensible(JSContext *ctx, JSValueConst obj) if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_isExtensible(ctx, obj); - else - return p->extensible; + if (unlikely(p->is_exotic)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->is_extensible) { + return em->is_extensible(ctx, obj); + } + } + return p->extensible; } -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +/* return -1 if exception (exotic object only) or TRUE/FALSE */ int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) { JSObject *p; @@ -7750,8 +8246,12 @@ int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_preventExtensions(ctx, obj); + if (unlikely(p->is_exotic)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->prevent_extensions) { + return em->prevent_extensions(ctx, obj); + } + } p->extensible = FALSE; return TRUE; } @@ -7842,45 +8342,59 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { JSObject *p; - uint32_t idx, len; + uint32_t idx; /* fast path for array access */ p = JS_VALUE_GET_OBJ(this_obj); - if (unlikely(!p->fast_array)) - goto slow_path; idx = JS_VALUE_GET_INT(prop); - len = (uint32_t)p->u.array.count; - if (unlikely(idx >= len)) - goto slow_path; switch(p->class_id) { case JS_CLASS_ARRAY: case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_DupValue(ctx, p->u.array.u.values[idx]); case JS_CLASS_INT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); case JS_CLASS_UINT8C_ARRAY: case JS_CLASS_UINT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); case JS_CLASS_INT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); case JS_CLASS_UINT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); case JS_CLASS_INT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); case JS_CLASS_UINT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); case JS_CLASS_BIG_INT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); case JS_CLASS_BIG_UINT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); + case JS_CLASS_FLOAT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; + return __JS_NewFloat64(ctx, fromfp16(p->u.array.u.fp16_ptr[idx])); case JS_CLASS_FLOAT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); case JS_CLASS_FLOAT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); default: goto slow_path; } } else { slow_path: + /* ToObject() must be done before ToPropertyKey() */ + if (JS_IsNull(this_obj) || JS_IsUndefined(this_obj)) { + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "cannot read property of %s", JS_IsNull(this_obj) ? "null" : "undefined"); + } atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); if (unlikely(atom == JS_ATOM_NULL)) @@ -7957,6 +8471,8 @@ JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, JSAtom atom; JSValue ret; atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; ret = JS_GetProperty(ctx, this_obj, atom); JS_FreeAtom(ctx, atom); return ret; @@ -8292,6 +8808,30 @@ static int add_fast_array_element(JSContext *ctx, JSObject *p, return TRUE; } +/* Allocate a new fast array. Its 'length' property is set to zero. It + maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit + integer. WARNING: the content of the array is not initialized. */ +static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len) +{ + JSValue arr; + JSObject *p; + + if (len > INT32_MAX) + return JS_ThrowRangeError(ctx, "invalid array length"); + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return arr; + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, len) < 0) { + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; + } + p->u.array.count = len; + } + return arr; +} + static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) { JS_FreeValue(ctx, desc->getter); @@ -8381,7 +8921,7 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, goto read_only_prop; } } - + for(;;) { if (p1->is_exotic) { if (p1->fast_array) { @@ -8405,17 +8945,21 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, return -1; } typed_array_oob: - /* must convert the argument even if out of bound access */ - if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || - p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { - int64_t v; - if (JS_ToBigInt64Free(ctx, &v, val)) - return -1; + if (p == p1) { + /* must convert the argument even if out of bound access */ + if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || + p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + } else { + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + } } else { - val = JS_ToNumberFree(ctx, val); JS_FreeValue(ctx, val); - if (JS_IsException(val)) - return -1; } return TRUE; } @@ -8490,6 +9034,8 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { goto read_only_prop; + } else { + break; } } } @@ -8669,6 +9215,13 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, p->u.array.u.uint64_ptr[idx] = v; } break; + case JS_CLASS_FLOAT16_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.fp16_ptr[idx] = tofp16(d); + break; case JS_CLASS_FLOAT32_ARRAY: if (JS_ToFloat64Free(ctx, &d, val)) return -1; @@ -8739,6 +9292,10 @@ int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, JSAtom atom; int ret; atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW); JS_FreeAtom(ctx, atom); return ret; @@ -8880,15 +9437,13 @@ static BOOL check_define_prop_flags(int prop_flags, int flags) if ((flags & JS_PROP_HAS_ENUMERABLE) && (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) return FALSE; - } - if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | - JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); if (has_accessor != is_getset) return FALSE; - if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + if (!is_getset && !(prop_flags & JS_PROP_WRITABLE)) { /* not writable: cannot set the writable bit */ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) @@ -8974,7 +9529,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, return -1; } /* this code relies on the fact that Uint32 are never allocated */ - val = (JSValueConst)JS_NewUint32(ctx, array_length); + val = JS_NewUint32(ctx, array_length); /* prs may have been modified */ prs = find_own_property(&pr, p, prop); assert(prs != NULL); @@ -9079,15 +9634,19 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, spaces. */ if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) goto not_configurable; + } else { + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, + JS_DupValue(ctx, val)); } - /* update the reference */ - set_value(ctx, pr->u.var_ref->pvalue, - JS_DupValue(ctx, val)); } /* if writable is set to false, no longer a reference (for mapped arguments) */ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { JSValue val1; + if (p->class_id == JS_CLASS_MODULE_NS) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false"); + } if (js_shape_prepare_update(ctx, p, &prs)) return -1; val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); @@ -9293,6 +9852,10 @@ int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, JSAtom atom; int ret; atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); JS_FreeAtom(ctx, atom); return ret; @@ -9328,7 +9891,7 @@ static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj) JSShapeProperty *prs; JSValueConst val; JSString *p; - + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); if (!prs) return FALSE; @@ -9587,12 +10150,38 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, set_value(ctx, &pr->u.value, val); return 0; } + /* XXX: add a fast path where the property exists and the object + is not exotic. Otherwise do as in OP_put_ref_value and remove + JS_PROP_NO_ADD which is no longer necessary */ flags = JS_PROP_THROW_STRICT; - if (is_strict_mode(ctx)) + if (is_strict_mode(ctx)) flags |= JS_PROP_NO_ADD; return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags); } +/* return -1, FALSE or TRUE */ +static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int ret; + + /* 9.1.1.4.7 DeleteBinding ( N ) */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) + return FALSE; /* lexical variables cannot be deleted */ + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + return JS_DeleteProperty(ctx, ctx->global_obj, prop, 0); + } else { + return TRUE; + } +} + /* return -1, FALSE or TRUE. return FALSE if not configurable or invalid object. return -1 in case of exception. flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ @@ -9601,7 +10190,7 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) JSValue obj1; JSObject *p; int res; - + obj1 = JS_ToObject(ctx, obj); if (JS_IsException(obj1)) return -1; @@ -9691,29 +10280,10 @@ BOOL JS_IsError(JSContext *ctx, JSValueConst val) return (p->class_id == JS_CLASS_ERROR); } -/* used to avoid catching interrupt exceptions */ -BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; -} - -void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_ERROR) - p->is_uncatchable_error = flag; -} - -void JS_ResetUncatchableError(JSContext *ctx) +/* must be called after JS_Throw() */ +void JS_SetUncatchableException(JSContext *ctx, BOOL flag) { - JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); + ctx->rt->current_exception_is_uncatchable = flag; } void JS_SetOpaque(JSValue obj, void *opaque) @@ -9746,11 +10316,17 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) return p; } -#define HINT_STRING 0 -#define HINT_NUMBER 1 -#define HINT_NONE 2 -/* don't try Symbol.toPrimitive */ -#define HINT_FORCE_ORDINARY (1 << 4) +void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + *class_id = 0; + return NULL; + } + p = JS_VALUE_GET_OBJ(obj); + *class_id = p->class_id; + return p->u.opaque; +} static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) { @@ -9848,7 +10424,7 @@ static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) p = JS_VALUE_GET_OBJ(obj); return p->is_HTMLDDA; } - + static int JS_ToBoolFree(JSContext *ctx, JSValue val) { uint32_t tag = JS_VALUE_GET_TAG(val); @@ -9867,27 +10443,33 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); return ret; } - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif + case JS_TAG_STRING_ROPE: { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - BOOL ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + BOOL ret = JS_VALUE_GET_STRING_ROPE(val)->len != 0; JS_FreeValue(ctx, val); return ret; } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: + case JS_TAG_SHORT_BIG_INT: + return JS_VALUE_GET_SHORT_BIG_INT(val) != 0; + case JS_TAG_BIG_INT: { - JSBigDecimal *p = JS_VALUE_GET_PTR(val); + JSBigInt *p = JS_VALUE_GET_PTR(val); BOOL ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + int i; + + /* fail safe: we assume it is not necessarily + normalized. Beginning from the MSB ensures that the + test is fast. */ + ret = FALSE; + for(i = p->len - 1; i >= 0; i--) { + if (p->tab[i] != 0) { + ret = TRUE; + break; + } + } JS_FreeValue(ctx, val); return ret; } -#endif case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -9947,1604 +10529,2390 @@ static inline int to_digit(int c) return 36; } -/* XXX: remove */ -static double js_strtod(const char *p, int radix, BOOL is_float) +/* bigint support */ + +#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ + +/* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */ +#if JS_SHORT_BIG_INT_BITS == 32 +#define JS_SHORT_BIG_INT_MIN INT32_MIN +#define JS_SHORT_BIG_INT_MAX INT32_MAX +#elif JS_SHORT_BIG_INT_BITS == 64 +#define JS_SHORT_BIG_INT_MIN INT64_MIN +#define JS_SHORT_BIG_INT_MAX INT64_MAX +#else +#error unsupported +#endif + +#define ADDC(res, carry_out, op1, op2, carry_in) \ +do { \ + js_limb_t __v, __a, __k, __k1; \ + __v = (op1); \ + __a = __v + (op2); \ + __k1 = __a < __v; \ + __k = (carry_in); \ + __a = __a + __k; \ + carry_out = (__a < __k) | __k1; \ + res = __a; \ +} while (0) + +#if JS_LIMB_BITS == 32 +/* a != 0 */ +static inline js_limb_t js_limb_clz(js_limb_t a) { - double d; - int c; - - if (!is_float || radix != 10) { - uint64_t n_max, n; - int int_exp, is_neg; - - is_neg = 0; - if (*p == '-') { - is_neg = 1; - p++; - } + return clz32(a); +} +#else +static inline js_limb_t js_limb_clz(js_limb_t a) +{ + return clz64(a); +} +#endif - /* skip leading zeros */ - while (*p == '0') - p++; - n = 0; - if (radix == 10) - n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ - else - n_max = ((uint64_t)-1 - (radix - 1)) / radix; - /* XXX: could be more precise */ - int_exp = 0; - while (*p != '\0') { - c = to_digit((uint8_t)*p); - if (c >= radix) - break; - if (n <= n_max) { - n = n * radix + c; - } else { - int_exp++; - } - p++; - } - d = n; - if (int_exp != 0) { - d *= pow(radix, int_exp); - } - if (is_neg) - d = -d; - } else { - d = strtod(p, NULL); +/* handle a = 0 too */ +static inline js_limb_t js_limb_safe_clz(js_limb_t a) +{ + if (a == 0) + return JS_LIMB_BITS; + else + return js_limb_clz(a); +} + +static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + js_limb_t n, js_limb_t carry) +{ + int i; + for(i = 0;i < n; i++) { + ADDC(res[i], carry, op1[i], op2[i], carry); } - return d; + return carry; } -#define ATOD_INT_ONLY (1 << 0) -/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ -#define ATOD_ACCEPT_BIN_OCT (1 << 2) -/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ -#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) -/* accept _ between digits as a digit separator */ -#define ATOD_ACCEPT_UNDERSCORES (1 << 5) -/* allow a suffix to override the type */ -#define ATOD_ACCEPT_SUFFIX (1 << 6) -/* default type */ -#define ATOD_TYPE_MASK (3 << 7) -#define ATOD_TYPE_FLOAT64 (0 << 7) -#define ATOD_TYPE_BIG_INT (1 << 7) -#ifdef CONFIG_BIGNUM -#define ATOD_TYPE_BIG_FLOAT (2 << 7) -#define ATOD_TYPE_BIG_DECIMAL (3 << 7) -/* assume bigint mode: floats are parsed as integers if no decimal - point nor exponent */ -#define ATOD_MODE_BIGINT (1 << 9) -#endif -/* accept -0x1 */ -#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) +static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + int n, js_limb_t carry) +{ + int i; + js_limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} -static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) +/* compute 0 - op2. carry = 0 or 1. */ +static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n) { - bf_t a_s, *a = &a_s; - int ret; - JSValue val; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); + int i; + js_limb_t v, carry; + + carry = 1; + for(i=0;ifp_env.prec, - ctx->fp_env.flags); + js_limb_t i; + js_dlimb_t t; + + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l; + tabr[i] = t; + l = t >> JS_LIMB_BITS; } - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); + return l; +} + +static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; } - return val; + return r; } -static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) +/* tabr[] += taba[] * b, return the high word. */ +static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) { - bfdec_t *a; - int ret; - JSValue val; - - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigDecimal(val); - ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, - BF_RNDZ | BF_ATOF_NO_NAN_INF); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> JS_LIMB_BITS; } - return val; + return l; } -#endif -/* return an exception in case of memory error. Return JS_NAN if - invalid syntax */ -#ifdef CONFIG_BIGNUM -static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, - int radix, int flags, slimb_t *pexponent) -#else -static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - int radix, int flags) -#endif +/* size of the result : op1_size + op2_size. */ +static void mp_mul_basecase(js_limb_t *result, + const js_limb_t *op1, js_limb_t op1_size, + const js_limb_t *op2, js_limb_t op2_size) { - const char *p, *p_start; - int sep, is_neg; - BOOL is_float, has_legacy_octal; - int atod_type = flags & ATOD_TYPE_MASK; - char buf1[64], *buf; - int i, j, len; - BOOL buf_allocated = FALSE; - JSValue val; - - /* optional separator between digits */ - sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - has_legacy_octal = FALSE; + int i; + js_limb_t r; - p = str; - p_start = p; - is_neg = 0; - if (p[0] == '+') { - p++; - p_start++; - if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) - goto no_radix_prefix; - } else if (p[0] == '-') { - p++; - p_start++; - is_neg = 1; - if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) - goto no_radix_prefix; + result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i= '0' && p[1] <= '9') && - radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { - int i; - has_legacy_octal = TRUE; - sep = 256; - for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) - continue; - if (p[i] == '8' || p[i] == '9') - goto no_prefix; - p += 1; - radix = 8; - } else { - goto no_prefix; +} + +/* tabr[] -= taba[] * b. Return the value to substract to the high + word. */ +static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) +{ + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l; + tabr[i] = t; + l = -(t >> JS_LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */ +static inline js_limb_t udiv1norm_init(js_limb_t d) +{ + js_limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0, + js_limb_t d, js_limb_t d_inv) +{ + js_limb_t n1m, n_adj, q, r, ah; + js_dlimb_t a; + n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> JS_LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0; + a = a - (js_dlimb_t)q * d - d; + ah = a >> JS_LIMB_BITS; + q += 1 + ah; + r = (js_limb_t)a + (ah & d); + *pr = r; + return q; +} + +#define UDIV1NORM_THRESHOLD 3 + +/* b must be >= 1 << (JS_LIMB_BITS - 1) */ +static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + js_limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); } - /* there must be a digit after the prefix */ - if (to_digit((uint8_t)*p) >= radix) - goto fail; - no_prefix: ; } else { - no_radix_prefix: - if (!(flags & ATOD_INT_ONLY) && - (atod_type == ATOD_TYPE_FLOAT64 -#ifdef CONFIG_BIGNUM - || atod_type == ATOD_TYPE_BIG_FLOAT -#endif - ) && - strstart(p, "Infinity", &p)) { -#ifdef CONFIG_BIGNUM - if (atod_type == ATOD_TYPE_BIG_FLOAT) { - bf_t *a; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - goto done; - a = JS_GetBigFloat(val); - bf_set_inf(a, is_neg); - } else -#endif - { - double d = 1.0 / 0.0; - if (is_neg) - d = -d; - val = JS_NewFloat64(ctx, d); - } - goto done; + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; } } - if (radix == 0) - radix = 10; - is_float = FALSE; - p_start = p; - while (to_digit((uint8_t)*p) < radix - || (*p == sep && (radix != 10 || - p != p_start + 1 || p[-1] != '0') && - to_digit((uint8_t)p[1]) < radix)) { - p++; - } - if (!(flags & ATOD_INT_ONLY)) { - if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { - is_float = TRUE; - p++; - if (*p == sep) - goto fail; - while (to_digit((uint8_t)*p) < radix || - (*p == sep && to_digit((uint8_t)p[1]) < radix)) - p++; - } - if (p > p_start && - (((*p == 'e' || *p == 'E') && radix == 10) || - ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { - const char *p1 = p + 1; - is_float = TRUE; - if (*p1 == '+') { - p1++; - } else if (*p1 == '-') { - p1++; - } - if (is_digit((uint8_t)*p1)) { - p = p1 + 1; - while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) - p++; - } - } + return r; +} + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na, + const js_limb_t *tabb, js_limb_t nb) +{ + js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + int i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = mp_div1norm(tabq, taba, na, b1, 0); + return; } - if (p == p_start) - goto fail; + n = na - nb; - buf = buf1; - buf_allocated = FALSE; - len = p - p_start; - if (unlikely((len + 2) > sizeof(buf1))) { - buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ - if (!buf) - goto mem_error; - buf_allocated = TRUE; + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } } - /* remove the separators and the radix prefixes */ - j = 0; - if (is_neg) - buf[j++] = '-'; - for (i = 0; i < len; i++) { - if (p_start[i] != '_') - buf[j++] = p_start[i]; + tabq[n] = q; + if (q) { + mp_sub(taba + n, taba + n, tabb, nb, 0); } - buf[j] = '\0'; - if (flags & ATOD_ACCEPT_SUFFIX) { - if (*p == 'n') { - p++; - atod_type = ATOD_TYPE_BIG_INT; - } else -#ifdef CONFIG_BIGNUM - if (*p == 'l') { - p++; - atod_type = ATOD_TYPE_BIG_FLOAT; - } else if (*p == 'm') { - p++; - atod_type = ATOD_TYPE_BIG_DECIMAL; - } else if (flags & ATOD_MODE_BIGINT) { - if (!is_float) - atod_type = ATOD_TYPE_BIG_INT; - if (has_legacy_octal) - goto fail; - } else -#endif - { - if (is_float && radix != 10) - goto fail; + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + js_dlimb_t al; + al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; } - } else { - if (atod_type == ATOD_TYPE_FLOAT64) { -#ifdef CONFIG_BIGNUM - if (flags & ATOD_MODE_BIGINT) { - if (!is_float) - atod_type = ATOD_TYPE_BIG_INT; - if (has_legacy_octal) - goto fail; - } else -#endif - { - if (is_float && radix != 10) - goto fail; + r = mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } } } + tabq[i] = q; } +} - switch(atod_type) { - case ATOD_TYPE_FLOAT64: - { - double d; - d = js_strtod(buf, radix, is_float); - /* return int or float64 */ - val = JS_NewFloat64(ctx, d); - } - break; - case ATOD_TYPE_BIG_INT: - if (has_legacy_octal || is_float) - goto fail; - val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL); - break; -#ifdef CONFIG_BIGNUM - case ATOD_TYPE_BIG_FLOAT: - if (has_legacy_octal) - goto fail; - val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags, - pexponent); - break; - case ATOD_TYPE_BIG_DECIMAL: - if (radix != 10) - goto fail; - val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL); - break; -#endif - default: - abort(); +/* 1 <= shift <= JS_LIMB_BITS - 1 */ +static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n, + int shift) +{ + int i; + js_limb_t l, v; + l = 0; + for(i = 0; i < n; i++) { + v = taba[i]; + tabr[i] = (v << shift) | l; + l = v >> (JS_LIMB_BITS - shift); } - -done: - if (buf_allocated) - js_free_rt(ctx->rt, buf); - if (pp) - *pp = p; - return val; - fail: - val = JS_NAN; - goto done; - mem_error: - val = JS_ThrowOutOfMemory(ctx); - goto done; + return l; } -#ifdef CONFIG_BIGNUM -static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - int radix, int flags) +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n, + int shift, js_limb_t high) { - return js_atof2(ctx, str, pp, radix, flags, NULL); + int i; + js_limb_t l, a; + + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift)); + l = a; + } + return l & (((js_limb_t)1 << shift) - 1); } -#endif -typedef enum JSToNumberHintEnum { - TON_FLAG_NUMBER, - TON_FLAG_NUMERIC, -} JSToNumberHintEnum; +static JSBigInt *js_bigint_new(JSContext *ctx, int len) +{ + JSBigInt *r; + if (len > JS_BIGINT_MAX_SIZE) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); + if (!r) + return NULL; + r->header.ref_count = 1; + r->len = len; + return r; +} -static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, - JSToNumberHintEnum flag) +static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) { - uint32_t tag; - JSValue ret; + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + r->len = 1; + r->tab[0] = a; + return r; +} - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_BIG_INT: - if (flag != TON_FLAG_NUMERIC) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); - } - ret = val; - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - if (flag != TON_FLAG_NUMERIC) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number"); - } - ret = val; - break; - case JS_TAG_BIG_FLOAT: - if (flag != TON_FLAG_NUMERIC) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); - } - ret = val; - break; -#endif - case JS_TAG_FLOAT64: - case JS_TAG_INT: - case JS_TAG_EXCEPTION: - ret = val; - break; - case JS_TAG_BOOL: - case JS_TAG_NULL: - ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_UNDEFINED: - ret = JS_NAN; - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - return JS_EXCEPTION; - goto redo; - case JS_TAG_STRING: - { - const char *str; - const char *p; - size_t len; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - ret = JS_NewInt32(ctx, 0); - } else { - int flags = ATOD_ACCEPT_BIN_OCT; - ret = js_atof(ctx, p, &p, 0, flags); - if (!JS_IsException(ret)) { - p += skip_spaces(p); - if ((p - str) != len) { - JS_FreeValue(ctx, ret); - ret = JS_NAN; - } - } - } - JS_FreeCString(ctx, str); - } - break; - case JS_TAG_SYMBOL: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); - default: - JS_FreeValue(ctx, val); - ret = JS_NAN; - break; +static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) +{ +#if JS_LIMB_BITS == 64 + return js_bigint_set_si(buf, a); +#else + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + if (a >= INT32_MIN && a <= INT32_MAX) { + r->len = 1; + r->tab[0] = a; + } else { + r->len = 2; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; } - return ret; + return r; +#endif } -static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) +/* val must be a short big int */ +static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val) { - return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); + return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val)); } -static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) +static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str, + const js_limb_t *tab, int len) { - return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); + int i; + printf("%s: ", str); + for(i = len - 1; i >= 0; i--) { +#if JS_LIMB_BITS == 32 + printf(" %08x", tab[i]); +#else + printf(" %016" PRIx64, tab[i]); +#endif + } + printf("\n"); } -static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) +static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, + const JSBigInt *p) { - return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); + js_bigint_dump1(ctx, str, p->tab, p->len); } -static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, - JSValue val) +static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a) { - double d; - uint32_t tag; + JSBigInt *r; + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + return r; +} - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = JS_FLOAT64_NAN; - return -1; +static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a) +{ +#if JS_LIMB_BITS == 64 + return js_bigint_new_si(ctx, a); +#else + if (a >= INT32_MIN && a <= INT32_MAX) { + return js_bigint_new_si(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + return r; } - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - d = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: - d = JS_VALUE_GET_FLOAT64(val); - break; - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: #endif - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - /* XXX: there can be a double rounding issue with some - primitives (such as JS_ToUint8ClampFree()), but it is - not critical to fix it. */ - bf_get_float64(&p->num, &d, BF_RNDN); - JS_FreeValue(ctx, val); - } - break; - default: - abort(); - } - *pres = d; - return 0; } -static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) { - uint32_t tag; + if (a <= INT64_MAX) { + return js_bigint_new_si64(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS); + if (!r) + return NULL; +#if JS_LIMB_BITS == 64 + r->tab[0] = a; + r->tab[1] = 0; +#else + r->tab[0] = a; + r->tab[1] = a >> 32; + r->tab[2] = 0; +#endif + return r; + } +} - tag = JS_VALUE_GET_TAG(val); - if (tag <= JS_TAG_NULL) { - *pres = JS_VALUE_GET_INT(val); - return 0; - } else if (JS_TAG_IS_FLOAT64(tag)) { - *pres = JS_VALUE_GET_FLOAT64(val); - return 0; +static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) +{ + JSBigInt *r; + if (a == (js_slimb_t)a) { + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; } else { - return __JS_ToFloat64Free(ctx, pres, val); + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; } + return r; } -int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) +/* Remove redundant high order limbs. Warning: 'a' may be + reallocated. Can never fail. +*/ +static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) { - return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); + js_limb_t v; + + assert(a->header.ref_count == 1); + while (l > 1) { + v = a->tab[l - 1]; + if ((v != 0 && v != -1) || + (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) { + break; + } + l--; + } + if (l != a->len) { + JSBigInt *a1; + /* realloc to reduce the size */ + a->len = l; + a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t)); + if (a1) + a = a1; + } + return a; } -static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) +static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) { - return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); + return js_bigint_normalize1(ctx, a, a->len); } -/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ -static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) +/* return 0 or 1 depending on the sign */ +static inline int js_bigint_sign(const JSBigInt *a) { - uint32_t tag; - JSValue ret; + return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); +} - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = JS_NewInt32(ctx, 0); - } else { - /* convert -0 to +0 */ - d = trunc(d) + 0.0; - ret = JS_NewFloat64(ctx, d); - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - bf_t a_s, *a, r_s, *r = &r_s; - BOOL is_nan; - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (!bf_is_finite(a)) { - is_nan = bf_is_nan(a); - if (is_nan) - ret = JS_NewInt32(ctx, 0); - else - ret = JS_DupValue(ctx, val); - } else { - ret = JS_NewBigInt(ctx); - if (!JS_IsException(ret)) { - r = JS_GetBigInt(ret); - bf_set(r, a); - bf_rint(r, BF_RNDZ); - ret = JS_CompactBigInt(ctx, ret); - } - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, val); - } - break; +static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) +{ + if (a->len == 1) { + return a->tab[0]; + } else { +#if JS_LIMB_BITS == 32 + if (js_bigint_sign(a)) + return INT32_MIN; + else + return INT32_MAX; +#else + if (js_bigint_sign(a)) + return INT64_MIN; + else + return INT64_MAX; #endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return val; - goto redo; } - return ret; } -/* Note: the integer value is satured to 32 bits */ -static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) +/* add the op1 limb */ +static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, + js_limb_t op1) { - uint32_t tag; - int ret; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_EXCEPTION: - *pres = 0; - return -1; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = 0; - } else { - if (d < INT32_MIN) - ret = INT32_MIN; - else if (d > INT32_MAX) - ret = INT32_MAX; - else - ret = (int)d; - } + int n2 = r->len; + if ((op1 != 0 && op1 != -1) || + (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) { + JSBigInt *r1; + r1 = js_realloc(ctx, r, + sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t)); + if (!r1) { + js_free(ctx, r); + return NULL; } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int32(&ret, &p->num, 0); - JS_FreeValue(ctx, val); + r = r1; + r->len = n2 + 1; + r->tab[n2] = op1; + } else { + /* otherwise still need to normalize the result */ + r = js_bigint_normalize(ctx, r); + } + return r; +} + +/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b + (b_neg = 1) */ +/* XXX: optimize */ +static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, int b_neg) +{ + JSBigInt *r; + int n1, n2, i; + js_limb_t carry, op1, op2, a_sign, b_sign; + + n2 = max_int(a->len, b->len); + n1 = min_int(a->len, b->len); + r = js_bigint_new(ctx, n2); + if (!r) + return NULL; + /* XXX: optimize */ + /* common part */ + carry = b_neg; + for(i = 0; i < n1; i++) { + op1 = a->tab[i]; + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, op1, op2, carry); + } + a_sign = -js_bigint_sign(a); + b_sign = (-js_bigint_sign(b)) ^ (-b_neg); + /* part with sign extension of one operand */ + if (a->len > b->len) { + for(i = n1; i < n2; i++) { + op1 = a->tab[i]; + ADDC(r->tab[i], carry, op1, b_sign, carry); } - break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; + } else if (a->len < b->len) { + for(i = n1; i < n2; i++) { + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, a_sign, op2, carry); } - goto redo; } - *pres = ret; - return 0; + + /* part with sign extension for both operands. Extend the result + if necessary */ + return js_bigint_extend(ctx, r, a_sign + b_sign + carry); } -int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) +/* XXX: optimize */ +static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) { - return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); + JSBigIntBuf buf; + JSBigInt *b; + b = js_bigint_set_si(&buf, 0); + return js_bigint_add(ctx, b, a, 1); } -int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, - int min, int max, int min_offset) +static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) { - int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); - if (res == 0) { - if (*pres < min) { - *pres += min_offset; - if (*pres < min) - *pres = min; + JSBigInt *r; + + r = js_bigint_new(ctx, a->len + b->len); + if (!r) + return NULL; + mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len); + /* correct the result if negative operands (no overflow is + possible) */ + if (js_bigint_sign(a)) + mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); + if (js_bigint_sign(b)) + mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0); + return js_bigint_normalize(ctx, r); +} + +/* return the division or the remainder. 'b' must be != 0. return NULL + in case of exception (division by zero or memory error) */ +static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, BOOL is_rem) +{ + JSBigInt *r, *q; + js_limb_t *tabb, h; + int na, nb, a_sign, b_sign, shift; + + if (b->len == 1 && b->tab[0] == 0) { + JS_ThrowRangeError(ctx, "BigInt division by zero"); + return NULL; + } + + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + na = a->len; + nb = b->len; + + r = js_bigint_new(ctx, na + 2); + if (!r) + return NULL; + if (a_sign) { + mp_neg(r->tab, a->tab, na); + } else { + memcpy(r->tab, a->tab, na * sizeof(a->tab[0])); + } + /* normalize */ + while (na > 1 && r->tab[na - 1] == 0) + na--; + + tabb = js_malloc(ctx, nb * sizeof(tabb[0])); + if (!tabb) { + js_free(ctx, r); + return NULL; + } + if (b_sign) { + mp_neg(tabb, b->tab, nb); + } else { + memcpy(tabb, b->tab, nb * sizeof(tabb[0])); + } + /* normalize */ + while (nb > 1 && tabb[nb - 1] == 0) + nb--; + + /* trivial case if 'a' is small */ + if (na < nb) { + js_free(ctx, r); + js_free(ctx, tabb); + if (is_rem) { + /* r = a */ + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + return r; } else { - if (*pres > max) - *pres = max; + /* q = 0 */ + return js_bigint_new_si(ctx, 0); } } - return res; -} -static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) -{ - uint32_t tag; + /* normalize 'b' */ + shift = js_limb_clz(tabb[nb - 1]); + if (shift != 0) { + mp_shl(tabb, tabb, nb, shift); + h = mp_shl(r->tab, r->tab, na, shift); + if (h != 0) + r->tab[na++] = h; + } - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - *pres = JS_VALUE_GET_INT(val); - return 0; - case JS_TAG_EXCEPTION: - *pres = 0; - return -1; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - *pres = 0; - } else { - if (d < INT64_MIN) - *pres = INT64_MIN; - else if (d > INT64_MAX) - *pres = INT64_MAX; - else - *pres = (int64_t)d; - } - } - return 0; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int64(pres, &p->num, 0); - JS_FreeValue(ctx, val); - } - return 0; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; + q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */ + if (!q) { + js_free(ctx, r); + js_free(ctx, tabb); + return NULL; } -} -int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); -} + // js_bigint_dump1(ctx, "a", r->tab, na); + // js_bigint_dump1(ctx, "b", tabb, nb); + mp_divnorm(q->tab, r->tab, na, tabb, nb); + js_free(ctx, tabb); -int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, - int64_t min, int64_t max, int64_t neg_offset) -{ - int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); - if (res == 0) { - if (*pres < 0) - *pres += neg_offset; - if (*pres < min) - *pres = min; - else if (*pres > max) - *pres = max; + if (is_rem) { + js_free(ctx, q); + if (shift != 0) + mp_shr(r->tab, r->tab, nb, shift, 0); + r->tab[nb++] = 0; + if (a_sign) + mp_neg(r->tab, r->tab, nb); + r = js_bigint_normalize1(ctx, r, nb); + return r; + } else { + js_free(ctx, r); + q->tab[na - nb + 1] = 0; + if (a_sign ^ b_sign) { + mp_neg(q->tab, q->tab, q->len); + } + q = js_bigint_normalize(ctx, q); + return q; } - return res; } -/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) - in case of exception */ -static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +/* and, or, xor */ +static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, OPCodeEnum op) { - uint32_t tag; - int64_t ret; + JSBigInt *r; + js_limb_t b_sign; + int a_len, b_len, i; - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); + if (a->len < b->len) { + const JSBigInt *tmp; + tmp = a; + a = b; + b = tmp; + } + /* a_len >= b_len */ + a_len = a->len; + b_len = b->len; + b_sign = -js_bigint_sign(b); + + r = js_bigint_new(ctx, a_len); + if (!r) + return NULL; + switch(op) { + case OP_or: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] | b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] | b_sign; + } break; - case JS_TAG_FLOAT64: - { - JSFloat64Union u; - double d; - int e; - d = JS_VALUE_GET_FLOAT64(val); - u.d = d; - /* we avoid doing fmod(x, 2^64) */ - e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 62))) { - /* fast case */ - ret = (int64_t)d; - } else if (e <= (1023 + 62 + 53)) { - uint64_t v; - /* remainder modulo 2^64 */ - v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); - ret = v << ((e - 1023) - 52); - /* take the sign into account */ - if (u.u64 >> 63) - ret = -ret; - } else { - ret = 0; /* also handles NaN and +inf */ - } + case OP_and: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] & b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] & b_sign; } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); - JS_FreeValue(ctx, val); + case OP_xor: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] ^ b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] ^ b_sign; } break; -#endif default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; + abort(); } - *pres = ret; - return 0; + return js_bigint_normalize(ctx, r); } -int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) { - return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); + JSBigInt *r; + int i; + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + for(i = 0; i < a->len; i++) { + r->tab[i] = ~a->tab[i]; + } + /* no normalization is needed */ + return r; } -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) +static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) { - if (JS_IsBigInt(ctx, val)) - return JS_ToBigInt64(ctx, pres, val); - else - return JS_ToInt64(ctx, pres, val); + int d, i, shift; + JSBigInt *r; + js_limb_t l; + + if (a->len == 1 && a->tab[0] == 0) + return js_bigint_new_si(ctx, 0); /* zero case */ + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + r = js_bigint_new(ctx, a->len + d); + if (!r) + return NULL; + for(i = 0; i < d; i++) + r->tab[i] = 0; + if (shift == 0) { + for(i = 0; i < a->len; i++) { + r->tab[i + d] = a->tab[i]; + } + } else { + l = mp_shl(r->tab + d, a->tab, a->len, shift); + if (js_bigint_sign(a)) + l |= (js_limb_t)(-1) << shift; + r = js_bigint_extend(ctx, r, l); + } + return r; } -/* return (<0, 0) in case of exception */ -static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) { - uint32_t tag; - int32_t ret; + int d, i, shift, a_sign, n1; + JSBigInt *r; - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: - { - JSFloat64Union u; - double d; - int e; - d = JS_VALUE_GET_FLOAT64(val); - u.d = d; - /* we avoid doing fmod(x, 2^32) */ - e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 30))) { - /* fast case */ - ret = (int32_t)d; - } else if (e <= (1023 + 30 + 53)) { - uint64_t v; - /* remainder modulo 2^32 */ - v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); - v = v << ((e - 1023) - 52 + 32); - ret = v >> 32; - /* take the sign into account */ - if (u.u64 >> 63) - ret = -ret; - } else { - ret = 0; /* also handles NaN and +inf */ - } + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + a_sign = js_bigint_sign(a); + if (d >= a->len) + return js_bigint_new_si(ctx, -a_sign); + n1 = a->len - d; + r = js_bigint_new(ctx, n1); + if (!r) + return NULL; + if (shift == 0) { + for(i = 0; i < n1; i++) { + r->tab[i] = a->tab[i + d]; } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); - JS_FreeValue(ctx, val); + /* no normalization is needed */ + } else { + mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); + r = js_bigint_normalize(ctx, r); + } + return r; +} + +static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) +{ + uint32_t e; + int n_bits, i; + JSBigInt *r, *r1; + + /* b must be >= 0 */ + if (js_bigint_sign(b)) { + JS_ThrowRangeError(ctx, "BigInt negative exponent"); + return NULL; + } + if (b->len == 1 && b->tab[0] == 0) { + /* a^0 = 1 */ + return js_bigint_new_si(ctx, 1); + } else if (a->len == 1) { + js_limb_t v; + BOOL is_neg; + + v = a->tab[0]; + if (v <= 1) + return js_bigint_new_si(ctx, v); + else if (v == -1) + return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1)); + is_neg = (js_slimb_t)v < 0; + if (is_neg) + v = -v; + if ((v & (v - 1)) == 0) { + uint64_t e1; + int n; + /* v = 2^n */ + n = JS_LIMB_BITS - 1 - js_limb_clz(v); + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + e1 = (uint64_t)e * n; + if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) + goto overflow; + e = e1; + if (is_neg) + is_neg = b->tab[0] & 1; + r = js_bigint_new(ctx, + (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS); + if (!r) + return NULL; + memset(r->tab, 0, sizeof(r->tab[0]) * r->len); + r->tab[e / JS_LIMB_BITS] = + (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS); + return r; } - break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; + } + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + n_bits = 32 - clz32(e); + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + for(i = n_bits - 2; i >= 0; i--) { + r1 = js_bigint_mul(ctx, r, r); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + if ((e >> i) & 1) { + r1 = js_bigint_mul(ctx, r, a); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; } - goto redo; } - *pres = ret; - return 0; + return r; + overflow: + JS_ThrowRangeError(ctx, "BigInt is too large"); + return NULL; } -int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) -{ - return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); +/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits - + 1). a must be != 0. */ +static uint64_t js_bigint_get_mant_exp(JSContext *ctx, + int *pexp, const JSBigInt *a) +{ + js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits; + int n1, n2, sgn, shift, i, j, e; + uint64_t a1, a0; + + n2 = 4 - JS_LIMB_BITS / 32; + n1 = a->len - n2; + sgn = js_bigint_sign(a); + + /* low_bits != 0 if there are a non zero low bit in abs(a) */ + low_bits = 0; + carry = sgn; + for(i = 0; i < n1; i++) { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + low_bits |= v; + } + /* get the n2 high limbs of abs(a) */ + for(j = 0; j < n2; j++) { + i = j + n1; + if (i < 0) { + v = 0; + } else { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + } + t[j] = v; + } + +#if JS_LIMB_BITS == 32 + a1 = ((uint64_t)t[2] << 32) | t[1]; + a0 = (uint64_t)t[0] << 32; +#else + a1 = t[1]; + a0 = t[0]; +#endif + a0 |= (low_bits != 0); + /* normalize */ + if (a1 == 0) { + /* JS_LIMB_BITS = 64 bit only */ + shift = 64; + a1 = a0; + a0 = 0; + } else { + shift = clz64(a1); + if (shift != 0) { + a1 = (a1 << shift) | (a0 >> (64 - shift)); + a0 <<= shift; + } + } + a1 |= (a0 != 0); /* keep the bits for the final rounding */ + /* compute the exponent */ + e = a->len * JS_LIMB_BITS - shift - 1; + *pexp = e; + return a1; } -static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) +/* shift left with round to nearest, ties to even. n >= 1 */ +static uint64_t shr_rndn(uint64_t a, int n) { - return JS_ToInt32Free(ctx, (int32_t *)pres, val); + uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1); + return (a + addend) >> n; } -static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +/* convert to float64 with round to nearest, ties to even. Return + +/-infinity if too large. */ +static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a) { - uint32_t tag; - int res; + int sgn, e; + uint64_t mant; - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - res = JS_VALUE_GET_INT(val); -#ifdef CONFIG_BIGNUM - int_clamp: -#endif - res = max_int(0, min_int(255, res)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - res = 0; - } else { - if (d < 0) - res = 0; - else if (d > 255) - res = 255; - else - res = lrint(d); - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - bf_rint(r, BF_RNDN); - bf_get_int32(&res, r, 0); - bf_delete(r); - JS_FreeValue(ctx, val); - } - goto int_clamp; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; + if (a->len == 1) { + /* fast case, including zero */ + return (double)(js_slimb_t)a->tab[0]; + } + + sgn = js_bigint_sign(a); + mant = js_bigint_get_mant_exp(ctx, &e, a); + if (e > 1023) { + /* overflow: return infinity */ + mant = 0; + e = 1024; + } else { + mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */ + mant = shr_rndn(mant, 10); + /* rounding can cause an overflow */ + if (mant >= ((uint64_t)1 << 53)) { + mant >>= 1; + e++; } - goto redo; + mant &= (((uint64_t)1 << 52) - 1); } - *pres = res; - return 0; + return uint64_as_float64(((uint64_t)sgn << 63) | + ((uint64_t)(e + 1023) << 52) | + mant); } -static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, BOOL is_array_ctor) +/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity, + (0, n) if an integer, (0, NULL) in case of memory error */ +static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1) { - uint32_t tag, len; + uint64_t a = float64_as_uint64(a1); + int sgn, e, shift; + uint64_t mant; + JSBigIntBuf buf; + JSBigInt *r; + + sgn = a >> 63; + e = (a >> 52) & ((1 << 11) - 1); + mant = a & (((uint64_t)1 << 52) - 1); + if (e == 2047) { + /* NaN, Infinity */ + *pres = 2; + return NULL; + } + if (e == 0 && mant == 0) { + /* zero */ + *pres = 0; + return js_bigint_new_si(ctx, 0); + } + e -= 1023; + /* 0 < a < 1 : not an integer */ + if (e < 0) + goto not_an_integer; + mant |= (uint64_t)1 << 52; + if (e < 52) { + shift = 52 - e; + /* check that there is no fractional part */ + if (mant & (((uint64_t)1 << shift) - 1)) { + not_an_integer: + *pres = 1; + return NULL; + } + mant >>= shift; + e = 0; + } else { + e -= 52; + } + if (sgn) + mant = -mant; + /* the integer is mant*2^e */ + r = js_bigint_set_si64(&buf, (int64_t)mant); + *pres = 0; + return js_bigint_shl(ctx, r, e); +} - tag = JS_VALUE_GET_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - { - int v; - v = JS_VALUE_GET_INT(val); - if (v < 0) - goto fail; - len = v; +/* return -1, 0, 1 or (2) (unordered) */ +static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a, + double b) +{ + int b_sign, a_sign, e, f; + uint64_t mant, b1, a_mant; + + b1 = float64_as_uint64(b); + b_sign = b1 >> 63; + e = (b1 >> 52) & ((1 << 11) - 1); + mant = b1 & (((uint64_t)1 << 52) - 1); + a_sign = js_bigint_sign(a); + if (e == 2047) { + if (mant != 0) { + /* NaN */ + return 2; + } else { + /* +/- infinity */ + return 2 * b_sign - 1; } - break; - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_t a; - BOOL res; - bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); - bf_init(ctx->bf_ctx, &a); - bf_set_ui(&a, len); - res = bf_cmp_eq(&a, &p->num); - bf_delete(&a); - JS_FreeValue(ctx, val); - if (!res) - goto fail; + } else if (e == 0 && mant == 0) { + /* b = +/-0 */ + if (a->len == 1 && a->tab[0] == 0) + return 0; + else + return 1 - 2 * a_sign; + } else if (a->len == 1 && a->tab[0] == 0) { + /* a = 0, b != 0 */ + return 2 * b_sign - 1; + } else if (a_sign != b_sign) { + return 1 - 2 * a_sign; + } else { + e -= 1023; + /* Note: handling denormals is not necessary because we + compare to integers hence f >= 0 */ + /* compute f so that 2^f <= abs(a) < 2^(f+1) */ + a_mant = js_bigint_get_mant_exp(ctx, &f, a); + if (f != e) { + if (f < e) + return -1; + else + return 1; + } else { + mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */ + if (a_mant < mant) + return 2 * a_sign - 1; + else if (a_mant > mant) + return 1 - 2 * a_sign; + else + return 0; } - break; - default: - if (JS_TAG_IS_FLOAT64(tag)) { - double d; - d = JS_VALUE_GET_FLOAT64(val); - len = (uint32_t)d; - if (len != d) - goto fail; + } +} + +/* return -1, 0 or 1 */ +static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + int a_sign, b_sign, res, i; + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + if (a_sign != b_sign) { + res = 1 - 2 * a_sign; + } else { + /* we assume the numbers are normalized */ + if (a->len != b->len) { + if (a->len < b->len) + res = 2 * a_sign - 1; + else + res = 1 - 2 * a_sign; } else { - uint32_t len1; + res = 0; + for(i = a->len -1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + res = -1; + else + res = 1; + break; + } + } + } + } + return res; +} - if (is_array_ctor) { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - /* cannot recurse because val is a number */ - if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) - return -1; +/* contains 10^i */ +static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +#if JS_LIMB_BITS == 64 + 10000000000U, + 100000000000U, + 1000000000000U, + 10000000000000U, + 100000000000000U, + 1000000000000000U, + 10000000000000000U, + 100000000000000000U, + 1000000000000000000U, + 10000000000000000000U, +#endif +}; + +/* syntax: [-]digits in base radix. Return NULL if memory error. radix + = 10, 2, 8 or 16. */ +static JSBigInt *js_bigint_from_string(JSContext *ctx, + const char *str, int radix) +{ + const char *p = str; + int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i; + JSBigInt *r; + js_limb_t v, c, h; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + while (*p == '0') + p++; + n_digits = strlen(p); + log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */ + /* compute the maximum number of limbs */ + /* XXX: overflow */ + if (radix == 10) { + n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */ + } else { + n_bits = n_digits * log2_radix; + } + /* we add one extra bit for the sign */ + n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1); + r = js_bigint_new(ctx, n_limbs); + if (!r) + return NULL; + if (radix == 10) { + int digits_per_limb = JS_LIMB_DIGITS; + len = 1; + r->tab[0] = 0; + for(;;) { + /* XXX: slow */ + v = 0; + for(i = 0; i < digits_per_limb; i++) { + c = to_digit(*p); + if (c >= radix) + break; + p++; + v = v * 10 + c; + } + if (i == 0) + break; + if (len == 1 && r->tab[0] == 0) { + r->tab[0] = v; } else { - /* legacy behavior: must do the conversion twice and compare */ - if (JS_ToUint32(ctx, &len, val)) { - JS_FreeValue(ctx, val); - return -1; - } - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - /* cannot recurse because val is a number */ - if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) - return -1; - if (len1 != len) { - fail: - JS_ThrowRangeError(ctx, "invalid array length"); - return -1; + h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v); + if (h != 0) { + r->tab[len++] = h; } } } - break; + /* add one extra limb to have the correct sign*/ + if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0) + r->tab[len++] = 0; + r->len = len; + } else { + unsigned int bit_pos, shift, pos; + + /* power of two base: no multiplication is needed */ + r->len = n_limbs; + memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs); + for(i = 0; i < n_digits; i++) { + c = to_digit(p[n_digits - 1 - i]); + assert(c < radix); + bit_pos = i * log2_radix; + shift = bit_pos & (JS_LIMB_BITS - 1); + pos = bit_pos / JS_LIMB_BITS; + r->tab[pos] |= c << shift; + /* if log2_radix does not divide JS_LIMB_BITS, needed an + additional op */ + if (shift + log2_radix > JS_LIMB_BITS) { + r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift); + } + } + } + r = js_bigint_normalize(ctx, r); + /* XXX: could do it in place */ + if (is_neg) { + JSBigInt *r1; + r1 = js_bigint_neg(ctx, r); + js_free(ctx, r); + r = r1; } - *plen = len; - return 0; + return r; } -#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) +/* 2 <= base <= 36 */ +static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; -static BOOL is_safe_integer(double d) +/* special version going backwards */ +/* XXX: use dtoa.c */ +static char *js_u64toa(char *q, int64_t n, unsigned int base) { - return isfinite(d) && floor(d) == d && - fabs(d) <= (double)MAX_SAFE_INTEGER; + int digit; + if (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); + } else { + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); + } + return q; } -int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) +/* len >= 1. 2 <= radix <= 36 */ +static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) { - int64_t v; - if (JS_ToInt64Sat(ctx, &v, val)) - return -1; - if (v < 0 || v > MAX_SAFE_INTEGER) { - JS_ThrowRangeError(ctx, "invalid array index"); - *plen = 0; - return -1; + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + /* XXX: optimize */ + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % 10; + n = (js_limb_t)n / 10; + *--q = digit + '0'; + } + } else { + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % radix; + n = (js_limb_t)n / radix; + *--q = digits[digit]; + } } - *plen = v; - return 0; + return q; } -/* convert a value to a length between 0 and MAX_SAFE_INTEGER. - return -1 for exception */ -static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, - JSValue val) +#define JS_RADIX_MAX 36 + +static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { +#if JS_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 js_limb_t radix_base_table[JS_RADIX_MAX - 1] = { +#if JS_LIMB_BITS == 32 + 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, +#else + 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d, + 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821, + 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d, + 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1, + 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49, + 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9, + 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd, + 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981, + 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000, +#endif +}; + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) { - int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); - JS_FreeValue(ctx, val); - return res; + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + char buf[66]; + int len; + len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix); + return js_new_string8_len(ctx, buf, len); + } else { + JSBigInt *r, *tmp = NULL; + char *buf, *q, *buf_end; + int is_neg, n_bits, log2_radix, n_digits; + BOOL is_binary_radix; + JSValue res; + + assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT); + r = JS_VALUE_GET_PTR(val); + if (r->len == 1 && r->tab[0] == 0) { + /* '0' case */ + return js_new_string8_len(ctx, "0", 1); + } + is_binary_radix = ((radix & (radix - 1)) == 0); + is_neg = js_bigint_sign(r); + if (is_neg) { + tmp = js_bigint_neg(ctx, r); + if (!tmp) + return JS_EXCEPTION; + r = tmp; + } else if (!is_binary_radix) { + /* need to modify 'r' */ + tmp = js_bigint_new(ctx, r->len); + if (!tmp) + return JS_EXCEPTION; + memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0])); + r = tmp; + } + log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */ + n_bits = r->len * JS_LIMB_BITS - js_limb_safe_clz(r->tab[r->len - 1]); + /* n_digits is exact only if radix is a power of + two. Otherwise it is >= the exact number of digits */ + n_digits = (n_bits + log2_radix - 1) / log2_radix; + /* XXX: could directly build the JSString */ + buf = js_malloc(ctx, n_digits + is_neg + 1); + if (!buf) { + js_free(ctx, tmp); + return JS_EXCEPTION; + } + q = buf + n_digits + is_neg + 1; + *--q = '\0'; + buf_end = q; + if (!is_binary_radix) { + int len; + js_limb_t radix_base, v; + radix_base = radix_base_table[radix - 2]; + len = r->len; + for(;;) { + /* remove leading zero limbs */ + while (len > 1 && r->tab[len - 1] == 0) + len--; + if (len == 1 && r->tab[0] < radix_base) { + v = r->tab[0]; + if (v != 0) { + q = js_u64toa(q, v, radix); + } + break; + } else { + v = mp_div1(r->tab, r->tab, len, radix_base, 0); + q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]); + } + } + } else { + int i, shift; + unsigned int bit_pos, pos, c; + + /* radix is a power of two */ + for(i = 0; i < n_digits; i++) { + bit_pos = i * log2_radix; + pos = bit_pos / JS_LIMB_BITS; + shift = bit_pos % JS_LIMB_BITS; + if (likely((shift + log2_radix) <= JS_LIMB_BITS)) { + c = r->tab[pos] >> shift; + } else { + c = (r->tab[pos] >> shift) | + (r->tab[pos + 1] << (JS_LIMB_BITS - shift)); + } + c &= (radix - 1); + *--q = digits[c]; + } + } + if (is_neg) + *--q = '-'; + js_free(ctx, tmp); + res = js_new_string8_len(ctx, q, buf_end - q); + js_free(ctx, buf); + return res; + } } -/* Note: can return an exception */ -static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) +/* if possible transform a BigInt to short big and free it, otherwise + return a normal bigint */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) { - double d; - if (!JS_IsNumber(val)) - return FALSE; - if (unlikely(JS_ToFloat64(ctx, &d, val))) - return -1; - return isfinite(d) && floor(d) == d; + JSValue res; + if (p->len == 1) { + res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]); + js_free(ctx, p); + return res; + } else { + return JS_MKPTR(JS_TAG_BIG_INT, p); + } } -static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) +#define ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define ATOD_ACCEPT_BIN_OCT (1 << 2) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) +/* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 5) +/* allow a suffix to override the type */ +#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */ +#define ATOD_TYPE_MASK (3 << 7) +#define ATOD_TYPE_FLOAT64 (0 << 7) +#define ATOD_TYPE_BIG_INT (1 << 7) +/* accept -0x1 */ +#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) + +/* return an exception in case of memory error. Return JS_NAN if + invalid syntax */ +/* XXX: directly use js_atod() */ +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) { - uint32_t tag; + const char *p, *p_start; + int sep, is_neg; + BOOL is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf = NULL; + int i, j, len; + BOOL buf_allocated = FALSE; + JSValue val; + JSATODTempMem atod_mem; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + has_legacy_octal = FALSE; - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - { - int v; - v = JS_VALUE_GET_INT(val); - return (v < 0); + p = str; + p_start = p; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start++; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } else if (p[0] == '-') { + p++; + p_start++; + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + has_legacy_octal = TRUE; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; } - case JS_TAG_FLOAT64: - { - JSFloat64Union u; - u.d = JS_VALUE_GET_FLOAT64(val); - return (u.u64 >> 63); + /* there must be a digit after the prefix */ + if (to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + no_radix_prefix: + if (!(flags & ATOD_INT_ONLY) && + (atod_type == ATOD_TYPE_FLOAT64) && + strstart(p, "Infinity", &p)) { +#ifdef _MSC_VER + double d = INFINITY; +#else + double d = 1.0 / 0.0; +#endif + if (is_neg) + d = -d; + val = JS_NewFloat64(ctx, d); + goto done; } - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - /* Note: integer zeros are not necessarily positive */ - return p->num.sign && !bf_is_zero(&p->num); + } + if (radix == 0) + radix = 10; + is_float = FALSE; + p_start = p; + while (to_digit((uint8_t)*p) < radix + || (*p == sep && (radix != 10 || + p != p_start + 1 || p[-1] != '0') && + to_digit((uint8_t)p[1]) < radix)) { + p++; + } + if (!(flags & ATOD_INT_ONLY)) { + if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { + is_float = TRUE; + p++; + if (*p == sep) + goto fail; + while (to_digit((uint8_t)*p) < radix || + (*p == sep && to_digit((uint8_t)p[1]) < radix)) + p++; + } + if (p > p_start && + (((*p == 'e' || *p == 'E') && radix == 10) || + ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { + const char *p1 = p + 1; + is_float = TRUE; + if (*p1 == '+') { + p1++; + } else if (*p1 == '-') { + p1++; + } + if (is_digit((uint8_t)*p1)) { + p = p1 + 1; + while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) + p++; + } + } + } + if (p == p_start) + goto fail; + + buf = buf1; + buf_allocated = FALSE; + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) + goto mem_error; + buf_allocated = TRUE; + } + /* remove the separators and the radix prefixes */ + j = 0; + if (is_neg) + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + + if (flags & ATOD_ACCEPT_SUFFIX) { + if (*p == 'n') { + p++; + atod_type = ATOD_TYPE_BIG_INT; + } else { + if (is_float && radix != 10) + goto fail; + } + } else { + if (atod_type == ATOD_TYPE_FLOAT64) { + if (is_float && radix != 10) + goto fail; } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: + } + + switch(atod_type) { + case ATOD_TYPE_FLOAT64: { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return p->num.sign; + double d; + d = js_atod(buf, NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY, + &atod_mem); + /* return int or float64 */ + val = JS_NewFloat64(ctx, d); } break; - case JS_TAG_BIG_DECIMAL: + case ATOD_TYPE_BIG_INT: { - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - return p->num.sign; + JSBigInt *r; + if (has_legacy_octal || is_float) + goto fail; + r = js_bigint_from_string(ctx, buf, radix); + if (!r) + goto mem_error; + val = JS_CompactBigInt(ctx, r); } break; -#endif default: - return FALSE; + abort(); } -} - -static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) -{ - JSValue ret; - bf_t a_s, *a; - char *str; - int saved_sign; - - a = JS_ToBigInt(ctx, &a_s, val); - if (!a) - return JS_EXCEPTION; - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | - BF_FTOA_JS_QUIRKS); - a->sign = saved_sign; - JS_FreeBigInt(ctx, a, &a_s); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; -} -static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) -{ - return js_bigint_to_string1(ctx, val, 10); +done: + if (buf_allocated) + js_free_rt(ctx->rt, buf); + if (pp) + *pp = p; + return val; + fail: + val = JS_NAN; + goto done; + mem_error: + val = JS_ThrowOutOfMemory(ctx); + goto done; } -#ifdef CONFIG_BIGNUM +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; -static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, - limb_t prec, bf_flags_t flags) +static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, + JSToNumberHintEnum flag) { - JSValue val, ret; - bf_t a_s, *a; - char *str; - int saved_sign; + uint32_t tag; + JSValue ret; - val = JS_ToNumeric(ctx, val1); - if (JS_IsException(val)) - return val; - a = JS_ToBigFloat(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - flags |= BF_FTOA_JS_QUIRKS; - if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) { - /* Note: for floating point numbers with a radix which is not - a power of two, the current precision is used to compute - the number of digits. */ - if ((radix & (radix - 1)) != 0) { - bf_t r_s, *r = &r_s; - int prec, flags1; - /* must round first */ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - prec = ctx->fp_env.prec; - flags1 = ctx->fp_env.flags & - (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)); + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_INT: + case JS_TAG_SHORT_BIG_INT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); + } + ret = val; + break; + case JS_TAG_FLOAT64: + case JS_TAG_INT: + case JS_TAG_EXCEPTION: + ret = val; + break; + case JS_TAG_BOOL: + case JS_TAG_NULL: + ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_UNDEFINED: + ret = JS_NAN; + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + goto redo; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + { + const char *str; + const char *p; + size_t len; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + ret = JS_NewInt32(ctx, 0); } else { - prec = 53; - flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; - } - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_round(r, prec, flags1 | BF_RNDN); - str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); - bf_delete(r); - } else { - str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); + int flags = ATOD_ACCEPT_BIN_OCT; + ret = js_atof(ctx, p, &p, 0, flags); + if (!JS_IsException(ret)) { + p += skip_spaces(p); + if ((p - str) != len) { + JS_FreeValue(ctx, ret); + ret = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); } - } else { - str = bf_ftoa(NULL, a, radix, prec, flags); + break; + case JS_TAG_SYMBOL: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); + default: + JS_FreeValue(ctx, val); + ret = JS_NAN; + break; } - a->sign = saved_sign; - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, val); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); return ret; } -static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) { - return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); } -static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, - limb_t prec, int flags) +static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) { - JSValue ret; - bfdec_t *a; - char *str; - int saved_sign; - - a = JS_ToBigDecimal(ctx, val); - if (!a) - return JS_EXCEPTION; - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS); - a->sign = saved_sign; - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); } -static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) { - return js_bigdecimal_to_string1(ctx, val, 0, - BF_RNDZ | BF_FTOA_FORMAT_FREE); + return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); } -#endif /* CONFIG_BIGNUM */ - -/* 2 <= base <= 36 */ -static char *i64toa(char *buf_end, int64_t n, unsigned int base) +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, + JSValue val) { - char *q = buf_end; - int digit, is_neg; - - is_neg = 0; - if (n < 0) { - is_neg = 1; - n = -n; - } - *--q = '\0'; - do { - digit = (uint64_t)n % base; - n = (uint64_t)n / base; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - *--q = digit; - } while (n != 0); - if (is_neg) - *--q = '-'; - return q; + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + goto fail; + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; + default: + abort(); + } + *pres = d; + return 0; + fail: + *pres = JS_FLOAT64_NAN; + return -1; } -/* buf1 contains the printf result */ -static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf, - int rounding_mode, char *buf1, int buf1_size) -{ - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - *sign = (buf1[0] == '-'); - /* mantissa */ - buf[0] = buf1[1]; - if (n_digits > 1) - memcpy(buf + 1, buf1 + 3, n_digits - 1); - buf[n_digits] = '\0'; - /* exponent */ - *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; -} - -/* maximum buffer size for js_dtoa */ -#define JS_DTOA_BUF_SIZE 128 - -/* needed because ecvt usually limits the number of digits to - 17. Return the number of digits. */ -static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, - BOOL is_fixed) -{ - int rounding_mode; - char buf_tmp[JS_DTOA_BUF_SIZE]; - - if (!is_fixed) { - unsigned int n_digits_min, n_digits_max; - /* find the minimum amount of digits (XXX: inefficient but simple) */ - n_digits_min = 1; - n_digits_max = 17; - while (n_digits_min < n_digits_max) { - n_digits = (n_digits_min + n_digits_max) / 2; - js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); - if (strtod(buf_tmp, NULL) == d) { - /* no need to keep the trailing zeros */ - while (n_digits >= 2 && buf[n_digits - 1] == '0') - n_digits--; - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; - } - } - n_digits = n_digits_max; - rounding_mode = FE_TONEAREST; +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; } else { - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; - int decpt1, sign1, decpt2, sign2; - /* The JS rounding is specified as round to nearest ties away - from zero (RNDNA), but in printf the "ties" case is not - specified (for example it is RNDN for glibc, RNDNA for - Windows), so we must round manually. */ - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n_digits] == '5') { - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, - buf_tmp, sizeof(buf_tmp)); - js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, - buf_tmp, sizeof(buf_tmp)); - if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { - /* exact result: round away from zero */ - if (sign1) - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ + return __JS_ToFloat64Free(ctx, pres, val); } - js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, - buf_tmp, sizeof(buf_tmp)); - return n_digits; } -static int js_fcvt1(char *buf, int buf_size, double d, int n_digits, - int rounding_mode) +int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) { - int n; - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - n = snprintf(buf, buf_size, "%.*f", n_digits, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - assert(n < buf_size); - return n; + return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); } -static void js_fcvt(char *buf, int buf_size, double d, int n_digits) +static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) { - int rounding_mode; - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - int n1, n2; - char buf1[JS_DTOA_BUF_SIZE]; - char buf2[JS_DTOA_BUF_SIZE]; - - /* The JS rounding is specified as round to nearest ties away from - zero (RNDNA), but in printf the "ties" case is not specified - (for example it is RNDN for glibc, RNDNA for Windows), so we - must round manually. */ - n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST); - rounding_mode = FE_TONEAREST; - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n1 - 1] == '5') { - n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD); - n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD); - if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { - /* exact result: round away from zero */ - if (buf1[0] == '-') - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ - js_fcvt1(buf, buf_size, d, n_digits, rounding_mode); + return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); } -/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ -/* use as many digits as necessary */ -#define JS_DTOA_VAR_FORMAT (0 << 0) -/* use n_digits significant digits (1 <= n_digits <= 101) */ -#define JS_DTOA_FIXED_FORMAT (1 << 0) -/* force fractional format: [-]dd.dd with n_digits fractional digits */ -#define JS_DTOA_FRAC_FORMAT (2 << 0) -/* force exponential notation either in fixed or variable format */ -#define JS_DTOA_FORCE_EXP (1 << 2) - -/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. - XXX: radix != 10 is only supported for small integers -*/ -static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags) +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { - char *q; + uint32_t tag; + JSValue ret; - if (!isfinite(d)) { - if (isnan(d)) { - strcpy(buf, "NaN"); - } else { - q = buf; - if (d < 0) - *q++ = '-'; - strcpy(q, "Infinity"); - } - } else if (flags == JS_DTOA_VAR_FORMAT) { - int64_t i64; - char buf1[70], *ptr; - i64 = (int64_t)d; - if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER) - goto generic_conv; - /* fast path for integers */ - ptr = i64toa(buf1 + sizeof(buf1), i64, radix); - strcpy(buf, ptr); - } else { - if (d == 0.0) - d = 0.0; /* convert -0 to 0 */ - if (flags == JS_DTOA_FRAC_FORMAT) { - js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits); - } else { - char buf1[JS_DTOA_BUF_SIZE]; - int sign, decpt, k, n, i, p, n_max; - BOOL is_fixed; - generic_conv: - is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); - if (is_fixed) { - n_max = n_digits; - } else { - n_max = 21; - } - /* the number has k digits (k >= 1) */ - k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); - n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ - q = buf; - if (sign) - *q++ = '-'; - if (flags & JS_DTOA_FORCE_EXP) - goto force_exp; - if (n >= 1 && n <= n_max) { - if (k <= n) { - memcpy(q, buf1, k); - q += k; - for(i = 0; i < (n - k); i++) - *q++ = '0'; - *q = '\0'; - } else { - /* k > n */ - memcpy(q, buf1, n); - q += n; - *q++ = '.'; - for(i = 0; i < (k - n); i++) - *q++ = buf1[n + i]; - *q = '\0'; - } - } else if (n >= -5 && n <= 0) { - *q++ = '0'; - *q++ = '.'; - for(i = 0; i < -n; i++) - *q++ = '0'; - memcpy(q, buf1, k); - q += k; - *q = '\0'; + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = JS_NewInt32(ctx, 0); } else { - force_exp: - /* exponential notation */ - *q++ = buf1[0]; - if (k > 1) { - *q++ = '.'; - for(i = 1; i < k; i++) - *q++ = buf1[i]; - } - *q++ = 'e'; - p = n - 1; - if (p >= 0) - *q++ = '+'; - sprintf(q, "%d", p); + /* convert -0 to +0 */ + d = trunc(d) + 0.0; + ret = JS_NewFloat64(ctx, d); } } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; } + return ret; } -static JSValue js_dtoa(JSContext *ctx, - double d, int radix, int n_digits, int flags) -{ - char buf[JS_DTOA_BUF_SIZE]; - js_dtoa1(buf, d, radix, n_digits, flags); - return JS_NewString(ctx, buf); -} - -JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) +/* Note: the integer value is satured to 32 bits */ +static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) { uint32_t tag; - const char *str; - char buf[32]; + int ret; + redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { - case JS_TAG_STRING: - return JS_DupValue(ctx, val); case JS_TAG_INT: - snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); - str = buf; - goto new_string; case JS_TAG_BOOL: - return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? - JS_ATOM_true : JS_ATOM_false); case JS_TAG_NULL: - return JS_AtomToString(ctx, JS_ATOM_null); case JS_TAG_UNDEFINED: - return JS_AtomToString(ctx, JS_ATOM_undefined); + ret = JS_VALUE_GET_INT(val); + break; case JS_TAG_EXCEPTION: - return JS_EXCEPTION; - case JS_TAG_OBJECT: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: { - JSValue val1, ret; - val1 = JS_ToPrimitive(ctx, val, HINT_STRING); - if (JS_IsException(val1)) - return val1; - ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); - JS_FreeValue(ctx, val1); - return ret; + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = 0; + } else { + if (d < INT32_MIN) + ret = INT32_MIN; + else if (d > INT32_MAX) + ret = INT32_MAX; + else + ret = (int)d; + } } break; - case JS_TAG_FUNCTION_BYTECODE: - str = "[function bytecode]"; - goto new_string; - case JS_TAG_SYMBOL: - if (is_ToPropertyKey) { - return JS_DupValue(ctx, val); + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) +{ + return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } +} + +int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, + int64_t min, int64_t max, int64_t neg_offset) +{ + int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); + if (res == 0) { + if (*pres < 0) + *pres += neg_offset; + if (*pres < min) + *pres = min; + else if (*pres > max) + *pres = max; + } + return res; +} + +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + int64_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^64) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 62))) { + /* fast case */ + ret = (int64_t)d; + } else if (e <= (1023 + 62 + 53)) { + uint64_t v; + /* remainder modulo 2^64 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + ret = v << ((e - 1023) - 52); + /* take the sign into account */ + if (u.u64 >> 63) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + if (JS_IsBigInt(ctx, val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + +/* return (<0, 0) in case of exception */ +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int32_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^32) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + v = v << ((e - 1023) - 52 + 32); + ret = v >> 32; + /* take the sign into account */ + if (u.u64 >> 63) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) +{ + return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); +} + +static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, (int32_t *)pres, val); +} + +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int res; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = JS_VALUE_GET_INT(val); + res = max_int(0, min_int(255, res)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + res = 0; + } else { + if (d < 0) + res = 0; + else if (d > 255) + res = 255; + else + res = lrint(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, BOOL is_array_ctor) +{ + uint32_t tag, len; + + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + if (!(d >= 0 && d <= UINT32_MAX)) + goto fail; + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +static BOOL is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) +{ + int64_t v; + if (JS_ToInt64Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > MAX_SAFE_INTEGER) { + JS_ThrowRangeError(ctx, "invalid array index"); + *plen = 0; + return -1; + } + *plen = v; + return 0; +} + +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, + JSValue val) +{ + int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); + JS_FreeValue(ctx, val); + return res; +} + +/* Note: can return an exception */ +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) +{ + double d; + if (!JS_IsNumber(val)) + return FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } + case JS_TAG_SHORT_BIG_INT: + return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + return js_bigint_sign(p); + } + default: + return FALSE; + } +} + +static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigint_to_string1(ctx, val, 10); +} + +static JSValue js_dtoa2(JSContext *ctx, + double d, int radix, int n_digits, int flags) +{ + char static_buf[128], *buf, *tmp_buf; + int len, len_max; + JSValue res; + JSDTOATempMem dtoa_mem; + len_max = js_dtoa_max_len(d, radix, n_digits, flags); + + /* longer buffer may be used if radix != 10 */ + if (len_max > sizeof(static_buf) - 1) { + tmp_buf = js_malloc(ctx, len_max + 1); + if (!tmp_buf) + return JS_EXCEPTION; + buf = tmp_buf; + } else { + tmp_buf = NULL; + buf = static_buf; + } + len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem); + res = js_new_string8_len(ctx, buf, len); + js_free(ctx, tmp_buf); + return res; +} + +static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) +{ + uint32_t tag; + char buf[32]; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_STRING: + return JS_DupValue(ctx, val); + case JS_TAG_STRING_ROPE: + return js_linearize_string_rope(ctx, JS_DupValue(ctx, val)); + case JS_TAG_INT: + { + size_t len; + len = i32toa(buf, JS_VALUE_GET_INT(val)); + return js_new_string8_len(ctx, buf, len); + } + break; + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: + { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); + JS_FreeValue(ctx, val1); + return ret; + } + break; + case JS_TAG_FUNCTION_BYTECODE: + return js_new_string8(ctx, "[function bytecode]"); + case JS_TAG_SYMBOL: + if (is_ToPropertyKey) { + return JS_DupValue(ctx, val); } else { return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); } case JS_TAG_FLOAT64: - return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, - JS_DTOA_VAR_FORMAT); + return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, + JS_DTOA_FORMAT_FREE); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: - return ctx->rt->bigint_ops.to_string(ctx, val); -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - return ctx->rt->bigfloat_ops.to_string(ctx, val); - case JS_TAG_BIG_DECIMAL: - return ctx->rt->bigdecimal_ops.to_string(ctx, val); -#endif + return js_bigint_to_string(ctx, val); default: - str = "[unsupported type]"; - new_string: - return JS_NewString(ctx, str); + return js_new_string8(ctx, "[unsupported type]"); } } @@ -11627,7 +12995,7 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) goto fail; break; default: - if (c < 32 || (c >= 0xd800 && c < 0xe000)) { + if (c < 32 || is_surrogate(c)) { snprintf(buf, sizeof(buf), "\\u%04x", c); if (string_buffer_puts8(b, buf)) goto fail; @@ -11648,158 +13016,495 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) return JS_EXCEPTION; } -static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +#define JS_PRINT_MAX_DEPTH 8 + +typedef struct { + JSRuntime *rt; + JSContext *ctx; /* may be NULL */ + JSPrintValueOptions options; + JSPrintValueWrite *write_func; + void *write_opaque; + int level; + JSObject *print_stack[JS_PRINT_MAX_DEPTH]; /* level values */ +} JSPrintValueState; + +static void js_print_value(JSPrintValueState *s, JSValueConst val); + +static void js_putc(JSPrintValueState *s, char c) { - printf("%14s %4s %4s %14s %10s %s\n", - "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); + s->write_func(s->write_opaque, &c, 1); } -/* for debug only: dump an object without side effect */ -static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +static void js_puts(JSPrintValueState *s, const char *str) { - uint32_t i; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - JSShape *sh; - JSShapeProperty *prs; - JSProperty *pr; - BOOL is_first = TRUE; + s->write_func(s->write_opaque, str, strlen(str)); +} - /* XXX: should encode atoms with special characters */ - sh = p->shape; /* the shape can be NULL while freeing an object */ - printf("%14p %4d ", - (void *)p, - p->header.ref_count); - if (sh) { - printf("%3d%c %14p ", - sh->header.ref_count, - " *"[sh->is_hashed], - (void *)sh->proto); +static void __attribute__((format(printf, 2, 3))) js_printf(JSPrintValueState *s, const char *fmt, ...) +{ + va_list ap; + char buf[256]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + s->write_func(s->write_opaque, buf, strlen(buf)); +} + +static void js_print_float64(JSPrintValueState *s, double d) +{ + JSDTOATempMem dtoa_mem; + char buf[32]; + int len; + len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &dtoa_mem); + s->write_func(s->write_opaque, buf, len); +} + +static uint32_t js_string_get_length(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + JSString *p = JS_VALUE_GET_STRING(val); + return p->len; + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { + JSStringRope *r = JS_VALUE_GET_PTR(val); + return r->len; } else { - printf("%3s %14s ", "-", "-"); - } - printf("%10s ", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); - if (p->is_exotic && p->fast_array) { - printf("[ "); - for(i = 0; i < p->u.array.count; i++) { - if (i != 0) - printf(", "); - switch (p->class_id) { - case JS_CLASS_ARRAY: - case JS_CLASS_ARGUMENTS: - JS_DumpValueShort(rt, p->u.array.u.values[i]); - break; - case JS_CLASS_UINT8C_ARRAY: - 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_BIG_INT64_ARRAY: - case JS_CLASS_BIG_UINT64_ARRAY: - case JS_CLASS_FLOAT32_ARRAY: - case JS_CLASS_FLOAT64_ARRAY: - { - int size = 1 << typed_array_size_log2(p->class_id); - const uint8_t *b = p->u.array.u.uint8_ptr + i * size; - while (size-- > 0) - printf("%02X", *b++); - } - break; - } - } - printf(" ] "); + return 0; } +} - if (sh) { - printf("{ "); +static void js_dump_char(JSPrintValueState *s, int c, int sep) +{ + if (c == sep || c == '\\') { + js_putc(s, '\\'); + js_putc(s, c); + } else if (c >= ' ' && c <= 126) { + js_putc(s, c); + } else if (c == '\n') { + js_putc(s, '\\'); + js_putc(s, 'n'); + } else { + js_printf(s, "\\u%04x", c); + } +} + +static void js_print_string_rec(JSPrintValueState *s, JSValueConst val, + int sep, uint32_t pos) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + JSString *p = JS_VALUE_GET_STRING(val); + uint32_t i, len; + if (pos < s->options.max_string_length) { + len = min_uint32(p->len, s->options.max_string_length - pos); + for(i = 0; i < len; i++) { + js_dump_char(s, string_get(p, i), sep); + } + } + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { + JSStringRope *r = JS_VALUE_GET_PTR(val); + js_print_string_rec(s, r->left, sep, pos); + js_print_string_rec(s, r->right, sep, pos + js_string_get_length(r->left)); + } else { + js_printf(s, "", (int)JS_VALUE_GET_TAG(val)); + } +} + +static void js_print_string(JSPrintValueState *s, JSValueConst val) +{ + int sep; + if (s->options.raw_dump && JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + JSString *p = JS_VALUE_GET_STRING(val); + js_printf(s, "%d", p->header.ref_count); + sep = (p->header.ref_count == 1) ? '\"' : '\''; + } else { + sep = '\"'; + } + js_putc(s, sep); + js_print_string_rec(s, val, sep, 0); + js_putc(s, sep); + if (js_string_get_length(val) > s->options.max_string_length) { + uint32_t n = js_string_get_length(val) - s->options.max_string_length; + js_printf(s, "... %u more character%s", n, n > 1 ? "s" : ""); + } +} + +static void js_print_raw_string2(JSPrintValueState *s, JSValueConst val, BOOL remove_last_lf) +{ + const char *cstr; + size_t len; + cstr = JS_ToCStringLen(s->ctx, &len, val); + if (cstr) { + if (remove_last_lf && len > 0 && cstr[len - 1] == '\n') + len--; + s->write_func(s->write_opaque, cstr, len); + JS_FreeCString(s->ctx, cstr); + } +} + +static void js_print_raw_string(JSPrintValueState *s, JSValueConst val) +{ + js_print_raw_string2(s, val, FALSE); +} + +static BOOL is_ascii_ident(const JSString *p) +{ + int i, c; + + if (p->len == 0) + return FALSE; + for(i = 0; i < p->len; i++) { + c = string_get(p, i); + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) + return FALSE; + } + return TRUE; +} + +static void js_print_atom(JSPrintValueState *s, JSAtom atom) +{ + int i; + if (__JS_AtomIsTaggedInt(atom)) { + js_printf(s, "%u", __JS_AtomToUInt32(atom)); + } else if (atom == JS_ATOM_NULL) { + js_puts(s, ""); + } else { + assert(atom < s->rt->atom_size); + JSString *p; + p = s->rt->atom_array[atom]; + if (is_ascii_ident(p)) { + for(i = 0; i < p->len; i++) { + js_putc(s, string_get(p, i)); + } + } else { + js_putc(s, '"'); + for(i = 0; i < p->len; i++) { + js_dump_char(s, string_get(p, i), '\"'); + } + js_putc(s, '"'); + } + } +} + +/* return 0 if invalid length */ +static uint32_t js_print_array_get_length(JSObject *p) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValueConst val; + + prs = find_own_property(&pr, p, JS_ATOM_length); + if (!prs) + return 0; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return 0; + val = pr->u.value; + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val); + case JS_TAG_FLOAT64: + return (uint32_t)JS_VALUE_GET_FLOAT64(val); + default: + return 0; + } +} + +static void js_print_comma(JSPrintValueState *s, int *pcomma_state) +{ + switch(*pcomma_state) { + case 0: + break; + case 1: + js_printf(s, ", "); + break; + case 2: + js_printf(s, " { "); + break; + } + *pcomma_state = 1; +} + +static void js_print_more_items(JSPrintValueState *s, int *pcomma_state, + uint32_t n) +{ + js_print_comma(s, pcomma_state); + js_printf(s, "... %u more item%s", n, n > 1 ? "s" : ""); +} + +static void js_print_object(JSPrintValueState *s, JSObject *p) +{ + JSRuntime *rt = s->rt; + JSShape *sh; + JSShapeProperty *prs; + JSProperty *pr; + int comma_state; + BOOL is_array; + uint32_t i; + + comma_state = 0; + is_array = FALSE; + if (p->class_id == JS_CLASS_ARRAY) { + is_array = TRUE; + js_printf(s, "[ "); + /* XXX: print array like properties even if not fast array */ + if (p->fast_array) { + uint32_t len, n, len1; + len = js_print_array_get_length(p); + + len1 = min_uint32(p->u.array.count, s->options.max_item_count); + for(i = 0; i < len1; i++) { + js_print_comma(s, &comma_state); + js_print_value(s, p->u.array.u.values[i]); + } + if (len1 < p->u.array.count) + js_print_more_items(s, &comma_state, p->u.array.count - len1); + if (p->u.array.count < len) { + n = len - p->u.array.count; + js_print_comma(s, &comma_state); + js_printf(s, "<%u empty item%s>", n, n > 1 ? "s" : ""); + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + uint32_t size = 1 << typed_array_size_log2(p->class_id); + uint32_t len1; + int64_t v; + + js_print_atom(s, rt->class_array[p->class_id].class_name); + js_printf(s, "(%u) [ ", p->u.array.count); + + is_array = TRUE; + len1 = min_uint32(p->u.array.count, s->options.max_item_count); + for(i = 0; i < len1; i++) { + const uint8_t *ptr = p->u.array.u.uint8_ptr + i * size; + js_print_comma(s, &comma_state); + switch(p->class_id) { + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + v = *ptr; + goto ta_int64; + case JS_CLASS_INT8_ARRAY: + v = *(int8_t *)ptr; + goto ta_int64; + case JS_CLASS_INT16_ARRAY: + v = *(int16_t *)ptr; + goto ta_int64; + case JS_CLASS_UINT16_ARRAY: + v = *(uint16_t *)ptr; + goto ta_int64; + case JS_CLASS_INT32_ARRAY: + v = *(int32_t *)ptr; + goto ta_int64; + case JS_CLASS_UINT32_ARRAY: + v = *(uint32_t *)ptr; + goto ta_int64; + case JS_CLASS_BIG_INT64_ARRAY: + v = *(int64_t *)ptr; + ta_int64: + js_printf(s, "%" PRId64, v); + break; + case JS_CLASS_BIG_UINT64_ARRAY: + js_printf(s, "%" PRIu64, *(uint64_t *)ptr); + break; + case JS_CLASS_FLOAT16_ARRAY: + js_print_float64(s, fromfp16(*(uint16_t *)ptr)); + break; + case JS_CLASS_FLOAT32_ARRAY: + js_print_float64(s, *(float *)ptr); + break; + case JS_CLASS_FLOAT64_ARRAY: + js_print_float64(s, *(double *)ptr); + break; + } + } + if (len1 < p->u.array.count) + js_print_more_items(s, &comma_state, p->u.array.count - len1); + } else if (p->class_id == JS_CLASS_BYTECODE_FUNCTION || + (rt->class_array[p->class_id].call != NULL && + p->class_id != JS_CLASS_PROXY)) { + js_printf(s, "[Function"); + /* XXX: allow dump without ctx */ + if (!s->options.raw_dump && s->ctx) { + const char *func_name_str; + js_putc(s, ' '); + func_name_str = get_func_name(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (!func_name_str || func_name_str[0] == '\0') + js_puts(s, "(anonymous)"); + else + js_puts(s, func_name_str); + JS_FreeCString(s->ctx, func_name_str); + } + js_printf(s, "]"); + comma_state = 2; + } else if (p->class_id == JS_CLASS_MAP || p->class_id == JS_CLASS_SET) { + JSMapState *ms = p->u.opaque; + struct list_head *el; + + if (!ms) + goto default_obj; + js_print_atom(s, rt->class_array[p->class_id].class_name); + js_printf(s, "(%u) { ", ms->record_count); + i = 0; + list_for_each(el, &ms->records) { + JSMapRecord *mr = list_entry(el, JSMapRecord, link); + js_print_comma(s, &comma_state); + if (mr->empty) + continue; + js_print_value(s, mr->key); + if (p->class_id == JS_CLASS_MAP) { + js_printf(s, " => "); + js_print_value(s, mr->value); + } + i++; + if (i >= s->options.max_item_count) + break; + } + if (i < ms->record_count) + js_print_more_items(s, &comma_state, ms->record_count - i); + } else if (p->class_id == JS_CLASS_REGEXP && s->ctx && !s->options.raw_dump) { + JSValue str = js_regexp_toString(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL); + if (JS_IsException(str)) + goto default_obj; + js_print_raw_string(s, str); + JS_FreeValueRT(s->rt, str); + comma_state = 2; + } else if (p->class_id == JS_CLASS_DATE && s->ctx && !s->options.raw_dump) { + JSValue str = get_date_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL, 0x23); /* toISOString() */ + if (JS_IsException(str)) + goto default_obj; + js_print_raw_string(s, str); + JS_FreeValueRT(s->rt, str); + comma_state = 2; + } else if (p->class_id == JS_CLASS_ERROR && s->ctx && !s->options.raw_dump) { + JSValue str = js_error_toString(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL); + if (JS_IsException(str)) + goto default_obj; + js_print_raw_string(s, str); + JS_FreeValueRT(s->rt, str); + /* dump the stack if present */ + str = JS_GetProperty(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_stack); + if (JS_IsString(str)) { + js_putc(s, '\n'); + /* XXX: should remove the last '\n' in stack as + v8. SpiderMonkey does not do it */ + js_print_raw_string2(s, str, TRUE); + } + JS_FreeValueRT(s->rt, str); + comma_state = 2; + } else { + default_obj: + if (p->class_id != JS_CLASS_OBJECT) { + js_print_atom(s, rt->class_array[p->class_id].class_name); + js_printf(s, " "); + } + js_printf(s, "{ "); + } + + sh = p->shape; /* the shape can be NULL while freeing an object */ + if (sh) { + uint32_t j; + + j = 0; for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { if (prs->atom != JS_ATOM_NULL) { - pr = &p->prop[i]; - if (!is_first) - printf(", "); - printf("%s: ", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - printf("[getset %p %p]", (void *)pr->u.getset.getter, - (void *)pr->u.getset.setter); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - printf("[varref %p]", (void *)pr->u.var_ref); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - printf("[autoinit %p %d %p]", - (void *)js_autoinit_get_realm(pr), - js_autoinit_get_id(pr), - (void *)pr->u.init.opaque); - } else { - JS_DumpValueShort(rt, pr->u.value); + if (!(prs->flags & JS_PROP_ENUMERABLE) && + !s->options.show_hidden) { + continue; + } + if (j < s->options.max_item_count) { + pr = &p->prop[i]; + js_print_comma(s, &comma_state); + js_print_atom(s, prs->atom); + js_printf(s, ": "); + + /* XXX: autoinit property */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (s->options.raw_dump) { + js_printf(s, "[Getter %p Setter %p]", + pr->u.getset.getter, pr->u.getset.setter); + } else { + if (pr->u.getset.getter && pr->u.getset.setter) { + js_printf(s, "[Getter/Setter]"); + } else if (pr->u.getset.setter) { + js_printf(s, "[Setter]"); + } else { + js_printf(s, "[Getter]"); + } + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (s->options.raw_dump) { + js_printf(s, "[varref %p]", (void *)pr->u.var_ref); + } else { + js_print_value(s, *pr->u.var_ref->pvalue); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + if (s->options.raw_dump) { + js_printf(s, "[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), + (void *)pr->u.init.opaque); + } else { + /* XXX: could autoinit but need to restart + the iteration */ + js_printf(s, "[autoinit]"); + } + } else { + js_print_value(s, pr->u.value); + } } - is_first = FALSE; + j++; } } - printf(" }"); + if (j > s->options.max_item_count) + js_print_more_items(s, &comma_state, j - s->options.max_item_count); } - - if (js_class_has_bytecode(p->class_id)) { + if (s->options.raw_dump && js_class_has_bytecode(p->class_id)) { JSFunctionBytecode *b = p->u.func.function_bytecode; - JSVarRef **var_refs; if (b->closure_var_count) { + JSVarRef **var_refs; var_refs = p->u.func.var_refs; - printf(" Closure:"); + + js_print_comma(s, &comma_state); + js_printf(s, "[[Closure]]: ["); for(i = 0; i < b->closure_var_count; i++) { - printf(" "); - JS_DumpValueShort(rt, var_refs[i]->value); - } - if (p->u.func.home_object) { - printf(" HomeObject: "); - JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); + if (i != 0) + js_printf(s, ", "); + js_print_value(s, var_refs[i]->value); } + js_printf(s, " ]"); + } + if (p->u.func.home_object) { + js_print_comma(s, &comma_state); + js_printf(s, "[[HomeObject]]: "); + js_print_value(s, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); } } - printf("\n"); -} -static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) -{ - if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - JS_DumpObject(rt, (JSObject *)p); - } else { - printf("%14p %4d ", - (void *)p, - p->ref_count); - switch(p->gc_obj_type) { - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - printf("[function bytecode]"); - break; - case JS_GC_OBJ_TYPE_SHAPE: - printf("[shape]"); - break; - case JS_GC_OBJ_TYPE_VAR_REF: - printf("[var_ref]"); - break; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - printf("[async_function]"); - break; - case JS_GC_OBJ_TYPE_JS_CONTEXT: - printf("[js_context]"); - break; - default: - printf("[unknown %d]", p->gc_obj_type); - break; + if (!is_array) { + if (comma_state != 2) { + js_printf(s, " }"); } - printf("\n"); + } else { + js_printf(s, " ]"); } } -static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val) +static int js_print_stack_index(JSPrintValueState *s, JSObject *p) +{ + int i; + for(i = 0; i < s->level; i++) + if (s->print_stack[i] == p) + return i; + return -1; +} + +static void js_print_value(JSPrintValueState *s, JSValueConst val) { uint32_t tag = JS_VALUE_GET_NORM_TAG(val); const char *str; switch(tag) { case JS_TAG_INT: - printf("%d", JS_VALUE_GET_INT(val)); + js_printf(s, "%d", JS_VALUE_GET_INT(val)); break; case JS_TAG_BOOL: if (JS_VALUE_GET_BOOL(val)) @@ -11819,108 +13524,254 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, case JS_TAG_UNDEFINED: str = "undefined"; print_str: - printf("%s", str); + js_puts(s, str); break; case JS_TAG_FLOAT64: - printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + js_print_float64(s, JS_VALUE_GET_FLOAT64(val)); break; - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - char *str; - str = bf_ftoa(NULL, &p->num, 10, 0, - BF_RNDZ | BF_FTOA_FORMAT_FRAC); - printf("%sn", str); - bf_realloc(&rt->bf_ctx, str, 0); - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - char *str; - str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF, - BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); - printf("%sl", str); - bf_free(&rt->bf_ctx, str); - } + case JS_TAG_SHORT_BIG_INT: + js_printf(s, "%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); break; - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - char *str; - str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF, - BF_RNDZ | BF_FTOA_FORMAT_FREE); - printf("%sm", str); - bf_free(&rt->bf_ctx, str); + case JS_TAG_BIG_INT: + if (!s->options.raw_dump && s->ctx) { + JSValue str = js_bigint_to_string(s->ctx, val); + if (JS_IsException(str)) + goto raw_bigint; + js_print_raw_string(s, str); + js_putc(s, 'n'); + JS_FreeValueRT(s->rt, str); + } else { + JSBigInt *p; + int sgn, i; + raw_bigint: + p = JS_VALUE_GET_PTR(val); + /* In order to avoid allocations we just dump the limbs */ + sgn = js_bigint_sign(p); + if (sgn) + js_printf(s, "BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); + js_printf(s, "0x"); + for(i = p->len - 1; i >= 0; i--) { + if (i != p->len - 1) + js_putc(s, '_'); +#if JS_LIMB_BITS == 32 + js_printf(s, "%08x", p->tab[i]); +#else + js_printf(s, "%016" PRIx64, p->tab[i]); +#endif + } + js_putc(s, 'n'); + if (sgn) + js_putc(s, ')'); } break; -#endif case JS_TAG_STRING: - { - JSString *p; - p = JS_VALUE_GET_STRING(val); - JS_DumpString(rt, p); + case JS_TAG_STRING_ROPE: + if (s->options.raw_dump && tag == JS_TAG_STRING_ROPE) { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + js_printf(s, "[rope len=%d depth=%d]", r->len, r->depth); + } else { + js_print_string(s, val); } break; case JS_TAG_FUNCTION_BYTECODE: { JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + js_puts(s, "[bytecode "); + js_print_atom(s, b->func_name); + js_putc(s, ']'); } break; case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(val); - JSAtom atom = rt->class_array[p->class_id].class_name; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - printf("[%s %p]", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); + int idx; + idx = js_print_stack_index(s, p); + if (idx >= 0) { + js_printf(s, "[circular %d]", idx); + } else if (s->level < s->options.max_depth) { + s->print_stack[s->level++] = p; + js_print_object(s, JS_VALUE_GET_OBJ(val)); + s->level--; + } else { + JSAtom atom = s->rt->class_array[p->class_id].class_name; + js_putc(s, '['); + js_print_atom(s, atom); + if (s->options.raw_dump) { + js_printf(s, " %p", (void *)p); + } + js_putc(s, ']'); + } } break; case JS_TAG_SYMBOL: { JSAtomStruct *p = JS_VALUE_GET_PTR(val); - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - printf("Symbol(%s)", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + js_puts(s, "Symbol("); + js_print_atom(s, js_get_atom_index(s->rt, p)); + js_putc(s, ')'); } break; case JS_TAG_MODULE: - printf("[module]"); + js_puts(s, "[module]"); break; default: - printf("[unknown tag %d]", tag); + js_printf(s, "[unknown tag %d]", tag); break; } } -static __maybe_unused void JS_DumpValue(JSContext *ctx, - JSValueConst val) +void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options) +{ + memset(options, 0, sizeof(*options)); + options->max_depth = 2; + options->max_string_length = 1000; + options->max_item_count = 100; +} + +static void JS_PrintValueInternal(JSRuntime *rt, JSContext *ctx, + JSPrintValueWrite *write_func, void *write_opaque, + JSValueConst val, const JSPrintValueOptions *options) +{ + JSPrintValueState ss, *s = &ss; + if (options) + s->options = *options; + else + JS_PrintValueSetDefaultOptions(&s->options); + if (s->options.max_depth <= 0) + s->options.max_depth = JS_PRINT_MAX_DEPTH; + else + s->options.max_depth = min_int(s->options.max_depth, JS_PRINT_MAX_DEPTH); + if (s->options.max_string_length == 0) + s->options.max_string_length = UINT32_MAX; + if (s->options.max_item_count == 0) + s->options.max_item_count = UINT32_MAX; + s->rt = rt; + s->ctx = ctx; + s->write_func = write_func; + s->write_opaque = write_opaque; + s->level = 0; + js_print_value(s, val); +} + +void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_opaque, + JSValueConst val, const JSPrintValueOptions *options) +{ + JS_PrintValueInternal(rt, NULL, write_func, write_opaque, val, options); +} + +void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque, + JSValueConst val, const JSPrintValueOptions *options) +{ + JS_PrintValueInternal(ctx->rt, ctx, write_func, write_opaque, val, options); +} + +static void js_dump_value_write(void *opaque, const char *buf, size_t len) +{ + FILE *fo = opaque; + fwrite(buf, 1, len, fo); +} + +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +{ + JSPrintValueState ss, *s = &ss; + memset(s, 0, sizeof(*s)); + s->rt = ctx->rt; + s->ctx = ctx; + s->write_func = js_dump_value_write; + s->write_opaque = stdout; + js_print_atom(s, atom); +} + +static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val) { - JS_DumpValueShort(ctx->rt, val); + printf("%s=", str); + JS_PrintValue(ctx, js_dump_value_write, stdout, val, NULL); + printf("\n"); } -static __maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val) +static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val) { printf("%s=", str); - JS_DumpValueShort(ctx->rt, val); + JS_PrintValueRT(rt, js_dump_value_write, stdout, val, NULL); + printf("\n"); +} + +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +{ + printf("%14s %4s %4s %14s %s\n", + "ADDRESS", "REFS", "SHRF", "PROTO", "CONTENT"); +} + +/* for debug only: dump an object without side effect */ +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +{ + JSShape *sh; + JSPrintValueOptions options; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", + (void *)p, + p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", + sh->header.ref_count, + " *"[sh->is_hashed], + (void *)sh->proto); + } else { + printf("%3s %14s ", "-", "-"); + } + + JS_PrintValueSetDefaultOptions(&options); + options.max_depth = 1; + options.show_hidden = TRUE; + options.raw_dump = TRUE; + JS_PrintValueRT(rt, js_dump_value_write, stdout, JS_MKPTR(JS_TAG_OBJECT, p), &options); + printf("\n"); } +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +{ + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject *)p); + } else { + printf("%14p %4d ", + (void *)p, + p->ref_count); + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + /* return -1 if exception (proxy case) or TRUE/FALSE */ +// TODO: should take flags to make proxy resolution and exceptions optional int JS_IsArray(JSContext *ctx, JSValueConst val) { - JSObject *p; + if (js_resolve_proxy(ctx, &val, TRUE)) + return -1; if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(val); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_isArray(ctx, val); - else - return p->class_id == JS_CLASS_ARRAY; + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ARRAY; } else { return FALSE; } @@ -11936,48 +13787,34 @@ static double js_pow(double a, double b) } } -JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) -{ - JSValue val; - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_si(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - return val; -} - JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { - if (is_math_mode(ctx) && - v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { - return JS_NewInt64(ctx, v); +#if JS_SHORT_BIG_INT_BITS == 64 + return __JS_NewShortBigInt(ctx, v); +#else + if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); } else { - return JS_NewBigInt64_1(ctx, v); + JSBigInt *p; + p = js_bigint_new_si64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } +#endif } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - JSValue val; - if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { - val = JS_NewInt64(ctx, v); + if (v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); } else { - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_ui(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } + JSBigInt *p; + p = js_bigint_new_ui64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); } - return val; } /* return NaN if bad bigint literal */ @@ -11986,7 +13823,7 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) const char *str, *p; size_t len; int flags; - + str = JS_ToCStringLen(ctx, &len, val); JS_FreeValue(ctx, val); if (!str) @@ -11997,10 +13834,6 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) val = JS_NewBigInt64(ctx, 0); } else { flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; -#ifdef CONFIG_BIGNUM - if (is_math_mode(ctx)) - flags |= ATOD_MODE_BIGINT; -#endif val = js_atof(ctx, p, &p, 0, flags); p += skip_spaces(p); if (!JS_IsException(val)) { @@ -12022,135 +13855,72 @@ static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) return val; } -/* if the returned bigfloat is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */ -static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +/* JS Numbers are not allowed */ +static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) { uint32_t tag; - bf_t *r; - JSBigFloat *p; redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + break; case JS_TAG_INT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: - if (!is_math_mode(ctx)) - goto fail; - /* fall tru */ - case JS_TAG_BOOL: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - break; case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (!is_math_mode(ctx)) - goto fail; - if (!isfinite(d)) - goto fail; - r = buf; - bf_init(ctx->bf_ctx, r); - d = trunc(d); - bf_set_float64(r, d); - } - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - if (!is_math_mode(ctx)) - goto fail; - p = JS_VALUE_GET_PTR(val); - if (!bf_is_finite(&p->num)) - goto fail; - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - bf_rint(r, BF_RNDZ); - JS_FreeValue(ctx, val); + goto fail; + case JS_TAG_BOOL: + val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); break; -#endif case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: val = JS_StringToBigIntErr(ctx, val); if (JS_IsException(val)) - return NULL; + return val; goto redo; case JS_TAG_OBJECT: val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) - return NULL; + return val; goto redo; default: fail: JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "cannot convert to bigint"); - return NULL; - } - return r; -} - -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val)); -} - -static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { - return val; - } else { - bf_t a_s, *a, *r; - int ret; - JSValue res; - - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - r = JS_GetBigInt(res); - ret = bf_set(r, a); - JS_FreeBigInt(ctx, a, &a_s); - if (ret) { - JS_FreeValue(ctx, res); - return JS_ThrowOutOfMemory(ctx); - } - return JS_CompactBigInt(ctx, res); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); } + return val; } -/* free the bf_t allocated by JS_ToBigInt */ -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) { - if (a == buf) { - bf_delete(a); - } else { - JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - - offsetof(JSBigFloat, num)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); - } + return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val)); } -/* XXX: merge with JS_ToInt64Free with a specific flag */ +/* XXX: merge with JS_ToInt64Free with a specific flag ? */ static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) { - bf_t a_s, *a; + uint64_t res; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { + val = JS_ToBigIntFree(ctx, val); + if (JS_IsException(val)) { *pres = 0; return -1; } - bf_get_int64(pres, a, BF_GET_INT_MOD); - JS_FreeBigInt(ctx, a, &a_s); + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + res = JS_VALUE_GET_SHORT_BIG_INT(val); + } else { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* return the value mod 2^64 */ + res = p->tab[0]; +#if JS_LIMB_BITS == 32 + if (p->len >= 2) + res |= (uint64_t)p->tab[1] << 32; +#endif + JS_FreeValue(ctx, val); + } + *pres = res; return 0; } @@ -12159,727 +13929,121 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); } -static JSBigFloat *js_new_bf(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return NULL; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return p; -} - -static JSValue JS_NewBigInt(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return JS_EXCEPTION; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return JS_MKPTR(JS_TAG_BIG_INT, p); -} - -static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, - BOOL convert_to_safe_integer) -{ - int64_t v; - bf_t *a; - - if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) - return val; /* fail safe */ - a = JS_GetBigInt(val); - if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && - v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { - JS_FreeValue(ctx, val); - return JS_NewInt64(ctx, v); - } else if (a->expn == BF_EXP_ZERO && a->sign) { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - assert(p->header.ref_count == 1); - a->sign = 0; - } - return val; -} - -/* Convert the big int to a safe integer if in math mode. normalize - the zero representation. Could also be used to convert the bigint - to a short bigint value. The reference count of the value must be - 1. Cannot fail */ -static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) -{ - return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); -} - -static JSValue throw_bf_exception(JSContext *ctx, int status) -{ - const char *str; - if (status & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - if (status & BF_ST_DIVIDE_ZERO) { - str = "division by zero"; - } else if (status & BF_ST_INVALID_OP) { - str = "invalid operation"; - } else { - str = "integer overflow"; - } - return JS_ThrowRangeError(ctx, "%s", str); -} - -/* if the returned bigfloat is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return - NULL in case of error. */ -static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) { + JSValue op1; + int v; uint32_t tag; - bf_t *r; - JSBigFloat *p; + JSBigIntBuf buf1; + JSBigInt *p1; - tag = JS_VALUE_GET_NORM_TAG(val); + op1 = sp[-1]; + /* fast path for float64 */ + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) + goto handle_float64; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); switch(tag) { case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_si(r, JS_VALUE_GET_INT(val))) - goto fail; + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = __JS_NewFloat64(ctx, -0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = JS_NewInt64(ctx, v64); + } break; - case JS_TAG_FLOAT64: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { - fail: - bf_delete(r); - return NULL; + case JS_TAG_SHORT_BIG_INT: + { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + goto exception; + case OP_inc: + if (v == JS_SHORT_BIG_INT_MAX) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v + 1); + break; + case OP_dec: + if (v == JS_SHORT_BIG_INT_MIN) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v - 1); + break; + case OP_neg: + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (v == JS_SHORT_BIG_INT_MIN) { + bigint_slow_case: + p1 = js_bigint_set_short(&buf1, op1); + goto bigint_slow_case1; + } + sp[-1] = __JS_NewShortBigInt(ctx, -v); + break; + default: + abort(); + } } break; case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; - case JS_TAG_UNDEFINED: - default: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_nan(r); - break; - } - return r; -} - -#ifdef CONFIG_BIGNUM -/* return NULL if invalid type */ -static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val) -{ - uint32_t tag; - JSBigDecimal *p; - bfdec_t *r; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_BIG_DECIMAL: - p = JS_VALUE_GET_PTR(val); - r = &p->num; + { + JSBigInt *r; + p1 = JS_VALUE_GET_PTR(op1); + bigint_slow_case1: + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + goto exception; + case OP_inc: + case OP_dec: + { + JSBigIntBuf buf2; + JSBigInt *p2; + p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1); + r = js_bigint_add(ctx, p1, p2, 0); + } + break; + case OP_neg: + r = js_bigint_neg(ctx, p1); + break; + case OP_not: + r = js_bigint_not(ctx, p1); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } break; - default: - JS_ThrowTypeError(ctx, "bigdecimal expected"); - r = NULL; - break; - } - return r; -} - -static JSValue JS_NewBigFloat(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return JS_EXCEPTION; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return JS_MKPTR(JS_TAG_BIG_FLOAT, p); -} - -static JSValue JS_NewBigDecimal(JSContext *ctx) -{ - JSBigDecimal *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return JS_EXCEPTION; - p->header.ref_count = 1; - bfdec_init(ctx->bf_ctx, &p->num); - return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); -} - -/* must be kept in sync with JSOverloadableOperatorEnum */ -/* XXX: use atoms ? */ -static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { - "+", - "-", - "*", - "/", - "%", - "**", - "|", - "&", - "^", - "<<", - ">>", - ">>>", - "==", - "<", - "pos", - "neg", - "++", - "--", - "~", -}; - -static int get_ovop_from_opcode(OPCodeEnum op) -{ - switch(op) { - case OP_add: - return JS_OVOP_ADD; - case OP_sub: - return JS_OVOP_SUB; - case OP_mul: - return JS_OVOP_MUL; - case OP_div: - return JS_OVOP_DIV; - case OP_mod: - case OP_math_mod: - return JS_OVOP_MOD; - case OP_pow: - return JS_OVOP_POW; - case OP_or: - return JS_OVOP_OR; - case OP_and: - return JS_OVOP_AND; - case OP_xor: - return JS_OVOP_XOR; - case OP_shl: - return JS_OVOP_SHL; - case OP_sar: - return JS_OVOP_SAR; - case OP_shr: - return JS_OVOP_SHR; - case OP_eq: - case OP_neq: - return JS_OVOP_EQ; - case OP_lt: - case OP_lte: - case OP_gt: - case OP_gte: - return JS_OVOP_LESS; - case OP_plus: - return JS_OVOP_POS; - case OP_neg: - return JS_OVOP_NEG; - case OP_inc: - return JS_OVOP_INC; - case OP_dec: - return JS_OVOP_DEC; - default: - abort(); - } -} - -/* return NULL if not present */ -static JSObject *find_binary_op(JSBinaryOperatorDef *def, - uint32_t operator_index, - JSOverloadableOperatorEnum op) -{ - JSBinaryOperatorDefEntry *ent; - int i; - for(i = 0; i < def->count; i++) { - ent = &def->tab[i]; - if (ent->operator_index == operator_index) - return ent->ops[op]; - } - return NULL; -} - -/* return -1 if exception, 0 if no operator overloading, 1 if - overloaded operator called */ -static __exception int js_call_binary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - JSValueConst op2, - OPCodeEnum op, - BOOL is_numeric, - int hint) -{ - JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; - JSOperatorSetData *opset1, *opset2; - JSOverloadableOperatorEnum ovop; - JSObject *p; - JSValueConst args[2]; - - if (!ctx->allow_operator_overloading) - return 0; - - opset2_obj = JS_UNDEFINED; - opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset1_obj)) - goto exception; - if (JS_IsUndefined(opset1_obj)) - return 0; - opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); - if (!opset1) - goto exception; - - opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset2_obj)) - goto exception; - if (JS_IsUndefined(opset2_obj)) { - JS_FreeValue(ctx, opset1_obj); - return 0; - } - opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); - if (!opset2) - goto exception; - - if (opset1->is_primitive && opset2->is_primitive) { - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - return 0; - } - - ovop = get_ovop_from_opcode(op); - - if (opset1->operator_counter == opset2->operator_counter) { - p = opset1->self_ops[ovop]; - } else if (opset1->operator_counter > opset2->operator_counter) { - p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); - } else { - p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); - } - if (!p) { - JS_ThrowTypeError(ctx, "operator %s: no function defined", - js_overloadable_operator_names[ovop]); - goto exception; - } - - if (opset1->is_primitive) { - if (is_numeric) { - new_op1 = JS_ToNumeric(ctx, op1); - } else { - new_op1 = JS_ToPrimitive(ctx, op1, hint); - } - if (JS_IsException(new_op1)) - goto exception; - } else { - new_op1 = JS_DupValue(ctx, op1); - } - - if (opset2->is_primitive) { - if (is_numeric) { - new_op2 = JS_ToNumeric(ctx, op2); - } else { - new_op2 = JS_ToPrimitive(ctx, op2, hint); - } - if (JS_IsException(new_op2)) { - JS_FreeValue(ctx, new_op1); - goto exception; - } - } else { - new_op2 = JS_DupValue(ctx, op2); - } - - /* XXX: could apply JS_ToPrimitive() if primitive type so that the - operator function does not get a value object */ - - method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { - args[0] = new_op2; - args[1] = new_op1; - } else { - args[0] = new_op1; - args[1] = new_op2; - } - ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); - JS_FreeValue(ctx, new_op1); - JS_FreeValue(ctx, new_op2); - if (JS_IsException(ret)) - goto exception; - if (ovop == JS_OVOP_EQ) { - BOOL res = JS_ToBoolFree(ctx, ret); - if (op == OP_neq) - res ^= 1; - ret = JS_NewBool(ctx, res); - } else if (ovop == JS_OVOP_LESS) { - if (JS_IsUndefined(ret)) { - ret = JS_FALSE; - } else { - BOOL res = JS_ToBoolFree(ctx, ret); - if (op == OP_lte || op == OP_gte) - res ^= 1; - ret = JS_NewBool(ctx, res); - } - } - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - *pret = ret; - return 1; - exception: - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - *pret = JS_UNDEFINED; - return -1; -} - -/* try to call the operation on the operatorSet field of 'obj'. Only - used for "/" and "**" on the BigInt prototype in math mode */ -static __exception int js_call_binary_op_simple(JSContext *ctx, - JSValue *pret, - JSValueConst obj, - JSValueConst op1, - JSValueConst op2, - OPCodeEnum op) -{ - JSValue opset1_obj, method, ret, new_op1, new_op2; - JSOperatorSetData *opset1; - JSOverloadableOperatorEnum ovop; - JSObject *p; - JSValueConst args[2]; - - opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset1_obj)) - goto exception; - if (JS_IsUndefined(opset1_obj)) - return 0; - opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); - if (!opset1) - goto exception; - ovop = get_ovop_from_opcode(op); - - p = opset1->self_ops[ovop]; - if (!p) { - JS_FreeValue(ctx, opset1_obj); - return 0; - } - - new_op1 = JS_ToNumeric(ctx, op1); - if (JS_IsException(new_op1)) - goto exception; - new_op2 = JS_ToNumeric(ctx, op2); - if (JS_IsException(new_op2)) { - JS_FreeValue(ctx, new_op1); - goto exception; - } - - method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - args[0] = new_op1; - args[1] = new_op2; - ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); - JS_FreeValue(ctx, new_op1); - JS_FreeValue(ctx, new_op2); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, opset1_obj); - *pret = ret; - return 1; - exception: - JS_FreeValue(ctx, opset1_obj); - *pret = JS_UNDEFINED; - return -1; -} - -/* return -1 if exception, 0 if no operator overloading, 1 if - overloaded operator called */ -static __exception int js_call_unary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - OPCodeEnum op) -{ - JSValue opset1_obj, method, ret; - JSOperatorSetData *opset1; - JSOverloadableOperatorEnum ovop; - JSObject *p; - - if (!ctx->allow_operator_overloading) - return 0; - - opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset1_obj)) - goto exception; - if (JS_IsUndefined(opset1_obj)) - return 0; - opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); - if (!opset1) - goto exception; - if (opset1->is_primitive) { - JS_FreeValue(ctx, opset1_obj); - return 0; - } - - ovop = get_ovop_from_opcode(op); - - p = opset1->self_ops[ovop]; - if (!p) { - JS_ThrowTypeError(ctx, "no overloaded operator %s", - js_overloadable_operator_names[ovop]); - goto exception; - } - method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, opset1_obj); - *pret = ret; - return 1; - exception: - JS_FreeValue(ctx, opset1_obj); - *pret = JS_UNDEFINED; - return -1; -} - -static int js_unary_arith_bigfloat(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bf_t a_s, *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) { - JS_FreeValue(ctx, op1); - return -1; - } - r = JS_GetBigFloat(res); - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, op1); - return -1; - } - ret = 0; - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_plus: - ret = bf_set(r, a); - break; - case OP_neg: - ret = bf_set(r, a); - bf_neg(r); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - if (unlikely(ret & BF_ST_MEM_ERROR)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; -} - -static int js_unary_arith_bigdecimal(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bfdec_t *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - - res = JS_NewBigDecimal(ctx); - if (JS_IsException(res)) { - JS_FreeValue(ctx, op1); - return -1; - } - r = JS_GetBigDecimal(res); - a = JS_ToBigDecimal(ctx, op1); - if (!a) { - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, op1); - return -1; - } - ret = 0; - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); - break; - case OP_plus: - ret = bfdec_set(r, a); - break; - case OP_neg: - ret = bfdec_set(r, a); - bfdec_neg(r); - break; - default: - abort(); - } - JS_FreeValue(ctx, op1); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; -} - -#endif /* CONFIG_BIGNUM */ - -static int js_unary_arith_bigint(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) -{ - bf_t a_s, *r, *a; - int ret, v; - JSValue res; - - if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigint argument with unary +"); - JS_FreeValue(ctx, op1); - return -1; - } - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) { - JS_FreeValue(ctx, op1); - return -1; - } - r = JS_GetBigInt(res); - a = JS_ToBigInt(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, op1); - return -1; - } - ret = 0; - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); - break; - case OP_plus: - ret = bf_set(r, a); - break; - case OP_neg: - ret = bf_set(r, a); - bf_neg(r); - break; - case OP_not: - ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); - bf_neg(r); - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeValue(ctx, op1); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - res = JS_CompactBigInt(ctx, res); - *pres = res; - return 0; -} - -static no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) -{ - JSValue op1; - int v; - uint32_t tag; - - op1 = sp[-1]; - /* fast path for float64 */ - if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) - goto handle_float64; -#ifdef CONFIG_BIGNUM - if (JS_IsObject(op1)) { - JSValue val; - int ret = js_call_unary_op_fallback(ctx, &val, op1, op); - if (ret < 0) - return -1; - if (ret) { - JS_FreeValue(ctx, op1); - sp[-1] = val; - return 0; - } - } -#endif - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) - goto exception; - tag = JS_VALUE_GET_TAG(op1); - switch(tag) { - case JS_TAG_INT: - { - int64_t v64; - v64 = JS_VALUE_GET_INT(op1); - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - v64 += v; - break; - case OP_plus: - break; - case OP_neg: - if (v64 == 0) { - sp[-1] = __JS_NewFloat64(ctx, -0.0); - return 0; - } else { - v64 = -v64; - } - break; - default: - abort(); - } - sp[-1] = JS_NewInt64(ctx, v64); - } - break; - case JS_TAG_BIG_INT: - handle_bigint: - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - case JS_TAG_BIG_DECIMAL: - if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; -#endif default: handle_float64: { double d; - if (is_math_mode(ctx)) - goto handle_bigint; d = JS_VALUE_GET_FLOAT64(op1); switch(op) { case OP_inc: @@ -12925,27 +14089,20 @@ static __exception int js_post_inc_slow(JSContext *ctx, static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { JSValue op1; - + op1 = sp[-1]; -#ifdef CONFIG_BIGNUM - if (JS_IsObject(op1)) { - JSValue val; - int ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); - if (ret < 0) - return -1; - if (ret) { - JS_FreeValue(ctx, op1); - sp[-1] = val; - return 0; - } - } -#endif op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; - if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) { + sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1)); + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + JSBigInt *r; + r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1)); + JS_FreeValue(ctx, op1); + if (!r) goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); } else { int32_t v1; if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) @@ -12958,328 +14115,6 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return -1; } -static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *r, *a, *b; - int ret; - JSValue res; - - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - goto fail; - a = JS_ToBigInt(ctx, &a_s, op1); - if (!a) - goto fail; - b = JS_ToBigInt(ctx, &b_s, op2); - if (!b) { - JS_FreeBigInt(ctx, a, &a_s); - goto fail; - } - r = JS_GetBigInt(res); - ret = 0; - switch(op) { - case OP_add: - ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_sub: - ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_mul: - ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_div: - if (!is_math_mode(ctx)) { - bf_t rem_s, *rem = &rem_s; - bf_init(ctx->bf_ctx, rem); - ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, - BF_RNDZ); - bf_delete(rem); - } else { - goto math_mode_div_pow; - } - break; -#ifdef CONFIG_BIGNUM - case OP_math_mod: - /* Euclidian remainder */ - ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, - BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP; - break; -#endif - case OP_mod: - ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, - BF_RNDZ) & BF_ST_INVALID_OP; - break; - case OP_pow: - if (b->sign) { - if (!is_math_mode(ctx)) { - ret = BF_ST_INVALID_OP; - } else { - math_mode_div_pow: -#ifdef CONFIG_BIGNUM - JS_FreeValue(ctx, res); - ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op); - if (ret != 0) { - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - return -1; - } else { - *pres = res; - return 0; - } - } - /* if no BigInt power operator defined, return a - bigfloat */ - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) { - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - goto fail; - } - r = JS_GetBigFloat(res); - if (op == OP_div) { - ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR; - } else { - ret = bf_pow(r, a, b, ctx->fp_env.prec, - ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR; - } - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; -#else - abort(); -#endif - } - } else { - ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); - } - break; - - /* logical operations */ - case OP_shl: - case OP_sar: - { - slimb_t v2; -#if LIMB_BITS == 32 - bf_get_int32(&v2, b, 0); - if (v2 == INT32_MIN) - v2 = INT32_MIN + 1; -#else - bf_get_int64(&v2, b, 0); - if (v2 == INT64_MIN) - v2 = INT64_MIN + 1; -#endif - if (op == OP_sar) - v2 = -v2; - ret = bf_set(r, a); - ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); - if (v2 < 0) { - ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); - } - } - break; - case OP_and: - ret = bf_logic_and(r, a, b); - break; - case OP_or: - ret = bf_logic_or(r, a, b); - break; - case OP_xor: - ret = bf_logic_xor(r, a, b); - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = JS_CompactBigInt(ctx, res); - return 0; - fail: - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return -1; -} - -#ifdef CONFIG_BIGNUM -static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *r, *a, *b; - int ret; - JSValue res; - - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) - goto fail; - r = JS_GetBigFloat(res); - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, res); - goto fail; - } - b = JS_ToBigFloat(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, res); - goto fail; - } - bf_init(ctx->bf_ctx, r); - switch(op) { - case OP_add: - ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_sub: - ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_mul: - ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_div: - ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_math_mod: - /* Euclidian remainder */ - ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, - BF_DIVREM_EUCLIDIAN); - break; - case OP_mod: - ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, - BF_RNDZ); - break; - case OP_pow: - ret = bf_pow(r, a, b, ctx->fp_env.prec, - ctx->fp_env.flags | BF_POW_JS_QUIRKS); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (unlikely(ret & BF_ST_MEM_ERROR)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; - fail: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return -1; -} - -/* b must be a positive integer */ -static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b) -{ - bfdec_t b1; - int32_t b2; - int ret; - - bfdec_init(b->ctx, &b1); - ret = bfdec_set(&b1, b); - if (ret) { - bfdec_delete(&b1); - return ret; - } - ret = bfdec_rint(&b1, BF_RNDZ); - if (ret) { - bfdec_delete(&b1); - return BF_ST_INVALID_OP; /* must be an integer */ - } - ret = bfdec_get_int32(&b2, &b1); - bfdec_delete(&b1); - if (ret) - return ret; /* overflow */ - if (b2 < 0) - return BF_ST_INVALID_OP; /* must be positive */ - return bfdec_pow_ui(r, a, b2); -} - -static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2) -{ - bfdec_t *r, *a, *b; - int ret; - JSValue res; - - res = JS_NewBigDecimal(ctx); - if (JS_IsException(res)) - goto fail; - r = JS_GetBigDecimal(res); - - a = JS_ToBigDecimal(ctx, op1); - if (!a) - goto fail; - b = JS_ToBigDecimal(ctx, op2); - if (!b) - goto fail; - switch(op) { - case OP_add: - ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_sub: - ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_mul: - ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_div: - ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ); - break; - case OP_math_mod: - /* Euclidian remainder */ - ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN); - break; - case OP_mod: - ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); - break; - case OP_pow: - ret = js_bfdec_pow(r, a, b); - break; - default: - abort(); - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (unlikely(ret)) { - JS_FreeValue(ctx, res); - throw_bf_exception(ctx, ret); - return -1; - } - *pres = res; - return 0; - fail: - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return -1; -} -#endif /* CONFIG_BIGNUM */ - static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { @@ -13297,28 +14132,50 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s d2 = JS_VALUE_GET_FLOAT64(op2); goto handle_float64; } - -#ifdef CONFIG_BIGNUM - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - JSValue res; - int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); - if (ret != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - goto exception; - } else { - sp[-2] = res; - return 0; + /* fast path for short big int operations */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + switch(op) { + case OP_sub: + v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2; + break; + case OP_mul: + v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2; + break; + case OP_div: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2); + return 0; + case OP_mod: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; } + sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2); + return 0; + case OP_pow: + goto slow_big_int; + default: + abort(); + } + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); } + return 0; } -#endif - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13343,34 +14200,14 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s break; case OP_mul: v = (int64_t)v1 * (int64_t)v2; - if (is_math_mode(ctx) && - (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) - goto handle_bigint; if (v == 0 && (v1 | v2) < 0) { sp[-2] = __JS_NewFloat64(ctx, -0.0); return 0; } break; case OP_div: - if (is_math_mode(ctx)) - goto handle_bigint; - sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); + sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); return 0; -#ifdef CONFIG_BIGNUM - case OP_math_mod: - if (unlikely(v2 == 0)) { - throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); - goto exception; - } - v = (int64_t)v1 % (int64_t)v2; - if (v < 0) { - if (v2 < 0) - v -= v2; - else - v += v2; - } - break; -#endif case OP_mod: if (v1 < 0 || v2 <= 0) { sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); @@ -13380,65 +14217,76 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } break; case OP_pow: - if (!is_math_mode(ctx)) { - sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); - return 0; - } else { - goto handle_bigint; - } - break; + sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); + return 0; default: abort(); } sp[-2] = JS_NewInt64(ctx, v); - } else -#ifdef CONFIG_BIGNUM - if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } else -#endif - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - handle_bigint: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } else { - double dr; - /* float64 result */ - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - handle_float64: - if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) - goto handle_bigint; + } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); switch(op) { + case OP_add: + r = js_bigint_add(ctx, p1, p2, 0); + break; case OP_sub: - dr = d1 - d2; + r = js_bigint_add(ctx, p1, p2, 1); break; case OP_mul: - dr = d1 * d2; + r = js_bigint_mul(ctx, p1, p2); break; case OP_div: - dr = d1 / d2; + r = js_bigint_divrem(ctx, p1, p2, FALSE); break; case OP_mod: - dr = fmod(d1, d2); + r = js_bigint_divrem(ctx, p1, p2, TRUE); + break; + case OP_pow: + r = js_bigint_pow(ctx, p1, p2); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + dr = d1 / d2; break; -#ifdef CONFIG_BIGNUM - case OP_math_mod: - d2 = fabs(d2); + case OP_mod: dr = fmod(d1, d2); - /* XXX: loss of accuracy if dr < 0 */ - if (dr < 0) - dr += d2; break; -#endif case OP_pow: dr = js_pow(d1, d2); break; @@ -13454,6 +14302,11 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s return -1; } +static inline BOOL tag_is_string(uint32_t tag) +{ + return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE; +} + static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) { JSValue op1, op2; @@ -13472,31 +14325,25 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) sp[-2] = __JS_NewFloat64(ctx, d1 + d2); return 0; } - - if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { -#ifdef CONFIG_BIGNUM - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && - tag2 != JS_TAG_STRING)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED && - tag1 != JS_TAG_STRING))) { - JSValue res; - int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add, - FALSE, HINT_NONE); - if (ret != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - goto exception; - } else { - sp[-2] = res; - return 0; - } - } + /* fast path for short bigint */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2; + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); } -#endif + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13512,7 +14359,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) tag2 = JS_VALUE_GET_NORM_TAG(op2); } - if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + if (tag_is_string(tag1) || tag_is_string(tag2)) { sp[-2] = JS_ConcatString(ctx, op1, op2); if (JS_IsException(sp[-2])) goto exception; @@ -13539,20 +14386,25 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) v2 = JS_VALUE_GET_INT(op2); v = (int64_t)v1 + (int64_t)v2; sp[-2] = JS_NewInt64(ctx, v); - } else -#ifdef CONFIG_BIGNUM - if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) - goto exception; - } else -#endif - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - handle_bigint: - if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + r = js_bigint_add(ctx, p1, p2, 0); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { double d1, d2; /* float64 result */ @@ -13562,8 +14414,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) } if (JS_ToFloat64Free(ctx, &d2, op2)) goto exception; - if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) - goto handle_bigint; sp[-2] = __JS_NewFloat64(ctx, d1 + d2); } return 0; @@ -13586,27 +14436,62 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); -#ifdef CONFIG_BIGNUM - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - JSValue res; - int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); - if (ret != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - goto exception; + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2, v; + js_sdlimb_t vd; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + /* bigint fast path */ + switch(op) { + case OP_and: + v = v1 & v2; + break; + case OP_or: + v = v1 | v2; + break; + case OP_xor: + v = v1 ^ v2; + break; + case OP_sar: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_shl; + } + bigint_sar: + v = v1 >> v2; + break; + case OP_shl: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_sar; + } + bigint_shl: + vd = (js_dlimb_t)v1 << v2; + if (likely(vd >= JS_SHORT_BIG_INT_MIN && + vd <= JS_SHORT_BIG_INT_MAX)) { + v = vd; } else { - sp[-2] = res; + JSBigInt *r = js_bigint_new_di(ctx, vd); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); return 0; } + break; + default: + abort(); } + sp[-2] = __JS_NewShortBigInt(ctx, v); + return 0; } -#endif - op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13618,22 +14503,52 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, goto exception; } - if (is_math_mode(ctx)) - goto bigint_op; - tag1 = JS_VALUE_GET_TAG(op1); tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } else { - bigint_op: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; + if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_and: + case OP_or: + case OP_xor: + r = js_bigint_logic(ctx, p1, p2, op); + break; + case OP_shl: + case OP_sar: + { + js_slimb_t shift; + shift = js_bigint_get_si_sat(p2); + if (shift > INT32_MAX) + shift = INT32_MAX; + else if (shift < -INT32_MAX) + shift = -INT32_MAX; + if (op == OP_sar) + shift = -shift; + if (shift >= 0) + r = js_bigint_shl(ctx, p1, shift); + else + r = js_bigint_shr(ctx, p1, -shift); + } + break; + default: + abort(); } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); } else { if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { JS_FreeValue(ctx, op2); @@ -13669,100 +14584,98 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, return -1; } -/* Note: also used for bigint */ -static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2) +/* op1 must be a bigint or int. */ +static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1, + JSValue op1) { - bf_t a_s, b_s, *a, *b; - int res; + JSBigInt *p1; - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigFloat(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return -1; - } - switch(op) { - case OP_lt: - res = bf_cmp_lt(a, b); /* if NaN return false */ - break; - case OP_lte: - res = bf_cmp_le(a, b); /* if NaN return false */ - break; - case OP_gt: - res = bf_cmp_lt(b, a); /* if NaN return false */ + switch(JS_VALUE_GET_TAG(op1)) { + case JS_TAG_INT: + p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1)); break; - case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ + case JS_TAG_SHORT_BIG_INT: + p1 = js_bigint_set_short(buf1, op1); break; - case OP_eq: - res = bf_cmp_eq(a, b); /* if NaN return false */ + case JS_TAG_BIG_INT: + p1 = JS_VALUE_GET_PTR(op1); break; default: abort(); } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return res; + return p1; } -#ifdef CONFIG_BIGNUM -static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2) +/* op1 and op2 must be numeric types and at least one must be a + bigint. No exception is generated. */ +static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) { - bfdec_t *a, *b; - int res; - - /* Note: binary floats are converted to bigdecimal with - toString(). It is not mathematically correct but is consistent - with the BigDecimal() constructor behavior */ - op1 = JS_ToBigDecimalFree(ctx, op1, TRUE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - return -1; - } - op2 = JS_ToBigDecimalFree(ctx, op2, TRUE); - if (JS_IsException(op2)) { + int res, val, tag1, tag2; + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) { + /* fast path */ + js_slimb_t v1, v2; + if (tag1 == JS_TAG_INT) + v1 = JS_VALUE_GET_INT(op1); + else + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (tag2 == JS_TAG_INT) + v2 = JS_VALUE_GET_INT(op2); + else + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + val = (v1 > v2) - (v1 < v2); + } else { + if (tag1 == JS_TAG_FLOAT64) { + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1)); + if (val == 2) + goto unordered; + val = -val; + } else if (tag2 == JS_TAG_FLOAT64) { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2)); + if (val == 2) { + unordered: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return FALSE; + } + } else { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_cmp(ctx, p1, p2); + } JS_FreeValue(ctx, op1); - return -1; + JS_FreeValue(ctx, op2); } - a = JS_ToBigDecimal(ctx, op1); /* cannot fail */ - b = JS_ToBigDecimal(ctx, op2); /* cannot fail */ - + switch(op) { case OP_lt: - res = bfdec_cmp_lt(a, b); /* if NaN return false */ + res = val < 0; break; case OP_lte: - res = bfdec_cmp_le(a, b); /* if NaN return false */ + res = val <= 0; break; case OP_gt: - res = bfdec_cmp_lt(b, a); /* if NaN return false */ + res = val > 0; break; case OP_gte: - res = bfdec_cmp_le(b, a); /* if NaN return false */ + res = val >= 0; break; case OP_eq: - res = bfdec_cmp_eq(a, b); /* if NaN return false */ + res = val == 0; break; default: abort(); } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); return res; } -#endif /* !CONFIG_BIGNUM */ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) @@ -13775,27 +14688,6 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, op2 = sp[-1]; tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); -#ifdef CONFIG_BIGNUM - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - JSValue ret; - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, - FALSE, HINT_NUMBER); - if (res != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (res < 0) { - goto exception; - } else { - sp[-2] = ret; - return 0; - } - } - } -#endif op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13809,11 +14701,13 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { - JSString *p1, *p2; - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = js_string_compare(ctx, p1, p2); + if (tag_is_string(tag1) && tag_is_string(tag2)) { + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + res = js_string_compare(ctx, JS_VALUE_GET_STRING(op1), + JS_VALUE_GET_STRING(op2)); + } else { + res = js_string_rope_compare(ctx, op1, op2, FALSE); + } switch(op) { case OP_lt: res = (res < 0); @@ -13836,17 +14730,20 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, /* fast path for float64/int */ goto float64_compare; } else { - if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || - (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && - !is_math_mode(ctx)) { - if (tag1 == JS_TAG_STRING) { + if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + tag_is_string(tag2)) || + ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && + tag_is_string(tag1)))) { + if (tag_is_string(tag1)) { op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) goto invalid_bigint_string; } - if (tag2 == JS_TAG_STRING) { + if (tag_is_string(tag2)) { op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { invalid_bigint_string: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13870,21 +14767,9 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); -#ifdef CONFIG_BIGNUM - if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); - if (res < 0) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2); - if (res < 0) - goto exception; - } else -#endif - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2); - if (res < 0) - goto exception; + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { + res = js_compare_bigint(ctx, op, op1, op2); } else { double d1, d2; @@ -13928,21 +14813,15 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, static BOOL tag_is_number(uint32_t tag) { - return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_FLOAT64 -#ifdef CONFIG_BIGNUM - || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL -#endif - ); + return (tag == JS_TAG_INT || + tag == JS_TAG_FLOAT64 || + tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT); } static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, BOOL is_neq) { JSValue op1, op2; -#ifdef CONFIG_BIGNUM - JSValue ret; -#endif int res; uint32_t tag1, tag2; @@ -13970,59 +14849,32 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, d2 = JS_VALUE_GET_INT(op2); } res = (d1 == d2); - } else -#ifdef CONFIG_BIGNUM - if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2); - if (res < 0) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2); - if (res < 0) - goto exception; - } else -#endif - { - res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2); - if (res < 0) - goto exception; + } else { + res = js_compare_bigint(ctx, OP_eq, op1, op2); } } else if (tag1 == tag2) { -#ifdef CONFIG_BIGNUM - if (tag1 == JS_TAG_OBJECT) { - /* try the fallback operator */ - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq, - FALSE, HINT_NONE); - if (res != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (res < 0) { - goto exception; - } else { - sp[-2] = ret; - return 0; - } - } - } -#endif res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { res = TRUE; - } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || - (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { + } else if (tag_is_string(tag1) && tag_is_string(tag2)) { + /* needed when comparing strings and ropes */ + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag_is_string(tag1) && tag_is_number(tag2)) || + (tag_is_string(tag2) && tag_is_number(tag1))) { - if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - !is_math_mode(ctx)) { - if (tag1 == JS_TAG_STRING) { + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { + if (tag_is_string(tag1)) { op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) goto invalid_bigint_string; } - if (tag2 == JS_TAG_STRING) { + if (tag_is_string(tag2)) { op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) { invalid_bigint_string: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -14042,7 +14894,7 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, goto exception; } } - res = js_strict_eq(ctx, op1, op2); + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); } else if (tag1 == JS_TAG_BOOL) { op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); goto redo; @@ -14050,25 +14902,9 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); goto redo; } else if ((tag1 == JS_TAG_OBJECT && - (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || + (tag_is_number(tag2) || tag_is_string(tag2) || tag2 == JS_TAG_SYMBOL)) || (tag2 == JS_TAG_OBJECT && - (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { -#ifdef CONFIG_BIGNUM - /* try the fallback operator */ - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq, - FALSE, HINT_NONE); - if (res != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (res < 0) { - goto exception; - } else { - sp[-2] = ret; - return 0; - } - } -#endif + (tag_is_number(tag1) || tag_is_string(tag1) || tag1 == JS_TAG_SYMBOL))) { op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -14119,10 +14955,10 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) JS_FreeValue(ctx, op1); goto exception; } - /* XXX: could forbid >>> in bignum mode */ - if (!is_math_mode(ctx) && - (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) { JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -14140,67 +14976,6 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) return -1; } -#ifdef CONFIG_BIGNUM -static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, - int64_t exponent) -{ - bf_t r_s, *r = &r_s; - double d; - int ret; - - /* always convert to Float64 */ - bf_init(ctx->bf_ctx, r); - ret = bf_mul_pow_radix(r, a, 10, exponent, - 53, bf_set_exp_bits(11) | BF_RNDN | - BF_FLAG_SUBNORMAL); - bf_get_float64(r, &d, BF_RNDN); - bf_delete(r); - if (ret & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - else - return __JS_NewFloat64(ctx, d); -} - -static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) -{ - bf_t a_s, *a, *r; - JSValue op1, op2, res; - int64_t e; - int ret; - - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) - return -1; - r = JS_GetBigFloat(res); - op1 = sp[-2]; - op2 = sp[-1]; - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, res); - return -1; - } - if (JS_IsBigInt(ctx, op2)) { - ret = JS_ToBigInt64(ctx, &e, op2); - } else { - ret = JS_ToInt64(ctx, &e, op2); - } - if (ret) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, res); - return -1; - } - - bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = res; - return 0; -} -#endif - /* XXX: Should take JSValueConst arguments */ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode) @@ -14225,14 +15000,15 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, res = (tag1 == tag2); break; case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: { - JSString *p1, *p2; - if (tag1 != tag2) { + if (!tag_is_string(tag2)) { res = FALSE; + } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + res = (js_string_compare(ctx, JS_VALUE_GET_STRING(op1), + JS_VALUE_GET_STRING(op2)) == 0); } else { - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = (js_string_compare(ctx, p1, p2) == 0); + res = (js_string_rope_compare(ctx, op1, op2, TRUE) == 0); } } break; @@ -14293,63 +15069,29 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, res = (d1 == d2); /* if NaN return false and +0 == -0 */ } goto done_no_free; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: { - bf_t a_s, *a, b_s, *b; - if (tag1 != tag2) { - res = FALSE; - break; - } - a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */ - b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */ - res = bf_cmp_eq(a, b); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p1, *p2; - const bf_t *a, *b; - if (tag1 != tag2) { - res = FALSE; - break; - } - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - a = &p1->num; - b = &p2->num; - if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { - if (eq_mode == JS_EQ_SAME_VALUE_ZERO && - a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) { - res = TRUE; - } else { - res = (bf_cmp_full(a, b) == 0); - } - } else { - res = bf_cmp_eq(a, b); - } - } - break; - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *p1, *p2; - const bfdec_t *a, *b; - if (tag1 != tag2) { + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + if (tag2 != JS_TAG_SHORT_BIG_INT && + tag2 != JS_TAG_BIG_INT) { res = FALSE; break; } - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - a = &p1->num; - b = &p2->num; - res = bfdec_cmp_eq(a, b); + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + res = (js_bigint_cmp(ctx, p1, p2) == 0); } break; -#endif default: res = FALSE; break; @@ -14360,9 +15102,16 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, return res; } -static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2) { - return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + return js_strict_eq2(ctx, + JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), + JS_EQ_STRICT); +} + +BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq(ctx, op1, op2); } static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) @@ -14372,6 +15121,11 @@ static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) JS_EQ_SAME_VALUE); } +BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_same_value(ctx, op1, op2); +} + static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_strict_eq2(ctx, @@ -14379,11 +15133,16 @@ static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op JS_EQ_SAME_VALUE_ZERO); } +BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_same_value_zero(ctx, op1, op2); +} + static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, BOOL is_neq) { BOOL res; - res = js_strict_eq(ctx, sp[-2], sp[-1]); + res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT); sp[-2] = JS_NewBool(ctx, res ^ is_neq); return 0; } @@ -14414,6 +15173,43 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp) return 0; } +static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + + op1 = sp[-2]; /* object */ + op2 = sp[-1]; /* field name or method function */ + + if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + if (JS_IsObject(op2)) { + /* method: use the brand */ + ret = JS_CheckBrand(ctx, op1, op2); + if (ret < 0) + return -1; + } else { + JSAtom atom; + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* field */ + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + p = JS_VALUE_GET_OBJ(op1); + prs = find_own_property(&pr, p, atom); + JS_FreeAtom(ctx, atom); + ret = (prs != NULL); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, JSAtom atom) { @@ -14455,17 +15251,10 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) tag = JS_VALUE_GET_NORM_TAG(op1); switch(tag) { + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: atom = JS_ATOM_bigint; break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - atom = JS_ATOM_bigfloat; - break; - case JS_TAG_BIG_DECIMAL: - atom = JS_ATOM_bigdecimal; - break; -#endif case JS_TAG_INT: case JS_TAG_FLOAT64: atom = JS_ATOM_number; @@ -14477,13 +15266,14 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) atom = JS_ATOM_boolean; break; case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: atom = JS_ATOM_string; break; case JS_TAG_OBJECT: { JSObject *p; p = JS_VALUE_GET_OBJ(op1); - if (unlikely(p->is_HTMLDDA)) + if (unlikely(p->is_HTMLDDA)) atom = JS_ATOM_undefined; else if (JS_IsFunction(ctx, op1)) atom = JS_ATOM_function; @@ -14526,21 +15316,15 @@ static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) return 0; } -static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ThrowTypeError(ctx, "invalid property access"); -} - /* XXX: not 100% compatible, but mozilla seems to use a similar implementation to ensure that caller in non strict mode does not throw (ES5 compatibility) */ -static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); - if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { - return js_throw_type_error(ctx, this_val, 0, NULL); + if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype || argc >= 1) { + return JS_ThrowTypeError(ctx, "invalid property access"); } return JS_UNDEFINED; } @@ -14556,11 +15340,16 @@ static JSValue js_function_proto_fileName(JSContext *ctx, } static JSValue js_function_proto_lineNumber(JSContext *ctx, - JSValueConst this_val) + JSValueConst this_val, int is_col) { JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); if (b && b->has_debug) { - return JS_NewInt32(ctx, b->debug.line_num); + int line_num, col_num; + line_num = find_line_num(ctx, b, -1, &col_num); + if (is_col) + return JS_NewInt32(ctx, col_num); + else + return JS_NewInt32(ctx, line_num); } return JS_UNDEFINED; } @@ -14604,16 +15393,16 @@ static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) /* add the length field (cannot fail) */ pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (unlikely(!pr)) + goto fail; pr->u.value = JS_NewInt32(ctx, argc); /* initialize the fast array part */ tab = NULL; if (argc > 0) { tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } + if (!tab) + goto fail; for(i = 0; i < argc; i++) { tab[i] = JS_DupValue(ctx, argv[i]); } @@ -14629,6 +15418,9 @@ static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) ctx->throw_type_error, ctx->throw_type_error, JS_PROP_HAS_GET | JS_PROP_HAS_SET); return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; } #define GLOBAL_VAR_OFFSET 0x40000000 @@ -14653,6 +15445,8 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, /* add the length field (cannot fail) */ pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (unlikely(!pr)) + goto fail; pr->u.value = JS_NewInt32(ctx, argc); for(i = 0; i < arg_count; i++) { @@ -14712,10 +15506,10 @@ static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst * static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) { - JSObject *p; + JSObject *p, *p1; JSPropertyEnum *tab_atom; int i; - JSValue enum_obj, obj1; + JSValue enum_obj; JSForInIterator *it; uint32_t tag, tab_atom_count; @@ -14738,40 +15532,16 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) it->is_array = FALSE; it->obj = obj; it->idx = 0; - p = JS_VALUE_GET_OBJ(enum_obj); - p->u.for_in_iterator = it; + it->tab_atom = NULL; + it->atom_count = 0; + it->in_prototype_chain = FALSE; + p1 = JS_VALUE_GET_OBJ(enum_obj); + p1->u.for_in_iterator = it; if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) return enum_obj; - /* fast path: assume no enumerable properties in the prototype chain */ - obj1 = JS_DupValue(ctx, obj); - for(;;) { - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - if (JS_IsException(obj1)) - goto fail; - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, - JS_VALUE_GET_OBJ(obj1), - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - if (tab_atom_count != 0) { - JS_FreeValue(ctx, obj1); - goto slow_path; - } - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - } - p = JS_VALUE_GET_OBJ(obj); - if (p->fast_array) { JSShape *sh; JSShapeProperty *prs; @@ -14783,61 +15553,90 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) } /* for fast arrays, we only store the number of elements */ it->is_array = TRUE; - it->array_length = p->u.array.count; + it->atom_count = p->u.array.count; } else { normal_case: if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) - goto fail; - for(i = 0; i < tab_atom_count; i++) { - JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, enum_obj, 0); + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); + it->tab_atom = tab_atom; + it->atom_count = tab_atom_count; } return enum_obj; +} - slow_path: - /* non enumerable properties hide the enumerables ones in the - prototype chain */ - obj1 = JS_DupValue(ctx, obj); +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */ +static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx, + JSValueConst enum_obj) +{ + JSObject *p; + JSForInIterator *it; + JSPropertyEnum *tab_atom; + uint32_t tab_atom_count, i; + JSValue obj1; + + p = JS_VALUE_GET_OBJ(enum_obj); + it = p->u.for_in_iterator; + + /* check if there are enumerable properties in the prototype chain (fast path) */ + obj1 = JS_DupValue(ctx, it->obj); for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, JS_VALUE_GET_OBJ(obj1), - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { JS_FreeValue(ctx, obj1); goto fail; } - for(i = 0; i < tab_atom_count; i++) { - JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, - (tab_atom[i].is_enumerable ? - JS_PROP_ENUMERABLE : 0)); + JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - if (JS_IsException(obj1)) - goto fail; /* must check for timeout to avoid infinite loop */ if (js_poll_interrupts(ctx)) { JS_FreeValue(ctx, obj1); goto fail; } } - return enum_obj; + JS_FreeValue(ctx, obj1); + return 1; - fail: - JS_FreeValue(ctx, enum_obj); - return JS_EXCEPTION; -} + slow_path: + /* add the visited properties, even if they are not enumerable */ + if (it->is_array) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(it->obj), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + goto fail; + } + it->is_array = FALSE; + it->tab_atom = tab_atom; + it->atom_count = tab_atom_count; + } -/* obj -> enum_obj */ -static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) -{ - sp[-1] = build_for_in_iterator(ctx, sp[-1]); - if (JS_IsException(sp[-1])) - return -1; + for(i = 0; i < it->atom_count; i++) { + if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0) + goto fail; + } return 0; + fail: + return -1; } /* enum_obj -> enum_obj value done */ @@ -14847,6 +15646,8 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) JSObject *p; JSAtom prop; JSForInIterator *it; + JSPropertyEnum *tab_atom; + uint32_t tab_atom_count; int ret; enum_obj = sp[-1]; @@ -14859,28 +15660,68 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) it = p->u.for_in_iterator; for(;;) { - if (it->is_array) { - if (it->idx >= it->array_length) - goto done; - prop = __JS_AtomFromUInt32(it->idx); - it->idx++; + if (it->idx >= it->atom_count) { + if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj)) + goto done; /* not an object */ + /* no more property in the current object: look in the prototype */ + if (!it->in_prototype_chain) { + ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj); + if (ret < 0) + return -1; + if (ret) + goto done; + it->in_prototype_chain = TRUE; + } + it->obj = JS_GetPrototypeFree(ctx, it->obj); + if (JS_IsException(it->obj)) + return -1; + if (JS_IsNull(it->obj)) + goto done; /* no more prototype */ + + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) + return -1; + + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(it->obj), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + return -1; + } + JS_FreePropertyEnum(ctx, it->tab_atom, it->atom_count); + it->tab_atom = tab_atom; + it->atom_count = tab_atom_count; + it->idx = 0; } else { - JSShape *sh = p->shape; - JSShapeProperty *prs; - if (it->idx >= sh->prop_count) - goto done; - prs = get_shape_prop(sh) + it->idx; - prop = prs->atom; - it->idx++; - if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) - continue; + if (it->is_array) { + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + BOOL is_enumerable; + prop = it->tab_atom[it->idx].atom; + is_enumerable = it->tab_atom[it->idx].is_enumerable; + it->idx++; + if (it->in_prototype_chain) { + /* slow case: we are in the prototype chain */ + ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop); + if (ret < 0) + return ret; + if (ret) + continue; /* already visited */ + /* add to the visited property list */ + if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL, + JS_PROP_ENUMERABLE) < 0) + return -1; + } + if (!is_enumerable) + continue; + } + /* check if the property was deleted */ + ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop); + if (ret < 0) + return ret; + if (ret) + break; } - /* check if the property was deleted */ - ret = JS_HasProperty(ctx, it->obj, prop); - if (ret < 0) - return ret; - if (ret) - break; } /* return the property */ sp[0] = JS_AtomToValue(ctx, prop); @@ -14983,6 +15824,7 @@ static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, return JS_EXCEPTION; } +/* Note: always return JS_UNDEFINED when *pdone = TRUE. */ static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, JSValueConst method, int argc, JSValueConst *argv, BOOL *pdone) @@ -14993,9 +15835,13 @@ static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); if (JS_IsException(obj)) goto fail; - if (done != 2) { - *pdone = done; + if (likely(done == 0)) { + *pdone = FALSE; return obj; + } else if (done != 2) { + JS_FreeValue(ctx, obj); + *pdone = TRUE; + return JS_UNDEFINED; } else { done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); if (JS_IsException(done_val)) @@ -15023,7 +15869,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, if (is_exception_pending) { ex_obj = ctx->rt->current_exception; - ctx->rt->current_exception = JS_NULL; + ctx->rt->current_exception = JS_UNINITIALIZED; res = -1; } else { ex_obj = JS_UNDEFINED; @@ -15103,6 +15949,21 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) return 0; } +static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp) +{ + JSValue obj, iter, next; + + sp[-1] = JS_UNDEFINED; /* disable the catch offset so that + exceptions do not close the iterator */ + iter = sp[-3]; + next = sp[-2]; + obj = JS_Call(ctx, next, iter, 0, NULL); + if (JS_IsException(obj)) + return -1; + sp[0] = obj; + return 0; +} + static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, BOOL *pdone) { @@ -15135,6 +15996,9 @@ static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) if (JS_IsException(value)) return -1; JS_FreeValue(ctx, obj); + /* put again the catch offset so that exceptions close the + iterator */ + sp[-2] = JS_NewCatchOffset(ctx, 0); sp[-1] = value; sp[0] = JS_NewBool(ctx, done); return 0; @@ -15204,7 +16068,7 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) int is_array_iterator; JSValue *arrp; uint32_t i, count32, pos; - + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { JS_ThrowInternalError(ctx, "invalid index for append"); return -1; @@ -15292,7 +16156,7 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, int ret, gpn_flags; JSPropertyDescriptor desc; BOOL is_enumerable; - + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) return 0; @@ -15313,7 +16177,7 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, gpn_flags)) return -1; - + for (i = 0; i < tab_atom_count; i++) { if (pexcl) { ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); @@ -15346,10 +16210,10 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, if (ret < 0) goto exception; } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); + JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count); return 0; exception: - js_free_prop_enum(ctx, tab_atom, tab_atom_count); + JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count); return -1; } @@ -15364,10 +16228,16 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, { JSVarRef *var_ref; struct list_head *el; + JSValue *pvalue; + + if (is_arg) + pvalue = &sf->arg_buf[var_idx]; + else + pvalue = &sf->var_buf[var_idx]; list_for_each(el, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + var_ref = list_entry(el, JSVarRef, var_ref_link); + if (var_ref->pvalue == pvalue) { var_ref->header.ref_count++; return var_ref; } @@ -15377,15 +16247,24 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, if (!var_ref) return NULL; var_ref->header.ref_count = 1; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); var_ref->is_detached = FALSE; - var_ref->is_arg = is_arg; - var_ref->var_idx = var_idx; - list_add_tail(&var_ref->header.link, &sf->var_ref_list); - if (is_arg) - var_ref->pvalue = &sf->arg_buf[var_idx]; - else - var_ref->pvalue = &sf->var_buf[var_idx]; - var_ref->value = JS_UNDEFINED; + list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list); + if (sf->js_mode & JS_MODE_ASYNC) { + /* The stack frame is detached and may be destroyed at any + time so its reference count must be increased. Calling + close_var_refs() when destroying the stack frame is not + possible because it would change the graph between the GC + objects. Another solution could be to temporarily detach + the JSVarRef of async functions during the GC. It would + have the advantage of allowing the release of unused stack + frames in a cycle. */ + var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame); + var_ref->async_func->header.ref_count++; + } else { + var_ref->async_func = NULL; + } + var_ref->pvalue = pvalue; return var_ref; } @@ -15613,37 +16492,36 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) { struct list_head *el, *el1; JSVarRef *var_ref; - int var_idx; list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - var_idx = var_ref->var_idx; - if (var_ref->is_arg) - var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); - else - var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); + var_ref = list_entry(el, JSVarRef, var_ref_link); + /* no need to unlink var_ref->var_ref_link as the list is never used afterwards */ + if (var_ref->async_func) + async_func_free(rt, var_ref->async_func); + var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue); var_ref->pvalue = &var_ref->value; /* the reference is no longer to a local variable */ var_ref->is_detached = TRUE; - add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); } } -static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) { + JSValue *pvalue; struct list_head *el, *el1; JSVarRef *var_ref; - int var_idx = idx; + pvalue = &sf->var_buf[var_idx]; list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { - var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); + var_ref = list_entry(el, JSVarRef, var_ref_link); + if (var_ref->pvalue == pvalue) { + list_del(&var_ref->var_ref_link); + if (var_ref->async_func) + async_func_free(ctx->rt, var_ref->async_func); + var_ref->value = JS_DupValue(ctx, *var_ref->pvalue); var_ref->pvalue = &var_ref->value; - list_del(&var_ref->header.link); /* the reference is no longer to a local variable */ var_ref->is_detached = TRUE; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); } } } @@ -15676,18 +16554,8 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, sf->prev_frame = prev_sf; rt->current_stack_frame = sf; ctx = p->u.cfunc.realm; /* change the current realm */ - -#ifdef CONFIG_BIGNUM - /* we only propagate the bignum mode as some runtime functions - test it */ - if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; - else - sf->js_mode = 0; -#else sf->js_mode = 0; -#endif - sf->cur_func = (JSValue)func_obj; + sf->cur_func = func_obj; sf->arg_count = argc; arg_buf = argv; @@ -15834,9 +16702,10 @@ typedef enum { OP_SPECIAL_OBJECT_IMPORT_META, } OPSpecialObjectEnum; -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 +#define FUNC_RET_INITIAL_YIELD 3 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, @@ -15864,7 +16733,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, #if SHORT_OPCODES #define def(id, size, n_pop, n_push, f) -#else +#else #define def(id, size, n_pop, n_push, f) && case_default, #endif #include "quickjs-opcode.h" @@ -15931,7 +16800,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sf->js_mode = b->js_mode; arg_buf = argv; sf->arg_count = argc; - sf->cur_func = (JSValue)func_obj; + sf->cur_func = func_obj; init_list_head(&sf->var_ref_list); var_refs = p->u.func.var_refs; @@ -15958,7 +16827,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sf->prev_frame = rt->current_stack_frame; rt->current_stack_frame = sf; ctx = b->realm; /* set the current realm */ - + restart: for(;;) { int call_argc; @@ -15969,6 +16838,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, *sp++ = JS_NewInt32(ctx, get_u32(pc)); pc += 4; BREAK; + CASE(OP_push_bigint_i32): + *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc)); + pc += 4; + BREAK; CASE(OP_push_const): *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); pc += 4; @@ -16008,6 +16881,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSValue val; + sf->cur_pc = pc; val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); if (unlikely(JS_IsException(val))) goto exception; @@ -16360,6 +17234,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, int magic; magic = get_u16(pc); pc += 2; + sf->cur_pc = pc; ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); if (unlikely(JS_IsException(ret_val))) @@ -16393,13 +17268,37 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_check_ctor): if (JS_IsUndefined(new_target)) { + non_ctor_call: JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); goto exception; } BREAK; + CASE(OP_init_ctor): + { + JSValue super, ret; + sf->cur_pc = pc; + if (JS_IsUndefined(new_target)) + goto non_ctor_call; + super = JS_GetPrototype(ctx, func_obj); + if (JS_IsException(super)) + goto exception; + ret = JS_CallConstructor2(ctx, super, new_target, argc, (JSValueConst *)argv); + JS_FreeValue(ctx, super); + if (JS_IsException(ret)) + goto exception; + *sp++ = ret; + } + BREAK; CASE(OP_check_brand): - if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) - goto exception; + { + int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); + if (ret < 0) + goto exception; + if (!ret) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + goto exception; + } + } BREAK; CASE(OP_add_brand): if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) @@ -16408,7 +17307,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_FreeValue(ctx, sp[-1]); sp -= 2; BREAK; - + CASE(OP_throw): JS_Throw(ctx, *--sp); goto exception; @@ -16449,7 +17348,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSValueConst obj; int scope_idx; call_argc = get_u16(pc); - scope_idx = get_u16(pc + 2) - 1; + scope_idx = get_u16(pc + 2) + ARG_SCOPE_END; pc += 4; call_argv = sp - call_argc; sf->cur_pc = pc; @@ -16480,8 +17379,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSValue *tab; JSValueConst obj; - scope_idx = get_u16(pc) - 1; + scope_idx = get_u16(pc) + ARG_SCOPE_END; pc += 2; + sf->cur_pc = pc; tab = build_arg_list(ctx, &len, sp[-1]); if (!tab) goto exception; @@ -16517,6 +17417,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_get_super): { JSValue proto; + sf->cur_pc = pc; proto = JS_GetPrototype(ctx, sp[-1]); if (JS_IsException(proto)) goto exception; @@ -16528,10 +17429,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_import): { JSValue val; - val = js_dynamic_import(ctx, sp[-1]); + sf->cur_pc = pc; + val = js_dynamic_import(ctx, sp[-2], sp[-1]); if (JS_IsException(val)) goto exception; + JS_FreeValue(ctx, sp[-2]); JS_FreeValue(ctx, sp[-1]); + sp--; sp[-1] = val; } BREAK; @@ -16542,6 +17446,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSAtom atom; atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; ret = JS_CheckGlobalVar(ctx, atom); if (ret < 0) @@ -16557,6 +17462,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSAtom atom; atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); if (unlikely(JS_IsException(val))) @@ -16572,6 +17478,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSAtom atom; atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); sp--; @@ -16586,6 +17493,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSAtom atom; atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; /* sp[-2] is JS_TRUE or JS_FALSE */ if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { @@ -16606,6 +17514,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); flags = pc[4]; pc += 5; + sf->cur_pc = pc; if (JS_CheckDefineGlobalVar(ctx, atom, flags)) goto exception; } @@ -16617,6 +17526,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); flags = pc[4]; pc += 5; + sf->cur_pc = pc; if (JS_DefineGlobalVar(ctx, atom, flags)) goto exception; } @@ -16628,6 +17538,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); flags = pc[4]; pc += 5; + sf->cur_pc = pc; if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -16821,6 +17732,19 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp++; } BREAK; + CASE(OP_get_loc_checkthis): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE); + goto exception; + } + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; CASE(OP_put_loc_check): { int idx; @@ -16852,7 +17776,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, int idx; idx = get_u16(pc); pc += 2; - close_lexical_var(ctx, sf, idx, FALSE); + close_lexical_var(ctx, sf, idx); } BREAK; @@ -16893,6 +17817,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSAtom atom; atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; if (JS_GetGlobalVarRef(ctx, atom, sp)) goto exception; @@ -16944,6 +17869,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, op1 = sp[-1]; pc += 4; + /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { res = JS_VALUE_GET_INT(op1); } else { @@ -17037,15 +17963,18 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_for_in_start): + sf->cur_pc = pc; if (js_for_in_start(ctx, sp)) goto exception; BREAK; CASE(OP_for_in_next): + sf->cur_pc = pc; if (js_for_in_next(ctx, sp)) goto exception; sp += 2; BREAK; CASE(OP_for_of_start): + sf->cur_pc = pc; if (js_for_of_start(ctx, sp, FALSE)) goto exception; sp += 1; @@ -17055,18 +17984,27 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { int offset = -3 - pc[0]; pc += 1; + sf->cur_pc = pc; if (js_for_of_next(ctx, sp, offset)) goto exception; sp += 2; } BREAK; + CASE(OP_for_await_of_next): + sf->cur_pc = pc; + if (js_for_await_of_next(ctx, sp)) + goto exception; + sp++; + BREAK; CASE(OP_for_await_of_start): + sf->cur_pc = pc; if (js_for_of_start(ctx, sp, TRUE)) goto exception; sp += 1; *sp++ = JS_NewCatchOffset(ctx, 0); BREAK; CASE(OP_iterator_get_value_done): + sf->cur_pc = pc; if (js_iterator_get_value_done(ctx, sp)) goto exception; sp += 1; @@ -17084,32 +18022,28 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_FreeValue(ctx, sp[-1]); /* drop the next method */ sp--; if (!JS_IsUndefined(sp[-1])) { + sf->cur_pc = pc; if (JS_IteratorClose(ctx, sp[-1], FALSE)) goto exception; JS_FreeValue(ctx, sp[-1]); } sp--; BREAK; - CASE(OP_iterator_close_return): + CASE(OP_nip_catch): { JSValue ret_val; - /* iter_obj next catch_offset ... ret_val -> - ret_eval iter_obj next catch_offset */ + /* catch_offset ... ret_val -> ret_eval */ ret_val = *--sp; while (sp > stack_buf && JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { JS_FreeValue(ctx, *--sp); } - if (unlikely(sp < stack_buf + 3)) { - JS_ThrowInternalError(ctx, "iterator_close_return"); + if (unlikely(sp == stack_buf)) { + JS_ThrowInternalError(ctx, "nip_catch"); JS_FreeValue(ctx, ret_val); goto exception; } - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = ret_val; - sp++; + sp[-1] = ret_val; } BREAK; @@ -17117,6 +18051,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, /* stack: iter_obj next catch_offset val */ { JSValue ret; + sf->cur_pc = pc; ret = JS_Call(ctx, sp[-3], sp[-4], 1, (JSValueConst *)(sp - 1)); if (JS_IsException(ret)) @@ -17133,6 +18068,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BOOL ret_flag; int flags; flags = *pc++; + sf->cur_pc = pc; method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? JS_ATOM_throw : JS_ATOM_return); if (JS_IsException(method)) @@ -17181,6 +18117,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; val = JS_GetProperty(ctx, sp[-1], atom); if (unlikely(JS_IsException(val))) goto exception; @@ -17196,6 +18133,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; val = JS_GetProperty(ctx, sp[-1], atom); if (unlikely(JS_IsException(val))) goto exception; @@ -17209,6 +18147,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSAtom atom; atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2], JS_PROP_THROW_STRICT); @@ -17223,7 +18162,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSAtom atom; JSValue val; - + atom = get_u32(pc); pc += 4; val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); @@ -17232,7 +18171,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, *sp++ = val; } BREAK; - + CASE(OP_get_private_field): { JSValue val; @@ -17308,6 +18247,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_set_proto): { JSValue proto; + sf->cur_pc = pc; proto = sp[-1]; if (JS_IsObject(proto) || JS_IsNull(proto)) { if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) @@ -17385,7 +18325,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { int class_flags; JSAtom atom; - + atom = get_u32(pc); class_flags = pc[4]; pc += 5; @@ -17400,6 +18340,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSValue val; + sf->cur_pc = pc; val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); JS_FreeValue(ctx, sp[-2]); sp[-2] = val; @@ -17413,6 +18354,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSValue val; + sf->cur_pc = pc; val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); sp[-1] = val; if (unlikely(JS_IsException(val))) @@ -17420,19 +18362,69 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; + CASE(OP_get_array_el3): + { + JSValue val; + + switch (JS_VALUE_GET_TAG(sp[-2])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + /* undefined and null are tested in JS_GetPropertyValue() */ + break; + default: + /* must be tested nefore JS_ToPropertyKey */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], JS_DupValue(ctx, sp[-1])); + *sp++ = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + CASE(OP_get_ref_value): { JSValue val; + JSAtom atom; + int ret; + + sf->cur_pc = pc; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom == JS_ATOM_NULL) + goto exception; if (unlikely(JS_IsUndefined(sp[-2]))) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + goto exception; + } + ret = JS_HasProperty(ctx, sp[-2], atom); + if (unlikely(ret <= 0)) { + if (ret < 0) { JS_FreeAtom(ctx, atom); + goto exception; } - goto exception; + if (is_strict_mode(ctx)) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + goto exception; + } + val = JS_UNDEFINED; + } else { + val = JS_GetProperty(ctx, sp[-2], atom); } - val = JS_GetPropertyValue(ctx, sp[-2], - JS_DupValue(ctx, sp[-1])); + JS_FreeAtom(ctx, atom); if (unlikely(JS_IsException(val))) goto exception; sp[0] = val; @@ -17444,6 +18436,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSValue val; JSAtom atom; + sf->cur_pc = pc; atom = JS_ValueToAtom(ctx, sp[-1]); if (unlikely(atom == JS_ATOM_NULL)) goto exception; @@ -17463,6 +18456,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { int ret; + sf->cur_pc = pc; ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-3]); sp -= 3; @@ -17473,24 +18467,36 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_put_ref_value): { - int ret, flags; - flags = JS_PROP_THROW_STRICT; + int ret; + JSAtom atom; + sf->cur_pc = pc; + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; if (unlikely(JS_IsUndefined(sp[-3]))) { if (is_strict_mode(ctx)) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); goto exception; } else { sp[-3] = JS_DupValue(ctx, ctx->global_obj); } - } else { - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; } - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + ret = JS_HasProperty(ctx, sp[-3], atom); + if (unlikely(ret <= 0)) { + if (unlikely(ret < 0)) { + JS_FreeAtom(ctx, atom); + goto exception; + } + if (is_strict_mode(ctx)) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + goto exception; + } + } + ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-3], JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); JS_FreeValue(ctx, sp[-3]); sp -= 3; if (unlikely(ret < 0)) @@ -17502,6 +18508,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { int ret; JSAtom atom; + sf->cur_pc = pc; if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { JS_ThrowTypeErrorNotAnObject(ctx); goto exception; @@ -17534,6 +18541,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_append): /* array pos enumobj -- array pos */ { + sf->cur_pc = pc; if (js_append_enumerate(ctx, sp)) goto exception; JS_FreeValue(ctx, *--sp); @@ -17549,6 +18557,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, int mask; mask = *pc++; + sf->cur_pc = pc; if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], sp[-1 - ((mask >> 2) & 7)], sp[-1 - ((mask >> 5) & 7)], 0)) @@ -17572,8 +18581,14 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2)); sp--; + } else if (JS_IsString(op1) && JS_IsString(op2)) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + sp--; + if (JS_IsException(sp[-1])) + goto exception; } else { add_slow: + sf->cur_pc = pc; if (js_add_slow(ctx, sp)) goto exception; sp--; @@ -17582,38 +18597,47 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_add_loc): { + JSValue op2; JSValue *pv; int idx; idx = *pc; pc += 1; + op2 = sp[-1]; pv = &var_buf[idx]; - if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) { int64_t r; - r = (int64_t)JS_VALUE_GET_INT(*pv) + - JS_VALUE_GET_INT(sp[-1]); + r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2); if (unlikely((int)r != r)) goto add_loc_slow; *pv = JS_NewInt32(ctx, r); sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) { + *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) + + JS_VALUE_GET_FLOAT64(op2)); + sp--; } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { - JSValue op1; - op1 = sp[-1]; sp--; - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) - goto exception; - op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); - if (JS_IsException(op1)) + sf->cur_pc = pc; + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) goto exception; - set_value(ctx, pv, op1); + if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) { + JS_FreeValue(ctx, op2); + } else { + op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2); + if (JS_IsException(op2)) + goto exception; + set_value(ctx, pv, op2); + } } else { JSValue ops[2]; add_loc_slow: /* In case of exception, js_add_slow frees ops[0] and ops[1], so we must duplicate *pv */ + sf->cur_pc = pc; ops[0] = JS_DupValue(ctx, *pv); - ops[1] = sp[-1]; + ops[1] = op2; sp--; if (js_add_slow(ctx, ops + 2)) goto exception; @@ -17655,11 +18679,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, v2 = JS_VALUE_GET_INT(op2); r = (int64_t)v1 * v2; if (unlikely((int)r != r)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH) && - (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) - goto binary_arith_slow; -#endif d = (double)r; goto mul_fp_res; } @@ -17671,10 +18690,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-2] = JS_NewInt32(ctx, r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; -#endif d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); mul_fp_res: sp[-2] = __JS_NewFloat64(ctx, d); @@ -17691,8 +18706,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, op2 = sp[-1]; if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { int v1, v2; - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); @@ -17703,9 +18716,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; CASE(OP_mod): -#ifdef CONFIG_BIGNUM - CASE(OP_math_mod): -#endif { JSValue op1, op2; op1 = sp[-2]; @@ -17728,6 +18738,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_pow): binary_arith_slow: + sf->cur_pc = pc; if (js_binary_arith_slow(ctx, sp, opcode)) goto exception; sp--; @@ -17741,6 +18752,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, tag = JS_VALUE_GET_TAG(op1); if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { } else { + sf->cur_pc = pc; if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; } @@ -17771,6 +18783,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, neg_fp_res: sp[-1] = __JS_NewFloat64(ctx, d); } else { + sf->cur_pc = pc; if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; } @@ -17788,6 +18801,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-1] = JS_NewInt32(ctx, val + 1); } else { inc_slow: + sf->cur_pc = pc; if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; } @@ -17805,6 +18819,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-1] = JS_NewInt32(ctx, val - 1); } else { dec_slow: + sf->cur_pc = pc; if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; } @@ -17812,6 +18827,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_post_inc): CASE(OP_post_dec): + sf->cur_pc = pc; if (js_post_inc_slow(ctx, sp, opcode)) goto exception; sp++; @@ -17832,6 +18848,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val + 1); } else { inc_loc_slow: + sf->cur_pc = pc; /* must duplicate otherwise the variable value may be destroyed before JS code accesses it */ op1 = JS_DupValue(ctx, op1); @@ -17857,6 +18874,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val - 1); } else { dec_loc_slow: + sf->cur_pc = pc; /* must duplicate otherwise the variable value may be destroyed before JS code accesses it */ op1 = JS_DupValue(ctx, op1); @@ -17873,6 +18891,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); } else { + sf->cur_pc = pc; if (js_not_slow(ctx, sp)) goto exception; } @@ -17888,28 +18907,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, uint32_t v1, v2; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - { - int64_t r; - if (unlikely(sf->js_mode & JS_MODE_MATH)) { - if (v2 > 0x1f) - goto shl_slow; - r = (int64_t)v1 << v2; - if ((int)r != r) - goto shl_slow; - } else { - v2 &= 0x1f; - } - } -#else v2 &= 0x1f; -#endif sp[-2] = JS_NewInt32(ctx, v1 << v2); sp--; } else { -#ifdef CONFIG_BIGNUM - shl_slow: -#endif + sf->cur_pc = pc; if (js_binary_logic_slow(ctx, sp, opcode)) goto exception; sp--; @@ -17924,13 +18926,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { uint32_t v2; v2 = JS_VALUE_GET_INT(op2); - /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ v2 &= 0x1f; sp[-2] = JS_NewUint32(ctx, (uint32_t)JS_VALUE_GET_INT(op1) >> v2); sp--; } else { + sf->cur_pc = pc; if (js_shr_slow(ctx, sp)) goto exception; sp--; @@ -17945,23 +18947,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { uint32_t v2; v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - if (unlikely(v2 > 0x1f)) { - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto sar_slow; - else - v2 &= 0x1f; - } -#else v2 &= 0x1f; -#endif sp[-2] = JS_NewInt32(ctx, (int)JS_VALUE_GET_INT(op1) >> v2); sp--; } else { -#ifdef CONFIG_BIGNUM - sar_slow: -#endif + sf->cur_pc = pc; if (js_binary_logic_slow(ctx, sp, opcode)) goto exception; sp--; @@ -17979,6 +18970,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_VALUE_GET_INT(op2)); sp--; } else { + sf->cur_pc = pc; if (js_binary_logic_slow(ctx, sp, opcode)) goto exception; sp--; @@ -17996,6 +18988,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_VALUE_GET_INT(op2)); sp--; } else { + sf->cur_pc = pc; if (js_binary_logic_slow(ctx, sp, opcode)) goto exception; sp--; @@ -18013,6 +19006,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_VALUE_GET_INT(op2)); sp--; } else { + sf->cur_pc = pc; if (js_binary_logic_slow(ctx, sp, opcode)) goto exception; sp--; @@ -18031,6 +19025,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ sp--; \ } else { \ + sf->cur_pc = pc; \ if (slow_call) \ goto exception; \ sp--; \ @@ -18047,19 +19042,20 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); -#ifdef CONFIG_BIGNUM - CASE(OP_mul_pow10): - if (rt->bigfloat_ops.mul_pow10(ctx, sp)) + CASE(OP_in): + sf->cur_pc = pc; + if (js_operator_in(ctx, sp)) goto exception; sp--; BREAK; -#endif - CASE(OP_in): - if (js_operator_in(ctx, sp)) + CASE(OP_private_in): + sf->cur_pc = pc; + if (js_operator_private_in(ctx, sp)) goto exception; sp--; BREAK; CASE(OP_instanceof): + sf->cur_pc = pc; if (js_operator_instanceof(ctx, sp)) goto exception; sp--; @@ -18076,6 +19072,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; CASE(OP_delete): + sf->cur_pc = pc; if (js_operator_delete(ctx, sp)) goto exception; sp--; @@ -18087,8 +19084,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); pc += 4; + sf->cur_pc = pc; - ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + ret = JS_DeleteGlobalVar(ctx, atom); if (unlikely(ret < 0)) goto exception; *sp++ = JS_NewBool(ctx, ret); @@ -18097,6 +19095,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_to_object): if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + sf->cur_pc = pc; ret_val = JS_ToObject(ctx, sp[-1]); if (JS_IsException(ret_val)) goto exception; @@ -18112,6 +19111,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, case JS_TAG_SYMBOL: break; default: + sf->cur_pc = pc; ret_val = JS_ToPropertyKey(ctx, sp[-1]); if (JS_IsException(ret_val)) goto exception; @@ -18121,26 +19121,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; - CASE(OP_to_propkey2): - /* must be tested first */ - if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { - JS_ThrowTypeError(ctx, "value has no property"); - goto exception; - } - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; #if 0 CASE(OP_to_string): if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { @@ -18157,7 +19137,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_with_delete_var): CASE(OP_with_make_ref): CASE(OP_with_get_ref): - CASE(OP_with_get_ref_undef): { JSAtom atom; int32_t diff; @@ -18167,6 +19146,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, diff = get_u32(pc + 4); is_with = pc[8]; pc += 9; + sf->cur_pc = pc; obj = sp[-1]; ret = JS_HasProperty(ctx, obj, atom); @@ -18182,13 +19162,34 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } switch (opcode) { case OP_with_get_var: - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; + /* in Object Environment Records, GetBindingValue() calls HasProperty() */ + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret <= 0)) { + if (ret < 0) + goto exception; + if (is_strict_mode(ctx)) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + val = JS_UNDEFINED; + } else { + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + } set_value(ctx, &sp[-1], val); break; - case OP_with_put_var: - /* XXX: check if strict mode */ + case OP_with_put_var: /* used e.g. in for in/of */ + /* in Object Environment Records, SetMutableBinding() calls HasProperty() */ + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret <= 0)) { + if (ret < 0) + goto exception; + if (is_strict_mode(ctx)) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + } ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj, JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-1]); @@ -18209,18 +19210,17 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, break; case OP_with_get_ref: /* produce a pair object/method on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - break; - case OP_with_get_ref_undef: - /* produce a pair undefined/function on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) + /* in Object Environment Records, GetBindingValue() calls HasProperty() */ + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_UNDEFINED; + if (!ret) { + val = JS_UNDEFINED; + } else { + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + } *sp++ = val; break; } @@ -18245,9 +19245,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); goto done_generator; CASE(OP_return_async): - CASE(OP_initial_yield): ret_val = JS_UNDEFINED; goto done_generator; + CASE(OP_initial_yield): + ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD); + goto done_generator; CASE(OP_nop): BREAK; @@ -18308,9 +19310,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, before if the exception happens in a bytecode operation */ sf->cur_pc = pc; - build_backtrace(ctx, rt->current_exception, NULL, 0, 0); + build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0); } - if (!JS_IsUncatchableError(ctx, rt->current_exception)) { + if (!rt->current_exception_is_uncatchable) { while (sp > stack_buf) { JSValue val = *--sp; JS_FreeValue(ctx, val); @@ -18323,7 +19325,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_IteratorClose(ctx, sp[-1], TRUE); } else { *sp++ = rt->current_exception; - rt->current_exception = JS_NULL; + rt->current_exception = JS_UNINITIALIZED; pc = b->byte_code_buf + pos; goto restart; } @@ -18375,7 +19377,7 @@ static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) { JSObject *p; JSContext *realm; - + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) return ctx; p = JS_VALUE_GET_OBJ(func_obj); @@ -18424,7 +19426,7 @@ static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, { JSValue proto, obj; JSContext *realm; - + if (JS_IsUndefined(ctor)) { proto = JS_DupValue(ctx, ctx->class_proto[class_id]); } else { @@ -18529,26 +19531,35 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, } /* JSAsyncFunctionState (used by generator and async functions) */ -static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, - JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) +static JSAsyncFunctionState *async_func_init(JSContext *ctx, + JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) { + JSAsyncFunctionState *s; JSObject *p; JSFunctionBytecode *b; JSStackFrame *sf; int local_count, i, arg_buf_len, n; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return NULL; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + sf = &s->frame; init_list_head(&sf->var_ref_list); p = JS_VALUE_GET_OBJ(func_obj); b = p->u.func.function_bytecode; - sf->js_mode = b->js_mode; + sf->js_mode = b->js_mode | JS_MODE_ASYNC; sf->cur_pc = b->byte_code_buf; arg_buf_len = max_int(b->arg_count, argc); local_count = arg_buf_len + b->var_count + b->stack_size; sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); - if (!sf->arg_buf) - return -1; + if (!sf->arg_buf) { + js_free(ctx, s); + return NULL; + } sf->cur_func = JS_DupValue(ctx, func_obj); s->this_val = JS_DupValue(ctx, this_obj); s->argc = argc; @@ -18560,38 +19571,17 @@ static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, n = arg_buf_len + b->var_count; for(i = argc; i < n; i++) sf->arg_buf[i] = JS_UNDEFINED; - return 0; -} - -static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, - JS_MarkFunc *mark_func) -{ - JSStackFrame *sf; - JSValue *sp; - - sf = &s->frame; - JS_MarkValue(rt, sf->cur_func, mark_func); - JS_MarkValue(rt, s->this_val, mark_func); - if (sf->cur_sp) { - /* if the function is running, cur_sp is not known so we - cannot mark the stack. Marking the variables is not needed - because a running function cannot be part of a removable - cycle */ - for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) - JS_MarkValue(rt, *sp, mark_func); - } + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + s->is_completed = FALSE; + return s; } -static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s) { - JSStackFrame *sf; + JSStackFrame *sf = &s->frame; JSValue *sp; - sf = &s->frame; - - /* close the closure variables. */ - close_var_refs(rt, sf); - if (sf->arg_buf) { /* cannot free the function if it is running */ assert(sf->cur_sp != NULL); @@ -18599,6 +19589,7 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) JS_FreeValueRT(rt, *sp); } js_free_rt(rt, sf->arg_buf); + sf->arg_buf = NULL; } JS_FreeValueRT(rt, sf->cur_func); JS_FreeValueRT(rt, s->this_val); @@ -18606,17 +19597,66 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) { - JSValue func_obj; + JSRuntime *rt = ctx->rt; + JSStackFrame *sf = &s->frame; + JSValue func_obj, ret; - if (js_check_stack_overflow(ctx->rt, 0)) - return JS_ThrowStackOverflow(ctx); + assert(!s->is_completed); + if (js_check_stack_overflow(ctx->rt, 0)) { + ret = JS_ThrowStackOverflow(ctx); + } else { + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR); + } + if (JS_IsException(ret) || JS_IsUndefined(ret)) { + if (JS_IsUndefined(ret)) { + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + } + /* end of execution */ + s->is_completed = TRUE; + + /* close the closure variables. */ + close_var_refs(rt, sf); - /* the tag does not matter provided it is not an object */ - func_obj = JS_MKPTR(JS_TAG_INT, s); - return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, - s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); + async_func_free_frame(rt, s); + } + return ret; } +static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + /* cannot close the closure variables here because it would + potentially modify the object graph */ + if (!s->is_completed) { + async_func_free_frame(rt, s); + } + + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + + remove_gc_object(&s->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) { + list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, s); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + if (--s->header.ref_count == 0) { + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&s->header.link); + list_add(&s->header.link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } +} /* Generators */ @@ -18630,14 +19670,17 @@ typedef enum JSGeneratorStateEnum { typedef struct JSGeneratorData { JSGeneratorStateEnum state; - JSAsyncFunctionState func_state; + JSAsyncFunctionState *func_state; } JSGeneratorData; static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) { if (s->state == JS_GENERATOR_STATE_COMPLETED) return; - async_func_free(rt, &s->func_state); + if (s->func_state) { + async_func_free(rt, s->func_state); + s->func_state = NULL; + } s->state = JS_GENERATOR_STATE_COMPLETED; } @@ -18662,9 +19705,9 @@ static void js_generator_mark(JSRuntime *rt, JSValueConst val, JSObject *p = JS_VALUE_GET_OBJ(val); JSGeneratorData *s = p->u.generator_data; - if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + if (!s || !s->func_state) return; - async_func_mark(rt, &s->func_state, mark_func); + mark_func(rt, &s->func_state->header); } /* XXX: use enum */ @@ -18683,10 +19726,10 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, *pdone = TRUE; if (!s) return JS_ThrowTypeError(ctx, "not a generator"); - sf = &s->func_state.frame; switch(s->state) { default: case JS_GENERATOR_STATE_SUSPENDED_START: + sf = &s->func_state->frame; if (magic == GEN_MAGIC_NEXT) { goto exec_no_arg; } else { @@ -18696,28 +19739,29 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, break; case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: case JS_GENERATOR_STATE_SUSPENDED_YIELD: + sf = &s->func_state->frame; /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ ret = JS_DupValue(ctx, argv[0]); if (magic == GEN_MAGIC_THROW && s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { JS_Throw(ctx, ret); - s->func_state.throw_flag = TRUE; + s->func_state->throw_flag = TRUE; } else { sf->cur_sp[-1] = ret; sf->cur_sp[0] = JS_NewInt32(ctx, magic); sf->cur_sp++; exec_no_arg: - s->func_state.throw_flag = FALSE; + s->func_state->throw_flag = FALSE; } s->state = JS_GENERATOR_STATE_EXECUTING; - func_ret = async_func_resume(ctx, &s->func_state); + func_ret = async_func_resume(ctx, s->func_state); s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; - if (JS_IsException(func_ret)) { - /* finalize the execution in case of exception */ + if (s->func_state->is_completed) { + /* finalize the execution in case of exception or normal return */ free_generator_stack(ctx, s); return func_ret; - } - if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + } else { + assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); /* get the returned yield value at the top of the stack */ ret = sf->cur_sp[-1]; sf->cur_sp[-1] = JS_UNDEFINED; @@ -18728,12 +19772,6 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, } else { *pdone = FALSE; } - } else { - /* end of iterator */ - ret = sf->cur_sp[-1]; - sf->cur_sp[-1] = JS_UNDEFINED; - JS_FreeValue(ctx, func_ret); - free_generator_stack(ctx, s); } break; case JS_GENERATOR_STATE_COMPLETED: @@ -18771,13 +19809,14 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, if (!s) return JS_EXCEPTION; s->state = JS_GENERATOR_STATE_SUSPENDED_START; - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); + if (!s->func_state) { s->state = JS_GENERATOR_STATE_COMPLETED; goto fail; } /* execute the function up to 'OP_initial_yield' */ - func_ret = async_func_resume(ctx, &s->func_state); + func_ret = async_func_resume(ctx, s->func_state); if (JS_IsException(func_ret)) goto fail; JS_FreeValue(ctx, func_ret); @@ -18795,36 +19834,12 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, /* AsyncFunction */ -static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) -{ - if (s->is_active) { - async_func_free(rt, &s->func_state); - s->is_active = FALSE; - } -} - -static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) -{ - js_async_function_terminate(rt, s); - JS_FreeValueRT(rt, s->resolving_funcs[0]); - JS_FreeValueRT(rt, s->resolving_funcs[1]); - remove_gc_object(&s->header); - js_free_rt(rt, s); -} - -static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) -{ - if (--s->header.ref_count == 0) { - js_async_function_free0(rt, s); - } -} - static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionData *s = p->u.async_function_data; + JSAsyncFunctionState *s = p->u.async_function_data; if (s) { - js_async_function_free(rt, s); + async_func_free(rt, s); } } @@ -18832,14 +19847,14 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionData *s = p->u.async_function_data; + JSAsyncFunctionState *s = p->u.async_function_data; if (s) { mark_func(rt, &s->header); } } static int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionData *s, + JSAsyncFunctionState *s, JSValue *resolving_funcs) { int i; @@ -18861,60 +19876,58 @@ static int js_async_function_resolve_create(JSContext *ctx, return 0; } -static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s) { JSValue func_ret, ret2; - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) { - JSValue error; - fail: - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - js_async_function_terminate(ctx->rt, s); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - } else { - JSValue value; - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; - if (JS_IsUndefined(func_ret)) { - /* function returned */ - ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&value); + func_ret = async_func_resume(ctx, s); + if (s->is_completed) { + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, value); - js_async_function_terminate(ctx->rt, s); } else { - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; + /* normal return */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&func_ret); + JS_FreeValue(ctx, func_ret); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } + } else { + JSValue value, promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; - /* await */ - JS_FreeValue(ctx, func_ret); /* not used */ - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - JS_FreeValue(ctx, value); - if (JS_IsException(promise)) - goto fail; - if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { - JS_FreeValue(ctx, promise); - goto fail; - } + value = s->frame.cur_sp[-1]; + s->frame.cur_sp[-1] = JS_UNDEFINED; - /* Note: no need to create 'thrownawayCapability' as in - the spec */ - for(i = 0; i < 2; i++) - resolving_funcs1[i] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs, - (JSValueConst *)resolving_funcs1); + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { JS_FreeValue(ctx, promise); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; + goto fail; } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs, + (JSValueConst *)resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; } } @@ -18925,7 +19938,7 @@ static JSValue js_async_function_resolve_call(JSContext *ctx, int flags) { JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSAsyncFunctionData *s = p->u.async_function_data; + JSAsyncFunctionState *s = p->u.async_function_data; BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; JSValueConst arg; @@ -18933,12 +19946,12 @@ static JSValue js_async_function_resolve_call(JSContext *ctx, arg = argv[0]; else arg = JS_UNDEFINED; - s->func_state.throw_flag = is_reject; + s->throw_flag = is_reject; if (is_reject) { JS_Throw(ctx, JS_DupValue(ctx, arg)); } else { /* return value of await */ - s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); + s->frame.cur_sp[-1] = JS_DupValue(ctx, arg); } js_async_function_resume(ctx, s); return JS_UNDEFINED; @@ -18949,32 +19962,21 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, int argc, JSValueConst *argv, int flags) { JSValue promise; - JSAsyncFunctionData *s; + JSAsyncFunctionState *s; - s = js_mallocz(ctx, sizeof(*s)); + s = async_func_init(ctx, func_obj, this_obj, argc, argv); if (!s) return JS_EXCEPTION; - s->header.ref_count = 1; - add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); - s->is_active = FALSE; - s->resolving_funcs[0] = JS_UNDEFINED; - s->resolving_funcs[1] = JS_UNDEFINED; promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); - if (JS_IsException(promise)) - goto fail; - - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - fail: - JS_FreeValue(ctx, promise); - js_async_function_free(ctx->rt, s); + if (JS_IsException(promise)) { + async_func_free(ctx->rt, s); return JS_EXCEPTION; } - s->is_active = TRUE; js_async_function_resume(ctx, s); - js_async_function_free(ctx->rt, s); + async_func_free(ctx->rt, s); return promise; } @@ -19003,7 +20005,8 @@ typedef struct JSAsyncGeneratorRequest { typedef struct JSAsyncGeneratorData { JSObject *generator; /* back pointer to the object (const) */ JSAsyncGeneratorStateEnum state; - JSAsyncFunctionState func_state; + /* func_state is NULL is state AWAITING_RETURN and COMPLETED */ + JSAsyncFunctionState *func_state; struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ } JSAsyncGeneratorData; @@ -19021,10 +20024,8 @@ static void js_async_generator_free(JSRuntime *rt, JS_FreeValueRT(rt, req->resolving_funcs[1]); js_free_rt(rt, req); } - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && - s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { - async_func_free(rt, &s->func_state); - } + if (s->func_state) + async_func_free(rt, s->func_state); js_free_rt(rt, s); } @@ -19051,9 +20052,8 @@ static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, JS_MarkValue(rt, req->resolving_funcs[0], mark_func); JS_MarkValue(rt, req->resolving_funcs[1], mark_func); } - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && - s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { - async_func_mark(rt, &s->func_state, mark_func); + if (s->func_state) { + mark_func(rt, &s->func_state->header); } } } @@ -19163,7 +20163,8 @@ static void js_async_generator_complete(JSContext *ctx, { if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - async_func_free(ctx->rt, &s->func_state); + async_func_free(ctx->rt, s->func_state); + s->func_state = NULL; } } @@ -19174,10 +20175,19 @@ static int js_async_generator_completed_return(JSContext *ctx, JSValue promise, resolving_funcs[2], resolving_funcs1[2]; int res; - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - if (JS_IsException(promise)) - return -1; + // Can fail looking up JS_ATOM_constructor when is_reject==0. + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, + /*is_reject*/0); + // A poisoned .constructor property is observable and the resulting + // exception should be delivered to the catch handler. + if (JS_IsException(promise)) { + JSValue err = JS_GetException(ctx); + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err, + /*is_reject*/1); + JS_FreeValue(ctx, err); + if (JS_IsException(promise)) + return -1; + } if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), resolving_funcs1, @@ -19225,7 +20235,6 @@ static void js_async_generator_resume_next(JSContext *ctx, } else if (next->completion_type == GEN_MAGIC_RETURN) { s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; js_async_generator_completed_return(ctx, s, next->result); - goto done; } else { js_async_generator_reject(ctx, s, next->result); } @@ -19236,30 +20245,38 @@ static void js_async_generator_resume_next(JSContext *ctx, if (next->completion_type == GEN_MAGIC_THROW && s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { JS_Throw(ctx, value); - s->func_state.throw_flag = TRUE; + s->func_state->throw_flag = TRUE; } else { /* 'yield' returns a value. 'yield *' also returns a value in case the 'throw' method is called */ - s->func_state.frame.cur_sp[-1] = value; - s->func_state.frame.cur_sp[0] = + s->func_state->frame.cur_sp[-1] = value; + s->func_state->frame.cur_sp[0] = JS_NewInt32(ctx, next->completion_type); - s->func_state.frame.cur_sp++; + s->func_state->frame.cur_sp++; exec_no_arg: - s->func_state.throw_flag = FALSE; + s->func_state->throw_flag = FALSE; } s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; resume_exec: - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) { - value = JS_GetException(ctx); - js_async_generator_complete(ctx, s); - js_async_generator_reject(ctx, s, value); - JS_FreeValue(ctx, value); - } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { - int func_ret_code; - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret = async_func_resume(ctx, s->func_state); + if (s->func_state->is_completed) { + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else { + /* end of function */ + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, func_ret, TRUE); + JS_FreeValue(ctx, func_ret); + } + } else { + int func_ret_code, ret; + assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); func_ret_code = JS_VALUE_GET_INT(func_ret); + value = s->func_state->frame.cur_sp[-1]; + s->func_state->frame.cur_sp[-1] = JS_UNDEFINED; switch(func_ret_code) { case FUNC_RET_YIELD: case FUNC_RET_YIELD_STAR: @@ -19271,20 +20288,17 @@ static void js_async_generator_resume_next(JSContext *ctx, JS_FreeValue(ctx, value); break; case FUNC_RET_AWAIT: - js_async_generator_await(ctx, s, value); + ret = js_async_generator_await(ctx, s, value); JS_FreeValue(ctx, value); + if (ret < 0) { + /* exception: throw it */ + s->func_state->throw_flag = TRUE; + goto resume_exec; + } goto done; default: abort(); } - } else { - assert(JS_IsUndefined(func_ret)); - /* end of function */ - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; - js_async_generator_complete(ctx, s); - js_async_generator_resolve(ctx, s, value, TRUE); - JS_FreeValue(ctx, value); } break; default: @@ -19318,12 +20332,12 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx, } else { /* restart function execution after await() */ assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); - s->func_state.throw_flag = is_reject; + s->func_state->throw_flag = is_reject; if (is_reject) { JS_Throw(ctx, JS_DupValue(ctx, arg)); } else { /* return value of await */ - s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); + s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg); } js_async_generator_resume_next(ctx, s); } @@ -19387,14 +20401,12 @@ static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst fun return JS_EXCEPTION; s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; init_list_head(&s->queue); - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); + if (!s->func_state) goto fail; - } - /* execute the function up to 'OP_initial_yield' (no yield nor await are possible) */ - func_ret = async_func_resume(ctx, &s->func_state); + func_ret = async_func_resume(ctx, s->func_state); if (JS_IsException(func_ret)) goto fail; JS_FreeValue(ctx, func_ret); @@ -19430,9 +20442,6 @@ enum { TOK_AND_ASSIGN, TOK_XOR_ASSIGN, TOK_OR_ASSIGN, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW_ASSIGN, -#endif TOK_POW_ASSIGN, TOK_LAND_ASSIGN, TOK_LOR_ASSIGN, @@ -19452,9 +20461,6 @@ enum { TOK_STRICT_NEQ, TOK_LAND, TOK_LOR, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW, -#endif TOK_POW, TOK_ARROW, TOK_ELLIPSIS, @@ -19533,7 +20539,8 @@ typedef struct BlockEnv { int drop_count; /* number of stack elements to drop */ int label_finally; /* -1 if none */ int scope_level; - int has_iterator; + uint8_t has_iterator : 1; + uint8_t is_regular_stmt : 1; /* i.e. not a loop statement */ } BlockEnv; typedef struct JSGlobalVar { @@ -19569,9 +20576,17 @@ typedef struct LabelSlot { typedef struct LineNumberSlot { uint32_t pc; - int line_num; + uint32_t source_pos; } LineNumberSlot; +typedef struct { + /* last source position */ + const uint8_t *ptr; + int line_num; + int col_num; + const uint8_t *buf_start; +} GetLineColCache; + typedef enum JSParseFunctionEnum { JS_PARSE_FUNC_STATEMENT, JS_PARSE_FUNC_VAR, @@ -19580,6 +20595,7 @@ typedef enum JSParseFunctionEnum { JS_PARSE_FUNC_GETTER, JS_PARSE_FUNC_SETTER, JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_STATIC_INIT, JS_PARSE_FUNC_CLASS_CONSTRUCTOR, JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, } JSParseFunctionEnum; @@ -19621,7 +20637,6 @@ typedef struct JSFunctionDef { BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ BOOL is_derived_class_constructor; BOOL in_function_body; - BOOL backtrace_barrier; JSFunctionKindEnum func_kind : 8; JSParseFunctionEnum func_type : 8; uint8_t js_mode; /* bitmap of JS_MODE_x */ @@ -19637,7 +20652,7 @@ typedef struct JSFunctionDef { int var_object_idx; /* -1 if none */ int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ int arguments_var_idx; /* -1 if none */ - int arguments_arg_idx; /* argument variable definition in argument scope, + int arguments_arg_idx; /* argument variable definition in argument scope, -1 if none */ int func_var_idx; /* variable containing the current function (-1 if none, only used if is_func_expr is true) */ @@ -19647,7 +20662,7 @@ typedef struct JSFunctionDef { int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ int home_object_var_idx; BOOL need_home_object; - + int scope_level; /* index into fd->scopes if the current lexical scope */ int scope_first; /* index into vd->vars of first lexically scoped variable */ int scope_size; /* allocated size of fd->scopes array */ @@ -19662,9 +20677,9 @@ typedef struct JSFunctionDef { DynBuf byte_code; int last_opcode_pos; /* -1 if no last opcode */ - int last_opcode_line_num; + const uint8_t *last_opcode_source_ptr; BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ - + LabelSlot *label_slots; int label_size; /* allocated size for label_slots[] */ int label_count; @@ -19691,20 +20706,23 @@ typedef struct JSFunctionDef { int line_number_last_pc; /* pc2line table */ + BOOL strip_debug : 1; /* strip all debug info (implies strip_source = TRUE) */ + BOOL strip_source : 1; /* strip only source code */ JSAtom filename; - int line_num; + uint32_t source_pos; /* pointer in the eval() source */ + GetLineColCache *get_line_col_cache; /* XXX: could remove to save memory */ DynBuf pc2line; char *source; /* raw source, utf-8 encoded */ int source_len; JSModuleDef *module; /* != NULL when parsing a module */ + BOOL has_await; /* TRUE if await is used (used in module eval) */ } JSFunctionDef; typedef struct JSToken { int val; - int line_num; /* line number of token start */ - const uint8_t *ptr; + const uint8_t *ptr; /* position in the source */ union { struct { JSValue str; @@ -19712,9 +20730,6 @@ typedef struct JSToken { } str; struct { JSValue val; -#ifdef CONFIG_BIGNUM - slimb_t exponent; /* may be != 0 only if val is a float */ -#endif } num; struct { JSAtom atom; @@ -19730,12 +20745,11 @@ typedef struct JSToken { typedef struct JSParseState { JSContext *ctx; - int last_line_num; /* line number of last token */ - int line_num; /* line number of current offset */ const char *filename; JSToken token; BOOL got_lf; /* true if got line feed before the current token */ const uint8_t *last_ptr; + const uint8_t *buf_start; const uint8_t *buf_ptr; const uint8_t *buf_end; @@ -19744,6 +20758,7 @@ typedef struct JSParseState { BOOL is_module; /* parsing a module */ BOOL allow_html_comments; BOOL ext_json; /* true if accepting JSON superset */ + GetLineColCache get_line_col_cache; } JSParseState; typedef struct JSOpCode { @@ -19872,21 +20887,95 @@ static void __attribute((unused)) dump_token(JSParseState *s, } } -int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) +/* return the zero based line and column number in the source. */ +/* Note: we no longer support '\r' as line terminator */ +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 int get_line_col_cached(GetLineColCache *s, int *pcol_num, const uint8_t *ptr) +{ + int line_num, col_num; + if (ptr >= s->ptr) { + line_num = get_line_col(&col_num, s->ptr, ptr - s->ptr); + if (line_num == 0) { + s->col_num += col_num; + } else { + s->line_num += line_num; + s->col_num = col_num; + } + } else { + line_num = get_line_col(&col_num, ptr, s->ptr - ptr); + if (line_num == 0) { + s->col_num -= col_num; + } else { + const uint8_t *p; + s->line_num -= line_num; + /* find the absolute column position */ + col_num = 0; + for(p = ptr - 1; p >= s->buf_start; p--) { + if (*p == '\n') { + break; + } else if (*p < 0x80 || *p >= 0xc0) { + col_num++; + } + } + s->col_num = col_num; + } + } + s->ptr = ptr; + *pcol_num = s->col_num; + return s->line_num; +} + +/* 'ptr' is the position of the error in the source */ +static int js_parse_error_v(JSParseState *s, const uint8_t *ptr, const char *fmt, va_list ap) { JSContext *ctx = s->ctx; + int line_num, col_num; + line_num = get_line_col(&col_num, s->buf_start, ptr - s->buf_start); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); + build_backtrace(ctx, ctx->rt->current_exception, s->filename, + line_num + 1, col_num + 1, 0); + return -1; +} + +static __attribute__((format(printf, 3, 4))) int js_parse_error_pos(JSParseState *s, const uint8_t *ptr, const char *fmt, ...) +{ va_list ap; - int backtrace_flags; + int ret; va_start(ap, fmt); - JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); + ret = js_parse_error_v(s, ptr, fmt, ap); va_end(ap); - backtrace_flags = 0; - if (s->cur_func && s->cur_func->backtrace_barrier) - backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; - build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num, - backtrace_flags); - return -1; + return ret; +} + +static __attribute__((format(printf, 2, 3))) int js_parse_error(JSParseState *s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = js_parse_error_v(s, s->token.ptr, fmt, ap); + va_end(ap); + return ret; } static int js_parse_expect(JSParseState *s, int tok) @@ -19922,6 +21011,7 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) { uint32_t c; StringBuffer b_s, *b = &b_s; + JSValue str; /* p points to the first byte of the template part */ if (string_buffer_init(s->ctx, b, 32)) @@ -19952,13 +21042,11 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) p++; c = '\n'; } - if (c == '\n') { - s->line_num++; - } else if (c >= 0x80) { + if (c >= 0x80) { const uint8_t *p_next; c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); if (c > 0x10FFFF) { - js_parse_error(s, "invalid UTF-8 sequence"); + js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence"); goto fail; } p = p_next; @@ -19966,9 +21054,12 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) if (string_buffer_putc(b, c)) goto fail; } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; s->token.val = TOK_TEMPLATE; s->token.u.str.sep = c; - s->token.u.str.str = string_buffer_end(b); + s->token.u.str.str = str; s->buf_ptr = p; return 0; @@ -19986,6 +21077,8 @@ static __exception int js_parse_string(JSParseState *s, int sep, int ret; uint32_t c; StringBuffer b_s, *b = &b_s; + const uint8_t *p_escape; + JSValue str; /* string */ if (string_buffer_init(s->ctx, b, 32)) @@ -19995,11 +21088,6 @@ static __exception int js_parse_string(JSParseState *s, int sep, goto invalid_char; c = *p; if (c < 0x20) { - if (!s->cur_func) { - if (do_throw) - js_parse_error(s, "invalid character in a JSON string"); - goto fail; - } if (sep == '`') { if (c == '\r') { if (p[1] == '\n') @@ -20019,6 +21107,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, break; } if (c == '\\') { + p_escape = p - 1; c = *p; /* XXX: need a specific JSON case to avoid accepting invalid escapes */ @@ -20041,13 +21130,9 @@ static __exception int js_parse_string(JSParseState *s, int sep, case '\n': /* ignore escaped newline sequence */ p++; - if (sep != '`') - s->line_num++; continue; default: if (c >= '0' && c <= '9') { - if (!s->cur_func) - goto invalid_escape; /* JSON case */ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') goto parse_escape; if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { @@ -20060,7 +21145,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, goto invalid_escape; } else { if (do_throw) - js_parse_error(s, "octal escape sequences are not allowed in strict mode"); + js_parse_error_pos(s, p_escape, "octal escape sequences are not allowed in strict mode"); } goto fail; } @@ -20080,7 +21165,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, if (ret == -1) { invalid_escape: if (do_throw) - js_parse_error(s, "malformed escape sequence in string literal"); + js_parse_error_pos(s, p_escape, "malformed escape sequence in string literal"); goto fail; } else if (ret < 0) { /* ignore the '\' (could output a warning) */ @@ -20101,9 +21186,12 @@ static __exception int js_parse_string(JSParseState *s, int sep, if (string_buffer_putc(b, c)) goto fail; } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; token->val = TOK_STRING; token->u.str.sep = c; - token->u.str.str = string_buffer_end(b); + token->u.str.str = str; *pp = p; return 0; @@ -20131,6 +21219,7 @@ static __exception int js_parse_regexp(JSParseState *s) StringBuffer b_s, *b = &b_s; StringBuffer b2_s, *b2 = &b2_s; uint32_t c; + JSValue body_str, flags_str; p = s->buf_ptr; p++; @@ -20179,16 +21268,16 @@ static __exception int js_parse_regexp(JSParseState *s) c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); if (c > 0x10FFFF) { invalid_utf8: - js_parse_error(s, "invalid UTF-8 sequence"); + js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence"); goto fail; } - p = p_next; /* LS or PS are considered as line terminator */ if (c == CP_LS || c == CP_PS) { eol_error: - js_parse_error(s, "unexpected line terminator in regexp"); + js_parse_error_pos(s, p - 1, "unexpected line terminator in regexp"); goto fail; } + p = p_next; } if (string_buffer_putc(b, c)) goto fail; @@ -20201,6 +21290,7 @@ static __exception int js_parse_regexp(JSParseState *s) if (c >= 0x80) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); if (c > 0x10FFFF) { + p++; goto invalid_utf8; } } @@ -20211,9 +21301,17 @@ static __exception int js_parse_regexp(JSParseState *s) p = p_next; } + body_str = string_buffer_end(b); + flags_str = string_buffer_end(b2); + if (JS_IsException(body_str) || + JS_IsException(flags_str)) { + JS_FreeValue(s->ctx, body_str); + JS_FreeValue(s->ctx, flags_str); + return -1; + } s->token.val = TOK_REGEXP; - s->token.u.regexp.body = string_buffer_end(b); - s->token.u.regexp.flags = string_buffer_end(b2); + s->token.u.regexp.body = body_str; + s->token.u.regexp.flags = flags_str; s->buf_ptr = p; return 0; fail: @@ -20227,7 +21325,7 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, { char *buf, *new_buf; size_t size, new_size; - + buf = *pbuf; size = *psize; if (size >= (SIZE_MAX / 3) * 2) @@ -20249,6 +21347,48 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, return 0; } +/* convert a TOK_IDENT to a keyword when needed */ +static void update_token_ident(JSParseState *s) +{ + if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || + (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && + (s->cur_func->js_mode & JS_MODE_STRICT)) || + (s->token.u.ident.atom == JS_ATOM_yield && + ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || + (s->token.u.ident.atom == JS_ATOM_await && + (s->is_module || + (s->cur_func->func_kind & JS_FUNC_ASYNC) || + s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || + s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { + if (s->token.u.ident.has_escape) { + s->token.u.ident.is_reserved = TRUE; + s->token.val = TOK_IDENT; + } else { + /* The keywords atoms are pre allocated */ + s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; + } + } +} + +/* if the current token is an identifier or keyword, reparse it + according to the current function type */ +static void reparse_ident_token(JSParseState *s) +{ + if (s->token.val == TOK_IDENT || + (s->token.val >= TOK_FIRST_KEYWORD && + s->token.val <= TOK_LAST_KEYWORD)) { + s->token.val = TOK_IDENT; + s->token.u.ident.is_reserved = FALSE; + update_token_ident(s); + } +} + /* 'c' is the first character. Return JS_ATOM_NULL in case of error */ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, BOOL *pident_has_escape, int c, BOOL is_private) @@ -20257,7 +21397,7 @@ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, char ident_buf[128], *buf; size_t ident_size, ident_pos; JSAtom atom; - + p = *pp; buf = ident_buf; ident_size = sizeof(ident_buf); @@ -20266,7 +21406,7 @@ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, buf[ident_pos++] = '#'; for(;;) { p1 = p; - + if (c < 128) { buf[ident_pos++] = c; } else { @@ -20304,18 +21444,16 @@ static __exception int next_token(JSParseState *s) int c; BOOL ident_has_escape; JSAtom atom; - + if (js_check_stack_overflow(s->ctx->rt, 0)) { return js_parse_error(s, "stack overflow"); } - + free_token(s, &s->token); p = s->last_ptr = s->buf_ptr; s->got_lf = FALSE; - s->last_line_num = s->token.line_num; redo: - s->token.line_num = s->line_num; s->token.ptr = p; c = *p; switch(c) { @@ -20345,7 +21483,6 @@ static __exception int next_token(JSParseState *s) p++; line_terminator: s->got_lf = TRUE; - s->line_num++; goto redo; case '\f': case '\v': @@ -20366,11 +21503,7 @@ static __exception int next_token(JSParseState *s) p += 2; break; } - if (*p == '\n') { - s->line_num++; - s->got_lf = TRUE; /* considered as LF for ASI */ - p++; - } else if (*p == '\r') { + if (*p == '\n' || *p == '\r') { s->got_lf = TRUE; /* considered as LF for ASI */ p++; } else if (*p >= 0x80) { @@ -20435,14 +21568,14 @@ static __exception int next_token(JSParseState *s) case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': + case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': + case 'Y': case 'Z': case '_': case '$': /* identifier */ @@ -20455,30 +21588,8 @@ static __exception int next_token(JSParseState *s) s->token.u.ident.atom = atom; s->token.u.ident.has_escape = ident_has_escape; s->token.u.ident.is_reserved = FALSE; - if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || - (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && - (s->cur_func->js_mode & JS_MODE_STRICT)) || - (s->token.u.ident.atom == JS_ATOM_yield && - ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || - (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && - !s->cur_func->in_function_body && s->cur_func->parent && - (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || - (s->token.u.ident.atom == JS_ATOM_await && - (s->is_module || - (((s->cur_func->func_kind & JS_FUNC_ASYNC) || - (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && - !s->cur_func->in_function_body && s->cur_func->parent && - (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) { - if (ident_has_escape) { - s->token.u.ident.is_reserved = TRUE; - s->token.val = TOK_IDENT; - } else { - /* The keywords atoms are pre allocated */ - s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; - } - } else { - s->token.val = TOK_IDENT; - } + s->token.val = TOK_IDENT; + update_token_ident(s); break; case '#': /* private name */ @@ -20526,32 +21637,17 @@ static __exception int next_token(JSParseState *s) goto parse_number; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': - case '9': + case '9': /* number */ parse_number: { JSValue ret; const uint8_t *p1; - int flags, radix; + int flags; flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | - ATOD_ACCEPT_UNDERSCORES; - flags |= ATOD_ACCEPT_SUFFIX; -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) { - flags |= ATOD_MODE_BIGINT; - if (s->cur_func->js_mode & JS_MODE_MATH) - flags |= ATOD_TYPE_BIG_FLOAT; - } -#endif - radix = 0; -#ifdef CONFIG_BIGNUM - s->token.u.num.exponent = 0; - ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix, - flags, &s->token.u.num.exponent); -#else - ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, + ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0, flags); -#endif if (JS_IsException(ret)) goto fail; /* reject `10instanceof Number` */ @@ -20605,8 +21701,8 @@ static __exception int next_token(JSParseState *s) p += 2; s->token.val = TOK_MINUS_ASSIGN; } else if (p[1] == '-') { - if (s->allow_html_comments && - p[2] == '>' && s->last_line_num != s->line_num) { + if (s->allow_html_comments && p[2] == '>' && + (s->got_lf || s->last_ptr == s->buf_start)) { /* Annex B: `-->` at beginning of line is an html comment end. It extends to the end of the line. */ @@ -20707,33 +21803,6 @@ static __exception int next_token(JSParseState *s) goto def_token; } break; -#ifdef CONFIG_BIGNUM - /* in math mode, '^' is the power operator. '^^' is always the - xor operator and '**' is always the power operator */ - case '^': - if (p[1] == '=') { - p += 2; - if (s->cur_func->js_mode & JS_MODE_MATH) - s->token.val = TOK_MATH_POW_ASSIGN; - else - s->token.val = TOK_XOR_ASSIGN; - } else if (p[1] == '^') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_XOR_ASSIGN; - } else { - p += 2; - s->token.val = '^'; - } - } else { - p++; - if (s->cur_func->js_mode & JS_MODE_MATH) - s->token.val = TOK_MATH_POW; - else - s->token.val = '^'; - } - break; -#else case '^': if (p[1] == '=') { p += 2; @@ -20742,7 +21811,6 @@ static __exception int next_token(JSParseState *s) goto def_token; } break; -#endif case '|': if (p[1] == '=') { p += 2; @@ -20784,7 +21852,7 @@ static __exception int next_token(JSParseState *s) case CP_LS: /* XXX: should avoid incrementing line_number, but needed to handle HTML comments */ - goto line_terminator; + goto line_terminator; default: if (lre_is_space(c)) { goto redo; @@ -20813,13 +21881,14 @@ static __exception int next_token(JSParseState *s) } /* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +/* XXX: accept unicode identifiers as JSON5 ? */ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) { const uint8_t *p; char ident_buf[128], *buf; size_t ident_size, ident_pos; JSAtom atom; - + p = *pp; buf = ident_buf; ident_size = sizeof(ident_buf); @@ -20827,8 +21896,7 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) for(;;) { buf[ident_pos++] = c; c = *p; - if (c >= 128 || - !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1)) + if (c >= 128 || !lre_is_id_continue_byte(c)) break; p++; if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { @@ -20846,22 +21914,196 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) return atom; } +static int json_parse_string(JSParseState *s, const uint8_t **pp, int sep) +{ + const uint8_t *p, *p_next; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + + p = *pp; + for(;;) { + if (p >= s->buf_end) { + goto end_of_input; + } + c = *p++; + if (c == sep) + break; + if (c < 0x20) { + js_parse_error_pos(s, p - 1, "Bad control character in string literal"); + goto fail; + } + if (c == '\\') { + 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 '\\': break; + case '/': break; + case 'u': + c = 0; + for(i = 0; i < 4; i++) { + int h = from_hex(*p++); + if (h < 0) { + js_parse_error_pos(s, p - 1, "Bad Unicode escape"); + goto fail; + } + c = (c << 4) | h; + } + break; + case '\n': + if (s->ext_json) + continue; + goto bad_escape; + case 'v': + if (s->ext_json) { + c = '\v'; + break; + } + goto bad_escape; + default: + if (c == sep) + break; + if (p > s->buf_end) + goto end_of_input; + bad_escape: + js_parse_error_pos(s, p - 1, "Bad escaped character"); + goto fail; + } + } else + if (c >= 0x80) { + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + js_parse_error_pos(s, p - 1, "Bad UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + s->token.val = TOK_STRING; + s->token.u.str.sep = sep; + s->token.u.str.str = string_buffer_end(b); + *pp = p; + return 0; + + end_of_input: + js_parse_error(s, "Unexpected end of JSON input"); + fail: + string_buffer_free(b); + return -1; +} + +static int json_parse_number(JSParseState *s, const uint8_t **pp) +{ + const uint8_t *p = *pp; + const uint8_t *p_start = p; + int radix; + double d; + JSATODTempMem atod_mem; + + if (*p == '+' || *p == '-') + p++; + + if (!is_digit(*p)) { + if (s->ext_json) { + if (strstart((const char *)p, "Infinity", (const char **)&p)) { +#ifdef _MSC_VER + d = INFINITY; +#else + d = 1.0 / 0.0; +#endif + if (*p_start == '-') + d = -d; + goto done; + } else if (strstart((const char *)p, "NaN", (const char **)&p)) { + d = NAN; + goto done; + } else if (*p != '.') { + goto unexpected_token; + } + } else { + goto unexpected_token; + } + } + + if (p[0] == '0') { + if (s->ext_json) { + /* also accepts base 16, 8 and 2 prefix for integers */ + radix = 10; + if (p[1] == 'x' || p[1] == 'X') { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O')) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B')) { + p += 2; + radix = 2; + } + if (radix != 10) { + /* prefix is present */ + if (to_digit(*p) >= radix) { + unexpected_token: + return js_parse_error_pos(s, p, "Unexpected token '%c'", *p); + } + d = js_atod((const char *)p_start, (const char **)&p, 0, + JS_ATOD_INT_ONLY | JS_ATOD_ACCEPT_BIN_OCT, &atod_mem); + goto done; + } + } + if (is_digit(p[1])) + return js_parse_error_pos(s, p, "Unexpected number"); + } + + while (is_digit(*p)) + p++; + + if (*p == '.') { + p++; + if (!is_digit(*p)) + return js_parse_error_pos(s, p, "Unterminated fractional number"); + while (is_digit(*p)) + p++; + } + if (*p == 'e' || *p == 'E') { + p++; + if (*p == '+' || *p == '-') + p++; + if (!is_digit(*p)) + return js_parse_error_pos(s, p, "Exponent part is missing a number"); + while (is_digit(*p)) + p++; + } + d = js_atod((const char *)p_start, NULL, 10, 0, &atod_mem); + done: + s->token.val = TOK_NUMBER; + s->token.u.num.val = JS_NewFloat64(s->ctx, d); + *pp = p; + return 0; +} + static __exception int json_next_token(JSParseState *s) { const uint8_t *p; int c; JSAtom atom; - + if (js_check_stack_overflow(s->ctx->rt, 0)) { return js_parse_error(s, "stack overflow"); } - + free_token(s, &s->token); p = s->last_ptr = s->buf_ptr; - s->last_line_num = s->token.line_num; redo: - s->token.line_num = s->line_num; s->token.ptr = p; c = *p; switch(c) { @@ -20879,7 +22121,8 @@ static __exception int json_next_token(JSParseState *s) } /* fall through */ case '\"': - if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) + p++; + if (json_parse_string(s, &p, c)) goto fail; break; case '\r': /* accept DOS and MAC newline sequences */ @@ -20889,7 +22132,6 @@ static __exception int json_next_token(JSParseState *s) /* fall thru */ case '\n': p++; - s->line_num++; goto redo; case '\f': case '\v': @@ -20919,12 +22161,7 @@ static __exception int json_next_token(JSParseState *s) p += 2; break; } - if (*p == '\n') { - s->line_num++; - p++; - } else if (*p == '\r') { - p++; - } else if (*p >= 0x80) { + if (*p >= 0x80) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); if (c == -1) { p++; /* skip invalid UTF-8 */ @@ -20965,17 +22202,16 @@ static __exception int json_next_token(JSParseState *s) case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': + case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': + case 'Y': case 'Z': case '_': case '$': - /* identifier : only pure ascii characters are accepted */ p++; atom = json_parse_ident(s, &p, c); if (atom == JS_ATOM_NULL) @@ -20986,39 +22222,23 @@ static __exception int json_next_token(JSParseState *s) s->token.val = TOK_IDENT; break; case '+': - if (!s->ext_json || !is_digit(p[1])) + if (!s->ext_json) goto def_token; goto parse_number; - case '0': - if (is_digit(p[1])) + case '.': + if (s->ext_json && is_digit(p[1])) + goto parse_number; + else goto def_token; - goto parse_number; case '-': - if (!is_digit(p[1])) - goto def_token; - goto parse_number; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': - case '9': + case '9': /* number */ parse_number: - { - JSValue ret; - int flags, radix; - if (!s->ext_json) { - flags = 0; - radix = 10; - } else { - flags = ATOD_ACCEPT_BIN_OCT; - radix = 0; - } - ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, - flags); - if (JS_IsException(ret)) - goto fail; - s->token.val = TOK_NUMBER; - s->token.u.num.val = ret; - } + if (json_parse_number(s, &p)) + goto fail; break; default: if (c >= 128) { @@ -21040,14 +22260,34 @@ static __exception int json_next_token(JSParseState *s) return -1; } -/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is - only set if TOK_IMPORT is returned */ -/* XXX: handle all unicode cases */ +static int match_identifier(const uint8_t *p, const char *s) { + uint32_t c; + while (*s) { + if ((uint8_t)*s++ != *p++) + return 0; + } + c = *p; + if (c >= 128) + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + return !lre_js_is_ident_next(c); +} + +/* simple_next_token() is used to check for the next token in simple cases. + It is only used for ':' and '=>', 'let' or 'function' look-ahead. + (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule() + Whitespace and comments are skipped correctly. + Then the next token is analyzed, only for specific words. + Return values: + - '\n' if !no_line_terminator + - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION + - TOK_IDENT is returned for other identifiers and keywords + - otherwise the next character or unicode codepoint is returned. + */ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) { const uint8_t *p; uint32_t c; - + /* skip spaces and comments */ p = *pp; for (;;) { @@ -21086,33 +22326,42 @@ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) if (*p == '>') return TOK_ARROW; break; + case 'i': + if (match_identifier(p, "n")) + return TOK_IN; + if (match_identifier(p, "mport")) { + *pp = p + 5; + return TOK_IMPORT; + } + return TOK_IDENT; + case 'o': + if (match_identifier(p, "f")) + return TOK_OF; + return TOK_IDENT; + case 'e': + if (match_identifier(p, "xport")) + return TOK_EXPORT; + return TOK_IDENT; + case 'f': + if (match_identifier(p, "unction")) + return TOK_FUNCTION; + return TOK_IDENT; + case '\\': + if (*p == 'u') { + if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE))) + return TOK_IDENT; + } + break; default: - if (lre_js_is_ident_first(c)) { - if (c == 'i') { - if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) { - return TOK_IN; - } - if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' && - p[3] == 'r' && p[4] == 't' && - !lre_js_is_ident_next(p[5])) { - *pp = p + 5; - return TOK_IMPORT; - } - } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) { - return TOK_OF; - } else if (c == 'e' && - p[0] == 'x' && p[1] == 'p' && p[2] == 'o' && - p[3] == 'r' && p[4] == 't' && - !lre_js_is_ident_next(p[5])) { - *pp = p + 5; - return TOK_EXPORT; - } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' && - p[2] == 'c' && p[3] == 't' && p[4] == 'i' && - p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) { - return TOK_FUNCTION; - } - return TOK_IDENT; + if (c >= 128) { + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p); + if (no_line_terminator && (c == CP_PS || c == CP_LS)) + return '\n'; } + if (lre_is_space(c)) + continue; + if (lre_js_is_ident_first(c)) + return TOK_IDENT; break; } return c; @@ -21125,6 +22374,31 @@ static int peek_token(JSParseState *s, BOOL no_line_terminator) return simple_next_token(&p, no_line_terminator); } +static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end) +{ + const uint8_t *p = *pp; + int c; + + if (p[0] == '#' && p[1] == '!') { + p += 2; + while (p < buf_end) { + if (*p == '\n' || *p == '\r') { + break; + } else if (*p >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + if (c == CP_LS || c == CP_PS) { + break; + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ + } + } else { + p++; + } + } + *pp = p; + } +} + /* return true if 'input' contains the source of a module (heuristic). 'input' must be a zero terminated. @@ -21135,6 +22409,8 @@ BOOL JS_DetectModule(const char *input, size_t input_len) { const uint8_t *p = (const uint8_t *)input; int tok; + + skip_shebang(&p, p + input_len); switch(simple_next_token(&p, FALSE)) { case TOK_IMPORT: tok = simple_next_token(&p, FALSE); @@ -21147,7 +22423,7 @@ BOOL JS_DetectModule(const char *input, size_t input_len) } static inline int get_prev_opcode(JSFunctionDef *fd) { - if (fd->last_opcode_pos < 0) + if (fd->last_opcode_pos < 0 || dbuf_error(&fd->byte_code)) return OP_invalid; else return fd->byte_code.buf[fd->last_opcode_pos]; @@ -21189,26 +22465,34 @@ static void emit_u32(JSParseState *s, uint32_t val) dbuf_put_u32(&s->cur_func->byte_code, val); } -static void emit_op(JSParseState *s, uint8_t val) +static void emit_source_pos(JSParseState *s, const uint8_t *source_ptr) { JSFunctionDef *fd = s->cur_func; DynBuf *bc = &fd->byte_code; - /* Use the line number of the last token used, not the next token, - nor the current offset in the source file. - */ - if (unlikely(fd->last_opcode_line_num != s->last_line_num)) { + if (unlikely(fd->last_opcode_source_ptr != source_ptr)) { dbuf_putc(bc, OP_line_num); - dbuf_put_u32(bc, s->last_line_num); - fd->last_opcode_line_num = s->last_line_num; + dbuf_put_u32(bc, source_ptr - s->buf_start); + fd->last_opcode_source_ptr = source_ptr; } +} + +static void emit_op(JSParseState *s, uint8_t val) +{ + JSFunctionDef *fd = s->cur_func; + DynBuf *bc = &fd->byte_code; + fd->last_opcode_pos = bc->size; dbuf_putc(bc, val); } static void emit_atom(JSParseState *s, JSAtom name) { - emit_u32(s, JS_DupAtom(s->ctx, name)); + DynBuf *bc = &s->cur_func->byte_code; + if (dbuf_realloc(bc, bc->size + 4)) + return; /* not enough memory : don't duplicate the atom */ + put_u32(bc->buf + bc->size, JS_DupAtom(s->ctx, name)); + bc->size += 4; } static int update_label(JSFunctionDef *s, int label, int delta) @@ -21222,29 +22506,41 @@ static int update_label(JSFunctionDef *s, int label, int delta) return ls->ref_count; } -static int new_label_fd(JSFunctionDef *fd, int label) +static int new_label_fd(JSFunctionDef *fd) { + int label; LabelSlot *ls; - if (label < 0) { - if (js_resize_array(fd->ctx, (void *)&fd->label_slots, - sizeof(fd->label_slots[0]), - &fd->label_size, fd->label_count + 1)) - return -1; - label = fd->label_count++; - ls = &fd->label_slots[label]; - ls->ref_count = 0; - ls->pos = -1; - ls->pos2 = -1; - ls->addr = -1; - ls->first_reloc = NULL; - } + if (js_resize_array(fd->ctx, (void *)&fd->label_slots, + sizeof(fd->label_slots[0]), + &fd->label_size, fd->label_count + 1)) + return -1; + label = fd->label_count++; + ls = &fd->label_slots[label]; + ls->ref_count = 0; + ls->pos = -1; + ls->pos2 = -1; + ls->addr = -1; + ls->first_reloc = NULL; return label; } static int new_label(JSParseState *s) { - return new_label_fd(s->cur_func, -1); + int label; + label = new_label_fd(s->cur_func); + if (unlikely(label < 0)) { + dbuf_set_error(&s->cur_func->byte_code); + } + return label; +} + +/* don't update the last opcode and don't emit line number info */ +static void emit_label_raw(JSParseState *s, int label) +{ + emit_u8(s, OP_label); + emit_u32(s, label); + s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; } /* return the label ID offset */ @@ -21264,8 +22560,11 @@ static int emit_label(JSParseState *s, int label) static int emit_goto(JSParseState *s, int opcode, int label) { if (js_is_live_code(s)) { - if (label < 0) + if (label < 0) { label = new_label(s); + if (label < 0) + return -1; + } emit_op(s, opcode); emit_u32(s, label); s->cur_func->label_slots[label].ref_count++; @@ -21278,7 +22577,7 @@ static int emit_goto(JSParseState *s, int opcode, int label) static int cpool_add(JSParseState *s, JSValue val) { JSFunctionDef *fd = s->cur_func; - + if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), &fd->cpool_size, fd->cpool_count + 1)) return -1; @@ -21664,7 +22963,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) { return js_parse_error(s, "invalid redefinition of a variable"); } - + if (fd->is_global_var) { JSGlobalVar *hf; hf = find_global_var(fd, name); @@ -21673,7 +22972,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, return js_parse_error(s, "invalid redefinition of global identifier"); } } - + if (fd->is_eval && (fd->eval_type == JS_EVAL_TYPE_GLOBAL || fd->eval_type == JS_EVAL_TYPE_MODULE) && @@ -21765,15 +23064,13 @@ static __exception int js_parse_expr(JSParseState *s); static __exception int js_parse_function_decl(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, - JSAtom func_name, const uint8_t *ptr, - int start_line); + JSAtom func_name, const uint8_t *ptr); static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); static __exception int js_parse_function_decl2(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, JSAtom func_name, const uint8_t *ptr, - int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd); static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); @@ -21896,7 +23193,6 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc) /* Resume TOK_TEMPLATE parsing (s->token.line_num and * s->token.ptr are OK) */ s->got_lf = FALSE; - s->last_line_num = s->token.line_num; if (js_parse_template_part(s, s->buf_ptr)) return -1; } @@ -21945,17 +23241,23 @@ static int __exception js_parse_property_name(JSParseState *s, BOOL is_non_reserved_ident; JSAtom name; int prop_type; - + prop_type = PROP_TYPE_IDENT; if (allow_method) { - if (token_is_pseudo_keyword(s, JS_ATOM_get) - || token_is_pseudo_keyword(s, JS_ATOM_set)) { + /* if allow_private is true (for class field parsing) and + get/set is following by ';' (or LF with ASI), then it + is a field name */ + if ((token_is_pseudo_keyword(s, JS_ATOM_get) || + token_is_pseudo_keyword(s, JS_ATOM_set)) && + (!allow_private || peek_token(s, TRUE) != '\n')) { /* get x(), set x() */ name = JS_DupAtom(s->ctx, s->token.u.ident.atom); if (next_token(s)) goto fail1; if (s->token.val == ':' || s->token.val == ',' || - s->token.val == '}' || s->token.val == '(') { + s->token.val == '}' || s->token.val == '(' || + s->token.val == '=' || + (s->token.val == ';' && allow_private)) { is_non_reserved_ident = TRUE; goto ident_found; } @@ -21971,7 +23273,8 @@ static int __exception js_parse_property_name(JSParseState *s, if (next_token(s)) goto fail1; if (s->token.val == ':' || s->token.val == ',' || - s->token.val == '}' || s->token.val == '(') { + s->token.val == '}' || s->token.val == '(' || + s->token.val == '=') { is_non_reserved_ident = TRUE; goto ident_found; } @@ -22011,21 +23314,7 @@ static int __exception js_parse_property_name(JSParseState *s, } else if (s->token.val == TOK_NUMBER) { JSValue val; val = s->token.u.num.val; -#ifdef CONFIG_BIGNUM - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - val = s->ctx->rt->bigfloat_ops. - mul_pow10_to_float64(s->ctx, &p->num, - s->token.u.num.exponent); - if (JS_IsException(val)) - goto fail; - name = JS_ValueToAtom(s->ctx, val); - JS_FreeValue(s->ctx, val); - } else -#endif - { - name = JS_ValueToAtom(s->ctx, val); - } + name = JS_ValueToAtom(s->ctx, val); if (name == JS_ATOM_NULL) goto fail; if (next_token(s)) @@ -22063,16 +23352,12 @@ static int __exception js_parse_property_name(JSParseState *s, } typedef struct JSParsePos { - int last_line_num; - int line_num; BOOL got_lf; const uint8_t *ptr; } JSParsePos; static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) { - sp->last_line_num = s->last_line_num; - sp->line_num = s->token.line_num; sp->ptr = s->token.ptr; sp->got_lf = s->got_lf; return 0; @@ -22080,8 +23365,6 @@ static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) { - s->token.line_num = sp->last_line_num; - s->line_num = sp->line_num; s->buf_ptr = sp->ptr; s->got_lf = sp->got_lf; return next_token(s); @@ -22114,6 +23397,17 @@ static BOOL is_regexp_allowed(int tok) #define SKIP_HAS_ELLIPSIS (1 << 1) #define SKIP_HAS_ASSIGNMENT (1 << 2) +static BOOL has_lf_in_range(const uint8_t *p1, const uint8_t *p2) +{ + const uint8_t *tmp; + if (p1 > p2) { + tmp = p1; + p1 = p2; + p2 = tmp; + } + return (memchr(p1, '\n', p2 - p1) != NULL); +} + /* XXX: improve speed with early bailout */ /* XXX: no longer works if regexps are present. Could use previous regexp parsing heuristics to handle most cases */ @@ -22124,7 +23418,8 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ JSParsePos pos; int last_tok, tok = TOK_EOF; int c, tok_len, bits = 0; - + const uint8_t *last_token_ptr; + /* protect from underflow */ state[level++] = 0; @@ -22155,7 +23450,6 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ /* Resume TOK_TEMPLATE parsing (s->token.line_num and * s->token.ptr are OK) */ s->got_lf = FALSE; - s->last_line_num = s->token.line_num; if (js_parse_template_part(s, s->buf_ptr)) goto done; goto handle_template; @@ -22171,7 +23465,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ if (level >= sizeof(state)) goto done; state[level++] = '`'; - } + } break; case TOK_EOF: goto done; @@ -22188,7 +23482,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ case '=': bits |= SKIP_HAS_ASSIGNMENT; break; - + case TOK_DIV_ASSIGN: tok_len = 2; goto parse_regexp; @@ -22212,6 +23506,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ } else { last_tok = s->token.val; } + last_token_ptr = s->token.ptr; if (next_token(s)) { /* XXX: should clear the exception generated by next_token() */ break; @@ -22220,7 +23515,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ tok = s->token.val; if (token_is_pseudo_keyword(s, JS_ATOM_of)) tok = TOK_OF; - if (no_line_terminator && s->last_line_num != s->token.line_num) + if (no_line_terminator && has_lf_in_range(last_token_ptr, s->token.ptr)) tok = '\n'; break; } @@ -22287,7 +23582,7 @@ static __exception int js_parse_object_literal(JSParseState *s) { JSAtom name = JS_ATOM_NULL; const uint8_t *start_ptr; - int start_line, prop_type; + int prop_type; BOOL has_proto; if (next_token(s)) @@ -22298,7 +23593,6 @@ static __exception int js_parse_object_literal(JSParseState *s) while (s->token.val != '}') { /* specific case for getter/setter */ start_ptr = s->token.ptr; - start_line = s->token.line_num; if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) @@ -22344,7 +23638,7 @@ static __exception int js_parse_object_literal(JSParseState *s) func_kind = JS_FUNC_ASYNC_GENERATOR; } if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL, - start_ptr, start_line)) + start_ptr)) goto fail; if (name == JS_ATOM_NULL) { emit_op(s, OP_define_method_computed); @@ -22360,6 +23654,10 @@ static __exception int js_parse_object_literal(JSParseState *s) } emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE); } else { + if (name == JS_ATOM_NULL) { + /* must be done before evaluating expr */ + emit_op(s, OP_to_propkey); + } if (js_parse_expect(s, ':')) goto fail; if (js_parse_assign_expr(s)) @@ -22398,56 +23696,84 @@ static __exception int js_parse_object_literal(JSParseState *s) } /* allow the 'in' binary operator */ -#define PF_IN_ACCEPTED (1 << 0) +#define PF_IN_ACCEPTED (1 << 0) /* allow function calls parsing in js_parse_postfix_expr() */ -#define PF_POSTFIX_CALL (1 << 1) -/* allow arrow functions parsing in js_parse_postfix_expr() */ -#define PF_ARROW_FUNC (1 << 2) +#define PF_POSTFIX_CALL (1 << 1) /* allow the exponentiation operator in js_parse_unary() */ -#define PF_POW_ALLOWED (1 << 3) +#define PF_POW_ALLOWED (1 << 2) /* forbid the exponentiation operator in js_parse_unary() */ -#define PF_POW_FORBIDDEN (1 << 4) +#define PF_POW_FORBIDDEN (1 << 3) static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); +static void emit_class_field_init(JSParseState *s); +static JSFunctionDef *js_new_function_def(JSContext *ctx, + JSFunctionDef *parent, + BOOL is_eval, + BOOL is_func_expr, + const char *filename, + const uint8_t *source_ptr, + GetLineColCache *get_line_col_cache); +static void emit_return(JSParseState *s, BOOL hasval); static __exception int js_parse_left_hand_side_expr(JSParseState *s) { return js_parse_postfix_expr(s, PF_POSTFIX_CALL); } -/* XXX: could generate specific bytecode */ static __exception int js_parse_class_default_ctor(JSParseState *s, BOOL has_super, JSFunctionDef **pfd) { - JSParsePos pos; - const char *str; - int ret, line_num; JSParseFunctionEnum func_type; - const uint8_t *saved_buf_end; - - js_parse_get_pos(s, &pos); + JSFunctionDef *fd = s->cur_func; + int idx; + + fd = js_new_function_def(s->ctx, fd, FALSE, FALSE, s->filename, + s->token.ptr, &s->get_line_col_cache); + if (!fd) + return -1; + + s->cur_func = fd; + fd->has_home_object = TRUE; + fd->super_allowed = TRUE; + fd->has_prototype = FALSE; + fd->has_this_binding = TRUE; + fd->new_target_allowed = TRUE; + + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; if (has_super) { - /* spec change: no argument evaluation */ - str = "(){super(...arguments);}"; + fd->is_derived_class_constructor = TRUE; + fd->super_call_allowed = TRUE; + fd->arguments_allowed = TRUE; + fd->has_arguments_binding = TRUE; func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; + emit_op(s, OP_init_ctor); + // TODO(bnoordhuis) roll into OP_init_ctor + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_class_field_init(s); } else { - str = "(){}"; func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; + /* error if not invoked as a constructor */ + emit_op(s, OP_check_ctor); + emit_class_field_init(s); } - line_num = s->token.line_num; - saved_buf_end = s->buf_end; - s->buf_ptr = (uint8_t *)str; - s->buf_end = (uint8_t *)(str + strlen(str)); - ret = next_token(s); - if (!ret) { - ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL, - JS_ATOM_NULL, (uint8_t *)str, - line_num, JS_PARSE_EXPORT_NONE, pfd); - } - s->buf_end = saved_buf_end; - ret |= js_parse_seek_token(s, &pos); - return ret; + + fd->func_kind = JS_FUNC_NORMAL; + fd->func_type = func_type; + emit_return(s, FALSE); + + s->cur_func = fd->parent; + if (pfd) + *pfd = fd; + + /* the real object will be set at the end of the compilation */ + idx = cpool_add(s, JS_NULL); + fd->parent_cpool_idx = idx; + + return 0; } /* find field in the current scope */ @@ -22472,7 +23798,7 @@ static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd, static void emit_class_field_init(JSParseState *s) { int label_next; - + emit_op(s, OP_scope_get_var); emit_atom(s, JS_ATOM_class_fields_init); emit_u16(s, s->cur_func->scope_level); @@ -22480,13 +23806,13 @@ static void emit_class_field_init(JSParseState *s) /* no need to call the class field initializer if not defined */ emit_op(s, OP_dup); label_next = emit_goto(s, OP_if_false, -1); - + emit_op(s, OP_scope_get_var); emit_atom(s, JS_ATOM_this); emit_u16(s, 0); - + emit_op(s, OP_swap); - + emit_op(s, OP_call_method); emit_u16(s, 0); @@ -22503,67 +23829,54 @@ static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name) typedef struct { JSFunctionDef *fields_init_fd; int computed_fields_count; - BOOL has_brand; + BOOL need_brand; int brand_push_pos; + BOOL is_static; } ClassFieldsDef; static __exception int emit_class_init_start(JSParseState *s, ClassFieldsDef *cf) { int label_add_brand; - + cf->fields_init_fd = js_parse_function_class_fields_init(s); if (!cf->fields_init_fd) return -1; s->cur_func = cf->fields_init_fd; - - /* XXX: would be better to add the code only if needed, maybe in a - later pass */ - emit_op(s, OP_push_false); /* will be patched later */ - cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; - label_add_brand = emit_goto(s, OP_if_false, -1); - - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_home_object); - emit_u16(s, 0); - - emit_op(s, OP_add_brand); - - emit_label(s, label_add_brand); - s->cur_func = s->cur_func->parent; - return 0; -} + if (!cf->is_static) { + /* add the brand to the newly created instance */ + /* XXX: would be better to add the code only if needed, maybe in a + later pass */ + emit_op(s, OP_push_false); /* will be patched later */ + cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; + label_add_brand = emit_goto(s, OP_if_false, -1); -static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf) -{ - if (!cf->has_brand) { - /* define the brand field in 'this' of the initializer */ - if (!cf->fields_init_fd) { - if (emit_class_init_start(s, cf)) - return -1; - } - /* patch the start of the function to enable the OP_add_brand code */ - cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; - - cf->has_brand = TRUE; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_home_object); + emit_u16(s, 0); + + emit_op(s, OP_add_brand); + + emit_label(s, label_add_brand); } + s->cur_func = s->cur_func->parent; return 0; } static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) { int cpool_idx; - + s->cur_func = cf->fields_init_fd; emit_op(s, OP_return_undef); s->cur_func = s->cur_func->parent; - + cpool_idx = cpool_add(s, JS_NULL); cf->fields_init_fd->parent_cpool_idx = cpool_idx; emit_op(s, OP_fclosure); @@ -22586,7 +23899,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, const uint8_t *class_start_ptr = s->token.ptr; const uint8_t *start_ptr; ClassFieldsDef class_fields[2]; - + /* classes are parsed and executed in strict mode */ saved_js_mode = fd->js_mode; fd->js_mode |= JS_MODE_STRICT; @@ -22649,7 +23962,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, } else { class_name1 = class_name; } - + emit_op(s, OP_define_class); emit_atom(s, class_name1); emit_u8(s, class_flags); @@ -22659,9 +23972,10 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, ClassFieldsDef *cf = &class_fields[i]; cf->fields_init_fd = NULL; cf->computed_fields_count = 0; - cf->has_brand = FALSE; + cf->need_brand = FALSE; + cf->is_static = i; } - + ctor_fd = NULL; while (s->token.val != '}') { if (s->token.val == ';') { @@ -22669,11 +23983,51 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, goto fail; continue; } - is_static = (s->token.val == TOK_STATIC); + is_static = FALSE; + if (s->token.val == TOK_STATIC) { + int next = peek_token(s, TRUE); + if (!(next == ';' || next == '}' || next == '(' || next == '=')) + is_static = TRUE; + } prop_type = -1; if (is_static) { if (next_token(s)) goto fail; + if (s->token.val == '{') { + ClassFieldsDef *cf = &class_fields[is_static]; + JSFunctionDef *init; + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + s->cur_func = cf->fields_init_fd; + /* XXX: could try to avoid creating a new function and + reuse 'fields_init_fd' with a specific 'var' + scope */ + // stack is now: + if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + JS_PARSE_EXPORT_NONE, &init) < 0) { + goto fail; + } + // stack is now: fclosure + push_scope(s); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + // stack is now: fclosure this + emit_op(s, OP_swap); + // stack is now: this fclosure + emit_op(s, OP_call_method); + emit_u16(s, 0); + // stack is now: returnvalue + emit_op(s, OP_drop); + // stack is now: + pop_scope(s); + s->cur_func = s->cur_func->parent; + continue; + } /* allow "static" field name */ if (s->token.val == ';' || s->token.val == '=') { is_static = FALSE; @@ -22691,7 +24045,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, } is_private = prop_type & PROP_TYPE_PRIVATE; prop_type &= ~PROP_TYPE_PRIVATE; - + if ((name == JS_ATOM_constructor && !is_static && prop_type != PROP_TYPE_IDENT) || (name == JS_ATOM_prototype && is_static) || @@ -22723,13 +24077,12 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0) goto fail; } - if (add_brand(s, &class_fields[is_static]) < 0) - goto fail; + class_fields[is_static].need_brand = TRUE; } if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set, JS_FUNC_NORMAL, JS_ATOM_NULL, - start_ptr, s->token.line_num, + start_ptr, JS_PARSE_EXPORT_NONE, &method_fd)) goto fail; if (is_private) { @@ -22740,7 +24093,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, if (is_set) { JSAtom setter_name; int ret; - + setter_name = get_private_setter_name(ctx, name); if (setter_name == JS_ATOM_NULL) goto fail; @@ -22766,7 +24119,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') { ClassFieldsDef *cf = &class_fields[is_static]; JSAtom field_var_name = JS_ATOM_NULL; - + /* class field */ /* XXX: spec: not consistent with method name checks */ @@ -22774,7 +24127,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, js_parse_error(s, "invalid field name"); goto fail; } - + if (is_private) { if (find_private_class_field(ctx, fd, name, fd->scope_level) >= 0) { @@ -22824,7 +24177,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, emit_atom(s, name); emit_u16(s, s->cur_func->scope_level); } - + if (s->token.val == '=') { if (next_token(s)) goto fail; @@ -22851,7 +24204,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, } else { JSParseFunctionEnum func_type; JSFunctionKindEnum func_kind; - + func_type = JS_PARSE_FUNC_METHOD; func_kind = JS_FUNC_NORMAL; if (prop_type == PROP_TYPE_STAR) { @@ -22871,10 +24224,9 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; } if (is_private) { - if (add_brand(s, &class_fields[is_static]) < 0) - goto fail; + class_fields[is_static].need_brand = TRUE; } - if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd)) + if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, JS_PARSE_EXPORT_NONE, &method_fd)) goto fail; if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR || func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { @@ -22925,7 +24277,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx); /* store the class source code in the constructor. */ - if (!(fd->js_mode & JS_MODE_STRIP)) { + if (!fd->strip_source) { js_free(ctx, ctor_fd->source); ctor_fd->source_len = s->buf_ptr - class_start_ptr; ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr, @@ -22938,12 +24290,29 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, if (next_token(s)) goto fail; - /* store the function to initialize the fields to that it can be - referenced by the constructor */ { ClassFieldsDef *cf = &class_fields[0]; int var_idx; - + + if (cf->need_brand) { + /* add a private brand to the prototype */ + emit_op(s, OP_dup); + emit_op(s, OP_null); + emit_op(s, OP_swap); + emit_op(s, OP_add_brand); + + /* define the brand field in 'this' of the initializer */ + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + /* patch the start of the function to enable the + OP_add_brand_instance code */ + cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; + } + + /* store the function to initialize the fields to that it can be + referenced by the constructor */ var_idx = define_var(s, fd, JS_ATOM_class_fields_init, JS_VAR_DEF_CONST); if (var_idx < 0) @@ -22961,16 +24330,13 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, /* drop the prototype */ emit_op(s, OP_drop); - /* initialize the static fields */ - if (class_fields[1].fields_init_fd != NULL) { - ClassFieldsDef *cf = &class_fields[1]; + if (class_fields[1].need_brand) { + /* add a private brand to the class */ emit_op(s, OP_dup); - emit_class_init_end(s, cf); - emit_op(s, OP_call_method); - emit_u16(s, 0); - emit_op(s, OP_drop); + emit_op(s, OP_dup); + emit_op(s, OP_add_brand); } - + if (class_name != JS_ATOM_NULL) { /* store the class name in the scoped class name variable (it is independent from the class statement variable @@ -22980,6 +24346,17 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, emit_atom(s, class_name); emit_u16(s, fd->scope_level); } + + /* initialize the static fields */ + if (class_fields[1].fields_init_fd != NULL) { + ClassFieldsDef *cf = &class_fields[1]; + emit_op(s, OP_dup); + emit_class_init_end(s, cf); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_drop); + } + pop_scope(s); pop_scope(s); @@ -23228,6 +24605,8 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, switch(opcode) { case OP_scope_get_var: label = new_label(s); + if (label < 0) + return -1; emit_op(s, OP_scope_make_ref); emit_atom(s, name); emit_u32(s, label); @@ -23246,10 +24625,7 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, emit_u16(s, scope); break; case OP_get_array_el: - /* XXX: replace by a single opcode ? */ - emit_op(s, OP_to_propkey2); - emit_op(s, OP_dup2); - emit_op(s, OP_get_array_el); + emit_op(s, OP_get_array_el3); break; case OP_get_super_value: emit_op(s, OP_to_propkey); @@ -23263,6 +24639,8 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, switch(opcode) { case OP_scope_get_var: label = new_label(s); + if (label < 0) + return -1; emit_op(s, OP_scope_make_ref); emit_atom(s, name); emit_u32(s, label); @@ -23270,11 +24648,7 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, update_label(fd, label, 1); opcode = OP_get_ref_value; break; - case OP_get_array_el: - emit_op(s, OP_to_propkey2); - break; - case OP_get_super_value: - emit_op(s, OP_to_propkey); + default: break; } } @@ -23374,7 +24748,7 @@ static void put_lvalue(JSParseState *s, int opcode, int scope, default: break; } - + switch(opcode) { case OP_scope_get_var: /* val -- */ assert(special == PUT_LVALUE_NOKEEP || @@ -23428,7 +24802,7 @@ static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) { JSFunctionDef *fd = s->cur_func; JSVarDefEnum var_def_type; - + if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { return js_parse_error(s, "yield is a reserved identifier"); } @@ -23436,7 +24810,7 @@ static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) && (fd->js_mode & JS_MODE_STRICT)) { return js_parse_error(s, "invalid variable name in strict mode"); } - if ((name == JS_ATOM_let || name == JS_ATOM_undefined) + if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) { return js_parse_error(s, "invalid lexical variable name"); } @@ -23506,6 +24880,25 @@ static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) return js_parse_error(s, "duplicate parameter names not allowed in this context"); } +/* tok = TOK_VAR, TOK_LET or TOK_CONST. Return whether a reference + must be taken to the variable for proper 'with' or global variable + evaluation */ +/* Note: this function is needed only because variable references are + not yet optimized in destructuring */ +static BOOL need_var_reference(JSParseState *s, int tok) +{ + JSFunctionDef *fd = s->cur_func; + if (tok != TOK_VAR) + return FALSE; /* no reference for let/const */ + if (fd->js_mode & JS_MODE_STRICT) { + if (!fd->is_global_var) + return FALSE; /* local definitions in strict mode in function or direct eval */ + if (s->is_module) + return FALSE; /* in a module global variables are like closure variables */ + } + return TRUE; +} + static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) { JSAtom name; @@ -23532,14 +24925,14 @@ static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) present at the top level. */ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, int hasval, int has_ellipsis, - BOOL allow_initializer) + BOOL allow_initializer, BOOL export_flag) { int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; int start_addr, assign_addr; JSAtom prop_name, var_name; int opcode, scope, tok1, skip_bits; BOOL has_initializer; - + if (has_ellipsis < 0) { /* pre-parse destructuration target for spread detection */ js_parse_skip_parens_token(s, &skip_bits, FALSE); @@ -23587,14 +24980,23 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, var_name = js_parse_destructuring_var(s, tok, is_arg); if (var_name == JS_ATOM_NULL) return -1; - opcode = OP_scope_get_var; - scope = s->cur_func->scope_level; - label_lvalue = -1; - depth_lvalue = 0; + if (need_var_reference(s, tok)) { + /* Must make a reference for proper `with` semantics */ + emit_op(s, OP_scope_get_var); + emit_atom(s, var_name); + emit_u16(s, s->cur_func->scope_level); + JS_FreeAtom(s->ctx, var_name); + goto lvalue0; + } else { + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + } } else { if (js_parse_left_hand_side_expr(s)) return -1; - + lvalue0: if (get_lvalue(s, &opcode, &scope, &var_name, &label_lvalue, &depth_lvalue, FALSE, '{')) return -1; @@ -23612,10 +25014,6 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (prop_type < 0) return -1; var_name = JS_ATOM_NULL; - opcode = OP_scope_get_var; - scope = s->cur_func->scope_level; - label_lvalue = -1; - depth_lvalue = 0; if (prop_type == PROP_TYPE_IDENT) { if (next_token(s)) goto prop_error; @@ -23648,7 +25046,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_op(s, OP_get_field2); emit_u32(s, prop_name); } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE, export_flag) < 0) return -1; if (s->token.val == '}') break; @@ -23658,7 +25056,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, continue; } if (prop_name == JS_ATOM_NULL) { - emit_op(s, OP_to_propkey2); + emit_op(s, OP_to_propkey); if (has_ellipsis) { /* define the property in excludeList */ emit_op(s, OP_perm3); @@ -23684,10 +25082,24 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, var_name = js_parse_destructuring_var(s, tok, is_arg); if (var_name == JS_ATOM_NULL) goto prop_error; + if (need_var_reference(s, tok)) { + /* Must make a reference for proper `with` semantics */ + emit_op(s, OP_scope_get_var); + emit_atom(s, var_name); + emit_u16(s, s->cur_func->scope_level); + JS_FreeAtom(s->ctx, var_name); + goto lvalue1; + } else { + /* no need to make a reference for let/const */ + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + } } else { if (js_parse_left_hand_side_expr(s)) goto prop_error; - lvalue: + lvalue1: if (get_lvalue(s, &opcode, &scope, &var_name, &label_lvalue, &depth_lvalue, FALSE, '{')) goto prop_error; @@ -23754,25 +25166,37 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_atom(s, prop_name); emit_op(s, OP_swap); } - if (!tok || tok == TOK_VAR) { + if (!tok || need_var_reference(s, tok)) { /* generate reference */ /* source -- source source */ emit_op(s, OP_dup); emit_op(s, OP_scope_get_var); emit_atom(s, prop_name); emit_u16(s, s->cur_func->scope_level); - goto lvalue; + goto lvalue1; + } else { + /* no need to make a reference for let/const */ + var_name = JS_DupAtom(s->ctx, prop_name); + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + + /* source -- source val */ + emit_op(s, OP_get_field2); + emit_u32(s, prop_name); } - var_name = JS_DupAtom(s->ctx, prop_name); - /* source -- source val */ - emit_op(s, OP_get_field2); - emit_u32(s, prop_name); } set_val: if (tok) { if (js_define_var(s, var_name, tok)) goto var_error; - scope = s->cur_func->scope_level; + if (export_flag) { + if (!add_export_entry(s, s->cur_func->module, var_name, var_name, + JS_EXPORT_TYPE_LOCAL)) + goto var_error; + } + scope = s->cur_func->scope_level; /* XXX: check */ } if (s->token.val == '=') { /* handle optional default value */ int label_hasval; @@ -23847,22 +25271,34 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_u8(s, 0); emit_op(s, OP_drop); } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0) return -1; } else { var_name = JS_ATOM_NULL; - enum_depth = 0; if (tok) { var_name = js_parse_destructuring_var(s, tok, is_arg); if (var_name == JS_ATOM_NULL) goto var_error; if (js_define_var(s, var_name, tok)) goto var_error; - opcode = OP_scope_get_var; - scope = s->cur_func->scope_level; + if (need_var_reference(s, tok)) { + /* Must make a reference for proper `with` semantics */ + emit_op(s, OP_scope_get_var); + emit_atom(s, var_name); + emit_u16(s, s->cur_func->scope_level); + JS_FreeAtom(s->ctx, var_name); + goto lvalue2; + } else { + /* no need to make a reference for let/const */ + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + enum_depth = 0; + } } else { if (js_parse_left_hand_side_expr(s)) return -1; + lvalue2: if (get_lvalue(s, &opcode, &scope, &var_name, &label_lvalue, &enum_depth, FALSE, '[')) { return -1; @@ -23972,12 +25408,13 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, emit_label(s, label_next); } -/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */ +/* allowed parse_flags: PF_POSTFIX_CALL */ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { FuncCallType call_type; int optional_chaining_label; BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; + const uint8_t *op_token_ptr; call_type = FUNC_CALL_NORMAL; switch(s->token.val) { @@ -23989,34 +25426,17 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { emit_op(s, OP_push_i32); emit_u32(s, JS_VALUE_GET_INT(val)); - } else -#ifdef CONFIG_BIGNUM - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - slimb_t e; - int ret; - - /* need a runtime conversion */ - /* XXX: could add a cache and/or do it once at - the start of the function */ - if (emit_push_const(s, val, 0) < 0) - return -1; - e = s->token.u.num.exponent; - if (e == (int32_t)e) { - emit_op(s, OP_push_i32); - emit_u32(s, e); + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(val); + if (v >= INT32_MIN && v <= INT32_MAX) { + emit_op(s, OP_push_bigint_i32); + emit_u32(s, v); } else { - val = JS_NewBigInt64_1(s->ctx, e); - if (JS_IsException(val)) - return -1; - ret = emit_push_const(s, val, 0); - JS_FreeValue(s->ctx, val); - if (ret < 0) - return -1; + goto large_number; } - emit_op(s, OP_mul_pow10); - } else -#endif - { + } else { + large_number: if (emit_push_const(s, val, 0) < 0) return -1; } @@ -24034,7 +25454,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) if (next_token(s)) return -1; break; - + case TOK_DIV_ASSIGN: s->buf_ptr -= 2; goto parse_regexp; @@ -24043,7 +25463,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) parse_regexp: { JSValue str; - int ret, backtrace_flags; + int ret; if (!s->ctx->compile_regexp) return js_parse_error(s, "RegExp are not supported"); /* the previous token is '/' or '/=', so no need to free */ @@ -24054,12 +25474,10 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) s->token.u.regexp.flags); if (JS_IsException(str)) { /* add the line number info */ - backtrace_flags = 0; - if (s->cur_func && s->cur_func->backtrace_barrier) - backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + int line_num, col_num; + line_num = get_line_col(&col_num, s->buf_start, s->token.ptr - s->buf_start); build_backtrace(s->ctx, s->ctx->rt->current_exception, - s->filename, s->token.line_num, - backtrace_flags); + s->filename, line_num + 1, col_num + 1, 0); return -1; } ret = emit_push_const(s, str, 0); @@ -24075,21 +25493,13 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) } break; case '(': - if ((parse_flags & PF_ARROW_FUNC) && - js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - return -1; - } else { - if (js_parse_expr_paren(s)) - return -1; - } + if (js_parse_expr_paren(s)) + return -1; break; case TOK_FUNCTION: if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) + s->token.ptr)) return -1; break; case TOK_CLASS: @@ -24121,37 +25531,19 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) case TOK_IDENT: { JSAtom name; + const uint8_t *source_ptr; if (s->token.u.ident.is_reserved) { return js_parse_error_reserved_identifier(s); } - if ((parse_flags & PF_ARROW_FUNC) && - peek_token(s, TRUE) == TOK_ARROW) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - return -1; - } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) != '\n') { - const uint8_t *source_ptr; - int source_line_num; - - source_ptr = s->token.ptr; - source_line_num = s->token.line_num; + source_ptr = s->token.ptr; + if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { if (next_token(s)) return -1; if (s->token.val == TOK_FUNCTION) { if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, JS_FUNC_ASYNC, JS_ATOM_NULL, - source_ptr, source_line_num)) - return -1; - } else if ((parse_flags & PF_ARROW_FUNC) && - ((s->token.val == '(' && - js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || - (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && - peek_token(s, TRUE) == TOK_ARROW))) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, - JS_FUNC_ASYNC, JS_ATOM_NULL, - source_ptr, source_line_num)) + source_ptr)) return -1; } else { name = JS_DupAtom(s->ctx, JS_ATOM_async); @@ -24164,9 +25556,12 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) return -1; } name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) /* update line number before emitting code */ + if (next_token(s)) { + JS_FreeAtom(s->ctx, name); return -1; + } do_get_var: + emit_source_pos(s, source_ptr); emit_op(s, OP_scope_get_var); emit_u32(s, name); emit_u16(s, s->cur_func->scope_level); @@ -24175,20 +25570,12 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) break; case '{': case '[': - { - int skip_bits; - if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { - if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) - return -1; - } else { - if (s->token.val == '{') { - if (js_parse_object_literal(s)) - return -1; - } else { - if (js_parse_array_literal(s)) - return -1; - } - } + if (s->token.val == '{') { + if (js_parse_object_literal(s)) + return -1; + } else { + if (js_parse_array_literal(s)) + return -1; } break; case TOK_NEW: @@ -24212,6 +25599,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) accept_lparen = TRUE; if (s->token.val != '(') { /* new operator on an object */ + emit_source_pos(s, s->token.ptr); emit_op(s, OP_dup); emit_op(s, OP_call_constructor); emit_u16(s, 0); @@ -24262,6 +25650,23 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) return js_parse_error(s, "invalid use of 'import()'"); if (js_parse_assign_expr(s)) return -1; + if (s->token.val == ',') { + if (next_token(s)) + return -1; + if (s->token.val != ')') { + if (js_parse_assign_expr(s)) + return -1; + /* accept a trailing comma */ + if (s->token.val == ',') { + if (next_token(s)) + return -1; + } + } else { + emit_op(s, OP_undefined); + } + } else { + emit_op(s, OP_undefined); + } if (js_parse_expect(s, ')')) return -1; emit_op(s, OP_import); @@ -24276,8 +25681,11 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) for(;;) { JSFunctionDef *fd = s->cur_func; BOOL has_optional_chain = FALSE; - + if (s->token.val == TOK_QUESTION_MARK_DOT) { + if ((parse_flags & PF_POSTFIX_CALL) == 0) + return js_parse_error(s, "new keyword cannot be used with an optional chain"); + op_token_ptr = s->token.ptr; /* optional chaining */ if (next_token(s)) return -1; @@ -24295,12 +25703,14 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) return js_parse_error(s, "template literal cannot appear in an optional chain"); } call_type = FUNC_CALL_TEMPLATE; + op_token_ptr = s->token.ptr; /* XXX: check if right position */ goto parse_func_call2; } else if (s->token.val == '(' && accept_lparen) { int opcode, arg_count, drop_count; /* function call */ parse_func_call: + op_token_ptr = s->token.ptr; if (next_token(s)) return -1; @@ -24312,6 +25722,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; drop_count = 2; break; + case OP_get_field_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 4 + 1); + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; + fd->byte_code.size = fd->last_opcode_pos + 1 + 4; + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* need an additional undefined value for the + case where the optional field does not + exists */ + emit_op(s, OP_undefined); + emit_label(s, next_label); + drop_count = 2; + opcode = OP_get_field; + } + break; case OP_scope_get_private_field: /* keep the object on the stack */ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; @@ -24322,6 +25751,25 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; drop_count = 2; break; + case OP_get_array_el_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 1); + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; + fd->byte_code.size = fd->last_opcode_pos + 1; + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* need an additional undefined value for the + case where the optional field does not + exists */ + emit_op(s, OP_undefined); + emit_label(s, next_label); + drop_count = 2; + opcode = OP_get_array_el; + } + break; case OP_scope_get_var: { JSAtom name; @@ -24461,6 +25909,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) /* drop the index */ emit_op(s, OP_drop); + emit_source_pos(s, op_token_ptr); /* apply function call */ switch(opcode) { case OP_get_field: @@ -24506,6 +25955,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) if (next_token(s)) return -1; emit_func_call: + emit_source_pos(s, op_token_ptr); switch(opcode) { case OP_get_field: case OP_scope_get_private_field: @@ -24544,9 +25994,11 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) } call_type = FUNC_CALL_NORMAL; } else if (s->token.val == '.') { + op_token_ptr = s->token.ptr; if (next_token(s)) return -1; parse_property: + emit_source_pos(s, op_token_ptr); if (s->token.val == TOK_PRIVATE_NAME) { /* private class field */ if (get_prev_opcode(fd) == OP_get_super) { @@ -24583,7 +26035,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) return -1; } else if (s->token.val == '[') { int prev_op; - + op_token_ptr = s->token.ptr; parse_array_access: prev_op = get_prev_opcode(fd); if (has_optional_chain) { @@ -24595,6 +26047,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) return -1; if (js_parse_expect(s, ']')) return -1; + emit_source_pos(s, op_token_ptr); if (prev_op == OP_get_super) { emit_op(s, OP_get_super_value); } else { @@ -24604,8 +26057,23 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) break; } } - if (optional_chaining_label >= 0) - emit_label(s, optional_chaining_label); + if (optional_chaining_label >= 0) { + JSFunctionDef *fd = s->cur_func; + int opcode; + emit_label_raw(s, optional_chaining_label); + /* modify the last opcode so that it is an indicator of an + optional chain */ + opcode = get_prev_opcode(fd); + if (opcode == OP_get_field || opcode == OP_get_array_el) { + if (opcode == OP_get_field) + opcode = OP_get_field_opt_chain; + else + opcode = OP_get_array_el_opt_chain; + fd->byte_code.buf[fd->last_opcode_pos] = opcode; + } else { + fd->last_opcode_pos = -1; + } + } return 0; } @@ -24621,27 +26089,57 @@ static __exception int js_parse_delete(JSParseState *s) return -1; switch(opcode = get_prev_opcode(fd)) { case OP_get_field: + case OP_get_field_opt_chain: { JSValue val; - int ret; - + int ret, opt_chain_label, next_label; + if (opcode == OP_get_field_opt_chain) { + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 4 + 1); + } else { + opt_chain_label = -1; + } name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; val = JS_AtomToValue(s->ctx, name); ret = emit_push_const(s, val, 1); JS_FreeValue(s->ctx, val); JS_FreeAtom(s->ctx, name); if (ret) return ret; + emit_op(s, OP_delete); + if (opt_chain_label >= 0) { + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* if the optional chain is not taken, return 'true' */ + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + emit_label(s, next_label); + } + fd->last_opcode_pos = -1; } - goto do_delete; + break; case OP_get_array_el: fd->byte_code.size = fd->last_opcode_pos; fd->last_opcode_pos = -1; - do_delete: emit_op(s, OP_delete); break; + case OP_get_array_el_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 1); + fd->byte_code.size = fd->last_opcode_pos; + emit_op(s, OP_delete); + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* if the optional chain is not taken, return 'true' */ + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + emit_label(s, next_label); + fd->last_opcode_pos = -1; + } + break; case OP_scope_get_var: /* 'delete this': this is not a reference */ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); @@ -24671,10 +26169,11 @@ static __exception int js_parse_delete(JSParseState *s) return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */ +/* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */ static __exception int js_parse_unary(JSParseState *s, int parse_flags) { int op; + const uint8_t *op_token_ptr; switch(s->token.val) { case '+': @@ -24682,6 +26181,7 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags) case '!': case '~': case TOK_VOID: + op_token_ptr = s->token.ptr; op = s->token.val; if (next_token(s)) return -1; @@ -24689,15 +26189,18 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags) return -1; switch(op) { case '-': + emit_source_pos(s, op_token_ptr); emit_op(s, OP_neg); break; case '+': + emit_source_pos(s, op_token_ptr); emit_op(s, OP_plus); break; case '!': emit_op(s, OP_lnot); break; case '~': + emit_source_pos(s, op_token_ptr); emit_op(s, OP_not); break; case TOK_VOID: @@ -24715,12 +26218,14 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags) int opcode, op, scope, label; JSAtom name; op = s->token.val; + op_token_ptr = s->token.ptr; if (next_token(s)) return -1; if (js_parse_unary(s, 0)) return -1; if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; + emit_source_pos(s, op_token_ptr); emit_op(s, OP_dec + op - TOK_DEC); put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); @@ -24757,47 +26262,31 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags) return -1; if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; + s->cur_func->has_await = TRUE; emit_op(s, OP_await); parse_flags = 0; break; default: - if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) | - PF_POSTFIX_CALL)) + if (js_parse_postfix_expr(s, PF_POSTFIX_CALL)) return -1; if (!s->got_lf && (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { int opcode, op, scope, label; JSAtom name; op = s->token.val; + op_token_ptr = s->token.ptr; if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; + emit_source_pos(s, op_token_ptr); emit_op(s, OP_post_dec + op - TOK_DEC); put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, FALSE); if (next_token(s)) - return -1; + return -1; } break; } if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { -#ifdef CONFIG_BIGNUM - if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) { - /* Extended exponentiation syntax rules: we extend the ES7 - grammar in order to have more intuitive semantics: - -2**2 evaluates to -4. */ - if (!(s->cur_func->js_mode & JS_MODE_MATH)) { - if (parse_flags & PF_POW_FORBIDDEN) { - JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); - return -1; - } - } - if (next_token(s)) - return -1; - if (js_parse_unary(s, PF_POW_ALLOWED)) - return -1; - emit_op(s, OP_pow); - } -#else if (s->token.val == TOK_POW) { /* Strict ES7 exponentiation syntax rules: To solve conficting semantics between different implementations @@ -24808,31 +26297,56 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags) JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); return -1; } + op_token_ptr = s->token.ptr; if (next_token(s)) return -1; if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; + emit_source_pos(s, op_token_ptr); emit_op(s, OP_pow); } -#endif } return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +/* allowed parse_flags: PF_IN_ACCEPTED */ static __exception int js_parse_expr_binary(JSParseState *s, int level, int parse_flags) { int op, opcode; - + const uint8_t *op_token_ptr; + if (level == 0) { - return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) | - PF_POW_ALLOWED); + return js_parse_unary(s, PF_POW_ALLOWED); + } else if (s->token.val == TOK_PRIVATE_NAME && + (parse_flags & PF_IN_ACCEPTED) && level == 4 && + peek_token(s, FALSE) == TOK_IN) { + JSAtom atom; + + atom = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail_private_in; + if (s->token.val != TOK_IN) + goto fail_private_in; + if (next_token(s)) + goto fail_private_in; + if (js_parse_expr_binary(s, level - 1, parse_flags)) { + fail_private_in: + JS_FreeAtom(s->ctx, atom); + return -1; + } + emit_op(s, OP_scope_in_private_field); + emit_atom(s, atom); + emit_u16(s, s->cur_func->scope_level); + JS_FreeAtom(s->ctx, atom); + return 0; + } else { + if (js_parse_expr_binary(s, level - 1, parse_flags)) + return -1; } - if (js_parse_expr_binary(s, level - 1, parse_flags)) - return -1; for(;;) { op = s->token.val; + op_token_ptr = s->token.ptr; switch(level) { case 1: switch(op) { @@ -24843,12 +26357,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_div; break; case '%': -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) - opcode = OP_math_mod; - else -#endif - opcode = OP_mod; + opcode = OP_mod; break; default: return 0; @@ -24959,14 +26468,15 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, } if (next_token(s)) return -1; - if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) + if (js_parse_expr_binary(s, level - 1, parse_flags)) return -1; + emit_source_pos(s, op_token_ptr); emit_op(s, opcode); } return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +/* allowed parse_flags: PF_IN_ACCEPTED */ static __exception int js_parse_logical_and_or(JSParseState *s, int op, int parse_flags) { @@ -24990,11 +26500,11 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, emit_op(s, OP_drop); if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; } else { if (js_parse_logical_and_or(s, TOK_LAND, - parse_flags & ~PF_ARROW_FUNC)) + parse_flags)) return -1; } if (s->token.val != op) { @@ -25012,7 +26522,7 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) { int label1; - + if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) return -1; if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { @@ -25020,13 +26530,13 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) for(;;) { if (next_token(s)) return -1; - + emit_op(s, OP_dup); emit_op(s, OP_is_undefined_or_null); emit_goto(s, OP_if_false, label1); emit_op(s, OP_drop); - - if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; if (s->token.val != TOK_DOUBLE_QUESTION_MARK) break; @@ -25036,7 +26546,7 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +/* allowed parse_flags: PF_IN_ACCEPTED */ static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) { int label1, label2; @@ -25065,18 +26575,16 @@ static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) return 0; } -static void emit_return(JSParseState *s, BOOL hasval); - /* allowed parse_flags: PF_IN_ACCEPTED */ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) { - int opcode, op, scope; + int opcode, op, scope, skip_bits; JSAtom name0 = JS_ATOM_NULL; JSAtom name; if (s->token.val == TOK_YIELD) { BOOL is_star = FALSE, is_async; - + if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) return js_parse_error(s, "unexpected 'yield' keyword"); if (!s->cur_func->in_function_body) @@ -25114,9 +26622,9 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) undefined) */ emit_op(s, OP_drop); emit_op(s, OP_undefined); - + emit_op(s, OP_undefined); /* initial value */ - + emit_label(s, label_loop); emit_op(s, OP_iterator_next); if (is_async) @@ -25130,7 +26638,6 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) /* OP_async_yield_star takes the value as parameter */ emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); - emit_op(s, OP_await); emit_op(s, OP_async_yield_star); } else { /* OP_yield_star takes (value, done) as parameter */ @@ -25140,13 +26647,13 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) label_return = emit_goto(s, OP_if_true, -1); emit_op(s, OP_drop); emit_goto(s, OP_goto, label_loop); - + emit_label(s, label_return); emit_op(s, OP_push_i32); emit_u32(s, 2); emit_op(s, OP_strict_eq); label_throw = emit_goto(s, OP_if_true, -1); - + /* return handling */ if (is_async) emit_op(s, OP_await); @@ -25162,13 +26669,13 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); - + emit_label(s, label_return1); emit_op(s, OP_nip); emit_op(s, OP_nip); emit_op(s, OP_nip); emit_return(s, TRUE); - + /* throw handling */ emit_label(s, label_throw); emit_op(s, OP_iterator_call); @@ -25193,7 +26700,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_op(s, OP_throw_error); emit_atom(s, JS_ATOM_NULL); emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); - + emit_label(s, label_next); emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); @@ -25203,7 +26710,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_op(s, OP_nip); } else { int label_next; - + if (is_async) emit_op(s, OP_await); emit_op(s, OP_yield); @@ -25212,17 +26719,61 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_label(s, label_next); } return 0; + } else if (s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr); + } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) { + const uint8_t *source_ptr; + int tok; + JSParsePos pos; + + /* fast test */ + tok = peek_token(s, TRUE); + if (tok == TOK_FUNCTION || tok == '\n') + goto next; + + source_ptr = s->token.ptr; + js_parse_get_pos(s, &pos); + if (next_token(s)) + return -1; + if ((s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, TRUE) == TOK_ARROW)) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_ASYNC, JS_ATOM_NULL, + source_ptr); + } else { + /* undo the token parsing */ + if (js_parse_seek_token(s, &pos)) + return -1; + } + } else if (s->token.val == TOK_IDENT && + peek_token(s, TRUE) == TOK_ARROW) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr); + } else if ((s->token.val == '{' || s->token.val == '[') && + js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { + if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0) + return -1; + return 0; } + next: if (s->token.val == TOK_IDENT) { /* name0 is used to check for OP_set_name pattern, not duplicated */ name0 = s->token.u.ident.atom; } - if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC)) + if (js_parse_cond_expr(s, parse_flags)) return -1; op = s->token.val; if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) { int label; + const uint8_t *op_token_ptr; + op_token_ptr = s->token.ptr; if (next_token(s)) return -1; if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) @@ -25241,24 +26792,16 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) static const uint8_t assign_opcodes[] = { OP_mul, OP_div, OP_mod, OP_add, OP_sub, OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or, -#ifdef CONFIG_BIGNUM - OP_pow, -#endif OP_pow, }; op = assign_opcodes[op - TOK_MUL_ASSIGN]; -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) { - if (op == OP_mod) - op = OP_math_mod; - } -#endif + emit_source_pos(s, op_token_ptr); emit_op(s, op); } put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { int label, label1, depth_lvalue, label2; - + if (next_token(s)) return -1; if (get_lvalue(s, &opcode, &scope, &name, &label, @@ -25271,7 +26814,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, -1); emit_op(s, OP_drop); - + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(s->ctx, name); return -1; @@ -25280,7 +26823,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) if (opcode == OP_get_ref_value && name == name0) { set_object_name(s, name); } - + switch(depth_lvalue) { case 1: emit_op(s, OP_insert2); @@ -25300,7 +26843,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, FALSE); label2 = emit_goto(s, OP_goto, -1); - + emit_label(s, label1); /* remove the lvalue stack entries */ @@ -25363,6 +26906,7 @@ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, be->label_finally = -1; be->scope_level = fd->scope_level; be->has_iterator = FALSE; + be->is_regular_stmt = FALSE; } static void pop_break_entry(JSFunctionDef *fd) @@ -25391,7 +26935,8 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) } if (!is_cont && top->label_break != -1 && - (name == JS_ATOM_NULL || top->label_name == name)) { + ((name == JS_ATOM_NULL && !top->is_regular_stmt) || + top->label_name == name)) { emit_goto(s, OP_goto, top->label_break); return 0; } @@ -25424,61 +26969,61 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) static void emit_return(JSParseState *s, BOOL hasval) { BlockEnv *top; - int drop_count; - drop_count = 0; + if (s->cur_func->func_kind != JS_FUNC_NORMAL) { + if (!hasval) { + /* no value: direct return in case of async generator */ + emit_op(s, OP_undefined); + hasval = TRUE; + } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + /* the await must be done before handling the "finally" in + case it raises an exception */ + emit_op(s, OP_await); + } + } + top = s->cur_func->top_break; while (top != NULL) { - /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not - required as all local variables will be closed upon returning - from JS_CallInternal, but not in the same order. */ - if (top->has_iterator) { - /* with 'yield', the exact number of OP_drop to emit is - unknown, so we use a specific operation to look for - the catch offset */ + if (top->has_iterator || top->label_finally != -1) { if (!hasval) { emit_op(s, OP_undefined); hasval = TRUE; } - emit_op(s, OP_iterator_close_return); - if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { - int label_next, label_next2; - - emit_op(s, OP_drop); /* catch offset */ - emit_op(s, OP_drop); /* next */ - emit_op(s, OP_get_field2); - emit_atom(s, JS_ATOM_return); - /* stack: iter_obj return_func */ - emit_op(s, OP_dup); - emit_op(s, OP_is_undefined_or_null); - label_next = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_call_method); - emit_u16(s, 0); - emit_op(s, OP_iterator_check_object); - emit_op(s, OP_await); - label_next2 = emit_goto(s, OP_goto, -1); - emit_label(s, label_next); - emit_op(s, OP_drop); - emit_label(s, label_next2); - emit_op(s, OP_drop); + /* Remove the stack elements up to and including the catch + offset. When 'yield' is used in an expression we have + no easy way to count them, so we use this specific + instruction instead. */ + emit_op(s, OP_nip_catch); + /* stack: iter_obj next ret_val */ + if (top->has_iterator) { + if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + int label_next, label_next2; + emit_op(s, OP_nip); /* next */ + emit_op(s, OP_swap); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_return); + /* stack: iter_obj return_func */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_await); + label_next2 = emit_goto(s, OP_goto, -1); + emit_label(s, label_next); + emit_op(s, OP_drop); + emit_label(s, label_next2); + emit_op(s, OP_drop); + } else { + emit_op(s, OP_rot3r); + emit_op(s, OP_undefined); /* dummy catch offset */ + emit_op(s, OP_iterator_close); + } } else { - emit_op(s, OP_iterator_close); - } - drop_count = -3; - } - drop_count += top->drop_count; - if (top->label_finally != -1) { - while(drop_count) { - /* must keep the stack top if hasval */ - emit_op(s, hasval ? OP_nip : OP_drop); - drop_count--; - } - if (!hasval) { - /* must push return value to keep same stack size */ - emit_op(s, OP_undefined); - hasval = TRUE; + /* execute the "finally" block */ + emit_goto(s, OP_gosub, top->label_finally); } - emit_goto(s, OP_gosub, top->label_finally); } top = top->prev; } @@ -25495,20 +27040,15 @@ static void emit_return(JSParseState *s, BOOL hasval) label_return = -1; } - /* XXX: if this is not initialized, should throw the - ReferenceError in the caller realm */ - emit_op(s, OP_scope_get_var); + /* The error should be raised in the caller context, so we use + a specific opcode */ + emit_op(s, OP_scope_get_var_checkthis); emit_atom(s, JS_ATOM_this); emit_u16(s, 0); emit_label(s, label_return); emit_op(s, OP_return); } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) { - if (!hasval) { - emit_op(s, OP_undefined); - } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { - emit_op(s, OP_await); - } emit_op(s, OP_return_async); } else { emit_op(s, hasval ? OP_return : OP_return_undef); @@ -25579,7 +27119,7 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, if (s->token.val == '=') { if (next_token(s)) goto var_error; - if (tok == TOK_VAR) { + if (need_var_reference(s, tok)) { /* Must make a reference for proper `with` semantics */ int opcode, scope, label; JSAtom name1; @@ -25624,7 +27164,7 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, if ((s->token.val == '[' || s->token.val == '{') && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { emit_op(s, OP_undefined); - if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0) return -1; } else { return js_parse_error(s, "variable name expected"); @@ -25653,12 +27193,13 @@ static BOOL is_label(JSParseState *s) static int is_let(JSParseState *s, int decl_mask) { int res = FALSE; - + const uint8_t *last_token_ptr; + if (token_is_pseudo_keyword(s, JS_ATOM_let)) { -#if 1 JSParsePos pos; js_parse_get_pos(s, &pos); for (;;) { + last_token_ptr = s->token.ptr; if (next_token(s)) { res = -1; break; @@ -25677,7 +27218,8 @@ static int is_let(JSParseState *s, int decl_mask) /* Check for possible ASI if not scanning for Declaration */ /* XXX: should also check that `{` introduces a BindingPattern, but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */ - if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) { + if (!has_lf_in_range(last_token_ptr, s->token.ptr) || + (decl_mask & DECL_MASK_OTHER)) { res = TRUE; break; } @@ -25688,12 +27230,6 @@ static int is_let(JSParseState *s, int decl_mask) if (js_parse_seek_token(s, &pos)) { res = -1; } -#else - int tok = peek_token(s, TRUE); - if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') { - res = TRUE; - } -#endif } return res; } @@ -25753,7 +27289,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { - if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) + if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE, FALSE) < 0) return -1; has_destructuring = TRUE; } else { @@ -25775,11 +27311,14 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, emit_atom(s, var_name); emit_u16(s, fd->scope_level); } + } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, FALSE) == TOK_OF) { + return js_parse_error(s, "'for of' expression cannot start with 'async'"); } else { int skip_bits; if ((s->token.val == '[' || s->token.val == '{') && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { - if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0) return -1; } else { int lvalue_label; @@ -25888,12 +27427,9 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, emit_label(s, label_cont); if (is_for_of) { if (is_async) { - /* call the next method */ /* stack: iter_obj next catch_offset */ - emit_op(s, OP_dup3); - emit_op(s, OP_drop); - emit_op(s, OP_call_method); - emit_u16(s, 0); + /* call the next method */ + emit_op(s, OP_for_await_of_next); /* get the result of the promise */ emit_op(s, OP_await); /* unwrap the value and done values */ @@ -25967,6 +27503,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, label_break = new_label(s); push_break_entry(s->cur_func, &break_entry, label_name, label_break, -1, 0); + break_entry.is_regular_stmt = TRUE; if (!(s->cur_func->js_mode & JS_MODE_STRICT) && (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; @@ -25987,34 +27524,49 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, goto fail; break; case TOK_RETURN: - if (s->cur_func->is_eval) { - js_parse_error(s, "return not in a function"); - goto fail; - } - if (next_token(s)) - goto fail; - if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { - if (js_parse_expr(s)) + { + const uint8_t *op_token_ptr; + if (s->cur_func->is_eval) { + js_parse_error(s, "return not in a function"); + goto fail; + } + if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { + js_parse_error(s, "return in a static initializer block"); + goto fail; + } + op_token_ptr = s->token.ptr; + if (next_token(s)) + goto fail; + if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { + if (js_parse_expr(s)) + goto fail; + emit_source_pos(s, op_token_ptr); + emit_return(s, TRUE); + } else { + emit_source_pos(s, op_token_ptr); + emit_return(s, FALSE); + } + if (js_parse_expect_semi(s)) goto fail; - emit_return(s, TRUE); - } else { - emit_return(s, FALSE); } - if (js_parse_expect_semi(s)) - goto fail; break; case TOK_THROW: - if (next_token(s)) - goto fail; - if (s->got_lf) { - js_parse_error(s, "line terminator not allowed after throw"); - goto fail; + { + const uint8_t *op_token_ptr; + op_token_ptr = s->token.ptr; + if (next_token(s)) + goto fail; + if (s->got_lf) { + js_parse_error(s, "line terminator not allowed after throw"); + goto fail; + } + if (js_parse_expr(s)) + goto fail; + emit_source_pos(s, op_token_ptr); + emit_op(s, OP_throw); + if (js_parse_expect_semi(s)) + goto fail; } - if (js_parse_expr(s)) - goto fail; - emit_op(s, OP_throw); - if (js_parse_expect_semi(s)) - goto fail; break; case TOK_LET: case TOK_CONST: @@ -26159,6 +27711,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, is_async = TRUE; if (next_token(s)) goto fail; + s->cur_func->has_await = TRUE; } if (js_parse_expect(s, '(')) goto fail; @@ -26451,7 +28004,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { /* XXX: TOK_LET is not completely correct */ - if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) + if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE, FALSE) < 0) goto fail; } else { js_parse_error(s, "identifier expected"); @@ -26518,7 +28071,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, emit_label(s, label_finally); if (s->token.val == TOK_FINALLY) { int saved_eval_ret_idx = 0; /* avoid warning */ - + if (next_token(s)) goto fail; /* on the stack: ret_value gosub_ret_value */ @@ -26538,7 +28091,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, emit_u16(s, saved_eval_ret_idx); set_eval_ret_undefined(s); } - + if (js_parse_block(s)) goto fail; @@ -26621,7 +28174,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, parse_func_var: if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR, JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) + s->token.ptr)) goto fail; break; } @@ -26643,7 +28196,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (js_parse_expect_semi(s)) goto fail; break; - + case TOK_ENUM: case TOK_EXPORT: case TOK_EXTENDS: @@ -26652,6 +28205,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, default: hasexpr: + emit_source_pos(s, s->token.ptr); if (js_parse_expr(s)) goto fail; if (s->cur_func->eval_ret_idx >= 0) { @@ -26689,6 +28243,10 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) m->func_obj = JS_UNDEFINED; m->eval_exception = JS_UNDEFINED; m->meta_obj = JS_UNDEFINED; + m->promise = JS_UNDEFINED; + m->resolving_funcs[0] = JS_UNDEFINED; + m->resolving_funcs[1] = JS_UNDEFINED; + m->private_value = JS_UNDEFINED; list_add_tail(&m->link, &ctx->loaded_modules); return m; } @@ -26698,6 +28256,11 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, { int i; + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + JS_MarkValue(rt, rme->attributes, mark_func); + } + for(i = 0; i < m->export_entries_count; i++) { JSExportEntry *me = &m->export_entries[i]; if (me->export_type == JS_EXPORT_TYPE_LOCAL && @@ -26710,6 +28273,10 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, JS_MarkValue(rt, m->func_obj, mark_func); JS_MarkValue(rt, m->eval_exception, mark_func); JS_MarkValue(rt, m->meta_obj, mark_func); + JS_MarkValue(rt, m->promise, mark_func); + JS_MarkValue(rt, m->resolving_funcs[0], mark_func); + JS_MarkValue(rt, m->resolving_funcs[1], mark_func); + JS_MarkValue(rt, m->private_value, mark_func); } static void js_free_module_def(JSContext *ctx, JSModuleDef *m) @@ -26721,6 +28288,7 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; JS_FreeAtom(ctx, rme->module_name); + JS_FreeValue(ctx, rme->attributes); } js_free(ctx, m->req_module_entries); @@ -26740,11 +28308,16 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) JS_FreeAtom(ctx, mi->import_name); } js_free(ctx, m->import_entries); + js_free(ctx, m->async_parent_modules); JS_FreeValue(ctx, m->module_ns); JS_FreeValue(ctx, m->func_obj); JS_FreeValue(ctx, m->eval_exception); JS_FreeValue(ctx, m->meta_obj); + JS_FreeValue(ctx, m->promise); + JS_FreeValue(ctx, m->resolving_funcs[0]); + JS_FreeValue(ctx, m->resolving_funcs[1]); + JS_FreeValue(ctx, m->private_value); list_del(&m->link); js_free(ctx, m); } @@ -26753,14 +28326,6 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, JSAtom module_name) { JSReqModuleEntry *rme; - int i; - - /* no need to add the module request if it is already present */ - for(i = 0; i < m->req_module_entries_count; i++) { - rme = &m->req_module_entries[i]; - if (rme->module_name == module_name) - return i; - } if (js_resize_array(ctx, (void **)&m->req_module_entries, sizeof(JSReqModuleEntry), @@ -26770,7 +28335,8 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, rme = &m->req_module_entries[m->req_module_entries_count++]; rme->module_name = JS_DupAtom(ctx, module_name); rme->module = NULL; - return i; + rme->attributes = JS_UNDEFINED; + return m->req_module_entries_count - 1; } static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, @@ -26850,6 +28416,8 @@ JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, if (name == JS_ATOM_NULL) return NULL; m = js_new_module_def(ctx, name); + if (!m) + return NULL; m->init_func = func; return m; } @@ -26889,12 +28457,38 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, return -1; } +int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val) +{ + set_value(ctx, &m->private_value, val); + return 0; +} + +JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m) +{ + return JS_DupValue(ctx, m->private_value); +} + void JS_SetModuleLoaderFunc(JSRuntime *rt, JSModuleNormalizeFunc *module_normalize, JSModuleLoaderFunc *module_loader, void *opaque) { rt->module_normalize_func = module_normalize; - rt->module_loader_func = module_loader; + rt->module_loader_has_attr = FALSE; + rt->u.module_loader_func = module_loader; + rt->module_check_attrs = NULL; + rt->module_loader_opaque = opaque; +} + +void JS_SetModuleLoaderFunc2(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc2 *module_loader, + JSModuleCheckSupportedImportAttributes *module_check_attrs, + void *opaque) +{ + rt->module_normalize_func = module_normalize; + rt->module_loader_has_attr = TRUE; + rt->u.module_loader_func2 = module_loader; + rt->module_check_attrs = module_check_attrs; rt->module_loader_opaque = opaque; } @@ -26905,6 +28499,7 @@ static char *js_default_module_normalize_name(JSContext *ctx, { char *filename, *p; const char *r; + int cap; int len; if (name[0] != '.') { @@ -26918,7 +28513,8 @@ static char *js_default_module_normalize_name(JSContext *ctx, else len = 0; - filename = js_malloc(ctx, len + strlen(name) + 1 + 1); + cap = len + strlen(name) + 1 + 1; + filename = js_malloc(ctx, cap); if (!filename) return NULL; memcpy(filename, base_name, len); @@ -26950,8 +28546,8 @@ static char *js_default_module_normalize_name(JSContext *ctx, } } if (filename[0] != '\0') - strcat(filename, "/"); - strcat(filename, r); + pstrcat(filename, cap, "/"); + pstrcat(filename, cap, r); // printf("normalize: %s %s -> %s\n", base_name, name, filename); return filename; } @@ -26960,7 +28556,7 @@ static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) { struct list_head *el; JSModuleDef *m; - + /* first look at the loaded modules */ list_for_each(el, &ctx->loaded_modules) { m = list_entry(el, JSModuleDef, link); @@ -26973,7 +28569,8 @@ static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) /* return NULL in case of exception (e.g. module could not be loaded) */ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, const char *base_cname, - const char *cname1) + const char *cname1, + JSValueConst attributes) { JSRuntime *rt = ctx->rt; JSModuleDef *m; @@ -27006,26 +28603,30 @@ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, JS_FreeAtom(ctx, module_name); /* load the module */ - if (!rt->module_loader_func) { + if (!rt->u.module_loader_func) { /* XXX: use a syntax error ? */ JS_ThrowReferenceError(ctx, "could not load module '%s'", cname); js_free(ctx, cname); return NULL; } - - m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque); + if (rt->module_loader_has_attr) { + m = rt->u.module_loader_func2(ctx, cname, rt->module_loader_opaque, attributes); + } else { + m = rt->u.module_loader_func(ctx, cname, rt->module_loader_opaque); + } js_free(ctx, cname); return m; } static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, - JSAtom base_module_name, - JSAtom module_name1) + JSAtom base_module_name, + JSAtom module_name1, + JSValueConst attributes) { const char *base_cname, *cname; JSModuleDef *m; - + base_cname = JS_AtomToCString(ctx, base_module_name); if (!base_cname) return NULL; @@ -27034,7 +28635,7 @@ static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, JS_FreeCString(ctx, base_cname); return NULL; } - m = js_host_resolve_imported_module(ctx, base_cname, cname); + m = js_host_resolve_imported_module(ctx, base_cname, cname, attributes); JS_FreeCString(ctx, base_cname); JS_FreeCString(ctx, cname); return m; @@ -27218,7 +28819,7 @@ static void js_resolve_export_throw_error(JSContext *ctx, typedef enum { EXPORTED_NAME_AMBIGUOUS, EXPORTED_NAME_NORMAL, - EXPORTED_NAME_NS, + EXPORTED_NAME_DELAYED, } ExportedNameEntryEnum; typedef struct ExportedNameEntry { @@ -27227,7 +28828,6 @@ typedef struct ExportedNameEntry { union { JSExportEntry *me; /* using when the list is built */ JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ - JSModuleDef *module; /* for EXPORTED_NAME_NS */ } u; } ExportedNameEntry; @@ -27333,13 +28933,33 @@ static int exported_names_cmp(const void *p1, const void *p2, void *opaque) return ret; } -static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); - static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) { JSModuleDef *m = opaque; - return js_get_module_ns(ctx, m); + JSResolveResultEnum res; + JSExportEntry *res_me; + JSModuleDef *res_m; + JSVarRef *var_ref; + + res = js_resolve_export(ctx, &res_m, &res_me, m, atom); + if (res != JS_RESOLVE_RES_FOUND) { + /* fail safe: normally no error should happen here except for memory */ + js_resolve_export_throw_error(ctx, res, m, atom); + return JS_EXCEPTION; + } + if (res_me->local_name == JS_ATOM__star_) { + return JS_GetModuleNamespace(ctx, res_m->req_module_entries[res_me->u.req_module_idx].module); + } else { + if (res_me->u.local.var_ref) { + var_ref = res_me->u.local.var_ref; + } else { + JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; + } + /* WARNING: a varref is returned as a string ! */ + return JS_MKPTR(JS_TAG_STRING, var_ref); + } } static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) @@ -27384,17 +29004,18 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) en->export_type = EXPORTED_NAME_AMBIGUOUS; } else { if (res_me->local_name == JS_ATOM__star_) { - en->export_type = EXPORTED_NAME_NS; - en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module; + en->export_type = EXPORTED_NAME_DELAYED; } else { - en->export_type = EXPORTED_NAME_NORMAL; if (res_me->u.local.var_ref) { en->u.var_ref = res_me->u.local.var_ref; } else { JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - p1 = JS_VALUE_GET_OBJ(res_m->func_obj); en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; } + if (en->u.var_ref == NULL) + en->export_type = EXPORTED_NAME_DELAYED; + else + en->export_type = EXPORTED_NAME_NORMAL; } } } @@ -27418,13 +29039,13 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) pr->u.var_ref = var_ref; } break; - case EXPORTED_NAME_NS: - /* the exported namespace must be created on demand */ + case EXPORTED_NAME_DELAYED: + /* the exported namespace or reference may depend on + circular references, so we resolve it lazily */ if (JS_DefineAutoInitProperty(ctx, obj, en->export_name, JS_AUTOINIT_ID_MODULE_NS, - en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - goto fail; + m, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) break; default: break; @@ -27445,7 +29066,7 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) return JS_EXCEPTION; } -static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m) +JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) { if (JS_IsUndefined(m->module_ns)) { JSValue val; @@ -27476,7 +29097,8 @@ static int js_resolve_module(JSContext *ctx, JSModuleDef *m) for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, - rme->module_name); + rme->module_name, + rme->attributes); if (!m1) return -1; rme->module = m1; @@ -27563,7 +29185,7 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) BOOL is_c_module; int i; JSVarRef *var_ref; - + if (m->func_created) return 0; @@ -27587,7 +29209,7 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) m->func_created = TRUE; /* do it on the dependencies */ - + for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; if (js_create_module_function(ctx, rme->module) < 0) @@ -27595,12 +29217,13 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) } return 0; -} +} + - /* Prepare a module to be executed by resolving all the imported variables. */ -static int js_link_module(JSContext *ctx, JSModuleDef *m) +static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, + JSModuleDef **pstack_top, int index) { int i; JSImportEntry *mi; @@ -27609,22 +29232,48 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) JSObject *p; BOOL is_c_module; JSValue ret_val; - - if (m->instantiated) - return 0; - m->instantiated = TRUE; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } #ifdef DUMP_MODULE_RESOLVE { char buf1[ATOM_GET_STR_BUF_SIZE]; - printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); } #endif + if (m->status == JS_MODULE_STATUS_LINKING || + m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) + return index; + + assert(m->status == JS_MODULE_STATUS_UNLINKED); + m->status = JS_MODULE_STATUS_LINKING; + m->dfs_index = index; + m->dfs_ancestor_index = index; + index++; + /* push 'm' on stack */ + m->stack_prev = *pstack_top; + *pstack_top = m; + for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (js_link_module(ctx, rme->module) < 0) + m1 = rme->module; + index = js_inner_module_linking(ctx, m1, pstack_top, index); + if (index < 0) goto fail; + assert(m1->status == JS_MODULE_STATUS_LINKING || + m1->status == JS_MODULE_STATUS_LINKED || + m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->status == JS_MODULE_STATUS_LINKING) { + m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, + m1->dfs_ancestor_index); + } } #ifdef DUMP_MODULE_RESOLVE @@ -27676,10 +29325,10 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) printf(": "); #endif m1 = m->req_module_entries[mi->req_module_idx].module; - if (mi->import_name == JS_ATOM__star_) { + if (mi->is_star) { JSValue val; /* name space import */ - val = js_get_module_ns(ctx, m1); + val = JS_GetModuleNamespace(ctx, m1); if (JS_IsException(val)) goto fail; set_value(ctx, &var_refs[mi->var_idx]->value, val); @@ -27703,7 +29352,7 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) JSModuleDef *m2; /* name space import from */ m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; - val = js_get_module_ns(ctx, m2); + val = JS_GetModuleNamespace(ctx, m2); if (JS_IsException(val)) goto fail; var_ref = js_create_module_var(ctx, TRUE); @@ -27750,14 +29399,59 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) JS_FreeValue(ctx, ret_val); } + assert(m->dfs_ancestor_index <= m->dfs_index); + if (m->dfs_index == m->dfs_ancestor_index) { + for(;;) { + /* pop m1 from stack */ + m1 = *pstack_top; + *pstack_top = m1->stack_prev; + m1->status = JS_MODULE_STATUS_LINKED; + if (m1 == m) + break; + } + } + #ifdef DUMP_MODULE_RESOLVE - printf("done instantiate\n"); + printf("js_inner_module_linking done\n"); #endif - return 0; + return index; fail: return -1; } +/* Prepare a module to be executed by resolving all the imported + variables. */ +static int js_link_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *stack_top, *m1; + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + assert(m->status == JS_MODULE_STATUS_UNLINKED || + m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + stack_top = NULL; + if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) { + while (stack_top != NULL) { + m1 = stack_top; + assert(m1->status == JS_MODULE_STATUS_LINKING); + m1->status = JS_MODULE_STATUS_UNLINKED; + stack_top = m1->stack_prev; + } + return -1; + } + assert(stack_top == NULL); + assert(m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + return 0; +} + /* return JS_ATOM_NULL if the name cannot be found. Only works with not striped bytecode functions. */ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) @@ -27766,8 +29460,8 @@ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) JSFunctionBytecode *b; JSObject *p; /* XXX: currently we just use the filename of the englobing - function. It does not work for eval(). Need to add a - ScriptOrModule info in JSFunctionBytecode */ + function from the debug info. May need to add a ScriptOrModule + info in JSFunctionBytecode. */ sf = ctx->rt->current_stack_frame; if (!sf) return JS_ATOM_NULL; @@ -27776,15 +29470,23 @@ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) if (!sf) return JS_ATOM_NULL; } - if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) - return JS_ATOM_NULL; - p = JS_VALUE_GET_OBJ(sf->cur_func); - if (!js_class_has_bytecode(p->class_id)) - return JS_ATOM_NULL; - b = p->u.func.function_bytecode; - if (!b->has_debug) - return JS_ATOM_NULL; - return JS_DupAtom(ctx, b->debug.filename); + for(;;) { + if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) + return JS_ATOM_NULL; + p = JS_VALUE_GET_OBJ(sf->cur_func); + if (!js_class_has_bytecode(p->class_id)) + return JS_ATOM_NULL; + b = p->u.func.function_bytecode; + if (!b->is_direct_or_indirect_eval) { + if (!b->has_debug) + return JS_ATOM_NULL; + return JS_DupAtom(ctx, b->debug.filename); + } else { + sf = sf->prev_frame; + if (!sf) + return JS_ATOM_NULL; + } + } } JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) @@ -27810,7 +29512,7 @@ static JSValue js_import_meta(JSContext *ctx) { JSAtom filename; JSModuleDef *m; - + filename = JS_GetScriptOrModuleName(ctx, 0); if (filename == JS_ATOM_NULL) goto fail; @@ -27827,29 +29529,111 @@ static JSValue js_import_meta(JSContext *ctx) return JS_GetImportMeta(ctx, m); } -/* used by os.Worker() and import() */ -JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, - const char *filename) +static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m) +{ + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); +} + +static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSValueConst error; + JSValue ret; + + /* XXX: check if the test is necessary */ + if (argc >= 1) + error = argv[0]; + else + error = JS_UNDEFINED; + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, ret); + return JS_UNDEFINED; +} + +static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]); + JSValue ret, ns; + + /* return the module namespace */ + ns = JS_GetModuleNamespace(ctx, m); + if (JS_IsException(ns)) { + JSValue err = JS_GetException(ctx); + js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data); + return JS_UNDEFINED; + } + ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&ns); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, ns); + return JS_UNDEFINED; +} + +static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, + const char *filename, + JSValueConst *resolving_funcs, + JSValueConst attributes) { + JSValue evaluate_promise; JSModuleDef *m; - JSValue ret, func_obj; - - m = js_host_resolve_imported_module(ctx, basename, filename); + JSValue ret, err, func_obj, evaluate_resolving_funcs[2]; + JSValueConst func_data[3]; + + m = js_host_resolve_imported_module(ctx, basename, filename, attributes); if (!m) - return NULL; - + goto fail; + if (js_resolve_module(ctx, m) < 0) { js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - return NULL; + goto fail; } /* Evaluate the module code */ - func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); - ret = JS_EvalFunction(ctx, func_obj); - if (JS_IsException(ret)) - return NULL; + func_obj = JS_NewModuleValue(ctx, m); + evaluate_promise = JS_EvalFunction(ctx, func_obj); + if (JS_IsException(evaluate_promise)) { + fail: + err = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, err); + return; + } + + func_obj = JS_NewModuleValue(ctx, m); + func_data[0] = resolving_funcs[0]; + func_data[1] = resolving_funcs[1]; + func_data[2] = func_obj; + evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data); + evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data); + JS_FreeValue(ctx, func_obj); + ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs); JS_FreeValue(ctx, ret); - return m; + JS_FreeValue(ctx, evaluate_resolving_funcs[0]); + JS_FreeValue(ctx, evaluate_resolving_funcs[1]); + JS_FreeValue(ctx, evaluate_promise); +} + +/* Return a promise or an exception in case of memory error. Used by + os.Worker() */ +JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename) +{ + JSValue promise, resolving_funcs[2]; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + JS_LoadModuleInternal(ctx, basename, filename, + (JSValueConst *)resolving_funcs, JS_UNDEFINED); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; } static JSValue js_dynamic_import_job(JSContext *ctx, @@ -27858,9 +29642,9 @@ static JSValue js_dynamic_import_job(JSContext *ctx, JSValueConst *resolving_funcs = argv; JSValueConst basename_val = argv[2]; JSValueConst specifier = argv[3]; - JSModuleDef *m; + JSValueConst attributes = argv[4]; const char *basename = NULL, *filename; - JSValue ret, err, ns; + JSValue ret, err; if (!JS_IsString(basename_val)) { JS_ThrowTypeError(ctx, "no function filename for import()"); @@ -27873,25 +29657,13 @@ static JSValue js_dynamic_import_job(JSContext *ctx, filename = JS_ToCString(ctx, specifier); if (!filename) goto exception; - - m = JS_RunModule(ctx, basename, filename); - JS_FreeCString(ctx, filename); - if (!m) - goto exception; - - /* return the module namespace */ - ns = js_get_module_ns(ctx, m); - if (JS_IsException(ns)) - goto exception; - ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&ns); - JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, ns); + JS_LoadModuleInternal(ctx, basename, filename, + resolving_funcs, attributes); + JS_FreeCString(ctx, filename); JS_FreeCString(ctx, basename); return JS_UNDEFINED; exception: - err = JS_GetException(ctx); ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, (JSValueConst *)&err); @@ -27901,11 +29673,12 @@ static JSValue js_dynamic_import_job(JSContext *ctx, return JS_UNDEFINED; } -static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options) { JSAtom basename; - JSValue promise, resolving_funcs[2], basename_val; - JSValueConst args[4]; + JSValue promise, resolving_funcs[2], basename_val, err, ret; + JSValue specifier_str = JS_UNDEFINED, attributes = JS_UNDEFINED, attributes_obj = JS_UNDEFINED; + JSValueConst args[5]; basename = JS_GetScriptOrModuleName(ctx, 0); if (basename == JS_ATOM_NULL) @@ -27915,103 +29688,587 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) JS_FreeAtom(ctx, basename); if (JS_IsException(basename_val)) return basename_val; - + promise = JS_NewPromiseCapability(ctx, resolving_funcs); if (JS_IsException(promise)) { JS_FreeValue(ctx, basename_val); return promise; } + /* the string conversion must occur here */ + specifier_str = JS_ToString(ctx, specifier); + if (JS_IsException(specifier_str)) + goto exception; + + if (!JS_IsUndefined(options)) { + if (!JS_IsObject(options)) { + JS_ThrowTypeError(ctx, "options must be an object"); + goto exception; + } + attributes_obj = JS_GetProperty(ctx, options, JS_ATOM_with); + if (JS_IsException(attributes_obj)) + goto exception; + if (!JS_IsUndefined(attributes_obj)) { + JSPropertyEnum *atoms; + uint32_t atoms_len, i; + JSValue val; + + if (!JS_IsObject(attributes_obj)) { + JS_ThrowTypeError(ctx, "options.with must be an object"); + goto exception; + } + attributes = JS_NewObjectProto(ctx, JS_NULL); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &atoms_len, JS_VALUE_GET_OBJ(attributes_obj), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + goto exception; + } + for(i = 0; i < atoms_len; i++) { + val = JS_GetProperty(ctx, attributes_obj, atoms[i].atom); + if (JS_IsException(val)) + goto exception1; + if (!JS_IsString(val)) { + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "module attribute values must be strings"); + goto exception1; + } + if (JS_DefinePropertyValue(ctx, attributes, atoms[i].atom, val, + JS_PROP_C_W_E) < 0) { + exception1: + JS_FreePropertyEnum(ctx, atoms, atoms_len); + goto exception; + } + } + JS_FreePropertyEnum(ctx, atoms, atoms_len); + if (ctx->rt->module_check_attrs && + ctx->rt->module_check_attrs(ctx, ctx->rt->module_loader_opaque, attributes) < 0) { + goto exception; + } + JS_FreeValue(ctx, attributes_obj); + } + } + args[0] = resolving_funcs[0]; args[1] = resolving_funcs[1]; args[2] = basename_val; - args[3] = specifier; + args[3] = specifier_str; + args[4] = attributes; - JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); - + /* cannot run JS_LoadModuleInternal synchronously because it would + cause an unexpected recursion in js_evaluate_module() */ + JS_EnqueueJob(ctx, js_dynamic_import_job, 5, args); + done: JS_FreeValue(ctx, basename_val); JS_FreeValue(ctx, resolving_funcs[0]); JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, specifier_str); + JS_FreeValue(ctx, attributes); return promise; + exception: + JS_FreeValue(ctx, attributes_obj); + err = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, err); + goto done; } -/* Run the function of the module and of all its requested - modules. */ -static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) +static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m) +{ + m->status = JS_MODULE_STATUS_EVALUATED; + if (!JS_IsUndefined(m->promise)) { + JSValue value, ret_val; + assert(m->cycle_root == m); + value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret_val); + } +} + +typedef struct { + JSModuleDef **tab; + int count; + int size; +} ExecModuleList; + +/* XXX: slow. Could use a linked list instead of ExecModuleList */ +static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m) +{ + int i; + for(i = 0; i < exec_list->count; i++) { + if (exec_list->tab[i] == m) + return TRUE; + } + return FALSE; +} + +static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module, + ExecModuleList *exec_list) +{ + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + if (!find_in_exec_module_list(exec_list, m) && + !m->cycle_root->eval_has_exception) { + assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!m->eval_has_exception); + assert(m->async_evaluation); + assert(m->pending_async_dependencies > 0); + m->pending_async_dependencies--; + if (m->pending_async_dependencies == 0) { + if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) { + return -1; + } + exec_list->tab[exec_list->count++] = m; + if (!m->has_tla) { + if (gather_available_ancestors(ctx, m, exec_list)) + return -1; + } + } + } + } + return 0; +} + +static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque) +{ + JSModuleDef *m1 = *(JSModuleDef **)p1; + JSModuleDef *m2 = *(JSModuleDef **)p2; + return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) - + (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp); +} + +static int js_execute_async_module(JSContext *ctx, JSModuleDef *m); +static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, + JSValue *pvalue); + +static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); + JSValueConst error = argv[0]; + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + if (module->status == JS_MODULE_STATUS_EVALUATED) { + assert(module->eval_has_exception); + return JS_UNDEFINED; + } + + assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!module->eval_has_exception); + assert(module->async_evaluation); + + module->eval_has_exception = TRUE; + module->eval_exception = JS_DupValue(ctx, error); + module->status = JS_MODULE_STATUS_EVALUATED; + + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + } + + if (!JS_IsUndefined(module->promise)) { + JSValue ret_val; + assert(module->cycle_root == module); + ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, ret_val); + } + return JS_UNDEFINED; +} + +static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); + ExecModuleList exec_list_s, *exec_list = &exec_list_s; + int i; + + if (module->status == JS_MODULE_STATUS_EVALUATED) { + assert(module->eval_has_exception); + return JS_UNDEFINED; + } + assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!module->eval_has_exception); + assert(module->async_evaluation); + module->async_evaluation = FALSE; + js_set_module_evaluated(ctx, module); + + exec_list->tab = NULL; + exec_list->count = 0; + exec_list->size = 0; + + if (gather_available_ancestors(ctx, module, exec_list) < 0) { + js_free(ctx, exec_list->tab); + return JS_EXCEPTION; + } + + /* sort by increasing async_evaluation timestamp */ + rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]), + exec_module_list_cmp, NULL); + + for(i = 0; i < exec_list->count; i++) { + JSModuleDef *m = exec_list->tab[i]; + if (m->status == JS_MODULE_STATUS_EVALUATED) { + assert(m->eval_has_exception); + } else if (m->has_tla) { + js_execute_async_module(ctx, m); + } else { + JSValue error; + if (js_execute_sync_module(ctx, m, &error) < 0) { + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, + 1, (JSValueConst *)&error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + JS_FreeValue(ctx, error); + } else { + js_set_module_evaluated(ctx, m); + } + } + } + js_free(ctx, exec_list->tab); + return JS_UNDEFINED; +} + +static int js_execute_async_module(JSContext *ctx, JSModuleDef *m) +{ + JSValue promise, m_obj; + JSValue resolve_funcs[2], ret_val; + promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(promise)) + return -1; + m_obj = JS_NewModuleValue(ctx, m); + resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj); + resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj); + ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs); + JS_FreeValue(ctx, ret_val); + JS_FreeValue(ctx, m_obj); + JS_FreeValue(ctx, resolve_funcs[0]); + JS_FreeValue(ctx, resolve_funcs[1]); + JS_FreeValue(ctx, promise); + return 0; +} + +/* return < 0 in case of exception. *pvalue contains the exception. */ +static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, + JSValue *pvalue) +{ + if (m->init_func) { + /* C module init : no asynchronous execution */ + if (m->init_func(ctx, m) < 0) + goto fail; + } else { + JSValue promise; + JSPromiseStateEnum state; + + promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(promise)) + goto fail; + state = JS_PromiseState(ctx, promise); + if (state == JS_PROMISE_FULFILLED) { + JS_FreeValue(ctx, promise); + } else if (state == JS_PROMISE_REJECTED) { + *pvalue = JS_PromiseResult(ctx, promise); + JS_FreeValue(ctx, promise); + return -1; + } else { + JS_FreeValue(ctx, promise); + JS_ThrowTypeError(ctx, "promise is pending"); + fail: + *pvalue = JS_GetException(ctx); + return -1; + } + } + *pvalue = JS_UNDEFINED; + return 0; +} + +/* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1, + exception) */ +static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, + int index, JSModuleDef **pstack_top, + JSValue *pvalue) { JSModuleDef *m1; int i; - JSValue ret_val; - if (m->eval_mark) - return JS_UNDEFINED; /* avoid cycles */ + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + *pvalue = JS_GetException(ctx); + return -1; + } + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif - if (m->evaluated) { - /* if the module was already evaluated, rethrow the exception - it raised */ + if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) { if (m->eval_has_exception) { - return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception)); + *pvalue = JS_DupValue(ctx, m->eval_exception); + return -1; } else { - return JS_UNDEFINED; + *pvalue = JS_UNDEFINED; + return index; } } + if (m->status == JS_MODULE_STATUS_EVALUATING) { + *pvalue = JS_UNDEFINED; + return index; + } + assert(m->status == JS_MODULE_STATUS_LINKED); - m->eval_mark = TRUE; + m->status = JS_MODULE_STATUS_EVALUATING; + m->dfs_index = index; + m->dfs_ancestor_index = index; + m->pending_async_dependencies = 0; + index++; + /* push 'm' on stack */ + m->stack_prev = *pstack_top; + *pstack_top = m; for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; m1 = rme->module; - if (!m1->eval_mark) { - ret_val = js_evaluate_module(ctx, m1); - if (JS_IsException(ret_val)) { - m->eval_mark = FALSE; - return ret_val; + index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue); + if (index < 0) + return -1; + assert(m1->status == JS_MODULE_STATUS_EVALUATING || + m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->status == JS_MODULE_STATUS_EVALUATING) { + m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, + m1->dfs_ancestor_index); + } else { + m1 = m1->cycle_root; + assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->eval_has_exception) { + *pvalue = JS_DupValue(ctx, m1->eval_exception); + return -1; } - JS_FreeValue(ctx, ret_val); + } + if (m1->async_evaluation) { + m->pending_async_dependencies++; + if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) { + *pvalue = JS_GetException(ctx); + return -1; + } + m1->async_parent_modules[m1->async_parent_modules_count++] = m; } } - if (m->init_func) { - /* C module init */ - if (m->init_func(ctx, m) < 0) - ret_val = JS_EXCEPTION; - else - ret_val = JS_UNDEFINED; + if (m->pending_async_dependencies > 0) { + assert(!m->async_evaluation); + m->async_evaluation = TRUE; + m->async_evaluation_timestamp = + ctx->rt->module_async_evaluation_next_timestamp++; + } else if (m->has_tla) { + assert(!m->async_evaluation); + m->async_evaluation = TRUE; + m->async_evaluation_timestamp = + ctx->rt->module_async_evaluation_next_timestamp++; + js_execute_async_module(ctx, m); } else { - ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL); - m->func_obj = JS_UNDEFINED; + if (js_execute_sync_module(ctx, m, pvalue) < 0) + return -1; } - if (JS_IsException(ret_val)) { - /* save the thrown exception value */ - m->eval_has_exception = TRUE; - m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception); + + assert(m->dfs_ancestor_index <= m->dfs_index); + if (m->dfs_index == m->dfs_ancestor_index) { + for(;;) { + /* pop m1 from stack */ + m1 = *pstack_top; + *pstack_top = m1->stack_prev; + if (!m1->async_evaluation) { + m1->status = JS_MODULE_STATUS_EVALUATED; + } else { + m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC; + } + /* spec bug: cycle_root must be assigned before the test */ + m1->cycle_root = m; + if (m1 == m) + break; + } } - m->eval_mark = FALSE; - m->evaluated = TRUE; - return ret_val; + *pvalue = JS_UNDEFINED; + return index; +} + +/* Run the function of the module and of all its requested + modules. Return a promise or an exception. */ +static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *m1, *stack_top; + JSValue ret_val, result; + + assert(m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) { + m = m->cycle_root; + } + /* a promise may be created only on the cycle_root of a cycle */ + if (!JS_IsUndefined(m->promise)) + return JS_DupValue(ctx, m->promise); + m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs); + if (JS_IsException(m->promise)) + return JS_EXCEPTION; + + stack_top = NULL; + if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) { + while (stack_top != NULL) { + m1 = stack_top; + assert(m1->status == JS_MODULE_STATUS_EVALUATING); + m1->status = JS_MODULE_STATUS_EVALUATED; + m1->eval_has_exception = TRUE; + m1->eval_exception = JS_DupValue(ctx, result); + m1->cycle_root = m; /* spec bug: should be present */ + stack_top = m1->stack_prev; + } + JS_FreeValue(ctx, result); + assert(m->status == JS_MODULE_STATUS_EVALUATED); + assert(m->eval_has_exception); + ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&m->eval_exception); + JS_FreeValue(ctx, ret_val); + } else { + assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + assert(!m->eval_has_exception); + if (!m->async_evaluation) { + JSValue value; + assert(m->status == JS_MODULE_STATUS_EVALUATED); + value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret_val); + } + assert(stack_top == NULL); + } + return JS_DupValue(ctx, m->promise); +} + +static __exception int js_parse_with_clause(JSParseState *s, JSReqModuleEntry *rme) +{ + JSContext *ctx = s->ctx; + JSAtom key; + int ret; + const uint8_t *key_token_ptr; + + if (next_token(s)) + return -1; + if (js_parse_expect(s, '{')) + return -1; + while (s->token.val != '}') { + key_token_ptr = s->token.ptr; + if (s->token.val == TOK_STRING) { + key = JS_ValueToAtom(ctx, s->token.u.str.str); + if (key == JS_ATOM_NULL) + return -1; + } else { + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + key = JS_DupAtom(ctx, s->token.u.ident.atom); + } + if (next_token(s)) + return -1; + if (js_parse_expect(s, ':')) { + JS_FreeAtom(ctx, key); + return -1; + } + if (s->token.val != TOK_STRING) { + js_parse_error_pos(s, key_token_ptr, "string expected"); + return -1; + } + if (JS_IsUndefined(rme->attributes)) { + JSValue attributes = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(attributes)) { + JS_FreeAtom(ctx, key); + return -1; + } + rme->attributes = attributes; + } + ret = JS_HasProperty(ctx, rme->attributes, key); + if (ret != 0) { + JS_FreeAtom(ctx, key); + if (ret < 0) + return -1; + else + return js_parse_error(s, "duplicate with key"); + } + ret = JS_DefinePropertyValue(ctx, rme->attributes, key, + JS_DupValue(ctx, s->token.u.str.str), JS_PROP_C_W_E); + JS_FreeAtom(ctx, key); + if (ret < 0) + return -1; + if (next_token(s)) + return -1; + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (!JS_IsUndefined(rme->attributes) && + ctx->rt->module_check_attrs && + ctx->rt->module_check_attrs(ctx, ctx->rt->module_loader_opaque, rme->attributes) < 0) { + return -1; + } + return js_parse_expect(s, '}'); } -static __exception JSAtom js_parse_from_clause(JSParseState *s) +/* return the module index in m->req_module_entries[] or < 0 if error */ +static __exception int js_parse_from_clause(JSParseState *s, JSModuleDef *m) { JSAtom module_name; + int idx; + if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { js_parse_error(s, "from clause expected"); - return JS_ATOM_NULL; + return -1; } if (next_token(s)) - return JS_ATOM_NULL; + return -1; if (s->token.val != TOK_STRING) { js_parse_error(s, "string expected"); - return JS_ATOM_NULL; + return -1; } module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); if (module_name == JS_ATOM_NULL) - return JS_ATOM_NULL; + return -1; if (next_token(s)) { JS_FreeAtom(s->ctx, module_name); - return JS_ATOM_NULL; + return -1; } - return module_name; + + idx = add_req_module_entry(s->ctx, m, module_name); + JS_FreeAtom(s->ctx, module_name); + if (idx < 0) + return -1; + if (s->token.val == TOK_WITH) { + if (js_parse_with_clause(s, &m->req_module_entries[idx])) + return -1; + } + return idx; } static __exception int js_parse_export(JSParseState *s) @@ -28020,7 +30277,6 @@ static __exception int js_parse_export(JSParseState *s) JSModuleDef *m = s->cur_func->module; JSAtom local_name, export_name; int first_export, idx, i, tok; - JSAtom module_name; JSExportEntry *me; if (next_token(s)) @@ -28034,7 +30290,7 @@ static __exception int js_parse_export(JSParseState *s) peek_token(s, TRUE) == TOK_FUNCTION)) { return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num, + s->token.ptr, JS_PARSE_EXPORT_NAMED, NULL); } @@ -28056,11 +30312,21 @@ static __exception int js_parse_export(JSParseState *s) if (token_is_pseudo_keyword(s, JS_ATOM_as)) { if (next_token(s)) goto fail; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - goto fail; + if (s->token.val == TOK_STRING) { + if (js_string_find_invalid_codepoint(JS_VALUE_GET_STRING(s->token.u.str.str)) >= 0) { + js_parse_error(s, "contains unpaired surrogate"); + goto fail; + } + export_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); + if (export_name == JS_ATOM_NULL) + goto fail; + } else { + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + goto fail; + } + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); } - export_name = JS_DupAtom(ctx, s->token.u.ident.atom); if (next_token(s)) { fail: JS_FreeAtom(ctx, local_name); @@ -28085,11 +30351,7 @@ static __exception int js_parse_export(JSParseState *s) if (js_parse_expect(s, '}')) return -1; if (token_is_pseudo_keyword(s, JS_ATOM_from)) { - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - return -1; - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); + idx = js_parse_from_clause(s, m); if (idx < 0) return -1; for(i = first_export; i < m->export_entries_count; i++) { @@ -28111,11 +30373,7 @@ static __exception int js_parse_export(JSParseState *s) export_name = JS_DupAtom(ctx, s->token.u.ident.atom); if (next_token(s)) goto fail1; - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - goto fail1; - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); + idx = js_parse_from_clause(s, m); if (idx < 0) goto fail1; me = add_export_entry(s, m, JS_ATOM__star_, export_name, @@ -28125,11 +30383,7 @@ static __exception int js_parse_export(JSParseState *s) return -1; me->u.req_module_idx = idx; } else { - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - return -1; - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); + idx = js_parse_from_clause(s, m); if (idx < 0) return -1; if (add_star_export_entry(ctx, m, idx) < 0) @@ -28144,7 +30398,7 @@ static __exception int js_parse_export(JSParseState *s) peek_token(s, TRUE) == TOK_FUNCTION)) { return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num, + s->token.ptr, JS_PARSE_EXPORT_DEFAULT, NULL); } else { if (js_parse_assign_expr(s)) @@ -28183,12 +30437,11 @@ static int add_closure_var(JSContext *ctx, JSFunctionDef *s, JSVarKindEnum var_kind); static int add_import(JSParseState *s, JSModuleDef *m, - JSAtom local_name, JSAtom import_name) + JSAtom local_name, JSAtom import_name, BOOL is_star) { JSContext *ctx = s->ctx; int i, var_idx; JSImportEntry *mi; - BOOL is_local; if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval) return js_parse_error(s, "invalid import binding"); @@ -28200,8 +30453,7 @@ static int add_import(JSParseState *s, JSModuleDef *m, } } - is_local = (import_name == JS_ATOM__star_); - var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE, + var_idx = add_closure_var(ctx, s->cur_func, is_star, FALSE, m->import_entries_count, local_name, TRUE, TRUE, FALSE); if (var_idx < 0) @@ -28214,6 +30466,7 @@ static int add_import(JSParseState *s, JSModuleDef *m, mi = &m->import_entries[m->import_entries_count++]; mi->import_name = JS_DupAtom(ctx, import_name); mi->var_idx = var_idx; + mi->is_star = is_star; return 0; } @@ -28236,6 +30489,14 @@ static __exception int js_parse_import(JSParseState *s) JS_FreeAtom(ctx, module_name); return -1; } + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + if (s->token.val == TOK_WITH) { + if (js_parse_with_clause(s, &m->req_module_entries[idx])) + return -1; + } } else { if (s->token.val == TOK_IDENT) { if (s->token.u.ident.is_reserved) { @@ -28246,7 +30507,7 @@ static __exception int js_parse_import(JSParseState *s) import_name = JS_ATOM_default; if (next_token(s)) goto fail; - if (add_import(s, m, local_name, import_name)) + if (add_import(s, m, local_name, import_name, FALSE)) goto fail; JS_FreeAtom(ctx, local_name); @@ -28272,7 +30533,7 @@ static __exception int js_parse_import(JSParseState *s) import_name = JS_ATOM__star_; if (next_token(s)) goto fail; - if (add_import(s, m, local_name, import_name)) + if (add_import(s, m, local_name, import_name, TRUE)) goto fail; JS_FreeAtom(ctx, local_name); } else if (s->token.val == '{') { @@ -28280,11 +30541,24 @@ static __exception int js_parse_import(JSParseState *s) return -1; while (s->token.val != '}') { - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; + BOOL is_string; + if (s->token.val == TOK_STRING) { + is_string = TRUE; + if (js_string_find_invalid_codepoint(JS_VALUE_GET_STRING(s->token.u.str.str)) >= 0) { + js_parse_error(s, "contains unpaired surrogate"); + return -1; + } + import_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); + if (import_name == JS_ATOM_NULL) + return -1; + } else { + is_string = FALSE; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + import_name = JS_DupAtom(ctx, s->token.u.ident.atom); } - import_name = JS_DupAtom(ctx, s->token.u.ident.atom); local_name = JS_ATOM_NULL; if (next_token(s)) goto fail; @@ -28296,16 +30570,19 @@ static __exception int js_parse_import(JSParseState *s) goto fail; } local_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) { + if (next_token(s)) + goto fail; + } else { + if (is_string) { + js_parse_error(s, "expecting 'as'"); fail: JS_FreeAtom(ctx, local_name); JS_FreeAtom(ctx, import_name); return -1; } - } else { local_name = JS_DupAtom(ctx, import_name); } - if (add_import(s, m, local_name, import_name)) + if (add_import(s, m, local_name, import_name, FALSE)) goto fail; JS_FreeAtom(ctx, local_name); JS_FreeAtom(ctx, import_name); @@ -28318,14 +30595,10 @@ static __exception int js_parse_import(JSParseState *s) return -1; } end_import_clause: - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) + idx = js_parse_from_clause(s, m); + if (idx < 0) return -1; } - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); - if (idx < 0) - return -1; for(i = first_import; i < m->import_entries_count; i++) m->import_entries[i].req_module_idx = idx; @@ -28336,13 +30609,13 @@ static __exception int js_parse_source_element(JSParseState *s) { JSFunctionDef *fd = s->cur_func; int tok; - + if (s->token.val == TOK_FUNCTION || (token_is_pseudo_keyword(s, JS_ATOM_async) && peek_token(s, TRUE) == TOK_FUNCTION)) { if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) + s->token.ptr)) return -1; } else if (s->token.val == TOK_EXPORT && fd->module) { if (js_parse_export(s)) @@ -28364,7 +30637,9 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, JSFunctionDef *parent, BOOL is_eval, BOOL is_func_expr, - const char *filename, int line_num) + const char *filename, + const uint8_t *source_ptr, + GetLineColCache *get_line_col_cache) { JSFunctionDef *fd; @@ -28383,6 +30658,8 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, fd->js_mode = parent->js_mode; fd->parent_scope_level = parent->scope_level; } + fd->strip_debug = ((ctx->rt->strip_flags & JS_STRIP_DEBUG) != 0); + fd->strip_source = ((ctx->rt->strip_flags & (JS_STRIP_DEBUG | JS_STRIP_SOURCE)) != 0); fd->is_eval = is_eval; fd->is_func_expr = is_func_expr; @@ -28411,13 +30688,13 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, fd->body_scope = -1; fd->filename = JS_NewAtom(ctx, filename); - fd->line_num = line_num; - + fd->source_pos = source_ptr - get_line_col_cache->buf_start; + fd->get_line_col_cache = get_line_col_cache; + js_dbuf_init(ctx, &fd->pc2line); //fd->pc2line_last_line_num = line_num; //fd->pc2line_last_pc = 0; - fd->last_opcode_line_num = line_num; - + fd->last_opcode_source_ptr = source_ptr; return fd; } @@ -28428,7 +30705,7 @@ static void free_bytecode_atoms(JSRuntime *rt, int pos, len, op; JSAtom atom; const JSOpCode *oi; - + pos = 0; while (pos < bc_len) { op = bc_buf[pos]; @@ -28436,7 +30713,7 @@ static void free_bytecode_atoms(JSRuntime *rt, oi = &short_opcode_info(op); else oi = &opcode_info[op]; - + len = oi->size; switch(oi->fmt) { case OP_FMT_atom: @@ -28444,6 +30721,8 @@ static void free_bytecode_atoms(JSRuntime *rt, case OP_FMT_atom_u16: case OP_FMT_atom_label_u8: case OP_FMT_atom_label_u16: + if ((pos + 1 + 4) > bc_len) + break; /* may happen if there is not enough memory when emiting bytecode */ atom = get_u32(bc_buf + pos + 1); JS_FreeAtomRT(rt, atom); break; @@ -28546,14 +30825,19 @@ static void dump_byte_code(JSContext *ctx, int pass, const JSVarDef *vars, int var_count, const JSClosureVar *closure_var, int closure_var_count, const JSValue *cpool, uint32_t cpool_count, - const char *source, int line_num, + const char *source, const LabelSlot *label_slots, JSFunctionBytecode *b) { const JSOpCode *oi; - int pos, pos_next, op, size, idx, addr, line, line1, in_source; + int pos, pos_next, op, size, idx, addr, line, line1, in_source, line_num; uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits)); BOOL use_short_opcodes = (b != NULL); + if (b) { + int col_num; + line_num = find_line_num(ctx, b, -1, &col_num); + } + /* scan for jump targets */ for (pos = 0; pos < len; pos = pos_next) { op = tab[pos]; @@ -28606,10 +30890,12 @@ static void dump_byte_code(JSContext *ctx, int pass, pos = 0; while (pos < len) { op = tab[pos]; - if (source) { + if (source && b) { + int col_num; if (b) { - line1 = find_line_num(ctx, b, pos) - line_num + 1; + line1 = find_line_num(ctx, b, pos, &col_num) - line_num + 1; } else if (op == OP_line_num) { + /* XXX: no longer works */ line1 = get_u32(tab + pos + 1) - line_num + 1; } if (line1 > line) { @@ -28727,7 +31013,7 @@ static void dump_byte_code(JSContext *ctx, int pass, has_pool_idx: printf(" %u: ", idx); if (idx < cpool_count) { - JS_DumpValue(ctx, cpool[idx]); + JS_PrintValue(ctx, js_dump_value_write, stdout, cpool[idx], NULL); } break; case OP_FMT_atom: @@ -28810,49 +31096,64 @@ static void dump_byte_code(JSContext *ctx, int pass, js_free(ctx, bits); } -static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len, - int line_num) +static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len) { - const uint8_t *p_end, *p_next, *p; - int pc, v; + const uint8_t *p_end, *p; + int pc, v, line_num, col_num, ret; unsigned int op; - + uint32_t val; + if (len <= 0) return; - printf("%5s %5s\n", "PC", "LINE"); + printf("%5s %5s %5s\n", "PC", "LINE", "COL"); p = buf; p_end = buf + len; + + /* get the function line and column numbers */ + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + p += ret; + line_num = val + 1; + + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + p += ret; + col_num = val + 1; + + printf("%5s %5d %5d\n", "-", line_num, col_num); + pc = 0; while (p < p_end) { op = *p++; if (op == 0) { - v = unicode_from_utf8(p, p_end - p, &p_next); - if (v < 0) + ret = get_leb128(&val, p, p_end); + if (ret < 0) goto fail; - pc += v; - p = p_next; - v = unicode_from_utf8(p, p_end - p, &p_next); - if (v < 0) { - fail: - printf("invalid pc2line encode pos=%d\n", (int)(p - buf)); - return; - } - if (!(v & 1)) { - v = v >> 1; - } else { - v = -(v >> 1) - 1; - } + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; line_num += v; - p = p_next; } else { op -= PC2LINE_OP_FIRST; pc += (op / PC2LINE_RANGE); line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE; } - printf("%5d %5d\n", pc, line_num); + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + col_num += v; + + printf("%5d %5d %5d\n", pc, line_num, col_num); } + fail: ; } static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b) @@ -28862,8 +31163,10 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB const char *str; if (b->has_debug && b->debug.filename != JS_ATOM_NULL) { + int line_num, col_num; str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename); - printf("%s:%d: ", str, b->debug.line_num); + line_num = find_line_num(ctx, b, -1, &col_num); + printf("%s:%d:%d: ", str, line_num, col_num); } str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name); @@ -28872,10 +31175,6 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB printf(" mode:"); if (b->js_mode & JS_MODE_STRICT) printf(" strict"); -#ifdef CONFIG_BIGNUM - if (b->js_mode & JS_MODE_MATH) - printf(" math"); -#endif printf("\n"); } if (b->arg_count && b->vardefs) { @@ -28922,10 +31221,10 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB b->closure_var, b->closure_var_count, b->cpool, b->cpool_count, b->has_debug ? b->debug.source : NULL, - b->has_debug ? b->debug.line_num : -1, NULL, b); + NULL, b); #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32) if (b->has_debug) - dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num); + dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len); #endif printf("\n"); } @@ -29213,13 +31512,19 @@ static void var_object_test(JSContext *ctx, JSFunctionDef *s, { dbuf_putc(bc, get_with_scope_opcode(op)); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - *plabel_done = new_label_fd(s, *plabel_done); + if (*plabel_done < 0) { + *plabel_done = new_label_fd(s); + if (*plabel_done < 0) { + dbuf_set_error(bc); + return; + } + } dbuf_put_u32(bc, *plabel_done); dbuf_putc(bc, is_with); update_label(s, *plabel_done, 1); s->jump_size++; } - + /* return the position of the next opcode */ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, JSAtom var_name, int scope_level, int op, @@ -29342,6 +31647,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, case OP_scope_get_ref: dbuf_putc(bc, OP_undefined); /* fall thru */ + case OP_scope_get_var_checkthis: case OP_scope_get_var_undef: case OP_scope_get_var: case OP_scope_put_var: @@ -29367,7 +31673,12 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } } else { if (s->vars[var_idx].is_lexical) { - dbuf_putc(bc, OP_get_loc_check); + if (op == OP_scope_get_var_checkthis) { + /* only used for 'this' return in derived class constructors */ + dbuf_putc(bc, OP_get_loc_checkthis); + } else { + dbuf_putc(bc, OP_get_loc_check); + } } else { dbuf_putc(bc, OP_get_loc); } @@ -29425,7 +31736,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, is_arg_scope = (idx == ARG_SCOPE_END); if (var_idx >= 0) break; - + if (!is_arg_scope) { var_idx = find_var(ctx, fd, var_name); if (var_idx >= 0) @@ -29469,7 +31780,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, dbuf_put_u16(bc, idx); var_object_test(ctx, s, var_name, op, bc, &label_done, 0); } - + if (fd->is_eval) break; /* it it necessarily the top level function */ } @@ -29672,7 +31983,7 @@ static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx) { /* if the field is not initialized, the error is catched when accessing it */ - if (is_ref) + if (is_ref) dbuf_putc(bc, OP_get_var_ref); else dbuf_putc(bc, OP_get_loc); @@ -29687,7 +31998,7 @@ static int resolve_scope_private_field1(JSContext *ctx, int idx, var_kind; JSFunctionDef *fd; BOOL is_ref; - + fd = s; is_ref = FALSE; for(;;) { @@ -29831,6 +32142,10 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, abort(); } break; + case OP_scope_in_private_field: + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_private_in); + break; default: abort(); } @@ -30015,7 +32330,7 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, int i, count; JSVarDef *vd; BOOL is_arg_scope; - + count = b->arg_count + b->var_count + b->closure_var_count; s->closure_var = NULL; s->closure_var_count = 0; @@ -30248,8 +32563,11 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy evaluating the module so that the exported functions are visible if there are cyclic module references */ if (s->module) { - label_next = new_label_fd(s, -1); - + label_next = new_label_fd(s); + if (label_next < 0) { + dbuf_set_error(bc); + return; + } /* if 'this' is true, initialize the global variables and return */ dbuf_putc(bc, OP_push_this); dbuf_putc(bc, OP_if_false); @@ -30257,7 +32575,7 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy update_label(s, label_next, 1); s->jump_size++; } - + /* add the global variables (only happens if s->is_global_var is true) */ for(i = 0; i < s->global_var_count; i++) { @@ -30286,7 +32604,7 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy } if (!has_closure) { int flags; - + flags = 0; if (s->eval_type != JS_EVAL_TYPE_GLOBAL) flags |= JS_PROP_CONFIGURABLE; @@ -30294,11 +32612,11 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy /* global function definitions need a specific handling */ dbuf_putc(bc, OP_fclosure); dbuf_put_u32(bc, hf->cpool_idx); - + dbuf_putc(bc, OP_define_func); dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); dbuf_putc(bc, flags); - + goto done_global_var; } else { if (hf->is_lexical) { @@ -30342,7 +32660,7 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy if (s->module) { dbuf_putc(bc, OP_return_undef); - + dbuf_putc(bc, OP_label); dbuf_put_u32(bc, label_next); s->label_slots[label_next].pos2 = bc->size; @@ -30447,7 +32765,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) for(i = 0; i < s->global_var_count; i++) { JSGlobalVar *hf = &s->global_vars[i]; int flags; - + /* check if global variable (XXX: simplify) */ for(idx = 0; idx < s->closure_var_count; idx++) { JSClosureVar *cv = &s->closure_var[idx]; @@ -30469,7 +32787,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) cv->var_name == JS_ATOM__arg_var_) goto next; } - + dbuf_putc(&bc_out, OP_check_define_var); dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); flags = 0; @@ -30499,15 +32817,16 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) mark_eval_captured_variables(ctx, s, scope); dbuf_putc(&bc_out, op); dbuf_put_u16(&bc_out, call_argc); - dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); + dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); } break; case OP_apply_eval: /* convert scope index to adjusted variable index */ scope = get_u16(bc_buf + pos + 1); mark_eval_captured_variables(ctx, s, scope); dbuf_putc(&bc_out, op); - dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); + dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); break; + case OP_scope_get_var_checkthis: case OP_scope_get_var_undef: case OP_scope_get_var: case OP_scope_put_var: @@ -30537,6 +32856,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) case OP_scope_get_private_field: case OP_scope_get_private_field2: case OP_scope_put_private_field: + case OP_scope_in_private_field: { int ret; var_name = get_u32(bc_buf + pos + 1); @@ -30748,7 +33068,18 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) case OP_set_class_name: /* only used during parsing */ break; - + + case OP_get_field_opt_chain: /* equivalent to OP_get_field */ + { + JSAtom name = get_u32(bc_buf + pos + 1); + dbuf_putc(&bc_out, OP_get_field); + dbuf_put_u32(&bc_out, name); + } + break; + case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */ + dbuf_putc(&bc_out, OP_get_array_el); + break; + default: no_change: dbuf_put(&bc_out, bc_buf + pos, len); @@ -30778,40 +33109,58 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) return -1; } -/* the pc2line table gives a line number for each PC value */ -static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num) +/* the pc2line table gives a source position for each PC value */ +static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, uint32_t source_pos) { if (s->line_number_slots != NULL && s->line_number_count < s->line_number_size && pc >= s->line_number_last_pc - && line_num != s->line_number_last) { + && source_pos != s->line_number_last) { s->line_number_slots[s->line_number_count].pc = pc; - s->line_number_slots[s->line_number_count].line_num = line_num; + s->line_number_slots[s->line_number_count].source_pos = source_pos; s->line_number_count++; s->line_number_last_pc = pc; - s->line_number_last = line_num; + s->line_number_last = source_pos; } } +/* XXX: could use a more compact storage */ +/* XXX: get_line_col_cached() is slow. For more predictable + performance, line/cols could be stored every N source + bytes. Alternatively, get_line_col_cached() could be issued in + emit_source_pos() so that the deltas are more likely to be + small. */ static void compute_pc2line_info(JSFunctionDef *s) { - if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) { - int last_line_num = s->line_num; + if (!s->strip_debug) { + int last_line_num, last_col_num; uint32_t last_pc = 0; - int i; - + int i, line_num, col_num; + const uint8_t *buf_start = s->get_line_col_cache->buf_start; js_dbuf_init(s->ctx, &s->pc2line); + + last_line_num = get_line_col_cached(s->get_line_col_cache, + &last_col_num, + buf_start + s->source_pos); + dbuf_put_leb128(&s->pc2line, last_line_num); /* line number minus 1 */ + dbuf_put_leb128(&s->pc2line, last_col_num); /* column number minus 1 */ + for (i = 0; i < s->line_number_count; i++) { uint32_t pc = s->line_number_slots[i].pc; - int line_num = s->line_number_slots[i].line_num; - int diff_pc, diff_line; + uint32_t source_pos = s->line_number_slots[i].source_pos; + int diff_pc, diff_line, diff_col; - if (line_num < 0) + if (source_pos == -1) continue; - diff_pc = pc - last_pc; + if (diff_pc < 0) + continue; + + line_num = get_line_col_cached(s->get_line_col_cache, &col_num, + buf_start + source_pos); diff_line = line_num - last_line_num; - if (diff_line == 0 || diff_pc < 0) + diff_col = col_num - last_col_num; + if (diff_line == 0 && diff_col == 0) continue; if (diff_line >= PC2LINE_BASE && @@ -30825,8 +33174,11 @@ static void compute_pc2line_info(JSFunctionDef *s) dbuf_put_leb128(&s->pc2line, diff_pc); dbuf_put_sleb128(&s->pc2line, diff_line); } + dbuf_put_sleb128(&s->pc2line, diff_col); + last_pc = pc; last_line_num = line_num; + last_col_num = col_num; } } } @@ -30872,10 +33224,11 @@ static BOOL code_has_label(CodeContext *s, int pos, int label) /* return the target label, following the OP_goto jumps the first opcode at destination is stored in *pop */ -static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline) +static int find_jump_target(JSFunctionDef *s, int label0, int *pop, int *pline) { - int i, pos, op; + int i, pos, op, label; + label = label0; update_label(s, label, -1); for (i = 0; i < 10; i++) { assert(label >= 0 && label < s->label_count); @@ -30906,6 +33259,19 @@ static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline) } } /* cycle detected, could issue a warning */ + /* XXX: the combination of find_jump_target() and skip_dead_code() + seems incorrect with cyclic labels. See for exemple: + + for (;;) { + l:break l; + l:break l; + l:break l; + l:break l; + } + + Avoiding changing the target is just a workaround and might not + suffice to completely fix the problem. */ + label = label0; done: *pop = op; update_label(s, label, +1); @@ -31008,7 +33374,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) label_slots = s->label_slots; - line_num = s->line_num; + line_num = s->source_pos; cc.bc_buf = bc_buf = s->byte_code.buf; cc.bc_len = bc_len = s->byte_code.size; @@ -31022,11 +33388,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) } #endif /* XXX: Should skip this phase if not generating SHORT_OPCODES */ - if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) { + if (s->line_number_size && !s->strip_debug) { s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size); if (s->line_number_slots == NULL) return -1; - s->line_number_last = s->line_num; + s->line_number_last = s->source_pos; s->line_number_last_pc = 0; } @@ -31175,7 +33541,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) { /* jump to return/throw: remove jump, append return/throw */ /* updating the line number obfuscates assembly listing */ - //if (line1 >= 0) line_num = line1; + //if (line1 != -1) line_num = line1; update_label(s, label, -1); add_pc2line_info(s, bc_out.size, line_num); dbuf_putc(&bc_out, op1); @@ -31223,7 +33589,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) int pos1 = cc.pos; int line1 = cc.line_num; if (code_has_label(&cc, pos1, label)) { - if (line1 >= 0) line_num = line1; + if (line1 != -1) line_num = line1; pos_next = pos1; update_label(s, label, -1); label = cc.label; @@ -31296,7 +33662,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) case OP_with_delete_var: case OP_with_make_ref: case OP_with_get_ref: - case OP_with_get_ref_undef: { JSAtom atom; int is_with; @@ -31423,6 +33788,26 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) } goto no_change; + case OP_push_bigint_i32: + if (OPTIMIZE) { + /* transform i32(val) neg -> i32(-val) */ + val = get_i32(bc_buf + pos + 1); + if (val != INT32_MIN + && code_match(&cc, pos_next, OP_neg, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (code_match(&cc, cc.pos, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + } else { + add_pc2line_info(s, bc_out.size, line_num); + dbuf_putc(&bc_out, OP_push_bigint_i32); + dbuf_put_u32(&bc_out, -val); + } + pos_next = cc.pos; + break; + } + } + goto no_change; + #if SHORT_OPCODES case OP_push_const: case OP_fclosure: @@ -31471,9 +33856,8 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) goto no_change; case OP_to_propkey: - case OP_to_propkey2: if (OPTIMIZE) { - /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */ + /* remove redundant to_propkey opcodes when storing simple data */ if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1) || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) { @@ -31880,6 +34264,7 @@ typedef struct StackSizeState { int bc_len; int stack_len_max; uint16_t *stack_level_tab; + int32_t *catch_pos_tab; int *pc_stack; int pc_stack_len; int pc_stack_size; @@ -31887,7 +34272,7 @@ typedef struct StackSizeState { /* 'op' is only used for error indication */ static __exception int ss_check(JSContext *ctx, StackSizeState *s, - int pos, int op, int stack_len) + int pos, int op, int stack_len, int catch_pos) { if ((unsigned)pos >= s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); @@ -31906,6 +34291,10 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s, JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)", s->stack_level_tab[pos], stack_len, pos); return -1; + } else if (s->catch_pos_tab[pos] != catch_pos) { + JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)", + s->catch_pos_tab[pos], catch_pos, pos); + return -1; } else { return 0; } @@ -31913,6 +34302,7 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s, /* mark as explored and store the stack size */ s->stack_level_tab[pos] = stack_len; + s->catch_pos_tab[pos] = catch_pos; /* queue the new PC to explore */ if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), @@ -31927,7 +34317,7 @@ static __exception int compute_stack_size(JSContext *ctx, int *pstack_size) { StackSizeState s_s, *s = &s_s; - int i, diff, n_pop, pos_next, stack_len, pos, op; + int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level; const JSOpCode *oi; const uint8_t *bc_buf; @@ -31940,24 +34330,33 @@ static __exception int compute_stack_size(JSContext *ctx, return -1; for(i = 0; i < s->bc_len; i++) s->stack_level_tab[i] = 0xffff; - s->stack_len_max = 0; s->pc_stack = NULL; + s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * + s->bc_len); + if (!s->catch_pos_tab) + goto fail; + + s->stack_len_max = 0; s->pc_stack_len = 0; s->pc_stack_size = 0; /* breadth-first graph exploration */ - if (ss_check(ctx, s, 0, OP_invalid, 0)) + if (ss_check(ctx, s, 0, OP_invalid, 0, -1)) goto fail; while (s->pc_stack_len > 0) { pos = s->pc_stack[--s->pc_stack_len]; stack_len = s->stack_level_tab[pos]; + catch_pos = s->catch_pos_tab[pos]; op = bc_buf[pos]; if (op == 0 || op >= OP_COUNT) { JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); goto fail; } oi = &short_opcode_info(op); +#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64) + printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos); +#endif pos_next = pos + oi->size; if (pos_next > s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); @@ -32013,55 +34412,103 @@ static __exception int compute_stack_size(JSContext *ctx, case OP_if_true8: case OP_if_false8: diff = (int8_t)bc_buf[pos + 1]; - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) goto fail; break; #endif case OP_if_true: case OP_if_false: - case OP_catch: diff = get_u32(bc_buf + pos + 1); - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) goto fail; break; case OP_gosub: diff = get_u32(bc_buf + pos + 1); - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1)) + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos)) goto fail; break; case OP_with_get_var: case OP_with_delete_var: diff = get_u32(bc_buf + pos + 5); - if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1)) + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos)) goto fail; break; case OP_with_make_ref: case OP_with_get_ref: - case OP_with_get_ref_undef: diff = get_u32(bc_buf + pos + 5); - if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2)) + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos)) goto fail; break; case OP_with_put_var: diff = get_u32(bc_buf + pos + 5); - if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1)) + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos)) goto fail; break; - + case OP_catch: + diff = get_u32(bc_buf + pos + 1); + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) + goto fail; + catch_pos = pos; + break; + case OP_for_of_start: + case OP_for_await_of_start: + catch_pos = pos; + break; + /* we assume the catch offset entry is only removed with + some op codes */ + case OP_drop: + catch_level = stack_len; + goto check_catch; + case OP_nip: + catch_level = stack_len - 1; + goto check_catch; + case OP_nip1: + catch_level = stack_len - 1; + goto check_catch; + case OP_iterator_close: + catch_level = stack_len + 2; + check_catch: + /* Note: for for_of_start/for_await_of_start we consider + the catch offset is on the first stack entry instead of + the thirst */ + if (catch_pos >= 0) { + int level; + level = s->stack_level_tab[catch_pos]; + if (bc_buf[catch_pos] != OP_catch) + level++; /* for_of_start, for_wait_of_start */ + /* catch_level = stack_level before op_catch is executed ? */ + if (catch_level == level) { + catch_pos = s->catch_pos_tab[catch_pos]; + } + } + break; + case OP_nip_catch: + if (catch_pos < 0) { + JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos); + goto fail; + } + stack_len = s->stack_level_tab[catch_pos]; + if (bc_buf[catch_pos] != OP_catch) + stack_len++; /* for_of_start, for_wait_of_start */ + stack_len++; /* no stack overflow is possible by construction */ + catch_pos = s->catch_pos_tab[catch_pos]; + break; default: break; } - if (ss_check(ctx, s, pos_next, op, stack_len)) + if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos)) goto fail; done_insn: ; } - js_free(ctx, s->stack_level_tab); js_free(ctx, s->pc_stack); + js_free(ctx, s->catch_pos_tab); + js_free(ctx, s->stack_level_tab); *pstack_size = s->stack_len_max; return 0; fail: - js_free(ctx, s->stack_level_tab); js_free(ctx, s->pc_stack); + js_free(ctx, s->catch_pos_tab); + js_free(ctx, s->stack_level_tab); *pstack_size = 0; return -1; } @@ -32167,12 +34614,12 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4) - if (!(fd->js_mode & JS_MODE_STRIP)) { + if (!fd->strip_debug) { printf("pass 1\n"); dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size, fd->args, fd->arg_count, fd->vars, fd->var_count, fd->closure_var, fd->closure_var_count, - fd->cpool, fd->cpool_count, fd->source, fd->line_num, + fd->cpool, fd->cpool_count, fd->source, fd->label_slots, NULL); printf("\n"); } @@ -32182,12 +34629,12 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) goto fail; #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2) - if (!(fd->js_mode & JS_MODE_STRIP)) { + if (!fd->strip_debug) { printf("pass 2\n"); dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size, fd->args, fd->arg_count, fd->vars, fd->var_count, fd->closure_var, fd->closure_var_count, - fd->cpool, fd->cpool_count, fd->source, fd->line_num, + fd->cpool, fd->cpool_count, fd->source, fd->label_slots, NULL); printf("\n"); } @@ -32199,7 +34646,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) if (compute_stack_size(ctx, fd, &stack_size) < 0) goto fail; - if (fd->js_mode & JS_MODE_STRIP) { + if (fd->strip_debug) { function_size = offsetof(JSFunctionBytecode, debug); } else { function_size = sizeof(*b); @@ -32207,7 +34654,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) cpool_offset = function_size; function_size += fd->cpool_count * sizeof(*fd->cpool); vardefs_offset = function_size; - if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) { + if (!fd->strip_debug || fd->has_eval_call) { function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs); } closure_var_offset = function_size; @@ -32228,7 +34675,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b->func_name = fd->func_name; if (fd->arg_count + fd->var_count > 0) { - if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) { + if (fd->strip_debug && !fd->has_eval_call) { /* Strip variable definitions not needed at runtime */ int i; for(i = 0; i < fd->var_count; i++) { @@ -32243,8 +34690,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } } else { b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); - memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); + memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); + memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); } b->var_count = fd->var_count; b->arg_count = fd->arg_count; @@ -32262,7 +34709,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b->stack_size = stack_size; - if (fd->js_mode & JS_MODE_STRIP) { + if (fd->strip_debug) { JS_FreeAtom(ctx, fd->filename); dbuf_free(&fd->pc2line); // probably useless } else { @@ -32271,7 +34718,6 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) */ b->has_debug = 1; b->debug.filename = fd->filename; - b->debug.line_num = fd->line_num; //DynBuf pc2line; //compute_pc2line_info(fd, &pc2line); @@ -32305,13 +34751,14 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b->super_call_allowed = fd->super_call_allowed; b->super_allowed = fd->super_allowed; b->arguments_allowed = fd->arguments_allowed; - b->backtrace_barrier = fd->backtrace_barrier; + b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT || + fd->eval_type == JS_EVAL_TYPE_INDIRECT); b->realm = JS_DupContext(ctx); add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - + #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1) - if (!(fd->js_mode & JS_MODE_STRIP)) { + if (!fd->strip_debug) { js_dump_function_bytecode(ctx, b); } #endif @@ -32453,20 +34900,16 @@ static __exception int js_parse_directives(JSParseState *s) s->cur_func->has_use_strict = TRUE; s->cur_func->js_mode |= JS_MODE_STRICT; } -#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8) - else if (!strcmp(str, "use strip")) { - s->cur_func->js_mode |= JS_MODE_STRIP; - } -#endif -#ifdef CONFIG_BIGNUM - else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { - s->cur_func->js_mode |= JS_MODE_MATH; - } -#endif } return js_parse_seek_token(s, &pos); } +/* return TRUE if the keyword is forbidden only in strict mode */ +static BOOL is_strict_future_keyword(JSAtom atom) +{ + return (atom >= JS_ATOM_LAST_KEYWORD + 1 && atom <= JS_ATOM_LAST_STRICT_KEYWORD); +} + static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, JSAtom func_name) { @@ -32477,13 +34920,15 @@ static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, if (!fd->has_simple_parameter_list && fd->has_use_strict) { return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); } - if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) { + if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments || + is_strict_future_keyword(func_name)) { return js_parse_error(s, "invalid function name in strict code"); } for (idx = 0; idx < fd->arg_count; idx++) { name = fd->args[idx].var_name; - if (name == JS_ATOM_eval || name == JS_ATOM_arguments) { + if (name == JS_ATOM_eval || name == JS_ATOM_arguments || + is_strict_future_keyword(name)) { return js_parse_error(s, "invalid argument name in strict code"); } } @@ -32521,15 +34966,16 @@ static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) { JSFunctionDef *fd; - + fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE, - s->filename, 0); + s->filename, s->buf_start, + &s->get_line_col_cache); if (!fd) return NULL; fd->func_name = JS_ATOM_NULL; fd->has_prototype = FALSE; fd->has_home_object = TRUE; - + fd->has_arguments_binding = FALSE; fd->has_this_binding = TRUE; fd->is_derived_class_constructor = FALSE; @@ -32537,7 +34983,7 @@ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) fd->super_call_allowed = FALSE; fd->super_allowed = fd->has_home_object; fd->arguments_allowed = FALSE; - + fd->func_kind = JS_FUNC_NORMAL; fd->func_type = JS_PARSE_FUNC_METHOD; return fd; @@ -32550,7 +34996,6 @@ static __exception int js_parse_function_decl2(JSParseState *s, JSFunctionKindEnum func_kind, JSAtom func_name, const uint8_t *ptr, - int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd) { @@ -32588,8 +35033,9 @@ static __exception int js_parse_function_decl2(JSParseState *s, func_type == JS_PARSE_FUNC_EXPR && (func_kind & JS_FUNC_GENERATOR)) || (s->token.u.ident.atom == JS_ATOM_await && - func_type == JS_PARSE_FUNC_EXPR && - (func_kind & JS_FUNC_ASYNC))) { + ((func_type == JS_PARSE_FUNC_EXPR && + (func_kind & JS_FUNC_ASYNC)) || + func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { return js_parse_error_reserved_identifier(s); } } @@ -32664,7 +35110,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, } fd = js_new_function_def(ctx, fd, FALSE, is_expr, - s->filename, function_line_num); + s->filename, ptr, + &s->get_line_col_cache); if (!fd) { JS_FreeAtom(ctx, func_name); return -1; @@ -32683,7 +35130,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, func_type == JS_PARSE_FUNC_SETTER || func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); - fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW); + fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW && + func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT); fd->has_this_binding = fd->has_arguments_binding; fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); if (func_type == JS_PARSE_FUNC_ARROW) { @@ -32691,6 +35139,11 @@ static __exception int js_parse_function_decl2(JSParseState *s, fd->super_call_allowed = fd->parent->super_call_allowed; fd->super_allowed = fd->parent->super_allowed; fd->arguments_allowed = fd->parent->arguments_allowed; + } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { + fd->new_target_allowed = TRUE; // although new.target === undefined + fd->super_call_allowed = FALSE; + fd->super_allowed = TRUE; + fd->arguments_allowed = FALSE; } else { fd->new_target_allowed = TRUE; fd->super_call_allowed = fd->is_derived_class_constructor; @@ -32713,7 +35166,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { emit_class_field_init(s); } - + /* parse arguments */ fd->has_simple_parameter_list = TRUE; fd->has_parameter_expressions = FALSE; @@ -32728,7 +35181,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (add_arg(ctx, fd, name) < 0) goto fail; fd->defined_arg_count = 1; - } else { + } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { if (s->token.val == '(') { int skip_bits; /* if there is an '=' inside the parameter list, we @@ -32748,13 +35201,15 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (push_scope(s) < 0) return -1; } - + while (s->token.val != ')') { JSAtom name; BOOL rest = FALSE; int idx, has_initializer; if (s->token.val == TOK_ELLIPSIS) { + if (func_type == JS_PARSE_FUNC_SETTER) + goto fail_accessor; fd->has_simple_parameter_list = FALSE; rest = TRUE; if (next_token(s)) @@ -32771,7 +35226,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, emit_op(s, OP_get_arg); emit_u16(s, idx); } - has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE, FALSE); if (has_initializer < 0) goto fail; if (has_initializer) @@ -32789,6 +35244,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, goto fail; } if (fd->has_parameter_expressions) { + if (js_parse_check_duplicate_parameter(s, name)) + goto fail; if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) goto fail; } @@ -32813,7 +35270,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, has_opt_arg = TRUE; } else if (s->token.val == '=') { int label; - + fd->has_simple_parameter_list = FALSE; has_opt_arg = TRUE; @@ -32866,6 +35323,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, } if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { + fail_accessor: js_parse_error(s, "invalid number of arguments for getter or setter"); goto fail; } @@ -32896,7 +35354,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, } idx = vd->scope_next; } - + /* the argument scope has no parent, hence we don't use pop_scope(s) */ emit_op(s, OP_leave_scope); emit_u16(s, fd->scope_level); @@ -32905,7 +35363,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, fd->scope_level = 0; fd->scope_first = fd->scopes[fd->scope_level].first; } - + if (next_token(s)) goto fail; @@ -32920,7 +35378,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, push_scope(s); /* enter body scope */ fd->body_scope = fd->scope_level; - if (s->token.val == TOK_ARROW) { + if (s->token.val == TOK_ARROW && func_type == JS_PARSE_FUNC_ARROW) { if (next_token(s)) goto fail; @@ -32936,7 +35394,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, else emit_op(s, OP_return); - if (!(fd->js_mode & JS_MODE_STRIP)) { + if (!fd->strip_source) { /* save the function source code */ /* the end of the function source code is after the last token of the function source stored into s->last_ptr */ @@ -32949,8 +35407,10 @@ static __exception int js_parse_function_decl2(JSParseState *s, } } - if (js_parse_expect(s, '{')) - goto fail; + if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { + if (js_parse_expect(s, '{')) + goto fail; + } if (js_parse_directives(s)) goto fail; @@ -32963,7 +35423,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (js_parse_source_element(s)) goto fail; } - if (!(fd->js_mode & JS_MODE_STRIP)) { + if (!fd->strip_source) { /* save the function source code */ fd->source_len = s->buf_ptr - ptr; fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); @@ -32980,9 +35440,15 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (js_is_live_code(s)) { emit_return(s, FALSE); } -done: + done: s->cur_func = fd->parent; + /* Reparse identifiers after the function is terminated so that + the token is parsed in the englobing function. It could be done + by just using next_token() here for normal functions, but it is + necessary for arrow functions with an expression body. */ + reparse_ident_token(s); + /* create the function object */ { int idx; @@ -33019,7 +35485,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, /* it is considered as defined at the top level (needed for annex B.3.3.4 and B.3.3.5 checks) */ - hf->scope_level = 0; + hf->scope_level = 0; hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0); /* store directly into global var, bypass lexical scope */ emit_op(s, OP_dup); @@ -33097,12 +35563,10 @@ static __exception int js_parse_function_decl(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, JSAtom func_name, - const uint8_t *ptr, - int function_line_num) + const uint8_t *ptr) { return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr, - function_line_num, JS_PARSE_EXPORT_NONE, - NULL); + JS_PARSE_EXPORT_NONE, NULL); } static __exception int js_parse_program(JSParseState *s) @@ -33134,12 +35598,24 @@ static __exception int js_parse_program(JSParseState *s) if (!s->is_module) { /* return the value of the hidden variable eval_ret_idx */ - emit_op(s, OP_get_loc); - emit_u16(s, fd->eval_ret_idx); + if (fd->func_kind == JS_FUNC_ASYNC) { + /* wrap the return value in an object so that promises can + be safely returned */ + emit_op(s, OP_object); + emit_op(s, OP_dup); - emit_op(s, OP_return); + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_value); + } else { + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + } + emit_return(s, TRUE); } else { - emit_op(s, OP_return_undef); + emit_return(s, FALSE); } return 0; @@ -33152,11 +35628,15 @@ static void js_parse_init(JSContext *ctx, JSParseState *s, memset(s, 0, sizeof(*s)); s->ctx = ctx; s->filename = filename; - s->line_num = 1; - s->buf_ptr = (const uint8_t *)input; + s->buf_start = s->buf_ptr = (const uint8_t *)input; s->buf_end = s->buf_ptr + input_len; s->token.val = ' '; - s->token.line_num = 1; + s->token.ptr = s->buf_ptr; + + s->get_line_col_cache.ptr = s->buf_start; + s->get_line_col_cache.buf_start = s->buf_start; + s->get_line_col_cache.line_num = 0; + s->get_line_col_cache.col_num = 0; } static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, @@ -33182,7 +35662,6 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, ret_val = js_evaluate_module(ctx, m); if (JS_IsException(ret_val)) { fail: - js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED); return JS_EXCEPTION; } } else { @@ -33197,31 +35676,6 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); } -static void skip_shebang(JSParseState *s) -{ - const uint8_t *p = s->buf_ptr; - int c; - - if (p[0] == '#' && p[1] == '!') { - p += 2; - while (p < s->buf_end) { - if (*p == '\n' || *p == '\r') { - break; - } else if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == CP_LS || c == CP_PS) { - break; - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - s->buf_ptr = p; - } -} - /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, @@ -33237,7 +35691,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, JSModuleDef *m; js_parse_init(ctx, s, input, input_len, filename); - skip_shebang(s); + skip_shebang(&s->buf_ptr, s->buf_end); eval_type = flags & JS_EVAL_TYPE_MASK; m = NULL; @@ -33258,8 +35712,6 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, js_mode = 0; if (flags & JS_EVAL_FLAG_STRICT) js_mode |= JS_MODE_STRICT; - if (flags & JS_EVAL_FLAG_STRIP) - js_mode |= JS_MODE_STRIP; if (eval_type == JS_EVAL_TYPE_MODULE) { JSAtom module_name = JS_NewAtom(ctx, filename); if (module_name == JS_ATOM_NULL) @@ -33270,13 +35722,13 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, js_mode |= JS_MODE_STRICT; } } - fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1); + fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, + s->buf_start, &s->get_line_col_cache); if (!fd) goto fail1; s->cur_func = fd; fd->eval_type = eval_type; fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT); - fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); if (eval_type == JS_EVAL_TYPE_DIRECT) { fd->new_target_allowed = b->new_target_allowed; fd->super_call_allowed = b->super_call_allowed; @@ -33295,12 +35747,16 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, goto fail; } fd->module = m; + if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) { + fd->in_function_body = TRUE; + fd->func_kind = JS_FUNC_ASYNC; + } s->is_module = (m != NULL); s->allow_html_comments = !s->is_module; push_scope(s); /* body scope */ fd->body_scope = fd->scope_level; - + err = js_parse_program(s); if (err) { fail: @@ -33309,6 +35765,9 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, goto fail1; } + if (m != NULL) + m->has_tla = fd->has_await; + /* create the function object and all the enclosed functions */ fun_obj = js_create_function(ctx, fd); if (JS_IsException(fun_obj)) @@ -33318,7 +35777,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, m->func_obj = fun_obj; if (js_resolve_module(ctx, m) < 0) goto fail1; - fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + fun_obj = JS_NewModuleValue(ctx, m); } if (flags & JS_EVAL_FLAG_COMPILE_ONLY) { ret_val = fun_obj; @@ -33338,11 +35797,22 @@ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx) { + BOOL backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); + int saved_js_mode = 0; + JSValue ret; + if (unlikely(!ctx->eval_internal)) { return JS_ThrowTypeError(ctx, "eval is not supported"); } - return ctx->eval_internal(ctx, this_obj, input, input_len, filename, - flags, scope_idx); + if (backtrace_barrier && ctx->rt->current_stack_frame) { + saved_js_mode = ctx->rt->current_stack_frame->js_mode; + ctx->rt->current_stack_frame->js_mode |= JS_MODE_BACKTRACE_BARRIER; + } + ret = ctx->eval_internal(ctx, this_obj, input, input_len, filename, + flags, scope_idx); + if (backtrace_barrier && ctx->rt->current_stack_frame) + ctx->rt->current_stack_frame->js_mode = saved_js_mode; + return ret; } static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, @@ -33435,13 +35905,13 @@ static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, js_free(ctx, s->hash_table); s->hash_table = new_hash_table; s->hash_size = new_hash_size; - + for(i = 0; i < s->hash_size; i++) { s->hash_table[i] = -1; } for(i = 0; i < s->object_count; i++) { e = &s->object_tab[i]; - h = js_object_list_get_hash(e->obj, s->hash_size); + h = js_object_list_get_hash(e->obj, s->hash_size); e->hash_next = s->hash_table[h]; s->hash_table[h] = i; } @@ -33454,7 +35924,7 @@ static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) { JSObjectListEntry *e; uint32_t h, new_hash_size; - + if (js_resize_array(ctx, (void *)&s->object_tab, sizeof(s->object_tab[0]), &s->object_size, s->object_count + 1)) @@ -33467,7 +35937,7 @@ static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) return -1; } e = &s->object_tab[s->object_count++]; - h = js_object_list_get_hash(obj, s->hash_size); + h = js_object_list_get_hash(obj, s->hash_size); e->obj = obj; e->hash_next = s->hash_table[h]; s->hash_table[h] = s->object_count - 1; @@ -33483,7 +35953,7 @@ static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) /* must test empty size because there is no hash table */ if (s->object_count == 0) return -1; - h = js_object_list_get_hash(obj, s->hash_size); + h = js_object_list_get_hash(obj, s->hash_size); p = s->hash_table[h]; while (p != -1) { e = &s->object_tab[p]; @@ -33514,8 +35984,6 @@ typedef enum BCTagEnum { BC_TAG_OBJECT, BC_TAG_ARRAY, BC_TAG_BIG_INT, - BC_TAG_BIG_FLOAT, - BC_TAG_BIG_DECIMAL, BC_TAG_TEMPLATE_OBJECT, BC_TAG_FUNCTION_BYTECODE, BC_TAG_MODULE, @@ -33527,22 +35995,11 @@ typedef enum BCTagEnum { BC_TAG_OBJECT_REFERENCE, } BCTagEnum; -#ifdef CONFIG_BIGNUM -#define BC_BASE_VERSION 2 -#else -#define BC_BASE_VERSION 1 -#endif -#define BC_BE_VERSION 0x40 -#ifdef WORDS_BIGENDIAN -#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION) -#else -#define BC_VERSION BC_BASE_VERSION -#endif +#define BC_VERSION 5 typedef struct BCWriterState { JSContext *ctx; DynBuf dbuf; - BOOL byte_swap : 8; BOOL allow_bytecode : 8; BOOL allow_sab : 8; BOOL allow_reference : 8; @@ -33572,8 +36029,6 @@ static const char * const bc_tag_str[] = { "object", "array", "bigint", - "bigfloat", - "bigdecimal", "template", "function", "module", @@ -33586,6 +36041,15 @@ static const char * const bc_tag_str[] = { }; #endif +static inline BOOL is_be(void) +{ + union { + uint16_t a; + uint8_t b; + } u = {0x100}; + return u.b; +} + static void bc_put_u8(BCWriterState *s, uint8_t v) { dbuf_putc(&s->dbuf, v); @@ -33593,21 +36057,21 @@ static void bc_put_u8(BCWriterState *s, uint8_t v) static void bc_put_u16(BCWriterState *s, uint16_t v) { - if (s->byte_swap) + if (is_be()) v = bswap16(v); dbuf_put_u16(&s->dbuf, v); } static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) { - if (s->byte_swap) + if (is_be()) v = bswap32(v); dbuf_put_u32(&s->dbuf, v); } static void bc_put_u64(BCWriterState *s, uint64_t v) { - if (s->byte_swap) + if (is_be()) v = bswap64(v); dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); } @@ -33777,7 +36241,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s, pos += len; } - if (s->byte_swap) + if (is_be()) bc_byte_swap(bc_buf, bc_len); dbuf_put(&s->dbuf, bc_buf, bc_len); @@ -33801,138 +36265,50 @@ static void JS_WriteString(BCWriterState *s, JSString *p) } } -static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) +static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj) { - uint32_t tag, tag1; - int64_t e; - JSBigFloat *bf = JS_VALUE_GET_PTR(obj); - bf_t *a = &bf->num; - size_t len, i, n1, j; - limb_t v; - - tag = JS_VALUE_GET_TAG(obj); - switch(tag) { - case JS_TAG_BIG_INT: - tag1 = BC_TAG_BIG_INT; - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - tag1 = BC_TAG_BIG_FLOAT; - break; - case JS_TAG_BIG_DECIMAL: - tag1 = BC_TAG_BIG_DECIMAL; - break; -#endif - default: - abort(); - } - bc_put_u8(s, tag1); + JSBigIntBuf buf; + JSBigInt *p; + uint32_t len, i; + js_limb_t v, b; + int shift; + + bc_put_u8(s, BC_TAG_BIG_INT); - /* sign + exponent */ - if (a->expn == BF_EXP_ZERO) - e = 0; - else if (a->expn == BF_EXP_INF) - e = 1; - else if (a->expn == BF_EXP_NAN) - e = 2; - else if (a->expn >= 0) - e = a->expn + 3; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT) + p = js_bigint_set_short(&buf, obj); else - e = a->expn; - e = (e << 1) | a->sign; - if (e < INT32_MIN || e > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); - return -1; + p = JS_VALUE_GET_PTR(obj); + if (p->len == 1 && p->tab[0] == 0) { + /* zero case */ + len = 0; + } else { + /* compute the length of the two's complement representation + in bytes */ + len = p->len * (JS_LIMB_BITS / 8); + v = p->tab[p->len - 1]; + shift = JS_LIMB_BITS - 8; + while (shift > 0) { + b = (v >> shift) & 0xff; + if (b != 0x00 && b != 0xff) + break; + if ((b & 1) != ((v >> (shift - 1)) & 1)) + break; + shift -= 8; + len--; + } } - bc_put_sleb128(s, e); - - /* mantissa */ - if (a->len != 0) { - if (tag != JS_TAG_BIG_DECIMAL) { - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - v = a->tab[i]; - n1 = sizeof(limb_t); - while ((v & 0xff) == 0) { - n1--; - v >>= 8; - } - i++; - len = (a->len - i) * sizeof(limb_t) + n1; - if (len > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum is too large"); - return -1; - } - bc_put_leb128(s, len); - /* always saved in byte based little endian representation */ - for(j = 0; j < n1; j++) { - dbuf_putc(&s->dbuf, v >> (j * 8)); - } - for(; i < a->len; i++) { - limb_t v = a->tab[i]; -#if LIMB_BITS == 32 -#ifdef WORDS_BIGENDIAN - v = bswap32(v); -#endif - dbuf_put_u32(&s->dbuf, v); + bc_put_leb128(s, len); + if (len > 0) { + for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) { +#if JS_LIMB_BITS == 32 + bc_put_u32(s, p->tab[i]); #else -#ifdef WORDS_BIGENDIAN - v = bswap64(v); -#endif - dbuf_put_u64(&s->dbuf, v); + bc_put_u64(s, p->tab[i]); #endif - } - } else { - int bpos, d; - uint8_t v8; - size_t i0; - - /* little endian BCD */ - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - len = a->len * LIMB_DIGITS; - v = a->tab[i]; - j = 0; - while ((v % 10) == 0) { - j++; - v /= 10; - } - len -= j; - assert(len > 0); - if (len > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum is too large"); - return -1; - } - bc_put_leb128(s, len); - - bpos = 0; - v8 = 0; - i0 = i; - for(; i < a->len; i++) { - if (i != i0) { - v = a->tab[i]; - j = 0; - } - for(; j < LIMB_DIGITS; j++) { - d = v % 10; - v /= 10; - if (bpos == 0) { - v8 = d; - bpos = 1; - } else { - dbuf_putc(&s->dbuf, v8 | (d << 4)); - bpos = 0; - } - } - } - /* flush the last digit */ - if (bpos) { - dbuf_putc(&s->dbuf, v8); - } + } + for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) { + bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff); } } return 0; @@ -33945,7 +36321,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); uint32_t flags; int idx, i; - + bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); flags = idx = 0; bc_set_flags(&flags, &idx, b->has_prototype, 1); @@ -33958,12 +36334,12 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) bc_set_flags(&flags, &idx, b->super_allowed, 1); bc_set_flags(&flags, &idx, b->arguments_allowed, 1); bc_set_flags(&flags, &idx, b->has_debug, 1); - bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); + bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1); assert(idx <= 16); bc_put_u16(s, flags); bc_put_u8(s, b->js_mode); bc_put_atom(s, b->func_name); - + bc_put_leb128(s, b->arg_count); bc_put_leb128(s, b->var_count); bc_put_leb128(s, b->defined_arg_count); @@ -33990,7 +36366,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) } else { bc_put_leb128(s, 0); } - + for(i = 0; i < b->closure_var_count; i++) { JSClosureVar *cv = &b->closure_var[i]; bc_put_atom(s, cv->var_name); @@ -34004,17 +36380,22 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) assert(idx <= 8); bc_put_u8(s, flags); } - + if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) goto fail; - + if (b->has_debug) { bc_put_atom(s, b->debug.filename); - bc_put_leb128(s, b->debug.line_num); bc_put_leb128(s, b->debug.pc2line_len); dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); + if (b->debug.source) { + bc_put_leb128(s, b->debug.source_len); + dbuf_put(&s->dbuf, (uint8_t *)b->debug.source, b->debug.source_len); + } else { + bc_put_leb128(s, 0); + } } - + for(i = 0; i < b->cpool_count; i++) { if (JS_WriteObjectRec(s, b->cpool[i])) goto fail; @@ -34028,16 +36409,18 @@ static int JS_WriteModule(BCWriterState *s, JSValueConst obj) { JSModuleDef *m = JS_VALUE_GET_PTR(obj); int i; - + bc_put_u8(s, BC_TAG_MODULE); bc_put_atom(s, m->module_name); - + bc_put_leb128(s, m->req_module_entries_count); for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; bc_put_atom(s, rme->module_name); + if (JS_WriteObjectRec(s, rme->attributes)) + goto fail; } - + bc_put_leb128(s, m->export_entries_count); for(i = 0; i < m->export_entries_count; i++) { JSExportEntry *me = &m->export_entries[i]; @@ -34050,21 +36433,24 @@ static int JS_WriteModule(BCWriterState *s, JSValueConst obj) } bc_put_atom(s, me->export_name); } - + bc_put_leb128(s, m->star_export_entries_count); for(i = 0; i < m->star_export_entries_count; i++) { JSStarExportEntry *se = &m->star_export_entries[i]; bc_put_leb128(s, se->req_module_idx); } - + bc_put_leb128(s, m->import_entries_count); for(i = 0; i < m->import_entries_count; i++) { JSImportEntry *mi = &m->import_entries[i]; bc_put_leb128(s, mi->var_idx); + bc_put_u8(s, mi->is_star); bc_put_atom(s, mi->import_name); bc_put_leb128(s, mi->req_module_idx); } - + + bc_put_u8(s, m->has_tla); + if (JS_WriteObjectRec(s, m->func_obj)) goto fail; return 0; @@ -34079,7 +36465,7 @@ static int JS_WriteArray(BCWriterState *s, JSValueConst obj) JSValue val; int ret; BOOL is_template; - + if (s->allow_bytecode && !p->extensible) { /* not extensible array: we consider it is a template when we are saving bytecode */ @@ -34237,6 +36623,16 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) JS_WriteString(s, p); } break; + case JS_TAG_STRING_ROPE: + { + JSValue str; + str = JS_ToString(s->ctx, obj); + if (JS_IsException(str)) + goto fail; + JS_WriteObjectRec(s, str); + JS_FreeValue(s->ctx, str); + } + break; case JS_TAG_FUNCTION_BYTECODE: if (!s->allow_bytecode) goto invalid_tag; @@ -34253,7 +36649,7 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) { JSObject *p = JS_VALUE_GET_OBJ(obj); int ret, idx; - + if (s->allow_reference) { idx = js_object_list_find(s->ctx, &s->object_list, p); if (idx >= 0) { @@ -34294,10 +36690,6 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) case JS_CLASS_STRING: case JS_CLASS_BOOLEAN: case JS_CLASS_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_FLOAT: - case JS_CLASS_BIG_DECIMAL: -#endif bc_put_u8(s, BC_TAG_OBJECT_VALUE); ret = JS_WriteObjectRec(s, p->u.object_data); break; @@ -34316,12 +36708,9 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) goto fail; } break; + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: -#endif - if (JS_WriteBigNum(s, obj)) + if (JS_WriteBigInt(s, obj)) goto fail; break; default: @@ -34341,15 +36730,10 @@ static int JS_WriteObjectAtoms(BCWriterState *s) JSRuntime *rt = s->ctx->rt; DynBuf dbuf1; int i, atoms_size; - uint8_t version; dbuf1 = s->dbuf; js_dbuf_init(s->ctx, &s->dbuf); - - version = BC_VERSION; - if (s->byte_swap) - version ^= BC_BE_VERSION; - bc_put_u8(s, version); + bc_put_u8(s, BC_VERSION); bc_put_leb128(s, s->idx_to_atom_count); for(i = 0; i < s->idx_to_atom_count; i++) { @@ -34382,8 +36766,6 @@ uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, memset(s, 0, sizeof(*s)); s->ctx = ctx; - /* XXX: byte swapped output is untested */ - s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0); s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); @@ -34394,7 +36776,7 @@ uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, s->first_atom = 1; js_dbuf_init(ctx, &s->dbuf); js_object_list_init(&s->object_list); - + if (JS_WriteObjectRec(s, obj)) goto fail; if (JS_WriteObjectAtoms(s)) @@ -34442,7 +36824,7 @@ typedef struct BCReaderState { JSObject **objects; int objects_count; int objects_size; - + #ifdef DUMP_READ_OBJECT const uint8_t *ptr_last; int level; @@ -34504,33 +36886,45 @@ static int bc_get_u8(BCReaderState *s, uint8_t *pval) static int bc_get_u16(BCReaderState *s, uint16_t *pval) { + uint16_t v; if (unlikely(s->buf_end - s->ptr < 2)) { *pval = 0; /* avoid warning */ return bc_read_error_end(s); } - *pval = get_u16(s->ptr); + v = get_u16(s->ptr); + if (is_be()) + v = bswap16(v); + *pval = v; s->ptr += 2; return 0; } static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) { + uint32_t v; if (unlikely(s->buf_end - s->ptr < 4)) { *pval = 0; /* avoid warning */ return bc_read_error_end(s); } - *pval = get_u32(s->ptr); + v = get_u32(s->ptr); + if (is_be()) + v = bswap32(v); + *pval = v; s->ptr += 4; return 0; } static int bc_get_u64(BCReaderState *s, uint64_t *pval) { + uint64_t v; if (unlikely(s->buf_end - s->ptr < 8)) { *pval = 0; /* avoid warning */ return bc_read_error_end(s); } - *pval = get_u64(s->ptr); + v = get_u64(s->ptr); + if (is_be()) + v = bswap64(v); + *pval = v; s->ptr += 8; return 0; } @@ -34629,6 +37023,10 @@ static JSString *JS_ReadString(BCReaderState *s) return NULL; is_wide_char = len & 1; len >>= 1; + if (len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(s->ctx, "string too long"); + return NULL; + } p = js_alloc_string(s->ctx, len, is_wide_char); if (!p) { s->error_state = -1; @@ -34642,7 +37040,13 @@ static JSString *JS_ReadString(BCReaderState *s) } memcpy(p->u.str8, s->ptr, size); s->ptr += size; - if (!is_wide_char) { + if (is_wide_char) { + if (is_be()) { + uint32_t i; + for (i = 0; i < len; i++) + p->u.str16[i] = bswap16(p->u.str16[i]); + } + } else { p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ } #ifdef DUMP_READ_OBJECT @@ -34681,6 +37085,9 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, } b->byte_code_buf = bc_buf; + if (is_be()) + bc_byte_swap(bc_buf, bc_len); + pos = 0; while (pos < bc_len) { op = bc_buf[pos]; @@ -34715,135 +37122,53 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, return 0; } -static JSValue JS_ReadBigNum(BCReaderState *s, int tag) +static JSValue JS_ReadBigInt(BCReaderState *s) { JSValue obj = JS_UNDEFINED; + uint32_t len, i, n; + JSBigInt *p; + js_limb_t v; uint8_t v8; - int32_t e; - uint32_t len; - limb_t l, i, n, j; - JSBigFloat *p; - limb_t v; - bf_t *a; - int bpos, d; - p = js_new_bf(s->ctx); - if (!p) + if (bc_get_leb128(s, &len)) goto fail; - switch(tag) { - case BC_TAG_BIG_INT: - obj = JS_MKPTR(JS_TAG_BIG_INT, p); - break; -#ifdef CONFIG_BIGNUM - case BC_TAG_BIG_FLOAT: - obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - break; - case BC_TAG_BIG_DECIMAL: - obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); - break; -#endif - default: - abort(); + bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); + if (len == 0) { + /* zero case */ + bc_read_trace(s, "}\n"); + return __JS_NewShortBigInt(s->ctx, 0); } - - /* sign + exponent */ - if (bc_get_sleb128(s, &e)) + p = js_bigint_new(s->ctx, (len - 1) / (JS_LIMB_BITS / 8) + 1); + if (!p) goto fail; - - a = &p->num; - a->sign = e & 1; - e >>= 1; - if (e == 0) - a->expn = BF_EXP_ZERO; - else if (e == 1) - a->expn = BF_EXP_INF; - else if (e == 2) - a->expn = BF_EXP_NAN; - else if (e >= 3) - a->expn = e - 3; - else - a->expn = e; - - /* mantissa */ - if (a->expn != BF_EXP_ZERO && - a->expn != BF_EXP_INF && - a->expn != BF_EXP_NAN) { - if (bc_get_leb128(s, &len)) - goto fail; - bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); - if (len == 0) { - JS_ThrowInternalError(s->ctx, "invalid bignum length"); - goto fail; - } - if (tag != BC_TAG_BIG_DECIMAL) - l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); - else - l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS; - if (bf_resize(a, l)) { - JS_ThrowOutOfMemory(s->ctx); + for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) { +#if JS_LIMB_BITS == 32 + if (bc_get_u32(s, &v)) goto fail; - } - if (tag != BC_TAG_BIG_DECIMAL) { - n = len & (sizeof(limb_t) - 1); - if (n != 0) { - v = 0; - for(i = 0; i < n; i++) { - if (bc_get_u8(s, &v8)) - goto fail; - v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); - } - a->tab[0] = v; - i = 1; - } else { - i = 0; - } - for(; i < l; i++) { -#if LIMB_BITS == 32 - if (bc_get_u32(s, &v)) - goto fail; -#ifdef WORDS_BIGENDIAN - v = bswap32(v); -#endif #else - if (bc_get_u64(s, &v)) - goto fail; -#ifdef WORDS_BIGENDIAN - v = bswap64(v); -#endif + if (bc_get_u64(s, &v)) + goto fail; #endif - a->tab[i] = v; - } - } else { - bpos = 0; - for(i = 0; i < l; i++) { - if (i == 0 && (n = len % LIMB_DIGITS) != 0) { - j = LIMB_DIGITS - n; - } else { - j = 0; - } - v = 0; - for(; j < LIMB_DIGITS; j++) { - if (bpos == 0) { - if (bc_get_u8(s, &v8)) - goto fail; - d = v8 & 0xf; - bpos = 1; - } else { - d = v8 >> 4; - bpos = 0; - } - if (d >= 10) { - JS_ThrowInternalError(s->ctx, "invalid digit"); - goto fail; - } - v += mp_pow_dec[j] * d; - } - a->tab[i] = v; - } + p->tab[i] = v; + } + n = len % (JS_LIMB_BITS / 8); + if (n != 0) { + int shift; + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) + goto fail; + v |= (js_limb_t)v8 << (i * 8); } + shift = JS_LIMB_BITS - n * 8; + /* extend the sign */ + if (shift != 0) { + v = (js_slimb_t)(v << shift) >> shift; + } + p->tab[p->len - 1] = v; } bc_read_trace(s, "}\n"); - return obj; + return JS_CompactBigInt(s->ctx, p); fail: JS_FreeValue(s->ctx, obj); return JS_EXCEPTION; @@ -34896,7 +37221,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) bc.super_allowed = bc_get_flags(v16, &idx, 1); bc.arguments_allowed = bc_get_flags(v16, &idx, 1); bc.has_debug = bc_get_flags(v16, &idx, 1); - bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); + bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1); bc.read_only_bytecode = s->is_rom_data; if (bc_get_u8(s, &v8)) goto fail; @@ -34939,7 +37264,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) b = js_mallocz(ctx, function_size); if (!b) return JS_EXCEPTION; - + memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); b->header.ref_count = 1; if (local_count != 0) { @@ -34951,9 +37276,9 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) if (b->cpool_count != 0) { b->cpool = (void *)((uint8_t*)b + cpool_offset); } - + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); #ifdef DUMP_READ_OBJECT @@ -35024,8 +37349,9 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) bc_read_trace(s, "debug {\n"); if (bc_get_atom(s, &b->debug.filename)) goto fail; - if (bc_get_leb128_int(s, &b->debug.line_num)) - goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); +#endif if (bc_get_leb128_int(s, &b->debug.pc2line_len)) goto fail; if (b->debug.pc2line_len) { @@ -35035,9 +37361,16 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) goto fail; } -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); -#endif + if (bc_get_leb128_int(s, &b->debug.source_len)) + goto fail; + if (b->debug.source_len) { + bc_read_trace(s, "source: %d bytes\n", b->source_len); + b->debug.source = js_mallocz(ctx, b->debug.source_len); + if (!b->debug.source) + goto fail; + if (bc_get_buf(s, (uint8_t *)b->debug.source, b->debug.source_len)) + goto fail; + } bc_read_trace(s, "}\n"); } if (b->cpool_count != 0) { @@ -35066,7 +37399,7 @@ static JSValue JS_ReadModule(BCReaderState *s) JSAtom module_name; int i; uint8_t v8; - + if (bc_get_atom(s, &module_name)) goto fail; #ifdef DUMP_READ_OBJECT @@ -35075,7 +37408,7 @@ static JSValue JS_ReadModule(BCReaderState *s) m = js_new_module_def(ctx, module_name); if (!m) goto fail; - obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + obj = JS_NewModuleValue(ctx, m); if (bc_get_leb128_int(s, &m->req_module_entries_count)) goto fail; if (m->req_module_entries_count != 0) { @@ -35085,8 +37418,13 @@ static JSValue JS_ReadModule(BCReaderState *s) goto fail; for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; + JSValue val; if (bc_get_atom(s, &rme->module_name)) goto fail; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + rme->attributes = val; } } @@ -35139,8 +37477,12 @@ static JSValue JS_ReadModule(BCReaderState *s) goto fail; for(i = 0; i < m->import_entries_count; i++) { JSImportEntry *mi = &m->import_entries[i]; + uint8_t v8; if (bc_get_leb128_int(s, &mi->var_idx)) goto fail; + if (bc_get_u8(s, &v8)) + goto fail; + mi->is_star = (v8 != 0); if (bc_get_atom(s, &mi->import_name)) goto fail; if (bc_get_leb128_int(s, &mi->req_module_idx)) @@ -35148,6 +37490,10 @@ static JSValue JS_ReadModule(BCReaderState *s) } } + if (bc_get_u8(s, &v8)) + goto fail; + m->has_tla = (v8 != 0); + m->func_obj = JS_ReadObjectRec(s); if (JS_IsException(m->func_obj)) goto fail; @@ -35167,7 +37513,7 @@ static JSValue JS_ReadObjectTag(BCReaderState *s) JSAtom atom; JSValue val; int ret; - + obj = JS_NewObject(ctx); if (BC_add_object_ref(s, obj)) goto fail; @@ -35247,7 +37593,7 @@ static JSValue JS_ReadTypedArray(BCReaderState *s) uint8_t array_tag; JSValueConst args[3]; uint32_t offset, len, idx; - + if (bc_get_u8(s, &array_tag)) return JS_EXCEPTION; if (array_tag >= JS_TYPED_ARRAY_COUNT) @@ -35292,7 +37638,7 @@ static JSValue JS_ReadArrayBuffer(BCReaderState *s) JSContext *ctx = s->ctx; uint32_t byte_length; JSValue obj; - + if (bc_get_leb128(s, &byte_length)) return JS_EXCEPTION; if (unlikely(s->buf_end - s->ptr < byte_length)) { @@ -35318,7 +37664,7 @@ static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) uint8_t *data_ptr; JSValue obj; uint64_t u64; - + if (bc_get_leb128(s, &byte_length)) return JS_EXCEPTION; if (bc_get_u64(s, &u64)) @@ -35473,11 +37819,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) obj = JS_ReadObjectValue(s); break; case BC_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case BC_TAG_BIG_FLOAT: - case BC_TAG_BIG_DECIMAL: -#endif - obj = JS_ReadBigNum(s, tag); + obj = JS_ReadBigInt(s); break; case BC_TAG_OBJECT_REFERENCE: { @@ -35512,7 +37854,6 @@ static int JS_ReadObjectAtoms(BCReaderState *s) if (bc_get_u8(s, &v8)) return -1; - /* XXX: could support byte swapped input */ if (v8 != BC_VERSION) { JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", v8, BC_VERSION); @@ -35751,17 +38092,22 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, return 0; } -void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, - const JSCFunctionListEntry *tab, int len) +int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, + const JSCFunctionListEntry *tab, int len) { - int i; + int i, ret; for (i = 0; i < len; i++) { const JSCFunctionListEntry *e = &tab[i]; JSAtom atom = find_atom(ctx, e->name); - JS_InstantiateFunctionListItem(ctx, obj, atom, e); + if (atom == JS_ATOM_NULL) + return -1; + ret = JS_InstantiateFunctionListItem(ctx, obj, atom, e); JS_FreeAtom(ctx, atom); + if (ret) + return -1; } + return 0; } int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, @@ -35828,7 +38174,7 @@ static void JS_SetConstructor2(JSContext *ctx, set_cycle_flag(ctx, proto); } -void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, +void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, JSValueConst proto) { JS_SetConstructor2(ctx, func_obj, proto, @@ -35878,7 +38224,6 @@ static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, { double d; - /* XXX: does this work for bigfloat? */ if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) return JS_EXCEPTION; return JS_NewBool(ctx, isnan(d)); @@ -35887,12 +38232,10 @@ static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - BOOL res; double d; if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) return JS_EXCEPTION; - res = isfinite(d); - return JS_NewBool(ctx, res); + return JS_NewBool(ctx, isfinite(d)); } /* Object class */ @@ -35910,29 +38253,31 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) case JS_TAG_OBJECT: case JS_TAG_EXCEPTION: return JS_DupValue(ctx, val); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); - goto set_value; - case JS_TAG_BIG_DECIMAL: - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); - goto set_value; -#endif case JS_TAG_INT: case JS_TAG_FLOAT64: obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); goto set_value; case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: /* XXX: should call the string constructor */ { - JSString *p1 = JS_VALUE_GET_STRING(val); + JSValue str; + str = JS_ToString(ctx, val); /* ensure that we never store a rope */ + if (JS_IsException(str)) + return JS_EXCEPTION; obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); + if (!JS_IsException(obj)) { + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, + JS_NewInt32(ctx, JS_VALUE_GET_STRING(str)->len), 0); + JS_SetObjectData(ctx, obj, JS_DupValue(ctx, str)); + } + JS_FreeValue(ctx, str); + return obj; } - goto set_value; case JS_TAG_BOOL: obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); goto set_value; @@ -35966,6 +38311,14 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, val = JS_UNDEFINED; getter = JS_UNDEFINED; setter = JS_UNDEFINED; + if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_ENUMERABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_ENUMERABLE; + } if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); if (JS_IsException(prop)) @@ -35974,6 +38327,12 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, if (JS_ToBoolFree(ctx, prop)) flags |= JS_PROP_CONFIGURABLE; } + if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { + flags |= JS_PROP_HAS_VALUE; + val = JS_GetProperty(ctx, desc, JS_ATOM_value); + if (JS_IsException(val)) + goto fail; + } if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); if (JS_IsException(prop)) @@ -35982,20 +38341,6 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, if (JS_ToBoolFree(ctx, prop)) flags |= JS_PROP_WRITABLE; } - if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_ENUMERABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_ENUMERABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { - flags |= JS_PROP_HAS_VALUE; - val = JS_GetProperty(ctx, desc, JS_ATOM_value); - if (JS_IsException(val)) - goto fail; - } if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { flags |= JS_PROP_HAS_GET; getter = JS_GetProperty(ctx, desc, JS_ATOM_get); @@ -36066,6 +38411,7 @@ static __exception int JS_ObjectDefineProperties(JSContext *ctx, if (JS_IsException(props)) return -1; p = JS_VALUE_GET_OBJ(props); + /* XXX: not done in the same order as the spec */ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) goto exception; for(i = 0; i < len; i++) { @@ -36079,7 +38425,7 @@ static __exception int JS_ObjectDefineProperties(JSContext *ctx, ret = 0; exception: - js_free_prop_enum(ctx, atoms, len); + JS_FreePropertyEnum(ctx, atoms, len); JS_FreeValue(ctx, props); JS_FreeValue(ctx, desc); return ret; @@ -36286,13 +38632,13 @@ static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst t } else { if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0) + JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0) goto exception1; } if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, - JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0 + JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, - JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0) + JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0) goto exception1; js_free_desc(ctx, &desc); } @@ -36350,12 +38696,12 @@ static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst goto exception; } } - js_free_prop_enum(ctx, props, len); + JS_FreePropertyEnum(ctx, props, len); JS_FreeValue(ctx, obj); return r; exception: - js_free_prop_enum(ctx, props, len); + JS_FreePropertyEnum(ctx, props, len); JS_FreeValue(ctx, obj); JS_FreeValue(ctx, r); return JS_EXCEPTION; @@ -36435,7 +38781,7 @@ static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, JS_FreeValue(ctx, r); r = JS_EXCEPTION; done: - js_free_prop_enum(ctx, atoms, len); + JS_FreePropertyEnum(ctx, atoms, len); JS_FreeValue(ctx, obj); return r; } @@ -36573,9 +38919,9 @@ static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val, JSObject *p; if (JS_IsNull(this_val)) { - tag = JS_NewString(ctx, "Null"); + tag = js_new_string8(ctx, "Null"); } else if (JS_IsUndefined(this_val)) { - tag = JS_NewString(ctx, "Undefined"); + tag = js_new_string8(ctx, "Undefined"); } else { obj = JS_ToObject(ctx, this_val); if (JS_IsException(obj)) @@ -36671,7 +39017,7 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, if (!res) { return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); } - + p = JS_VALUE_GET_OBJ(obj); flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) @@ -36696,11 +39042,11 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) goto exception; } - js_free_prop_enum(ctx, props, len); + JS_FreePropertyEnum(ctx, props, len); return JS_DupValue(ctx, obj); exception: - js_free_prop_enum(ctx, props, len); + JS_FreePropertyEnum(ctx, props, len); return JS_EXCEPTION; } @@ -36712,7 +39058,7 @@ static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, JSPropertyEnum *props; uint32_t len, i; int flags, res; - + if (!JS_IsObject(obj)) return JS_TRUE; @@ -36741,12 +39087,12 @@ static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, if (res < 0) return JS_EXCEPTION; res ^= 1; -done: - js_free_prop_enum(ctx, props, len); +done: + JS_FreePropertyEnum(ctx, props, len); return JS_NewBool(ctx, res); exception: - js_free_prop_enum(ctx, props, len); + JS_FreePropertyEnum(ctx, props, len); return JS_EXCEPTION; } @@ -36764,24 +39110,22 @@ static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, obj = JS_NewObject(ctx); if (JS_IsException(obj)) return obj; - + iter = JS_GetIterator(ctx, iterable, FALSE); if (JS_IsException(iter)) goto fail; next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); if (JS_IsException(next_method)) goto fail; - + for(;;) { JSValue key, value, item; item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(item)) goto fail; - if (done) { - JS_FreeValue(ctx, item); + if (done) break; - } - + key = JS_UNDEFINED; value = JS_UNDEFINED; if (!JS_IsObject(item)) { @@ -37024,23 +39368,23 @@ static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - JSValue obj, res = JS_EXCEPTION; - JSAtom prop = JS_ATOM_NULL; + JSValue obj = JS_UNDEFINED, res = JS_EXCEPTION; + JSAtom prop; JSPropertyDescriptor desc; int has_prop; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; prop = JS_ValueToAtom(ctx, argv[0]); if (unlikely(prop == JS_ATOM_NULL)) goto exception; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); if (has_prop < 0) goto exception; if (has_prop) { - res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0); + res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE); js_free_desc(ctx, &desc); } else { res = JS_FALSE; @@ -37105,6 +39449,7 @@ static const JSCFunctionListEntry js_object_funcs[] = { JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), + JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ), JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), @@ -37166,7 +39511,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, string_buffer_init(ctx, b, 0); string_buffer_putc8(b, '('); - + if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { string_buffer_puts8(b, "async "); } @@ -37264,6 +39609,7 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, JSValueConst array_arg) { uint32_t len, i; + int64_t len64; JSValue *tab, ret; JSObject *p; @@ -37271,12 +39617,15 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, JS_ThrowTypeError(ctx, "not a object"); return NULL; } - if (js_get_length32(ctx, &len, array_arg)) + if (js_get_length64(ctx, &len64, array_arg)) return NULL; - if (len > JS_MAX_LOCAL_VARS) { - JS_ThrowInternalError(ctx, "too many arguments"); + if (len64 > JS_MAX_LOCAL_VARS) { + // XXX: check for stack overflow? + JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", + JS_MAX_LOCAL_VARS); return NULL; } + len = len64; /* avoid allocating 0 bytes */ tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); if (!tab) @@ -37488,7 +39837,8 @@ static const JSCFunctionListEntry js_function_proto_funcs[] = { JS_CFUNC_DEF("toString", 0, js_function_toString ), JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ), JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ), - JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ), + JS_CGETSET_MAGIC_DEF("lineNumber", js_function_proto_lineNumber, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("columnNumber", js_function_proto_lineNumber, NULL, 1 ), }; /* Error class */ @@ -37499,7 +39849,7 @@ static JSValue iterator_to_array(JSContext *ctx, JSValueConst items) JSValue v, r = JS_UNDEFINED; int64_t k; BOOL done; - + iter = JS_GetIterator(ctx, items, FALSE); if (JS_IsException(iter)) goto exception; @@ -37535,7 +39885,8 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic) { JSValue obj, msg, proto; - JSValueConst message; + JSValueConst message, options; + int arg_index; if (JS_IsUndefined(new_target)) new_target = JS_GetActiveFunction(ctx); @@ -37545,7 +39896,7 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, if (!JS_IsObject(proto)) { JSContext *realm; JSValueConst proto1; - + JS_FreeValue(ctx, proto); realm = JS_GetFunctionRealm(ctx, new_target); if (!realm) @@ -37561,12 +39912,9 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, JS_FreeValue(ctx, proto); if (JS_IsException(obj)) return obj; - if (magic == JS_AGGREGATE_ERROR) { - message = argv[1]; - } else { - message = argv[0]; - } + arg_index = (magic == JS_AGGREGATE_ERROR); + message = argv[arg_index++]; if (!JS_IsUndefined(message)) { msg = JS_ToString(ctx, message); if (unlikely(JS_IsException(msg))) @@ -37575,6 +39923,22 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } + if (arg_index < argc) { + options = argv[arg_index]; + if (JS_IsObject(options)) { + int present = JS_HasProperty(ctx, options, JS_ATOM_cause); + if (present < 0) + goto exception; + if (present) { + JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause); + if (JS_IsException(cause)) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + } + } + if (magic == JS_AGGREGATE_ERROR) { JSValue error_list = iterator_to_array(ctx, argv[0]); if (JS_IsException(error_list)) @@ -37584,7 +39948,7 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, } /* skip the Error() function in the backtrace */ - build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); + build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); return obj; exception: JS_FreeValue(ctx, obj); @@ -37633,7 +39997,7 @@ static JSValue js_aggregate_error_constructor(JSContext *ctx, JSValueConst errors) { JSValue obj; - + obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[JS_AGGREGATE_ERROR], JS_CLASS_ERROR); @@ -37699,7 +40063,7 @@ static int JS_CopySubArray(JSContext *ctx, fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); if (fromPresent < 0) goto exception; - + if (fromPresent) { if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) goto exception; @@ -37749,8 +40113,7 @@ static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, // from(items, mapfn = void 0, this_arg = void 0) JSValueConst items = argv[0], mapfn, this_arg; JSValueConst args[2]; - JSValue stack[2]; - JSValue iter, r, v, v2, arrayLike; + JSValue iter, r, v, v2, arrayLike, next_method, enum_obj; int64_t k, len; int done, mapping; @@ -37759,8 +40122,9 @@ static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, this_arg = JS_UNDEFINED; r = JS_UNDEFINED; arrayLike = JS_UNDEFINED; - stack[0] = JS_UNDEFINED; - stack[1] = JS_UNDEFINED; + iter = JS_UNDEFINED; + enum_obj = JS_UNDEFINED; + next_method = JS_UNDEFINED; if (argc > 1) { mapfn = argv[1]; @@ -37775,21 +40139,27 @@ static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); if (JS_IsException(iter)) goto exception; - if (!JS_IsUndefined(iter)) { - JS_FreeValue(ctx, iter); + if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { + if (!JS_IsFunction(ctx, iter)) { + JS_ThrowTypeError(ctx, "value is not iterable"); + goto exception; + } if (JS_IsConstructor(ctx, this_val)) r = JS_CallConstructor(ctx, this_val, 0, NULL); else r = JS_NewArray(ctx); if (JS_IsException(r)) goto exception; - stack[0] = JS_DupValue(ctx, items); - if (js_for_of_start(ctx, &stack[1], FALSE)) + enum_obj = JS_GetIterator2(ctx, items, iter); + if (JS_IsException(enum_obj)) + goto exception; + next_method = JS_GetProperty(ctx, enum_obj, JS_ATOM_next); + if (JS_IsException(next_method)) goto exception; for (k = 0;; k++) { - v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + v = JS_IteratorNext(ctx, enum_obj, next_method, 0, NULL, &done); if (JS_IsException(v)) - goto exception_close; + goto exception; if (done) break; if (mapping) { @@ -37844,15 +40214,15 @@ static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, goto done; exception_close: - if (!JS_IsUndefined(stack[0])) - JS_IteratorClose(ctx, stack[0], TRUE); + JS_IteratorClose(ctx, enum_obj, TRUE); exception: JS_FreeValue(ctx, r); r = JS_EXCEPTION; done: JS_FreeValue(ctx, arrayLike); - JS_FreeValue(ctx, stack[0]); - JS_FreeValue(ctx, stack[1]); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, enum_obj); + JS_FreeValue(ctx, next_method); return r; } @@ -37907,7 +40277,7 @@ static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, JSValue ctor, ret, species; int res; JSContext *realm; - + res = JS_IsArray(ctx, obj); if (res < 0) return JS_EXCEPTION; @@ -37975,7 +40345,7 @@ static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, int64_t len, idx; JSValue *arrp; uint32_t count; - + obj = JS_ToObject(ctx, this_val); if (js_get_length64(ctx, &len, obj)) goto exception; @@ -38003,6 +40373,71 @@ static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +static JSValue js_array_with(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len, idx; + uint32_t count32; + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + goto exception; + + if (idx < 0) + idx = len + idx; + + if (idx < 0 || idx >= len) { + JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx); + goto exception; + } + + arr = js_allocate_fast_array(ctx, len); + if (JS_IsException(arr)) + goto exception; + + p = JS_VALUE_GET_OBJ(arr); + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < idx; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + *pval = JS_DupValue(ctx, argv[1]); + for (i++, pval++; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (; i < idx; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto fill_and_fail; + *pval = JS_DupValue(ctx, argv[1]); + for (i++, pval++; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + fill_and_fail: + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -38107,7 +40542,7 @@ static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, this_arg = JS_UNDEFINED; if (argc > 1) this_arg = argv[1]; - + if (check_function(ctx, func)) goto exception; @@ -38368,9 +40803,10 @@ static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue obj, val; - int64_t len, n, res; + int64_t len, n; JSValue *arrp; uint32_t count; + int res; obj = JS_ToObject(ctx, this_val); if (js_get_length64(ctx, &len, obj)) @@ -38502,10 +40938,10 @@ static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, } enum { - special_find, - special_findIndex, - special_findLast, - special_findLastIndex, + ArrayFind, + ArrayFindIndex, + ArrayFindLast, + ArrayFindLastIndex, }; static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, @@ -38531,14 +40967,13 @@ static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, if (argc > 1) this_arg = argv[1]; - if (mode == special_findLast || mode == special_findLastIndex) { + k = 0; + dir = 1; + end = len; + if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { k = len - 1; dir = -1; end = -1; - } else { - k = 0; - dir = 1; - end = len; } // TODO(bnoordhuis) add fast path for fast arrays @@ -38556,7 +40991,7 @@ static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, if (JS_IsException(res)) goto exception; if (JS_ToBoolFree(ctx, res)) { - if (mode == special_findIndex || mode == special_findLastIndex) { + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { JS_FreeValue(ctx, val); JS_FreeValue(ctx, obj); return index_val; @@ -38570,7 +41005,7 @@ static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, index_val); } JS_FreeValue(ctx, obj); - if (mode == special_findIndex || mode == special_findLastIndex) + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) return JS_NewInt32(ctx, -1); else return JS_UNDEFINED; @@ -38821,6 +41256,61 @@ static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +// Note: a.toReversed() is a.slice().reverse() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toReversed() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + arr = js_allocate_fast_array(ctx, len); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + + i = len - 1; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i >= 0; i--, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + // Query order is observable; test262 expects descending order. + for (; i >= 0; i--, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + // Exception; initialize remaining elements. + for (; i >= 0; i--, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + } + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int splice) { @@ -38928,6 +41418,92 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval, *last; + JSObject *p; + int64_t i, j, len, newlen, start, add, del; + uint32_t count32; + + pval = NULL; + last = NULL; + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + start = 0; + if (argc > 0) + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + del = 0; + if (argc > 0) + del = len - start; + if (argc > 1) + if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0)) + goto exception; + + add = 0; + if (argc > 2) + add = argc - 2; + + newlen = len + add - del; + if (newlen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "invalid array length"); + goto exception; + } + + arr = js_allocate_fast_array(ctx, newlen); + if (JS_IsException(arr)) + goto exception; + + if (newlen <= 0) + goto done; + + p = JS_VALUE_GET_OBJ(arr); + pval = &p->u.array.u.values[0]; + last = &p->u.array.u.values[newlen]; + + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (i = 0; i < start; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + for (j = 0; j < add; j++, pval++) + *pval = JS_DupValue(ctx, argv[2 + j]); + for (i += del; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (i = 0; i < start; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto exception; + for (j = 0; j < add; j++, pval++) + *pval = JS_DupValue(ctx, argv[2 + j]); + for (i += del; i < len; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto exception; + } + + assert(pval == last); + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, newlen)) < 0) + goto exception; + +done: + ret = arr; + arr = JS_UNDEFINED; + +exception: + while (pval != last) + *pval++ = JS_UNDEFINED; + + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -38987,8 +41563,8 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, if (!JS_IsUndefined(mapperFunction)) { JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; element = JS_Call(ctx, mapperFunction, thisArg, 3, args); - JS_FreeValue(ctx, (JSValue)args[0]); - JS_FreeValue(ctx, (JSValue)args[1]); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); if (JS_IsException(element)) return -1; } @@ -39231,6 +41807,68 @@ static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +// Note: a.toSorted() is a.slice().sort() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toSorted() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + int ok; + + ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); + if (!ok) + return JS_ThrowTypeError(ctx, "not a function"); + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + arr = js_allocate_fast_array(ctx, len); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + } + + ret = js_array_sort(ctx, arr, argc, argv); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, ret); + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + typedef struct JSArrayIteratorData { JSValue obj; JSIteratorKindEnum kind; @@ -39384,6 +42022,7 @@ static const JSCFunctionListEntry js_iterator_proto_funcs[] = { static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_DEF("at", 1, js_array_at ), + JS_CFUNC_DEF("with", 2, js_array_with ), JS_CFUNC_DEF("concat", 1, js_array_concat ), JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), @@ -39393,10 +42032,10 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), JS_CFUNC_DEF("fill", 1, js_array_fill ), - JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, special_find ), - JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, special_findIndex ), - JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, special_findLast ), - JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, special_findLastIndex ), + JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ), + JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ), + JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ), JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), JS_CFUNC_DEF("includes", 1, js_array_includes ), @@ -39408,9 +42047,12 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), JS_CFUNC_DEF("reverse", 0, js_array_reverse ), + JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ), JS_CFUNC_DEF("sort", 1, js_array_sort ), + JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ), JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), + JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ), JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), @@ -39438,28 +42080,20 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: + val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val)); + if (JS_IsException(val)) + return val; + break; case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif { - JSBigFloat *p = JS_VALUE_GET_PTR(val); + JSBigInt *p = JS_VALUE_GET_PTR(val); double d; - bf_get_float64(&p->num, &d, BF_RNDN); + d = js_bigint_to_float64(ctx, p); JS_FreeValue(ctx, val); - val = __JS_NewFloat64(ctx, d); + val = JS_NewFloat64(ctx, d); } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - return val; - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return val; - break; -#endif default: break; } @@ -39539,9 +42173,9 @@ static const JSCFunctionListEntry js_number_funcs[] = { JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ), JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), - JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), - JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), - JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), + JS_PROP_DOUBLE_DEF("NaN", (0.0 / 0.0), 0 ), + JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", (-1.0 / 0.0), 0 ), + JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", (1.0 / 0.0), 0 ), JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ @@ -39586,7 +42220,7 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSValue val; - int base; + int base, flags; double d; val = js_thisNumberValue(ctx, this_val); @@ -39599,9 +42233,18 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, if (base < 0) goto fail; } + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { + char buf1[70]; + int len; + len = i64toa_radix(buf1, JS_VALUE_GET_INT(val), base); + return js_new_string8_len(ctx, buf1, len); + } if (JS_ToFloat64Free(ctx, &d, val)) return JS_EXCEPTION; - return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT); + flags = JS_DTOA_FORMAT_FREE; + if (base != 10) + flags |= JS_DTOA_EXP_DISABLED; + return js_dtoa2(ctx, d, base, 0, flags); fail: JS_FreeValue(ctx, val); return JS_EXCEPTION; @@ -39611,7 +42254,7 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue val; - int f; + int f, flags; double d; val = js_thisNumberValue(ctx, this_val); @@ -39623,11 +42266,11 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (f < 0 || f > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); - if (fabs(d) >= 1e21) { - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } else { - return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); - } + if (fabs(d) >= 1e21) + flags = JS_DTOA_FORMAT_FREE; + else + flags = JS_DTOA_FORMAT_FRAC; + return js_dtoa2(ctx, d, 10, f, flags); } static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, @@ -39648,15 +42291,15 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); } if (JS_IsUndefined(argv[0])) { - flags = 0; + flags = JS_DTOA_FORMAT_FREE; f = 0; } else { if (f < 0 || f > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); f++; - flags = JS_DTOA_FIXED_FORMAT; + flags = JS_DTOA_FORMAT_FIXED; } - return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP); + return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED); } static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, @@ -39681,7 +42324,7 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, } if (p < 1 || p > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); - return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT); + return js_dtoa2(ctx, d, 10, p, JS_DTOA_FORMAT_FIXED); } static const JSCFunctionListEntry js_number_proto_funcs[] = { @@ -39805,10 +42448,7 @@ static int js_string_get_own_property(JSContext *ctx, idx = __JS_AtomToUInt32(prop); if (idx < p1->len) { if (desc) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; - else - ch = p1->u.str8[idx]; + ch = string_get(p1, idx); desc->flags = JS_PROP_ENUMERABLE; desc->value = js_new_string_char(ctx, ch); desc->getter = JS_UNDEFINED; @@ -39830,7 +42470,7 @@ static int js_string_define_own_property(JSContext *ctx, uint32_t idx; JSObject *p; JSString *p1, *p2; - + if (__JS_AtomIsTaggedInt(prop)) { idx = __JS_AtomToUInt32(prop); p = JS_VALUE_GET_OBJ(this_obj); @@ -39901,7 +42541,9 @@ static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, JSString *p1 = JS_VALUE_GET_STRING(val); obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); - if (!JS_IsException(obj)) { + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + } else { JS_SetObjectData(ctx, obj, val); JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); } @@ -39913,7 +42555,8 @@ static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val) { - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING || + JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING_ROPE) return JS_DupValue(ctx, this_val); if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { @@ -39963,7 +42606,7 @@ static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, } else { if (JS_ToFloat64(ctx, &d, argv[i])) goto fail; - if (d < 0 || d > 0x10ffff || (c = (int)d) != d) + if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d) goto range_error; } if (string_buffer_putc(b, c)) @@ -39996,7 +42639,7 @@ static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val, goto exception; if (js_get_length64(ctx, &n, raw) < 0) goto exception; - + for (i = 0; i < n; i++) { val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); if (JS_IsException(val)) @@ -40074,10 +42717,7 @@ static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, if (idx < 0 || idx >= p->len) { ret = JS_NAN; } else { - if (p->is_wide_char) - c = p->u.str16[idx]; - else - c = p->u.str8[idx]; + c = string_get(p, idx); ret = JS_NewInt32(ctx, c); } JS_FreeValue(ctx, val); @@ -40105,12 +42745,9 @@ static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, if (is_at) ret = JS_UNDEFINED; else - ret = js_new_string8(ctx, NULL, 0); + ret = JS_AtomToString(ctx, JS_ATOM_empty_string); } else { - if (p->is_wide_char) - c = p->u.str16[idx]; - else - c = p->u.str8[idx]; + c = string_get(p, idx); ret = js_new_string_char(ctx, c); } JS_FreeValue(ctx, val); @@ -40218,6 +42855,80 @@ static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) return index; } +/* return the position of the first invalid character in the string or + -1 if none */ +static int js_string_find_invalid_codepoint(JSString *p) +{ + int i; + if (!p->is_wide_char) + return -1; + for(i = 0; i < p->len; i++) { + uint32_t c = p->u.str16[i]; + if (is_surrogate(c)) { + if (is_hi_surrogate(c) && (i + 1) < p->len + && is_lo_surrogate(p->u.str16[i + 1])) { + i++; + } else { + return i; + } + } + } + return -1; +} + +static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + JSString *p; + BOOL ret; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + p = JS_VALUE_GET_STRING(str); + ret = (js_string_find_invalid_codepoint(p) < 0); + JS_FreeValue(ctx, str); + return JS_NewBool(ctx, ret); +} + +static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str, ret; + JSString *p; + int i; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(str); + /* avoid reallocating the string if it is well-formed */ + i = js_string_find_invalid_codepoint(p); + if (i < 0) + return str; + + ret = js_new_string16_len(ctx, p->u.str16, p->len); + JS_FreeValue(ctx, str); + if (JS_IsException(ret)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(ret); + for (; i < p->len; i++) { + uint32_t c = p->u.str16[i]; + if (is_surrogate(c)) { + if (is_hi_surrogate(c) && (i + 1) < p->len + && is_lo_surrogate(p->u.str16[i + 1])) { + i++; + } else { + p->u.str16[i] = 0xFFFD; + } + } + } + return ret; +} + static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int lastIndexOf) { @@ -40300,7 +43011,7 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, ret = js_is_regexp(ctx, argv[0]); if (ret) { if (ret > 0) - JS_ThrowTypeError(ctx, "regex not supported"); + JS_ThrowTypeError(ctx, "regexp not supported"); goto fail; } v = JS_ToString(ctx, argv[0]); @@ -40354,7 +43065,7 @@ static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp) { int ret; JSValue flags; - + ret = js_is_regexp(ctx, regexp); if (ret < 0) return -1; @@ -40412,10 +43123,10 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, args[0] = regexp; str = JS_UNDEFINED; if (atom == JS_ATOM_Symbol_matchAll) { - str = JS_NewString(ctx, "g"); + str = js_new_string8(ctx, "g"); if (JS_IsException(str)) goto fail; - args[args_len++] = (JSValueConst)str; + args[args_len++] = str; } rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); JS_FreeValue(ctx, str); @@ -40628,7 +43339,7 @@ static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, } if (JS_IsException(repl_str)) goto exception; - + string_buffer_concat(b, sp, endOfLastMatch, pos); string_buffer_concat_value_free(b, repl_str); endOfLastMatch = pos + searchp->len; @@ -40862,8 +43573,8 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, } } if (n > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); - goto fail2; + JS_ThrowRangeError(ctx, "invalid string length"); + goto fail3; } if (string_buffer_init(ctx, b, n)) goto fail3; @@ -40924,8 +43635,9 @@ static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, len = p->len; if (len == 0 || n == 1) return str; + // XXX: potential arithmetic overflow if (val * len > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); + JS_ThrowRangeError(ctx, "invalid string length"); goto fail; } if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) @@ -40988,10 +43700,10 @@ static int string_prevc(JSString *p, int *pidx) idx--; if (p->is_wide_char) { c = p->u.str16[idx]; - if (c >= 0xdc00 && c < 0xe000 && idx > 0) { + if (is_lo_surrogate(c) && idx > 0) { c1 = p->u.str16[idx - 1]; - if (c1 >= 0xd800 && c1 <= 0xdc00) { - c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; + if (is_hi_surrogate(c1)) { + c = from_surrogate(c1, c); idx--; } } @@ -41030,26 +43742,6 @@ static BOOL test_final_sigma(JSString *p, int sigma_pos) return !lre_is_cased(c1); } -static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue a, b; - int cmp; - - a = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(a)) - return JS_EXCEPTION; - b = JS_ToString(ctx, argv[0]); - if (JS_IsException(b)) { - JS_FreeValue(ctx, a); - return JS_EXCEPTION; - } - cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); - JS_FreeValue(ctx, a); - JS_FreeValue(ctx, b); - return JS_NewInt32(ctx, cmp); -} - static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int to_lower) { @@ -41135,23 +43827,38 @@ static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) return JS_EXCEPTION; } +static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf, + JSValueConst val, + UnicodeNormalizationEnum n_type) +{ + int buf_len, out_len; + uint32_t *buf, *out_buf; + + buf_len = JS_ToUTF32String(ctx, &buf, val); + if (buf_len < 0) + return -1; + out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, + ctx->rt, (DynBufReallocFunc *)js_realloc_rt); + js_free(ctx, buf); + if (out_len < 0) + return -1; + *pout_buf = out_buf; + return out_len; +} + static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { const char *form, *p; size_t form_len; - int is_compat, buf_len, out_len; + int is_compat, out_len; UnicodeNormalizationEnum n_type; JSValue val; - uint32_t *buf, *out_buf; + uint32_t *out_buf; val = JS_ToStringCheckObject(ctx, this_val); if (JS_IsException(val)) return val; - buf_len = JS_ToUTF32String(ctx, &buf, val); - JS_FreeValue(ctx, val); - if (buf_len < 0) - return JS_EXCEPTION; if (argc == 0 || JS_IsUndefined(argv[0])) { n_type = UNICODE_NFC; @@ -41177,22 +43884,96 @@ static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, form); JS_ThrowRangeError(ctx, "bad normalization form"); fail1: - js_free(ctx, buf); + JS_FreeValue(ctx, val); return JS_EXCEPTION; } JS_FreeCString(ctx, form); } - out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, - ctx->rt, (DynBufReallocFunc *)js_realloc_rt); - js_free(ctx, buf); + out_len = js_string_normalize1(ctx, &out_buf, val, n_type); + JS_FreeValue(ctx, val); if (out_len < 0) return JS_EXCEPTION; val = JS_NewUTF32String(ctx, out_buf, out_len); js_free(ctx, out_buf); return val; } -#endif /* CONFIG_ALL_UNICODE */ + +/* return < 0, 0 or > 0 */ +static int js_UTF32_compare(const uint32_t *buf1, int buf1_len, + const uint32_t *buf2, int buf2_len) +{ + int i, len, c, res; + len = min_int(buf1_len, buf2_len); + for(i = 0; i < len; i++) { + /* Note: range is limited so a subtraction is valid */ + c = buf1[i] - buf2[i]; + if (c != 0) + return c; + } + if (buf1_len == buf2_len) + res = 0; + else if (buf1_len < buf2_len) + res = -1; + else + res = 1; + return res; +} + +static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue a, b; + int cmp, a_len, b_len; + uint32_t *a_buf, *b_buf; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC); + JS_FreeValue(ctx, a); + if (a_len < 0) { + JS_FreeValue(ctx, b); + return JS_EXCEPTION; + } + + b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC); + JS_FreeValue(ctx, b); + if (b_len < 0) { + js_free(ctx, a_buf); + return JS_EXCEPTION; + } + cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len); + js_free(ctx, a_buf); + js_free(ctx, b_buf); + return JS_NewInt32(ctx, cmp); +} +#else /* CONFIG_ALL_UNICODE */ +static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue a, b; + int cmp; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); + JS_FreeValue(ctx, a); + JS_FreeValue(ctx, b); + return JS_NewInt32(ctx, cmp); +} +#endif /* !CONFIG_ALL_UNICODE */ /* also used for String.prototype.valueOf */ static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val, @@ -41276,7 +44057,7 @@ static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val, if (c <= 0xffff) { return js_new_string_char(ctx, c); } else { - return js_new_string16(ctx, p->u.str16 + start, 2); + return js_new_string16_len(ctx, p->u.str16 + start, 2); } } @@ -41306,7 +44087,7 @@ static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val, static struct { const char *tag, *attr; } const defs[] = { { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL }, - { "a", "href" }, { "small", NULL }, { "strike", NULL }, + { "a", "href" }, { "small", NULL }, { "strike", NULL }, { "sub", NULL }, { "sup", NULL }, }; @@ -41369,6 +44150,8 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ), JS_CFUNC_DEF("concat", 1, js_string_concat ), JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ), + JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ), + JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ), JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ), @@ -41394,7 +44177,6 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = { JS_CFUNC_DEF("toString", 0, js_string_toString ), JS_CFUNC_DEF("valueOf", 0, js_string_toString ), JS_CFUNC_DEF("__quote", 1, js_string___quote ), - JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), @@ -41421,18 +44203,17 @@ static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = { JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ), }; -#ifdef CONFIG_ALL_UNICODE static const JSCFunctionListEntry js_string_proto_normalize[] = { +#ifdef CONFIG_ALL_UNICODE JS_CFUNC_DEF("normalize", 0, js_string_normalize ), -}; #endif + JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), +}; void JS_AddIntrinsicStringNormalize(JSContext *ctx) { -#ifdef CONFIG_ALL_UNICODE JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize, countof(js_string_proto_normalize)); -#endif } /* Math */ @@ -41474,7 +44255,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, uint32_t tag; if (unlikely(argc == 0)) { - return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); + return __JS_NewFloat64(ctx, is_max ? -INFINITY : INFINITY); } tag = JS_VALUE_GET_TAG(argv[0]); @@ -41580,6 +44361,11 @@ static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, return JS_NewFloat64(ctx, r); } +static double js_math_f16round(double a) +{ + return fromfp16(tofp16(a)); +} + static double js_math_fround(double a) { return (float)a; @@ -41588,14 +44374,16 @@ static double js_math_fround(double a) static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - int a, b; + uint32_t a, b, c; + int32_t d; - if (JS_ToInt32(ctx, &a, argv[0])) + if (JS_ToUint32(ctx, &a, argv[0])) return JS_EXCEPTION; - if (JS_ToInt32(ctx, &b, argv[1])) + if (JS_ToUint32(ctx, &b, argv[1])) return JS_EXCEPTION; - /* purposely ignoring overflow */ - return JS_NewInt32(ctx, a * b); + c = a * b; + memcpy(&d, &c, sizeof(d)); + return JS_NewInt32(ctx, d); } static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, @@ -41681,6 +44469,7 @@ static const JSCFunctionListEntry js_math_funcs[] = { JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), JS_CFUNC_DEF("hypot", 2, js_math_hypot ), JS_CFUNC_DEF("random", 0, js_math_random ), + JS_CFUNC_SPECIAL_DEF("f16round", 1, f_f, js_math_f16round ), JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), JS_CFUNC_DEF("imul", 2, js_math_imul ), JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), @@ -41701,39 +44490,12 @@ static const JSCFunctionListEntry js_math_obj[] = { /* Date */ -#if 0 -/* OS dependent: return the UTC time in ms since 1970. */ -static JSValue js___date_now(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); - return JS_NewInt64(ctx, d); -} -#endif - -/* OS dependent: return the UTC time in microseconds since 1970. */ -static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; - return JS_NewInt64(ctx, d); -} - /* OS dependent. d = argv[0] is in ms from 1970. Return the difference between UTC time and local time 'd' in minutes */ -static int getTimezoneOffset(int64_t time) { -#if defined(_WIN32) - /* XXX: TODO */ - return 0; -#else +static int getTimezoneOffset(int64_t time) +{ time_t ti; - struct tm tm; + int res; time /= 1000; /* convert to seconds */ if (sizeof(time_t) == 4) { @@ -41757,9 +44519,31 @@ static int getTimezoneOffset(int64_t time) { } } ti = time; - localtime_r(&ti, &tm); - return -tm.tm_gmtoff / 60; +#if defined(_WIN32) + { + struct tm *tm; + time_t gm_ti, loc_ti; + + tm = gmtime(&ti); + if (!tm) + return 0; + gm_ti = mktime(tm); + + tm = localtime(&ti); + if (!tm) + return 0; + loc_ti = mktime(tm); + + res = (gm_ti - loc_ti) / 60; + } +#else + { + struct tm tm; + localtime_r(&ti, &tm); + res = -tm.tm_gmtoff / 60; + } #endif + return res; } #if 0 @@ -41836,6 +44620,9 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ for (i = 0; i < len; i++) { switch(str[i]) { + case 'd': + mask = LRE_FLAG_INDICES; + break; case 'g': mask = LRE_FLAG_GLOBAL; break; @@ -41849,7 +44636,10 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, mask = LRE_FLAG_DOTALL; break; case 'u': - mask = LRE_FLAG_UTF16; + mask = LRE_FLAG_UNICODE; + break; + case 'v': + mask = LRE_FLAG_UNICODE_SETS; break; case 'y': mask = LRE_FLAG_STICKY; @@ -41860,14 +44650,20 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, if ((re_flags & mask) != 0) { bad_flags: JS_FreeCString(ctx, str); - return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + goto bad_flags1; } re_flags |= mask; } JS_FreeCString(ctx, str); } - str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16)); + /* 'u' and 'v' cannot be both set */ + if ((re_flags & LRE_FLAG_UNICODE_SETS) && (re_flags & LRE_FLAG_UNICODE)) { + bad_flags1: + return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + } + + str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS))); if (!str) return JS_EXCEPTION; re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, @@ -41878,7 +44674,7 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, return JS_EXCEPTION; } - ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len); + ret = js_new_string8_len(ctx, (const char *)re_bytecode_buf, re_bytecode_len); js_free(ctx, re_bytecode_buf); return ret; } @@ -42106,8 +44902,8 @@ static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) if (p->len == 0) { empty_regex: - return JS_NewString(ctx, "(?:)"); - } + return js_new_string8(ctx, "(?:)"); + } string_buffer_init2(ctx, b, p->len, p->is_wide_char); /* Escape '/' and newline sequences as needed */ @@ -42166,49 +44962,39 @@ static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mas else return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); } - + flags = lre_get_flags(re->bytecode->u.str8); - return JS_NewBool(ctx, (flags & mask) != 0); + return JS_NewBool(ctx, flags & mask); } +#define RE_FLAG_COUNT 8 + static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) { - char str[8], *p = str; - int res; - + char str[RE_FLAG_COUNT], *p = str; + int res, i; + static const int flag_atom[RE_FLAG_COUNT] = { + JS_ATOM_hasIndices, + JS_ATOM_global, + JS_ATOM_ignoreCase, + JS_ATOM_multiline, + JS_ATOM_dotAll, + JS_ATOM_unicode, + JS_ATOM_unicodeSets, + JS_ATOM_sticky, + }; + static const char flag_char[RE_FLAG_COUNT] = { 'd', 'g', 'i', 'm', 's', 'u', 'v', 'y' }; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) return JS_ThrowTypeErrorNotAnObject(ctx); - res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); - if (res < 0) - goto exception; - if (res) - *p++ = 'g'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); - if (res < 0) - goto exception; - if (res) - *p++ = 'i'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); - if (res < 0) - goto exception; - if (res) - *p++ = 'm'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); - if (res < 0) - goto exception; - if (res) - *p++ = 's'; - res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); - if (res < 0) - goto exception; - if (res) - *p++ = 'u'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); - if (res < 0) - goto exception; - if (res) - *p++ = 'y'; + for(i = 0; i < RE_FLAG_COUNT; i++) { + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, flag_atom[i])); + if (res < 0) + goto exception; + if (res) + *p++ = flag_char[i]; + } return JS_NewStringLen(ctx, str, p - str); exception: @@ -42240,12 +45026,20 @@ static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } -BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +int lre_check_stack_overflow(void *opaque, size_t alloca_size) { JSContext *ctx = opaque; return js_check_stack_overflow(ctx->rt, alloca_size); } +int lre_check_timeout(void *opaque) +{ + JSContext *ctx = opaque; + JSRuntime *rt = ctx->rt; + return (rt->interrupt_handler && + rt->interrupt_handler(rt, rt->interrupt_opaque)); +} + void *lre_realloc(void *opaque, void *ptr, size_t size) { JSContext *ctx = opaque; @@ -42253,30 +45047,89 @@ void *lre_realloc(void *opaque, void *ptr, size_t size) return js_realloc_rt(ctx->rt, ptr, size); } +static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + uint32_t c, i; + char s[16]; + + if (!JS_IsString(argv[0])) + return JS_ThrowTypeError(ctx, "not a string"); + str = JS_ToString(ctx, argv[0]); /* must call it to linearlize ropes */ + if (JS_IsException(str)) + return JS_EXCEPTION; + p = JS_VALUE_GET_STRING(str); + string_buffer_init2(ctx, b, 0, p->is_wide_char); + for (i = 0; i < p->len; i++) { + c = string_get(p, i); + if (c < 33) { + if (c >= 9 && c <= 13) { + string_buffer_putc8(b, '\\'); + string_buffer_putc8(b, "tnvfr"[c - 9]); + } else { + goto hex2; + } + } else if (c < 128) { + if ((c >= '0' && c <= '9') + || (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z')) { + if (i == 0) + goto hex2; + } else if (strchr(",-=<>#&!%:;@~'`\"", c)) { + goto hex2; + } else if (c != '_') { + string_buffer_putc8(b, '\\'); + } + string_buffer_putc8(b, c); + } else if (c < 256) { + hex2: + snprintf(s, sizeof(s), "\\x%02x", c); + string_buffer_puts8(b, s); + } else if (is_surrogate(c) || lre_is_space(c)) { + snprintf(s, sizeof(s), "\\u%04x", c); + string_buffer_puts8(b, s); + } else { + string_buffer_putc16(b, c); + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); +} + static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); JSString *str; - JSValue str_val, obj, val, groups = JS_UNDEFINED; + JSValue t, ret, str_val, obj, val, groups; + JSValue indices, indices_groups; uint8_t *re_bytecode; - int ret; uint8_t **capture, *str_buf; - int capture_count, shift, i, re_flags; + int rc, capture_count, shift, i, re_flags; int64_t last_index; const char *group_name_ptr; if (!re) return JS_EXCEPTION; + str_val = JS_ToString(ctx, argv[0]); if (JS_IsException(str_val)) - return str_val; - val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); - if (JS_IsException(val) || - JS_ToLengthFree(ctx, &last_index, val)) { - JS_FreeValue(ctx, str_val); return JS_EXCEPTION; - } + + ret = JS_EXCEPTION; + obj = JS_NULL; + groups = JS_UNDEFINED; + indices = JS_UNDEFINED; + indices_groups = JS_UNDEFINED; + capture = NULL; + + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) + goto fail; + re_bytecode = re->bytecode->u.str8; re_flags = lre_get_flags(re_bytecode); if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { @@ -42284,36 +45137,35 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, } str = JS_VALUE_GET_STRING(str_val); capture_count = lre_get_capture_count(re_bytecode); - capture = NULL; if (capture_count > 0) { capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); - if (!capture) { - JS_FreeValue(ctx, str_val); - return JS_EXCEPTION; - } + if (!capture) + goto fail; } shift = str->is_wide_char; str_buf = str->u.str8; if (last_index > str->len) { - ret = 2; + rc = 2; } else { - ret = lre_exec(capture, re_bytecode, - str_buf, last_index, str->len, - shift, ctx); + rc = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, + shift, ctx); } - obj = JS_NULL; - if (ret != 1) { - if (ret >= 0) { - if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (rc != 1) { + if (rc >= 0) { + if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (rc == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } - JS_FreeValue(ctx, str_val); } else { int prop_flags; if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { @@ -42331,52 +45183,124 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, if (JS_IsException(groups)) goto fail; } + if (re_flags & LRE_FLAG_INDICES) { + indices = JS_NewArray(ctx); + if (JS_IsException(indices)) + goto fail; + if (group_name_ptr) { + indices_groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(indices_groups)) + goto fail; + } + } for(i = 0; i < capture_count; i++) { - int start, end; + const char *name = NULL; + uint8_t **match = &capture[2 * i]; + int start = -1; + int end = -1; JSValue val; - if (capture[2 * i] == NULL || - capture[2 * i + 1] == NULL) { + + if (group_name_ptr && i > 0) { + if (*group_name_ptr) name = group_name_ptr; + group_name_ptr += strlen(group_name_ptr) + 1; + } + + if (match[0] && match[1]) { + start = (match[0] - str_buf) >> shift; + end = (match[1] - str_buf) >> shift; + } + + if (!JS_IsUndefined(indices)) { val = JS_UNDEFINED; - } else { - start = (capture[2 * i] - str_buf) >> shift; - end = (capture[2 * i + 1] - str_buf) >> shift; + if (start != -1) { + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto fail; + if (JS_DefinePropertyValueUint32(ctx, val, 0, + JS_NewInt32(ctx, start), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + if (JS_DefinePropertyValueUint32(ctx, val, 1, + JS_NewInt32(ctx, end), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + if (name && !JS_IsUndefined(indices_groups)) { + val = JS_DupValue(ctx, val); + if (JS_DefinePropertyValueStr(ctx, indices_groups, + name, val, prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + if (JS_DefinePropertyValueUint32(ctx, indices, i, val, + prop_flags) < 0) { + goto fail; + } + } + + val = JS_UNDEFINED; + if (start != -1) { val = js_sub_string(ctx, str, start, end); if (JS_IsException(val)) goto fail; } - if (group_name_ptr && i > 0) { - if (*group_name_ptr) { - if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr, - JS_DupValue(ctx, val), - prop_flags) < 0) { - JS_FreeValue(ctx, val); - goto fail; - } + + if (name) { + if (JS_DefinePropertyValueStr(ctx, groups, name, + JS_DupValue(ctx, val), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; } - group_name_ptr += strlen(group_name_ptr) + 1; } + if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) goto fail; } - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, - groups, prop_flags) < 0) + + t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift); + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0) + goto fail; + + t = str_val, str_val = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0) goto fail; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, - JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0) + + t = groups, groups = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, + t, prop_flags) < 0) { goto fail; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0) - goto fail1; + } + + if (!JS_IsUndefined(indices)) { + t = indices_groups, indices_groups = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups, + t, prop_flags) < 0) { + goto fail; + } + t = indices, indices = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices, + t, prop_flags) < 0) { + goto fail; + } + } } - js_free(ctx, capture); - return obj; + ret = obj; + obj = JS_UNDEFINED; fail: - JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, indices_groups); + JS_FreeValue(ctx, indices); JS_FreeValue(ctx, str_val); -fail1: + JS_FreeValue(ctx, groups); JS_FreeValue(ctx, obj); js_free(ctx, capture); - return JS_EXCEPTION; + return ret; } /* delete portions of a string that match a given regex */ @@ -42435,7 +45359,11 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueCon goto fail; } } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + if (ret == LRE_RET_TIMEOUT) { + JS_ThrowInterrupted(ctx); + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + } goto fail; } break; @@ -42455,7 +45383,7 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueCon break; } if (end == start) { - if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) { + if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) { end++; } else { string_getc(str, &end); @@ -42551,14 +45479,12 @@ static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, goto exception; p = JS_VALUE_GET_STRING(flags); - // TODO(bnoordhuis) query 'u' flag the same way? global = (-1 != string_indexof_char(p, 'g', 0)); if (!global) { A = JS_RegExpExec(ctx, rx, S); } else { - fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); - if (fullUnicode < 0) - goto exception; + fullUnicode = (string_indexof_char(p, 'u', 0) >= 0 || + string_indexof_char(p, 'v', 0) >= 0); if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) goto exception; @@ -42577,7 +45503,7 @@ static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, if (JS_IsException(matchStr)) goto exception; isEmpty = JS_IsEmptyString(matchStr); - if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, n++, matchStr, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (isEmpty) { int64_t thisIndex, nextIndex; @@ -42702,7 +45628,7 @@ static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, JSString *strp; int64_t lastIndex; JSRegExpStringIteratorData *it; - + if (!JS_IsObject(R)) return JS_ThrowTypeErrorNotAnObject(ctx); @@ -42710,7 +45636,7 @@ static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, flags = JS_UNDEFINED; matcher = JS_UNDEFINED; iter = JS_UNDEFINED; - + S = JS_ToString(ctx, argv[0]); if (JS_IsException(S)) goto exception; @@ -42731,7 +45657,7 @@ static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, JS_NewInt64(ctx, lastIndex)) < 0) goto exception; - + iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); if (JS_IsException(iter)) goto exception; @@ -42742,7 +45668,8 @@ static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, it->iterated_string = S; strp = JS_VALUE_GET_STRING(flags); it->global = string_indexof_char(strp, 'g', 0) >= 0; - it->unicode = string_indexof_char(strp, 'u', 0) >= 0; + it->unicode = (string_indexof_char(strp, 'u', 0) >= 0 || + string_indexof_char(strp, 'v', 0) >= 0); it->done = FALSE; JS_SetOpaque(iter, it); @@ -42870,7 +45797,7 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) goto exception; - + sp = JS_VALUE_GET_STRING(str); rp = NULL; functionalReplace = JS_IsFunction(ctx, rep); @@ -42889,13 +45816,11 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, goto exception; p = JS_VALUE_GET_STRING(flags); - // TODO(bnoordhuis) query 'u' flag the same way? fullUnicode = 0; is_global = (-1 != string_indexof_char(p, 'g', 0)); if (is_global) { - fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); - if (fullUnicode < 0) - goto exception; + fullUnicode = (string_indexof_char(p, 'u', 0) >= 0 || + string_indexof_char(p, 'v', 0) >= 0); if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) goto exception; } @@ -43121,7 +46046,8 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, if (JS_IsException(flags)) goto exception; strp = JS_VALUE_GET_STRING(flags); - unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; + unicodeMatching = (string_indexof_char(strp, 'u', 0) >= 0 || + string_indexof_char(strp, 'v', 0) >= 0); if (string_indexof_char(strp, 'y', 0) < 0) { flags = JS_ConcatString3(ctx, "", flags, "y"); if (JS_IsException(flags)) @@ -43158,7 +46084,7 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, while (q < size) { if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) goto exception; - JS_FreeValue(ctx, z); + JS_FreeValue(ctx, z); z = JS_RegExpExec(ctx, splitter, str); if (JS_IsException(z)) goto exception; @@ -43184,7 +46110,7 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, if (js_get_length64(ctx, &numberOfCaptures, z)) goto exception; for(i = 1; i < numberOfCaptures; i++) { - sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); + sub = JS_GetPropertyInt64(ctx, z, i); if (JS_IsException(sub)) goto exception; if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) @@ -43213,11 +46139,12 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, ctor); JS_FreeValue(ctx, splitter); JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, z); + JS_FreeValue(ctx, z); return A; } static const JSCFunctionListEntry js_regexp_funcs[] = { + JS_CFUNC_DEF("escape", 1, js_regexp_escape ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ), //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ), @@ -43226,12 +46153,14 @@ static const JSCFunctionListEntry js_regexp_funcs[] = { static const JSCFunctionListEntry js_regexp_proto_funcs[] = { JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), - JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ), - JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ), - JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ), - JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ), + JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ), + JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ), + JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ), + JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ), + JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ), + JS_CGETSET_MAGIC_DEF("unicodeSets", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE_SETS ), + JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ), + JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ), JS_CFUNC_DEF("exec", 1, js_regexp_exec ), JS_CFUNC_DEF("compile", 2, js_regexp_compile ), JS_CFUNC_DEF("test", 1, js_regexp_test ), @@ -43298,7 +46227,7 @@ static JSValue json_parse_value(JSParseState *s) { JSValue prop_val; JSAtom prop_name; - + if (json_next_token(s)) goto fail; val = JS_NewObject(ctx); @@ -43389,9 +46318,15 @@ static JSValue json_parse_value(JSParseState *s) case TOK_IDENT: if (s->token.u.ident.atom == JS_ATOM_false || s->token.u.ident.atom == JS_ATOM_true) { - val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true)); + val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true); } else if (s->token.u.ident.atom == JS_ATOM_null) { val = JS_NULL; + } else if (s->token.u.ident.atom == JS_ATOM_NaN && s->ext_json) { + /* Note: json5 identifier handling is ambiguous e.g. is + '{ NaN: 1 }' a valid JSON5 production ? */ + val = JS_NewFloat64(s->ctx, NAN); + } else if (s->token.u.ident.atom == JS_ATOM_Infinity && s->ext_json) { + val = JS_NewFloat64(s->ctx, INFINITY); } else { goto def_token; } @@ -43401,7 +46336,7 @@ static JSValue json_parse_value(JSParseState *s) default: def_token: if (s->token.val == TOK_EOF) { - js_parse_error(s, "unexpected end of input"); + js_parse_error(s, "Unexpected end of JSON input"); } else { js_parse_error(s, "unexpected token: '%.*s'", (int)(s->buf_ptr - s->token.ptr), s->token.ptr); @@ -43441,7 +46376,7 @@ JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, const char *filename) { - return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); + return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); } static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, @@ -43496,7 +46431,7 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, goto fail; } } - js_free_prop_enum(ctx, atoms, len); + JS_FreePropertyEnum(ctx, atoms, len); atoms = NULL; name_val = JS_AtomToValue(ctx, name); if (JS_IsException(name_val)) @@ -43508,7 +46443,7 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, JS_FreeValue(ctx, val); return res; fail: - js_free_prop_enum(ctx, atoms, len); + JS_FreePropertyEnum(ctx, atoms, len); JS_FreeValue(ctx, val); return JS_EXCEPTION; } @@ -43568,22 +46503,22 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, JSValue v; JSValueConst args[2]; - if (JS_IsObject(val) || - JS_IsBigInt(ctx, val) /* XXX: probably useless */ - ) { - JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); - if (JS_IsException(f)) + /* check for object.toJSON method */ + /* ECMA specifies this is done only for Object and BigInt */ + if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) { + JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); + if (JS_IsException(f)) + goto exception; + if (JS_IsFunction(ctx, f)) { + v = JS_CallFree(ctx, f, val, 1, &key); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) goto exception; - if (JS_IsFunction(ctx, f)) { - v = JS_CallFree(ctx, f, val, 1, &key); - JS_FreeValue(ctx, val); - val = v; - if (JS_IsException(val)) - goto exception; - } else { - JS_FreeValue(ctx, f); - } + } else { + JS_FreeValue(ctx, f); } + } if (!JS_IsUndefined(jsc->replacer_func)) { args[0] = key; @@ -43600,13 +46535,12 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, if (JS_IsFunction(ctx, val)) break; case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: case JS_TAG_INT: case JS_TAG_FLOAT64: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif case JS_TAG_BOOL: case JS_TAG_NULL: + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: case JS_TAG_EXCEPTION: return val; @@ -43630,43 +46564,36 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, int64_t i, len; int cl, ret; BOOL has_content; - + indent1 = JS_UNDEFINED; sep = JS_UNDEFINED; sep1 = JS_UNDEFINED; tab = JS_UNDEFINED; prop = JS_UNDEFINED; - switch (JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_OBJECT: + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + goto exception; + } + + if (JS_IsObject(val)) { p = JS_VALUE_GET_OBJ(val); cl = p->class_id; if (cl == JS_CLASS_STRING) { val = JS_ToStringFree(ctx, val); if (JS_IsException(val)) goto exception; - val = JS_ToQuotedStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - return string_buffer_concat_value_free(jsc->b, val); + goto concat_primitive; } else if (cl == JS_CLASS_NUMBER) { val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) goto exception; - return string_buffer_concat_value_free(jsc->b, val); - } else if (cl == JS_CLASS_BOOLEAN) { - ret = string_buffer_concat_value(jsc->b, p->u.object_data); - JS_FreeValue(ctx, val); - return ret; - } else -#ifdef CONFIG_BIGNUM - if (cl == JS_CLASS_BIG_FLOAT) { - return string_buffer_concat_value_free(jsc->b, val); - } else -#endif - if (cl == JS_CLASS_BIG_INT) { - JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); - goto exception; + goto concat_primitive; + } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT) + { + /* This will thow the same error as for the primitive object */ + set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data)); + goto concat_primitive; } v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); if (JS_IsException(v)) @@ -43682,7 +46609,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); if (JS_IsException(sep)) goto exception; - sep1 = JS_NewString(ctx, " "); + sep1 = js_new_string8(ctx, " "); if (JS_IsException(sep1)) goto exception; } else { @@ -43764,7 +46691,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, has_content = TRUE; } } - if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { + if (has_content && !JS_IsEmptyString(jsc->gap)) { string_buffer_putc8(jsc->b, '\n'); string_buffer_concat_value(jsc->b, indent); } @@ -43779,7 +46706,11 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, JS_FreeValue(ctx, indent1); JS_FreeValue(ctx, prop); return 0; + } + concat_primitive: + switch (JS_VALUE_GET_NORM_TAG(val)) { case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: val = JS_ToQuotedStringFree(ctx, val); if (JS_IsException(val)) goto exception; @@ -43790,21 +46721,20 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, } goto concat_value; case JS_TAG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif case JS_TAG_BOOL: case JS_TAG_NULL: concat_value: return string_buffer_concat_value_free(jsc->b, val); + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: - JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); + /* reject big numbers: use toJSON method to override */ + JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt"); goto exception; default: JS_FreeValue(ctx, val); return 0; } - + exception: JS_FreeValue(ctx, val); JS_FreeValue(ctx, tab); @@ -43905,7 +46835,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, int n; if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) goto exception; - jsc->gap = JS_NewStringLen(ctx, " ", n); + jsc->gap = js_new_string8_len(ctx, " ", n); } else if (JS_IsString(space)) { JSString *p = JS_VALUE_GET_STRING(space); jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); @@ -43922,7 +46852,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) goto exception; val = JS_DupValue(ctx, obj); - + val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); if (JS_IsException(val)) goto exception; @@ -44178,7 +47108,7 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, JS_ThrowStackOverflow(ctx); return NULL; } - + /* 's' should never be NULL */ if (s->is_revoked) { JS_ThrowTypeErrorRevokedProxy(ctx); @@ -44193,7 +47123,7 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, return s; } -static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) +static JSValue js_proxy_get_prototype(JSContext *ctx, JSValueConst obj) { JSProxyData *s; JSValue method, ret, proto1; @@ -44223,7 +47153,7 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) JS_FreeValue(ctx, ret); return JS_EXCEPTION; } - if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { + if (!js_same_value(ctx, proto1, ret)) { JS_FreeValue(ctx, proto1); fail: JS_FreeValue(ctx, ret); @@ -44234,8 +47164,8 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) return ret; } -static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, BOOL throw_flag) +static int js_proxy_set_prototype(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val) { JSProxyData *s; JSValue method, ret, proto1; @@ -44247,21 +47177,15 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, if (!s) return -1; if (JS_IsUndefined(method)) - return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag); + return JS_SetPrototypeInternal(ctx, s->target, proto_val, FALSE); args[0] = s->target; args[1] = proto_val; ret = JS_CallFree(ctx, method, s->handler, 2, args); if (JS_IsException(ret)) return -1; res = JS_ToBoolFree(ctx, ret); - if (!res) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "proxy: bad prototype"); - return -1; - } else { - return FALSE; - } - } + if (!res) + return FALSE; res2 = JS_IsExtensible(ctx, s->target); if (res2 < 0) return -1; @@ -44269,7 +47193,7 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, proto1 = JS_GetPrototype(ctx, s->target); if (JS_IsException(proto1)) return -1; - if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { + if (!js_same_value(ctx, proto_val, proto1)) { JS_FreeValue(ctx, proto1); JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); return -1; @@ -44279,7 +47203,7 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, return TRUE; } -static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) +static int js_proxy_is_extensible(JSContext *ctx, JSValueConst obj) { JSProxyData *s; JSValue method, ret; @@ -44305,7 +47229,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) return res; } -static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) +static int js_proxy_prevent_extensions(JSContext *ctx, JSValueConst obj) { JSProxyData *s; JSValue method, ret; @@ -44405,8 +47329,10 @@ static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, if (JS_IsException(ret)) return JS_EXCEPTION; res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res < 0) + if (res < 0) { + JS_FreeValue(ctx, ret); return JS_EXCEPTION; + } if (res) { if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { if (!js_same_value(ctx, desc.value, ret)) { @@ -44505,17 +47431,17 @@ static JSValue js_create_desc(JSContext *ctx, JSValueConst val, } if (flags & JS_PROP_HAS_WRITABLE) { JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0), + JS_NewBool(ctx, flags & JS_PROP_WRITABLE), JS_PROP_C_W_E); } if (flags & JS_PROP_HAS_ENUMERABLE) { JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, - JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0), + JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE), JS_PROP_C_W_E); } if (flags & JS_PROP_HAS_CONFIGURABLE) { JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, - JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0), + JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE), JS_PROP_C_W_E); } return ret; @@ -44577,6 +47503,14 @@ static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc JS_FreeValue(ctx, trap_result_obj); if (res < 0) return -1; + + /* convert the result_desc.flags to property flags */ + if (result_desc.flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + result_desc.flags |= JS_PROP_GETSET; + } else { + result_desc.flags |= JS_PROP_NORMAL; + } + result_desc.flags &= (JS_PROP_C_W_E | JS_PROP_TMASK); if (target_desc_ret) { /* convert result_desc.flags to defineProperty flags */ @@ -44676,13 +47610,11 @@ static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, if (!p->extensible || setting_not_configurable) goto fail; } else { - if (!check_define_prop_flags(desc.flags, flags) || - ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { + if (!check_define_prop_flags(desc.flags, flags)) goto fail1; - } - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == - JS_PROP_GETSET) { + /* do the missing check from check_define_prop_flags() */ + if (!(desc.flags & JS_PROP_CONFIGURABLE)) { + if ((desc.flags & JS_PROP_TMASK) == JS_PROP_GETSET) { if ((flags & JS_PROP_HAS_GET) && !js_same_value(ctx, getter, desc.getter)) { goto fail1; @@ -44691,27 +47623,26 @@ static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, !js_same_value(ctx, setter, desc.setter)) { goto fail1; } - } - } else if (flags & JS_PROP_HAS_VALUE) { - if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == - JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { - /* missing-proxy-check feature */ - goto fail1; - } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && - !js_same_value(ctx, val, desc.value)) { - goto fail1; + } else if (!(desc.flags & JS_PROP_WRITABLE)) { + if ((flags & JS_PROP_HAS_VALUE) && + !js_same_value(ctx, val, desc.value)) { + goto fail1; + } } } - if (flags & JS_PROP_HAS_WRITABLE) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | - JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { - /* proxy-missing-checks */ - fail1: - js_free_desc(ctx, &desc); - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); - return -1; - } + + /* additional checks */ + if ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable) + goto fail1; + + if ((desc.flags & JS_PROP_TMASK) != JS_PROP_GETSET && + (desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == JS_PROP_WRITABLE && + (flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + fail1: + js_free_desc(ctx, &desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); + return -1; } js_free_desc(ctx, &desc); } @@ -44886,14 +47817,14 @@ static int js_proxy_get_own_property_names(JSContext *ctx, } } - js_free_prop_enum(ctx, tab2, len2); + JS_FreePropertyEnum(ctx, tab2, len2); JS_FreeValue(ctx, prop_array); *ptab = tab; *plen = len; return 0; fail: - js_free_prop_enum(ctx, tab2, len2); - js_free_prop_enum(ctx, tab, len); + JS_FreePropertyEnum(ctx, tab2, len2); + JS_FreePropertyEnum(ctx, tab, len); JS_FreeValue(ctx, prop_array); return -1; } @@ -44942,7 +47873,7 @@ static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, if (flags & JS_CALL_FLAG_CONSTRUCTOR) return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); - + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply); if (!s) return JS_EXCEPTION; @@ -44967,20 +47898,35 @@ static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, return ret; } -static int js_proxy_isArray(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); - if (!s) - return FALSE; - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return -1; - } - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return -1; +/* `js_resolve_proxy`: resolve the proxy chain + `*pval` is updated with to ultimate proxy target + `throw_exception` controls whether exceptions are thown or not + - return -1 in case of error + - otherwise return 0 + */ +static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) { + int depth = 0; + JSObject *p; + JSProxyData *s; + + while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(*pval); + if (p->class_id != JS_CLASS_PROXY) + break; + if (depth++ > 1000) { + if (throw_exception) + JS_ThrowStackOverflow(ctx); + return -1; + } + s = p->u.opaque; + if (s->is_revoked) { + if (throw_exception) + JS_ThrowTypeErrorRevokedProxy(ctx); + return -1; + } + *pval = s->target; } - return JS_IsArray(ctx, s->target); + return 0; } static const JSClassExoticMethods js_proxy_exotic_methods = { @@ -44991,6 +47937,10 @@ static const JSClassExoticMethods js_proxy_exotic_methods = { .has_property = js_proxy_has, .get_property = js_proxy_get, .set_property = js_proxy_set, + .get_prototype = js_proxy_get_prototype, + .set_prototype = js_proxy_set_prototype, + .is_extensible = js_proxy_is_extensible, + .prevent_extensions = js_proxy_prevent_extensions, }; static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, @@ -45210,26 +48160,78 @@ static const JSCFunctionListEntry js_symbol_funcs[] = { /* Set/Map/WeakSet/WeakMap */ -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - BOOL empty; /* TRUE if the record is deleted */ - struct JSMapState *map; - struct JSMapRecord *next_weak_ref; - struct list_head link; - struct list_head hash_link; - JSValue key; - JSValue value; -} JSMapRecord; +static BOOL js_weakref_is_target(JSValueConst val) +{ + switch (JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + return TRUE; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + if (p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash != JS_ATOM_HASH_PRIVATE) + return TRUE; + } + break; + default: + break; + } + return FALSE; +} -typedef struct JSMapState { - BOOL is_weak; /* TRUE if WeakSet/WeakMap */ - struct list_head records; /* list of JSMapRecord.link */ - uint32_t record_count; - struct list_head *hash_table; - uint32_t hash_size; /* must be a power of two */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ -} JSMapState; +/* JS_UNDEFINED is considered as a live weakref */ +/* XXX: add a specific JSWeakRef value type ? */ +static BOOL js_weakref_is_live(JSValueConst val) +{ + int *pref_count; + if (JS_IsUndefined(val)) + return TRUE; + pref_count = JS_VALUE_GET_PTR(val); + return (*pref_count != 0); +} + +/* 'val' can be JS_UNDEFINED */ +static void js_weakref_free(JSRuntime *rt, JSValue val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(val); + assert(p->weakref_count >= 1); + p->weakref_count--; + /* 'mark' is tested to avoid freeing the object structure when + it is about to be freed in a cycle or in + free_zero_refcount() */ + if (p->weakref_count == 0 && p->header.ref_count == 0 && + p->header.mark == 0) { + js_free_rt(rt, p); + } + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) { + JSString *p = JS_VALUE_GET_STRING(val); + assert(p->hash >= 1); + p->hash--; + if (p->hash == 0 && p->header.ref_count == 0) { + /* can remove the dummy structure */ + js_free_rt(rt, p); + } + } +} + +/* val must be an object, a symbol or undefined (see + js_weakref_is_target). */ +static JSValue js_weakref_new(JSContext *ctx, JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(val); + p->weakref_count++; + } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) { + JSString *p = JS_VALUE_GET_STRING(val); + /* XXX: could return an exception if too many references */ + assert(p->hash < JS_ATOM_HASH_MASK - 2); + p->hash++; + } else { + assert(JS_IsUndefined(val)); + } + return val; +} #define MAGIC_SET (1 << 0) #define MAGIC_WEAK (1 << 1) @@ -45252,12 +48254,16 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, goto fail; init_list_head(&s->records); s->is_weak = is_weak; + if (is_weak) { + s->weakref_header.weakref_type = JS_WEAKREF_TYPE_MAP; + list_add_tail(&s->weakref_header.link, &ctx->rt->weakref_list); + } JS_SetOpaque(obj, s); - s->hash_size = 1; - s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); + s->hash_bits = 1; + s->hash_size = 1U << s->hash_bits; + s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size); if (!s->hash_table) goto fail; - init_list_head(&s->hash_table[0]); s->record_count_threshold = 4; arr = JS_UNDEFINED; @@ -45286,15 +48292,13 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(item)) goto fail; - if (done) { - JS_FreeValue(ctx, item); + if (done) break; - } if (is_set) { ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); if (JS_IsException(ret)) { JS_FreeValue(ctx, item); - goto fail; + goto fail_close; } } else { JSValue key, value; @@ -45319,7 +48323,7 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, JS_FreeValue(ctx, item); JS_FreeValue(ctx, key); JS_FreeValue(ctx, value); - goto fail; + goto fail_close; } JS_FreeValue(ctx, key); JS_FreeValue(ctx, value); @@ -45332,11 +48336,10 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, JS_FreeValue(ctx, adder); } return obj; + fail_close: + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, iter, TRUE); fail: - if (JS_IsObject(iter)) { - /* close the iterator object, preserving pending exception */ - JS_IteratorClose(ctx, iter, TRUE); - } JS_FreeValue(ctx, next_method); JS_FreeValue(ctx, iter); JS_FreeValue(ctx, adder); @@ -45355,27 +48358,56 @@ static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key) return key; } +/* hash multipliers, same as the Linux kernel (see Knuth vol 3, + section 6.4, exercise 9) */ +#define HASH_MUL32 0x61C88647 +#define HASH_MUL64 UINT64_C(0x61C8864680B583EB) + +static uint32_t map_hash32(uint32_t a, int hash_bits) +{ + return (a * HASH_MUL32) >> (32 - hash_bits); +} + +static uint32_t map_hash64(uint64_t a, int hash_bits) +{ + return (a * HASH_MUL64) >> (64 - hash_bits); +} + +static uint32_t map_hash_pointer(uintptr_t a, int hash_bits) +{ +#ifdef JS_PTR64 + return map_hash64(a, hash_bits); +#else + return map_hash32(a, hash_bits); +#endif +} + /* XXX: better hash ? */ -static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) +/* precondition: 1 <= hash_bits <= 32 */ +static uint32_t map_hash_key(JSValueConst key, int hash_bits) { uint32_t tag = JS_VALUE_GET_NORM_TAG(key); uint32_t h; double d; - JSFloat64Union u; - + JSBigInt *p; + JSBigIntBuf buf; + switch(tag) { case JS_TAG_BOOL: - h = JS_VALUE_GET_INT(key); + h = map_hash32(JS_VALUE_GET_INT(key) ^ JS_TAG_BOOL, hash_bits); break; case JS_TAG_STRING: - h = hash_string(JS_VALUE_GET_STRING(key), 0); + h = map_hash32(hash_string(JS_VALUE_GET_STRING(key), 0) ^ JS_TAG_STRING, hash_bits); + break; + case JS_TAG_STRING_ROPE: + h = map_hash32(hash_string_rope(key, 0) ^ JS_TAG_STRING, hash_bits); break; case JS_TAG_OBJECT: case JS_TAG_SYMBOL: - h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; + h = map_hash_pointer((uintptr_t)JS_VALUE_GET_PTR(key) ^ tag, hash_bits); break; case JS_TAG_INT: - d = JS_VALUE_GET_INT(key) * 3163; + d = JS_VALUE_GET_INT(key); goto hash_float64; case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(key); @@ -45383,61 +48415,77 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) if (isnan(d)) d = JS_FLOAT64_NAN; hash_float64: - u.d = d; - h = (u.u32[0] ^ u.u32[1]) * 3163; + h = map_hash64(float64_as_uint64(d) ^ JS_TAG_FLOAT64, hash_bits); + break; + case JS_TAG_SHORT_BIG_INT: + p = js_bigint_set_short(&buf, key); + goto hash_bigint; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(key); + hash_bigint: + { + int i; + h = 1; + for(i = p->len - 1; i >= 0; i--) { + h = h * 263 + p->tab[i]; + } + /* the final step is necessary otherwise h mod n only + depends of p->tab[i] mod n */ + h = map_hash32(h ^ JS_TAG_BIG_INT, hash_bits); + } break; default: - h = 0; /* XXX: bignum support */ + h = 0; break; } - h ^= tag; return h; } static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, JSValueConst key) { - struct list_head *el; JSMapRecord *mr; uint32_t h; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_for_each(el, &s->hash_table[h]) { - mr = list_entry(el, JSMapRecord, hash_link); - if (js_same_value_zero(ctx, mr->key, key)) - return mr; + h = map_hash_key(key, s->hash_bits); + for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) { + if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { + /* cannot match */ + } else { + if (js_same_value_zero(ctx, mr->key, key)) + return mr; + } } return NULL; } static void map_hash_resize(JSContext *ctx, JSMapState *s) { - uint32_t new_hash_size, i, h; - size_t slack; - struct list_head *new_hash_table, *el; - JSMapRecord *mr; + uint32_t new_hash_size, h; + int new_hash_bits; + struct list_head *el; + JSMapRecord *mr, **new_hash_table; /* XXX: no reporting of memory allocation failure */ - if (s->hash_size == 1) - new_hash_size = 4; - else - new_hash_size = s->hash_size * 2; - new_hash_table = js_realloc2(ctx, s->hash_table, - sizeof(new_hash_table[0]) * new_hash_size, &slack); + new_hash_bits = min_int(s->hash_bits + 1, 31); + new_hash_size = 1U << new_hash_bits; + new_hash_table = js_realloc(ctx, s->hash_table, + sizeof(new_hash_table[0]) * new_hash_size); if (!new_hash_table) return; - new_hash_size += slack / sizeof(*new_hash_table); - for(i = 0; i < new_hash_size; i++) - init_list_head(&new_hash_table[i]); + memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size); list_for_each(el, &s->records) { mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); - list_add_tail(&mr->hash_link, &new_hash_table[h]); + if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { + } else { + h = map_hash_key(mr->key, new_hash_bits); + mr->hash_next = new_hash_table[h]; + new_hash_table[h] = mr; } } s->hash_table = new_hash_table; + s->hash_bits = new_hash_bits; s->hash_size = new_hash_size; s->record_count_threshold = new_hash_size * 2; } @@ -45452,19 +48500,15 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, if (!mr) return NULL; mr->ref_count = 1; - mr->map = s; mr->empty = FALSE; if (s->is_weak) { - JSObject *p = JS_VALUE_GET_OBJ(key); - /* Add the weak reference */ - mr->next_weak_ref = p->first_weak_ref; - p->first_weak_ref = mr; + mr->key = js_weakref_new(ctx, key); } else { - JS_DupValue(ctx, key); + mr->key = JS_DupValue(ctx, key); } - mr->key = (JSValue)key; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_add_tail(&mr->hash_link, &s->hash_table[h]); + h = map_hash_key(key, s->hash_bits); + mr->hash_next = s->hash_table[h]; + s->hash_table[h] = mr; list_add_tail(&mr->link, &s->records); s->record_count++; if (s->record_count >= s->record_count_threshold) { @@ -45473,34 +48517,14 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, return mr; } -/* Remove the weak reference from the object weak - reference list. we don't use a doubly linked list to - save space, assuming a given object has few weak - references to it */ -static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) -{ - JSMapRecord **pmr, *mr1; - JSObject *p; - - p = JS_VALUE_GET_OBJ(mr->key); - pmr = &p->first_weak_ref; - for(;;) { - mr1 = *pmr; - assert(mr1 != NULL); - if (mr1 == mr) - break; - pmr = &mr1->next_weak_ref; - } - *pmr = mr1->next_weak_ref; -} - +/* warning: the record must be removed from the hash table before */ static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) { if (mr->empty) return; - list_del(&mr->hash_link); + if (s->is_weak) { - delete_weak_ref(rt, mr); + js_weakref_free(rt, mr->key); } else { JS_FreeValueRT(rt, mr->key); } @@ -45527,30 +48551,36 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) } } -static void reset_weak_ref(JSRuntime *rt, JSObject *p) +static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh) { - JSMapRecord *mr, *mr_next; - JSMapState *s; - - /* first pass to remove the records from the WeakMap/WeakSet - lists */ - for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { - s = mr->map; - assert(s->is_weak); - assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ - list_del(&mr->hash_link); - list_del(&mr->link); - } - - /* second pass to free the values to avoid modifying the weak - reference list while traversing it. */ - for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) { - mr_next = mr->next_weak_ref; - JS_FreeValueRT(rt, mr->value); - js_free_rt(rt, mr); - } + JSMapState *s = container_of(wh, JSMapState, weakref_header); + struct list_head *el, *el1; + JSMapRecord *mr1, **pmr; + uint32_t h; - p->first_weak_ref = NULL; /* fail safe */ + list_for_each_safe(el, el1, &s->records) { + JSMapRecord *mr = list_entry(el, JSMapRecord, link); + if (!js_weakref_is_live(mr->key)) { + + /* even if key is not live it can be hashed as a pointer */ + h = map_hash_key(mr->key, s->hash_bits); + pmr = &s->hash_table[h]; + for(;;) { + mr1 = *pmr; + /* the entry may already be removed from the hash + table if the map was resized */ + if (mr1 == NULL) + goto done; + if (mr1 == mr) + break; + pmr = &mr1->hash_next; + } + /* remove from the hash table */ + *pmr = mr1->hash_next; + done: + map_delete_record(rt, s, mr); + } + } } static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, @@ -45563,8 +48593,8 @@ static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, if (!s) return JS_EXCEPTION; key = map_normalize_key(ctx, argv[0]); - if (s->is_weak && !JS_IsObject(key)) - return JS_ThrowTypeErrorNotAnObject(ctx); + if (s->is_weak && !js_weakref_is_target(key)) + return JS_ThrowTypeError(ctx, "invalid value used as %s key", (magic & MAGIC_SET) ? "WeakSet" : "WeakMap"); if (magic & MAGIC_SET) value = JS_UNDEFINED; else @@ -45609,22 +48639,39 @@ static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; key = map_normalize_key(ctx, argv[0]); mr = map_find_record(ctx, s, key); - return JS_NewBool(ctx, (mr != NULL)); + return JS_NewBool(ctx, mr != NULL); } static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; + JSMapRecord *mr, **pmr; JSValueConst key; + uint32_t h; if (!s) return JS_EXCEPTION; key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - if (!mr) - return JS_FALSE; + + h = map_hash_key(key, s->hash_bits); + pmr = &s->hash_table[h]; + for(;;) { + mr = *pmr; + if (mr == NULL) + return JS_FALSE; + if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { + /* not valid */ + } else { + if (js_same_value_zero(ctx, mr->key, key)) + break; + } + pmr = &mr->hash_next; + } + + /* remove from the hash table */ + *pmr = mr->hash_next; + map_delete_record(ctx->rt, s, mr); return JS_TRUE; } @@ -45638,6 +48685,10 @@ static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, if (!s) return JS_EXCEPTION; + + /* remove from the hash table */ + memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size); + list_for_each_safe(el, el1, &s->records) { mr = list_entry(el, JSMapRecord, link); map_delete_record(ctx->rt, s, mr); @@ -45684,7 +48735,7 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, args[0] = args[1]; else args[0] = JS_DupValue(ctx, mr->value); - args[2] = (JSValue)this_val; + args[2] = this_val; ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); JS_FreeValue(ctx, args[0]); if (!magic) @@ -45701,6 +48752,123 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, return JS_UNDEFINED; } +static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_map) +{ + JSValueConst cb, args[2]; + JSValue res, iter, next, groups, key, v, prop; + JSAtom key_atom = JS_ATOM_NULL; + int64_t idx; + BOOL done; + + // "is function?" check must be observed before argv[0] is accessed + cb = argv[1]; + if (check_function(ctx, cb)) + return JS_EXCEPTION; + + iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); + if (JS_IsException(iter)) + return JS_EXCEPTION; + + key = JS_UNDEFINED; + key_atom = JS_ATOM_NULL; + v = JS_UNDEFINED; + prop = JS_UNDEFINED; + groups = JS_UNDEFINED; + + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + + if (is_map) { + groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0); + } else { + groups = JS_NewObjectProto(ctx, JS_NULL); + } + if (JS_IsException(groups)) + goto exception; + + for (idx = 0; ; idx++) { + if (idx >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "too many elements"); + goto iterator_close_exception; + } + v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(v)) + goto exception; + if (done) + break; // v is JS_UNDEFINED + + args[0] = v; + args[1] = JS_NewInt64(ctx, idx); + key = JS_Call(ctx, cb, ctx->global_obj, 2, args); + if (JS_IsException(key)) + goto iterator_close_exception; + + if (is_map) { + prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0); + } else { + key_atom = JS_ValueToAtom(ctx, key); + JS_FreeValue(ctx, key); + key = JS_UNDEFINED; + if (key_atom == JS_ATOM_NULL) + goto iterator_close_exception; + prop = JS_GetProperty(ctx, groups, key_atom); + } + if (JS_IsException(prop)) + goto exception; + + if (JS_IsUndefined(prop)) { + prop = JS_NewArray(ctx); + if (JS_IsException(prop)) + goto exception; + if (is_map) { + args[0] = key; + args[1] = prop; + res = js_map_set(ctx, groups, 2, args, 0); + if (JS_IsException(res)) + goto exception; + JS_FreeValue(ctx, res); + } else { + prop = JS_DupValue(ctx, prop); + if (JS_DefinePropertyValue(ctx, groups, key_atom, prop, + JS_PROP_C_W_E) < 0) { + goto exception; + } + } + } + res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0); + if (JS_IsException(res)) + goto exception; + // res is an int64 + + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, key); + JS_FreeAtom(ctx, key_atom); + JS_FreeValue(ctx, v); + prop = JS_UNDEFINED; + key = JS_UNDEFINED; + key_atom = JS_ATOM_NULL; + v = JS_UNDEFINED; + } + + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return groups; + + iterator_close_exception: + JS_IteratorClose(ctx, iter, TRUE); + exception: + JS_FreeAtom(ctx, key_atom); + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return JS_EXCEPTION; +} + static void js_map_finalizer(JSRuntime *rt, JSValue val) { JSObject *p; @@ -45717,7 +48885,7 @@ static void js_map_finalizer(JSRuntime *rt, JSValue val) mr = list_entry(el, JSMapRecord, link); if (!mr->empty) { if (s->is_weak) - delete_weak_ref(rt, mr); + js_weakref_free(rt, mr->key); else JS_FreeValueRT(rt, mr->key); JS_FreeValueRT(rt, mr->value); @@ -45725,6 +48893,9 @@ static void js_map_finalizer(JSRuntime *rt, JSValue val) js_free_rt(rt, mr); } js_free_rt(rt, s->hash_table); + if (s->is_weak) { + list_del(&s->weakref_header.link); + } js_free_rt(rt, s); } } @@ -45881,6 +49052,7 @@ static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, } static const JSCFunctionListEntry js_map_funcs[] = { + JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), }; @@ -46001,12 +49173,6 @@ static const JSCFunctionListEntry js_generator_proto_funcs[] = { /* Promise */ -typedef enum JSPromiseStateEnum { - JS_PROMISE_PENDING, - JS_PROMISE_FULFILLED, - JS_PROMISE_REJECTED, -} JSPromiseStateEnum; - typedef struct JSPromiseData { JSPromiseStateEnum promise_state; /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ @@ -46032,6 +49198,22 @@ typedef struct JSPromiseReactionData { JSValue handler; } JSPromiseReactionData; +JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + if (!s) + return -1; + return s->promise_state; +} + +JSValue JS_PromiseResult(JSContext *ctx, JSValue promise) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + if (!s) + return JS_UNDEFINED; + return JS_DupValue(ctx, s->promise_result); +} + static int js_create_resolving_functions(JSContext *ctx, JSValue *args, JSValueConst promise); @@ -46267,8 +49449,8 @@ static JSValue js_promise_resolve_function_call(JSContext *ctx, else resolution = JS_UNDEFINED; #ifdef DUMP_PROMISE - printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); - JS_DumpValue(ctx, resolution); + printf("js_promise_resolving_function_call: is_reject=%d ", is_reject); + JS_DumpValue(ctx, "resolution", resolution); printf("\n"); #endif if (is_reject || !JS_IsObject(resolution)) { @@ -46494,17 +49676,14 @@ static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, return result_promise; } -#if 0 -static JSValue js_promise___newPromiseCapability(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_promise_withResolvers(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue result_promise, resolving_funcs[2], obj; - JSValueConst ctor; - ctor = argv[0]; - if (!JS_IsObject(ctor)) + if (!JS_IsObject(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); if (JS_IsException(result_promise)) return result_promise; obj = JS_NewObject(ctx); @@ -46519,7 +49698,34 @@ static JSValue js_promise___newPromiseCapability(JSContext *ctx, JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); return obj; } -#endif + +static JSValue js_promise_try(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue result_promise, resolving_funcs[2], ret, ret2; + BOOL is_reject = 0; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + ret = JS_Call(ctx, argv[0], JS_UNDEFINED, argc - 1, argv + 1); + if (JS_IsException(ret)) { + is_reject = 1; + ret = JS_GetException(ctx); + } + ret2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, (JSValueConst *)&ret); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, ret); + if (JS_IsException(ret2)) { + JS_FreeValue(ctx, result_promise); + return ret2; + } + JS_FreeValue(ctx, ret2); + return result_promise; +} static __exception int remainingElementsCount_add(JSContext *ctx, JSValueConst resolve_element_env, @@ -46558,7 +49764,7 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, JSValueConst resolve_element_env = func_data[4]; JSValue ret, obj; int is_zero, index; - + if (JS_ToInt32(ctx, &index, func_data[1])) return JS_EXCEPTION; if (alreadyCalled) @@ -46567,11 +49773,11 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, if (resolve_type == PROMISE_MAGIC_allSettled) { JSValue str; - + obj = JS_NewObject(ctx); if (JS_IsException(obj)) return JS_EXCEPTION; - str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled"); + str = js_new_string8(ctx, is_reject ? "rejected" : "fulfilled"); if (JS_IsException(str)) goto fail1; if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, @@ -46592,7 +49798,7 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, if (JS_DefinePropertyValueUint32(ctx, values, index, obj, JS_PROP_C_W_E) < 0) return JS_EXCEPTION; - + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); if (is_zero < 0) return JS_EXCEPTION; @@ -46625,7 +49831,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, JSValueConst then_args[2], resolve_element_data[5]; BOOL done; int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); - + if (!JS_IsObject(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); @@ -46661,7 +49867,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, JS_NewInt32(ctx, 1), JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) goto fail_reject; - + index = 0; for(;;) { /* XXX: conformance: should close the iterator if error on 'done' @@ -46671,7 +49877,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, goto fail_reject; if (done) break; - next_promise = JS_Call(ctx, promise_resolve, + next_promise = JS_Call(ctx, promise_resolve, this_val, 1, (JSValueConst *)&item); JS_FreeValue(ctx, item); if (JS_IsException(next_promise)) { @@ -46680,7 +49886,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, goto fail_reject; } resolve_element_data[0] = JS_NewBool(ctx, FALSE); - resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); + resolve_element_data[1] = JS_NewInt32(ctx, index); resolve_element_data[2] = values; resolve_element_data[3] = resolving_funcs[is_promise_any]; resolve_element_data[4] = resolve_element_env; @@ -46691,7 +49897,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, next_promise); goto fail_reject1; } - + if (magic == PROMISE_MAGIC_allSettled) { reject_element = JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, @@ -47008,8 +50214,9 @@ static const JSCFunctionListEntry js_promise_funcs[] = { JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), + JS_CFUNC_DEF("try", 1, js_promise_try ), JS_CFUNC_DEF("race", 1, js_promise_race ), - //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), + JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), }; @@ -47025,25 +50232,6 @@ static const JSCFunctionListEntry js_async_function_proto_funcs[] = { JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), }; -static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), - JS_ToBool(ctx, func_data[0])); -} - -static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, - BOOL done) -{ - JSValueConst func_data[1]; - - func_data[0] = (JSValueConst)JS_NewBool(ctx, done); - return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, - 1, 0, 1, func_data); -} - /* AsyncIteratorPrototype */ static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { @@ -47105,6 +50293,41 @@ static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, return async_iter; } +static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), + JS_ToBool(ctx, func_data[0])); +} + +static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, + BOOL done) +{ + JSValueConst func_data[1]; + + func_data[0] = JS_NewBool(ctx, done); + return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, + 1, 0, 1, func_data); +} + +static JSValue js_async_from_sync_iterator_close_wrap(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + JS_Throw(ctx, JS_DupValue(ctx, argv[0])); + JS_IteratorClose(ctx, func_data[0], TRUE); + return JS_EXCEPTION; +} + +static JSValue js_async_from_sync_iterator_close_wrap_func_create(JSContext *ctx, JSValueConst sync_iter) +{ + return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_close_wrap, + 1, 0, 1, &sync_iter); +} + static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) @@ -47135,11 +50358,13 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi if (magic == GEN_MAGIC_RETURN) { err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); is_reject = 0; + goto done_resolve; } else { - err = JS_DupValue(ctx, argv[0]); - is_reject = 1; + if (JS_IteratorClose(ctx, s->sync_iter, FALSE)) + goto reject; + JS_ThrowTypeError(ctx, "throw is not a method"); + goto reject; } - goto done_resolve; } } value = JS_IteratorNext2(ctx, s->sync_iter, method, @@ -47154,21 +50379,9 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi if (JS_IsException(value)) goto reject; } - - if (JS_IsException(value)) { - JSValue res2; - reject: - err = JS_GetException(ctx); - is_reject = 1; - done_resolve: - res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, - 1, (JSValueConst *)&err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - } + + if (JS_IsException(value)) + goto reject; { JSValue value_wrapper_promise, resolve_reject[2]; int res; @@ -47176,8 +50389,22 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&value, 0); if (JS_IsException(value_wrapper_promise)) { + JSValue res2; JS_FreeValue(ctx, value); - goto reject; + if (magic != GEN_MAGIC_RETURN && !done) { + JS_IteratorClose(ctx, s->sync_iter, TRUE); + } + reject: + err = JS_GetException(ctx); + is_reject = 1; + done_resolve: + res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; } resolve_reject[0] = @@ -47186,13 +50413,23 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi JS_FreeValue(ctx, value_wrapper_promise); goto fail; } + if (done || magic == GEN_MAGIC_RETURN) { + resolve_reject[1] = JS_UNDEFINED; + } else { + resolve_reject[1] = + js_async_from_sync_iterator_close_wrap_func_create(ctx, s->sync_iter); + if (JS_IsException(resolve_reject[1])) { + JS_FreeValue(ctx, value_wrapper_promise); + JS_FreeValue(ctx, resolve_reject[0]); + goto fail; + } + } JS_FreeValue(ctx, value); - resolve_reject[1] = JS_UNDEFINED; - res = perform_promise_then(ctx, value_wrapper_promise, (JSValueConst *)resolve_reject, (JSValueConst *)resolving_funcs); JS_FreeValue(ctx, resolve_reject[0]); + JS_FreeValue(ctx, resolve_reject[1]); JS_FreeValue(ctx, value_wrapper_promise); JS_FreeValue(ctx, resolving_funcs[0]); JS_FreeValue(ctx, resolving_funcs[1]); @@ -47422,8 +50659,7 @@ static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, } c = (c << 6) | (c1 & 0x3f); } - if (c < c_min || c > 0x10FFFF || - (c >= 0xd800 && c < 0xe000)) { + if (c < c_min || c > 0x10FFFF || is_surrogate(c)) { js_throw_URIError(ctx, "malformed UTF-8"); goto fail; } @@ -47498,21 +50734,21 @@ static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, if (isURIUnescaped(c, isComponent)) { string_buffer_putc16(b, c); } else { - if (c >= 0xdc00 && c <= 0xdfff) { + if (is_lo_surrogate(c)) { js_throw_URIError(ctx, "invalid character"); goto fail; - } else if (c >= 0xd800 && c <= 0xdbff) { + } else if (is_hi_surrogate(c)) { if (k >= p->len) { js_throw_URIError(ctx, "expecting surrogate pair"); goto fail; } c1 = string_get(p, k); k++; - if (c1 < 0xdc00 || c1 > 0xdfff) { + if (!is_lo_surrogate(c1)) { js_throw_URIError(ctx, "expecting surrogate pair"); goto fail; } - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + c = from_surrogate(c, c1); } if (c < 0x80) { encodeURI_hex(b, c); @@ -47617,15 +50853,10 @@ static const JSCFunctionListEntry js_global_funcs[] = { JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ), JS_CFUNC_DEF("escape", 1, js_global_escape ), JS_CFUNC_DEF("unescape", 1, js_global_unescape ), - JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), - JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_DOUBLE_DEF("Infinity", (1.0 / 0.0), 0 ), + JS_PROP_DOUBLE_DEF("NaN", (0.0 / 0.0), 0 ), JS_PROP_UNDEFINED_DEF("undefined", 0 ), - - /* for the 'Date' implementation */ - JS_CFUNC_DEF("__date_clock", 0, js___date_clock ), - //JS_CFUNC_DEF("__date_now", 0, js___date_now ), - //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ), - //JS_CFUNC_DEF("__date_create", 3, js___date_create ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ), }; /* Date */ @@ -47706,7 +50937,7 @@ static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char const day_names[] = "SunMonTueWedThuFriSat"; static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, - double fields[9], int is_local, int force) + double fields[minimum_length(9)], int is_local, int force) { double dval; int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; @@ -47719,7 +50950,7 @@ static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, return FALSE; /* NaN */ d = 0; /* initialize all fields to 0 */ } else { - d = dval; + d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */ if (is_local) { tz = -getTimezoneOffset(d); d += tz * 60000; @@ -47765,33 +50996,63 @@ static double time_clip(double t) { return NAN; } -/* The spec mandates the use of 'double' and it fixes the order +/* The spec mandates the use of 'double' and it specifies the order of the operations */ -static double set_date_fields(double fields[], int is_local) { - int64_t y; - double days, d, h, m1; - int i, m, md; - - m1 = fields[1]; - m = fmod(m1, 12); - if (m < 0) - m += 12; - y = (int64_t)(fields[0] + floor(m1 / 12)); - days = days_from_year(y); - - for(i = 0; i < m; i++) { - md = month_days[i]; +static double set_date_fields(double fields[minimum_length(7)], int is_local) { + double y, m, dt, ym, mn, day, h, s, milli, time, tv; + int yi, mi, i; + int64_t days; + volatile double temp; /* enforce evaluation order */ + + /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ + y = fields[0]; + m = fields[1]; + dt = fields[2]; + ym = y + floor(m / 12); + mn = fmod(m, 12); + if (mn < 0) + mn += 12; + if (ym < -271821 || ym > 275760) + return NAN; + + yi = ym; + mi = mn; + days = days_from_year(yi); + for(i = 0; i < mi; i++) { + days += month_days[i]; if (i == 1) - md += days_in_year(y) - 365; - days += md; + days += days_in_year(yi) - 365; + } + day = days + dt - 1; + + /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */ + h = fields[3]; + m = fields[4]; + s = fields[5]; + milli = fields[6]; + /* Use a volatile intermediary variable to ensure order of evaluation + * as specified in ECMA. This fixes a test262 error on + * test262/test/built-ins/Date/UTC/fp-evaluation-order.js. + * Without the volatile qualifier, the compile can generate code + * that performs the computation in a different order or with instructions + * that produce a different result such as FMA (float multiply and add). + */ + time = h * 3600000; + time += (temp = m * 60000); + time += (temp = s * 1000); + time += milli; + + /* emulate 21.4.1.16 MakeDate ( day, time ) */ + tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ + if (!isfinite(tv)) + return NAN; + + /* adjust for local time and clip */ + if (is_local) { + int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; + tv += getTimezoneOffset(ti) * 60000; } - days += fields[2] - 1; - h = fields[3] * 3600000 + fields[4] * 60000 + - fields[5] * 1000 + fields[6]; - d = days * 86400000 + h; - if (is_local) - d += getTimezoneOffset(d) * 60000; - return time_clip(d); + return time_clip(tv); } static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, @@ -47820,7 +51081,7 @@ static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, { // _field(obj, first_field, end_field, args, is_local) double fields[9]; - int res, first_field, end_field, is_local, i, n; + int res, first_field, end_field, is_local, i, n, res1; double d, a; d = NAN; @@ -47831,7 +51092,8 @@ static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); if (res < 0) return JS_EXCEPTION; - + res1 = res; + // Argument coercion is observable and must be done unconditionally. n = min_int(argc, end_field - first_field); for(i = 0; i < n; i++) { @@ -47841,9 +51103,13 @@ static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, res = FALSE; fields[first_field + i] = trunc(a); } - if (res && argc > 0) { + + if (!res1) + return JS_NAN; /* thisTimeValue is NaN */ + + if (res && argc > 0) d = set_date_fields(fields, is_local); - } + return JS_SetThisTimeValue(ctx, this_val, d); } @@ -47874,7 +51140,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, if (fmt == 2) return JS_ThrowRangeError(ctx, "Date value is NaN"); else - return JS_NewString(ctx, "Invalid Date"); + return js_new_string8(ctx, "Invalid Date"); } y = fields[0]; @@ -47953,7 +51219,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, break; case 3: pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s, + "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, (h < 12) ? 'A' : 'P'); break; } @@ -48029,2484 +51295,813 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, args[1] = ctx->class_proto[JS_CLASS_DATE]; args[2] = JS_NewFloat64(ctx, val); rv = js___date_create(ctx, JS_UNDEFINED, 3, args); -#else - rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); - if (!JS_IsException(rv)) - JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); -#endif - if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { - /* invoked as a function, return (new Date()).toString(); */ - JSValue s; - s = get_date_string(ctx, rv, 0, NULL, 0x13); - JS_FreeValue(ctx, rv); - rv = s; - } - return rv; -} - -static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // UTC(y, mon, d, h, m, s, ms) - double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; - int i, n; - double a; - - n = argc; - if (n == 0) - return JS_NAN; - if (n > 7) - n = 7; - for(i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - return JS_NAN; - fields[i] = trunc(a); - if (i == 0 && fields[0] >= 0 && fields[0] < 100) - fields[0] += 1900; - } - return JS_NewFloat64(ctx, set_date_fields(fields, 0)); -} - -static void string_skip_spaces(JSString *sp, int *pp) { - while (*pp < sp->len && string_get(sp, *pp) == ' ') - *pp += 1; -} - -static void string_skip_non_spaces(JSString *sp, int *pp) { - while (*pp < sp->len && string_get(sp, *pp) != ' ') - *pp += 1; -} - -/* parse a numeric field with an optional sign if accept_sign is TRUE */ -static int string_get_digits(JSString *sp, int *pp, int64_t *pval) { - int64_t v = 0; - int c, p = *pp, p_start; - - if (p >= sp->len) - return -1; - p_start = p; - while (p < sp->len) { - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) { - if (p == p_start) - return -1; - else - break; - } - v = v * 10 + c - '0'; - p++; - } - *pval = v; - *pp = p; - return 0; -} - -static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) { - int res, sgn, p = *pp; - - if (p >= sp->len) - return -1; - - sgn = string_get(sp, p); - if (sgn == '-' || sgn == '+') - p++; - - res = string_get_digits(sp, &p, pval); - if (res == 0 && sgn == '-') { - if (*pval == 0) - return -1; // reject negative zero - *pval = -*pval; - } - *pp = p; - return res; -} - -/* parse a fixed width numeric field */ -static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) { - int64_t v = 0; - int i, c, p = *pp; - - for(i = 0; i < n; i++) { - if (p >= sp->len) - return -1; - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) - return -1; - v = v * 10 + c - '0'; - p++; - } - *pval = v; - *pp = p; - return 0; -} - -static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) { - /* parse milliseconds as a fractional part, round to nearest */ - /* XXX: the spec does not indicate which rounding should be used */ - int mul = 1000, ms = 0, p = *pp, c, p_start; - if (p >= sp->len) - return -1; - p_start = p; - while (p < sp->len) { - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) { - if (p == p_start) - return -1; - else - break; - } - if (mul == 1 && c >= '5') - ms += 1; - ms += (c - '0') * (mul /= 10); - p++; - } - *pval = ms; - *pp = p; - return 0; -} - - -static int find_abbrev(JSString *sp, int p, const char *list, int count) { - int n, i; - - if (p + 3 <= sp->len) { - for (n = 0; n < count; n++) { - for (i = 0; i < 3; i++) { - if (string_get(sp, p + i) != month_names[n * 3 + i]) - goto next; - } - return n; - next:; - } - } - return -1; -} - -static int string_get_month(JSString *sp, int *pp, int64_t *pval) { - int n; - - string_skip_spaces(sp, pp); - n = find_abbrev(sp, *pp, month_names, 12); - if (n < 0) - return -1; - - *pval = n; - *pp += 3; - return 0; -} - -static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // parse(s) - JSValue s, rv; - int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; - double fields1[7]; - int64_t tz, hh, mm; - double d; - int p, i, c, sgn, l; - JSString *sp; - BOOL is_local; - - rv = JS_NAN; - - s = JS_ToString(ctx, argv[0]); - if (JS_IsException(s)) - return JS_EXCEPTION; - - sp = JS_VALUE_GET_STRING(s); - p = 0; - if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { - /* ISO format */ - /* year field can be negative */ - if (string_get_signed_digits(sp, &p, &fields[0])) - goto done; - - for (i = 1; i < 7; i++) { - if (p >= sp->len) - break; - switch(i) { - case 1: - case 2: - c = '-'; - break; - case 3: - c = 'T'; - break; - case 4: - case 5: - c = ':'; - break; - case 6: - c = '.'; - break; - } - if (string_get(sp, p) != c) - break; - p++; - if (i == 6) { - if (string_get_milliseconds(sp, &p, &fields[i])) - goto done; - } else { - if (string_get_digits(sp, &p, &fields[i])) - goto done; - } - } - /* no time: UTC by default */ - is_local = (i > 3); - fields[1] -= 1; - - /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ - tz = 0; - if (p < sp->len) { - sgn = string_get(sp, p); - if (sgn == '+' || sgn == '-') { - p++; - l = sp->len - p; - if (l != 4 && l != 5) - goto done; - if (string_get_fixed_width_digits(sp, &p, 2, &hh)) - goto done; - if (l == 5) { - if (string_get(sp, p) != ':') - goto done; - p++; - } - if (string_get_fixed_width_digits(sp, &p, 2, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; - is_local = FALSE; - } else if (sgn == 'Z') { - p++; - is_local = FALSE; - } else { - goto done; - } - /* error if extraneous characters */ - if (p != sp->len) - goto done; - } - } else { - /* toString or toUTCString format */ - /* skip the day of the week */ - string_skip_non_spaces(sp, &p); - string_skip_spaces(sp, &p); - if (p >= sp->len) - goto done; - c = string_get(sp, p); - if (c >= '0' && c <= '9') { - /* day of month first */ - if (string_get_digits(sp, &p, &fields[2])) - goto done; - if (string_get_month(sp, &p, &fields[1])) - goto done; - } else { - /* month first */ - if (string_get_month(sp, &p, &fields[1])) - goto done; - string_skip_spaces(sp, &p); - if (string_get_digits(sp, &p, &fields[2])) - goto done; - } - /* year */ - string_skip_spaces(sp, &p); - if (string_get_signed_digits(sp, &p, &fields[0])) - goto done; - - /* hour, min, seconds */ - string_skip_spaces(sp, &p); - for(i = 0; i < 3; i++) { - if (i == 1 || i == 2) { - if (p >= sp->len) - goto done; - if (string_get(sp, p) != ':') - goto done; - p++; - } - if (string_get_digits(sp, &p, &fields[3 + i])) - goto done; - } - // XXX: parse optional milliseconds? - - /* parse the time zone offset if present: [+-]HHmm */ - is_local = FALSE; - tz = 0; - for (tz = 0; p < sp->len; p++) { - sgn = string_get(sp, p); - if (sgn == '+' || sgn == '-') { - p++; - if (string_get_fixed_width_digits(sp, &p, 2, &hh)) - goto done; - if (string_get_fixed_width_digits(sp, &p, 2, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; - break; - } - } - } - for(i = 0; i < 7; i++) - fields1[i] = fields[i]; - d = set_date_fields(fields1, is_local) - tz * 60000; - rv = JS_NewFloat64(ctx, d); - -done: - JS_FreeValue(ctx, s); - return rv; -} - -static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // now() - return JS_NewInt64(ctx, date_now()); -} - -static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // Symbol_toPrimitive(hint) - JSValueConst obj = this_val; - JSAtom hint = JS_ATOM_NULL; - int hint_num; - - if (!JS_IsObject(obj)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - if (JS_IsString(argv[0])) { - hint = JS_ValueToAtom(ctx, argv[0]); - if (hint == JS_ATOM_NULL) - return JS_EXCEPTION; - JS_FreeAtom(ctx, hint); - } - switch (hint) { - case JS_ATOM_number: -#ifdef CONFIG_BIGNUM - case JS_ATOM_integer: -#endif - hint_num = HINT_NUMBER; - break; - case JS_ATOM_string: - case JS_ATOM_default: - hint_num = HINT_STRING; - break; - default: - return JS_ThrowTypeError(ctx, "invalid hint"); - } - return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); -} - -static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // getTimezoneOffset() - double v; - - if (JS_ThisTimeValue(ctx, &v, this_val)) - return JS_EXCEPTION; - if (isnan(v)) - return JS_NAN; - else - return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); -} - -static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // getTime() - double v; - - if (JS_ThisTimeValue(ctx, &v, this_val)) - return JS_EXCEPTION; - return JS_NewFloat64(ctx, v); -} - -static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // setTime(v) - double v; - - if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) - return JS_EXCEPTION; - return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); -} - -static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // setYear(y) - double y; - JSValueConst args[1]; - - if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) - return JS_EXCEPTION; - y = +y; - if (isfinite(y)) { - y = trunc(y); - if (y >= 0 && y < 100) - y += 1900; - } - args[0] = JS_NewFloat64(ctx, y); - return set_date_field(ctx, this_val, 1, args, 0x011); -} - -static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // toJSON(key) - JSValue obj, tv, method, rv; - double d; - - rv = JS_EXCEPTION; - tv = JS_UNDEFINED; - - obj = JS_ToObject(ctx, this_val); - tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); - if (JS_IsException(tv)) - goto exception; - if (JS_IsNumber(tv)) { - if (JS_ToFloat64(ctx, &d, tv) < 0) - goto exception; - if (!isfinite(d)) { - rv = JS_NULL; - goto done; - } - } - method = JS_GetPropertyStr(ctx, obj, "toISOString"); - if (JS_IsException(method)) - goto exception; - if (!JS_IsFunction(ctx, method)) { - JS_ThrowTypeError(ctx, "object needs toISOString method"); - JS_FreeValue(ctx, method); - goto exception; - } - rv = JS_CallFree(ctx, method, obj, 0, NULL); -exception: -done: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, tv); - return rv; -} - -static const JSCFunctionListEntry js_date_funcs[] = { - JS_CFUNC_DEF("now", 0, js_Date_now ), - JS_CFUNC_DEF("parse", 1, js_Date_parse ), - JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), -}; - -static const JSCFunctionListEntry js_date_proto_funcs[] = { - JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), - JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), - JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), - JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), - JS_ALIAS_DEF("toGMTString", "toUTCString" ), - JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), - JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), - JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), - JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), - JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), - JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), - JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), - JS_CFUNC_DEF("getTime", 0, js_date_getTime ), - JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), - JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), - JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), - JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), - JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), - JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), - JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), - JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), - JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), - JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), - JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), - JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), - JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), - JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), - JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), - JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), - JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), - JS_CFUNC_DEF("setTime", 1, js_date_setTime ), - JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), - JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), - JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), - JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), - JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), - JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), - JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), - JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), - JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), - JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), - JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), - JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), - JS_CFUNC_DEF("setYear", 1, js_date_setYear ), - JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), - JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), - JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), -}; - -void JS_AddIntrinsicDate(JSContext *ctx) -{ - JSValueConst obj; - - /* Date */ - ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, - countof(js_date_proto_funcs)); - obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, - ctx->class_proto[JS_CLASS_DATE]); - JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); -} - -/* eval */ - -void JS_AddIntrinsicEval(JSContext *ctx) -{ - ctx->eval_internal = __JS_EvalInternal; -} - -#ifdef CONFIG_BIGNUM - -/* Operators */ - -static void js_operator_set_finalizer(JSRuntime *rt, JSValue val) -{ - JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); - int i, j; - JSBinaryOperatorDefEntry *ent; - - if (opset) { - for(i = 0; i < JS_OVOP_COUNT; i++) { - if (opset->self_ops[i]) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i])); - } - for(j = 0; j < opset->left.count; j++) { - ent = &opset->left.tab[j]; - for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { - if (ent->ops[i]) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); - } - } - js_free_rt(rt, opset->left.tab); - for(j = 0; j < opset->right.count; j++) { - ent = &opset->right.tab[j]; - for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { - if (ent->ops[i]) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); - } - } - js_free_rt(rt, opset->right.tab); - js_free_rt(rt, opset); - } -} - -static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); - int i, j; - JSBinaryOperatorDefEntry *ent; - - if (opset) { - for(i = 0; i < JS_OVOP_COUNT; i++) { - if (opset->self_ops[i]) - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]), - mark_func); - } - for(j = 0; j < opset->left.count; j++) { - ent = &opset->left.tab[j]; - for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { - if (ent->ops[i]) - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), - mark_func); - } - } - for(j = 0; j < opset->right.count; j++) { - ent = &opset->right.tab[j]; - for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { - if (ent->ops[i]) - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), - mark_func); - } - } - } -} - - -/* create an OperatorSet object */ -static JSValue js_operators_create_internal(JSContext *ctx, - int argc, JSValueConst *argv, - BOOL is_primitive) -{ - JSValue opset_obj, prop, obj; - JSOperatorSetData *opset, *opset1; - JSBinaryOperatorDef *def; - JSValueConst arg; - int i, j; - JSBinaryOperatorDefEntry *new_tab; - JSBinaryOperatorDefEntry *ent; - uint32_t op_count; - - if (ctx->rt->operator_count == UINT32_MAX) { - return JS_ThrowTypeError(ctx, "too many operators"); - } - opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET); - if (JS_IsException(opset_obj)) - goto fail; - opset = js_mallocz(ctx, sizeof(*opset)); - if (!opset) - goto fail; - JS_SetOpaque(opset_obj, opset); - if (argc >= 1) { - arg = argv[0]; - /* self operators */ - for(i = 0; i < JS_OVOP_COUNT; i++) { - prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]); - if (JS_IsException(prop)) - goto fail; - if (!JS_IsUndefined(prop)) { - if (check_function(ctx, prop)) { - JS_FreeValue(ctx, prop); - goto fail; - } - opset->self_ops[i] = JS_VALUE_GET_OBJ(prop); - } - } - } - /* left & right operators */ - for(j = 1; j < argc; j++) { - arg = argv[j]; - prop = JS_GetPropertyStr(ctx, arg, "left"); - if (JS_IsException(prop)) - goto fail; - def = &opset->right; - if (JS_IsUndefined(prop)) { - prop = JS_GetPropertyStr(ctx, arg, "right"); - if (JS_IsException(prop)) - goto fail; - if (JS_IsUndefined(prop)) { - JS_ThrowTypeError(ctx, "left or right property must be present"); - goto fail; - } - def = &opset->left; - } - /* get the operator set */ - obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype); - JS_FreeValue(ctx, prop); - if (JS_IsException(obj)) - goto fail; - prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); - JS_FreeValue(ctx, obj); - if (JS_IsException(prop)) - goto fail; - opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET); - if (!opset1) { - JS_FreeValue(ctx, prop); - goto fail; - } - op_count = opset1->operator_counter; - JS_FreeValue(ctx, prop); - - /* we assume there are few entries */ - new_tab = js_realloc(ctx, def->tab, - (def->count + 1) * sizeof(def->tab[0])); - if (!new_tab) - goto fail; - def->tab = new_tab; - def->count++; - ent = def->tab + def->count - 1; - memset(ent, 0, sizeof(def->tab[0])); - ent->operator_index = op_count; - - for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { - prop = JS_GetPropertyStr(ctx, arg, - js_overloadable_operator_names[i]); - if (JS_IsException(prop)) - goto fail; - if (!JS_IsUndefined(prop)) { - if (check_function(ctx, prop)) { - JS_FreeValue(ctx, prop); - goto fail; - } - ent->ops[i] = JS_VALUE_GET_OBJ(prop); - } - } - } - opset->is_primitive = is_primitive; - opset->operator_counter = ctx->rt->operator_count++; - return opset_obj; - fail: - JS_FreeValue(ctx, opset_obj); - return JS_EXCEPTION; -} - -static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_operators_create_internal(ctx, argc, argv, FALSE); -} - -static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue opset_obj, prop; - JSOperatorSetData *opset; - const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW }; - JSOverloadableOperatorEnum op; - int i; - - opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT], - JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset_obj)) - goto fail; - opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET); - if (!opset) - goto fail; - for(i = 0; i < countof(ops); i++) { - op = ops[i]; - prop = JS_GetPropertyStr(ctx, argv[0], - js_overloadable_operator_names[op]); - if (JS_IsException(prop)) - goto fail; - if (!JS_IsUndefined(prop)) { - if (!JS_IsNull(prop) && check_function(ctx, prop)) { - JS_FreeValue(ctx, prop); - goto fail; - } - if (opset->self_ops[op]) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op])); - if (JS_IsNull(prop)) { - opset->self_ops[op] = NULL; - } else { - opset->self_ops[op] = JS_VALUE_GET_PTR(prop); - } - } - } - JS_FreeValue(ctx, opset_obj); - return JS_UNDEFINED; - fail: - JS_FreeValue(ctx, opset_obj); - return JS_EXCEPTION; -} - -static int js_operators_set_default(JSContext *ctx, JSValueConst obj) -{ - JSValue opset_obj; - - if (!JS_IsObject(obj)) /* in case the prototype is not defined */ - return 0; - opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE); - if (JS_IsException(opset_obj)) - return -1; - /* cannot be modified by the user */ - JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet, - opset_obj, 0); - return 0; -} - -static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); -} - -static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue func_obj, proto, opset_obj; - - func_obj = JS_UNDEFINED; - proto = JS_NewObject(ctx); - if (JS_IsException(proto)) - return JS_EXCEPTION; - opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE); - if (JS_IsException(opset_obj)) - goto fail; - JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet, - opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators", - 0, JS_CFUNC_constructor, 0); - if (JS_IsException(func_obj)) - goto fail; - JS_SetConstructor2(ctx, func_obj, proto, - 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, proto); - return func_obj; - fail: - JS_FreeValue(ctx, proto); - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_operators_funcs[] = { - JS_CFUNC_DEF("create", 1, js_operators_create ), - JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ), -}; - -/* must be called after all overloadable base types are initialized */ -void JS_AddIntrinsicOperators(JSContext *ctx) -{ - JSValue obj; - - ctx->allow_operator_overloading = TRUE; - obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1); - JS_SetPropertyFunctionList(ctx, obj, - js_operators_funcs, - countof(js_operators_funcs)); - JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators, - obj, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - /* add default operatorSets */ - js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]); - js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]); - js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]); - js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]); - js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]); - js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]); -} -#endif /* CONFIG_BIGNUM */ - -/* BigInt */ - -static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) -{ - uint32_t tag; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_BIG_INT: - break; - case JS_TAG_FLOAT64: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - { - bf_t *a, a_s; - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (!bf_is_finite(a)) { - JS_FreeValue(ctx, val); - val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint"); - } else { - JSValue val1 = JS_NewBigInt(ctx); - bf_t *r; - int ret; - if (JS_IsException(val1)) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - r = JS_GetBigInt(val1); - ret = bf_set(r, a); - ret |= bf_rint(r, BF_RNDZ); - JS_FreeValue(ctx, val); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val1); - val = JS_ThrowOutOfMemory(ctx); - } else if (ret & BF_ST_INEXACT) { - JS_FreeValue(ctx, val1); - val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); - } else { - val = JS_CompactBigInt(ctx, val1); - } - } - if (a == &a_s) - bf_delete(a); - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - break; - goto redo; -#endif - case JS_TAG_STRING: - val = JS_StringToBigIntErr(ctx, val); - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigint"); - } - return val; -} - -static JSValue js_bigint_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); -} - -static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigInt(ctx, this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_INT) { - if (JS_IsBigInt(ctx, p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigint"); -} - -static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int base; - JSValue ret; - - val = js_thisBigIntValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (argc == 0 || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; - } - ret = js_bigint_to_string1(ctx, val, base); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigIntValue(ctx, this_val); -} - -#ifdef CONFIG_BIGNUM -static JSValue js_bigint_div(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, b_s, *a, *b, *r, *q; - int status; - JSValue q_val, r_val; - - q_val = JS_NewBigInt(ctx); - if (JS_IsException(q_val)) - return JS_EXCEPTION; - r_val = JS_NewBigInt(ctx); - if (JS_IsException(r_val)) - goto fail; - b = NULL; - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - goto fail; - b = JS_ToBigInt(ctx, &b_s, argv[1]); - if (!b) { - JS_FreeBigInt(ctx, a, &a_s); - goto fail; - } - q = JS_GetBigInt(q_val); - r = JS_GetBigInt(r_val); - status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - if (unlikely(status)) { - throw_bf_exception(ctx, status); - goto fail; - } - q_val = JS_CompactBigInt(ctx, q_val); - if (magic & 0x10) { - JSValue ret; - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto fail; - JS_SetPropertyUint32(ctx, ret, 0, q_val); - JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); - return ret; - } else { - JS_FreeValue(ctx, r_val); - return q_val; +#else + rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); + if (!JS_IsException(rv)) + JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); +#endif + if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { + /* invoked as a function, return (new Date()).toString(); */ + JSValue s; + s = get_date_string(ctx, rv, 0, NULL, 0x13); + JS_FreeValue(ctx, rv); + rv = s; } - fail: - JS_FreeValue(ctx, q_val); - JS_FreeValue(ctx, r_val); - return JS_EXCEPTION; + return rv; } -static JSValue js_bigint_sqrt(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int magic) +static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - bf_t a_s, *a, *r, *rem; - int status; - JSValue r_val, rem_val; - - r_val = JS_NewBigInt(ctx); - if (JS_IsException(r_val)) - return JS_EXCEPTION; - rem_val = JS_NewBigInt(ctx); - if (JS_IsException(rem_val)) - return JS_EXCEPTION; - r = JS_GetBigInt(r_val); - rem = JS_GetBigInt(rem_val); + // UTC(y, mon, d, h, m, s, ms) + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + int i, n; + double a; - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - goto fail; - status = bf_sqrtrem(r, rem, a); - JS_FreeBigInt(ctx, a, &a_s); - if (unlikely(status & ~BF_ST_INEXACT)) { - throw_bf_exception(ctx, status); - goto fail; - } - r_val = JS_CompactBigInt(ctx, r_val); - if (magic) { - JSValue ret; - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto fail; - JS_SetPropertyUint32(ctx, ret, 0, r_val); - JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); - return ret; - } else { - JS_FreeValue(ctx, rem_val); - return r_val; + n = argc; + if (n == 0) + return JS_NAN; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + return JS_NAN; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; } - fail: - JS_FreeValue(ctx, r_val); - JS_FreeValue(ctx, rem_val); - return JS_EXCEPTION; + return JS_NewFloat64(ctx, set_date_fields(fields, 0)); } -static JSValue js_bigint_op1(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - bf_t a_s, *a; - int64_t res; +/* Date string parsing */ - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - return JS_EXCEPTION; - switch(magic) { - case 0: /* floorLog2 */ - if (a->sign || a->expn <= 0) { - res = -1; - } else { - res = a->expn - 1; - } - break; - case 1: /* ctz */ - if (bf_is_zero(a)) { - res = -1; - } else { - res = bf_get_exp_min(a); - } - break; - default: - abort(); +static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) { + if (sp[*pp] == c) { + *pp += 1; + return TRUE; + } else { + return FALSE; } - JS_FreeBigInt(ctx, a, &a_s); - return JS_NewBigInt64(ctx, res); } -#endif -static JSValue js_bigint_asUintN(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int asIntN) -{ - uint64_t bits; - bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; - JSValue res; - - if (JS_ToIndex(ctx, &bits, argv[0])) - return JS_EXCEPTION; - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - r = JS_GetBigInt(res); - a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - /* XXX: optimize */ - r = JS_GetBigInt(res); - bf_init(ctx->bf_ctx, mask); - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); - bf_logic_and(r, a, mask); - if (asIntN && bits != 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); - if (bf_cmpu(r, mask) >= 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); - } - } - bf_delete(mask); - JS_FreeBigInt(ctx, a, &a_s); - return JS_CompactBigInt(ctx, res); +/* skip spaces, update offset, return next char */ +static int string_skip_spaces(const uint8_t *sp, int *pp) { + int c; + while ((c = sp[*pp]) == ' ') + *pp += 1; + return c; } -static const JSCFunctionListEntry js_bigint_funcs[] = { - JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), - JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), -#ifdef CONFIG_BIGNUM - /* QuickJS extensions */ - JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ), - JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ), - JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ), - JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ), - JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ), - JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ), - JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ), - JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ), - JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ), - JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ), - JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ), -#endif -}; - -static const JSCFunctionListEntry js_bigint_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigint_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicBigInt(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValueConst obj1; - - rt->bigint_ops.to_string = js_bigint_to_string; - rt->bigint_ops.from_string = js_string_to_bigint; - rt->bigint_ops.unary_arith = js_unary_arith_bigint; - rt->bigint_ops.binary_arith = js_binary_arith_bigint; - rt->bigint_ops.compare = js_compare_bigfloat; - - ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], - js_bigint_proto_funcs, - countof(js_bigint_proto_funcs)); - obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, - ctx->class_proto[JS_CLASS_BIG_INT]); - JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, - countof(js_bigint_funcs)); +/* skip dashes dots and commas */ +static int string_skip_separators(const uint8_t *sp, int *pp) { + int c; + while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',') + *pp += 1; + return c; } -#ifdef CONFIG_BIGNUM - -/* BigFloat */ +/* skip a word, stop on spaces, digits and separators, update offset */ +static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) { + int c; + while (!strchr(stoplist, c = sp[*pp])) + *pp += 1; + return c; +} -static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) +/* parse a numeric field (max_digits = 0 -> no maximum) */ +static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval, + int min_digits, int max_digits) { - if (JS_IsBigFloat(this_val)) - return JS_DupValue(ctx, this_val); + int v = 0; + int c, p = *pp, p_start; - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_FLOAT) { - if (JS_IsBigFloat(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } + p_start = p; + while ((c = sp[p]) >= '0' && c <= '9') { + /* arbitrary limit to 9 digits */ + if (v >= 100000000) + return FALSE; + v = v * 10 + c - '0'; + p++; + if (p - p_start == max_digits) + break; } - return JS_ThrowTypeError(ctx, "not a bigfloat"); + if (p - p_start < min_digits) + return FALSE; + *pval = v; + *pp = p; + return TRUE; } -static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int base; - JSValue ret; +static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) { + /* parse optional fractional part as milliseconds and truncate. */ + /* spec does not indicate which rounding should be used */ + int mul = 100, ms = 0, c, p_start, p = *pp; - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (argc == 0 || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; + c = sp[p]; + if (c == '.' || c == ',') { + p++; + p_start = p; + while ((c = sp[p]) >= '0' && c <= '9') { + ms += (c - '0') * mul; + mul /= 10; + p++; + if (p - p_start == 9) + break; + } + if (p > p_start) { + /* only consume the separator if digits are present */ + *pval = ms; + *pp = p; + } } - ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; + return TRUE; } -static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigFloatValue(ctx, this_val); +static uint8_t upper_ascii(uint8_t c) { + return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; } -static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) -{ - int rnd_mode; - if (JS_ToInt32Sat(ctx, &rnd_mode, val)) - return -1; - if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) { - JS_ThrowRangeError(ctx, "invalid rounding mode"); - return -1; +static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) { + int tz = 0, sgn, hh, mm, p = *pp; + + sgn = sp[p++]; + if (sgn == '+' || sgn == '-') { + int n = p; + if (!string_get_digits(sp, &p, &hh, 1, 0)) + return FALSE; + n = p - n; + if (strict && n != 2 && n != 4) + return FALSE; + while (n > 4) { + n -= 2; + hh /= 100; + } + if (n > 2) { + mm = hh % 100; + hh = hh / 100; + } else { + mm = 0; + if (string_skip_char(sp, &p, ':') /* optional separator */ + && !string_get_digits(sp, &p, &mm, 2, 2)) + return FALSE; + } + if (hh > 23 || mm > 59) + return FALSE; + tz = hh * 60 + mm; + if (sgn != '+') + tz = -tz; + } else + if (sgn != 'Z') { + return FALSE; } - return rnd_mode; + *pp = p; + *tzp = tz; + return TRUE; } -static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode, radix; - - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - radix = 10; - /* XXX: swap parameter order for rounding mode and radix */ - if (argc > 1) { - rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - if (argc > 2) { - radix = js_get_radix(ctx, argv[2]); - if (radix < 0) - goto fail; +static BOOL string_match(const uint8_t *sp, int *pp, const char *s) { + int p = *pp; + while (*s != '\0') { + if (upper_ascii(sp[p]) != upper_ascii(*s++)) + return FALSE; + p++; } - ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; + *pp = p; + return TRUE; } -static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val) -{ - BOOL res; - uint32_t tag; +static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) { + int n, i; - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - res = bf_is_finite(&p->num); + for (n = 0; n < count; n++) { + for (i = 0;; i++) { + if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i])) + break; + if (i == 2) + return n; } - break; - default: - res = FALSE; - break; } - return res; + return -1; } -static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode, radix; +static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) { + int n; - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (!js_bigfloat_is_finite(ctx, val)) { - ret = JS_ToString(ctx, val); - } else if (JS_IsUndefined(argv[0])) { - ret = js_ftoa(ctx, val, 10, 0, - BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); - } else { - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - radix = 10; - if (argc > 1) { - rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - if (argc > 2) { - radix = js_get_radix(ctx, argv[2]); - if (radix < 0) - goto fail; - } - ret = js_ftoa(ctx, val, radix, f + 1, - rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); - } - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; + n = find_abbrev(sp, *pp, month_names, 12); + if (n < 0) + return FALSE; + + *pval = n + 1; + *pp += 3; + return TRUE; } -static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t p; - int rnd_mode, radix; +/* parse toISOString format */ +static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) { + int sgn, i, p = 0; - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_IsUndefined(argv[0])) - goto to_string; - if (JS_ToInt64Sat(ctx, &p, argv[0])) - goto fail; - if (!js_bigfloat_is_finite(ctx, val)) { - to_string: - ret = JS_ToString(ctx, this_val); + /* initialize fields to the beginning of the Epoch */ + for (i = 0; i < 9; i++) { + fields[i] = (i == 2); + } + *is_local = FALSE; + + /* year is either yyyy digits or [+-]yyyyyy */ + sgn = sp[p]; + if (sgn == '-' || sgn == '+') { + p++; + if (!string_get_digits(sp, &p, &fields[0], 6, 6)) + return FALSE; + if (sgn == '-') { + if (fields[0] == 0) + return FALSE; // reject -000000 + fields[0] = -fields[0]; + } } else { - if (p < 1 || p > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; + if (!string_get_digits(sp, &p, &fields[0], 4, 4)) + return FALSE; + } + if (string_skip_char(sp, &p, '-')) { + if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */ + return FALSE; + if (fields[1] < 1) + return FALSE; + fields[1] -= 1; + if (string_skip_char(sp, &p, '-')) { + if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */ + return FALSE; + if (fields[2] < 1) + return FALSE; } - rnd_mode = BF_RNDNA; - radix = 10; - if (argc > 1) { - rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; + } + if (string_skip_char(sp, &p, 'T')) { + *is_local = TRUE; + if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */ + || !string_skip_char(sp, &p, ':') + || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */ + fields[3] = 100; // reject unconditionally + return TRUE; } - if (argc > 2) { - radix = js_get_radix(ctx, argv[2]); - if (radix < 0) - goto fail; + if (string_skip_char(sp, &p, ':')) { + if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */ + return FALSE; + string_get_milliseconds(sp, &p, &fields[6]); } - ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED); } - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), - JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), - JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), - JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), + /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ + if (sp[p]) { + *is_local = FALSE; + if (!string_get_tzoffset(sp, &p, &fields[8], TRUE)) + return FALSE; + } + /* error if extraneous characters */ + return sp[p] == '\0'; +} + +static struct { + char name[6]; + int16_t offset; +} const js_tzabbr[] = { + { "GMT", 0 }, // Greenwich Mean Time + { "UTC", 0 }, // Coordinated Universal Time + { "UT", 0 }, // Universal Time + { "Z", 0 }, // Zulu Time + { "EDT", -4 * 60 }, // Eastern Daylight Time + { "EST", -5 * 60 }, // Eastern Standard Time + { "CDT", -5 * 60 }, // Central Daylight Time + { "CST", -6 * 60 }, // Central Standard Time + { "MDT", -6 * 60 }, // Mountain Daylight Time + { "MST", -7 * 60 }, // Mountain Standard Time + { "PDT", -7 * 60 }, // Pacific Daylight Time + { "PST", -8 * 60 }, // Pacific Standard Time + { "WET", +0 * 60 }, // Western European Time + { "WEST", +1 * 60 }, // Western European Summer Time + { "CET", +1 * 60 }, // Central European Time + { "CEST", +2 * 60 }, // Central European Summer Time + { "EET", +2 * 60 }, // Eastern European Time + { "EEST", +3 * 60 }, // Eastern European Summer Time }; -static JSValue js_bigfloat_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue val; - if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (argc == 0) { - bf_t *r; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - r = JS_GetBigFloat(val); - bf_set_zero(r, 0); - } else { - val = JS_DupValue(ctx, argv[0]); - redo: - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_BIG_FLOAT: - break; - case JS_TAG_FLOAT64: - { - bf_t *r; - double d = JS_VALUE_GET_FLOAT64(val); - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - if (bf_set_float64(r, d)) - goto fail; - } - break; - case JS_TAG_INT: - { - bf_t *r; - int32_t v = JS_VALUE_GET_INT(val); - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - if (bf_set_si(r, v)) - goto fail; - } - break; - case JS_TAG_BIG_INT: - /* We keep the full precision of the integer */ - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - } - break; - case JS_TAG_BIG_DECIMAL: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_STRING: - { - const char *str, *p; - size_t len; - int err; +static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) { + for (size_t i = 0; i < countof(js_tzabbr); i++) { + if (string_match(sp, pp, js_tzabbr[i].name)) { + *offset = js_tzabbr[i].offset; + return TRUE; + } + } + return FALSE; +} - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - bf_t *r; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - bf_set_zero(r, 0); - err = 0; - } else { - val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | - ATOD_TYPE_BIG_FLOAT | - ATOD_ACCEPT_PREFIX_AFTER_SIGN); - if (JS_IsException(val)) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; +/* parse toString, toUTCString and other formats */ +static BOOL js_date_parse_otherstring(const uint8_t *sp, + int fields[minimum_length(9)], + BOOL *is_local) { + int c, i, val, p = 0, p_start; + int num[3]; + BOOL has_year = FALSE; + BOOL has_mon = FALSE; + BOOL has_time = FALSE; + int num_index = 0; + + /* initialize fields to the beginning of 2001-01-01 */ + fields[0] = 2001; + fields[1] = 1; + fields[2] = 1; + for (i = 3; i < 9; i++) { + fields[i] = 0; + } + *is_local = TRUE; + + while (string_skip_spaces(sp, &p)) { + p_start = p; + if ((c = sp[p]) == '+' || c == '-') { + if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) { + *is_local = FALSE; + } else { + p++; + if (string_get_digits(sp, &p, &val, 1, 0)) { + if (c == '-') { + if (val == 0) + return FALSE; + val = -val; } - p += skip_spaces(p); - err = ((p - str) != len); + fields[0] = val; + has_year = TRUE; } - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal"); + } + } else + if (string_get_digits(sp, &p, &val, 1, 0)) { + if (string_skip_char(sp, &p, ':')) { + /* time part */ + fields[3] = val; + if (!string_get_digits(sp, &p, &fields[4], 1, 2)) + return FALSE; + if (string_skip_char(sp, &p, ':')) { + if (!string_get_digits(sp, &p, &fields[5], 1, 2)) + return FALSE; + string_get_milliseconds(sp, &p, &fields[6]); + } + has_time = TRUE; + } else { + if (p - p_start > 2) { + fields[0] = val; + has_year = TRUE; + } else + if (val < 1 || val > 31) { + fields[0] = val + (val < 100) * 1900 + (val < 50) * 100; + has_year = TRUE; + } else { + if (num_index == 3) + return FALSE; + num[num_index++] = val; } } - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigfloat"); + } else + if (string_get_month(sp, &p, &fields[1])) { + has_mon = TRUE; + string_skip_until(sp, &p, "0123456789 -/("); + } else + if (has_time && string_match(sp, &p, "PM")) { + if (fields[3] < 12) + fields[3] += 12; + continue; + } else + if (has_time && string_match(sp, &p, "AM")) { + if (fields[3] == 12) + fields[3] -= 12; + continue; + } else + if (string_get_tzabbr(sp, &p, &fields[8])) { + *is_local = FALSE; + continue; + } else + if (c == '(') { /* skip parenthesized phrase */ + int level = 0; + while ((c = sp[p]) != '\0') { + p++; + level += (c == '('); + level -= (c == ')'); + if (!level) + break; + } + if (level > 0) + return FALSE; + } else + if (c == ')') { + return FALSE; + } else { + if (has_year + has_mon + has_time + num_index) + return FALSE; + /* skip a word */ + string_skip_until(sp, &p, " -/("); } + string_skip_separators(sp, &p); } - return val; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} + if (num_index + has_year + has_mon > 3) + return FALSE; -static JSValue js_bigfloat_get_const(JSContext *ctx, - JSValueConst this_val, int magic) -{ - bf_t *r; - JSValue val; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - r = JS_GetBigFloat(val); - switch(magic) { - case 0: /* PI */ - bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); + switch (num_index) { + case 0: + if (!has_year) + return FALSE; break; - case 1: /* LN2 */ - bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); + case 1: + if (has_mon) + fields[2] = num[0]; + else + fields[1] = num[0]; break; - case 2: /* MIN_VALUE */ - case 3: /* MAX_VALUE */ - { - slimb_t e_range, e; - e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1); - bf_set_ui(r, 1); - if (magic == 2) { - e = -e_range + 2; - if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL) - e -= ctx->fp_env.prec - 1; - bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags); - } else { - bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec, - ctx->fp_env.flags); - bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags); - bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec, - ctx->fp_env.flags); - } + case 2: + if (has_year) { + fields[1] = num[0]; + fields[2] = num[1]; + } else + if (has_mon) { + fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100; + fields[2] = num[0]; + } else { + fields[1] = num[0]; + fields[2] = num[1]; } break; - case 4: /* EPSILON */ - bf_set_ui(r, 1); - bf_mul_2exp(r, 1 - ctx->fp_env.prec, - ctx->fp_env.prec, ctx->fp_env.flags); + case 3: + fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100; + fields[1] = num[0]; + fields[2] = num[1]; break; default: - abort(); + return FALSE; } - return val; + if (fields[1] < 1 || fields[2] < 1) + return FALSE; + fields[1] -= 1; + return TRUE; } -static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - bf_t *a; - const char *str; - JSValue ret; - int radix; - JSFloatEnv *fe; + JSValue s, rv; + int fields[9]; + double fields1[9]; + double d; + int i, c; + JSString *sp; + uint8_t buf[128]; + BOOL is_local; - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &radix, argv[1])) { - fail: - JS_FreeCString(ctx, str); + rv = JS_NAN; + + s = JS_ToString(ctx, argv[0]); + if (JS_IsException(s)) return JS_EXCEPTION; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } - fe = &ctx->fp_env; - if (argc > 2) { - fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); - if (!fe) - goto fail; - } - ret = JS_NewBigFloat(ctx); - if (JS_IsException(ret)) - goto done; - a = JS_GetBigFloat(ret); - /* XXX: use js_atof() */ - bf_atof(a, str, NULL, radix, fe->prec, fe->flags); - done: - JS_FreeCString(ctx, str); - return ret; -} -static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst val = argv[0]; - JSBigFloat *p; - - if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) - return JS_FALSE; - p = JS_VALUE_GET_PTR(val); - return JS_NewBool(ctx, bf_is_finite(&p->num)); + sp = JS_VALUE_GET_STRING(s); + /* convert the string as a byte array */ + for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) { + c = string_get(sp, i); + if (c > 255) + c = (c == 0x2212) ? '-' : 'x'; + buf[i] = c; + } + buf[i] = '\0'; + if (js_date_parse_isostring(buf, fields, &is_local) + || js_date_parse_otherstring(buf, fields, &is_local)) { + static int const field_max[6] = { 0, 11, 31, 24, 59, 59 }; + BOOL valid = TRUE; + /* check field maximum values */ + for (i = 1; i < 6; i++) { + if (fields[i] > field_max[i]) + valid = FALSE; + } + /* special case 24:00:00.000 */ + if (fields[3] == 24 && (fields[4] | fields[5] | fields[6])) + valid = FALSE; + if (valid) { + for(i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - fields[8] * 60000; + rv = JS_NewFloat64(ctx, d); + } + } + JS_FreeValue(ctx, s); + return rv; } -static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValueConst val = argv[0]; - JSBigFloat *p; - - if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) - return JS_FALSE; - p = JS_VALUE_GET_PTR(val); - return JS_NewBool(ctx, bf_is_nan(&p->num)); + // now() + return JS_NewInt64(ctx, date_now()); } -enum { - MATH_OP_ABS, - MATH_OP_FLOOR, - MATH_OP_CEIL, - MATH_OP_ROUND, - MATH_OP_TRUNC, - MATH_OP_SQRT, - MATH_OP_FPROUND, - MATH_OP_ACOS, - MATH_OP_ASIN, - MATH_OP_ATAN, - MATH_OP_ATAN2, - MATH_OP_COS, - MATH_OP_EXP, - MATH_OP_LOG, - MATH_OP_POW, - MATH_OP_SIN, - MATH_OP_TAN, - MATH_OP_FMOD, - MATH_OP_REM, - MATH_OP_SIGN, - - MATH_OP_ADD, - MATH_OP_SUB, - MATH_OP_MUL, - MATH_OP_DIV, -}; - -static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) +static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - bf_t a_s, *a, *r; - JSFloatEnv *fe; - int rnd_mode; - JSValue op1, res; - - op1 = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - fe = &ctx->fp_env; - if (argc > 1) { - fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); - if (!fe) - goto fail; - } - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) { - fail: - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - r = JS_GetBigFloat(res); - switch (magic) { - case MATH_OP_ABS: - bf_set(r, a); - r->sign = 0; - break; - case MATH_OP_FLOOR: - rnd_mode = BF_RNDD; - goto rint; - case MATH_OP_CEIL: - rnd_mode = BF_RNDU; - goto rint; - case MATH_OP_ROUND: - rnd_mode = BF_RNDNA; - goto rint; - case MATH_OP_TRUNC: - rnd_mode = BF_RNDZ; - rint: - bf_set(r, a); - fe->status |= bf_rint(r, rnd_mode); - break; - case MATH_OP_SQRT: - fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); - break; - case MATH_OP_FPROUND: - bf_set(r, a); - fe->status |= bf_round(r, fe->prec, fe->flags); - break; - case MATH_OP_ACOS: - fe->status |= bf_acos(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ASIN: - fe->status |= bf_asin(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ATAN: - fe->status |= bf_atan(r, a, fe->prec, fe->flags); - break; - case MATH_OP_COS: - fe->status |= bf_cos(r, a, fe->prec, fe->flags); - break; - case MATH_OP_EXP: - fe->status |= bf_exp(r, a, fe->prec, fe->flags); - break; - case MATH_OP_LOG: - fe->status |= bf_log(r, a, fe->prec, fe->flags); - break; - case MATH_OP_SIN: - fe->status |= bf_sin(r, a, fe->prec, fe->flags); - break; - case MATH_OP_TAN: - fe->status |= bf_tan(r, a, fe->prec, fe->flags); - break; - case MATH_OP_SIGN: - if (bf_is_nan(a) || bf_is_zero(a)) { - bf_set(r, a); - } else { - bf_set_si(r, 1 - 2 * a->sign); - } - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return res; -} + // Symbol_toPrimitive(hint) + JSValueConst obj = this_val; + JSAtom hint = JS_ATOM_NULL; + int hint_num; -static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; - JSFloatEnv *fe; - JSValue op1, op2, res; + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); - op1 = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - op2 = JS_ToNumeric(ctx, argv[1]); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return op2; - } - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) - goto fail1; - b = JS_ToBigFloat(ctx, &b_s, op2); - if (!b) - goto fail2; - fe = &ctx->fp_env; - if (argc > 2) { - fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); - if (!fe) - goto fail; - } - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) { - fail: - if (b == &b_s) - bf_delete(b); - fail2: - if (a == &a_s) - bf_delete(a); - fail1: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; + if (JS_IsString(argv[0])) { + hint = JS_ValueToAtom(ctx, argv[0]); + if (hint == JS_ATOM_NULL) + return JS_EXCEPTION; + JS_FreeAtom(ctx, hint); } - r = JS_GetBigFloat(res); - switch (magic) { - case MATH_OP_ATAN2: - fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_POW: - fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); - break; - case MATH_OP_FMOD: - fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); - break; - case MATH_OP_REM: - fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); - break; - case MATH_OP_ADD: - fe->status |= bf_add(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_SUB: - fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_MUL: - fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); + switch (hint) { + case JS_ATOM_number: + case JS_ATOM_integer: + hint_num = HINT_NUMBER; break; - case MATH_OP_DIV: - fe->status |= bf_div(r, a, b, fe->prec, fe->flags); + case JS_ATOM_string: + case JS_ATOM_default: + hint_num = HINT_STRING; break; default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return res; + return JS_ThrowTypeError(ctx, "invalid hint"); + } + return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); } -static const JSCFunctionListEntry js_bigfloat_funcs[] = { - JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ), - JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ), - JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ), - JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ), - JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ), - JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ), - JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ), - JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ), - JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ), - JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ), - JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ), - JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ), - JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ), - JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ), - JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ), - JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ), - JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ), - JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ), - JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ), - JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ), - JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ), - JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ), - JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ), - JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ), - JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ), - JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ), - JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ), - JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ), - JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ), -}; - -/* FloatEnv */ - -static JSValue js_float_env_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) +static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue obj; - JSFloatEnv *fe; - int64_t prec; - int flags, rndmode; - - prec = ctx->fp_env.prec; - flags = ctx->fp_env.flags; - if (!JS_IsUndefined(argv[0])) { - if (JS_ToInt64Sat(ctx, &prec, argv[0])) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */ - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt32Sat(ctx, &rndmode, argv[1])) - return JS_EXCEPTION; - if (rndmode < BF_RNDN || rndmode > BF_RNDF) - return JS_ThrowRangeError(ctx, "invalid rounding mode"); - flags = rndmode; - } - } + // getTimezoneOffset() + double v; - obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); - if (JS_IsException(obj)) - return JS_EXCEPTION; - fe = js_malloc(ctx, sizeof(*fe)); - if (!fe) + if (JS_ThisTimeValue(ctx, &v, this_val)) return JS_EXCEPTION; - fe->prec = prec; - fe->flags = flags; - fe->status = 0; - JS_SetOpaque(obj, fe); - return obj; + if (isnan(v)) + return JS_NAN; + else + /* assuming -8.64e15 <= v <= -8.64e15 */ + return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); } -static void js_float_env_finalizer(JSRuntime *rt, JSValue val) +static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); - js_free_rt(rt, fe); -} + // getTime() + double v; -static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt64(ctx, ctx->fp_env.prec); + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, v); } -static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) +static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); + // setTime(v) + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) + return JS_EXCEPTION; + return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); } -static JSValue js_float_env_setPrec(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValueConst func; - int exp_bits, flags, saved_flags; - JSValue ret; - limb_t saved_prec; - int64_t prec; + // setYear(y) + double y; + JSValueConst args[1]; - func = argv[0]; - if (JS_ToInt64Sat(ctx, &prec, argv[1])) + if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - exp_bits = BF_EXP_BITS_MAX; - - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) - return JS_EXCEPTION; - if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) - return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + y = +y; + if (isfinite(y)) { + y = trunc(y); + if (y >= 0 && y < 100) + y += 1900; } + args[0] = JS_NewFloat64(ctx, y); + return set_date_field(ctx, this_val, 1, args, 0x011); +} - flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); - - saved_prec = ctx->fp_env.prec; - saved_flags = ctx->fp_env.flags; +static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // toJSON(key) + JSValue obj, tv, method, rv; + double d; - ctx->fp_env.prec = prec; - ctx->fp_env.flags = flags; + rv = JS_EXCEPTION; + tv = JS_UNDEFINED; - ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); - /* always restore the floating point precision */ - ctx->fp_env.prec = saved_prec; - ctx->fp_env.flags = saved_flags; - return ret; + obj = JS_ToObject(ctx, this_val); + tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); + if (JS_IsException(tv)) + goto exception; + if (JS_IsNumber(tv)) { + if (JS_ToFloat64(ctx, &d, tv) < 0) + goto exception; + if (!isfinite(d)) { + rv = JS_NULL; + goto done; + } + } + method = JS_GetPropertyStr(ctx, obj, "toISOString"); + if (JS_IsException(method)) + goto exception; + if (!JS_IsFunction(ctx, method)) { + JS_ThrowTypeError(ctx, "object needs toISOString method"); + JS_FreeValue(ctx, method); + goto exception; + } + rv = JS_CallFree(ctx, method, obj, 0, NULL); +exception: +done: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, tv); + return rv; } -#define FE_PREC (-1) -#define FE_EXP (-2) -#define FE_RNDMODE (-3) -#define FE_SUBNORMAL (-4) +static const JSCFunctionListEntry js_date_funcs[] = { + JS_CFUNC_DEF("now", 0, js_Date_now ), + JS_CFUNC_DEF("parse", 1, js_Date_parse ), + JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), +}; + +static const JSCFunctionListEntry js_date_proto_funcs[] = { + JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), + JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), + JS_ALIAS_DEF("toGMTString", "toUTCString" ), + JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), + JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), + JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), + JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), + JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), + JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), + JS_CFUNC_DEF("getTime", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), + JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), + JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), + JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), + JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), + JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), + JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), + JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), + JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), + JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), + JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), + JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), + JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), + JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), + JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), + JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), + JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), + JS_CFUNC_DEF("setTime", 1, js_date_setTime ), + JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), + JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), + JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), + JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), + JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), + JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), + JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), + JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), + JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), + JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), + JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), + JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), + JS_CFUNC_DEF("setYear", 1, js_date_setYear ), + JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), + JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), + JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), +}; -static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) +JSValue JS_NewDate(JSContext *ctx, double epoch_ms) { - JSFloatEnv *fe; - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) + JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE); + if (JS_IsException(obj)) return JS_EXCEPTION; - switch(magic) { - case FE_PREC: - return JS_NewInt64(ctx, fe->prec); - case FE_EXP: - return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); - case FE_RNDMODE: - return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); - case FE_SUBNORMAL: - return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0); - default: - return JS_NewBool(ctx, (fe->status & magic) != 0); - } + JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms))); + return obj; } -static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) +void JS_AddIntrinsicDate(JSContext *ctx) { - JSFloatEnv *fe; - int b; - int64_t prec; + JSValueConst obj; - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - switch(magic) { - case FE_PREC: - if (JS_ToInt64Sat(ctx, &prec, val)) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - fe->prec = prec; - break; - case FE_EXP: - if (JS_ToInt32Sat(ctx, &b, val)) - return JS_EXCEPTION; - if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) - return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | - bf_set_exp_bits(b); - break; - case FE_RNDMODE: - b = bigfloat_get_rnd_mode(ctx, val); - if (b < 0) - return JS_EXCEPTION; - fe->flags = (fe->flags & ~BF_RND_MASK) | b; - break; - case FE_SUBNORMAL: - b = JS_ToBool(ctx, val); - fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); - break; - default: - b = JS_ToBool(ctx, val); - fe->status = (fe->status & ~magic) & ((-b) & magic); - break; - } - return JS_UNDEFINED; + /* Date */ + ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, + countof(js_date_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, + ctx->class_proto[JS_CLASS_DATE]); + JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); } -static JSValue js_float_env_clearStatus(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) +/* eval */ + +void JS_AddIntrinsicEval(JSContext *ctx) { - JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - fe->status = 0; - return JS_UNDEFINED; + ctx->eval_internal = __JS_EvalInternal; } -static const JSCFunctionListEntry js_float_env_funcs[] = { - JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ), - JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ), - JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ), - JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ), - JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ), - JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), - JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), - JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), - JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), - JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), - JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), - JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), - JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), - JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), -}; - -static const JSCFunctionListEntry js_float_env_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_PREC ), - JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_EXP ), - JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_RNDMODE ), - JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_SUBNORMAL ), - JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_INVALID_OP ), - JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), - JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_OVERFLOW ), - JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_UNDERFLOW ), - JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_INEXACT ), - JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), -}; +/* BigInt */ -void JS_AddIntrinsicBigFloat(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValueConst obj1; - - rt->bigfloat_ops.to_string = js_bigfloat_to_string; - rt->bigfloat_ops.from_string = js_string_to_bigfloat; - rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; - rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; - rt->bigfloat_ops.compare = js_compare_bigfloat; - rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; - rt->bigfloat_ops.mul_pow10 = js_mul_pow10; - - ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], - js_bigfloat_proto_funcs, - countof(js_bigfloat_proto_funcs)); - obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, - ctx->class_proto[JS_CLASS_BIG_FLOAT]); - JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, - countof(js_bigfloat_funcs)); - - ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], - js_float_env_proto_funcs, - countof(js_float_env_proto_funcs)); - obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", - js_float_env_constructor, 1, - ctx->class_proto[JS_CLASS_FLOAT_ENV]); - JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, - countof(js_float_env_funcs)); -} - -/* BigDecimal */ - -static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, - BOOL allow_null_or_undefined) +static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) { + uint32_t tag; + redo: - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_BIG_DECIMAL: - break; - case JS_TAG_NULL: - if (!allow_null_or_undefined) - goto fail; - /* fall thru */ - case JS_TAG_BOOL: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { case JS_TAG_INT: - { - bfdec_t *r; - int32_t v = JS_VALUE_GET_INT(val); - - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigDecimal(val); - if (bfdec_set_si(r, v)) { - JS_FreeValue(ctx, val); - val = JS_EXCEPTION; - break; - } - } + case JS_TAG_BOOL: + val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); break; - case JS_TAG_FLOAT64: + case JS_TAG_SHORT_BIG_INT: case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_STRING: + break; + case JS_TAG_FLOAT64: { - const char *str, *p; - size_t len; - int err; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - bfdec_t *r; - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigDecimal(val); - bfdec_set_zero(r, 0); - err = 0; + double d = JS_VALUE_GET_FLOAT64(val); + JSBigInt *r; + int res; + r = js_bigint_from_float64(ctx, &res, d); + if (!r) { + if (res == 0) { + val = JS_EXCEPTION; + } else if (res == 1) { + val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); + } else { + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); } } else { - val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL); - if (JS_IsException(val)) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - p += skip_spaces(p); - err = ((p - str) != len); - } - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal"); + val = JS_CompactBigInt(ctx, r); } } break; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + val = JS_StringToBigIntErr(ctx, val); + break; case JS_TAG_OBJECT: val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) break; goto redo; + case JS_TAG_NULL: case JS_TAG_UNDEFINED: - { - bfdec_t *r; - if (!allow_null_or_undefined) - goto fail; - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigDecimal(val); - bfdec_set_nan(r); - } - break; default: - fail: JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal"); + return JS_ThrowTypeError(ctx, "cannot convert to BigInt"); } return val; } -static JSValue js_bigdecimal_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) +static JSValue js_bigint_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) { - JSValue val; if (!JS_IsUndefined(new_target)) return JS_ThrowTypeError(ctx, "not a constructor"); - if (argc == 0) { - bfdec_t *r; - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - return val; - r = JS_GetBigDecimal(val); - bfdec_set_zero(r, 0); - } else { - val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE); - } - return val; + return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); } -static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val) +static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) { - if (JS_IsBigDecimal(this_val)) + if (JS_IsBigInt(ctx, this_val)) return JS_DupValue(ctx, this_val); if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_DECIMAL) { - if (JS_IsBigDecimal(p->u.object_data)) + if (p->class_id == JS_CLASS_BIG_INT) { + if (JS_IsBigInt(ctx, p->u.object_data)) return JS_DupValue(ctx, p->u.object_data); } } - return JS_ThrowTypeError(ctx, "not a bigdecimal"); + return JS_ThrowTypeError(ctx, "not a BigInt"); } -static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue val; + int base; + JSValue ret; - val = js_thisBigDecimalValue(ctx, this_val); + val = js_thisBigIntValue(ctx, this_val); if (JS_IsException(val)) return val; - return JS_ToStringFree(ctx, val); -} - -static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigDecimalValue(ctx, this_val); -} - -static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj) -{ - const char *str; - size_t size; - int rnd_mode; - - str = JS_ToCStringLen(ctx, &size, obj); - if (!str) - return -1; - if (strlen(str) != size) - goto invalid_rounding_mode; - if (!strcmp(str, "floor")) { - rnd_mode = BF_RNDD; - } else if (!strcmp(str, "ceiling")) { - rnd_mode = BF_RNDU; - } else if (!strcmp(str, "down")) { - rnd_mode = BF_RNDZ; - } else if (!strcmp(str, "up")) { - rnd_mode = BF_RNDA; - } else if (!strcmp(str, "half-even")) { - rnd_mode = BF_RNDN; - } else if (!strcmp(str, "half-up")) { - rnd_mode = BF_RNDNA; - } else { - invalid_rounding_mode: - JS_FreeCString(ctx, str); - JS_ThrowTypeError(ctx, "invalid rounding mode"); - return -1; - } - JS_FreeCString(ctx, str); - return rnd_mode; -} - -typedef struct { - int64_t prec; - bf_flags_t flags; -} BigDecimalEnv; - -static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, - JSValueConst obj) -{ - JSValue prop; - int64_t val; - BOOL has_prec; - int rnd_mode; - - if (!JS_IsObject(obj)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); - if (JS_IsException(prop)) - return -1; - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop); - JS_FreeValue(ctx, prop); - if (rnd_mode < 0) - return -1; - fe->flags = rnd_mode; - - prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits); - if (JS_IsException(prop)) - return -1; - has_prec = FALSE; - if (!JS_IsUndefined(prop)) { - if (JS_ToInt64SatFree(ctx, &val, prop)) - return -1; - if (val < 1 || val > BF_PREC_MAX) - goto invalid_precision; - fe->prec = val; - has_prec = TRUE; - } - - prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits); - if (JS_IsException(prop)) - return -1; - if (!JS_IsUndefined(prop)) { - if (has_prec) { - JS_FreeValue(ctx, prop); - JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits"); - return -1; - } - if (JS_ToInt64SatFree(ctx, &val, prop)) - return -1; - if (val < 0 || val > BF_PREC_MAX) { - invalid_precision: - JS_ThrowTypeError(ctx, "invalid precision"); - return -1; - } - fe->prec = val; - fe->flags |= BF_FLAG_RADPNT_PREC; - has_prec = TRUE; - } - if (!has_prec) { - JS_ThrowTypeError(ctx, "precision must be present"); - return -1; - } - return 0; -} - - -static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bfdec_t *a, *b, r_s, *r = &r_s; - JSValue op1, op2, res; - BigDecimalEnv fe_s, *fe = &fe_s; - int op_count, ret; - - if (magic == MATH_OP_SQRT || - magic == MATH_OP_ROUND) - op_count = 1; - else - op_count = 2; - - op1 = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigDecimal(ctx, op1); - if (!a) { - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - if (op_count >= 2) { - op2 = JS_ToNumeric(ctx, argv[1]); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return op2; - } - b = JS_ToBigDecimal(ctx, op2); - if (!b) - goto fail; - } else { - op2 = JS_UNDEFINED; - b = NULL; - } - fe->flags = BF_RNDZ; - fe->prec = BF_PREC_INF; - if (op_count < argc) { - if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) - goto fail; - } - - res = JS_NewBigDecimal(ctx); - if (JS_IsException(res)) { - fail: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - r = JS_GetBigDecimal(res); - switch (magic) { - case MATH_OP_ADD: - ret = bfdec_add(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_SUB: - ret = bfdec_sub(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_MUL: - ret = bfdec_mul(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_DIV: - ret = bfdec_div(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_FMOD: - ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); - break; - case MATH_OP_SQRT: - ret = bfdec_sqrt(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ROUND: - ret = bfdec_set(r, a); - if (!(ret & BF_ST_MEM_ERROR)) - ret = bfdec_round(r, fe->prec, fe->flags); - break; - default: - abort(); - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP | - BF_ST_OVERFLOW; - if (ret != 0) { - JS_FreeValue(ctx, res); - return throw_bf_exception(ctx, ret); + if (argc == 0 || JS_IsUndefined(argv[0])) { + base = 10; } else { - return res; - } -} - -static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (argc > 1) { - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; } - ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + ret = js_bigint_to_string1(ctx, val, base); JS_FreeValue(ctx, val); return ret; fail: @@ -50514,123 +52109,100 @@ static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } -static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - JSValue val, ret; - int64_t f; - int rnd_mode; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (JS_IsUndefined(argv[0])) { - ret = js_bigdecimal_to_string1(ctx, val, 0, - BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); - } else { - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (argc > 1) { - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - ret = js_bigdecimal_to_string1(ctx, val, f + 1, - rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); - } - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; + return js_thisBigIntValue(ctx, this_val); } -static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_bigint_asUintN(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int asIntN) { - JSValue val, ret; - int64_t p; - int rnd_mode; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_IsUndefined(argv[0])) { - return JS_ToStringFree(ctx, val); - } - if (JS_ToInt64Sat(ctx, &p, argv[0])) - goto fail; - if (p < 1 || p > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (argc > 1) { - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; + uint64_t bits; + JSValue res, a; + + if (JS_ToIndex(ctx, &bits, argv[0])) + return JS_EXCEPTION; + a = JS_ToBigInt(ctx, argv[1]); + if (JS_IsException(a)) + return JS_EXCEPTION; + if (bits == 0) { + JS_FreeValue(ctx, a); + res = __JS_NewShortBigInt(ctx, 0); + } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) { + /* fast case */ + if (bits >= JS_SHORT_BIG_INT_BITS) { + res = a; + } else { + uint64_t v; + int shift; + shift = 64 - bits; + v = JS_VALUE_GET_SHORT_BIG_INT(a); + v = v << shift; + if (asIntN) + v = (int64_t)v >> shift; + else + v = v >> shift; + res = __JS_NewShortBigInt(ctx, v); + } + } else { + JSBigInt *r, *p = JS_VALUE_GET_PTR(a); + if (bits >= p->len * JS_LIMB_BITS) { + res = a; + } else { + int len, shift, i; + js_limb_t v; + len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS; + r = js_bigint_new(ctx, len); + if (!r) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + r->len = len; + for(i = 0; i < len - 1; i++) + r->tab[i] = p->tab[i]; + shift = (-bits) & (JS_LIMB_BITS - 1); + /* 0 <= shift <= JS_LIMB_BITS - 1 */ + v = p->tab[len - 1] << shift; + if (asIntN) + v = (js_slimb_t)v >> shift; + else + v = v >> shift; + r->tab[len - 1] = v; + r = js_bigint_normalize(ctx, r); + JS_FreeValue(ctx, a); + res = JS_CompactBigInt(ctx, r); + } } - ret = js_bigdecimal_to_string1(ctx, val, p, - rnd_mode | BF_FTOA_FORMAT_FIXED); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; + return res; } -static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), - JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ), - JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ), - JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ), +static const JSCFunctionListEntry js_bigint_funcs[] = { + JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), + JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), }; -static const JSCFunctionListEntry js_bigdecimal_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ), - JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ), - JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ), - JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ), - JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ), - JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), +static const JSCFunctionListEntry js_bigint_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigint_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), }; -void JS_AddIntrinsicBigDecimal(JSContext *ctx) +static void JS_AddIntrinsicBigInt(JSContext *ctx) { - JSRuntime *rt = ctx->rt; JSValueConst obj1; - rt->bigdecimal_ops.to_string = js_bigdecimal_to_string; - rt->bigdecimal_ops.from_string = js_string_to_bigdecimal; - rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal; - rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal; - rt->bigdecimal_ops.compare = js_compare_bigdecimal; - - ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL], - js_bigdecimal_proto_funcs, - countof(js_bigdecimal_proto_funcs)); - obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal", - js_bigdecimal_constructor, 1, - ctx->class_proto[JS_CLASS_BIG_DECIMAL]); - JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs, - countof(js_bigdecimal_funcs)); -} - -void JS_EnableBignumExt(JSContext *ctx, BOOL enable) -{ - ctx->bignum_ext = enable; + ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + js_bigint_proto_funcs, + countof(js_bigint_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_INT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, + countof(js_bigint_funcs)); } -#endif /* CONFIG_BIGNUM */ - static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = { "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError", @@ -50645,6 +52217,8 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx) int i; ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); + JS_SetImmutablePrototype(ctx, ctx->class_proto[JS_CLASS_OBJECT]); + ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, JS_CFUNC_generic, 0, ctx->class_proto[JS_CLASS_OBJECT]); @@ -50702,16 +52276,14 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); /* add caller and arguments properties to throw a TypeError */ - obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0); JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, - obj1, ctx->throw_type_error, + ctx->throw_type_error, ctx->throw_type_error, JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, - obj1, ctx->throw_type_error, + ctx->throw_type_error, ctx->throw_type_error, JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); ctx->global_obj = JS_NewObject(ctx); @@ -50738,11 +52310,13 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) JS_NewGlobalCConstructor2(ctx, obj1, "Error", ctx->class_proto[JS_CLASS_ERROR]); + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_error_constructor }; for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { JSValue func_obj; int n_args; n_args = 1 + (i == JS_AGGREGATE_ERROR); - func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor, + func_obj = JS_NewCFunction3(ctx, ft.generic, native_error_name[i], n_args, JS_CFUNC_constructor_or_func_magic, i, obj1); JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i], @@ -50769,7 +52343,8 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) /* XXX: create auto_initializer */ { /* initialize Array.prototype[Symbol.unscopables] */ - char const unscopables[] = + static const char unscopables[] = + "at" "\0" "copyWithin" "\0" "entries" "\0" "fill" "\0" @@ -50781,6 +52356,9 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) "flatMap" "\0" "includes" "\0" "keys" "\0" + "toReversed" "\0" + "toSorted" "\0" + "toSpliced" "\0" "values" "\0"; const char *p = unscopables; obj1 = JS_NewObjectProto(ctx, JS_NULL); @@ -50876,9 +52454,10 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) countof(js_generator_proto_funcs)); ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor, - "GeneratorFunction", 1, - JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "GeneratorFunction", 1, + JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR, + ctx->function_ctor); JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], js_generator_function_proto_funcs, @@ -50899,14 +52478,17 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, JS_DupValue(ctx, ctx->global_obj), JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + + /* BigInt */ + JS_AddIntrinsicBigInt(ctx); } /* Typed Arrays */ static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { 0, 0, 0, 1, 1, 2, 2, - 3, 3, /* BigInt64Array, BigUint64Array */ - 2, 3 + 3, 3, // BigInt64Array, BigUint64Array + 1, 2, 3 // Float16Array, Float32Array, Float64Array }; static JSValue js_array_buffer_constructor3(JSContext *ctx, @@ -51035,11 +52617,26 @@ static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSArrayBuffer *abuf = p->u.array_buffer; + struct list_head *el, *el1; + if (abuf) { /* The ArrayBuffer finalizer may be called before the typed array finalizers using it, so abuf->array_list is not necessarily empty. */ - // assert(list_empty(&abuf->array_list)); + list_for_each_safe(el, el1, &abuf->array_list) { + JSTypedArray *ta; + JSObject *p1; + + ta = list_entry(el, JSTypedArray, link); + ta->link.prev = NULL; + ta->link.next = NULL; + p1 = ta->obj; + /* Note: the typed array length and offset fields are not modified */ + if (p1->class_id != JS_CLASS_DATAVIEW) { + p1->u.array.count = 0; + p1->u.array.u.ptr = NULL; + } + } if (abuf->shared && rt->sab_funcs.sab_free) { rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); } else { @@ -51349,6 +52946,16 @@ static JSValue js_typed_array_get_byteOffset(JSContext *ctx, return JS_NewInt32(ctx, ta->offset); } +JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, + JSTypedArrayEnum type) +{ + if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64) + return JS_ThrowRangeError(ctx, "invalid typed array type"); + + return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv, + JS_CLASS_UINT8C_ARRAY + type); +} + /* Return the buffer associated to the typed array or an exception if it is not a typed array or if the buffer is detached. pbyte_offset, pbyte_length or pbytes_per_element can be NULL. */ @@ -51374,7 +52981,7 @@ JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, } return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); } - + static JSValue js_typed_array_get_toStringTag(JSContext *ctx, JSValueConst this_val) { @@ -51492,6 +53099,46 @@ static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val, return JS_GetPropertyInt64(ctx, this_val, idx); } +static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, val; + JSObject *p; + int64_t idx, len; + + p = get_typed_array(ctx, this_val, /*is_dataview*/0); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + return JS_EXCEPTION; + + len = p->u.array.count; + if (idx < 0) + idx = len + idx; + + val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + + if (typed_array_is_detached(ctx, p) || idx < 0 || idx >= len) + return JS_ThrowRangeError(ctx, "invalid array index"); + + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id); + if (JS_IsException(arr)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; + } + return arr; +} + static JSValue js_typed_array_set(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -51616,18 +53263,16 @@ static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, // from(items, mapfn = void 0, this_arg = void 0) JSValueConst items = argv[0], mapfn, this_arg; JSValueConst args[2]; - JSValue stack[2]; JSValue iter, arr, r, v, v2; int64_t k, len; - int done, mapping; + int mapping; mapping = FALSE; mapfn = JS_UNDEFINED; this_arg = JS_UNDEFINED; r = JS_UNDEFINED; arr = JS_UNDEFINED; - stack[0] = JS_UNDEFINED; - stack[1] = JS_UNDEFINED; + iter = JS_UNDEFINED; if (argc > 1) { mapfn = argv[1]; @@ -51642,30 +53287,23 @@ static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); if (JS_IsException(iter)) goto exception; - if (!JS_IsUndefined(iter)) { - JS_FreeValue(ctx, iter); - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - goto exception; - stack[0] = JS_DupValue(ctx, items); - if (js_for_of_start(ctx, &stack[1], FALSE)) + if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { + uint32_t len1; + if (!JS_IsFunction(ctx, iter)) { + JS_ThrowTypeError(ctx, "value is not iterable"); goto exception; - for (k = 0;; k++) { - v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); - if (JS_IsException(v)) - goto exception_close; - if (done) - break; - if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception_close; } + arr = js_array_from_iterator(ctx, &len1, items, iter); + if (JS_IsException(arr)) + goto exception; + len = len1; } else { arr = JS_ToObject(ctx, items); if (JS_IsException(arr)) goto exception; + if (js_get_length64(ctx, &len, arr) < 0) + goto exception; } - if (js_get_length64(ctx, &len, arr) < 0) - goto exception; v = JS_NewInt64(ctx, len); args[0] = v; r = js_typed_array_create(ctx, this_val, 1, args); @@ -51689,17 +53327,12 @@ static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, goto exception; } goto done; - - exception_close: - if (!JS_IsUndefined(stack[0])) - JS_IteratorClose(ctx, stack[0], TRUE); exception: JS_FreeValue(ctx, r); r = JS_EXCEPTION; done: JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, stack[0]); - JS_FreeValue(ctx, stack[1]); + JS_FreeValue(ctx, iter); return r; } @@ -51788,7 +53421,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, double d; if (JS_ToFloat64(ctx, &d, argv[0])) return JS_EXCEPTION; - if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { + if (p->class_id == JS_CLASS_FLOAT16_ARRAY) { + v64 = tofp16(d); + } else if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { union { float f; uint32_t u32; @@ -51816,7 +53451,7 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, if (typed_array_is_detached(ctx, p)) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - + shift = typed_array_size_log2(p->class_id); switch(shift) { case 0: @@ -51867,14 +53502,13 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, if (argc > 1) this_arg = argv[1]; - if (mode == special_findLast || mode == special_findLastIndex) { + k = 0; + dir = 1; + end = len; + if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { k = len - 1; dir = -1; end = -1; - } else { - k = 0; - dir = 1; - end = len; } for(; k != end; k += dir) { @@ -51889,7 +53523,7 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, if (JS_IsException(res)) goto exception; if (JS_ToBoolFree(ctx, res)) { - if (mode == special_findIndex || mode == special_findLastIndex) { + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { JS_FreeValue(ctx, val); return index_val; } else { @@ -51898,7 +53532,7 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, } JS_FreeValue(ctx, val); } - if (mode == special_findIndex || mode == special_findLastIndex) + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) return JS_NewInt32(ctx, -1); else return JS_UNDEFINED; @@ -51920,6 +53554,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, int64_t v64; double d; float f; + uint16_t hf; len = js_typed_array_get_length_internal(ctx, this_val); if (len < 0) @@ -51968,7 +53603,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, res = 0; goto done; } - + is_bigint = 0; is_int = 0; /* avoid warning */ v64 = 0; /* avoid warning */ @@ -51980,20 +53615,37 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } else if (tag == JS_TAG_FLOAT64) { d = JS_VALUE_GET_FLOAT64(argv[0]); - v64 = d; - is_int = (v64 == d); - } else if (tag == JS_TAG_BIG_INT) { - JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]); + if (d >= INT64_MIN && d < 0x1p63) { + v64 = d; + is_int = (v64 == d); + } + } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) { + JSBigIntBuf buf1; + JSBigInt *p1; + int sz = (64 / JS_LIMB_BITS); + if (tag == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, argv[0]); + else + p1 = JS_VALUE_GET_PTR(argv[0]); if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { - if (bf_get_int64(&v64, &p1->num, 0) != 0) - goto done; + if (p1->len > sz) + goto done; /* does not fit an int64 : cannot be found */ } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { - if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) + if (js_bigint_sign(p1)) + goto done; /* v < 0 */ + if (p1->len <= sz) { + /* OK */ + } else if (p1->len == sz + 1 && p1->tab[sz] == 0) { + /* 2^63 <= v <= 2^64-1 */ + } else { goto done; + } } else { goto done; } + if (JS_ToBigInt64(ctx, &v64, argv[0])) + goto exception; d = 0; is_bigint = 1; } else { @@ -52065,6 +53717,39 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } } break; + case JS_CLASS_FLOAT16_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const uint16_t *pv = p->u.array.u.fp16_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isfp16nan(pv[k])) { + res = k; + break; + } + } + } else if (d == 0) { + // special case: includes also finds negative zero + const uint16_t *pv = p->u.array.u.fp16_ptr; + for (; k != stop; k += inc) { + if (isfp16zero(pv[k])) { + res = k; + break; + } + } + } else if (hf = tofp16(d), d == fromfp16(hf)) { + const uint16_t *pv = p->u.array.u.fp16_ptr; + for (; k != stop; k += inc) { + if (pv[k] == hf) { + res = k; + break; + } + } + } + break; case JS_CLASS_FLOAT32_ARRAY: if (is_bigint) break; @@ -52114,15 +53799,12 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } break; case JS_CLASS_BIG_INT64_ARRAY: - if (is_bigint || (is_math_mode(ctx) && is_int && - v64 >= -MAX_SAFE_INTEGER && - v64 <= MAX_SAFE_INTEGER)) { + if (is_bigint) { goto scan64; } break; case JS_CLASS_BIG_UINT64_ARRAY: - if (is_bigint || (is_math_mode(ctx) && is_int && - v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) { + if (is_bigint) { const uint64_t *pv; uint64_t v; scan64: @@ -52270,6 +53952,36 @@ static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, return JS_DupValue(ctx, this_val); } +static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, ret; + JSObject *p; + + p = get_typed_array(ctx, this_val, /*is_dataview*/0); + if (!p) + return JS_EXCEPTION; + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id); + if (JS_IsException(arr)) + return JS_EXCEPTION; + ret = js_typed_array_reverse(ctx, arr, argc, argv); + JS_FreeValue(ctx, arr); + return ret; +} + +static void slice_memcpy(uint8_t *dst, const uint8_t *src, size_t len) +{ + if (dst + len <= src || dst >= src + len) { + /* no overlap: can use memcpy */ + memcpy(dst, src, len); + } else { + /* otherwise the spec mandates byte copy */ + while (len-- != 0) + *dst++ = *src++; + } +} + static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -52312,9 +54024,9 @@ static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, if (p1 != NULL && p->class_id == p1->class_id && typed_array_get_length(ctx, p1) >= count && typed_array_get_length(ctx, p) >= start + count) { - memcpy(p1->u.array.u.uint8_ptr, - p->u.array.u.uint8_ptr + (start << shift), - count << shift); + slice_memcpy(p1->u.array.u.uint8_ptr, + p->u.array.u.uint8_ptr + (start << shift), + count << shift); } else { for (n = 0; n < count; n++) { val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); @@ -52428,6 +54140,11 @@ static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { return (y < x) - (y > x); } +static int js_TA_cmp_float16(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(fromfp16(*(const uint16_t *)a), + fromfp16(*(const uint16_t *)b)); +} + static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { return js_cmp_doubles(*(const float *)a, *(const float *)b); } @@ -52468,6 +54185,10 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { return JS_NewBigUint64(ctx, *(uint64_t *)a); } +static JSValue js_TA_get_float16(JSContext *ctx, const void *a) { + return __JS_NewFloat64(ctx, fromfp16(*(const uint16_t *)a)); +} + static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { return __JS_NewFloat64(ctx, *(const float *)a); } @@ -52478,7 +54199,7 @@ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { struct TA_sort_context { JSContext *ctx; - int exception; + int exception; /* 1 = exception, 2 = detached typed array */ JSValueConst arr; JSValueConst cmp; JSValue (*getfun)(JSContext *ctx, const void *a); @@ -52496,6 +54217,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { cmp = 0; if (!psc->exception) { + /* Note: the typed array can be detached without causing an + error */ a_idx = *(uint32_t *)a; b_idx = *(uint32_t *)b; argv[0] = psc->getfun(ctx, psc->array_ptr + @@ -52523,12 +54246,13 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { /* make sort stable: compare array offsets */ cmp = (a_idx > b_idx) - (a_idx < b_idx); } - if (validate_typed_array(ctx, psc->arr) < 0) { - psc->exception = 1; + if (unlikely(typed_array_is_detached(ctx, + JS_VALUE_GET_PTR(psc->arr)))) { + psc->exception = 2; } done: - JS_FreeValue(ctx, (JSValue)argv[0]); - JS_FreeValue(ctx, (JSValue)argv[1]); + JS_FreeValue(ctx, argv[0]); + JS_FreeValue(ctx, argv[1]); } return cmp; } @@ -52548,11 +54272,11 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.arr = this_val; tsc.cmp = argv[0]; + if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) + return JS_EXCEPTION; len = js_typed_array_get_length_internal(ctx, this_val); if (len < 0) return JS_EXCEPTION; - if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) - return JS_EXCEPTION; if (len > 1) { p = JS_VALUE_GET_OBJ(this_val); @@ -52590,6 +54314,10 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.getfun = js_TA_get_uint64; cmpfun = js_TA_cmp_uint64; break; + case JS_CLASS_FLOAT16_ARRAY: + tsc.getfun = js_TA_get_float16; + cmpfun = js_TA_cmp_float16; + break; case JS_CLASS_FLOAT32_ARRAY: tsc.getfun = js_TA_get_float32; cmpfun = js_TA_cmp_float32; @@ -52607,7 +54335,7 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, uint32_t *array_idx; void *array_tmp; size_t i, j; - + /* XXX: a stable sort would use less memory */ array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); if (!array_idx) @@ -52618,44 +54346,48 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.elt_size = elt_size; rqsort(array_idx, len, sizeof(array_idx[0]), js_TA_cmp_generic, &tsc); - if (tsc.exception) - goto fail; - array_tmp = js_malloc(ctx, len * elt_size); - if (!array_tmp) { - fail: - js_free(ctx, array_idx); - return JS_EXCEPTION; - } - memcpy(array_tmp, array_ptr, len * elt_size); - switch(elt_size) { - case 1: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; - } - break; - case 2: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; - } - break; - case 4: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; + if (tsc.exception) { + if (tsc.exception == 1) + goto fail; + /* detached typed array during the sort: no error */ + } else { + array_tmp = js_malloc(ctx, len * elt_size); + if (!array_tmp) { + fail: + js_free(ctx, array_idx); + return JS_EXCEPTION; } - break; - case 8: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; + memcpy(array_tmp, array_ptr, len * elt_size); + switch(elt_size) { + case 1: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; + } + break; + case 2: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; + } + break; + case 4: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; + } + break; + case 8: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; + } + break; + default: + abort(); } - break; - default: - abort(); + js_free(ctx, array_tmp); } - js_free(ctx, array_tmp); js_free(ctx, array_idx); } else { rqsort(array_ptr, len, elt_size, cmpfun, &tsc); @@ -52666,6 +54398,24 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, return JS_DupValue(ctx, this_val); } +static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, ret; + JSObject *p; + + p = get_typed_array(ctx, this_val, /*is_dataview*/0); + if (!p) + return JS_EXCEPTION; + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id); + if (JS_IsException(arr)) + return JS_EXCEPTION; + ret = js_typed_array_sort(ctx, arr, argc, argv); + JS_FreeValue(ctx, arr); + return ret; +} + static const JSCFunctionListEntry js_typed_array_base_funcs[] = { JS_CFUNC_DEF("from", 1, js_typed_array_from ), JS_CFUNC_DEF("of", 0, js_typed_array_of ), @@ -52678,6 +54428,7 @@ static const JSCFunctionListEntry js_typed_array_base_funcs[] = { static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), JS_CFUNC_DEF("at", 1, js_typed_array_at ), + JS_CFUNC_DEF("with", 2, js_typed_array_with ), JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ), JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ), JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ), @@ -52696,14 +54447,16 @@ static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), - JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, special_find ), - JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, special_findIndex ), - JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, special_findLast ), - JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, special_findLastIndex ), + JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ), + JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ), + JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ), JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ), + JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ), JS_CFUNC_DEF("slice", 2, js_typed_array_slice ), JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), JS_CFUNC_DEF("sort", 1, js_typed_array_sort ), + JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ), JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ), JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ), JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ), @@ -52771,10 +54524,8 @@ static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); if (JS_IsException(val)) goto fail; - if (done) { - JS_FreeValue(ctx, val); + if (done) break; - } if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) goto fail; k++; @@ -52975,7 +54726,7 @@ static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) if (ta) { /* during the GC the finalizers are called in an arbitrary order so the ArrayBuffer finalizer may have been called */ - if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) { + if (ta->link.next) { list_del(&ta->link); } JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); @@ -53058,7 +54809,8 @@ static JSValue js_dataview_getValue(JSContext *ctx, { JSTypedArray *ta; JSArrayBuffer *abuf; - int is_swap, size; + BOOL littleEndian, is_swap; + int size; uint8_t *ptr; uint32_t v; uint64_t pos; @@ -53069,12 +54821,8 @@ static JSValue js_dataview_getValue(JSContext *ctx, size = 1 << typed_array_size_log2(class_id); if (JS_ToIndex(ctx, &pos, argv[0])) return JS_EXCEPTION; - is_swap = FALSE; - if (argc > 1) - is_swap = JS_ToBool(ctx, argv[1]); -#ifndef WORDS_BIGENDIAN - is_swap ^= 1; -#endif + littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]); + is_swap = littleEndian ^ !is_be(); abuf = ta->buffer->u.array_buffer; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); @@ -53125,6 +54873,14 @@ static JSValue js_dataview_getValue(JSContext *ctx, return JS_NewBigUint64(ctx, v); } break; + case JS_CLASS_FLOAT16_ARRAY: + { + uint16_t v; + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return __JS_NewFloat64(ctx, fromfp16(v)); + } case JS_CLASS_FLOAT32_ARRAY: { union { @@ -53159,7 +54915,8 @@ static JSValue js_dataview_setValue(JSContext *ctx, { JSTypedArray *ta; JSArrayBuffer *abuf; - int is_swap, size; + BOOL littleEndian, is_swap; + int size; uint8_t *ptr; uint64_t v64; uint32_t v; @@ -53185,7 +54942,9 @@ static JSValue js_dataview_setValue(JSContext *ctx, double d; if (JS_ToFloat64(ctx, &d, val)) return JS_EXCEPTION; - if (class_id == JS_CLASS_FLOAT32_ARRAY) { + if (class_id == JS_CLASS_FLOAT16_ARRAY) { + v = tofp16(d); + } else if (class_id == JS_CLASS_FLOAT32_ARRAY) { union { float f; uint32_t i; @@ -53198,12 +54957,8 @@ static JSValue js_dataview_setValue(JSContext *ctx, v64 = u.u64; } } - is_swap = FALSE; - if (argc > 2) - is_swap = JS_ToBool(ctx, argv[2]); -#ifndef WORDS_BIGENDIAN - is_swap ^= 1; -#endif + littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]); + is_swap = littleEndian ^ !is_be(); abuf = ta->buffer->u.array_buffer; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); @@ -53218,6 +54973,7 @@ static JSValue js_dataview_setValue(JSContext *ctx, break; case JS_CLASS_INT16_ARRAY: case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: if (is_swap) v = bswap16(v); put_u16(ptr, v); @@ -53254,6 +55010,7 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("getFloat16", 1, js_dataview_getValue, JS_CLASS_FLOAT16_ARRAY ), JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), @@ -53264,6 +55021,7 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setFloat16", 2, js_dataview_setValue, JS_CLASS_FLOAT16_ARRAY ), JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), @@ -53326,6 +55084,11 @@ static void *js_atomics_get_ptr(JSContext *ctx, if (JS_ToIndex(ctx, &idx, idx_val)) { return NULL; } + /* RevalidateAtomicAccess(): must test again detached after JS_ToIndex() */ + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return NULL; + } /* if the array buffer is detached, p->u.array.count = 0 */ if (idx >= p->u.array.count) { JS_ThrowRangeError(ctx, "out-of-bound access"); @@ -53387,7 +55150,7 @@ static JSValue js_atomics_op(JSContext *ctx, } switch(op | (size_log2 << 3)) { - + #define OP(op_name, func_name) \ case ATOMICS_OP_ ## op_name | (0 << 3): \ a = func_name((_Atomic(uint8_t) *)ptr, v); \ @@ -53401,7 +55164,7 @@ static JSValue js_atomics_op(JSContext *ctx, case ATOMICS_OP_ ## op_name | (3 << 3): \ a = func_name((_Atomic(uint64_t) *)ptr, v); \ break; - + OP(ADD, atomic_fetch_add) OP(AND, atomic_fetch_and) OP(OR, atomic_fetch_or) @@ -53422,7 +55185,7 @@ static JSValue js_atomics_op(JSContext *ctx, case ATOMICS_OP_LOAD | (3 << 3): a = atomic_load((_Atomic(uint64_t) *)ptr); break; - + case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): { uint8_t v1 = v; @@ -53502,7 +55265,7 @@ static JSValue js_atomics_store(JSContext *ctx, return JS_EXCEPTION; if (size_log2 == 3) { int64_t v64; - ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); + ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2])); if (JS_IsException(ret)) return ret; if (JS_ToBigInt64(ctx, &v64, ret)) { @@ -53583,14 +55346,15 @@ static JSValue js_atomics_wait(JSContext *ctx, if (size_log2 == 3) { if (JS_ToBigInt64(ctx, &v, argv[2])) return JS_EXCEPTION; - } else { + } else { if (JS_ToInt32(ctx, &v32, argv[2])) return JS_EXCEPTION; v = v32; } if (JS_ToFloat64(ctx, &d, argv[3])) return JS_EXCEPTION; - if (isnan(d) || d > INT64_MAX) + /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ + if (isnan(d) || d >= 0x1p63) timeout = INT64_MAX; else if (d < 0) timeout = 0; @@ -53761,13 +55525,15 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, - "TypedArray", 0); + typed_array_base_func = JS_NewCFunction2(ctx, js_typed_array_base_constructor, + "TypedArray", 0, JS_CFUNC_constructor_or_func, 0); JS_SetPropertyFunctionList(ctx, typed_array_base_func, js_typed_array_base_funcs, countof(js_typed_array_base_funcs)); JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_typed_array_constructor }; for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { JSValue func_obj; char buf[ATOM_GET_STR_BUF_SIZE]; @@ -53780,7 +55546,7 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) 0); name = JS_AtomGetStr(ctx, buf, sizeof(buf), JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); - func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor, + func_obj = JS_NewCFunction3(ctx, ft.generic, name, 3, JS_CFUNC_constructor_magic, i, typed_array_base_func); JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); @@ -53806,13 +55572,279 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) #endif } -JSClassID JS_GetClassID(JSValueConst v) +/* WeakRef */ + +typedef struct JSWeakRefData { + JSWeakRefHeader weakref_header; + JSValue target; +} JSWeakRefData; + +static void js_weakref_finalizer(JSRuntime *rt, JSValue val) { - JSObject *p; + JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF); + if (!wrd) + return; + js_weakref_free(rt, wrd->target); + list_del(&wrd->weakref_header.link); + js_free_rt(rt, wrd); +} - if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) - return 0; - p = JS_VALUE_GET_OBJ(v); - assert(p != 0); - return p->class_id; +static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh) +{ + JSWeakRefData *wrd = container_of(wh, JSWeakRefData, weakref_header); + + if (!js_weakref_is_live(wrd->target)) { + js_weakref_free(rt, wrd->target); + wrd->target = JS_UNDEFINED; + } +} + +static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValueConst arg; + JSValue obj; + + if (JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "constructor requires 'new'"); + arg = argv[0]; + if (!js_weakref_is_target(arg)) + return JS_ThrowTypeError(ctx, "invalid target"); + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JSWeakRefData *wrd = js_mallocz(ctx, sizeof(*wrd)); + if (!wrd) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + wrd->target = js_weakref_new(ctx, arg); + wrd->weakref_header.weakref_type = JS_WEAKREF_TYPE_WEAKREF; + list_add_tail(&wrd->weakref_header.link, &ctx->rt->weakref_list); + JS_SetOpaque(obj, wrd); + return obj; +} + +static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ + JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF); + if (!wrd) + return JS_EXCEPTION; + if (js_weakref_is_live(wrd->target)) + return JS_DupValue(ctx, wrd->target); + else + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_weakref_proto_funcs[] = { + JS_CFUNC_DEF("deref", 0, js_weakref_deref ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ), +}; + +static const JSClassShortDef js_weakref_class_def[] = { + { JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */ +}; + +typedef struct JSFinRecEntry { + struct list_head link; + JSValue target; + JSValue held_val; + JSValue token; +} JSFinRecEntry; + +typedef struct JSFinalizationRegistryData { + JSWeakRefHeader weakref_header; + struct list_head entries; /* list of JSFinRecEntry.link */ + JSContext *ctx; + JSValue cb; +} JSFinalizationRegistryData; + +static void js_finrec_finalizer(JSRuntime *rt, JSValue val) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); + if (frd) { + struct list_head *el, *el1; + list_for_each_safe(el, el1, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + js_weakref_free(rt, fre->target); + js_weakref_free(rt, fre->token); + JS_FreeValueRT(rt, fre->held_val); + js_free_rt(rt, fre); + } + JS_FreeValueRT(rt, frd->cb); + list_del(&frd->weakref_header.link); + js_free_rt(rt, frd); + } +} + +static void js_finrec_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); + struct list_head *el; + if (frd) { + list_for_each(el, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + JS_MarkValue(rt, fre->held_val, mark_func); + } + JS_MarkValue(rt, frd->cb, mark_func); + } +} + +static JSValue js_finrec_job(JSContext *ctx, int argc, JSValueConst *argv) +{ + return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]); +} + +static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh) +{ + JSFinalizationRegistryData *frd = container_of(wh, JSFinalizationRegistryData, weakref_header); + struct list_head *el, *el1; + + list_for_each_safe(el, el1, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + + if (!js_weakref_is_live(fre->token)) { + js_weakref_free(rt, fre->token); + fre->token = JS_UNDEFINED; + } + + if (!js_weakref_is_live(fre->target)) { + JSValueConst args[2]; + args[0] = frd->cb; + args[1] = fre->held_val; + JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args); + + js_weakref_free(rt, fre->target); + js_weakref_free(rt, fre->token); + JS_FreeValueRT(rt, fre->held_val); + list_del(&fre->link); + js_free_rt(rt, fre); + } + } +} + +static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValueConst cb; + JSValue obj; + JSFinalizationRegistryData *frd; + + if (JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "constructor requires 'new'"); + cb = argv[0]; + if (!JS_IsFunction(ctx, cb)) + return JS_ThrowTypeError(ctx, "argument must be a function"); + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY); + if (JS_IsException(obj)) + return JS_EXCEPTION; + frd = js_mallocz(ctx, sizeof(*frd)); + if (!frd) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + frd->weakref_header.weakref_type = JS_WEAKREF_TYPE_FINREC; + list_add_tail(&frd->weakref_header.link, &ctx->rt->weakref_list); + init_list_head(&frd->entries); + frd->ctx = ctx; /* XXX: JS_DupContext() ? */ + frd->cb = JS_DupValue(ctx, cb); + JS_SetOpaque(obj, frd); + return obj; +} + +static JSValue js_finrec_register(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst target, held_val, token; + JSFinalizationRegistryData *frd; + JSFinRecEntry *fre; + + frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); + if (!frd) + return JS_EXCEPTION; + target = argv[0]; + held_val = argv[1]; + token = argc > 2 ? argv[2] : JS_UNDEFINED; + + if (!js_weakref_is_target(target)) + return JS_ThrowTypeError(ctx, "invalid target"); + if (js_same_value(ctx, target, held_val)) + return JS_ThrowTypeError(ctx, "held value cannot be the target"); + if (!JS_IsUndefined(token) && !js_weakref_is_target(token)) + return JS_ThrowTypeError(ctx, "invalid unregister token"); + fre = js_malloc(ctx, sizeof(*fre)); + if (!fre) + return JS_EXCEPTION; + fre->target = js_weakref_new(ctx, target); + fre->held_val = JS_DupValue(ctx, held_val); + fre->token = js_weakref_new(ctx, token); + list_add_tail(&fre->link, &frd->entries); + return JS_UNDEFINED; +} + +static JSValue js_finrec_unregister(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); + JSValueConst token; + BOOL removed; + struct list_head *el, *el1; + + if (!frd) + return JS_EXCEPTION; + token = argv[0]; + if (!js_weakref_is_target(token)) + return JS_ThrowTypeError(ctx, "invalid unregister token"); + + removed = FALSE; + list_for_each_safe(el, el1, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + if (js_weakref_is_live(fre->token) && js_same_value(ctx, fre->token, token)) { + js_weakref_free(ctx->rt, fre->target); + js_weakref_free(ctx->rt, fre->token); + JS_FreeValue(ctx, fre->held_val); + list_del(&fre->link); + js_free(ctx, fre); + removed = TRUE; + } + } + return JS_NewBool(ctx, removed); +} + +static const JSCFunctionListEntry js_finrec_proto_funcs[] = { + JS_CFUNC_DEF("register", 2, js_finrec_register ), + JS_CFUNC_DEF("unregister", 1, js_finrec_unregister ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "FinalizationRegistry", JS_PROP_CONFIGURABLE ), +}; + +static const JSClassShortDef js_finrec_class_def[] = { + { JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */ +}; + +void JS_AddIntrinsicWeakRef(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + + /* WeakRef */ + if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) { + init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF, + countof(js_weakref_class_def)); + } + ctx->class_proto[JS_CLASS_WEAK_REF] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_WEAK_REF], + js_weakref_proto_funcs, + countof(js_weakref_proto_funcs)); + JS_NewGlobalCConstructor(ctx, "WeakRef", js_weakref_constructor, 1, ctx->class_proto[JS_CLASS_WEAK_REF]); + + /* FinalizationRegistry */ + if (!JS_IsRegisteredClass(rt, JS_CLASS_FINALIZATION_REGISTRY)) { + init_class_range(rt, js_finrec_class_def, JS_CLASS_FINALIZATION_REGISTRY, + countof(js_finrec_class_def)); + } + ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY], + js_finrec_proto_funcs, + countof(js_finrec_proto_funcs)); + JS_NewGlobalCConstructor(ctx, "FinalizationRegistry", js_finrec_constructor, 1, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY]); } diff --git a/quickjs/quickjs.h b/quickjs/quickjs.h index 296137e741..ce3350e00a 100644 --- a/quickjs/quickjs.h +++ b/quickjs/quickjs.h @@ -27,6 +27,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -48,7 +49,6 @@ extern "C" { typedef struct JSRuntime JSRuntime; typedef struct JSContext JSContext; -typedef struct JSObject JSObject; typedef struct JSClass JSClass; typedef uint32_t JSClassID; typedef uint32_t JSAtom; @@ -64,14 +64,21 @@ typedef uint32_t JSAtom; #define JS_NAN_BOXING #endif +#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX) +#define JS_LIMB_BITS 64 +#else +#define JS_LIMB_BITS 32 +#endif + +#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS + enum { /* all tags with a reference count are negative */ - JS_TAG_FIRST = -11, /* first negative tag */ - JS_TAG_BIG_DECIMAL = -11, - JS_TAG_BIG_INT = -10, - JS_TAG_BIG_FLOAT = -9, + JS_TAG_FIRST = -9, /* first negative tag */ + JS_TAG_BIG_INT = -9, JS_TAG_SYMBOL = -8, JS_TAG_STRING = -7, + JS_TAG_STRING_ROPE = -6, JS_TAG_MODULE = -3, /* used internally */ JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ JS_TAG_OBJECT = -1, @@ -83,7 +90,8 @@ enum { JS_TAG_UNINITIALIZED = 4, JS_TAG_CATCH_OFFSET = 5, JS_TAG_EXCEPTION = 6, - JS_TAG_FLOAT64 = 7, + JS_TAG_SHORT_BIG_INT = 7, + JS_TAG_FLOAT64 = 8, /* any larger tag is FLOAT64 if JS_NAN_BOXING */ }; @@ -108,6 +116,7 @@ typedef const struct __JSValue *JSValueConst; #define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) #define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) #define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v) #define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) #define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) @@ -126,7 +135,12 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { return 0; } - + +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d) +{ + return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d); +} + #elif defined(JS_NAN_BOXING) typedef uint64_t JSValue; @@ -136,6 +150,7 @@ typedef uint64_t JSValue; #define JS_VALUE_GET_TAG(v) (int)((v) >> 32) #define JS_VALUE_GET_INT(v) (int)(v) #define JS_VALUE_GET_BOOL(v) (int)(v) +#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v) #define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) #define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) @@ -191,13 +206,23 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) tag = JS_VALUE_GET_TAG(v); return tag == (JS_NAN >> 32); } - + +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d) +{ + return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d); +} + #else /* !JS_NAN_BOXING */ typedef union JSValueUnion { int32_t int32; double float64; void *ptr; +#if JS_SHORT_BIG_INT_BITS == 32 + int32_t short_big_int; +#else + int64_t short_big_int; +#endif } JSValueUnion; typedef struct JSValue { @@ -213,14 +238,24 @@ typedef struct JSValue { #define JS_VALUE_GET_INT(v) ((v).u.int32) #define JS_VALUE_GET_BOOL(v) ((v).u.int32) #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int) #define JS_VALUE_GET_PTR(v) ((v).u.ptr) +#ifdef __cplusplus +#define JS_MKVAL(tag, val) JSValue{ JSValueUnion{ .int32 = val }, tag } +#define JS_MKPTR(tag, p) JSValue{ JSValueUnion{ .ptr = p }, tag } + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +#define JS_NAN JSValue{ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } +#else // __cplusplus #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } #define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } +#endif // __cplusplus static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) { @@ -242,13 +277,19 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; } +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int64_t d) +{ + JSValue v; + v.tag = JS_TAG_SHORT_BIG_INT; + v.u.short_big_int = d; + return v; +} + #endif /* !JS_NAN_BOXING */ #define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) -#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) #define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) /* special values */ @@ -290,7 +331,9 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) #define JS_PROP_NO_ADD (1 << 16) /* internal use */ #define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ -#define JS_DEFAULT_STACK_SIZE (256 * 1024) +#ifndef JS_DEFAULT_STACK_SIZE +#define JS_DEFAULT_STACK_SIZE (1024 * 1024) +#endif /* JS_Eval() flags */ #define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ @@ -300,13 +343,15 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) #define JS_EVAL_TYPE_MASK (3 << 0) #define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ -#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ /* compile but do not run. The result is an object with a JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed with JS_EvalFunction(). */ #define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* don't include the stack frames before this eval in the Error() backtraces */ #define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) +/* allow top-level await in normal script. JS_Eval() returns a + promise. Only allowed with JS_EVAL_TYPE_GLOBAL */ +#define JS_EVAL_FLAG_ASYNC (1 << 7) typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); @@ -370,13 +415,7 @@ void JS_AddIntrinsicProxy(JSContext *ctx); void JS_AddIntrinsicMapSet(JSContext *ctx); void JS_AddIntrinsicTypedArrays(JSContext *ctx); void JS_AddIntrinsicPromise(JSContext *ctx); -void JS_AddIntrinsicBigInt(JSContext *ctx); -void JS_AddIntrinsicBigFloat(JSContext *ctx); -void JS_AddIntrinsicBigDecimal(JSContext *ctx); -/* enable operator overloading */ -void JS_AddIntrinsicOperators(JSContext *ctx); -/* enable "use math" */ -void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); +void JS_AddIntrinsicWeakRef(JSContext *ctx); JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); @@ -426,7 +465,11 @@ void JS_FreeAtom(JSContext *ctx, JSAtom v); void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); +const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom); +static inline const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +{ + return JS_AtomToCStringLen(ctx, NULL, atom); +} JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); /* object class support */ @@ -471,6 +514,17 @@ typedef struct JSClassExoticMethods { /* return < 0 if exception or TRUE/FALSE */ int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags); + + /* To get a consistent object behavior when get_prototype != NULL, + get_property, set_property and set_prototype must be != NULL + and the object must be created with a JS_NULL prototype. */ + JSValue (*get_prototype)(JSContext *ctx, JSValueConst obj); + /* return < 0 if exception or TRUE/FALSE */ + int (*set_prototype)(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); + /* return < 0 if exception or TRUE/FALSE */ + int (*is_extensible)(JSContext *ctx, JSValueConst obj); + /* return < 0 if exception or TRUE/FALSE */ + int (*prevent_extensions)(JSContext *ctx, JSValueConst obj); } JSClassExoticMethods; typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); @@ -496,8 +550,10 @@ typedef struct JSClassDef { JSClassExoticMethods *exotic; } JSClassDef; +#define JS_INVALID_CLASS_ID 0 JSClassID JS_NewClassID(JSClassID *pclass_id); -JSClassID JS_GetClassID(JSValueConst v); +/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */ +JSClassID JS_GetClassID(JSValue v); int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); @@ -545,23 +601,21 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) { - JSValue v; int32_t val; union { double d; uint64_t u; } u, t; - u.d = d; - val = (int32_t)d; - t.d = val; - /* -0 cannot be represented as integer, so we compare the bit - representation */ - if (u.u == t.u) { - v = JS_MKVAL(JS_TAG_INT, val); - } else { - v = __JS_NewFloat64(ctx, d); + if (d >= INT32_MIN && d <= INT32_MAX) { + u.d = d; + val = (int32_t)d; + t.d = val; + /* -0 cannot be represented as integer, so we compare the bit + representation */ + if (u.u == t.u) + return JS_MKVAL(JS_TAG_INT, val); } - return v; + return __JS_NewFloat64(ctx, d); } static inline JS_BOOL JS_IsNumber(JSValueConst v) @@ -573,19 +627,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v) static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) { int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_INT; -} - -static inline JS_BOOL JS_IsBigFloat(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_FLOAT; -} - -static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_DECIMAL; + return tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT; } static inline JS_BOOL JS_IsBool(JSValueConst v) @@ -615,7 +657,8 @@ static inline JS_BOOL JS_IsUninitialized(JSValueConst v) static inline JS_BOOL JS_IsString(JSValueConst v) { - return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING || + JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE; } static inline JS_BOOL JS_IsSymbol(JSValueConst v) @@ -629,9 +672,10 @@ static inline JS_BOOL JS_IsObject(JSValueConst v) } JSValue JS_Throw(JSContext *ctx, JSValue obj); +void JS_SetUncatchableException(JSContext *ctx, JS_BOOL flag); JSValue JS_GetException(JSContext *ctx); +JS_BOOL JS_HasException(JSContext *ctx); JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); -void JS_ResetUncatchableError(JSContext *ctx); JSValue JS_NewError(JSContext *ctx); JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); @@ -667,7 +711,7 @@ static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); p->ref_count++; } - return (JSValue)v; + return v; } static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) @@ -676,9 +720,13 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); p->ref_count++; } - return (JSValue)v; + return v; } +JS_BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2); + int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) @@ -694,7 +742,10 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); -JSValue JS_NewString(JSContext *ctx, const char *str); +static inline JSValue JS_NewString(JSContext *ctx, const char *str) +{ + return JS_NewStringLen(ctx, str, strlen(str)); +} JSValue JS_NewAtomString(JSContext *ctx, const char *str); JSValue JS_ToString(JSContext *ctx, JSValueConst val); JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); @@ -721,6 +772,8 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val) JSValue JS_NewArray(JSContext *ctx); int JS_IsArray(JSContext *ctx, JSValueConst val); +JSValue JS_NewDate(JSContext *ctx, double epoch_ms); + JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, JS_BOOL throw_ref_error); @@ -765,6 +818,8 @@ JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj, int flags); +void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len); int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj, JSAtom prop); @@ -802,6 +857,7 @@ int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, void JS_SetOpaque(JSValue obj, void *opaque); void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); +void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id); /* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, @@ -819,6 +875,24 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); + +typedef enum JSTypedArrayEnum { + JS_TYPED_ARRAY_UINT8C = 0, + JS_TYPED_ARRAY_INT8, + JS_TYPED_ARRAY_UINT8, + JS_TYPED_ARRAY_INT16, + JS_TYPED_ARRAY_UINT16, + JS_TYPED_ARRAY_INT32, + JS_TYPED_ARRAY_UINT32, + JS_TYPED_ARRAY_BIG_INT64, + JS_TYPED_ARRAY_BIG_UINT64, + JS_TYPED_ARRAY_FLOAT16, + JS_TYPED_ARRAY_FLOAT32, + JS_TYPED_ARRAY_FLOAT64, +} JSTypedArrayEnum; + +JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, + JSTypedArrayEnum array_type); JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, size_t *pbyte_offset, size_t *pbyte_length, @@ -832,7 +906,15 @@ typedef struct { void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, const JSSharedArrayBufferFunctions *sf); +typedef enum JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); +JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise); +JSValue JS_PromiseResult(JSContext *ctx, JSValue promise); /* is_handled = TRUE means that the rejection is handled */ typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, @@ -846,6 +928,12 @@ typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); /* if can_block is TRUE, Atomics.wait() can be used */ void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); +/* select which debug info is stripped from the compiled code */ +#define JS_STRIP_SOURCE (1 << 0) /* strip source code */ +#define JS_STRIP_DEBUG (1 << 1) /* strip all debug info including source code */ +void JS_SetStripInfo(JSRuntime *rt, int flags); +int JS_GetStripInfo(JSRuntime *rt); + /* set the [IsHTMLDDA] internal slot */ void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); @@ -858,15 +946,29 @@ typedef char *JSModuleNormalizeFunc(JSContext *ctx, const char *module_name, void *opaque); typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, const char *module_name, void *opaque); - +typedef JSModuleDef *JSModuleLoaderFunc2(JSContext *ctx, + const char *module_name, void *opaque, + JSValueConst attributes); +/* return -1 if exception, 0 if OK */ +typedef int JSModuleCheckSupportedImportAttributes(JSContext *ctx, void *opaque, + JSValueConst attributes); + /* module_normalize = NULL is allowed and invokes the default module filename normalizer */ void JS_SetModuleLoaderFunc(JSRuntime *rt, JSModuleNormalizeFunc *module_normalize, JSModuleLoaderFunc *module_loader, void *opaque); +/* same as JS_SetModuleLoaderFunc but with attributes. if + module_check_attrs = NULL, no attribute checking is done. */ +void JS_SetModuleLoaderFunc2(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc2 *module_loader, + JSModuleCheckSupportedImportAttributes *module_check_attrs, + void *opaque); /* return the import.meta object of a module */ JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); +JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m); /* JS Job support */ @@ -904,8 +1006,8 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj); /* only exported for os.Worker() */ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels); /* only exported for os.Worker() */ -JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, - const char *filename); +JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename); /* C function definition */ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ @@ -959,7 +1061,7 @@ static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *fun { return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); } -void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, +void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, JSValueConst proto); /* C property definition */ @@ -1021,9 +1123,9 @@ typedef struct JSCFunctionListEntry { #define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } #define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } -void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, - const JSCFunctionListEntry *tab, - int len); +int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, + const JSCFunctionListEntry *tab, + int len); /* C module definition */ @@ -1040,6 +1142,29 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, JSValue val); int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, const JSCFunctionListEntry *tab, int len); +/* associate a JSValue to a C module */ +int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val); +JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m); + +/* debug value output */ + +typedef struct { + JS_BOOL show_hidden : 8; /* only show enumerable properties */ + JS_BOOL raw_dump : 8; /* avoid doing autoinit and avoid any malloc() call (for internal use) */ + uint32_t max_depth; /* recurse up to this depth, 0 = no limit */ + uint32_t max_string_length; /* print no more than this length for + strings, 0 = no limit */ + uint32_t max_item_count; /* print no more than this count for + arrays or objects, 0 = no limit */ +} JSPrintValueOptions; + +typedef void JSPrintValueWrite(void *opaque, const char *buf, size_t len); + +void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options); +void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_opaque, + JSValueConst val, const JSPrintValueOptions *options); +void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque, + JSValueConst val, const JSPrintValueOptions *options); #undef js_unlikely #undef js_force_inline diff --git a/quickjs/win/dirent.h b/quickjs/win/dirent.h new file mode 100644 index 0000000000..077674e45c --- /dev/null +++ b/quickjs/win/dirent.h @@ -0,0 +1,1166 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n; +#else + /* WinRT */ + size_t n; +#endif + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ \ No newline at end of file diff --git a/quickjs/win/getopt.h b/quickjs/win/getopt.h new file mode 100644 index 0000000000..d78b753b2b --- /dev/null +++ b/quickjs/win/getopt.h @@ -0,0 +1,653 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + /* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma warning(disable:4996) + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +//extern int optind; /* index of first non-option in argv */ +//extern int optopt; /* single option character, as parsed */ +//extern int opterr; /* flag to enable built-in diagnostics... */ +// /* (user may set to zero, to suppress) */ +// +//extern char *optarg; /* pointer to argument of current option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +//extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (char*)strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +//extern int getopt_long(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ \ No newline at end of file diff --git a/quickjspp.hpp b/quickjspp.hpp index b1a2e3f25c..b158e92b0d 100644 --- a/quickjspp.hpp +++ b/quickjspp.hpp @@ -406,10 +406,10 @@ struct js_traits> case JS_TAG_BOOL: return is_boolean::value || std::is_integral_v || std::is_floating_point_v; - case JS_TAG_BIG_DECIMAL: - [[fallthrough]]; - case JS_TAG_BIG_FLOAT: - [[fallthrough]]; + //case JS_TAG_BIG_DECIMAL: + // [[fallthrough]]; + //case JS_TAG_BIG_FLOAT: + // [[fallthrough]]; case JS_TAG_FLOAT64: default: // >JS_TAG_FLOAT64 (JS_NAN_BOXING) return is_double::value || std::is_floating_point_v; @@ -474,10 +474,10 @@ struct js_traits> case JS_TAG_EXCEPTION: break; - case JS_TAG_BIG_DECIMAL: - [[fallthrough]]; - case JS_TAG_BIG_FLOAT: - [[fallthrough]]; + //case JS_TAG_BIG_DECIMAL: + // [[fallthrough]]; + //case JS_TAG_BIG_FLOAT: + // [[fallthrough]]; case JS_TAG_FLOAT64: [[fallthrough]]; @@ -908,7 +908,7 @@ struct js_traits> assert(pptr); const T * ptr = pptr->get(); assert(ptr); - for(Value T::* member : markOffsets) + for(Value (T::*member) : markOffsets) { JS_MarkValue(rt, (*ptr.*member).v, mark_func); }