diff options
author | bptato <nincsnevem662@gmail.com> | 2024-06-03 20:42:16 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-06-03 21:15:44 +0200 |
commit | 3aa8f1e0694d1606c3f3795f8b83e8a82caacd3e (patch) | |
tree | 9708d4599360116a96e4aa7f983eea387e8437c6 | |
parent | 3e12a95ab34e120fb958ba0eeebaada5def7cd11 (diff) | |
download | chawan-3aa8f1e0694d1606c3f3795f8b83e8a82caacd3e.tar.gz |
Move JS wrapper into Monoucha
Operation "modularize Chawan somewhat" part 3
71 files changed, 246 insertions, 81054 deletions
diff --git a/.gitmodules b/.gitmodules index 29bb3d1a..3789c9fe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/chagashi"] path = lib/chagashi url = https://git.sr.ht/~bptato/chagashi +[submodule "lib/monoucha"] + path = lib/monoucha + url = https://git.sr.ht/~bptato/monoucha diff --git a/Makefile b/Makefile index 616e6068..f4a816a2 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,6 @@ else ifeq ($(TARGET),release1) FLAGS += -d:release --debugger:native endif -QJSOBJ = $(OBJDIR)/quickjs - .PHONY: all all: $(OUTDIR_BIN)/cha $(OUTDIR_BIN)/mancha $(OUTDIR_CGI_BIN)/http \ $(OUTDIR_CGI_BIN)/gmifetch $(OUTDIR_LIBEXEC)/gmi2html \ @@ -56,8 +54,8 @@ all: $(OUTDIR_BIN)/cha $(OUTDIR_BIN)/mancha $(OUTDIR_CGI_BIN)/http \ $(OUTDIR_LIBEXEC)/urldec $(OUTDIR_LIBEXEC)/urlenc \ $(OUTDIR_LIBEXEC)/md2html $(OUTDIR_LIBEXEC)/ansi2html -$(OUTDIR_BIN)/cha: lib/libquickjs.a src/*.nim src/**/*.nim src/**/*.c res/* \ - res/**/* res/map/idna_gen.nim nim.cfg +$(OUTDIR_BIN)/cha: src/*.nim src/**/*.nim src/**/*.c res/* res/**/* \ + lib/**/* res/map/idna_gen.nim nim.cfg @mkdir -p "$(OUTDIR_BIN)" $(NIMC) --nimcache:"$(OBJDIR)/$(TARGET)/cha" -d:libexecPath=$(LIBEXECDIR) \ -d:disableSandbox=$(DANGER_DISABLE_SANDBOX) $(FLAGS) \ @@ -117,12 +115,11 @@ $(OUTDIR_CGI_BIN)/cha-finger: adapter/protocol/cha-finger @mkdir -p "$(OUTDIR_CGI_BIN)" cp adapter/protocol/cha-finger $(OUTDIR_CGI_BIN) -$(OUTDIR_CGI_BIN)/man: adapter/protocol/man.nim $(QJSOBJ)/libregexp.o \ - $(QJSOBJ)/libunicode.o $(QJSOBJ)/cutils.o src/js/jsregex.nim \ - src/bindings/libregexp.nim src/types/opt.nim src/utils/twtstr.nim +$(OUTDIR_CGI_BIN)/man: adapter/protocol/man.nim lib/monoucha/monoucha/jsregex.nim \ + lib/monoucha/monoucha/libregexp.nim src/types/opt.nim \ + src/utils/twtstr.nim @mkdir -p "$(OUTDIR_CGI_BIN)" $(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/man" \ - --passL:"$(QJSOBJ)/libregexp.o $(QJSOBJ)/cutils.o $(QJSOBJ)/libunicode.o" \ -o:"$(OUTDIR_CGI_BIN)/man" adapter/protocol/man.nim $(OUTDIR_CGI_BIN)/spartan: adapter/protocol/spartan @@ -180,30 +177,6 @@ $(OUTDIR_LIBEXEC)/urlenc: adapter/tools/urlenc.nim src/utils/twtstr.nim $(NIMC) $(FLAGS) --nimcache:"$(OBJDIR)/$(TARGET)/urlenc" \ -o:"$(OUTDIR_LIBEXEC)/urlenc" adapter/tools/urlenc.nim -CFLAGS = -fwrapv -g -Wall -O2 -DCONFIG_VERSION=\"$(shell cat lib/quickjs/VERSION)\" - -# Dependencies -$(QJSOBJ)/cutils.o: lib/quickjs/cutils.h -$(QJSOBJ)/libbf.o: lib/quickjs/cutils.h lib/quickjs/libbf.h -$(QJSOBJ)/libregexp.o: lib/quickjs/cutils.h lib/quickjs/libregexp.h \ - lib/quickjs/libunicode.h lib/quickjs/libregexp-opcode.h -$(QJSOBJ)/libunicode.o: lib/quickjs/cutils.h lib/quickjs/libunicode.h \ - lib/quickjs/libunicode-table.h -$(QJSOBJ)/quickjs.o: lib/quickjs/cutils.h lib/quickjs/list.h \ - lib/quickjs/quickjs.h lib/quickjs/libregexp.h \ - lib/quickjs/libunicode.h lib/quickjs/libbf.h \ - lib/quickjs/quickjs-atom.h lib/quickjs/quickjs-opcode.h - -$(QJSOBJ)/%.o: lib/quickjs/%.c - @mkdir -p "$(QJSOBJ)" - $(CC) $(CFLAGS) -c -o $@ $< - -lib/libquickjs.a: $(QJSOBJ)/quickjs.o $(QJSOBJ)/libregexp.o \ - $(QJSOBJ)/libunicode.o $(QJSOBJ)/cutils.o \ - $(QJSOBJ)/libbf.o - @mkdir -p "$(QJSOBJ)" - $(AR) rcs $@ $^ - $(OBJDIR)/man/cha-%.md: doc/%.md md2manpreproc @mkdir -p "$(OBJDIR)/man" ./md2manpreproc $< > $@ diff --git a/adapter/protocol/man.nim b/adapter/protocol/man.nim index 4d6de03f..50521ca1 100644 --- a/adapter/protocol/man.nim +++ b/adapter/protocol/man.nim @@ -3,15 +3,11 @@ import std/posix import std/strutils from std/unicode import runeLenAt -import bindings/libregexp -import js/jsregex +import monoucha/jsregex +import monoucha/libregexp import types/opt import utils/twtstr -proc lre_check_stack_overflow(opaque: pointer; alloca_size: csize_t): cint - {.exportc.} = - return 0 - proc parseSection(query: string): tuple[page, section: string] = var section = "" if query.len > 0 and query[^1] == ')': diff --git a/lib/monoucha b/lib/monoucha new file mode 160000 +Subproject 7b1607d5c6be2bc4736e79259ce1a3e684624b1 diff --git a/lib/quickjs/VERSION b/lib/quickjs/VERSION deleted file mode 100644 index e32e065c..00000000 --- a/lib/quickjs/VERSION +++ /dev/null @@ -1 +0,0 @@ -2024-02-14 diff --git a/lib/quickjs/cutils.c b/lib/quickjs/cutils.c deleted file mode 100644 index c0aacef6..00000000 --- a/lib/quickjs/cutils.c +++ /dev/null @@ -1,631 +0,0 @@ -/* - * C utilities - * - * Copyright (c) 2017 Fabrice Bellard - * Copyright (c) 2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> - -#include "cutils.h" - -void pstrcpy(char *buf, int buf_size, const char *str) -{ - int c; - char *q = buf; - - if (buf_size <= 0) - return; - - for(;;) { - c = *str++; - if (c == 0 || q >= buf + buf_size - 1) - break; - *q++ = c; - } - *q = '\0'; -} - -/* strcat and truncate. */ -char *pstrcat(char *buf, int buf_size, const char *s) -{ - int len; - len = strlen(buf); - if (len < buf_size) - pstrcpy(buf + len, buf_size - len, s); - return buf; -} - -int strstart(const char *str, const char *val, const char **ptr) -{ - const char *p, *q; - p = str; - q = val; - while (*q != '\0') { - if (*p != *q) - return 0; - p++; - q++; - } - if (ptr) - *ptr = p; - return 1; -} - -int has_suffix(const char *str, const char *suffix) -{ - size_t len = strlen(str); - size_t slen = strlen(suffix); - return (len >= slen && !memcmp(str + len - slen, suffix, slen)); -} - -/* Dynamic buffer package */ - -static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) -{ - memset(s, 0, sizeof(*s)); - if (!realloc_func) - realloc_func = dbuf_default_realloc; - s->opaque = opaque; - s->realloc_func = realloc_func; -} - -void dbuf_init(DynBuf *s) -{ - dbuf_init2(s, NULL, NULL); -} - -/* return < 0 if error */ -int dbuf_realloc(DynBuf *s, size_t new_size) -{ - size_t size; - uint8_t *new_buf; - if (new_size > s->allocated_size) { - if (s->error) - return -1; - size = s->allocated_size * 3 / 2; - if (size > new_size) - new_size = size; - new_buf = s->realloc_func(s->opaque, s->buf, new_size); - if (!new_buf) { - s->error = TRUE; - return -1; - } - s->buf = new_buf; - s->allocated_size = new_size; - } - return 0; -} - -int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len) -{ - size_t end; - end = offset + len; - if (dbuf_realloc(s, end)) - return -1; - memcpy(s->buf + offset, data, len); - if (end > s->size) - s->size = end; - return 0; -} - -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) -{ - if (unlikely((s->size + len) > s->allocated_size)) { - if (dbuf_realloc(s, s->size + len)) - return -1; - } - memcpy_no_ub(s->buf + s->size, data, len); - s->size += len; - return 0; -} - -int dbuf_put_self(DynBuf *s, size_t offset, size_t len) -{ - if (unlikely((s->size + len) > s->allocated_size)) { - if (dbuf_realloc(s, s->size + len)) - return -1; - } - memcpy(s->buf + s->size, s->buf + offset, len); - s->size += len; - return 0; -} - -int dbuf_putc(DynBuf *s, uint8_t c) -{ - return dbuf_put(s, &c, 1); -} - -int dbuf_putstr(DynBuf *s, const char *str) -{ - return dbuf_put(s, (const uint8_t *)str, strlen(str)); -} - -int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, - const char *fmt, ...) -{ - va_list ap; - char buf[128]; - int len; - - va_start(ap, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - if (len < sizeof(buf)) { - /* fast case */ - return dbuf_put(s, (uint8_t *)buf, len); - } else { - if (dbuf_realloc(s, s->size + len + 1)) - return -1; - va_start(ap, fmt); - vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, - fmt, ap); - va_end(ap); - s->size += len; - } - return 0; -} - -void dbuf_free(DynBuf *s) -{ - /* we test s->buf as a fail safe to avoid crashing if dbuf_free() - is called twice */ - if (s->buf) { - s->realloc_func(s->opaque, s->buf, 0); - } - memset(s, 0, sizeof(*s)); -} - -/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes - are output. */ -int unicode_to_utf8(uint8_t *buf, unsigned int c) -{ - uint8_t *q = buf; - - if (c < 0x80) { - *q++ = c; - } else { - if (c < 0x800) { - *q++ = (c >> 6) | 0xc0; - } else { - if (c < 0x10000) { - *q++ = (c >> 12) | 0xe0; - } else { - if (c < 0x00200000) { - *q++ = (c >> 18) | 0xf0; - } else { - if (c < 0x04000000) { - *q++ = (c >> 24) | 0xf8; - } else if (c < 0x80000000) { - *q++ = (c >> 30) | 0xfc; - *q++ = ((c >> 24) & 0x3f) | 0x80; - } else { - return 0; - } - *q++ = ((c >> 18) & 0x3f) | 0x80; - } - *q++ = ((c >> 12) & 0x3f) | 0x80; - } - *q++ = ((c >> 6) & 0x3f) | 0x80; - } - *q++ = (c & 0x3f) | 0x80; - } - return q - buf; -} - -static const unsigned int utf8_min_code[5] = { - 0x80, 0x800, 0x10000, 0x00200000, 0x04000000, -}; - -static const unsigned char utf8_first_code_mask[5] = { - 0x1f, 0xf, 0x7, 0x3, 0x1, -}; - -/* return -1 if error. *pp is not updated in this case. max_len must - be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */ -int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp) -{ - int l, c, b, i; - - c = *p++; - if (c < 0x80) { - *pp = p; - return c; - } - switch(c) { - case 0xc0: case 0xc1: case 0xc2: case 0xc3: - case 0xc4: case 0xc5: case 0xc6: case 0xc7: - case 0xc8: case 0xc9: case 0xca: case 0xcb: - case 0xcc: case 0xcd: case 0xce: case 0xcf: - case 0xd0: case 0xd1: case 0xd2: case 0xd3: - case 0xd4: case 0xd5: case 0xd6: case 0xd7: - case 0xd8: case 0xd9: case 0xda: case 0xdb: - case 0xdc: case 0xdd: case 0xde: case 0xdf: - l = 1; - break; - case 0xe0: case 0xe1: case 0xe2: case 0xe3: - case 0xe4: case 0xe5: case 0xe6: case 0xe7: - case 0xe8: case 0xe9: case 0xea: case 0xeb: - case 0xec: case 0xed: case 0xee: case 0xef: - l = 2; - break; - case 0xf0: case 0xf1: case 0xf2: case 0xf3: - case 0xf4: case 0xf5: case 0xf6: case 0xf7: - l = 3; - break; - case 0xf8: case 0xf9: case 0xfa: case 0xfb: - l = 4; - break; - case 0xfc: case 0xfd: - l = 5; - break; - default: - return -1; - } - /* check that we have enough characters */ - if (l > (max_len - 1)) - return -1; - c &= utf8_first_code_mask[l - 1]; - for(i = 0; i < l; i++) { - b = *p++; - if (b < 0x80 || b >= 0xc0) - return -1; - c = (c << 6) | (b & 0x3f); - } - if (c < utf8_min_code[l - 1]) - return -1; - *pp = p; - return c; -} - -#if 0 - -#if defined(EMSCRIPTEN) || defined(__ANDROID__) - -static void *rqsort_arg; -static int (*rqsort_cmp)(const void *, const void *, void *); - -static int rqsort_cmp2(const void *p1, const void *p2) -{ - return rqsort_cmp(p1, p2, rqsort_arg); -} - -/* not reentrant, but not needed with emscripten */ -void rqsort(void *base, size_t nmemb, size_t size, - int (*cmp)(const void *, const void *, void *), - void *arg) -{ - rqsort_arg = arg; - rqsort_cmp = cmp; - qsort(base, nmemb, size, rqsort_cmp2); -} - -#endif - -#else - -typedef void (*exchange_f)(void *a, void *b, size_t size); -typedef int (*cmp_f)(const void *, const void *, void *opaque); - -static void exchange_bytes(void *a, void *b, size_t size) { - uint8_t *ap = (uint8_t *)a; - uint8_t *bp = (uint8_t *)b; - - while (size-- != 0) { - uint8_t t = *ap; - *ap++ = *bp; - *bp++ = t; - } -} - -static void exchange_one_byte(void *a, void *b, size_t size) { - uint8_t *ap = (uint8_t *)a; - uint8_t *bp = (uint8_t *)b; - uint8_t t = *ap; - *ap = *bp; - *bp = t; -} - -static void exchange_int16s(void *a, void *b, size_t size) { - uint16_t *ap = (uint16_t *)a; - uint16_t *bp = (uint16_t *)b; - - for (size /= sizeof(uint16_t); size-- != 0;) { - uint16_t t = *ap; - *ap++ = *bp; - *bp++ = t; - } -} - -static void exchange_one_int16(void *a, void *b, size_t size) { - uint16_t *ap = (uint16_t *)a; - uint16_t *bp = (uint16_t *)b; - uint16_t t = *ap; - *ap = *bp; - *bp = t; -} - -static void exchange_int32s(void *a, void *b, size_t size) { - uint32_t *ap = (uint32_t *)a; - uint32_t *bp = (uint32_t *)b; - - for (size /= sizeof(uint32_t); size-- != 0;) { - uint32_t t = *ap; - *ap++ = *bp; - *bp++ = t; - } -} - -static void exchange_one_int32(void *a, void *b, size_t size) { - uint32_t *ap = (uint32_t *)a; - uint32_t *bp = (uint32_t *)b; - uint32_t t = *ap; - *ap = *bp; - *bp = t; -} - -static void exchange_int64s(void *a, void *b, size_t size) { - uint64_t *ap = (uint64_t *)a; - uint64_t *bp = (uint64_t *)b; - - for (size /= sizeof(uint64_t); size-- != 0;) { - uint64_t t = *ap; - *ap++ = *bp; - *bp++ = t; - } -} - -static void exchange_one_int64(void *a, void *b, size_t size) { - uint64_t *ap = (uint64_t *)a; - uint64_t *bp = (uint64_t *)b; - uint64_t t = *ap; - *ap = *bp; - *bp = t; -} - -static void exchange_int128s(void *a, void *b, size_t size) { - uint64_t *ap = (uint64_t *)a; - uint64_t *bp = (uint64_t *)b; - - for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { - uint64_t t = ap[0]; - uint64_t u = ap[1]; - ap[0] = bp[0]; - ap[1] = bp[1]; - bp[0] = t; - bp[1] = u; - } -} - -static void exchange_one_int128(void *a, void *b, size_t size) { - uint64_t *ap = (uint64_t *)a; - uint64_t *bp = (uint64_t *)b; - uint64_t t = ap[0]; - uint64_t u = ap[1]; - ap[0] = bp[0]; - ap[1] = bp[1]; - bp[0] = t; - bp[1] = u; -} - -static inline exchange_f exchange_func(const void *base, size_t size) { - switch (((uintptr_t)base | (uintptr_t)size) & 15) { - case 0: - if (size == sizeof(uint64_t) * 2) - return exchange_one_int128; - else - return exchange_int128s; - case 8: - if (size == sizeof(uint64_t)) - return exchange_one_int64; - else - return exchange_int64s; - case 4: - case 12: - if (size == sizeof(uint32_t)) - return exchange_one_int32; - else - return exchange_int32s; - case 2: - case 6: - case 10: - case 14: - if (size == sizeof(uint16_t)) - return exchange_one_int16; - else - return exchange_int16s; - default: - if (size == 1) - return exchange_one_byte; - else - return exchange_bytes; - } -} - -static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) -{ - uint8_t *basep = (uint8_t *)base; - size_t i, n, c, r; - exchange_f swap = exchange_func(base, size); - - if (nmemb > 1) { - i = (nmemb / 2) * size; - n = nmemb * size; - - while (i > 0) { - i -= size; - for (r = i; (c = r * 2 + size) < n; r = c) { - if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) - c += size; - if (cmp(basep + r, basep + c, opaque) > 0) - break; - swap(basep + r, basep + c, size); - } - } - for (i = n - size; i > 0; i -= size) { - swap(basep, basep + i, size); - - for (r = 0; (c = r * 2 + size) < i; r = c) { - if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) - c += size; - if (cmp(basep + r, basep + c, opaque) > 0) - break; - swap(basep + r, basep + c, size); - } - } - } -} - -static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) -{ - return cmp(a, b, opaque) < 0 ? - (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : - (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); -} - -/* pointer based version with local stack and insertion sort threshhold */ -void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) -{ - struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; - uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; - size_t m4, i, lt, gt, span, span2; - int c, depth; - exchange_f swap = exchange_func(base, size); - exchange_f swap_block = exchange_func(base, size | 128); - - if (nmemb < 2 || size <= 0) - return; - - sp->base = (uint8_t *)base; - sp->count = nmemb; - sp->depth = 0; - sp++; - - while (sp > stack) { - sp--; - ptr = sp->base; - nmemb = sp->count; - depth = sp->depth; - - while (nmemb > 6) { - if (++depth > 50) { - /* depth check to ensure worst case logarithmic time */ - heapsortx(ptr, nmemb, size, cmp, opaque); - nmemb = 0; - break; - } - /* select median of 3 from 1/4, 1/2, 3/4 positions */ - /* should use median of 5 or 9? */ - m4 = (nmemb >> 2) * size; - m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); - swap(ptr, m, size); /* move the pivot to the start or the array */ - i = lt = 1; - pi = plt = ptr + size; - gt = nmemb; - pj = pgt = top = ptr + nmemb * size; - for (;;) { - while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { - if (c == 0) { - swap(plt, pi, size); - lt++; - plt += size; - } - i++; - pi += size; - } - while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { - if (c == 0) { - gt--; - pgt -= size; - swap(pgt, pj, size); - } - } - if (pi >= pj) - break; - swap(pi, pj, size); - i++; - pi += size; - } - /* array has 4 parts: - * from 0 to lt excluded: elements identical to pivot - * from lt to pi excluded: elements smaller than pivot - * from pi to gt excluded: elements greater than pivot - * from gt to n excluded: elements identical to pivot - */ - /* move elements identical to pivot in the middle of the array: */ - /* swap values in ranges [0..lt[ and [i-lt..i[ - swapping the smallest span between lt and i-lt is sufficient - */ - span = plt - ptr; - span2 = pi - plt; - lt = i - lt; - if (span > span2) - span = span2; - swap_block(ptr, pi - span, span); - /* swap values in ranges [gt..top[ and [i..top-(top-gt)[ - swapping the smallest span between top-gt and gt-i is sufficient - */ - span = top - pgt; - span2 = pgt - pi; - pgt = top - span2; - gt = nmemb - (gt - i); - if (span > span2) - span = span2; - swap_block(pi, top - span, span); - - /* now array has 3 parts: - * from 0 to lt excluded: elements smaller than pivot - * from lt to gt excluded: elements identical to pivot - * from gt to n excluded: elements greater than pivot - */ - /* stack the larger segment and keep processing the smaller one - to minimize stack use for pathological distributions */ - if (lt > nmemb - gt) { - sp->base = ptr; - sp->count = lt; - sp->depth = depth; - sp++; - ptr = pgt; - nmemb -= gt; - } else { - sp->base = pgt; - sp->count = nmemb - gt; - sp->depth = depth; - sp++; - nmemb = lt; - } - } - /* Use insertion sort for small fragments */ - for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { - for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) - swap(pj, pj - size, size); - } - } -} - -#endif diff --git a/lib/quickjs/cutils.h b/lib/quickjs/cutils.h deleted file mode 100644 index f079e5c5..00000000 --- a/lib/quickjs/cutils.h +++ /dev/null @@ -1,347 +0,0 @@ -/* - * C utilities - * - * Copyright (c) 2017 Fabrice Bellard - * Copyright (c) 2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef CUTILS_H -#define CUTILS_H - -#include <stdlib.h> -#include <string.h> -#include <inttypes.h> - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#define force_inline inline __attribute__((always_inline)) -#define no_inline __attribute__((noinline)) -#define __maybe_unused __attribute__((unused)) - -#define xglue(x, y) x ## y -#define glue(x, y) xglue(x, y) -#define stringify(s) tostring(s) -#define tostring(s) #s - -#ifndef offsetof -#define offsetof(type, field) ((size_t) &((type *)0)->field) -#endif -#ifndef countof -#define countof(x) (sizeof(x) / sizeof((x)[0])) -#endif -#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; - -#ifndef FALSE -enum { - FALSE = 0, - TRUE = 1, -}; -#endif - -void pstrcpy(char *buf, int buf_size, const char *str); -char *pstrcat(char *buf, int buf_size, const char *s); -int strstart(const char *str, const char *val, const char **ptr); -int has_suffix(const char *str, const char *suffix); - -/* 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) - return a; - else - return b; -} - -static inline int min_int(int a, int b) -{ - if (a < b) - return a; - else - return b; -} - -static inline uint32_t max_uint32(uint32_t a, uint32_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline uint32_t min_uint32(uint32_t a, uint32_t b) -{ - if (a < b) - return a; - else - return b; -} - -static inline int64_t max_int64(int64_t a, int64_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline int64_t min_int64(int64_t a, int64_t b) -{ - if (a < b) - return a; - else - return b; -} - -/* WARNING: undefined if a = 0 */ -static inline int clz32(unsigned int a) -{ - return __builtin_clz(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int clz64(uint64_t a) -{ - return __builtin_clzll(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int ctz32(unsigned int a) -{ - return __builtin_ctz(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int ctz64(uint64_t a) -{ - return __builtin_ctzll(a); -} - -struct __attribute__((packed)) packed_u64 { - uint64_t v; -}; - -struct __attribute__((packed)) packed_u32 { - uint32_t v; -}; - -struct __attribute__((packed)) packed_u16 { - uint16_t v; -}; - -static inline uint64_t get_u64(const uint8_t *tab) -{ - return ((const struct packed_u64 *)tab)->v; -} - -static inline int64_t get_i64(const uint8_t *tab) -{ - return (int64_t)((const struct packed_u64 *)tab)->v; -} - -static inline void put_u64(uint8_t *tab, uint64_t val) -{ - ((struct packed_u64 *)tab)->v = val; -} - -static inline uint32_t get_u32(const uint8_t *tab) -{ - return ((const struct packed_u32 *)tab)->v; -} - -static inline int32_t get_i32(const uint8_t *tab) -{ - return (int32_t)((const struct packed_u32 *)tab)->v; -} - -static inline void put_u32(uint8_t *tab, uint32_t val) -{ - ((struct packed_u32 *)tab)->v = val; -} - -static inline uint32_t get_u16(const uint8_t *tab) -{ - return ((const struct packed_u16 *)tab)->v; -} - -static inline int32_t get_i16(const uint8_t *tab) -{ - return (int16_t)((const struct packed_u16 *)tab)->v; -} - -static inline void put_u16(uint8_t *tab, uint16_t val) -{ - ((struct packed_u16 *)tab)->v = val; -} - -static inline uint32_t get_u8(const uint8_t *tab) -{ - return *tab; -} - -static inline int32_t get_i8(const uint8_t *tab) -{ - return (int8_t)*tab; -} - -static inline void put_u8(uint8_t *tab, uint8_t val) -{ - *tab = val; -} - -#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)) | - ((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); - -typedef struct DynBuf { - uint8_t *buf; - size_t size; - size_t allocated_size; - BOOL error; /* true if a memory allocation error occurred */ - DynBufReallocFunc *realloc_func; - void *opaque; /* for realloc_func */ -} DynBuf; - -void dbuf_init(DynBuf *s); -void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); -int dbuf_realloc(DynBuf *s, size_t new_size); -int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); -int dbuf_put_self(DynBuf *s, size_t offset, size_t len); -int dbuf_putc(DynBuf *s, uint8_t c); -int dbuf_putstr(DynBuf *s, const char *str); -static inline int dbuf_put_u16(DynBuf *s, uint16_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 2); -} -static inline int dbuf_put_u32(DynBuf *s, uint32_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 4); -} -static inline int dbuf_put_u64(DynBuf *s, uint64_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 8); -} -int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, - const char *fmt, ...); -void dbuf_free(DynBuf *s); -static inline BOOL dbuf_error(DynBuf *s) { - return s->error; -} -static inline void dbuf_set_error(DynBuf *s) -{ - s->error = TRUE; -} - -#define UTF8_CHAR_LEN_MAX 6 - -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') - return c - '0'; - else if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - else - return -1; -} - -void rqsort(void *base, size_t nmemb, size_t size, - int (*cmp)(const void *, const void *, void *), - void *arg); - -#endif /* CUTILS_H */ diff --git a/lib/quickjs/libbf.c b/lib/quickjs/libbf.c deleted file mode 100644 index 05d62ed6..00000000 --- a/lib/quickjs/libbf.c +++ /dev/null @@ -1,8475 +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 <stdlib.h> -#include <stdio.h> -#include <inttypes.h> -#include <math.h> -#include <string.h> -#include <assert.h> - -#ifdef __AVX2__ -#include <immintrin.h> -#endif - -#include "cutils.h" -#include "libbf.h" - -/* enable it to check the multiplication result */ -//#define USE_MUL_CHECK -#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; -} - -#ifdef USE_BF_DEC -/* 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; - } -} -#endif - -/* 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_no_ub(r->tab, a->tab, a->len * sizeof(limb_t)); - return 0; -} - -/* equivalent to bf_set(r, a); bf_delete(a) */ -void bf_move(bf_t *r, bf_t *a) -{ - bf_context_t *s = r->ctx; - if (r == a) - return; - bf_free(s, r->tab); - *r = *a; -} - -static limb_t get_limbz(const bf_t *a, limb_t idx) -{ - if (idx >= a->len) - return 0; - else - return a->tab[idx]; -} - -/* get LIMB_BITS at bit position 'pos' in tab */ -static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) -{ - limb_t i, a0, a1; - int p; - - i = pos >> LIMB_LOG2_BITS; - p = pos & (LIMB_BITS - 1); - if (i < len) - a0 = tab[i]; - else - a0 = 0; - if (p == 0) { - return a0; - } else { - i++; - if (i < len) - a1 = tab[i]; - else - a1 = 0; - return (a0 >> p) | (a1 << (LIMB_BITS - p)); - } -} - -static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) -{ - slimb_t i; - i = pos >> LIMB_LOG2_BITS; - if (i < 0 || i >= len) - return 0; - return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; -} - -static inline limb_t limb_mask(int start, int last) -{ - limb_t v; - int n; - n = last - start + 1; - if (n == LIMB_BITS) - v = -1; - else - v = (((limb_t)1 << n) - 1) << start; - return v; -} - -static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) -{ - mp_size_t i; - for(i = 0; i < n; i++) { - if (tab[i] != 0) - return 1; - } - return 0; -} - -/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ -static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) -{ - slimb_t pos; - limb_t v; - - pos = bit_pos >> LIMB_LOG2_BITS; - if (pos < 0) - return 0; - v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); - if (v != 0) - return 1; - pos--; - while (pos >= 0) { - if (r->tab[pos] != 0) - return 1; - pos--; - } - return 0; -} - -/* return the addend for rounding. Note that prec can be <= 0 (for - BF_FLAG_RADPNT_PREC) */ -static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, - slimb_t prec, int rnd_mode) -{ - int add_one, inexact; - limb_t bit1, bit0; - - if (rnd_mode == BF_RNDF) { - bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ - } else { - /* starting limb for bit 'prec + 1' */ - bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); - } - - /* get the bit at 'prec' */ - bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); - inexact = (bit1 | bit0) != 0; - - add_one = 0; - switch(rnd_mode) { - case BF_RNDZ: - break; - case BF_RNDN: - if (bit1) { - if (bit0) { - add_one = 1; - } else { - /* round to even */ - add_one = - get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); - } - } - break; - case BF_RNDD: - case BF_RNDU: - if (r->sign == (rnd_mode == BF_RNDD)) - add_one = inexact; - break; - case BF_RNDA: - add_one = inexact; - break; - case BF_RNDNA: - case BF_RNDF: - add_one = bit1; - break; - default: - abort(); - } - - if (inexact) - *pret |= BF_ST_INEXACT; - return add_one; -} - -static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) -{ - slimb_t i, l, e_max; - int rnd_mode; - - rnd_mode = flags & BF_RND_MASK; - if (prec == BF_PREC_INF || - rnd_mode == BF_RNDN || - rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDA || - (rnd_mode == BF_RNDD && sign == 1) || - (rnd_mode == BF_RNDU && sign == 0)) { - bf_set_inf(r, sign); - } else { - /* set to maximum finite number */ - l = (prec + LIMB_BITS - 1) / LIMB_BITS; - if (bf_resize(r, l)) { - bf_set_nan(r); - return BF_ST_MEM_ERROR; - } - r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1), - LIMB_BITS - 1); - for(i = 1; i < l; i++) - r->tab[i] = (limb_t)-1; - e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - r->expn = e_max; - r->sign = sign; - } - return BF_ST_OVERFLOW | BF_ST_INEXACT; -} - -/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be - infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result - is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of - overflow not returning infinity. */ -static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, - int ret) -{ - limb_t v, a; - int shift, add_one, rnd_mode; - slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - - /* e_min and e_max are computed to match the IEEE 754 conventions */ - e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_range + 3; - e_max = e_range; - - if (flags & BF_FLAG_RADPNT_PREC) { - /* 'prec' is the precision after the radix point */ - if (prec1 != BF_PREC_INF) - prec = r->expn + prec1; - else - prec = prec1; - } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { - /* restrict the precision in case of potentially subnormal - result */ - assert(prec1 != BF_PREC_INF); - prec = prec1 - (e_min - r->expn); - } else { - prec = prec1; - } - - /* round to prec bits */ - rnd_mode = flags & BF_RND_MASK; - add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); - - if (prec <= 0) { - if (add_one) { - bf_resize(r, 1); /* cannot fail */ - r->tab[0] = (limb_t)1 << (LIMB_BITS - 1); - r->expn += 1 - prec; - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } else { - goto underflow; - } - } else if (add_one) { - limb_t carry; - - /* add one starting at digit 'prec - 1' */ - bit_pos = l * LIMB_BITS - 1 - (prec - 1); - pos = bit_pos >> LIMB_LOG2_BITS; - carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1)); - - for(i = pos; i < l; i++) { - v = r->tab[i] + carry; - carry = (v < carry); - r->tab[i] = v; - if (carry == 0) - break; - } - if (carry) { - /* shift right by one digit */ - v = 1; - for(i = l - 1; i >= pos; i--) { - a = r->tab[i]; - r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1)); - v = a; - } - r->expn++; - } - } - - /* check underflow */ - if (unlikely(r->expn < e_min)) { - if (flags & BF_FLAG_SUBNORMAL) { - /* if inexact, also set the underflow flag */ - if (ret & BF_ST_INEXACT) - ret |= BF_ST_UNDERFLOW; - } else { - underflow: - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - bf_set_zero(r, r->sign); - return ret; - } - } - - /* check overflow */ - if (unlikely(r->expn > e_max)) - return bf_set_overflow(r, r->sign, prec1, flags); - - /* keep the bits starting at 'prec - 1' */ - bit_pos = l * LIMB_BITS - 1 - (prec - 1); - i = bit_pos >> LIMB_LOG2_BITS; - if (i >= 0) { - shift = bit_pos & (LIMB_BITS - 1); - if (shift != 0) - r->tab[i] &= limb_mask(shift, LIMB_BITS - 1); - } else { - i = 0; - } - /* remove trailing zeros */ - while (r->tab[i] == 0) - i++; - if (i > 0) { - l -= i; - memmove(r->tab, r->tab + i, l * sizeof(limb_t)); - } - bf_resize(r, l); /* cannot fail */ - return ret; -} - -/* 'r' must be a finite number. */ -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) -{ - limb_t l, v, a; - int shift, ret; - slimb_t i; - - // bf_print_str("bf_renorm", r); - l = r->len; - while (l > 0 && r->tab[l - 1] == 0) - l--; - if (l == 0) { - /* zero */ - r->expn = BF_EXP_ZERO; - bf_resize(r, 0); /* cannot fail */ - ret = 0; - } else { - r->expn -= (r->len - l) * LIMB_BITS; - /* shift to have the MSB set to '1' */ - v = r->tab[l - 1]; - shift = clz(v); - if (shift != 0) { - v = 0; - for(i = 0; i < l; i++) { - a = r->tab[i]; - r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift)); - v = a; - } - r->expn -= shift; - } - ret = __bf_round(r, prec1, flags, l, 0); - } - // bf_print_str("r_final", r); - return ret; -} - -/* return true if rounding can be done at precision 'prec' assuming - the exact result r is such that |r-a| <= 2^(EXP(a)-k). */ -/* XXX: check the case where the exponent would be incremented by the - rounding */ -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) -{ - BOOL is_rndn; - slimb_t bit_pos, n; - limb_t bit; - - if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN) - return FALSE; - if (rnd_mode == BF_RNDF) { - return (k >= (prec + 1)); - } - if (a->expn == BF_EXP_ZERO) - return FALSE; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - if (k < (prec + 2)) - return FALSE; - bit_pos = a->len * LIMB_BITS - 1 - prec; - n = k - prec; - /* bit pattern for RNDN or RNDNA: 0111.. or 1000... - for other rounding modes: 000... or 111... - */ - bit = get_bit(a->tab, a->len, bit_pos); - bit_pos--; - n--; - bit ^= is_rndn; - /* XXX: slow, but a few iterations on average */ - while (n != 0) { - if (get_bit(a->tab, a->len, bit_pos) != bit) - return TRUE; - bit_pos--; - n--; - } - return FALSE; -} - -/* Cannot fail with BF_ST_MEM_ERROR. */ -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) -{ - if (r->len == 0) - return 0; - return __bf_round(r, prec, flags, r->len, 0); -} - -/* for debugging */ -static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n) -{ - limb_t i; - printf("%s: len=%" PRId_LIMB "\n", str, n); - for(i = 0; i < n; i++) { - printf("%" PRId_LIMB ": " FMT_LIMB "\n", - i, tab[i]); - } -} - -void mp_print_str(const char *str, const limb_t *tab, limb_t n) -{ - slimb_t i; - printf("%s= 0x", str); - for(i = n - 1; i >= 0; i--) { - if (i != (n - 1)) - printf("_"); - printf(FMT_LIMB, tab[i]); - } - printf("\n"); -} - -static __maybe_unused void mp_print_str_h(const char *str, - const limb_t *tab, limb_t n, - limb_t high) -{ - slimb_t i; - printf("%s= 0x", str); - printf(FMT_LIMB, high); - for(i = n - 1; i >= 0; i--) { - printf("_"); - printf(FMT_LIMB, tab[i]); - } - printf("\n"); -} - -/* for debugging */ -void bf_print_str(const char *str, const bf_t *a) -{ - slimb_t i; - printf("%s=", str); - - if (a->expn == BF_EXP_NAN) { - printf("NaN"); - } else { - if (a->sign) - putchar('-'); - if (a->expn == BF_EXP_ZERO) { - putchar('0'); - } else if (a->expn == BF_EXP_INF) { - printf("Inf"); - } else { - printf("0x0."); - for(i = a->len - 1; i >= 0; i--) - printf(FMT_LIMB, a->tab[i]); - printf("p%" PRId_LIMB, a->expn); - } - } - printf("\n"); -} - -/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0 - if a = b and > 0 otherwise. */ -int bf_cmpu(const bf_t *a, const bf_t *b) -{ - slimb_t i; - limb_t len, v1, v2; - - if (a->expn != b->expn) { - if (a->expn < b->expn) - return -1; - else - return 1; - } - len = bf_max(a->len, b->len); - for(i = len - 1; i >= 0; i--) { - v1 = get_limbz(a, a->len - len + i); - v2 = get_limbz(b, b->len - len + i); - if (v1 != v2) { - if (v1 < v2) - return -1; - else - return 1; - } - } - return 0; -} - -/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */ -int bf_cmp_full(const bf_t *a, const bf_t *b) -{ - int res; - - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - if (a->expn == b->expn) - res = 0; - else if (a->expn == BF_EXP_NAN) - res = 1; - else - res = -1; - } else if (a->sign != b->sign) { - res = 1 - 2 * a->sign; - } else { - res = bf_cmpu(a, b); - if (a->sign) - res = -res; - } - return res; -} - -/* Standard floating point comparison: return 2 if one of the operands - is NaN (unordered) or -1, 0, 1 depending on the ordering assuming - -0 == +0 */ -int bf_cmp(const bf_t *a, const bf_t *b) -{ - int res; - - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - res = 2; - } else if (a->sign != b->sign) { - if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) - res = 0; - else - res = 1 - 2 * a->sign; - } else { - res = bf_cmpu(a, b); - if (a->sign) - res = -res; - } - return res; -} - -/* Compute the number of bits 'n' matching the pattern: - a= X1000..0 - b= X0111..1 - - When computing a-b, the result will have at least n leading zero - bits. - - Precondition: a > b and a.expn - b.expn = 0 or 1 -*/ -static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) -{ - slimb_t bit_offset, b_offset, n; - int p, p1; - limb_t v1, v2, mask; - - bit_offset = a->len * LIMB_BITS - 1; - b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + - a->expn - b->expn; - n = 0; - - /* first search the equals bits */ - for(;;) { - v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); - v2 = get_bits(b->tab, b->len, bit_offset + b_offset); - // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); - if (v1 != v2) - break; - n += LIMB_BITS; - bit_offset -= LIMB_BITS; - } - /* find the position of the first different bit */ - p = clz(v1 ^ v2) + 1; - n += p; - /* then search for '0' in a and '1' in b */ - p = LIMB_BITS - p; - if (p > 0) { - /* search in the trailing p bits of v1 and v2 */ - mask = limb_mask(0, p - 1); - p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); - n += p1; - if (p1 != p) - goto done; - } - bit_offset -= LIMB_BITS; - for(;;) { - v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); - v2 = get_bits(b->tab, b->len, bit_offset + b_offset); - // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); - if (v1 != 0 || v2 != -1) { - /* different: count the matching bits */ - p1 = bf_min(clz(v1), clz(~v2)); - n += p1; - break; - } - n += LIMB_BITS; - bit_offset -= LIMB_BITS; - } - done: - return n; -} - -static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int b_neg) -{ - const bf_t *tmp; - int is_sub, ret, cmp_res, a_sign, b_sign; - - a_sign = a->sign; - b_sign = b->sign ^ b_neg; - is_sub = a_sign ^ b_sign; - cmp_res = bf_cmpu(a, b); - if (cmp_res < 0) { - tmp = a; - a = b; - b = tmp; - a_sign = b_sign; /* b_sign is never used later */ - } - /* abs(a) >= abs(b) */ - if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { - /* zero result */ - bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); - ret = 0; - } else if (a->len == 0 || b->len == 0) { - ret = 0; - if (a->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN) { - /* at least one operand is NaN */ - bf_set_nan(r); - } else if (b->expn == BF_EXP_INF && is_sub) { - /* infinities with different signs */ - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bf_set_inf(r, a_sign); - } - } else { - /* at least one zero and not subtract */ - bf_set(r, a); - r->sign = a_sign; - goto renorm; - } - } else { - slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; - limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; - - r->sign = a_sign; - r->expn = a->expn; - d = a->expn - b->expn; - /* must add more precision for the leading cancelled bits in - subtraction */ - if (is_sub) { - if (d <= 1) - cancelled_bits = count_cancelled_bits(a, b); - else - cancelled_bits = 1; - } else { - cancelled_bits = 0; - } - - /* add two extra bits for rounding */ - precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); - r_len = bf_min(precl, tot_len); - if (bf_resize(r, r_len)) - goto fail; - a_offset = a->len - r_len; - b_bit_offset = (b->len - r_len) * LIMB_BITS + d; - - /* compute the bits before for the rounding */ - carry = is_sub; - z = 0; - sub_mask = -is_sub; - i = r_len - tot_len; - while (i < 0) { - slimb_t ap, bp; - BOOL inflag; - - ap = a_offset + i; - bp = b_bit_offset + i * LIMB_BITS; - inflag = FALSE; - if (ap >= 0 && ap < a->len) { - v1 = a->tab[ap]; - inflag = TRUE; - } else { - v1 = 0; - } - if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { - v2 = get_bits(b->tab, b->len, bp); - inflag = TRUE; - } else { - v2 = 0; - } - if (!inflag) { - /* outside 'a' and 'b': go directly to the next value - inside a or b so that the running time does not - depend on the exponent difference */ - i = 0; - if (ap < 0) - i = bf_min(i, -a_offset); - /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 - equivalent to - i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) - */ - if (bp + LIMB_BITS <= 0) - i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); - } else { - i++; - } - v2 ^= sub_mask; - u = v1 + v2; - carry1 = u < v1; - u += carry; - carry = (u < carry) | carry1; - z |= u; - } - /* and the result */ - for(i = 0; i < r_len; i++) { - v1 = get_limbz(a, a_offset + i); - v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); - v2 ^= sub_mask; - u = v1 + v2; - carry1 = u < v1; - u += carry; - carry = (u < carry) | carry1; - r->tab[i] = u; - } - /* set the extra bits for the rounding */ - r->tab[0] |= (z != 0); - - /* carry is only possible in add case */ - if (!is_sub && carry) { - if (bf_resize(r, r_len + 1)) - goto fail; - r->tab[r_len] = 1; - r->expn += LIMB_BITS; - } - renorm: - ret = bf_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bf_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_add_internal(r, a, b, prec, flags, 0); -} - -static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_add_internal(r, a, b, prec, flags, 1); -} - -limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, - limb_t n, limb_t carry) -{ - slimb_t i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i<n;i++) { - v = op1[i]; - a = v + op2[i]; - k1 = a < v; - a = a + k; - k = (a < k) | k1; - res[i] = a; - } - return k; -} - -limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) -{ - size_t i; - limb_t k, a; - - k=b; - for(i=0;i<n;i++) { - if (k == 0) - break; - a = tab[i] + k; - k = (a < k); - tab[i] = a; - } - return k; -} - -limb_t mp_sub(limb_t *res, const limb_t *op1, const limb_t *op2, - mp_size_t n, limb_t carry) -{ - int i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i<n;i++) { - v = op1[i]; - a = v - op2[i]; - k1 = a > v; - v = a - k; - k = (v > a) | k1; - res[i] = v; - } - return k; -} - -/* compute 0 - op2 */ -static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) -{ - int i; - limb_t k, a, v, k1; - - k = carry; - for(i=0;i<n;i++) { - v = 0; - a = v - op2[i]; - k1 = a > v; - v = a - k; - k = (v > a) | k1; - res[i] = v; - } - return k; -} - -limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) -{ - mp_size_t i; - limb_t k, a, v; - - k=b; - for(i=0;i<n;i++) { - v = tab[i]; - a = v - k; - k = a > v; - tab[i] = a; - if (k == 0) - break; - } - return k; -} - -/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). - 1 <= shift <= LIMB_BITS - 1 */ -static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, - int shift, limb_t high) -{ - mp_size_t i; - limb_t l, a; - - assert(shift >= 1 && shift < LIMB_BITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); - l = a; - } - return l & (((limb_t)1 << shift) - 1); -} - -/* tabr[] = taba[] * b + l. Return the high carry */ -static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t l) -{ - limb_t i; - dlimb_t t; - - for(i = 0; i < n; i++) { - t = (dlimb_t)taba[i] * (dlimb_t)b + l; - tabr[i] = t; - l = t >> LIMB_BITS; - } - return l; -} - -/* tabr[] += taba[] * b, return the high word. */ -static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b) -{ - limb_t i, l; - dlimb_t t; - - l = 0; - for(i = 0; i < n; i++) { - t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; - tabr[i] = t; - l = t >> LIMB_BITS; - } - return l; -} - -/* size of the result : op1_size + op2_size. */ -static void mp_mul_basecase(limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size) -{ - limb_t i, r; - - result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); - for(i=1;i<op2_size;i++) { - r = mp_add_mul1(result + i, op1, op1_size, op2[i]); - result[i + op1_size] = r; - } -} - -/* return 0 if OK, -1 if memory error */ -/* XXX: change API so that result can be allocated */ -int mp_mul(bf_context_t *s, limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size) -{ -#ifdef USE_FFT_MUL - if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) { - bf_t r_s, *r = &r_s; - r->tab = result; - /* XXX: optimize memory usage in API */ - if (fft_mul(s, r, (limb_t *)op1, op1_size, - (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) - return -1; - } else -#endif - { - mp_mul_basecase(result, op1, op1_size, op2, op2_size); - } - return 0; -} - -/* tabr[] -= taba[] * b. Return the value to substract to the high - word. */ -static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b) -{ - limb_t i, l; - dlimb_t t; - - l = 0; - for(i = 0; i < n; i++) { - t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; - tabr[i] = t; - l = -(t >> LIMB_BITS); - } - return l; -} - -/* WARNING: d must be >= 2^(LIMB_BITS-1) */ -static inline limb_t udiv1norm_init(limb_t d) -{ - limb_t a0, a1; - a1 = -d - 1; - a0 = -1; - return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; -} - -/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 - / d' with 0 <= a1 < d. */ -static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, - limb_t d, limb_t d_inv) -{ - limb_t n1m, n_adj, q, r, ah; - dlimb_t a; - n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); - n_adj = a0 + (n1m & d); - a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; - q = (a >> LIMB_BITS) + a1; - /* compute a - q * r and update q so that the remainder is\ - between 0 and d - 1 */ - a = ((dlimb_t)a1 << LIMB_BITS) | a0; - a = a - (dlimb_t)q * d - d; - ah = a >> LIMB_BITS; - q += 1 + ah; - r = (limb_t)a + (ah & d); - *pr = r; - return q; -} - -/* b must be >= 1 << (LIMB_BITS - 1) */ -static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, - limb_t b, limb_t r) -{ - slimb_t i; - - if (n >= UDIV1NORM_THRESHOLD) { - limb_t b_inv; - b_inv = udiv1norm_init(b); - for(i = n - 1; i >= 0; i--) { - tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); - } - } else { - dlimb_t a1; - for(i = n - 1; i >= 0; i--) { - a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; - tabr[i] = a1 / b; - r = a1 % b; - } - } - return r; -} - -static int mp_divnorm_large(bf_context_t *s, - limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb); - -/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb - - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' - is modified and contains the remainder (nb limbs). tabq[0..na-nb] - contains the quotient with tabq[na - nb] <= 1. */ -static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb) -{ - limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; - slimb_t i, j; - - b1 = tabb[nb - 1]; - if (nb == 1) { - taba[0] = mp_div1norm(tabq, taba, na, b1, 0); - return 0; - } - n = na - nb; - if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { - return mp_divnorm_large(s, tabq, taba, na, tabb, nb); - } - - if (n >= UDIV1NORM_THRESHOLD) - b1_inv = udiv1norm_init(b1); - else - b1_inv = 0; - - /* first iteration: the quotient is only 0 or 1 */ - q = 1; - for(j = nb - 1; j >= 0; j--) { - if (taba[n + j] != tabb[j]) { - if (taba[n + j] < tabb[j]) - q = 0; - break; - } - } - tabq[n] = q; - if (q) { - mp_sub(taba + n, taba + n, tabb, nb, 0); - } - - for(i = n - 1; i >= 0; i--) { - if (unlikely(taba[i + nb] >= b1)) { - q = -1; - } else if (b1_inv) { - q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); - } else { - dlimb_t al; - al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; - q = al / b1; - r = al % b1; - } - r = mp_sub_mul1(taba + i, tabb, nb, q); - - v = taba[i + nb]; - a = v - r; - c = (a > v); - taba[i + nb] = a; - - if (c != 0) { - /* negative result */ - for(;;) { - q--; - c = mp_add(taba + i, taba + i, tabb, nb, 0); - /* propagate carry and test if positive result */ - if (c != 0) { - if (++taba[i + nb] == 0) { - break; - } - } - } - } - tabq[i] = q; - } - return 0; -} - -/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' - has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. - - See Modern Computer Arithmetic by Richard P. Brent and Paul - Zimmermann, algorithm 3.5 */ -int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) -{ - mp_size_t l, h, k, i; - limb_t *tabxh, *tabt, c, *tabu; - - if (n <= 2) { - /* return ceil(B^(2*n)/a) - 1 */ - /* XXX: could avoid allocation */ - tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); - tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); - if (!tabt || !tabu) - goto fail; - for(i = 0; i < 2 * n; i++) - tabu[i] = 0; - tabu[2 * n] = 1; - if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) - goto fail; - for(i = 0; i < n + 1; i++) - tabr[i] = tabt[i]; - if (mp_scan_nz(tabu, n) == 0) { - /* only happens for a=B^n/2 */ - mp_sub_ui(tabr, 1, n + 1); - } - } else { - l = (n - 1) / 2; - h = n - l; - /* n=2p -> l=p-1, h = p + 1, k = p + 3 - n=2p+1-> l=p, h = p + 1; k = p + 2 - */ - tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); - tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); - if (!tabt || !tabu) - goto fail; - tabxh = tabr + l; - if (mp_recip(s, tabxh, taba + l, h)) - goto fail; - if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ - goto fail; - while (tabt[n + h] != 0) { - mp_sub_ui(tabxh, 1, h + 1); - c = mp_sub(tabt, tabt, taba, n, 0); - mp_sub_ui(tabt + n, c, h + 1); - } - /* T = B^(n+h) - T */ - mp_neg(tabt, tabt, n + h + 1, 0); - tabt[n + h]++; - if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) - goto fail; - /* n + 2*h - l + 2 limbs */ - k = 2 * h - l; - for(i = 0; i < l; i++) - tabr[i] = tabu[i + k]; - mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); - } - bf_free(s, tabt); - bf_free(s, tabu); - return 0; - fail: - bf_free(s, tabt); - bf_free(s, tabu); - return -1; -} - -/* return -1, 0 or 1 */ -static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) -{ - mp_size_t i; - for(i = n - 1; i >= 0; i--) { - if (taba[i] != tabb[i]) { - if (taba[i] < tabb[i]) - return -1; - else - return 1; - } - } - return 0; -} - -//#define DEBUG_DIVNORM_LARGE -//#define DEBUG_DIVNORM_LARGE2 - -/* subquadratic divnorm */ -static int mp_divnorm_large(bf_context_t *s, - limb_t *tabq, limb_t *taba, limb_t na, - const limb_t *tabb, limb_t nb) -{ - limb_t *tabb_inv, nq, *tabt, i, n; - nq = na - nb; -#ifdef DEBUG_DIVNORM_LARGE - printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); - mp_print_str("a", taba, na); - mp_print_str("b", tabb, nb); -#endif - assert(nq >= 1); - n = nq; - if (nq < nb) - n++; - tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); - tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); - if (!tabb_inv || !tabt) - goto fail; - - if (n >= nb) { - for(i = 0; i < n - nb; i++) - tabt[i] = 0; - for(i = 0; i < nb; i++) - tabt[i + n - nb] = tabb[i]; - } else { - /* truncate B: need to increment it so that the approximate - inverse is smaller that the exact inverse */ - for(i = 0; i < n; i++) - tabt[i] = tabb[i + nb - n]; - if (mp_add_ui(tabt, 1, n)) { - /* tabt = B^n : tabb_inv = B^n */ - memset(tabb_inv, 0, n * sizeof(limb_t)); - tabb_inv[n] = 1; - goto recip_done; - } - } - if (mp_recip(s, tabb_inv, tabt, n)) - goto fail; - recip_done: - /* Q=A*B^-1 */ - if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) - goto fail; - - for(i = 0; i < nq + 1; i++) - tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; -#ifdef DEBUG_DIVNORM_LARGE - mp_print_str("q", tabq, nq + 1); -#endif - - bf_free(s, tabt); - bf_free(s, tabb_inv); - tabb_inv = NULL; - - /* R=A-B*Q */ - tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); - if (!tabt) - goto fail; - if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) - goto fail; - /* we add one more limb for the result */ - mp_sub(taba, taba, tabt, nb + 1, 0); - bf_free(s, tabt); - /* the approximated quotient is smaller than than the exact one, - hence we may have to increment it */ -#ifdef DEBUG_DIVNORM_LARGE2 - int cnt = 0; - static int cnt_max; -#endif - for(;;) { - if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) - break; - taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); - mp_add_ui(tabq, 1, nq + 1); -#ifdef DEBUG_DIVNORM_LARGE2 - cnt++; -#endif - } -#ifdef DEBUG_DIVNORM_LARGE2 - if (cnt > cnt_max) { - cnt_max = cnt; - printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); - } -#endif - return 0; - fail: - bf_free(s, tabb_inv); - bf_free(s, tabt); - return -1; -} - -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags) -{ - int ret, r_sign; - - if (a->len < b->len) { - const bf_t *tmp = a; - a = b; - b = tmp; - } - r_sign = a->sign ^ b->sign; - /* here b->len <= a->len */ - if (b->len == 0) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bf_set_nan(r); - ret = 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { - if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || - (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { - bf_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bf_set_inf(r, r_sign); - ret = 0; - } - } else { - bf_set_zero(r, r_sign); - ret = 0; - } - } else { - bf_t tmp, *r1 = NULL; - limb_t a_len, b_len, precl; - limb_t *a_tab, *b_tab; - - a_len = a->len; - b_len = b->len; - - if ((flags & BF_RND_MASK) == BF_RNDF) { - /* faithful rounding does not require using the full inputs */ - precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; - a_len = bf_min(a_len, precl); - b_len = bf_min(b_len, precl); - } - a_tab = a->tab + a->len - a_len; - b_tab = b->tab + b->len - b_len; - -#ifdef USE_FFT_MUL - if (b_len >= FFT_MUL_THRESHOLD) { - int mul_flags = 0; - if (r == a) - mul_flags |= FFT_MUL_R_OVERLAP_A; - if (r == b) - mul_flags |= FFT_MUL_R_OVERLAP_B; - if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) - goto fail; - } else -#endif - { - if (r == a || r == b) { - bf_init(r->ctx, &tmp); - r1 = r; - r = &tmp; - } - if (bf_resize(r, a_len + b_len)) { -#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<n;i++) { - /* XXX: reuse the trick in add_mod */ - v = op1[i]; - a = v + op2[i] + k - base; - k = a <= v; - if (!k) - a += base; - res[i]=a; - } - return k; -} - -limb_t mp_add_ui_dec(limb_t *tab, limb_t b, mp_size_t n) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, a, v; - - k=b; - for(i=0;i<n;i++) { - v = tab[i]; - a = v + k - base; - k = a <= v; - if (!k) - a += base; - tab[i] = a; - if (k == 0) - break; - } - return k; -} - -limb_t mp_sub_dec(limb_t *res, const limb_t *op1, const limb_t *op2, - mp_size_t n, limb_t carry) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, v, a; - - k=carry; - for(i=0;i<n;i++) { - v = op1[i]; - a = v - op2[i] - k; - k = a > v; - if (k) - a += base; - res[i] = a; - } - return k; -} - -limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t k, v, a; - - k=b; - for(i=0;i<n;i++) { - v = tab[i]; - a = v - k; - k = a > v; - if (k) - a += base; - tab[i]=a; - if (k == 0) - break; - } - return k; -} - -/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ -limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b, limb_t l) -{ - mp_size_t i; - limb_t t0, t1, r; - - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - divdq_base(l, r, t1, t0); - tabr[i] = r; - } - return l; -} - -/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add - to the high word */ -limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b) -{ - mp_size_t i; - limb_t l, t0, t1, r; - - l = 0; - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - adddq(t1, t0, 0, tabr[i]); - divdq_base(l, r, t1, t0); - tabr[i] = r; - } - return l; -} - -/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to - substract to the high word. */ -limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, - limb_t b) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t l, t0, t1, r, a, v, c; - - /* XXX: optimize */ - l = 0; - for(i = 0; i < n; i++) { - muldq(t1, t0, taba[i], b); - adddq(t1, t0, 0, l); - divdq_base(l, r, t1, t0); - v = tabr[i]; - a = v - r; - c = a > v; - if (c) - a += base; - /* never bigger than base because r = 0 when l = base - 1 */ - l += c; - tabr[i] = a; - } - return l; -} - -/* size of the result : op1_size + op2_size. */ -void mp_mul_basecase_dec(limb_t *result, - const limb_t *op1, mp_size_t op1_size, - const limb_t *op2, mp_size_t op2_size) -{ - mp_size_t i; - limb_t r; - - result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); - - for(i=1;i<op2_size;i++) { - r = mp_add_mul1_dec(result + i, op1, op1_size, op2[i]); - result[i + op1_size] = r; - } -} - -/* taba[] = (taba[] + r*base^na) / b. 0 <= b < base. 0 <= r < - b. Return the remainder. */ -limb_t mp_div1_dec(limb_t *tabr, const limb_t *taba, mp_size_t na, - limb_t b, limb_t r) -{ - limb_t base = BF_DEC_BASE; - mp_size_t i; - limb_t t0, t1, q; - int shift; - -#if (BF_DEC_BASE % 2) == 0 - if (b == 2) { - limb_t base_div2; - /* Note: only works if base is even */ - base_div2 = base >> 1; - if (r) - r = base_div2; - for(i = na - 1; i >= 0; i--) { - t0 = taba[i]; - tabr[i] = (t0 >> 1) + r; - r = 0; - if (t0 & 1) - r = base_div2; - } - if (r) - r = 1; - } else -#endif - if (na >= UDIV1NORM_THRESHOLD) { - shift = clz(b); - if (shift == 0) { - /* normalized case: b >= 2^(LIMB_BITS-1) */ - limb_t b_inv; - b_inv = udiv1norm_init(b); - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - q = udiv1norm(&r, t1, t0, b, b_inv); - tabr[i] = q; - } - } else { - limb_t b_inv; - b <<= shift; - b_inv = udiv1norm_init(b); - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); - t0 <<= shift; - q = udiv1norm(&r, t1, t0, b, b_inv); - r >>= shift; - tabr[i] = q; - } - } - } else { - for(i = na - 1; i >= 0; i--) { - muldq(t1, t0, r, base); - adddq(t1, t0, 0, taba[i]); - divdq(q, r, t1, t0, b); - tabr[i] = q; - } - } - return r; -} - -static __maybe_unused void mp_print_str_dec(const char *str, - const limb_t *tab, slimb_t n) -{ - slimb_t i; - printf("%s=", str); - for(i = n - 1; i >= 0; i--) { - if (i != n - 1) - printf("_"); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); - } - printf("\n"); -} - -static __maybe_unused void mp_print_str_h_dec(const char *str, - const limb_t *tab, slimb_t n, - limb_t high) -{ - slimb_t i; - printf("%s=", str); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); - for(i = n - 1; i >= 0; i--) { - printf("_"); - printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); - } - printf("\n"); -} - -//#define DEBUG_DIV_SLOW - -#define DIV_STATIC_ALLOC_LEN 16 - -/* return q = a / b and r = a % b. - - taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] - must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] - >= B / 2. - - The remainder is is returned in taba and contains nb libms. tabq - contains na - nb + 1 limbs. No overlap is permitted. - - Running time of the standard method: (na - nb + 1) * nb - Return 0 if OK, -1 if memory alloc error -*/ -/* XXX: optimize */ -static int mp_div_dec(bf_context_t *s, limb_t *tabq, - limb_t *taba, mp_size_t na, - const limb_t *tabb1, mp_size_t nb) -{ - limb_t base = BF_DEC_BASE; - limb_t r, mult, t0, t1, a, c, q, v, *tabb; - mp_size_t i, j; - limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; - -#ifdef DEBUG_DIV_SLOW - mp_print_str_dec("a", taba, na); - mp_print_str_dec("b", tabb1, nb); -#endif - - /* normalize tabb */ - r = tabb1[nb - 1]; - assert(r != 0); - i = na - nb; - if (r >= BF_DEC_BASE / 2) { - mult = 1; - tabb = (limb_t *)tabb1; - q = 1; - for(j = nb - 1; j >= 0; j--) { - if (taba[i + j] != tabb[j]) { - if (taba[i + j] < tabb[j]) - q = 0; - break; - } - } - tabq[i] = q; - if (q) { - mp_sub_dec(taba + i, taba + i, tabb, nb, 0); - } - i--; - } else { - mult = base / (r + 1); - if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { - tabb = static_tabb; - } else { - tabb = bf_malloc(s, sizeof(limb_t) * nb); - if (!tabb) - return -1; - } - mp_mul1_dec(tabb, tabb1, nb, mult, 0); - taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); - } - -#ifdef DEBUG_DIV_SLOW - printf("mult=" FMT_LIMB "\n", mult); - mp_print_str_dec("a_norm", taba, na + 1); - mp_print_str_dec("b_norm", tabb, nb); -#endif - - for(; i >= 0; i--) { - if (unlikely(taba[i + nb] >= tabb[nb - 1])) { - /* XXX: check if it is really possible */ - q = base - 1; - } else { - muldq(t1, t0, taba[i + nb], base); - adddq(t1, t0, 0, taba[i + nb - 1]); - divdq(q, r, t1, t0, tabb[nb - 1]); - } - // printf("i=%d q1=%ld\n", i, q); - - r = mp_sub_mul1_dec(taba + i, tabb, nb, q); - // mp_dump("r1", taba + i, nb, bd); - // printf("r2=%ld\n", r); - - v = taba[i + nb]; - a = v - r; - c = a > v; - if (c) - a += base; - taba[i + nb] = a; - - if (c != 0) { - /* negative result */ - for(;;) { - q--; - c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); - /* propagate carry and test if positive result */ - if (c != 0) { - if (++taba[i + nb] == base) { - break; - } - } - } - } - tabq[i] = q; - } - -#ifdef DEBUG_DIV_SLOW - mp_print_str_dec("q", tabq, na - nb + 1); - mp_print_str_dec("r", taba, nb); -#endif - - /* remove the normalization */ - if (mult != 1) { - mp_div1_dec(taba, taba, nb, mult, 0); - if (unlikely(tabb != static_tabb)) - bf_free(s, tabb); - } - return 0; -} - -/* divide by 10^shift */ -static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, - limb_t shift, limb_t high) -{ - mp_size_t i; - limb_t l, a, q, r; - - assert(shift >= 1 && shift < LIMB_DIGITS); - l = high; - for(i = n - 1; i >= 0; i--) { - a = tab[i]; - fast_shr_rem_dec(q, r, a, shift); - tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; - l = r; - } - return l; -} - -/* multiply by 10^shift */ -static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, - limb_t shift, limb_t low) -{ - mp_size_t i; - limb_t l, a, q, r; - - assert(shift >= 1 && shift < LIMB_DIGITS); - l = low; - for(i = 0; i < n; i++) { - a = tab[i]; - fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); - tab_r[i] = r * mp_pow_dec[shift] + l; - l = q; - } - return l; -} - -static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) -{ - int k; - dlimb_t a, b, r; - limb_t taba1[2], s, r0, r1; - - /* convert to binary and normalize */ - a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; - k = clz(a >> LIMB_BITS) & ~1; - b = a << k; - taba1[0] = b; - taba1[1] = b >> LIMB_BITS; - mp_sqrtrem2(&s, taba1); - s >>= (k >> 1); - /* convert the remainder back to decimal */ - r = a - (dlimb_t)s * (dlimb_t)s; - divdq_base(r1, r0, r >> LIMB_BITS, r); - taba[0] = r0; - tabs[0] = s; - return r1; -} - -//#define DEBUG_SQRTREM_DEC - -/* tmp_buf must contain (n / 2 + 1 limbs) */ -static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, - limb_t *tmp_buf) -{ - limb_t l, h, rh, ql, qh, c, i; - - if (n == 1) - return mp_sqrtrem2_dec(tabs, taba); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("a", taba, 2 * n); -#endif - l = n / 2; - h = n - l; - qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("s1", tabs + l, h); - mp_print_str_h_dec("r1", taba + 2 * l, h, qh); - mp_print_str_h_dec("r2", taba + l, n, qh); -#endif - - /* the remainder is in taba + 2 * l. Its high bit is in qh */ - if (qh) { - mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); - } - /* instead of dividing by 2*s, divide by s (which is normalized) - and update q and r */ - mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); - qh += tmp_buf[l]; - for(i = 0; i < l; i++) - tabs[i] = tmp_buf[i]; - ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); - qh = qh >> 1; /* 0 or 1 */ - if (ql) - rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); - else - rh = 0; -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_h_dec("q", tabs, l, qh); - mp_print_str_h_dec("u", taba + l, h, rh); -#endif - - mp_add_ui_dec(tabs + l, qh, h); -#ifdef DEBUG_SQRTREM_DEC - mp_print_str_dec("s2", tabs, n); -#endif - - /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ - /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ - if (qh) { - c = qh; - } else { - mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); - c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); - } - rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); - if ((slimb_t)rh < 0) { - mp_sub_ui_dec(tabs, 1, n); - rh += mp_add_mul1_dec(taba, tabs, n, 2); - rh += mp_add_ui_dec(taba, 1, n); - } - return rh; -} - -/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, - r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n - limbs. r is returned in the lower n limbs of taba. Its r[n] is the - returned value of the function. */ -int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) -{ - limb_t tmp_buf1[8]; - limb_t *tmp_buf; - mp_size_t n2; - n2 = n / 2 + 1; - if (n2 <= countof(tmp_buf1)) { - tmp_buf = tmp_buf1; - } else { - tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); - if (!tmp_buf) - return -1; - } - taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); - if (tmp_buf != tmp_buf1) - bf_free(s, tmp_buf); - return 0; -} - -/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ -static int clz_dec(limb_t a) -{ - if (a == 0) - return LIMB_DIGITS; - switch(LIMB_BITS - 1 - clz(a)) { - case 0: /* 1-1 */ - return LIMB_DIGITS - 1; - case 1: /* 2-3 */ - return LIMB_DIGITS - 1; - case 2: /* 4-7 */ - return LIMB_DIGITS - 1; - case 3: /* 8-15 */ - if (a < 10) - return LIMB_DIGITS - 1; - else - return LIMB_DIGITS - 2; - case 4: /* 16-31 */ - return LIMB_DIGITS - 2; - case 5: /* 32-63 */ - return LIMB_DIGITS - 2; - case 6: /* 64-127 */ - if (a < 100) - return LIMB_DIGITS - 2; - else - return LIMB_DIGITS - 3; - case 7: /* 128-255 */ - return LIMB_DIGITS - 3; - case 8: /* 256-511 */ - return LIMB_DIGITS - 3; - case 9: /* 512-1023 */ - if (a < 1000) - return LIMB_DIGITS - 3; - else - return LIMB_DIGITS - 4; - case 10: /* 1024-2047 */ - return LIMB_DIGITS - 4; - case 11: /* 2048-4095 */ - return LIMB_DIGITS - 4; - case 12: /* 4096-8191 */ - return LIMB_DIGITS - 4; - case 13: /* 8192-16383 */ - if (a < 10000) - return LIMB_DIGITS - 4; - else - return LIMB_DIGITS - 5; - case 14: /* 16384-32767 */ - return LIMB_DIGITS - 5; - case 15: /* 32768-65535 */ - return LIMB_DIGITS - 5; - case 16: /* 65536-131071 */ - if (a < 100000) - return LIMB_DIGITS - 5; - else - return LIMB_DIGITS - 6; - case 17: /* 131072-262143 */ - return LIMB_DIGITS - 6; - case 18: /* 262144-524287 */ - return LIMB_DIGITS - 6; - case 19: /* 524288-1048575 */ - if (a < 1000000) - return LIMB_DIGITS - 6; - else - return LIMB_DIGITS - 7; - case 20: /* 1048576-2097151 */ - return LIMB_DIGITS - 7; - case 21: /* 2097152-4194303 */ - return LIMB_DIGITS - 7; - case 22: /* 4194304-8388607 */ - return LIMB_DIGITS - 7; - case 23: /* 8388608-16777215 */ - if (a < 10000000) - return LIMB_DIGITS - 7; - else - return LIMB_DIGITS - 8; - case 24: /* 16777216-33554431 */ - return LIMB_DIGITS - 8; - case 25: /* 33554432-67108863 */ - return LIMB_DIGITS - 8; - case 26: /* 67108864-134217727 */ - if (a < 100000000) - return LIMB_DIGITS - 8; - else - return LIMB_DIGITS - 9; -#if LIMB_BITS == 64 - case 27: /* 134217728-268435455 */ - return LIMB_DIGITS - 9; - case 28: /* 268435456-536870911 */ - return LIMB_DIGITS - 9; - case 29: /* 536870912-1073741823 */ - if (a < 1000000000) - return LIMB_DIGITS - 9; - else - return LIMB_DIGITS - 10; - case 30: /* 1073741824-2147483647 */ - return LIMB_DIGITS - 10; - case 31: /* 2147483648-4294967295 */ - return LIMB_DIGITS - 10; - case 32: /* 4294967296-8589934591 */ - return LIMB_DIGITS - 10; - case 33: /* 8589934592-17179869183 */ - if (a < 10000000000) - return LIMB_DIGITS - 10; - else - return LIMB_DIGITS - 11; - case 34: /* 17179869184-34359738367 */ - return LIMB_DIGITS - 11; - case 35: /* 34359738368-68719476735 */ - return LIMB_DIGITS - 11; - case 36: /* 68719476736-137438953471 */ - if (a < 100000000000) - return LIMB_DIGITS - 11; - else - return LIMB_DIGITS - 12; - case 37: /* 137438953472-274877906943 */ - return LIMB_DIGITS - 12; - case 38: /* 274877906944-549755813887 */ - return LIMB_DIGITS - 12; - case 39: /* 549755813888-1099511627775 */ - if (a < 1000000000000) - return LIMB_DIGITS - 12; - else - return LIMB_DIGITS - 13; - case 40: /* 1099511627776-2199023255551 */ - return LIMB_DIGITS - 13; - case 41: /* 2199023255552-4398046511103 */ - return LIMB_DIGITS - 13; - case 42: /* 4398046511104-8796093022207 */ - return LIMB_DIGITS - 13; - case 43: /* 8796093022208-17592186044415 */ - if (a < 10000000000000) - return LIMB_DIGITS - 13; - else - return LIMB_DIGITS - 14; - case 44: /* 17592186044416-35184372088831 */ - return LIMB_DIGITS - 14; - case 45: /* 35184372088832-70368744177663 */ - return LIMB_DIGITS - 14; - case 46: /* 70368744177664-140737488355327 */ - if (a < 100000000000000) - return LIMB_DIGITS - 14; - else - return LIMB_DIGITS - 15; - case 47: /* 140737488355328-281474976710655 */ - return LIMB_DIGITS - 15; - case 48: /* 281474976710656-562949953421311 */ - return LIMB_DIGITS - 15; - case 49: /* 562949953421312-1125899906842623 */ - if (a < 1000000000000000) - return LIMB_DIGITS - 15; - else - return LIMB_DIGITS - 16; - case 50: /* 1125899906842624-2251799813685247 */ - return LIMB_DIGITS - 16; - case 51: /* 2251799813685248-4503599627370495 */ - return LIMB_DIGITS - 16; - case 52: /* 4503599627370496-9007199254740991 */ - return LIMB_DIGITS - 16; - case 53: /* 9007199254740992-18014398509481983 */ - if (a < 10000000000000000) - return LIMB_DIGITS - 16; - else - return LIMB_DIGITS - 17; - case 54: /* 18014398509481984-36028797018963967 */ - return LIMB_DIGITS - 17; - case 55: /* 36028797018963968-72057594037927935 */ - return LIMB_DIGITS - 17; - case 56: /* 72057594037927936-144115188075855871 */ - if (a < 100000000000000000) - return LIMB_DIGITS - 17; - else - return LIMB_DIGITS - 18; - case 57: /* 144115188075855872-288230376151711743 */ - return LIMB_DIGITS - 18; - case 58: /* 288230376151711744-576460752303423487 */ - return LIMB_DIGITS - 18; - case 59: /* 576460752303423488-1152921504606846975 */ - if (a < 1000000000000000000) - return LIMB_DIGITS - 18; - else - return LIMB_DIGITS - 19; -#endif - default: - return 0; - } -} - -/* for debugging */ -void bfdec_print_str(const char *str, const bfdec_t *a) -{ - slimb_t i; - printf("%s=", str); - - if (a->expn == BF_EXP_NAN) { - printf("NaN"); - } else { - if (a->sign) - putchar('-'); - if (a->expn == BF_EXP_ZERO) { - putchar('0'); - } else if (a->expn == BF_EXP_INF) { - printf("Inf"); - } else { - printf("0."); - for(i = a->len - 1; i >= 0; i--) - printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); - printf("e%" PRId_LIMB, a->expn); - } - } - printf("\n"); -} - -/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ -static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) -{ - slimb_t pos; - limb_t v, q; - int shift; - - if (bit_pos < 0) - return 0; - pos = (limb_t)bit_pos / LIMB_DIGITS; - shift = (limb_t)bit_pos % LIMB_DIGITS; - fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); - (void)q; - if (v != 0) - return 1; - pos--; - while (pos >= 0) { - if (r->tab[pos] != 0) - return 1; - pos--; - } - return 0; -} - -static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) -{ - slimb_t i; - int shift; - i = floor_div(pos, LIMB_DIGITS); - if (i < 0 || i >= len) - return 0; - shift = pos - i * LIMB_DIGITS; - return fast_shr_dec(tab[i], shift) % 10; -} - -#if 0 -static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos) -{ - limb_t a0, a1; - int shift; - slimb_t i; - - i = floor_div(pos, LIMB_DIGITS); - shift = pos - i * LIMB_DIGITS; - if (i >= 0 && i < len) - a0 = tab[i]; - else - a0 = 0; - if (shift == 0) { - return a0; - } else { - i++; - if (i >= 0 && i < len) - a1 = tab[i]; - else - a1 = 0; - return fast_shr_dec(a0, shift) + - fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) * - mp_pow_dec[shift]; - } -} -#endif - -/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ -static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, - slimb_t prec, int rnd_mode) -{ - int add_one, inexact; - limb_t digit1, digit0; - - // bfdec_print_str("get_rnd_add", r); - if (rnd_mode == BF_RNDF) { - digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ - } else { - /* starting limb for bit 'prec + 1' */ - digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); - } - - /* get the digit at 'prec' */ - digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); - inexact = (digit1 | digit0) != 0; - - add_one = 0; - switch(rnd_mode) { - case BF_RNDZ: - break; - case BF_RNDN: - if (digit1 == 5) { - if (digit0) { - add_one = 1; - } else { - /* round to even */ - add_one = - get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; - } - } else if (digit1 > 5) { - add_one = 1; - } - break; - case BF_RNDD: - case BF_RNDU: - if (r->sign == (rnd_mode == BF_RNDD)) - add_one = inexact; - break; - case BF_RNDNA: - case BF_RNDF: - add_one = (digit1 >= 5); - break; - case BF_RNDA: - add_one = inexact; - break; - default: - abort(); - } - - if (inexact) - *pret |= BF_ST_INEXACT; - return add_one; -} - -/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l' (1 <= l <= r->len). prec1 can be - BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with - BF_ST_MEM_ERROR. - */ -static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) -{ - int shift, add_one, rnd_mode, ret; - slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; - - /* XXX: align to IEEE 754 2008 for decimal numbers ? */ - e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); - e_min = -e_range + 3; - e_max = e_range; - - if (flags & BF_FLAG_RADPNT_PREC) { - /* 'prec' is the precision after the decimal point */ - if (prec1 != BF_PREC_INF) - prec = r->expn + prec1; - else - prec = prec1; - } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { - /* restrict the precision in case of potentially subnormal - result */ - assert(prec1 != BF_PREC_INF); - prec = prec1 - (e_min - r->expn); - } else { - prec = prec1; - } - - /* round to prec bits */ - rnd_mode = flags & BF_RND_MASK; - ret = 0; - add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); - - if (prec <= 0) { - if (add_one) { - bfdec_resize(r, 1); /* cannot fail because r is non zero */ - r->tab[0] = BF_DEC_BASE / 10; - r->expn += 1 - prec; - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } else { - goto underflow; - } - } else if (add_one) { - limb_t carry; - - /* add one starting at digit 'prec - 1' */ - bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); - pos = bit_pos / LIMB_DIGITS; - carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; - carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); - if (carry) { - /* shift right by one digit */ - mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); - r->expn++; - } - } - - /* check underflow */ - if (unlikely(r->expn < e_min)) { - if (flags & BF_FLAG_SUBNORMAL) { - /* if inexact, also set the underflow flag */ - if (ret & BF_ST_INEXACT) - ret |= BF_ST_UNDERFLOW; - } else { - underflow: - bfdec_set_zero(r, r->sign); - ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; - return ret; - } - } - - /* check overflow */ - if (unlikely(r->expn > e_max)) { - bfdec_set_inf(r, r->sign); - ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; - return ret; - } - - /* keep the bits starting at 'prec - 1' */ - bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); - i = floor_div(bit_pos, LIMB_DIGITS); - if (i >= 0) { - shift = smod(bit_pos, LIMB_DIGITS); - if (shift != 0) { - r->tab[i] = fast_shr_dec(r->tab[i], shift) * - mp_pow_dec[shift]; - } - } else { - i = 0; - } - /* remove trailing zeros */ - while (r->tab[i] == 0) - i++; - if (i > 0) { - l -= i; - memmove(r->tab, r->tab + i, l * sizeof(limb_t)); - } - bfdec_resize(r, l); /* cannot fail */ - return ret; -} - -/* Cannot fail with BF_ST_MEM_ERROR. */ -int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) -{ - if (r->len == 0) - return 0; - return __bfdec_round(r, prec, flags, r->len); -} - -/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ -int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) -{ - limb_t l, v; - int shift, ret; - - // bfdec_print_str("bf_renorm", r); - l = r->len; - while (l > 0 && r->tab[l - 1] == 0) - l--; - if (l == 0) { - /* zero */ - r->expn = BF_EXP_ZERO; - bfdec_resize(r, 0); /* cannot fail */ - ret = 0; - } else { - r->expn -= (r->len - l) * LIMB_DIGITS; - /* shift to have the MSB set to '1' */ - v = r->tab[l - 1]; - shift = clz_dec(v); - if (shift != 0) { - mp_shl_dec(r->tab, r->tab, l, shift, 0); - r->expn -= shift; - } - ret = __bfdec_round(r, prec1, flags, l); - } - // bf_print_str("r_final", r); - return ret; -} - -int bfdec_set_ui(bfdec_t *r, uint64_t v) -{ -#if LIMB_BITS == 32 - if (v >= BF_DEC_BASE * BF_DEC_BASE) { - if (bfdec_resize(r, 3)) - goto fail; - r->tab[0] = v % BF_DEC_BASE; - v /= BF_DEC_BASE; - r->tab[1] = v % BF_DEC_BASE; - r->tab[2] = v / BF_DEC_BASE; - r->expn = 3 * LIMB_DIGITS; - } else -#endif - if (v >= BF_DEC_BASE) { - if (bfdec_resize(r, 2)) - goto fail; - r->tab[0] = v % BF_DEC_BASE; - r->tab[1] = v / BF_DEC_BASE; - r->expn = 2 * LIMB_DIGITS; - } else { - if (bfdec_resize(r, 1)) - goto fail; - r->tab[0] = v; - r->expn = LIMB_DIGITS; - } - r->sign = 0; - return bfdec_normalize_and_round(r, BF_PREC_INF, 0); - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_set_si(bfdec_t *r, int64_t v) -{ - int ret; - if (v < 0) { - ret = bfdec_set_ui(r, -v); - r->sign = 1; - } else { - ret = bfdec_set_ui(r, v); - } - return ret; -} - -static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) -{ - bf_context_t *s = r->ctx; - int is_sub, cmp_res, a_sign, b_sign, ret; - - a_sign = a->sign; - b_sign = b->sign ^ b_neg; - is_sub = a_sign ^ b_sign; - cmp_res = bfdec_cmpu(a, b); - if (cmp_res < 0) { - const bfdec_t *tmp; - tmp = a; - a = b; - b = tmp; - a_sign = b_sign; /* b_sign is never used later */ - } - /* abs(a) >= abs(b) */ - if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { - /* zero result */ - bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); - ret = 0; - } else if (a->len == 0 || b->len == 0) { - ret = 0; - if (a->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN) { - /* at least one operand is NaN */ - bfdec_set_nan(r); - ret = 0; - } else if (b->expn == BF_EXP_INF && is_sub) { - /* infinities with different signs */ - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bfdec_set_inf(r, a_sign); - } - } else { - /* at least one zero and not subtract */ - if (bfdec_set(r, a)) - return BF_ST_MEM_ERROR; - r->sign = a_sign; - goto renorm; - } - } else { - slimb_t d, a_offset, b_offset, i, r_len; - limb_t carry; - limb_t *b1_tab; - int b_shift; - mp_size_t b1_len; - - d = a->expn - b->expn; - - /* XXX: not efficient in time and memory if the precision is - not infinite */ - r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); - if (bfdec_resize(r, r_len)) - goto fail; - r->sign = a_sign; - r->expn = a->expn; - - a_offset = r_len - a->len; - for(i = 0; i < a_offset; i++) - r->tab[i] = 0; - for(i = 0; i < a->len; i++) - r->tab[a_offset + i] = a->tab[i]; - - b_shift = d % LIMB_DIGITS; - if (b_shift == 0) { - b1_len = b->len; - b1_tab = (limb_t *)b->tab; - } else { - b1_len = b->len + 1; - b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); - if (!b1_tab) - goto fail; - b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * - mp_pow_dec[LIMB_DIGITS - b_shift]; - } - b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); - - if (is_sub) { - carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, - b1_tab, b1_len, 0); - if (carry != 0) { - carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, - r_len - (b_offset + b1_len)); - assert(carry == 0); - } - } else { - carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, - b1_tab, b1_len, 0); - if (carry != 0) { - carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, - r_len - (b_offset + b1_len)); - } - if (carry != 0) { - if (bfdec_resize(r, r_len + 1)) { - if (b_shift != 0) - bf_free(s, b1_tab); - goto fail; - } - r->tab[r_len] = 1; - r->expn += LIMB_DIGITS; - } - } - if (b_shift != 0) - bf_free(s, b1_tab); - renorm: - ret = bfdec_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bfdec_add_internal(r, a, b, prec, flags, 0); -} - -static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bfdec_add_internal(r, a, b, prec, flags, 1); -} - -int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_add); -} - -int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_sub); -} - -int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - int ret, r_sign; - - if (a->len < b->len) { - const bfdec_t *tmp = a; - a = b; - b = tmp; - } - r_sign = a->sign ^ b->sign; - /* here b->len <= a->len */ - if (b->len == 0) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - ret = 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { - if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || - (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - bfdec_set_inf(r, r_sign); - ret = 0; - } - } else { - bfdec_set_zero(r, r_sign); - ret = 0; - } - } else { - bfdec_t tmp, *r1 = NULL; - limb_t a_len, b_len; - limb_t *a_tab, *b_tab; - - a_len = a->len; - b_len = b->len; - a_tab = a->tab; - b_tab = b->tab; - - if (r == a || r == b) { - bfdec_init(r->ctx, &tmp); - r1 = r; - r = &tmp; - } - if (bfdec_resize(r, a_len + b_len)) { - bfdec_set_nan(r); - ret = BF_ST_MEM_ERROR; - goto done; - } - mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); - r->sign = r_sign; - r->expn = a->expn + b->expn; - ret = bfdec_normalize_and_round(r, prec, flags); - done: - if (r == &tmp) - bfdec_move(r1, &tmp); - } - return ret; -} - -int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bfdec_t b; - int ret; - bfdec_init(r->ctx, &b); - ret = bfdec_set_si(&b, b1); - ret |= bfdec_mul(r, a, &b, prec, flags); - bfdec_delete(&b); - return ret; -} - -int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags) -{ - bfdec_t b; - int ret; - - bfdec_init(r->ctx, &b); - ret = bfdec_set_si(&b, b1); - ret |= bfdec_add(r, a, &b, prec, flags); - bfdec_delete(&b); - return ret; -} - -static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags) -{ - int ret, r_sign; - limb_t n, nb, precl; - - r_sign = a->sign ^ b->sign; - if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else if (a->expn == BF_EXP_INF) { - bfdec_set_inf(r, r_sign); - return 0; - } else { - bfdec_set_zero(r, r_sign); - return 0; - } - } else if (a->expn == BF_EXP_ZERO) { - if (b->expn == BF_EXP_ZERO) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bfdec_set_zero(r, r_sign); - return 0; - } - } else if (b->expn == BF_EXP_ZERO) { - bfdec_set_inf(r, r_sign); - return BF_ST_DIVIDE_ZERO; - } - - nb = b->len; - if (prec == BF_PREC_INF) { - /* infinite precision: return BF_ST_INVALID_OP if not an exact - result */ - /* XXX: check */ - precl = nb + 1; - } else if (flags & BF_FLAG_RADPNT_PREC) { - /* number of digits after the decimal point */ - /* XXX: check (2 extra digits for rounding + 2 digits) */ - precl = (bf_max(a->expn - b->expn, 0) + 2 + - prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; - } else { - /* number of limbs of the quotient (2 extra digits for rounding) */ - precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; - } - n = bf_max(a->len, precl); - - { - limb_t *taba, na, i; - slimb_t d; - - na = n + nb; - taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); - if (!taba) - goto fail; - d = na - a->len; - memset(taba, 0, d * sizeof(limb_t)); - memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); - if (bfdec_resize(r, n + 1)) - goto fail1; - if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { - fail1: - bf_free(r->ctx, taba); - goto fail; - } - /* see if non zero remainder */ - for(i = 0; i < nb; i++) { - if (taba[i] != 0) - break; - } - bf_free(r->ctx, taba); - if (i != nb) { - if (prec == BF_PREC_INF) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - r->tab[0] |= 1; - } - } - r->expn = a->expn - b->expn + LIMB_DIGITS; - r->sign = r_sign; - ret = bfdec_normalize_and_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags) -{ - return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, - (bf_op2_func_t *)__bfdec_div); -} - -/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the - integer defined as floor(a/b) and r = a - q * b. */ -static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, - const bfdec_t *a, const bfdec_t *b) -{ - if (bfdec_cmpu(a, b) < 0) { - bfdec_set_ui(q, 0); - bfdec_set(r, a); - } else { - bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); - bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); - bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); - } -} - -/* division and remainder. - - rnd_mode is the rounding mode for the quotient. The additional - rounding mode BF_RND_EUCLIDIAN is supported. - - 'q' is an integer. 'r' is rounded with prec and flags (prec can be - BF_PREC_INF). -*/ -int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode) -{ - bf_context_t *s = q->ctx; - bfdec_t a1_s, *a1 = &a1_s; - bfdec_t b1_s, *b1 = &b1_s; - bfdec_t r1_s, *r1 = &r1_s; - int q_sign, res; - BOOL is_ceil, is_rndn; - - assert(q != a && q != b); - assert(r != a && r != b); - assert(q != r); - - if (a->len == 0 || b->len == 0) { - bfdec_set_zero(q, 0); - if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - return 0; - } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { - bfdec_set_nan(r); - return BF_ST_INVALID_OP; - } else { - bfdec_set(r, a); - return bfdec_round(r, prec, flags); - } - } - - q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); - switch(rnd_mode) { - default: - case BF_RNDZ: - case BF_RNDN: - case BF_RNDNA: - is_ceil = FALSE; - break; - case BF_RNDD: - is_ceil = q_sign; - break; - case BF_RNDU: - is_ceil = q_sign ^ 1; - break; - case BF_RNDA: - is_ceil = TRUE; - break; - case BF_DIVREM_EUCLIDIAN: - is_ceil = a->sign; - break; - } - - a1->expn = a->expn; - a1->tab = a->tab; - a1->len = a->len; - a1->sign = 0; - - b1->expn = b->expn; - b1->tab = b->tab; - b1->len = b->len; - b1->sign = 0; - - // bfdec_print_str("a1", a1); - // bfdec_print_str("b1", b1); - /* XXX: could improve to avoid having a large 'q' */ - bfdec_tdivremu(s, q, r, a1, b1); - if (bfdec_is_nan(q) || bfdec_is_nan(r)) - goto fail; - // bfdec_print_str("q", q); - // bfdec_print_str("r", r); - - if (r->len != 0) { - if (is_rndn) { - bfdec_init(s, r1); - if (bfdec_set(r1, r)) - goto fail; - if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { - bfdec_delete(r1); - goto fail; - } - res = bfdec_cmpu(r1, b); - bfdec_delete(r1); - if (res > 0 || - (res == 0 && - (rnd_mode == BF_RNDNA || - (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { - goto do_sub_r; - } - } else if (is_ceil) { - do_sub_r: - res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); - res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); - if (res & BF_ST_MEM_ERROR) - goto fail; - } - } - - r->sign ^= a->sign; - q->sign = q_sign; - return bfdec_round(r, prec, flags); - fail: - bfdec_set_nan(q); - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode) -{ - bfdec_t q_s, *q = &q_s; - int ret; - - bfdec_init(r->ctx, q); - ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); - bfdec_delete(q); - return ret; -} - -/* convert to integer (infinite precision) */ -int bfdec_rint(bfdec_t *r, int rnd_mode) -{ - return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); -} - -int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) -{ - bf_context_t *s = a->ctx; - int ret, k; - limb_t *a1, v; - slimb_t n, n1, prec1; - limb_t res; - - assert(r != a); - - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bfdec_set_nan(r); - } else if (a->expn == BF_EXP_INF && a->sign) { - goto invalid_op; - } else { - bfdec_set(r, a); - } - ret = 0; - } else if (a->sign || prec == BF_PREC_INF) { - invalid_op: - bfdec_set_nan(r); - ret = BF_ST_INVALID_OP; - } else { - if (flags & BF_FLAG_RADPNT_PREC) { - prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); - } else { - prec1 = prec; - } - /* convert the mantissa to an integer with at least 2 * - prec + 4 digits */ - n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); - if (bfdec_resize(r, n)) - goto fail; - a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); - if (!a1) - goto fail; - n1 = bf_min(2 * n, a->len); - memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); - memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); - if (a->expn & 1) { - res = mp_shr_dec(a1, a1, 2 * n, 1, 0); - } else { - res = 0; - } - /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 - because mp_sqrtrem2_dec already does it */ - k = 0; - if (n > 1) { - v = a1[2 * n - 1]; - while (v < BF_DEC_BASE / 4) { - k++; - v *= 4; - } - if (k != 0) - mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); - } - if (mp_sqrtrem_dec(s, r->tab, a1, n)) { - bf_free(s, a1); - goto fail; - } - if (k != 0) - mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); - if (!res) { - res = mp_scan_nz(a1, n + 1); - } - bf_free(s, a1); - if (!res) { - res = mp_scan_nz(a->tab, a->len - n1); - } - if (res != 0) - r->tab[0] |= 1; - r->sign = 0; - r->expn = (a->expn + 1) >> 1; - ret = bfdec_round(r, prec, flags); - } - return ret; - fail: - bfdec_set_nan(r); - return BF_ST_MEM_ERROR; -} - -/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there - is an overflow and 0 otherwise. No memory error is possible. */ -int bfdec_get_int32(int *pres, const bfdec_t *a) -{ - uint32_t v; - int ret; - if (a->expn >= BF_EXP_INF) { - ret = 0; - if (a->expn == BF_EXP_INF) { - v = (uint32_t)INT32_MAX + a->sign; - /* XXX: return overflow ? */ - } else { - v = INT32_MAX; - } - } else if (a->expn <= 0) { - v = 0; - ret = 0; - } else if (a->expn <= 9) { - v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); - if (a->sign) - v = -v; - ret = 0; - } else if (a->expn == 10) { - uint64_t v1; - uint32_t v_max; -#if LIMB_BITS == 64 - v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); -#else - v1 = (uint64_t)a->tab[a->len - 1] * 10 + - get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); -#endif - v_max = (uint32_t)INT32_MAX + a->sign; - if (v1 > v_max) { - v = v_max; - ret = BF_ST_OVERFLOW; - } else { - v = v1; - if (a->sign) - v = -v; - ret = 0; - } - } else { - v = (uint32_t)INT32_MAX + a->sign; - ret = BF_ST_OVERFLOW; - } - *pres = v; - return ret; -} - -/* power to an integer with infinite precision */ -int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) -{ - int ret, n_bits, i; - - assert(r != a); - if (b == 0) - return bfdec_set_ui(r, 1); - ret = bfdec_set(r, a); - n_bits = LIMB_BITS - clz(b); - for(i = n_bits - 2; i >= 0; i--) { - ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); - if ((b >> i) & 1) - ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); - } - return ret; -} - -char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) -{ - return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE); -} - -int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, - limb_t prec, bf_flags_t flags) -{ - slimb_t dummy_exp; - return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, - flags, TRUE); -} - -#endif /* USE_BF_DEC */ - -#ifdef USE_FFT_MUL -/***************************************************************/ -/* Integer multiplication with FFT */ - -/* or LIMB_BITS at bit position 'pos' in tab */ -static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val) -{ - limb_t i; - int p; - - i = pos >> LIMB_LOG2_BITS; - p = pos & (LIMB_BITS - 1); - if (i < len) - tab[i] |= val << p; - if (p != 0) { - i++; - if (i < len) { - tab[i] |= val >> (LIMB_BITS - p); - } - } -} - -#if defined(__AVX2__) - -typedef double NTTLimb; - -/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ -#define NTT_MOD_LOG2_MIN 50 -#define NTT_MOD_LOG2_MAX 51 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 39 -static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, }, - { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd, - 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a, - 0x00066015555557e3, 0x000725960b60b623, - 0x0002fc1fa1d6ce12, -}; - -#else - -typedef limb_t NTTLimb; - -#if LIMB_BITS == 64 - -#define NTT_MOD_LOG2_MIN 61 -#define NTT_MOD_LOG2_MAX 62 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 51 -static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, }, - { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4, - 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638, - 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f, - 0x3a5493e93e93e94a, -}; - -#elif LIMB_BITS == 32 - -/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ -#define NTT_MOD_LOG2_MIN 29 -#define NTT_MOD_LOG2_MAX 30 -#define NB_MODS 5 -#define NTT_PROOT_2EXP 20 -static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, }; - -static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001, -}; - -static const limb_t ntt_proot[2][NB_MODS] = { - { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, }, - { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, }, -}; - -static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { - 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b, - 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938, - 0x000000000932ab3e, 0x000000002f40eef8, - 0x000000002e760905, -}; - -#endif /* LIMB_BITS */ - -#endif /* !AVX2 */ - -#if defined(__AVX2__) -#define NTT_TRIG_K_MAX 18 -#else -#define NTT_TRIG_K_MAX 19 -#endif - -typedef struct BFNTTState { - bf_context_t *ctx; - - /* used for mul_mod_fast() */ - limb_t ntt_mods_div[NB_MODS]; - - limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1]; - limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1]; - NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1]; - /* 1/2^n mod m */ - limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2]; -#if defined(__AVX2__) - __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2]; - __m256d ntt_mods_vec[NB_MODS]; - __m256d ntt_mods_inv_vec[NB_MODS]; -#else - limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2]; -#endif -} BFNTTState; - -static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx); - -/* add modulo with up to (LIMB_BITS-1) bit modulo */ -static inline limb_t add_mod(limb_t a, limb_t b, limb_t m) -{ - limb_t r; - r = a + b; - if (r >= m) - r -= m; - return r; -} - -/* sub modulo with up to LIMB_BITS bit modulo */ -static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m) -{ - limb_t r; - r = a - b; - if (r > a) - r += m; - return r; -} - -/* return (r0+r1*B) mod m - precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) -*/ -static inline limb_t mod_fast(dlimb_t r, - limb_t m, limb_t m_inv) -{ - limb_t a1, q, t0, r1, r0; - - a1 = r >> NTT_MOD_LOG2_MIN; - - q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS; - r = r - (dlimb_t)q * m - m * 2; - r1 = r >> LIMB_BITS; - t0 = (slimb_t)r1 >> 1; - r += m & t0; - r0 = r; - r1 = r >> LIMB_BITS; - r0 += m & r1; - return r0; -} - -/* faster version using precomputed modulo inverse. - precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */ -static inline limb_t mul_mod_fast(limb_t a, limb_t b, - limb_t m, limb_t m_inv) -{ - dlimb_t r; - r = (dlimb_t)a * (dlimb_t)b; - return mod_fast(r, m, m_inv); -} - -static inline limb_t init_mul_mod_fast(limb_t m) -{ - dlimb_t t; - assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX); - assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN); - t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN); - return t / m; -} - -/* Faster version used when the multiplier is constant. 0 <= a < 2^64, - 0 <= b < m. */ -static inline limb_t mul_mod_fast2(limb_t a, limb_t b, - limb_t m, limb_t b_inv) -{ - limb_t r, q; - - q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; - r = a * b - q * m; - if (r >= m) - r -= m; - return r; -} - -/* Faster version used when the multiplier is constant. 0 <= a < 2^64, - 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r + - m'. */ -static inline limb_t mul_mod_fast3(limb_t a, limb_t b, - limb_t m, limb_t b_inv) -{ - limb_t r, q; - - q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; - r = a * b - q * m; - return r; -} - -static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m) -{ - return ((dlimb_t)b << LIMB_BITS) / m; -} - -#ifdef __AVX2__ - -static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) -{ - slimb_t v; - v = a; - if (v < 0) - v += m; - if (v >= m) - v -= m; - return v; -} - -static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m) -{ - return (slimb_t)a; -} - -static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m) -{ - if (a >= (m / 2)) - a -= m; - return (slimb_t)a; -} - -/* return r + m if r < 0 otherwise r. */ -static inline __m256d ntt_mod1(__m256d r, __m256d m) -{ - return _mm256_blendv_pd(r, r + m, r); -} - -/* input: abs(r) < 2 * m. Output: abs(r) < m */ -static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f) -{ - return _mm256_blendv_pd(r, r + m2f, r) - mf; -} - -/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */ -static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf, - __m256d m_inv) -{ - __m256d r, q, ab1, ab0, qm0, qm1; - ab1 = a * b; - q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */ - qm1 = q * mf; - qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */ - ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */ - r = (ab1 - qm1) + (ab0 - qm0); - return r; -} - -static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align) -{ - void *ptr; - void **ptr1; - ptr = bf_malloc(s, size + sizeof(void *) + align - 1); - if (!ptr) - return NULL; - ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) & - ~(align - 1)); - ptr1[-1] = ptr; - return ptr1; -} - -static void bf_aligned_free(bf_context_t *s, void *ptr) -{ - if (!ptr) - return; - bf_free(s, ((void **)ptr)[-1]); -} - -static void *ntt_malloc(BFNTTState *s, size_t size) -{ - return bf_aligned_malloc(s->ctx, size, 64); -} - -static void ntt_free(BFNTTState *s, void *ptr) -{ - bf_aligned_free(s->ctx, ptr); -} - -static no_inline int ntt_fft(BFNTTState *s, - NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) -{ - limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j; - NTTLimb *tab_in, *tab_out, *tmp, *trig; - __m256d m_inv, mf, m2f, c, a0, a1, b0, b1; - limb_t m; - int l; - - m = ntt_mods[m_idx]; - - m_inv = _mm256_set1_pd(1.0 / (double)m); - mf = _mm256_set1_pd(m); - m2f = _mm256_set1_pd(m * 2); - - n = (limb_t)1 << fft_len_log2; - assert(n >= 8); - stride_in = n / 2; - - tab_in = in_buf; - tab_out = tmp_buf; - trig = get_trig(s, fft_len_log2, inverse, m_idx); - if (!trig) - return -1; - p = 0; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - c = _mm256_load_pd(trig); - trig += 4; - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - a0 = _mm256_permute2f128_pd(b0, b1, 0x20); - a1 = _mm256_permute2f128_pd(b0, b1, 0x31); - a0 = _mm256_permute4x64_pd(a0, 0xd8); - a1 = _mm256_permute4x64_pd(a1, 0xd8); - _mm256_store_pd(&tab_out[p], a0); - _mm256_store_pd(&tab_out[p + 4], a1); - p += 2 * 4; - } - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - - trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx); - if (!trig) - return -1; - p = 0; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]); - trig += 2; - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - a0 = _mm256_permute2f128_pd(b0, b1, 0x20); - a1 = _mm256_permute2f128_pd(b0, b1, 0x31); - _mm256_store_pd(&tab_out[p], a0); - _mm256_store_pd(&tab_out[p + 4], a1); - p += 2 * 4; - } - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - - nb_blocks = n / 4; - fft_per_block = 4; - - l = fft_len_log2 - 2; - while (nb_blocks != 2) { - nb_blocks >>= 1; - p = 0; - k = 0; - trig = get_trig(s, l, inverse, m_idx); - if (!trig) - return -1; - for(i = 0; i < nb_blocks; i++) { - c = _mm256_set1_pd(trig[0]); - trig++; - for(j = 0; j < fft_per_block; j += 4) { - a0 = _mm256_load_pd(&tab_in[k + j]); - a1 = _mm256_load_pd(&tab_in[k + j + stride_in]); - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); - _mm256_store_pd(&tab_out[p + j], b0); - _mm256_store_pd(&tab_out[p + j + fft_per_block], b1); - } - k += fft_per_block; - p += 2 * fft_per_block; - } - fft_per_block <<= 1; - l--; - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - } - - tab_out = out_buf; - for(k = 0; k < stride_in; k += 4) { - a0 = _mm256_load_pd(&tab_in[k]); - a1 = _mm256_load_pd(&tab_in[k + stride_in]); - b0 = ntt_mod(a0 + a1, mf, m2f); - b1 = ntt_mod(a0 - a1, mf, m2f); - _mm256_store_pd(&tab_out[k], b0); - _mm256_store_pd(&tab_out[k + stride_in], b1); - } - return 0; -} - -static void ntt_vec_mul(BFNTTState *s, - NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2, - int k_tot, int m_idx) -{ - limb_t i, c_inv, n, m; - __m256d m_inv, mf, a, b, c; - - m = ntt_mods[m_idx]; - c_inv = s->ntt_len_inv[m_idx][k_tot][0]; - m_inv = _mm256_set1_pd(1.0 / (double)m); - mf = _mm256_set1_pd(m); - c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m)); - n = (limb_t)1 << fft_len_log2; - for(i = 0; i < n; i += 4) { - a = _mm256_load_pd(&tab1[i]); - b = _mm256_load_pd(&tab2[i]); - a = ntt_mul_mod(a, b, mf, m_inv); - a = ntt_mul_mod(a, c, mf, m_inv); - _mm256_store_pd(&tab1[i], a); - } -} - -static no_inline void mul_trig(NTTLimb *buf, - limb_t n, limb_t c1, limb_t m, limb_t m_inv1) -{ - limb_t i, c2, c3, c4; - __m256d c, c_mul, a0, mf, m_inv; - assert(n >= 2); - - mf = _mm256_set1_pd(m); - m_inv = _mm256_set1_pd(1.0 / (double)m); - - c2 = mul_mod_fast(c1, c1, m, m_inv1); - c3 = mul_mod_fast(c2, c1, m, m_inv1); - c4 = mul_mod_fast(c2, c2, m, m_inv1); - c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m), - int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m)); - c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m)); - for(i = 0; i < n; i += 4) { - a0 = _mm256_load_pd(&buf[i]); - a0 = ntt_mul_mod(a0, c, mf, m_inv); - _mm256_store_pd(&buf[i], a0); - c = ntt_mul_mod(c, c_mul, mf, m_inv); - } -} - -#else - -static void *ntt_malloc(BFNTTState *s, size_t size) -{ - return bf_malloc(s->ctx, size); -} - -static void ntt_free(BFNTTState *s, void *ptr) -{ - bf_free(s->ctx, ptr); -} - -static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) -{ - if (a >= m) - a -= m; - return a; -} - -static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) -{ - return a; -} - -static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) -{ - limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2; - NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv; - int l; - - m = ntt_mods[m_idx]; - m2 = 2 * m; - n = (limb_t)1 << fft_len_log2; - nb_blocks = n; - fft_per_block = 1; - stride_in = n / 2; - tab_in = in_buf; - tab_out = tmp_buf; - l = fft_len_log2; - while (nb_blocks != 2) { - nb_blocks >>= 1; - p = 0; - k = 0; - trig = get_trig(s, l, inverse, m_idx); - if (!trig) - return -1; - for(i = 0; i < nb_blocks; i++) { - c = trig[0]; - c_inv = trig[1]; - trig += 2; - for(j = 0; j < fft_per_block; j++) { - a0 = tab_in[k + j]; - a1 = tab_in[k + j + stride_in]; - b0 = add_mod(a0, a1, m2); - b1 = a0 - a1 + m2; - b1 = mul_mod_fast3(b1, c, m, c_inv); - tab_out[p + j] = b0; - tab_out[p + j + fft_per_block] = b1; - } - k += fft_per_block; - p += 2 * fft_per_block; - } - fft_per_block <<= 1; - l--; - tmp = tab_in; - tab_in = tab_out; - tab_out = tmp; - } - /* no twiddle in last step */ - tab_out = out_buf; - for(k = 0; k < stride_in; k++) { - a0 = tab_in[k]; - a1 = tab_in[k + stride_in]; - b0 = add_mod(a0, a1, m2); - b1 = sub_mod(a0, a1, m2); - tab_out[k] = b0; - tab_out[k + stride_in] = b1; - } - return 0; -} - -static void ntt_vec_mul(BFNTTState *s, - NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2, - int k_tot, int m_idx) -{ - limb_t i, norm, norm_inv, a, n, m, m_inv; - - m = ntt_mods[m_idx]; - m_inv = s->ntt_mods_div[m_idx]; - norm = s->ntt_len_inv[m_idx][k_tot][0]; - norm_inv = s->ntt_len_inv[m_idx][k_tot][1]; - n = (limb_t)1 << fft_len_log2; - for(i = 0; i < n; i++) { - a = tab1[i]; - /* need to reduce the range so that the product is < - 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */ - if (a >= m) - a -= m; - a = mul_mod_fast(a, tab2[i], m, m_inv); - a = mul_mod_fast3(a, norm, m, norm_inv); - tab1[i] = a; - } -} - -static no_inline void mul_trig(NTTLimb *buf, - limb_t n, limb_t c_mul, limb_t m, limb_t m_inv) -{ - limb_t i, c0, c_mul_inv; - - c0 = 1; - c_mul_inv = init_mul_mod_fast2(c_mul, m); - for(i = 0; i < n; i++) { - buf[i] = mul_mod_fast(buf[i], c0, m, m_inv); - c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv); - } -} - -#endif /* !AVX2 */ - -static no_inline NTTLimb *get_trig(BFNTTState *s, - int k, int inverse, int m_idx) -{ - NTTLimb *tab; - limb_t i, n2, c, c_mul, m, c_mul_inv; - - if (k > NTT_TRIG_K_MAX) - return NULL; - - tab = s->ntt_trig[m_idx][inverse][k]; - if (tab) - return tab; - n2 = (limb_t)1 << (k - 1); - m = ntt_mods[m_idx]; -#ifdef __AVX2__ - tab = ntt_malloc(s, sizeof(NTTLimb) * n2); -#else - tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); -#endif - if (!tab) - return NULL; - c = 1; - c_mul = s->ntt_proot_pow[m_idx][inverse][k]; - c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k]; - for(i = 0; i < n2; i++) { -#ifdef __AVX2__ - tab[i] = int_to_ntt_limb2(c, m); -#else - tab[2 * i] = int_to_ntt_limb(c, m); - tab[2 * i + 1] = init_mul_mod_fast2(c, m); -#endif - c = mul_mod_fast2(c, c_mul, m, c_mul_inv); - } - s->ntt_trig[m_idx][inverse][k] = tab; - return tab; -} - -void fft_clear_cache(bf_context_t *s1) -{ - int m_idx, inverse, k; - BFNTTState *s = s1->ntt_state; - if (s) { - for(m_idx = 0; m_idx < NB_MODS; m_idx++) { - for(inverse = 0; inverse < 2; inverse++) { - for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) { - if (s->ntt_trig[m_idx][inverse][k]) { - ntt_free(s, s->ntt_trig[m_idx][inverse][k]); - s->ntt_trig[m_idx][inverse][k] = NULL; - } - } - } - } -#if defined(__AVX2__) - bf_aligned_free(s1, s); -#else - bf_free(s1, s); -#endif - s1->ntt_state = NULL; - } -} - -#define STRIP_LEN 16 - -/* dst = buf1, src = buf2 */ -static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, - int k1, int k2, limb_t n1, limb_t n2, int inverse, - limb_t m_idx) -{ - limb_t i, j, c_mul, c0, m, m_inv, strip_len, l; - NTTLimb *buf2, *buf3; - - buf2 = NULL; - buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1); - if (!buf3) - goto fail; - if (k2 == 0) { - if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx)) - goto fail; - } else { - strip_len = STRIP_LEN; - buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len); - if (!buf2) - goto fail; - m = ntt_mods[m_idx]; - m_inv = s->ntt_mods_div[m_idx]; - c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2]; - c_mul = 1; - assert((n2 % strip_len) == 0); - for(j = 0; j < n2; j += strip_len) { - for(i = 0; i < n1; i++) { - for(l = 0; l < strip_len; l++) { - buf2[i + l * n1] = buf1[i * n2 + (j + l)]; - } - } - for(l = 0; l < strip_len; l++) { - if (inverse) - mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx)) - goto fail; - if (!inverse) - mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - c_mul = mul_mod_fast(c_mul, c0, m, m_inv); - } - - for(i = 0; i < n1; i++) { - for(l = 0; l < strip_len; l++) { - buf1[i * n2 + (j + l)] = buf2[i + l *n1]; - } - } - } - ntt_free(s, buf2); - } - ntt_free(s, buf3); - return 0; - fail: - ntt_free(s, buf2); - ntt_free(s, buf3); - return -1; -} - - -/* dst = buf1, src = buf2, tmp = buf3 */ -static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, - int k, int k_tot, limb_t m_idx) -{ - limb_t n1, n2, i; - int k1, k2; - - if (k <= NTT_TRIG_K_MAX) { - k1 = k; - } else { - /* recursive split of the FFT */ - k1 = bf_min(k / 2, NTT_TRIG_K_MAX); - } - k2 = k - k1; - n1 = (limb_t)1 << k1; - n2 = (limb_t)1 << k2; - - if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx)) - return -1; - if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx)) - return -1; - if (k2 == 0) { - ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx); - } else { - for(i = 0; i < n1; i++) { - ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); - } - } - if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) - return -1; - return 0; -} - - -static no_inline void limb_to_ntt(BFNTTState *s, - NTTLimb *tabr, limb_t fft_len, - const limb_t *taba, limb_t a_len, int dpl, - int first_m_idx, int nb_mods) -{ - slimb_t i, n; - dlimb_t a, b; - int j, shift; - limb_t base_mask1, a0, a1, a2, r, m, m_inv; - -#if 0 - for(i = 0; i < a_len; i++) { - printf("%" PRId64 ": " FMT_LIMB "\n", - (int64_t)i, taba[i]); - } -#endif - memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods); - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl); - for(i = 0; i < n; i++) { - a0 = get_bits(taba, a_len, i * dpl); - if (dpl <= LIMB_BITS) { - a0 &= base_mask1; - a = a0; - } else { - a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS); - if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) { - a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS); - } else { - if (dpl > 2 * LIMB_BITS) { - a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) & - base_mask1; - } else { - a1 &= base_mask1; - a2 = 0; - } - // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0); - a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | - ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) | - ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)); - a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1; - } - } - for(j = 0; j < nb_mods; j++) { - m = ntt_mods[first_m_idx + j]; - m_inv = s->ntt_mods_div[first_m_idx + j]; - r = mod_fast(a, m, m_inv); - if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) { - b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0; - r = mod_fast(b, m, m_inv); - } - tabr[i + j * fft_len] = int_to_ntt_limb(r, m); - } - } -} - -#if defined(__AVX2__) - -#define VEC_LEN 4 - -typedef union { - __m256d v; - double d[4]; -} VecUnion; - -static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, - const NTTLimb *buf, int fft_len_log2, int dpl, - int nb_mods) -{ - const limb_t *mods = ntt_mods + NB_MODS - nb_mods; - const __m256d *mods_cr_vec, *mf, *m_inv; - VecUnion y[NB_MODS]; - limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; - slimb_t i, len, pos; - int j, k, l, shift, n_limb1, p; - dlimb_t t; - - j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; - mods_cr_vec = s->ntt_mods_cr_vec + j; - mf = s->ntt_mods_vec + NB_MODS - nb_mods; - m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods; - - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; - for(j = 0; j < NB_MODS; j++) - carry[j] = 0; - for(j = 0; j < NB_MODS; j++) - u[j] = 0; /* avoid warnings */ - memset(tabr, 0, sizeof(limb_t) * r_len); - fft_len = (limb_t)1 << fft_len_log2; - len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); - len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1); - i = 0; - while (i < len) { - for(j = 0; j < nb_mods; j++) - y[j].v = *(__m256d *)&buf[i + fft_len * j]; - - /* Chinese remainder to get mixed radix representation */ - l = 0; - for(j = 0; j < nb_mods - 1; j++) { - y[j].v = ntt_mod1(y[j].v, mf[j]); - for(k = j + 1; k < nb_mods; k++) { - y[k].v = ntt_mul_mod(y[k].v - y[j].v, - mods_cr_vec[l], mf[k], m_inv[k]); - l++; - } - } - y[j].v = ntt_mod1(y[j].v, mf[j]); - - for(p = 0; p < VEC_LEN; p++) { - /* back to normal representation */ - u[0] = (int64_t)y[nb_mods - 1].d[p]; - l = 1; - for(j = nb_mods - 2; j >= 1; j--) { - r = (int64_t)y[j].d[p]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r; - l++; - } - /* XXX: for nb_mods = 5, l should be 4 */ - - /* last step adds the carry */ - r = (int64_t)y[0].d[p]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r + carry[k]; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r + carry[l]; - -#if 0 - printf("%" PRId64 ": ", i); - for(j = nb_mods - 1; j >= 0; j--) { - printf(" %019" PRIu64, u[j]); - } - printf("\n"); -#endif - - /* write the digits */ - pos = i * dpl; - for(j = 0; j < n_limb1; j++) { - put_bits(tabr, r_len, pos, u[j]); - pos += LIMB_BITS; - } - put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); - /* shift by dpl digits and set the carry */ - if (shift == 0) { - for(j = n_limb1 + 1; j < nb_mods; j++) - carry[j - (n_limb1 + 1)] = u[j]; - } else { - for(j = n_limb1; j < nb_mods - 1; j++) { - carry[j - n_limb1] = (u[j] >> shift) | - (u[j + 1] << (LIMB_BITS - shift)); - } - carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; - } - i++; - } - } -} -#else -static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, - const NTTLimb *buf, int fft_len_log2, int dpl, - int nb_mods) -{ - const limb_t *mods = ntt_mods + NB_MODS - nb_mods; - const limb_t *mods_cr, *mods_cr_inv; - limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; - slimb_t i, len, pos; - int j, k, l, shift, n_limb1; - dlimb_t t; - - j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; - mods_cr = ntt_mods_cr + j; - mods_cr_inv = s->ntt_mods_cr_inv + j; - - shift = dpl & (LIMB_BITS - 1); - if (shift == 0) - base_mask1 = -1; - else - base_mask1 = ((limb_t)1 << shift) - 1; - n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; - for(j = 0; j < NB_MODS; j++) - carry[j] = 0; - for(j = 0; j < NB_MODS; j++) - u[j] = 0; /* avoid warnings */ - memset(tabr, 0, sizeof(limb_t) * r_len); - fft_len = (limb_t)1 << fft_len_log2; - len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); - for(i = 0; i < len; i++) { - for(j = 0; j < nb_mods; j++) { - y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]); - } - - /* Chinese remainder to get mixed radix representation */ - l = 0; - for(j = 0; j < nb_mods - 1; j++) { - for(k = j + 1; k < nb_mods; k++) { - limb_t m; - m = mods[k]; - /* Note: there is no overflow in the sub_mod() because - the modulos are sorted by increasing order */ - y[k] = mul_mod_fast2(y[k] - y[j] + m, - mods_cr[l], m, mods_cr_inv[l]); - l++; - } - } - - /* back to normal representation */ - u[0] = y[nb_mods - 1]; - l = 1; - for(j = nb_mods - 2; j >= 1; j--) { - r = y[j]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r; - l++; - } - - /* last step adds the carry */ - r = y[0]; - for(k = 0; k < l; k++) { - t = (dlimb_t)u[k] * mods[j] + r + carry[k]; - r = t >> LIMB_BITS; - u[k] = t; - } - u[l] = r + carry[l]; - -#if 0 - printf("%" PRId64 ": ", (int64_t)i); - for(j = nb_mods - 1; j >= 0; j--) { - printf(" " FMT_LIMB, u[j]); - } - printf("\n"); -#endif - - /* write the digits */ - pos = i * dpl; - for(j = 0; j < n_limb1; j++) { - put_bits(tabr, r_len, pos, u[j]); - pos += LIMB_BITS; - } - put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); - /* shift by dpl digits and set the carry */ - if (shift == 0) { - for(j = n_limb1 + 1; j < nb_mods; j++) - carry[j - (n_limb1 + 1)] = u[j]; - } else { - for(j = n_limb1; j < nb_mods - 1; j++) { - carry[j - n_limb1] = (u[j] >> shift) | - (u[j + 1] << (LIMB_BITS - shift)); - } - carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; - } - } -} -#endif - -static int ntt_static_init(bf_context_t *s1) -{ - BFNTTState *s; - int inverse, i, j, k, l; - limb_t c, c_inv, c_inv2, m, m_inv; - - if (s1->ntt_state) - return 0; -#if defined(__AVX2__) - s = bf_aligned_malloc(s1, sizeof(*s), 64); -#else - s = bf_malloc(s1, sizeof(*s)); -#endif - if (!s) - return -1; - memset(s, 0, sizeof(*s)); - s1->ntt_state = s; - s->ctx = s1; - - for(j = 0; j < NB_MODS; j++) { - m = ntt_mods[j]; - m_inv = init_mul_mod_fast(m); - s->ntt_mods_div[j] = m_inv; -#if defined(__AVX2__) - s->ntt_mods_vec[j] = _mm256_set1_pd(m); - s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m); -#endif - c_inv2 = (m + 1) / 2; /* 1/2 */ - c_inv = 1; - for(i = 0; i <= NTT_PROOT_2EXP; i++) { - s->ntt_len_inv[j][i][0] = c_inv; - s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m); - c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv); - } - - for(inverse = 0; inverse < 2; inverse++) { - c = ntt_proot[inverse][j]; - for(i = 0; i < NTT_PROOT_2EXP; i++) { - s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c; - s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] = - init_mul_mod_fast2(c, m); - c = mul_mod_fast(c, c, m, m_inv); - } - } - } - - l = 0; - for(j = 0; j < NB_MODS - 1; j++) { - for(k = j + 1; k < NB_MODS; k++) { -#if defined(__AVX2__) - s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l], - ntt_mods[k])); -#else - s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l], - ntt_mods[k]); -#endif - l++; - } - } - return 0; -} - -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) -{ - int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found; - int int_bits, nb_mods_found; - limb_t cost, min_cost; - - min_cost = -1; - dpl_found = 0; - nb_mods_found = 4; - fft_len_log2_found = 0; - for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) { - int_bits = ntt_int_bits[NB_MODS - nb_mods]; - dpl = bf_min((int_bits - 4) / 2, - 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX); - for(;;) { - fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl); - if (fft_len_log2 > NTT_PROOT_2EXP) - goto next; - n_bits = fft_len_log2 + 2 * dpl; - if (n_bits <= int_bits) { - cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods; - // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost); - if (cost < min_cost) { - min_cost = cost; - dpl_found = dpl; - nb_mods_found = nb_mods; - fft_len_log2_found = fft_len_log2; - } - break; - } - dpl--; - if (dpl == 0) - break; - } - next: ; - } - if (!dpl_found) - abort(); - /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */ - if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) && - ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >= - len * LIMB_BITS) { - dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN; - } - *pnb_mods = nb_mods_found; - *pdpl = dpl_found; - return fft_len_log2_found; -} - -/* return 0 if OK, -1 if memory error */ -static no_inline int fft_mul(bf_context_t *s1, - bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags) -{ - BFNTTState *s; - int dpl, fft_len_log2, j, nb_mods, reduced_mem; - slimb_t len, fft_len; - NTTLimb *buf1, *buf2, *ptr; -#if defined(USE_MUL_CHECK) - limb_t ha, hb, hr, h_ref; -#endif - - if (ntt_static_init(s1)) - return -1; - s = s1->ntt_state; - - /* find the optimal number of digits per limb (dpl) */ - len = a_len + b_len; - fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len); - fft_len = (uint64_t)1 << fft_len_log2; - // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl); -#if defined(USE_MUL_CHECK) - ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0); - hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0); -#endif - if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) { - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); - } else if (mul_flags & FFT_MUL_R_OVERLAP_B) { - limb_t *tmp_tab, tmp_len; - /* it is better to free 'b' first */ - tmp_tab = a_tab; - a_tab = b_tab; - b_tab = tmp_tab; - tmp_len = a_len; - a_len = b_len; - b_len = tmp_len; - } - buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); - if (!buf1) - return -1; - limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl, - NB_MODS - nb_mods, nb_mods); - if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == - FFT_MUL_R_OVERLAP_A) { - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); - } - reduced_mem = (fft_len_log2 >= 14); - if (!reduced_mem) { - buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); - if (!buf2) - goto fail; - limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, - NB_MODS - nb_mods, nb_mods); - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); /* in case res == b */ - } else { - buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len); - if (!buf2) - goto fail; - } - for(j = 0; j < nb_mods; j++) { - if (reduced_mem) { - limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, - NB_MODS - nb_mods + j, 1); - ptr = buf2; - } else { - ptr = buf2 + fft_len * j; - } - if (ntt_conv(s, buf1 + fft_len * j, ptr, - fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) - goto fail; - } - if (!(mul_flags & FFT_MUL_R_NORESIZE)) - bf_resize(res, 0); /* in case res == b and reduced mem */ - ntt_free(s, buf2); - buf2 = NULL; - if (!(mul_flags & FFT_MUL_R_NORESIZE)) { - if (bf_resize(res, len)) - goto fail; - } - ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods); - ntt_free(s, buf1); -#if defined(USE_MUL_CHECK) - hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0); - h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD); - if (hr != h_ref) { - printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n", - len, fft_len_log2, dpl, nb_mods); - // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref); - exit(1); - } -#endif - return 0; - fail: - ntt_free(s, buf1); - ntt_free(s, buf2); - return -1; -} - -#else /* USE_FFT_MUL */ - -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) -{ - return 0; -} - -#endif /* !USE_FFT_MUL */ diff --git a/lib/quickjs/libbf.h b/lib/quickjs/libbf.h deleted file mode 100644 index a1436ab5..00000000 --- a/lib/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 <stddef.h> -#include <stdint.h> - -#if defined(__SIZEOF_INT128__) && (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/lib/quickjs/libregexp-opcode.h b/lib/quickjs/libregexp-opcode.h deleted file mode 100644 index f255e09f..00000000 --- a/lib/quickjs/libregexp-opcode.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Regular Expression Engine - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef DEF - -DEF(invalid, 1) /* never used */ -DEF(char, 3) -DEF(char32, 5) -DEF(dot, 1) -DEF(any, 1) /* same as dot but match any character including line terminator */ -DEF(line_start, 1) -DEF(line_end, 1) -DEF(goto, 5) -DEF(split_goto_first, 5) -DEF(split_next_first, 5) -DEF(match, 1) -DEF(save_start, 2) /* save start position */ -DEF(save_end, 2) /* save end position, must come after saved_start */ -DEF(save_reset, 3) /* reset save positions */ -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(not_word_boundary, 1) -DEF(back_reference, 2) -DEF(backward_back_reference, 2) /* must come after back_reference */ -DEF(range, 3) /* variable length */ -DEF(range32, 3) /* variable length */ -DEF(lookahead, 5) -DEF(negative_lookahead, 5) -DEF(push_char_pos, 1) /* push the character position on the stack */ -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) - -#endif /* DEF */ diff --git a/lib/quickjs/libregexp.c b/lib/quickjs/libregexp.c deleted file mode 100644 index e5693e1b..00000000 --- a/lib/quickjs/libregexp.c +++ /dev/null @@ -1,2523 +0,0 @@ -/* - * Regular Expression Engine - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <inttypes.h> -#include <string.h> -#include <assert.h> - -#include "cutils.h" -#include "libregexp.h" -#include "libunicode.h" - -/* - TODO: - - - Add a lock step execution mode (=linear time execution guaranteed) - when the regular expression is "simple" i.e. no backreference nor - complicated lookahead. The opcodes are designed for this execution - model. -*/ - -#if defined(TEST) -#define DUMP_REOP -#endif - -typedef enum { -#define DEF(id, size) REOP_ ## id, -#include "libregexp-opcode.h" -#undef DEF - REOP_COUNT, -} REOPCodeEnum; - -#define CAPTURE_COUNT_MAX 255 -#define STACK_SIZE_MAX 255 - -/* unicode code points */ -#define CP_LS 0x2028 -#define CP_PS 0x2029 - -#define TMP_BUF_SIZE 128 - -typedef struct { - DynBuf byte_code; - const uint8_t *buf_ptr; - const uint8_t *buf_end; - const uint8_t *buf_start; - int re_flags; - BOOL is_unicode; - BOOL ignore_case; - BOOL dotall; - int capture_count; - int total_capture_count; /* -1 = not computed yet */ - int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ - void *opaque; - DynBuf group_names; - union { - char error_msg[TMP_BUF_SIZE]; - char tmp_buf[TMP_BUF_SIZE]; - } u; -} REParseState; - -typedef struct { -#ifdef DUMP_REOP - const char *name; -#endif - uint8_t size; -} REOpCode; - -static const REOpCode reopcode_info[REOP_COUNT] = { -#ifdef DUMP_REOP -#define DEF(id, size) { #id, size }, -#else -#define DEF(id, size) { size }, -#endif -#include "libregexp-opcode.h" -#undef DEF -}; - -#define RE_HEADER_FLAGS 0 -#define RE_HEADER_CAPTURE_COUNT 1 -#define RE_HEADER_STACK_SIZE 2 -#define RE_HEADER_BYTECODE_LEN 3 - -#define RE_HEADER_LEN 7 - -static inline int is_digit(int c) { - return c >= '0' && c <= '9'; -} - -/* insert 'len' bytes at position 'pos'. Return < 0 if error. */ -static int dbuf_insert(DynBuf *s, int pos, int len) -{ - if (dbuf_realloc(s, s->size + len)) - return -1; - memmove(s->buf + pos + len, s->buf + pos, s->size - pos); - s->size += len; - return 0; -} - -static const uint16_t char_range_d[] = { - 1, - 0x0030, 0x0039 + 1, -}; - -/* code point ranges for Zs,Zl or Zp property */ -static const uint16_t char_range_s[] = { - 10, - 0x0009, 0x000D + 1, - 0x0020, 0x0020 + 1, - 0x00A0, 0x00A0 + 1, - 0x1680, 0x1680 + 1, - 0x2000, 0x200A + 1, - /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ - /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ - 0x2028, 0x2029 + 1, - 0x202F, 0x202F + 1, - 0x205F, 0x205F + 1, - 0x3000, 0x3000 + 1, - /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ - 0xFEFF, 0xFEFF + 1, -}; - -static const uint16_t char_range_w[] = { - 4, - 0x0030, 0x0039 + 1, - 0x0041, 0x005A + 1, - 0x005F, 0x005F + 1, - 0x0061, 0x007A + 1, -}; - -#define CLASS_RANGE_BASE 0x40000000 - -typedef enum { - CHAR_RANGE_d, - CHAR_RANGE_D, - CHAR_RANGE_s, - CHAR_RANGE_S, - CHAR_RANGE_w, - CHAR_RANGE_W, -} CharRangeEnum; - -static const uint16_t * 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) -{ - 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); - for(i = 0; i < len * 2; i++) { - if (cr_add_point(cr, c_pt[i])) - goto fail; - } - if (invert) { - if (cr_invert(cr)) - goto fail; - } - return 0; - fail: - cr_free(cr); - return -1; -} - -#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 = 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[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[RE_HEADER_CAPTURE_COUNT]; i++) { - if (i != 1) - printf(","); - printf("<%s>", p); - p += strlen(p) + 1; - } - printf("\n"); - assert(p == (char *)(buf + buf_len)); - } - printf("bytecode_len=%d\n", bc_len); - - buf += RE_HEADER_LEN; - pos = 0; - while (pos < bc_len) { - printf("%5u: ", pos); - opcode = buf[pos]; - len = reopcode_info[opcode].size; - if (opcode >= REOP_COUNT) { - printf(" invalid opcode=0x%02x\n", opcode); - break; - } - if ((pos + len) > bc_len) { - printf(" buffer overflow (opcode=0x%02x)\n", opcode); - break; - } - printf("%s", reopcode_info[opcode].name); - switch(opcode) { - case REOP_char: - val = get_u16(buf + pos + 1); - if (val >= ' ' && val <= 126) - printf(" '%c'", val); - else - printf(" 0x%04x", val); - break; - case REOP_char32: - val = get_u32(buf + pos + 1); - if (val >= ' ' && val <= 126) - printf(" '%c'", val); - else - printf(" 0x%08x", val); - break; - case REOP_goto: - case REOP_split_goto_first: - case REOP_split_next_first: - case REOP_loop: - case REOP_lookahead: - case REOP_negative_lookahead: - val = get_u32(buf + pos + 1); - val += (pos + 5); - printf(" %u", val); - break; - case REOP_simple_greedy_quant: - printf(" %u %u %u %u", - get_u32(buf + pos + 1) + (pos + 17), - get_u32(buf + pos + 1 + 4), - get_u32(buf + pos + 1 + 8), - get_u32(buf + pos + 1 + 12)); - break; - case REOP_save_start: - case REOP_save_end: - case REOP_back_reference: - case REOP_backward_back_reference: - printf(" %u", buf[pos + 1]); - break; - case REOP_save_reset: - printf(" %u %u", buf[pos + 1], buf[pos + 2]); - break; - case REOP_push_i32: - val = get_u32(buf + pos + 1); - printf(" %d", val); - break; - case REOP_range: - { - int n, i; - n = get_u16(buf + pos + 1); - len += n * 4; - for(i = 0; i < n * 2; i++) { - val = get_u16(buf + pos + 3 + i * 2); - printf(" 0x%04x", val); - } - } - break; - case REOP_range32: - { - int n, i; - n = get_u16(buf + pos + 1); - len += n * 8; - for(i = 0; i < n * 2; i++) { - val = get_u32(buf + pos + 3 + i * 4); - printf(" 0x%08x", val); - } - } - break; - default: - break; - } - printf("\n"); - pos += len; - } -} -#endif - -static void re_emit_op(REParseState *s, int op) -{ - dbuf_putc(&s->byte_code, op); -} - -/* return the offset of the u32 value */ -static int re_emit_op_u32(REParseState *s, int op, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val); - return pos; -} - -static int re_emit_goto(REParseState *s, int op, uint32_t val) -{ - int pos; - dbuf_putc(&s->byte_code, op); - pos = s->byte_code.size; - dbuf_put_u32(&s->byte_code, val - (pos + 4)); - return pos; -} - -static void re_emit_op_u8(REParseState *s, int op, uint32_t val) -{ - dbuf_putc(&s->byte_code, op); - dbuf_putc(&s->byte_code, val); -} - -static void re_emit_op_u16(REParseState *s, int op, uint32_t val) -{ - dbuf_putc(&s->byte_code, op); - dbuf_put_u16(&s->byte_code, val); -} - -static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap); - va_end(ap); - return -1; -} - -static int re_parse_out_of_memory(REParseState *s) -{ - return re_parse_error(s, "out of memory"); -} - -/* If allow_overflow is false, return -1 in case of - overflow. Otherwise return INT32_MAX. */ -static int parse_digits(const uint8_t **pp, BOOL allow_overflow) -{ - const uint8_t *p; - uint64_t v; - int c; - - p = *pp; - v = 0; - for(;;) { - c = *p; - if (c < '0' || c > '9') - break; - v = v * 10 + c - '0'; - if (v >= INT32_MAX) { - if (allow_overflow) - v = INT32_MAX; - else - return -1; - } - p++; - } - *pp = p; - return v; -} - -static int re_parse_expect(REParseState *s, const uint8_t **pp, int c) -{ - const uint8_t *p; - p = *pp; - if (*p != c) - return re_parse_error(s, "expecting '%c'", c); - p++; - *pp = p; - return 0; -} - -/* Parse an escape sequence, *pp points after the '\': - allow_utf16 value: - 0 : no UTF-16 escapes allowed - 1 : UTF-16 escapes allowed - 2 : UTF-16 escapes allowed and escapes of surrogate pairs are - converted to a unicode character (unicode regexp case). - - Return the unicode char and update *pp if recognized, - return -1 if malformed escape, - return -2 otherwise. */ -int lre_parse_escape(const uint8_t **pp, int allow_utf16) -{ - const uint8_t *p; - uint32_t c; - - p = *pp; - c = *p++; - switch(c) { - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case 'x': - case 'u': - { - int h, n, i; - uint32_t c1; - - if (*p == '{' && allow_utf16) { - p++; - c = 0; - for(;;) { - h = from_hex(*p++); - if (h < 0) - return -1; - c = (c << 4) | h; - if (c > 0x10FFFF) - return -1; - if (*p == '}') - break; - } - p++; - } else { - if (c == 'x') { - n = 2; - } else { - n = 4; - } - - c = 0; - for(i = 0; i < n; i++) { - h = from_hex(*p++); - if (h < 0) { - return -1; - } - c = (c << 4) | h; - } - if (is_hi_surrogate(c) && - allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { - /* convert an escaped surrogate pair into a - unicode char */ - c1 = 0; - for(i = 0; i < 4; i++) { - h = from_hex(p[2 + i]); - if (h < 0) - break; - c1 = (c1 << 4) | h; - } - if (i == 4 && is_lo_surrogate(c1)) { - p += 6; - c = from_surrogate(c, c1); - } - } - } - } - break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - c -= '0'; - if (allow_utf16 == 2) { - /* only accept \0 not followed by digit */ - if (c != 0 || is_digit(*p)) - return -1; - } else { - /* parse a legacy octal sequence */ - uint32_t v; - v = *p - '0'; - if (v > 7) - break; - c = (c << 3) | v; - p++; - if (c >= 32) - break; - v = *p - '0'; - if (v > 7) - break; - c = (c << 3) | v; - p++; - } - break; - default: - return -2; - } - *pp = p; - return c; -} - -#ifdef CONFIG_ALL_UNICODE -/* XXX: we use the same chars for name and value */ -static BOOL is_unicode_char(int c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c == '_')); -} - -static int parse_unicode_property(REParseState *s, CharRange *cr, - const uint8_t **pp, BOOL is_inv) -{ - const uint8_t *p; - char name[64], value[64]; - char *q; - BOOL script_ext; - int ret; - - p = *pp; - if (*p != '{') - return re_parse_error(s, "expecting '{' after \\p"); - p++; - q = name; - while (is_unicode_char(*p)) { - if ((q - name) >= sizeof(name) - 1) - goto unknown_property_name; - *q++ = *p++; - } - *q = '\0'; - q = value; - if (*p == '=') { - p++; - while (is_unicode_char(*p)) { - if ((q - value) >= sizeof(value) - 1) - return re_parse_error(s, "unknown unicode property value"); - *q++ = *p++; - } - } - *q = '\0'; - if (*p != '}') - return re_parse_error(s, "expecting '}'"); - p++; - // printf("name=%s value=%s\n", name, value); - - if (!strcmp(name, "Script") || !strcmp(name, "sc")) { - script_ext = FALSE; - goto do_script; - } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { - script_ext = TRUE; - do_script: - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_script(cr, value, script_ext); - if (ret) { - cr_free(cr); - if (ret == -2) - return re_parse_error(s, "unknown unicode script"); - else - goto out_of_memory; - } - } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_general_category(cr, value); - if (ret) { - cr_free(cr); - if (ret == -2) - return re_parse_error(s, "unknown unicode general category"); - else - goto out_of_memory; - } - } else if (value[0] == '\0') { - cr_init(cr, s->opaque, lre_realloc); - ret = unicode_general_category(cr, name); - if (ret == -1) { - cr_free(cr); - goto out_of_memory; - } - if (ret < 0) { - ret = unicode_prop(cr, name); - if (ret) { - cr_free(cr); - if (ret == -2) - goto unknown_property_name; - else - goto out_of_memory; - } - } - } else { - unknown_property_name: - return re_parse_error(s, "unknown unicode property name"); - } - - if (is_inv) { - if (cr_invert(cr)) { - cr_free(cr); - return -1; - } - } - *pp = p; - return 0; - out_of_memory: - return re_parse_out_of_memory(s); -} -#endif /* CONFIG_ALL_UNICODE */ - -/* return -1 if error otherwise the character or a class range - (CLASS_RANGE_BASE). In case of class range, 'cr' is - initialized. Otherwise, it is ignored. */ -static int get_class_atom(REParseState *s, CharRange *cr, - const uint8_t **pp, BOOL inclass) -{ - const uint8_t *p; - uint32_t c; - int ret; - - p = *pp; - - c = *p; - switch(c) { - case '\\': - p++; - if (p >= s->buf_end) - goto unexpected_end; - c = *p++; - switch(c) { - case 'd': - c = CHAR_RANGE_d; - goto class_range; - case 'D': - c = CHAR_RANGE_D; - goto class_range; - case 's': - c = CHAR_RANGE_s; - goto class_range; - case 'S': - c = CHAR_RANGE_S; - goto class_range; - case 'w': - c = CHAR_RANGE_w; - goto class_range; - case 'W': - c = CHAR_RANGE_W; - class_range: - if (cr_init_char_range(s, cr, c)) - return -1; - c = CLASS_RANGE_BASE; - break; - case 'c': - c = *p; - if ((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (((c >= '0' && c <= '9') || c == '_') && - inclass && !s->is_unicode)) { /* Annex B.1.4 */ - c &= 0x1f; - p++; - } else if (s->is_unicode) { - goto invalid_escape; - } else { - /* otherwise return '\' and 'c' */ - p--; - c = '\\'; - } - break; -#ifdef CONFIG_ALL_UNICODE - case 'p': - case 'P': - if (s->is_unicode) { - if (parse_unicode_property(s, cr, &p, (c == 'P'))) - return -1; - c = CLASS_RANGE_BASE; - break; - } - /* fall thru */ -#endif - default: - p--; - ret = lre_parse_escape(&p, s->is_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_unicode) { - invalid_escape: - return re_parse_error(s, "invalid escape sequence in regular expression"); - } else { - /* just ignore the '\' */ - goto normal_char; - } - } - break; - } - break; - case '\0': - if (p >= s->buf_end) { - unexpected_end: - return re_parse_error(s, "unexpected end"); - } - /* fall thru */ - default: - normal_char: - /* normal char */ - if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if ((unsigned)c > 0xffff && !s->is_unicode) { - /* XXX: should handle non BMP-1 code points */ - return re_parse_error(s, "malformed unicode char"); - } - } else { - p++; - } - break; - } - *pp = p; - return c; -} - -static int re_emit_range(REParseState *s, const CharRange *cr) -{ - int len, i; - uint32_t high; - - len = (unsigned)cr->len / 2; - if (len >= 65535) - return re_parse_error(s, "too many ranges"); - if (len == 0) { - /* not sure it can really happen. Emit a match that is always - false */ - re_emit_op_u32(s, REOP_char32, -1); - } else { - high = cr->points[cr->len - 1]; - if (high == UINT32_MAX) - high = cr->points[cr->len - 2]; - if (high <= 0xffff) { - /* can use 16 bit ranges with the conversion that 0xffff = - infinity */ - re_emit_op_u16(s, REOP_range, len); - for(i = 0; i < cr->len; i += 2) { - dbuf_put_u16(&s->byte_code, cr->points[i]); - high = cr->points[i + 1] - 1; - if (high == UINT32_MAX - 1) - high = 0xffff; - dbuf_put_u16(&s->byte_code, high); - } - } else { - re_emit_op_u16(s, REOP_range32, len); - for(i = 0; i < cr->len; i += 2) { - dbuf_put_u32(&s->byte_code, cr->points[i]); - dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); - } - } - } - return 0; -} - -static int re_parse_char_class(REParseState *s, const uint8_t **pp) -{ - const uint8_t *p; - uint32_t c1, c2; - CharRange cr_s, *cr = &cr_s; - CharRange cr1_s, *cr1 = &cr1_s; - BOOL invert; - - cr_init(cr, s->opaque, lre_realloc); - p = *pp; - p++; /* skip '[' */ - - invert = FALSE; - if (*p == '^') { - p++; - invert = TRUE; - } - - for(;;) { - if (*p == ']') - break; - c1 = get_class_atom(s, cr1, &p, TRUE); - if ((int)c1 < 0) - goto fail; - if (*p == '-' && p[1] != ']') { - const uint8_t *p0 = p + 1; - if (c1 >= CLASS_RANGE_BASE) { - if (s->is_unicode) { - cr_free(cr1); - goto invalid_class_range; - } - /* Annex B: match '-' character */ - goto class_atom; - } - c2 = get_class_atom(s, cr1, &p0, TRUE); - if ((int)c2 < 0) - goto fail; - if (c2 >= CLASS_RANGE_BASE) { - cr_free(cr1); - if (s->is_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 (cr_union_interval(cr, c1, c2)) - goto memory_error; - } else { - class_atom: - if (c1 >= CLASS_RANGE_BASE) { - int ret; - ret = cr_union1(cr, cr1->points, cr1->len); - cr_free(cr1); - if (ret) - goto memory_error; - } else { - if (cr_union_interval(cr, c1, c1)) - goto memory_error; - } - } - } - if (s->ignore_case) { - if (cr_regexp_canonicalize(cr, s->is_unicode)) - goto memory_error; - } - if (invert) { - if (cr_invert(cr)) - goto memory_error; - } - if (re_emit_range(s, cr)) - goto fail; - cr_free(cr); - p++; /* skip ']' */ - *pp = p; - return 0; - memory_error: - re_parse_out_of_memory(s); - fail: - cr_free(cr); - return -1; -} - -/* Return: - - true if the opcodes may not advance the char pointer - - false if the opcodes always advance the char pointer -*/ -static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len) -{ - int pos, opcode, len; - uint32_t val; - BOOL ret; - - ret = TRUE; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - switch(opcode) { - case REOP_range: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - goto simple_char; - case REOP_range32: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - goto simple_char; - case REOP_char: - case REOP_char32: - case REOP_dot: - case REOP_any: - simple_char: - ret = FALSE; - break; - case REOP_line_start: - case REOP_line_end: - case REOP_push_i32: - case REOP_push_char_pos: - case REOP_drop: - case REOP_word_boundary: - case REOP_not_word_boundary: - case REOP_prev: - /* no effect */ - break; - case REOP_save_start: - case REOP_save_end: - case REOP_save_reset: - case REOP_back_reference: - case REOP_backward_back_reference: - break; - default: - /* safe behavior: we cannot predict the outcome */ - return TRUE; - } - pos += len; - } - return ret; -} - -/* return -1 if a simple quantifier cannot be used. Otherwise return - the number of characters in the atom. */ -static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) -{ - int pos, opcode, len, count; - uint32_t val; - - count = 0; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - switch(opcode) { - case REOP_range: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - goto simple_char; - case REOP_range32: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - goto simple_char; - case REOP_char: - case REOP_char32: - case REOP_dot: - case REOP_any: - simple_char: - count++; - break; - case REOP_line_start: - case REOP_line_end: - case REOP_word_boundary: - case REOP_not_word_boundary: - break; - default: - return -1; - } - pos += len; - } - return count; -} - -/* '*pp' is the first char after '<' */ -static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) -{ - const uint8_t *p, *p1; - uint32_t c, d; - char *q; - - p = *pp; - q = buf; - for(;;) { - c = *p; - if (c == '\\') { - p++; - if (*p != 'u') - return -1; - c = lre_parse_escape(&p, 2); // accept surrogate pairs - } else if (c == '>') { - break; - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (is_hi_surrogate(c)) { - d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - if (is_lo_surrogate(d)) { - c = from_surrogate(c, d); - p = p1; - } - } - } else { - p++; - } - if (c > 0x10FFFF) - return -1; - if (q == buf) { - if (!lre_js_is_ident_first(c)) - return -1; - } else { - if (!lre_js_is_ident_next(c)) - return -1; - } - if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size) - return -1; - if (c < 128) { - *q++ = c; - } else { - q += unicode_to_utf8((uint8_t*)q, c); - } - } - if (q == buf) - return -1; - *q = '\0'; - p++; - *pp = p; - return 0; -} - -/* if capture_name = NULL: return the number of captures + 1. - Otherwise, return the capture index corresponding to capture_name - or -1 if none */ -static int re_parse_captures(REParseState *s, int *phas_named_captures, - const char *capture_name) -{ - const uint8_t *p; - int capture_index; - char name[TMP_BUF_SIZE]; - - capture_index = 1; - *phas_named_captures = 0; - for (p = s->buf_start; p < s->buf_end; p++) { - switch (*p) { - case '(': - if (p[1] == '?') { - if (p[2] == '<' && p[3] != '=' && p[3] != '!') { - *phas_named_captures = 1; - /* potential named capture */ - if (capture_name) { - p += 3; - if (re_parse_group_name(name, sizeof(name), &p) == 0) { - if (!strcmp(name, capture_name)) - return capture_index; - } - } - capture_index++; - if (capture_index >= CAPTURE_COUNT_MAX) - goto done; - } - } else { - capture_index++; - if (capture_index >= CAPTURE_COUNT_MAX) - goto done; - } - break; - case '\\': - p++; - break; - case '[': - for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) { - if (*p == '\\') - p++; - } - break; - } - } - done: - if (capture_name) - return -1; - else - return capture_index; -} - -static int re_count_captures(REParseState *s) -{ - if (s->total_capture_count < 0) { - s->total_capture_count = re_parse_captures(s, &s->has_named_captures, - NULL); - } - return s->total_capture_count; -} - -static BOOL re_has_named_captures(REParseState *s) -{ - if (s->has_named_captures < 0) - re_count_captures(s); - return s->has_named_captures; -} - -static int find_group_name(REParseState *s, const char *name) -{ - const char *p, *buf_end; - size_t len, name_len; - int capture_index; - - 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); - if (len == name_len && memcmp(name, p, name_len) == 0) - return capture_index; - p += len + 1; - capture_index++; - } - return -1; -} - -static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir); - -static int re_parse_term(REParseState *s, BOOL is_backward_dir) -{ - const uint8_t *p; - int c, last_atom_start, quant_min, quant_max, last_capture_count; - BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; - CharRange cr_s, *cr = &cr_s; - - last_atom_start = -1; - last_capture_count = 0; - p = s->buf_ptr; - c = *p; - switch(c) { - case '^': - p++; - re_emit_op(s, REOP_line_start); - break; - case '$': - p++; - re_emit_op(s, REOP_line_end); - break; - case '.': - p++; - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - re_emit_op(s, s->dotall ? REOP_any : REOP_dot); - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - case '{': - if (s->is_unicode) { - return re_parse_error(s, "syntax error"); - } else if (!is_digit(p[1])) { - /* Annex B: we accept '{' not followed by digits as a - normal atom */ - goto parse_class_atom; - } else { - const uint8_t *p1 = p + 1; - /* Annex B: error if it is like a repetition count */ - parse_digits(&p1, TRUE); - if (*p1 == ',') { - p1++; - if (is_digit(*p1)) { - parse_digits(&p1, TRUE); - } - } - if (*p1 != '}') { - goto parse_class_atom; - } - } - /* fall thru */ - case '*': - case '+': - case '?': - return re_parse_error(s, "nothing to repeat"); - case '(': - if (p[1] == '?') { - if (p[2] == ':') { - p += 3; - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - } else if ((p[2] == '=' || p[2] == '!')) { - is_neg = (p[2] == '!'); - is_backward_lookahead = FALSE; - p += 3; - goto lookahead; - } else if (p[2] == '<' && - (p[3] == '=' || p[3] == '!')) { - int pos; - is_neg = (p[3] == '!'); - is_backward_lookahead = TRUE; - p += 4; - /* lookahead */ - lookahead: - /* Annex B allows lookahead to be used as an atom for - the quantifiers */ - if (!s->is_unicode && !is_backward_lookahead) { - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - } - pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_lookahead)) - return -1; - p = s->buf_ptr; - if (re_parse_expect(s, &p, ')')) - return -1; - re_emit_op(s, REOP_match); - /* jump after the 'match' after the lookahead is successful */ - if (dbuf_error(&s->byte_code)) - return -1; - put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); - } else if (p[2] == '<') { - p += 3; - if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p)) { - return re_parse_error(s, "invalid group name"); - } - if (find_group_name(s, s->u.tmp_buf) > 0) { - return re_parse_error(s, "duplicate group name"); - } - /* group name with a trailing zero */ - dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf, - strlen(s->u.tmp_buf) + 1); - s->has_named_captures = 1; - goto parse_capture; - } else { - return re_parse_error(s, "invalid group"); - } - } else { - int capture_index; - p++; - /* capture without group name */ - dbuf_putc(&s->group_names, 0); - parse_capture: - if (s->capture_count >= CAPTURE_COUNT_MAX) - return re_parse_error(s, "too many captures"); - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - capture_index = s->capture_count++; - re_emit_op_u8(s, REOP_save_start + is_backward_dir, - capture_index); - - s->buf_ptr = p; - if (re_parse_disjunction(s, is_backward_dir)) - return -1; - p = s->buf_ptr; - - re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, - capture_index); - - if (re_parse_expect(s, &p, ')')) - return -1; - } - break; - case '\\': - switch(p[1]) { - case 'b': - case 'B': - re_emit_op(s, REOP_word_boundary + (p[1] != 'b')); - p += 2; - break; - case 'k': - { - const uint8_t *p1; - int dummy_res; - - p1 = p; - if (p1[2] != '<') { - /* annex B: we tolerate invalid group names in non - unicode mode if there is no named capture - definition */ - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "expecting group name"); - else - goto parse_class_atom; - } - p1 += 3; - if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p1)) { - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "invalid group name"); - else - goto parse_class_atom; - } - c = find_group_name(s, s->u.tmp_buf); - if (c < 0) { - /* no capture name parsed before, try to look - after (inefficient, but hopefully not common */ - c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); - if (c < 0) { - if (s->is_unicode || re_has_named_captures(s)) - return re_parse_error(s, "group name not defined"); - else - goto parse_class_atom; - } - } - p = p1; - } - goto emit_back_reference; - case '0': - p += 2; - c = 0; - if (s->is_unicode) { - if (is_digit(*p)) { - return re_parse_error(s, "invalid decimal escape in regular expression"); - } - } else { - /* Annex B.1.4: accept legacy octal */ - if (*p >= '0' && *p <= '7') { - c = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - } - } - } - goto normal_char; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': - 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_unicode) { - /* Annex B.1.4: accept legacy octal */ - p = q; - if (*p <= '7') { - c = 0; - if (*p <= '3') - c = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - if (*p >= '0' && *p <= '7') { - c = (c << 3) + *p++ - '0'; - } - } - } else { - c = *p++; - } - goto normal_char; - } - return re_parse_error(s, "back reference out of range in regular expression"); - } - emit_back_reference: - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c); - } - break; - default: - goto parse_class_atom; - } - break; - case '[': - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - if (re_parse_char_class(s, &p)) - return -1; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - case ']': - case '}': - if (s->is_unicode) - return re_parse_error(s, "syntax error"); - goto parse_class_atom; - default: - parse_class_atom: - c = get_class_atom(s, cr, &p, FALSE); - if ((int)c < 0) - return -1; - normal_char: - last_atom_start = s->byte_code.size; - last_capture_count = s->capture_count; - if (is_backward_dir) - re_emit_op(s, REOP_prev); - if (c >= CLASS_RANGE_BASE) { - int ret; - /* Note: canonicalization is not needed */ - ret = re_emit_range(s, cr); - cr_free(cr); - if (ret) - return -1; - } else { - if (s->ignore_case) - c = lre_canonicalize(c, s->is_unicode); - if (c <= 0xffff) - re_emit_op_u16(s, REOP_char, c); - else - re_emit_op_u32(s, REOP_char32, c); - } - if (is_backward_dir) - re_emit_op(s, REOP_prev); - break; - } - - /* quantifier */ - if (last_atom_start >= 0) { - c = *p; - switch(c) { - case '*': - p++; - quant_min = 0; - quant_max = INT32_MAX; - goto quantifier; - case '+': - p++; - quant_min = 1; - quant_max = INT32_MAX; - goto quantifier; - case '?': - p++; - quant_min = 0; - quant_max = 1; - goto quantifier; - case '{': - { - const uint8_t *p1 = p; - /* As an extension (see ES6 annex B), we accept '{' not - followed by digits as a normal atom */ - if (!is_digit(p[1])) { - if (s->is_unicode) - goto invalid_quant_count; - break; - } - p++; - quant_min = parse_digits(&p, TRUE); - quant_max = quant_min; - if (*p == ',') { - p++; - if (is_digit(*p)) { - quant_max = parse_digits(&p, TRUE); - if (quant_max < quant_min) { - invalid_quant_count: - return re_parse_error(s, "invalid repetition count"); - } - } else { - quant_max = INT32_MAX; /* infinity */ - } - } - if (*p != '}' && !s->is_unicode) { - /* Annex B: normal atom if invalid '{' syntax */ - p = p1; - break; - } - if (re_parse_expect(s, &p, '}')) - return -1; - } - quantifier: - greedy = TRUE; - if (*p == '?') { - p++; - greedy = FALSE; - } - if (last_atom_start < 0) { - return re_parse_error(s, "nothing to repeat"); - } - if (greedy) { - int len, pos; - - if (quant_max > 0) { - /* specific optimization for simple quantifiers */ - if (dbuf_error(&s->byte_code)) - goto out_of_memory; - len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start, - s->byte_code.size - last_atom_start); - if (len > 0) { - re_emit_op(s, REOP_match); - - if (dbuf_insert(&s->byte_code, last_atom_start, 17)) - goto out_of_memory; - pos = last_atom_start; - s->byte_code.buf[pos++] = REOP_simple_greedy_quant; - put_u32(&s->byte_code.buf[pos], - s->byte_code.size - last_atom_start - 17); - pos += 4; - put_u32(&s->byte_code.buf[pos], quant_min); - pos += 4; - put_u32(&s->byte_code.buf[pos], quant_max); - pos += 4; - put_u32(&s->byte_code.buf[pos], len); - pos += 4; - goto done; - } - } - - if (dbuf_error(&s->byte_code)) - goto out_of_memory; - } - /* 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; - if (quant_min == 0) { - /* need to reset the capture in case the atom is - not executed */ - if (last_capture_count != s->capture_count) { - if (dbuf_insert(&s->byte_code, last_atom_start, 3)) - goto out_of_memory; - s->byte_code.buf[last_atom_start++] = REOP_save_reset; - s->byte_code.buf[last_atom_start++] = last_capture_count; - s->byte_code.buf[last_atom_start++] = s->capture_count - 1; - } - if (quant_max == 0) { - s->byte_code.size = last_atom_start; - } else if (quant_max == 1 || 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 * has_goto + add_zero_advance_check * 2); - if (add_zero_advance_check) { - s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; - 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 + 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 + 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); - } - } else if (quant_min == 1 && quant_max == INT32_MAX && - !add_zero_advance_check) { - re_emit_goto(s, REOP_split_next_first - greedy, - last_atom_start); - } else { - if (quant_min == 1) { - /* nothing to add */ - } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 5)) - goto out_of_memory; - s->byte_code.buf[last_atom_start] = REOP_push_i32; - put_u32(s->byte_code.buf + last_atom_start + 1, - quant_min); - last_atom_start += 5; - re_emit_goto(s, REOP_loop, last_atom_start); - re_emit_op(s, REOP_drop); - } - if (quant_max == INT32_MAX) { - pos = s->byte_code.size; - re_emit_op_u32(s, REOP_split_goto_first + greedy, - len + 5 + add_zero_advance_check * 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_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 + 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); - } - } - last_atom_start = -1; - } - break; - default: - break; - } - } - done: - s->buf_ptr = p; - return 0; - out_of_memory: - return re_parse_out_of_memory(s); -} - -static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) -{ - const uint8_t *p; - int ret; - size_t start, term_start, end, term_size; - - start = s->byte_code.size; - for(;;) { - p = s->buf_ptr; - if (p >= s->buf_end) - break; - if (*p == '|' || *p == ')') - break; - term_start = s->byte_code.size; - ret = re_parse_term(s, is_backward_dir); - if (ret) - return ret; - if (is_backward_dir) { - /* reverse the order of the terms (XXX: inefficient, but - speed is not really critical here) */ - end = s->byte_code.size; - term_size = end - term_start; - if (dbuf_realloc(&s->byte_code, end + term_size)) - return -1; - memmove(s->byte_code.buf + start + term_size, - s->byte_code.buf + start, - end - start); - memcpy(s->byte_code.buf + start, s->byte_code.buf + end, - term_size); - } - } - return 0; -} - -static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) -{ - int start, len, pos; - - 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; - while (*s->buf_ptr == '|') { - s->buf_ptr++; - - len = s->byte_code.size - start; - - /* insert a split before the first alternative */ - if (dbuf_insert(&s->byte_code, start, 5)) { - return re_parse_out_of_memory(s); - } - s->byte_code.buf[start] = REOP_split_next_first; - put_u32(s->byte_code.buf + start + 1, len + 5); - - pos = re_emit_op_u32(s, REOP_goto, 0); - - if (re_parse_alternative(s, is_backward_dir)) - return -1; - - /* patch the goto */ - len = s->byte_code.size - (pos + 4); - put_u32(s->byte_code.buf + pos, len); - } - return 0; -} - -/* the control flow is recursive so the analysis can be linear */ -static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) -{ - int stack_size, stack_size_max, pos, opcode, len; - uint32_t val; - - stack_size = 0; - stack_size_max = 0; - bc_buf += RE_HEADER_LEN; - bc_buf_len -= RE_HEADER_LEN; - pos = 0; - while (pos < bc_buf_len) { - opcode = bc_buf[pos]; - len = reopcode_info[opcode].size; - assert(opcode < REOP_COUNT); - assert((pos + len) <= bc_buf_len); - switch(opcode) { - case REOP_push_i32: - case REOP_push_char_pos: - stack_size++; - if (stack_size > stack_size_max) { - if (stack_size > STACK_SIZE_MAX) - return -1; - stack_size_max = stack_size; - } - break; - case REOP_drop: - case REOP_check_advance: - assert(stack_size > 0); - stack_size--; - break; - case REOP_range: - val = get_u16(bc_buf + pos + 1); - len += val * 4; - break; - case REOP_range32: - val = get_u16(bc_buf + pos + 1); - len += val * 8; - break; - } - pos += len; - } - return stack_size_max; -} - -/* 'buf' must be a zero terminated UTF-8 string of length buf_len. - Return NULL if error and allocate an error message in *perror_msg, - otherwise the compiled bytecode and its length in plen. -*/ -uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, - const char *buf, size_t buf_len, int re_flags, - void *opaque) -{ - REParseState s_s, *s = &s_s; - int stack_size; - BOOL is_sticky; - - memset(s, 0, sizeof(*s)); - s->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_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0); - is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); - s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); - s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); - s->capture_count = 1; - s->total_capture_count = -1; - s->has_named_captures = -1; - - dbuf_init2(&s->byte_code, opaque, lre_realloc); - dbuf_init2(&s->group_names, opaque, lre_realloc); - - dbuf_putc(&s->byte_code, re_flags); /* first element is the flags */ - dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ - dbuf_putc(&s->byte_code, 0); /* stack size */ - dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ - - if (!is_sticky) { - /* iterate thru all positions (about the same as .*?( ... ) ) - . We do it without an explicit loop so that lock step - thread execution will be possible in an optimized - implementation */ - re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); - re_emit_op(s, REOP_any); - re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); - } - re_emit_op_u8(s, REOP_save_start, 0); - - if (re_parse_disjunction(s, FALSE)) { - error: - dbuf_free(&s->byte_code); - dbuf_free(&s->group_names); - pstrcpy(error_msg, error_msg_size, s->u.error_msg); - *plen = 0; - return NULL; - } - - re_emit_op_u8(s, REOP_save_end, 0); - - re_emit_op(s, REOP_match); - - if (*s->buf_ptr != '\0') { - re_parse_error(s, "extraneous characters at the end"); - goto error; - } - - if (dbuf_error(&s->byte_code)) { - re_parse_out_of_memory(s); - goto error; - } - - stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size); - if (stack_size < 0) { - re_parse_error(s, "too many imbricated quantifiers"); - goto error; - } - - s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; - s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; - put_u32(s->byte_code.buf + 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; - } - dbuf_free(&s->group_names); - -#ifdef DUMP_REOP - lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); -#endif - - error_msg[0] = '\0'; - *plen = s->byte_code.size; - return s->byte_code.buf; -} - -static BOOL is_line_terminator(uint32_t c) -{ - return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); -} - -static BOOL is_word_char(uint32_t c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c == '_')); -} - -#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = *cptr++; \ - } else if (cbuf_type < 3) { \ - 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; \ - } else { \ - c = unicode_from_utf8(cptr, cbuf_end - cptr, &cptr); \ - } \ - } while (0) - -#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[0]; \ - } else if (cbuf_type < 3) { \ - 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); \ - } \ - } \ - } else { \ - const uint8_t *__cpt2; \ - c = unicode_from_utf8(cptr, cbuf_end - cptr, &__cpt2); \ - } \ - } while (0) - -#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - c = cptr[-1]; \ - } else if (cbuf_type < 3) { \ - 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); \ - } \ - } \ - } else { \ - const uint8_t *__cpt2 = cptr; \ - int __i = 0; \ - while (__cpt2 > cbuf_start && ((*__cpt2-- >> 6) & 2)) \ - __i++; \ - c = unicode_from_utf8(__cpt2, __i, &__cpt2); \ - } \ - } while (0) - -#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - c = cptr[0]; \ - } else if (cbuf_type < 3) { \ - 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; \ - } else { \ - const uint8_t *__cpt2; \ - int __i = 0; \ - while (cptr > cbuf_start && ((*cptr-- >> 6) & 2)) \ - __i++; \ - c = unicode_from_utf8(cptr, __i, &__cpt2); \ - } \ - } while (0) - -#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ - do { \ - if (cbuf_type == 0) { \ - cptr--; \ - } else if (cbuf_type < 3) { \ - 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; \ - } else { \ - while (cptr > cbuf_start && ((*cptr-- >> 6) & 2)); \ - } \ - } while (0) - -typedef uintptr_t StackInt; - -typedef enum { - RE_EXEC_STATE_SPLIT, - RE_EXEC_STATE_LOOKAHEAD, - RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, - RE_EXEC_STATE_GREEDY_QUANT, -} REExecStateEnum; - -typedef struct REExecState { - REExecStateEnum type : 8; - uint8_t stack_len; - size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */ - const uint8_t *cptr; - const uint8_t *pc; - void *buf[0]; -} REExecState; - -typedef struct { - const uint8_t *cbuf; - const uint8_t *cbuf_end; - /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16, - 3 = 8 bit chars, UTF-8 */ - int cbuf_type; - int capture_count; - int stack_size_max; - BOOL multi_line; - BOOL ignore_case; - BOOL is_unicode; - void *opaque; /* used for stack overflow check */ - - size_t state_size; - uint8_t *state_stack; - size_t state_stack_size; - size_t state_stack_len; -} REExecContext; - -static int push_state(REExecContext *s, - uint8_t **capture, - StackInt *stack, size_t stack_len, - const uint8_t *pc, const uint8_t *cptr, - REExecStateEnum type, size_t count) -{ - REExecState *rs; - uint8_t *new_stack; - size_t new_size, i, n; - StackInt *stack_buf; - - if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) { - /* reallocate the stack */ - new_size = s->state_stack_size * 3 / 2; - if (new_size < 8) - new_size = 8; - new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size); - if (!new_stack) - return -1; - s->state_stack_size = new_size; - s->state_stack = new_stack; - } - rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size); - s->state_stack_len++; - rs->type = type; - rs->count = count; - rs->stack_len = stack_len; - rs->cptr = cptr; - rs->pc = pc; - n = 2 * s->capture_count; - for(i = 0; i < n; i++) - rs->buf[i] = capture[i]; - stack_buf = (StackInt *)(rs->buf + n); - for(i = 0; i < stack_len; i++) - stack_buf[i] = stack[i]; - return 0; -} - -/* return 1 if match, 0 if not match or -1 if error. */ -static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, - StackInt *stack, int stack_len, - const uint8_t *pc, const uint8_t *cptr, - BOOL no_recurse) -{ - int opcode, ret; - int cbuf_type; - uint32_t val, c; - const uint8_t *cbuf_end; - - cbuf_type = s->cbuf_type; - cbuf_end = s->cbuf_end; - - for(;;) { - // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN))); - opcode = *pc++; - switch(opcode) { - case REOP_match: - { - REExecState *rs; - if (no_recurse) - return (intptr_t)cptr; - ret = 1; - goto recurse; - no_match: - if (no_recurse) - return 0; - ret = 0; - recurse: - for(;;) { - if (s->state_stack_len == 0) - return ret; - rs = (REExecState *)(s->state_stack + - (s->state_stack_len - 1) * s->state_size); - if (rs->type == RE_EXEC_STATE_SPLIT) { - if (!ret) { - pop_state: - memcpy(capture, rs->buf, - sizeof(capture[0]) * 2 * s->capture_count); - pop_state1: - pc = rs->pc; - cptr = rs->cptr; - stack_len = rs->stack_len; - memcpy(stack, rs->buf + 2 * s->capture_count, - stack_len * sizeof(stack[0])); - s->state_stack_len--; - break; - } - } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) { - if (!ret) { - uint32_t char_count, i; - memcpy(capture, rs->buf, - sizeof(capture[0]) * 2 * s->capture_count); - stack_len = rs->stack_len; - memcpy(stack, rs->buf + 2 * s->capture_count, - stack_len * sizeof(stack[0])); - pc = rs->pc; - cptr = rs->cptr; - /* go backward */ - char_count = get_u32(pc + 12); - for(i = 0; i < char_count; i++) { - PREV_CHAR(cptr, s->cbuf, cbuf_type); - } - pc = (pc + 16) + (int)get_u32(pc); - rs->cptr = cptr; - rs->count--; - if (rs->count == 0) { - s->state_stack_len--; - } - break; - } - } else { - ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) || - (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret)); - if (ret) { - /* keep the capture in case of positive lookahead */ - if (rs->type == RE_EXEC_STATE_LOOKAHEAD) - goto pop_state1; - else - goto pop_state; - } - } - s->state_stack_len--; - } - } - break; - case REOP_char32: - val = get_u32(pc); - pc += 4; - goto test_char; - case REOP_char: - val = get_u16(pc); - pc += 2; - test_char: - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_unicode); - } - if (val != c) - goto no_match; - break; - case REOP_split_goto_first: - case REOP_split_next_first: - { - const uint8_t *pc1; - - val = get_u32(pc); - pc += 4; - if (opcode == REOP_split_next_first) { - pc1 = pc + (int)val; - } else { - pc1 = pc; - pc = pc + (int)val; - } - ret = push_state(s, capture, stack, stack_len, - pc1, cptr, RE_EXEC_STATE_SPLIT, 0); - if (ret < 0) - return -1; - break; - } - case REOP_lookahead: - case REOP_negative_lookahead: - val = get_u32(pc); - pc += 4; - ret = push_state(s, capture, stack, stack_len, - pc + (int)val, cptr, - RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, - 0); - if (ret < 0) - return -1; - break; - - case REOP_goto: - val = get_u32(pc); - pc += 4 + (int)val; - break; - case REOP_line_start: - if (cptr == s->cbuf) - break; - if (!s->multi_line) - goto no_match; - PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); - if (!is_line_terminator(c)) - goto no_match; - break; - case REOP_line_end: - if (cptr == cbuf_end) - break; - if (!s->multi_line) - goto no_match; - PEEK_CHAR(c, cptr, cbuf_end, 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, 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, cbuf_type); - break; - case REOP_save_start: - case REOP_save_end: - val = *pc++; - assert(val < s->capture_count); - capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr; - break; - case REOP_save_reset: - { - uint32_t val2; - val = pc[0]; - val2 = pc[1]; - pc += 2; - assert(val2 < s->capture_count); - while (val <= val2) { - capture[2 * val] = NULL; - capture[2 * val + 1] = NULL; - val++; - } - } - break; - case REOP_push_i32: - val = get_u32(pc); - pc += 4; - stack[stack_len++] = val; - break; - case REOP_drop: - stack_len--; - break; - case REOP_loop: - val = get_u32(pc); - pc += 4; - if (--stack[stack_len - 1] != 0) { - pc += (int)val; - } - break; - case REOP_push_char_pos: - stack[stack_len++] = (uintptr_t)cptr; - break; - case REOP_check_advance: - if (stack[--stack_len] == (uintptr_t)cptr) - goto no_match; - break; - case REOP_word_boundary: - case REOP_not_word_boundary: - { - BOOL v1, v2; - /* char before */ - if (cptr == s->cbuf) { - v1 = FALSE; - } else { - PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); - v1 = is_word_char(c); - } - /* current char */ - if (cptr >= cbuf_end) { - v2 = FALSE; - } else { - PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); - v2 = is_word_char(c); - } - if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) - goto no_match; - } - break; - case REOP_back_reference: - case REOP_backward_back_reference: - { - const uint8_t *cptr1, *cptr1_end, *cptr1_start; - uint32_t c1, c2; - - val = *pc++; - if (val >= s->capture_count) - goto no_match; - cptr1_start = capture[2 * val]; - cptr1_end = capture[2 * val + 1]; - if (!cptr1_start || !cptr1_end) - break; - if (opcode == REOP_back_reference) { - cptr1 = cptr1_start; - while (cptr1 < cptr1_end) { - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); - GET_CHAR(c2, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_unicode); - c2 = lre_canonicalize(c2, s->is_unicode); - } - if (c1 != c2) - goto no_match; - } - } else { - cptr1 = cptr1_end; - while (cptr1 > cptr1_start) { - if (cptr == s->cbuf) - goto no_match; - GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); - GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); - if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_unicode); - c2 = lre_canonicalize(c2, s->is_unicode); - } - if (c1 != c2) - goto no_match; - } - } - } - break; - case REOP_range: - { - int n; - uint32_t low, high, idx_min, idx_max, idx; - - n = get_u16(pc); /* n must be >= 1 */ - pc += 2; - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_unicode); - } - idx_min = 0; - low = get_u16(pc + 0 * 4); - if (c < low) - goto no_match; - idx_max = n - 1; - high = get_u16(pc + idx_max * 4 + 2); - /* 0xffff in for last value means +infinity */ - if (unlikely(c >= 0xffff) && high == 0xffff) - goto range_match; - if (c > high) - goto no_match; - while (idx_min <= idx_max) { - idx = (idx_min + idx_max) / 2; - low = get_u16(pc + idx * 4); - high = get_u16(pc + idx * 4 + 2); - if (c < low) - idx_max = idx - 1; - else if (c > high) - idx_min = idx + 1; - else - goto range_match; - } - goto no_match; - range_match: - pc += 4 * n; - } - break; - case REOP_range32: - { - int n; - uint32_t low, high, idx_min, idx_max, idx; - - n = get_u16(pc); /* n must be >= 1 */ - pc += 2; - if (cptr >= cbuf_end) - goto no_match; - GET_CHAR(c, cptr, cbuf_end, cbuf_type); - if (s->ignore_case) { - c = lre_canonicalize(c, s->is_unicode); - } - idx_min = 0; - low = get_u32(pc + 0 * 8); - if (c < low) - goto no_match; - idx_max = n - 1; - high = get_u32(pc + idx_max * 8 + 4); - if (c > high) - goto no_match; - while (idx_min <= idx_max) { - idx = (idx_min + idx_max) / 2; - low = get_u32(pc + idx * 8); - high = get_u32(pc + idx * 8 + 4); - if (c < low) - idx_max = idx - 1; - else if (c > high) - idx_min = idx + 1; - else - goto range32_match; - } - goto no_match; - range32_match: - pc += 8 * n; - } - break; - case REOP_prev: - /* go to the previous char */ - if (cptr == s->cbuf) - goto no_match; - PREV_CHAR(cptr, s->cbuf, cbuf_type); - break; - case REOP_simple_greedy_quant: - { - uint32_t next_pos, quant_min, quant_max; - size_t q; - intptr_t res; - const uint8_t *pc1; - - next_pos = get_u32(pc); - quant_min = get_u32(pc + 4); - quant_max = get_u32(pc + 8); - pc += 16; - pc1 = pc; - pc += (int)next_pos; - - q = 0; - for(;;) { - res = lre_exec_backtrack(s, capture, stack, stack_len, - pc1, cptr, TRUE); - if (res == -1) - return res; - if (!res) - break; - cptr = (uint8_t *)res; - q++; - if (q >= quant_max && quant_max != INT32_MAX) - break; - } - if (q < quant_min) - goto no_match; - if (q > quant_min) { - /* will examine all matches down to quant_min */ - ret = push_state(s, capture, stack, stack_len, - pc1 - 16, cptr, - RE_EXEC_STATE_GREEDY_QUANT, - q - quant_min); - if (ret < 0) - return -1; - } - } - break; - default: - abort(); - } - } -} - -/* Return 1 if match, 0 if not match or -1 if error. cindex is the - starting position of the match and must be such as 0 <= cindex <= - clen. */ -int lre_exec(uint8_t **capture, - const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, - int cbuf_type, void *opaque) -{ - REExecContext s_s, *s = &s_s; - int re_flags, i, alloca_size, ret, cbuf_width = cbuf_type; - StackInt *stack_buf; - - if (cbuf_width == 3) /* UTF-8 */ - cbuf_width = 0; - re_flags = lre_get_flags(bc_buf); - s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; - s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; - s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 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_width); - s->cbuf_type = cbuf_type; - if (s->cbuf_type == 1 && s->is_unicode) - s->cbuf_type = 2; - s->opaque = opaque; - - s->state_size = sizeof(REExecState) + - s->capture_count * sizeof(capture[0]) * 2 + - s->stack_size_max * sizeof(stack_buf[0]); - s->state_stack = NULL; - s->state_stack_len = 0; - s->state_stack_size = 0; - - for(i = 0; i < s->capture_count * 2; i++) - capture[i] = NULL; - alloca_size = s->stack_size_max * sizeof(stack_buf[0]); - stack_buf = alloca(alloca_size); - ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN, - cbuf + (cindex << cbuf_width), FALSE); - lre_realloc(s->opaque, s->state_stack, 0); - return ret; -} - -int lre_get_capture_count(const uint8_t *bc_buf) -{ - return bc_buf[RE_HEADER_CAPTURE_COUNT]; -} - -int lre_get_flags(const uint8_t *bc_buf) -{ - return bc_buf[RE_HEADER_FLAGS]; -} - -/* Return NULL if no group names. Otherwise, return a pointer to - 'capture_count - 1' zero terminated UTF-8 strings. */ -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 + RE_HEADER_BYTECODE_LEN); - return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); -} - -#ifdef TEST - -BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) -{ - return FALSE; -} - -void *lre_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -int main(int argc, char **argv) -{ - int len, 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 < 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]), flags, NULL); - if (!bc) { - fprintf(stderr, "error: %s\n", error_msg); - exit(1); - } - - 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) { - capture_count = lre_get_capture_count(bc); - for(i = 0; i < 2 * capture_count; i++) { - uint8_t *ptr; - ptr = capture[i]; - printf("%d: ", i); - if (!ptr) - printf("<nil>"); - else - printf("%u", (int)(ptr - (uint8_t *)input)); - printf("\n"); - } - } - return 0; -} -#endif diff --git a/lib/quickjs/libregexp.h b/lib/quickjs/libregexp.h deleted file mode 100644 index 7af7ece0..00000000 --- a/lib/quickjs/libregexp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Regular Expression Engine - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIBREGEXP_H -#define LIBREGEXP_H - -#include <stddef.h> -#include <stdint.h> - -#define LRE_FLAG_GLOBAL (1 << 0) -#define LRE_FLAG_IGNORECASE (1 << 1) -#define LRE_FLAG_MULTILINE (1 << 2) -#define LRE_FLAG_DOTALL (1 << 3) -#define LRE_FLAG_UNICODE (1 << 4) -#define LRE_FLAG_STICKY (1 << 5) -#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ -#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ - -uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, - const char *buf, size_t buf_len, int re_flags, - void *opaque); -int lre_get_capture_count(const uint8_t *bc_buf); -int lre_get_flags(const uint8_t *bc_buf); -const char *lre_get_groupnames(const uint8_t *bc_buf); -int lre_exec(uint8_t **capture, - const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, - int cbuf_type, void *opaque); - -int lre_parse_escape(const uint8_t **pp, int allow_utf16); - -/* must be provided by the user, return non zero if overflow */ -int lre_check_stack_overflow(void *opaque, size_t alloca_size); -void *lre_realloc(void *opaque, void *ptr, size_t size); - -#endif /* LIBREGEXP_H */ diff --git a/lib/quickjs/libunicode-table.h b/lib/quickjs/libunicode-table.h deleted file mode 100644 index 72d495e7..00000000 --- a/lib/quickjs/libunicode-table.h +++ /dev/null @@ -1,4557 +0,0 @@ -/* Compressed unicode tables */ -/* Automatically generated file - do not edit */ - -#include <stdint.h> - -static const uint32_t case_conv_table1[370] = { - 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, - 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, - 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, - 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, - 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, - 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, - 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, - 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001, - 0xf4802231, 0xf4912201, -}; - -static const uint8_t case_conv_table2[370] = { - 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, -}; - -static const uint16_t case_conv_ext[58] = { - 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, - 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, - 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, - 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, - 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, - 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, - 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, - 0x006b, 0x00e5, -}; - -static const uint8_t unicode_prop_Cased1_table[196] = { - 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, -}; - -static const uint8_t unicode_prop_Cased1_index[21] = { - 0xb9, 0x02, 0xe0, // 002B9 at 39 - 0xc0, 0x1d, 0x20, // 01DC0 at 65 - 0xe5, 0x2c, 0x20, // 02CE5 at 97 - 0xb1, 0x07, 0x21, // 107B1 at 129 - 0xc1, 0xd6, 0x21, // 1D6C1 at 161 - 0x4a, 0xf1, 0x01, // 1F14A at 192 - 0x8a, 0xf1, 0x01, // 1F18A at 224 (upper bound) -}; - -static const uint8_t unicode_prop_Case_Ignorable_table[737] = { - 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, - 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, - 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, - 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac, - 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85, - 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, - 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86, - 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, - 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, - 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80, - 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93, - 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f, - 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80, - 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14, - 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, - 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81, - 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80, - 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a, - 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x86, 0xc8, - 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, - 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5, - 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, - 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd, - 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c, - 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, - 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80, - 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88, - 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00, - 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07, - 0x9e, 0xb0, 0x83, 0xaf, 0x00, 0x20, 0x04, 0x80, - 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82, - 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81, - 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, 0x18, 0x28, - 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xe4, 0x41, - 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, - 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, 0x89, 0x01, - 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, 0x80, - 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0, - 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, 0x9f, 0xae, - 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, 0x80, - 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, - 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, 0xe1, 0x18, - 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, 0xa1, - 0xcd, 0x80, 0x96, 0x82, 0xe6, 0x12, 0x0f, 0x02, - 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, 0x96, 0x81, - 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a, - 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80, - 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, 0x8b, 0x07, - 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11, - 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08, - 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, 0x01, 0x03, - 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x90, - 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01, - 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, 0x8a, 0x80, - 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, - 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1, - 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, - 0xbe, 0x90, 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, - 0x18, 0x30, 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, - 0x5b, 0xad, 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, - 0x8f, 0x0e, 0x9d, 0x83, 0x40, 0x93, 0x82, 0x47, - 0xba, 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, - 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, -}; - -static const uint8_t unicode_prop_Case_Ignorable_index[69] = { - 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 - 0x75, 0x10, 0x01, // 11075 at 512 - 0xeb, 0x12, 0x21, // 112EB at 545 - 0x41, 0x16, 0x01, // 11641 at 576 - 0x5c, 0x1a, 0x01, // 11A5C at 608 - 0x43, 0x1f, 0x01, // 11F43 at 640 - 0x2e, 0xcf, 0x41, // 1CF2E at 674 - 0x25, 0xe0, 0x01, // 1E025 at 704 - 0xf0, 0x01, 0x0e, // E01F0 at 736 (upper bound) -}; - -static const uint8_t unicode_prop_ID_Start_table[1100] = { - 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, - 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, - 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, - 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, - 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, - 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, - 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, - 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, - 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, - 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, - 0x84, 0x97, 0x05, 0x90, 0xa9, 0xb9, 0xb5, 0x10, - 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81, - 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c, - 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09, - 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80, - 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06, - 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96, - 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, - 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00, - 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12, - 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80, - 0x8f, 0x10, 0x99, 0x11, 0x01, 0x81, 0x9d, 0x03, - 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e, - 0x08, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80, - 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c, - 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, - 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, - 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20, - 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, - 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, - 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90, - 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, - 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, - 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80, - 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, - 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, - 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92, - 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2, - 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84, - 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab, - 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, - 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, - 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80, - 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13, - 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c, - 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, - 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24, - 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b, - 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30, - 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88, - 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03, - 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f, - 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, - 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b, - 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80, - 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10, - 0x87, 0x93, 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11, - 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, 0x32, 0x8b, - 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80, - 0x8d, 0x85, 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b, - 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, 0x41, 0x6d, - 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89, - 0x80, 0x8c, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, - 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, - 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85, - 0x99, 0x8a, 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2, - 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, 0x80, 0x8e, - 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a, - 0x9c, 0x82, 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84, - 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, - 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, -}; - -static const uint8_t unicode_prop_ID_Start_index[105] = { - 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 - 0xda, 0xa7, 0x00, // 0A7DA 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 - 0xf3, 0x08, 0x01, // 108F3 at 672 - 0xb3, 0x0c, 0x21, // 10CB3 at 705 - 0x73, 0x11, 0x61, // 11173 at 739 - 0x34, 0x13, 0x01, // 11334 at 768 - 0x1b, 0x17, 0x21, // 1171B at 801 - 0x8a, 0x1a, 0x01, // 11A8A at 832 - 0x34, 0x1f, 0x21, // 11F34 at 865 - 0xbf, 0x6a, 0x01, // 16ABF at 896 - 0x23, 0xb1, 0xa1, // 1B123 at 933 - 0xad, 0xd4, 0x01, // 1D4AD at 960 - 0x6f, 0xd7, 0x01, // 1D76F at 992 - 0xff, 0xe7, 0x61, // 1E7FF at 1027 - 0x5e, 0xee, 0x01, // 1EE5E at 1056 - 0xe1, 0xeb, 0x22, // 2EBE1 at 1089 - 0xb0, 0x23, 0x03, // 323B0 at 1120 (upper bound) -}; - -static const uint8_t unicode_prop_ID_Continue1_table[660] = { - 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, - 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, - 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, - 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, - 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, - 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, - 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, - 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80, - 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, - 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81, - 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30, - 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x10, 0x8b, - 0x83, 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, - 0x89, 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, - 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, - 0x38, 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x30, 0x89, - 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, - 0xb0, 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, - 0x80, 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, - 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, - 0xbe, 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, - 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, - 0x01, 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, - 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, - 0xbb, 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, - 0x85, 0x89, 0x95, 0x8d, 0x80, 0x8f, 0xb0, 0x84, - 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, -}; - -static const uint8_t unicode_prop_ID_Continue1_index[63] = { - 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 - 0x41, 0x20, 0x00, // 02041 at 288 - 0x0c, 0xa8, 0x80, // 0A80C at 324 - 0x37, 0xaa, 0x20, // 0AA37 at 353 - 0x50, 0xfe, 0x20, // 0FE50 at 385 - 0x3a, 0x0d, 0x21, // 10D3A at 417 - 0x74, 0x11, 0x01, // 11174 at 448 - 0x5a, 0x14, 0x21, // 1145A at 481 - 0x44, 0x19, 0x81, // 11944 at 516 - 0x5a, 0x1d, 0xa1, // 11D5A at 549 - 0xf5, 0x6a, 0x21, // 16AF5 at 577 - 0x45, 0xd2, 0x41, // 1D245 at 610 - 0xaf, 0xe2, 0x21, // 1E2AF at 641 - 0xf0, 0x01, 0x0e, // E01F0 at 672 (upper bound) -}; - -#ifdef CONFIG_ALL_UNICODE - -static const uint8_t unicode_cc_table[899] = { - 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, - 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, - 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, - 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, - 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, - 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, - 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, - 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, - 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, - 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, - 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, - 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, - 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, - 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, - 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, - 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, - 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, - 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, - 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, - 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, - 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, - 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00, - 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, - 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, - 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f, - 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, - 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0, - 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, - 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d, - 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00, - 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, - 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, - 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, - 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, - 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, - 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, - 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, - 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, - 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, - 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, - 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, - 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, - 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d, - 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, - 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, - 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, - 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, - 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, - 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00, - 0xdc, 0xc3, 0xb0, 0x34, 0x00, 0x07, 0x8e, 0x00, - 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, 0x05, - 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, 0x01, - 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2, - 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, 0xc0, - 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, 0x85, - 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc, - 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, 0xd6, - 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, 0x01, - 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, 0xe9, - 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, 0xc1, - 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0, - 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01, - 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, 0xc2, - 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1, - 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00, - 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, 0xb8, - 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0, - 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, 0x00, - 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0, - 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e, - 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0, - 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1, - 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5, - 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2, - 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00, - 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, -}; - -static const uint8_t unicode_cc_index[87] = { - 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 - 0x51, 0x0f, 0x01, // 10F51 at 672 - 0x73, 0x11, 0x01, // 11173 at 704 - 0x75, 0x13, 0x01, // 11375 at 736 - 0x2b, 0x17, 0x21, // 1172B at 769 - 0x3f, 0x1c, 0x21, // 11C3F at 801 - 0x9e, 0xbc, 0x21, // 1BC9E at 833 - 0x08, 0xe0, 0x01, // 1E008 at 864 - 0x44, 0xe9, 0x01, // 1E944 at 896 - 0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound) -}; - -static const uint32_t unicode_decomp_table1[699] = { - 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, - 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, - 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, - 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, - 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, - 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, - 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, - 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, - 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, - 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, - 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, - 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, - 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, - 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, - 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, - 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, - 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, - 0x011d8144, 0x01304144, 0x01340244, 0x01358144, - 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, - 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, - 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, - 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, - 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, - 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, - 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, - 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, - 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, - 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, - 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, - 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, - 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, - 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, - 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, - 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, - 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, - 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, - 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, - 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, - 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, - 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, - 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, - 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, - 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, - 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, - 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, - 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, - 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, - 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, - 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, - 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, - 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, - 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, - 0x0805c097, 0x08090081, 0x08094097, 0x08098099, - 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, - 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, - 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, - 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, - 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, - 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, - 0x08458097, 0x08464295, 0x08480097, 0x08484099, - 0x08488097, 0x08490081, 0x08498080, 0x084a0081, - 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, - 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, - 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, - 0x08584097, 0x08588099, 0x0858c097, 0x08590081, - 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, - 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, - 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, - 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, - 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, - 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, - 0x088100be, 0x088240be, 0x088300be, 0x088901be, - 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, - 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, - 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, - 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, - 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, - 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, - 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, - 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, - 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, - 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, - 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, - 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, - 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, - 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, - 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, - 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, - 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, - 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, - 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, - 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, - 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, - 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, - 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, - 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, - 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, - 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, - 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, - 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, - 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, - 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, - 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, - 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, - 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, - 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, - 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, - 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, - 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, - 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, - 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, - 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, - 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, - 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, - 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, - 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, - 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, - 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, - 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, - 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, - 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, - 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, - 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, - 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, - 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, - 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, - 0x29dc0081, 0x29fc8195, 0x29fe0103, 0x2ad70203, - 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, - 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, - 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af, - 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304, - 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120, - 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d, - 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f, - 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, - 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31, - 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, - 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, - 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695, - 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, -}; - -static const uint16_t unicode_decomp_table2[699] = { - 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, - 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, - 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, - 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, - 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, - 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, - 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, - 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, - 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, - 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, - 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, - 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, - 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, - 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, - 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, - 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, - 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, - 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, - 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, - 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, - 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, - 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, - 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, - 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, - 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, - 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, - 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, - 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, - 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, - 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, - 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, - 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, - 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, - 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, - 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, - 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, - 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, - 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, - 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, - 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, - 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, - 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, - 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, - 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, - 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, - 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, - 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, - 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, - 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, - 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, - 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, - 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, - 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, - 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, - 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, - 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, - 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, - 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, - 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, - 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, - 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, - 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, - 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, - 0xa76f, 0x11af, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e, - 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e, - 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5, - 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e, - 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3, - 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, -}; - -static const uint8_t unicode_decomp_data[9345] = { - 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, - 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, - 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, - 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, - 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, - 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, - 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, - 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, - 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, - 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, - 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, - 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, - 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, - 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, - 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, - 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, - 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, - 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, - 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, - 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, - 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, - 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, - 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, - 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, - 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, - 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, - 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, - 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, - 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, - 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, - 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, - 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, - 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, - 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, - 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, - 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, - 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, - 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, - 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, - 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, - 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, - 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, - 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, - 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, - 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, - 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, - 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, - 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, - 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, - 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, - 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, - 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, - 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, - 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, - 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, - 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, - 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, - 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, - 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, - 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, - 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, - 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, - 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, - 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, - 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, - 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, - 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, - 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, - 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, - 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, - 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, - 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, - 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, - 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, - 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, - 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, - 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, - 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, - 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, - 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, - 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, - 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, - 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, - 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, - 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, - 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, - 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, - 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, - 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, - 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, - 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, - 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, - 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, - 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, - 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, - 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, - 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, - 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, - 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, - 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, - 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, - 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, - 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, - 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, - 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, - 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, - 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, - 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, - 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, - 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, - 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, - 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, - 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, - 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, - 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, - 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, - 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, - 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, - 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, - 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, - 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, - 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, - 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, - 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, - 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, - 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, - 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, - 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, - 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, - 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, - 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, - 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, - 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, - 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, - 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, - 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, - 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, - 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, - 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, - 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, - 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, - 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, - 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, - 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, - 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, - 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, - 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, - 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, - 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, - 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, - 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, - 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, - 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, - 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, - 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, - 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, - 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, - 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, - 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, - 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, - 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, - 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, - 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, - 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, - 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, - 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, - 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, - 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, - 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, - 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, - 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, - 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, - 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, - 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, - 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, - 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, - 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, - 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, - 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, - 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, - 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, - 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, - 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, - 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, - 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, - 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, - 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, - 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, - 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, - 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, - 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, - 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, - 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, - 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, - 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, - 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, - 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, - 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, - 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, - 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, - 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, - 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, - 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, - 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, - 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, - 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, - 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, - 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, - 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, - 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, - 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, - 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, - 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, - 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, - 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, - 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, - 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, - 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, - 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, - 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, - 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, - 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, - 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, - 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, - 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, - 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, - 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, - 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, - 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, - 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, - 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, - 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, - 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, - 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, - 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, - 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, - 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, - 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, - 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, - 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, - 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, - 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, - 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, - 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, - 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, - 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, - 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, - 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, - 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, - 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, - 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, - 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, - 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, - 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, - 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, - 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, - 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, - 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, - 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, - 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, - 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, - 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, - 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, - 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, - 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, - 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, - 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, - 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, - 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, - 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, - 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, - 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, - 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, - 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, - 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, - 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, - 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, - 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, - 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, - 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, - 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, - 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, - 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, - 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, - 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, - 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, - 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, - 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, - 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, - 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, - 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, - 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, - 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, - 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, - 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, - 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, - 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, - 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, - 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, - 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, - 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, - 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, - 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, - 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, - 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, - 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, - 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, - 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, - 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, - 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, - 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, - 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, - 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, - 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, - 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, - 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, - 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, - 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, - 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, - 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, - 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, - 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, - 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, - 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, - 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, - 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, - 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, - 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, - 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, - 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, - 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, - 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, - 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, - 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, - 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, - 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, - 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, - 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, - 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, - 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, - 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, - 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, - 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, - 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, - 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, - 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, - 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, - 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, - 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, - 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, - 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, - 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, - 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, - 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, - 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, - 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, - 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, - 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, - 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, - 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, - 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, - 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, - 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, - 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, - 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, - 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, - 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, - 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, - 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, - 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, - 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, - 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, - 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, - 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, - 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, - 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, - 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, - 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, - 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, - 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, - 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, - 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, - 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, - 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, - 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, - 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, - 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, - 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, - 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, - 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, - 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, - 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, - 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, - 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, - 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, - 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, - 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, - 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, - 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, - 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, - 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, - 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, - 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, - 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, - 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, - 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, - 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, - 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, - 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, - 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, - 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, - 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, - 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, - 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, - 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, - 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, - 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, - 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, - 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, - 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, - 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, - 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, - 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, - 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, - 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, - 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, - 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, - 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, - 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, - 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, - 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, - 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, - 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, - 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, - 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, - 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, - 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, - 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, - 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, - 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, - 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, - 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, - 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, - 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, - 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, - 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, - 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, - 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, - 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, - 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, - 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, - 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, - 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, - 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, - 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, - 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, - 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, - 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, - 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, - 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, - 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, - 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, - 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, - 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, - 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, - 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, - 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, - 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, - 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, - 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, - 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, - 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, - 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, - 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, - 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, - 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, - 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, - 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, - 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, - 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, - 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, - 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, - 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, - 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, - 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, - 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, - 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, - 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, - 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, - 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, - 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, - 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, - 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, - 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, - 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, - 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, - 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, - 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, - 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, - 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, - 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, - 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, - 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, - 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, - 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, - 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, - 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, - 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, - 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, - 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, - 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, - 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, - 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, - 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, - 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, - 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, - 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, - 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, - 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, - 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, - 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x43, - 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7, - 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, 0x8c, - 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, 0x6e, - 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f, - 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, 0x59, - 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, 0x86, - 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a, - 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, 0x84, - 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, 0x53, - 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e, - 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, 0x89, - 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, 0x5e, - 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90, - 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, 0x64, - 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, 0x80, - 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97, - 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, 0x79, - 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, 0x9e, - 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c, - 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, 0x8c, - 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, 0x6a, - 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e, - 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, 0x51, - 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, 0x83, - 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a, - 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, 0x60, - 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, 0x78, - 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, - 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, 0x58, - 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, 0x6b, - 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82, - 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, 0x51, - 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, 0x82, - 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54, - 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, 0x6f, - 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, 0x9e, - 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b, - 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, 0x62, - 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, 0x74, - 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f, - 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, 0x52, - 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, 0x88, - 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63, - 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, 0x4e, - 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, 0x60, - 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80, - 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, 0x98, - 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, 0x96, - 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b, - 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, 0x71, - 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, 0x9f, - 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67, - 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, 0x74, - 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, 0x98, - 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50, - 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, - 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, 0x96, - 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66, - 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, 0x74, - 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, 0x88, - 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e, - 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, 0x85, - 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, 0x67, - 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b, - 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, 0x8b, - 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, 0x52, - 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b, - 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, 0x88, - 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, 0x51, - 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58, - 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, 0x79, - 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97, - 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, 0x86, - 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98, - 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, 0x96, - 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, 0x52, - 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56, - 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, 0x5c, - 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, 0x61, - 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66, - 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, 0x6f, - 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, 0x78, - 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79, - 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, 0x79, - 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, 0x7d, - 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80, - 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, 0x84, - 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, 0x8b, - 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90, - 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, 0x60, - 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, 0xb5, - 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80, - 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, 0x55, - 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, 0xb3, - 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28, - 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, 0xad, - 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, 0x8e, - 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4, - 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, 0x74, - 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, 0x79, - 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb, - 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, 0xa7, - 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, 0x71, - 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f, - 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, 0x4a, - 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, 0xc0, - 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, - 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, 0x79, - 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, 0xbf, - 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe, - 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, 0x08, - 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, 0x76, - 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb, - 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, 0x12, - 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, 0xd5, - 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49, - 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, 0x8e, - 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, 0x69, - 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c, - 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, 0x05, - 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, 0x11, - 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00, - 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, 0x05, - 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, 0x18, - 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2, - 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, 0xc2, - 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, 0xb8, - 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc, - 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, 0xbc, - 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, 0x2d, - 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31, - 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, 0x2b, - 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, 0x00, - 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09, - 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, 0x08, - 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35, - 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, - 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, - 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17, - 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, 0x38, - 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, 0x42, - 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, 0x50, - 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, 0x62, - 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e, - 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, 0x82, - 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, 0x87, - 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01, - 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, 0x22, - 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, 0xa0, - 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa, - 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23, - 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, 0x06, - 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23, - 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, - 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, 0x06, - 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23, - 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, 0x23, - 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, - 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f, - 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f, - 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, - 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f, - 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06, - 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08, - 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07, - 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01, - 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, - 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06, - 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x23, - 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08, - 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06, - 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, 0x23, - 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x23, - 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08, - 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, 0x4a, - 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, 0x06, - 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26, - 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, 0x00, - 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, 0x02, - 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02, - 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, 0x26, - 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, 0x05, - 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05, - 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, 0x1c, - 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, 0x1d, - 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e, - 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, 0x20, - 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, 0x20, - 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24, - 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, 0x24, - 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, - 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02, - 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, 0x04, - 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, 0x05, - 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, - 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, 0x0d, - 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, - 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, - 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, 0x13, - 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, - 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c, - 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, 0x1d, - 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, 0x1e, - 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f, - 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x20, - 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, - 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24, - 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, - 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, 0x02, - 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05, - 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, 0x0e, - 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, 0x20, - 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40, - 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, 0x10, - 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, - 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, - 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, - 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, - 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, - 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, - 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x10, - 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, - 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, - 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, - 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, - 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, - 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, - 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, - 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0c, - 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, 0x0c, - 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d, - 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, 0x00, - 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, 0x03, - 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00, - 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, 0x03, - 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, 0x1b, - 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09, - 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, 0x1f, - 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, 0x1b, - 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b, - 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, 0x1b, - 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x02, - 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a, - 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, 0x1f, - 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, 0x1b, - 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d, - 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, 0x1b, - 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, 0x1b, - 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17, - 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, 0x03, - 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, 0x03, - 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a, - 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, 0x1b, - 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, 0x03, - 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b, - 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, 0x02, - 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, 0x06, - 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05, - 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, 0x1e, - 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, 0x22, - 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d, - 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, 0x02, - 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, 0x21, - 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d, - 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, 0x0b, - 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, 0x22, - 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d, - 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, 0x22, - 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, 0x22, - 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05, - 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, 0x1e, - 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, 0x1d, - 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04, - 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, 0x1d, - 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, 0x22, - 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d, - 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, 0x35, - 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, 0x27, - 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01, - 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, 0x12, - 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, 0x23, - 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00, - 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, 0xff, - 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, 0x23, - 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06, - 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, 0x20, - 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, 0x00, - 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00, - 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, 0x30, - 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, 0x5f, - 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c, - 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, 0x04, - 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, 0x3e, - 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f, - 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x01, - 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x3a, - 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28, - 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x14, - 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, 0x2d, - 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40, - 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, 0x0c, - 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, 0x0e, - 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10, - 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, 0x12, - 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, 0x02, - 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, - 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, - 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, - 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, - 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, - 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, - 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, - 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20, - 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, - 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, - 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29, - 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, 0x22, - 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22, - 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, 0x01, - 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, 0xa0, - 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, - 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, - 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, - 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb, - 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd, - 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xe8, - 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99, - 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, 0x31, - 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, 0x00, - 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, - 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, - 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, - 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] = { - 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, - 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, - 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, - 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, - 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, - 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, - 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, - 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, - 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, - 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, - 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, - 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, - 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, - 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, - 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, - 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, - 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, - 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, - 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, - 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, - 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, - 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, - 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, - 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, - 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, - 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, - 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, - 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, - 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, - 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, - 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, - 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, - 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, - 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, - 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, - 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, - 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, - 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, - 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, - 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, - 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, - 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, - 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, - 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, - 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, - 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, - 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, - 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, - 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, - 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, - 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, - 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, - 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, - 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, - 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, - 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, - 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, - 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, - 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, - 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, - 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, - 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, - 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, - 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, - 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, - 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, - 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, - 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, - 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, - 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, - 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, - 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, - 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, - 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, - 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, - 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, - 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, - 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, - 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, - 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, - 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, - 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, - 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, - 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, - 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, - 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, - 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, - 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, - 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, - 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, - 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, - 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, - 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, - 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, - 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, - 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, - 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, - 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, - 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, - 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, - 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, - 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, - 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, - 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, - 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, - 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, - 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, - 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, - 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, - 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, - 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, - 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, - 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, - 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, -}; - -typedef enum { - UNICODE_GC_Cn, - UNICODE_GC_Lu, - UNICODE_GC_Ll, - UNICODE_GC_Lt, - UNICODE_GC_Lm, - UNICODE_GC_Lo, - UNICODE_GC_Mn, - UNICODE_GC_Mc, - UNICODE_GC_Me, - UNICODE_GC_Nd, - UNICODE_GC_Nl, - UNICODE_GC_No, - UNICODE_GC_Sm, - UNICODE_GC_Sc, - UNICODE_GC_Sk, - UNICODE_GC_So, - UNICODE_GC_Pc, - UNICODE_GC_Pd, - UNICODE_GC_Ps, - UNICODE_GC_Pe, - UNICODE_GC_Pi, - UNICODE_GC_Pf, - UNICODE_GC_Po, - UNICODE_GC_Zs, - UNICODE_GC_Zl, - UNICODE_GC_Zp, - UNICODE_GC_Cc, - UNICODE_GC_Cf, - UNICODE_GC_Cs, - UNICODE_GC_Co, - UNICODE_GC_LC, - UNICODE_GC_L, - UNICODE_GC_M, - UNICODE_GC_N, - UNICODE_GC_S, - UNICODE_GC_P, - UNICODE_GC_Z, - UNICODE_GC_C, - UNICODE_GC_COUNT, -} UnicodeGCEnum; - -static const char unicode_gc_name_table[] = - "Cn,Unassigned" "\0" - "Lu,Uppercase_Letter" "\0" - "Ll,Lowercase_Letter" "\0" - "Lt,Titlecase_Letter" "\0" - "Lm,Modifier_Letter" "\0" - "Lo,Other_Letter" "\0" - "Mn,Nonspacing_Mark" "\0" - "Mc,Spacing_Mark" "\0" - "Me,Enclosing_Mark" "\0" - "Nd,Decimal_Number,digit" "\0" - "Nl,Letter_Number" "\0" - "No,Other_Number" "\0" - "Sm,Math_Symbol" "\0" - "Sc,Currency_Symbol" "\0" - "Sk,Modifier_Symbol" "\0" - "So,Other_Symbol" "\0" - "Pc,Connector_Punctuation" "\0" - "Pd,Dash_Punctuation" "\0" - "Ps,Open_Punctuation" "\0" - "Pe,Close_Punctuation" "\0" - "Pi,Initial_Punctuation" "\0" - "Pf,Final_Punctuation" "\0" - "Po,Other_Punctuation" "\0" - "Zs,Space_Separator" "\0" - "Zl,Line_Separator" "\0" - "Zp,Paragraph_Separator" "\0" - "Cc,Control,cntrl" "\0" - "Cf,Format" "\0" - "Cs,Surrogate" "\0" - "Co,Private_Use" "\0" - "LC,Cased_Letter" "\0" - "L,Letter" "\0" - "M,Mark,Combining_Mark" "\0" - "N,Number" "\0" - "S,Symbol" "\0" - "P,Punctuation,punct" "\0" - "Z,Separator" "\0" - "C,Other" "\0" -; - -static const uint8_t unicode_gc_table[3948] = { - 0xfa, 0x18, 0x17, 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, - 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, - 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, - 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, - 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, - 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, - 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, - 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, - 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, - 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, - 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, - 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, - 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, - 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, - 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, - 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, - 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, - 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, - 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, - 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, - 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, - 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, - 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, - 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, - 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, - 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, - 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, - 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, - 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, - 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5, - 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06, - 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6, - 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45, - 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06, - 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6, - 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19, - 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06, - 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, - 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05, - 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6, - 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, - 0xe5, 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, - 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05, - 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20, - 0x27, 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07, - 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, - 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06, - 0x20, 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20, - 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25, - 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60, - 0x26, 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00, - 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16, - 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00, - 0x45, 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, - 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00, - 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0, - 0x07, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d, - 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5, - 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, - 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07, - 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06, - 0xc0, 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, - 0x20, 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02, - 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65, - 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25, - 0x40, 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06, - 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05, - 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf, - 0x0d, 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00, - 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08, - 0x20, 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, - 0x66, 0xc0, 0x26, 0x00, 0x45, 0x20, 0x05, 0x20, - 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb, - 0x0f, 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00, - 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00, - 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00, - 0x06, 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xa0, - 0x25, 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00, - 0x25, 0x07, 0xe0, 0x04, 0x26, 0x27, 0xe5, 0x01, - 0x00, 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, - 0x66, 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, - 0x60, 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, - 0x02, 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, - 0x00, 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, - 0x01, 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, - 0x47, 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, - 0xe9, 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, - 0x28, 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, - 0xe6, 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, - 0x25, 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, - 0x00, 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, - 0x01, 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xc6, - 0x00, 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, - 0x4f, 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, - 0xe9, 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, - 0x0f, 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, - 0x00, 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, - 0x86, 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, - 0x1c, 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, - 0x96, 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, - 0x66, 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, - 0xe9, 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, - 0x05, 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, - 0x06, 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, - 0x02, 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, - 0x80, 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, - 0xe5, 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, - 0x05, 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, - 0x20, 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, - 0x05, 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, - 0x31, 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, - 0xf6, 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, - 0x02, 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, - 0xe5, 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, - 0xe5, 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, - 0x4a, 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, - 0xe0, 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, - 0x01, 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, - 0x00, 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, - 0x26, 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, - 0x03, 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, - 0xe9, 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, - 0x76, 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, - 0x1b, 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, - 0x1a, 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, - 0xe5, 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, - 0x27, 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, - 0xe9, 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, - 0xe5, 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, - 0x0b, 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, - 0x06, 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, - 0xc6, 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, - 0xa7, 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, - 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, - 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, - 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 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, 0x04, 0x60, 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, - 0x12, 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, - 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, - 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, - 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, - 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, - 0x0f, 0xc5, 0xe0, 0x80, 0x31, 0xe5, 0x24, 0x47, - 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, 0x5c, 0xe1, - 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, 0x01, 0xe0, - 0x04, 0xe5, 0x00, 0x20, 0x05, 0x20, 0xe5, 0x00, - 0x00, 0x25, 0x00, 0xe5, 0x10, 0xa7, 0x00, 0x27, - 0x20, 0x26, 0x07, 0x06, 0x05, 0x07, 0x05, 0x07, - 0x06, 0x56, 0xe0, 0x01, 0xe9, 0x02, 0xe0, 0x3e, - 0xe5, 0x00, 0x20, 0xe5, 0x1f, 0x47, 0x66, 0x20, - 0x26, 0x67, 0x06, 0x05, 0x16, 0x05, 0x07, 0xe0, - 0x13, 0x05, 0xe6, 0x02, 0xe5, 0x20, 0xa6, 0x07, - 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, - 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6, 0x07, - 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02, 0xeb, - 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6, 0x0e, - 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, 0x26, 0xe0, - 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e, 0xa6, - 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05, 0x06, - 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, 0x00, 0x25, - 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00, 0x27, - 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02, 0xe0, - 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, - 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, -}; - -typedef enum { - UNICODE_SCRIPT_Unknown, - UNICODE_SCRIPT_Adlam, - UNICODE_SCRIPT_Ahom, - UNICODE_SCRIPT_Anatolian_Hieroglyphs, - UNICODE_SCRIPT_Arabic, - UNICODE_SCRIPT_Armenian, - UNICODE_SCRIPT_Avestan, - UNICODE_SCRIPT_Balinese, - UNICODE_SCRIPT_Bamum, - UNICODE_SCRIPT_Bassa_Vah, - UNICODE_SCRIPT_Batak, - UNICODE_SCRIPT_Bengali, - UNICODE_SCRIPT_Bhaiksuki, - UNICODE_SCRIPT_Bopomofo, - UNICODE_SCRIPT_Brahmi, - UNICODE_SCRIPT_Braille, - UNICODE_SCRIPT_Buginese, - UNICODE_SCRIPT_Buhid, - UNICODE_SCRIPT_Canadian_Aboriginal, - UNICODE_SCRIPT_Carian, - UNICODE_SCRIPT_Caucasian_Albanian, - UNICODE_SCRIPT_Chakma, - UNICODE_SCRIPT_Cham, - UNICODE_SCRIPT_Cherokee, - UNICODE_SCRIPT_Chorasmian, - UNICODE_SCRIPT_Common, - UNICODE_SCRIPT_Coptic, - UNICODE_SCRIPT_Cuneiform, - UNICODE_SCRIPT_Cypriot, - UNICODE_SCRIPT_Cyrillic, - UNICODE_SCRIPT_Cypro_Minoan, - UNICODE_SCRIPT_Deseret, - UNICODE_SCRIPT_Devanagari, - UNICODE_SCRIPT_Dives_Akuru, - UNICODE_SCRIPT_Dogra, - UNICODE_SCRIPT_Duployan, - UNICODE_SCRIPT_Egyptian_Hieroglyphs, - UNICODE_SCRIPT_Elbasan, - UNICODE_SCRIPT_Elymaic, - UNICODE_SCRIPT_Ethiopic, - UNICODE_SCRIPT_Georgian, - UNICODE_SCRIPT_Glagolitic, - UNICODE_SCRIPT_Gothic, - UNICODE_SCRIPT_Grantha, - UNICODE_SCRIPT_Greek, - UNICODE_SCRIPT_Gujarati, - UNICODE_SCRIPT_Gunjala_Gondi, - UNICODE_SCRIPT_Gurmukhi, - UNICODE_SCRIPT_Han, - UNICODE_SCRIPT_Hangul, - UNICODE_SCRIPT_Hanifi_Rohingya, - UNICODE_SCRIPT_Hanunoo, - UNICODE_SCRIPT_Hatran, - UNICODE_SCRIPT_Hebrew, - UNICODE_SCRIPT_Hiragana, - UNICODE_SCRIPT_Imperial_Aramaic, - UNICODE_SCRIPT_Inherited, - UNICODE_SCRIPT_Inscriptional_Pahlavi, - UNICODE_SCRIPT_Inscriptional_Parthian, - UNICODE_SCRIPT_Javanese, - UNICODE_SCRIPT_Kaithi, - UNICODE_SCRIPT_Kannada, - UNICODE_SCRIPT_Katakana, - UNICODE_SCRIPT_Kawi, - UNICODE_SCRIPT_Kayah_Li, - UNICODE_SCRIPT_Kharoshthi, - UNICODE_SCRIPT_Khmer, - UNICODE_SCRIPT_Khojki, - UNICODE_SCRIPT_Khitan_Small_Script, - UNICODE_SCRIPT_Khudawadi, - UNICODE_SCRIPT_Lao, - UNICODE_SCRIPT_Latin, - UNICODE_SCRIPT_Lepcha, - UNICODE_SCRIPT_Limbu, - UNICODE_SCRIPT_Linear_A, - UNICODE_SCRIPT_Linear_B, - UNICODE_SCRIPT_Lisu, - UNICODE_SCRIPT_Lycian, - UNICODE_SCRIPT_Lydian, - UNICODE_SCRIPT_Makasar, - UNICODE_SCRIPT_Mahajani, - UNICODE_SCRIPT_Malayalam, - UNICODE_SCRIPT_Mandaic, - UNICODE_SCRIPT_Manichaean, - UNICODE_SCRIPT_Marchen, - UNICODE_SCRIPT_Masaram_Gondi, - UNICODE_SCRIPT_Medefaidrin, - UNICODE_SCRIPT_Meetei_Mayek, - UNICODE_SCRIPT_Mende_Kikakui, - UNICODE_SCRIPT_Meroitic_Cursive, - UNICODE_SCRIPT_Meroitic_Hieroglyphs, - UNICODE_SCRIPT_Miao, - UNICODE_SCRIPT_Modi, - UNICODE_SCRIPT_Mongolian, - UNICODE_SCRIPT_Mro, - UNICODE_SCRIPT_Multani, - UNICODE_SCRIPT_Myanmar, - UNICODE_SCRIPT_Nabataean, - UNICODE_SCRIPT_Nag_Mundari, - UNICODE_SCRIPT_Nandinagari, - UNICODE_SCRIPT_New_Tai_Lue, - UNICODE_SCRIPT_Newa, - UNICODE_SCRIPT_Nko, - UNICODE_SCRIPT_Nushu, - UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, - UNICODE_SCRIPT_Ogham, - UNICODE_SCRIPT_Ol_Chiki, - UNICODE_SCRIPT_Old_Hungarian, - UNICODE_SCRIPT_Old_Italic, - UNICODE_SCRIPT_Old_North_Arabian, - UNICODE_SCRIPT_Old_Permic, - UNICODE_SCRIPT_Old_Persian, - UNICODE_SCRIPT_Old_Sogdian, - UNICODE_SCRIPT_Old_South_Arabian, - UNICODE_SCRIPT_Old_Turkic, - UNICODE_SCRIPT_Old_Uyghur, - UNICODE_SCRIPT_Oriya, - UNICODE_SCRIPT_Osage, - UNICODE_SCRIPT_Osmanya, - UNICODE_SCRIPT_Pahawh_Hmong, - UNICODE_SCRIPT_Palmyrene, - UNICODE_SCRIPT_Pau_Cin_Hau, - UNICODE_SCRIPT_Phags_Pa, - UNICODE_SCRIPT_Phoenician, - UNICODE_SCRIPT_Psalter_Pahlavi, - UNICODE_SCRIPT_Rejang, - UNICODE_SCRIPT_Runic, - UNICODE_SCRIPT_Samaritan, - UNICODE_SCRIPT_Saurashtra, - UNICODE_SCRIPT_Sharada, - UNICODE_SCRIPT_Shavian, - UNICODE_SCRIPT_Siddham, - UNICODE_SCRIPT_SignWriting, - UNICODE_SCRIPT_Sinhala, - UNICODE_SCRIPT_Sogdian, - UNICODE_SCRIPT_Sora_Sompeng, - UNICODE_SCRIPT_Soyombo, - UNICODE_SCRIPT_Sundanese, - UNICODE_SCRIPT_Syloti_Nagri, - UNICODE_SCRIPT_Syriac, - UNICODE_SCRIPT_Tagalog, - UNICODE_SCRIPT_Tagbanwa, - UNICODE_SCRIPT_Tai_Le, - UNICODE_SCRIPT_Tai_Tham, - UNICODE_SCRIPT_Tai_Viet, - UNICODE_SCRIPT_Takri, - UNICODE_SCRIPT_Tamil, - UNICODE_SCRIPT_Tangut, - UNICODE_SCRIPT_Telugu, - UNICODE_SCRIPT_Thaana, - UNICODE_SCRIPT_Thai, - UNICODE_SCRIPT_Tibetan, - UNICODE_SCRIPT_Tifinagh, - UNICODE_SCRIPT_Tirhuta, - UNICODE_SCRIPT_Tangsa, - UNICODE_SCRIPT_Toto, - UNICODE_SCRIPT_Ugaritic, - UNICODE_SCRIPT_Vai, - UNICODE_SCRIPT_Vithkuqi, - UNICODE_SCRIPT_Wancho, - UNICODE_SCRIPT_Warang_Citi, - UNICODE_SCRIPT_Yezidi, - UNICODE_SCRIPT_Yi, - UNICODE_SCRIPT_Zanabazar_Square, - UNICODE_SCRIPT_COUNT, -} UnicodeScriptEnum; - -static const char unicode_script_name_table[] = - "Adlam,Adlm" "\0" - "Ahom,Ahom" "\0" - "Anatolian_Hieroglyphs,Hluw" "\0" - "Arabic,Arab" "\0" - "Armenian,Armn" "\0" - "Avestan,Avst" "\0" - "Balinese,Bali" "\0" - "Bamum,Bamu" "\0" - "Bassa_Vah,Bass" "\0" - "Batak,Batk" "\0" - "Bengali,Beng" "\0" - "Bhaiksuki,Bhks" "\0" - "Bopomofo,Bopo" "\0" - "Brahmi,Brah" "\0" - "Braille,Brai" "\0" - "Buginese,Bugi" "\0" - "Buhid,Buhd" "\0" - "Canadian_Aboriginal,Cans" "\0" - "Carian,Cari" "\0" - "Caucasian_Albanian,Aghb" "\0" - "Chakma,Cakm" "\0" - "Cham,Cham" "\0" - "Cherokee,Cher" "\0" - "Chorasmian,Chrs" "\0" - "Common,Zyyy" "\0" - "Coptic,Copt,Qaac" "\0" - "Cuneiform,Xsux" "\0" - "Cypriot,Cprt" "\0" - "Cyrillic,Cyrl" "\0" - "Cypro_Minoan,Cpmn" "\0" - "Deseret,Dsrt" "\0" - "Devanagari,Deva" "\0" - "Dives_Akuru,Diak" "\0" - "Dogra,Dogr" "\0" - "Duployan,Dupl" "\0" - "Egyptian_Hieroglyphs,Egyp" "\0" - "Elbasan,Elba" "\0" - "Elymaic,Elym" "\0" - "Ethiopic,Ethi" "\0" - "Georgian,Geor" "\0" - "Glagolitic,Glag" "\0" - "Gothic,Goth" "\0" - "Grantha,Gran" "\0" - "Greek,Grek" "\0" - "Gujarati,Gujr" "\0" - "Gunjala_Gondi,Gong" "\0" - "Gurmukhi,Guru" "\0" - "Han,Hani" "\0" - "Hangul,Hang" "\0" - "Hanifi_Rohingya,Rohg" "\0" - "Hanunoo,Hano" "\0" - "Hatran,Hatr" "\0" - "Hebrew,Hebr" "\0" - "Hiragana,Hira" "\0" - "Imperial_Aramaic,Armi" "\0" - "Inherited,Zinh,Qaai" "\0" - "Inscriptional_Pahlavi,Phli" "\0" - "Inscriptional_Parthian,Prti" "\0" - "Javanese,Java" "\0" - "Kaithi,Kthi" "\0" - "Kannada,Knda" "\0" - "Katakana,Kana" "\0" - "Kawi,Kawi" "\0" - "Kayah_Li,Kali" "\0" - "Kharoshthi,Khar" "\0" - "Khmer,Khmr" "\0" - "Khojki,Khoj" "\0" - "Khitan_Small_Script,Kits" "\0" - "Khudawadi,Sind" "\0" - "Lao,Laoo" "\0" - "Latin,Latn" "\0" - "Lepcha,Lepc" "\0" - "Limbu,Limb" "\0" - "Linear_A,Lina" "\0" - "Linear_B,Linb" "\0" - "Lisu,Lisu" "\0" - "Lycian,Lyci" "\0" - "Lydian,Lydi" "\0" - "Makasar,Maka" "\0" - "Mahajani,Mahj" "\0" - "Malayalam,Mlym" "\0" - "Mandaic,Mand" "\0" - "Manichaean,Mani" "\0" - "Marchen,Marc" "\0" - "Masaram_Gondi,Gonm" "\0" - "Medefaidrin,Medf" "\0" - "Meetei_Mayek,Mtei" "\0" - "Mende_Kikakui,Mend" "\0" - "Meroitic_Cursive,Merc" "\0" - "Meroitic_Hieroglyphs,Mero" "\0" - "Miao,Plrd" "\0" - "Modi,Modi" "\0" - "Mongolian,Mong" "\0" - "Mro,Mroo" "\0" - "Multani,Mult" "\0" - "Myanmar,Mymr" "\0" - "Nabataean,Nbat" "\0" - "Nag_Mundari,Nagm" "\0" - "Nandinagari,Nand" "\0" - "New_Tai_Lue,Talu" "\0" - "Newa,Newa" "\0" - "Nko,Nkoo" "\0" - "Nushu,Nshu" "\0" - "Nyiakeng_Puachue_Hmong,Hmnp" "\0" - "Ogham,Ogam" "\0" - "Ol_Chiki,Olck" "\0" - "Old_Hungarian,Hung" "\0" - "Old_Italic,Ital" "\0" - "Old_North_Arabian,Narb" "\0" - "Old_Permic,Perm" "\0" - "Old_Persian,Xpeo" "\0" - "Old_Sogdian,Sogo" "\0" - "Old_South_Arabian,Sarb" "\0" - "Old_Turkic,Orkh" "\0" - "Old_Uyghur,Ougr" "\0" - "Oriya,Orya" "\0" - "Osage,Osge" "\0" - "Osmanya,Osma" "\0" - "Pahawh_Hmong,Hmng" "\0" - "Palmyrene,Palm" "\0" - "Pau_Cin_Hau,Pauc" "\0" - "Phags_Pa,Phag" "\0" - "Phoenician,Phnx" "\0" - "Psalter_Pahlavi,Phlp" "\0" - "Rejang,Rjng" "\0" - "Runic,Runr" "\0" - "Samaritan,Samr" "\0" - "Saurashtra,Saur" "\0" - "Sharada,Shrd" "\0" - "Shavian,Shaw" "\0" - "Siddham,Sidd" "\0" - "SignWriting,Sgnw" "\0" - "Sinhala,Sinh" "\0" - "Sogdian,Sogd" "\0" - "Sora_Sompeng,Sora" "\0" - "Soyombo,Soyo" "\0" - "Sundanese,Sund" "\0" - "Syloti_Nagri,Sylo" "\0" - "Syriac,Syrc" "\0" - "Tagalog,Tglg" "\0" - "Tagbanwa,Tagb" "\0" - "Tai_Le,Tale" "\0" - "Tai_Tham,Lana" "\0" - "Tai_Viet,Tavt" "\0" - "Takri,Takr" "\0" - "Tamil,Taml" "\0" - "Tangut,Tang" "\0" - "Telugu,Telu" "\0" - "Thaana,Thaa" "\0" - "Thai,Thai" "\0" - "Tibetan,Tibt" "\0" - "Tifinagh,Tfng" "\0" - "Tirhuta,Tirh" "\0" - "Tangsa,Tnsa" "\0" - "Toto,Toto" "\0" - "Ugaritic,Ugar" "\0" - "Vai,Vaii" "\0" - "Vithkuqi,Vith" "\0" - "Wancho,Wcho" "\0" - "Warang_Citi,Wara" "\0" - "Yezidi,Yezi" "\0" - "Yi,Yiii" "\0" - "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, - 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, - 0x00, 0xb6, 0x35, 0x07, 0x9a, 0x35, 0x03, 0x85, - 0x35, 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, - 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, - 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, - 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, - 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, - 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, - 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, - 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, - 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, - 0x60, 0x03, 0xd2, 0xa0, 0x0b, 0x80, 0xa0, 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, -}; - -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_prop_Hyphen_table[28] = { - 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, - 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, - 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, - 0xa8, 0x80, 0xd6, 0x80, -}; - -static const uint8_t unicode_prop_Other_Math_table[200] = { - 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, - 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, - 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, - 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, - 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, - 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, - 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, - 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, - 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, - 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, - 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, - 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, - 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, - 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, - 0xc0, 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, 0x81, - 0xb1, 0x55, 0xff, 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, -}; - -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_Lowercase_table[69] = { - 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, - 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x4d, - 0x80, 0x80, 0x4c, 0x2e, 0xbe, 0x8c, 0x80, 0xa1, - 0xa4, 0x42, 0xb0, 0x80, 0x8c, 0x80, 0x8f, 0x8c, - 0x40, 0xd2, 0x8f, 0x43, 0x4f, 0x99, 0x47, 0x91, - 0x81, 0x60, 0x7a, 0x1d, 0x81, 0x40, 0xd1, 0x80, - 0x40, 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, - 0x80, 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, - 0x88, 0x60, 0xd8, 0x74, 0xbd, -}; - -static const uint8_t unicode_prop_Other_Uppercase_table[15] = { - 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, - 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = { - 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, -}; - -static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { - 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, - 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, - 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, - 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, -}; - -static const uint8_t unicode_prop_Other_ID_Start_table[11] = { - 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, - 0x4f, 0x6b, 0x81, -}; - -static const uint8_t unicode_prop_Other_ID_Continue_table[12] = { - 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, - 0x88, 0x46, 0x67, 0x80, -}; - -static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = { - 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, - 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9, - 0x80, 0x8e, 0x80, -}; - -static const uint8_t unicode_prop_XID_Start1_table[31] = { - 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, - 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, - 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, -}; - -static const uint8_t unicode_prop_XID_Continue1_table[23] = { - 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, - 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -}; - -static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { - 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, - 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, - 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, -}; - -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[447] = { - 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, - 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, 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, -}; - -static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { - 0xaf, 0x89, 0x35, 0x99, 0x85, -}; - -static const uint8_t unicode_prop_Bidi_Control_table[10] = { - 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, - 0xb6, 0x83, -}; - -static const uint8_t unicode_prop_Dash_table[55] = { - 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, -}; - -static const uint8_t unicode_prop_Deprecated_table[23] = { - 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, - 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, - 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, -}; - -static const uint8_t unicode_prop_Diacritic_table[399] = { - 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, - 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, - 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, - 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, - 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, - 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, - 0x81, 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc, - 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80, - 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, - 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, -}; - -static const uint8_t unicode_prop_Extender_table[92] = { - 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, - 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, - 0x81, 0x48, 0x05, 0x82, -}; - -static const uint8_t unicode_prop_Hex_Digit_table[12] = { - 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, - 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_Trinary_Operator_table[4] = { - 0x60, 0x2f, 0xf1, 0x81, -}; - -static const uint8_t unicode_prop_Ideographic_table[69] = { - 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, - 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, -}; - -static const uint8_t unicode_prop_Join_Control_table[4] = { - 0x60, 0x20, 0x0b, 0x81, -}; - -static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { - 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, - 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, -}; - -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, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, -}; - -static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { - 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, - 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, - 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, - 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, - 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, - 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, - 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, - 0x04, 0x81, -}; - -static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { - 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, - 0x81, 0x97, 0x81, -}; - -static const uint8_t unicode_prop_Quotation_Mark_table[31] = { - 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, - 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, - 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, - 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, -}; - -static const uint8_t unicode_prop_Radical_table[9] = { - 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, - 0xd5, -}; - -static const uint8_t unicode_prop_Regional_Indicator_table[4] = { - 0x61, 0xf1, 0xe5, 0x99, -}; - -static const uint8_t unicode_prop_Sentence_Terminal_table[196] = { - 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, -}; - -static const uint8_t unicode_prop_Soft_Dotted_table[79] = { - 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, - 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, - 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, - 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, - 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, - 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, - 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, - 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, - 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48, - 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, -}; - -static const uint8_t unicode_prop_Terminal_Punctuation_table[248] = { - 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, - 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, - 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, -}; - -static const uint8_t unicode_prop_Unified_Ideograph_table[45] = { - 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, -}; - -static const uint8_t unicode_prop_Variation_Selector_table[13] = { - 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f, - 0x6d, 0x02, 0xef, 0x40, 0xef, -}; - -static const uint8_t unicode_prop_White_Space_table[22] = { - 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, - 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, - 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, -}; - -static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { - 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, - 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, - 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, - 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, - 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, - 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, - 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, - 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, - 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, - 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, - 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, - 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, - 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, - 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, - 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce, - 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, - 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, - 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, - 0x80, 0xb8, 0x80, 0xb8, 0x80, -}; - -static const uint8_t unicode_prop_Emoji_table[239] = { - 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, - 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, - 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, - 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, - 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, - 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, - 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, - 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, - 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, - 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, - 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, - 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, - 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, - 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, - 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, - 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, - 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, - 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, - 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, - 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, - 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, - 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, - 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, - 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, - 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, - 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, -}; - -static const uint8_t unicode_prop_Emoji_Component_table[28] = { - 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, - 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, - 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, - 0x6c, 0x06, 0x6b, 0xdf, -}; - -static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { - 0x61, 0xf3, 0xfa, 0x84, -}; - -static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { - 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, - 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, - 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 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, - 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90, - 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, -}; - -static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { - 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, 0xca, 0x99, 0x00, 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, 0x88, - 0x86, 0xad, 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, - 0x88, -}; - -static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { - 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, - 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, - 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, - 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, - 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, - 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, - 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, - 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, - 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, - 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, - 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, - 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, - 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, - 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, - 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, - 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, - 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, - 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, - 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, - 0x40, 0xff, 0x43, 0xfd, -}; - -static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { - 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, - 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, - 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f, - 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, - 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, - 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, - 0x84, 0x4f, 0xff, -}; - -typedef enum { - UNICODE_PROP_Hyphen, - UNICODE_PROP_Other_Math, - UNICODE_PROP_Other_Alphabetic, - UNICODE_PROP_Other_Lowercase, - UNICODE_PROP_Other_Uppercase, - UNICODE_PROP_Other_Grapheme_Extend, - UNICODE_PROP_Other_Default_Ignorable_Code_Point, - UNICODE_PROP_Other_ID_Start, - UNICODE_PROP_Other_ID_Continue, - UNICODE_PROP_Prepended_Concatenation_Mark, - UNICODE_PROP_ID_Continue1, - UNICODE_PROP_XID_Start1, - UNICODE_PROP_XID_Continue1, - UNICODE_PROP_Changes_When_Titlecased1, - UNICODE_PROP_Changes_When_Casefolded1, - UNICODE_PROP_Changes_When_NFKC_Casefolded1, - UNICODE_PROP_ASCII_Hex_Digit, - UNICODE_PROP_Bidi_Control, - UNICODE_PROP_Dash, - UNICODE_PROP_Deprecated, - UNICODE_PROP_Diacritic, - UNICODE_PROP_Extender, - UNICODE_PROP_Hex_Digit, - UNICODE_PROP_IDS_Binary_Operator, - UNICODE_PROP_IDS_Trinary_Operator, - UNICODE_PROP_Ideographic, - UNICODE_PROP_Join_Control, - UNICODE_PROP_Logical_Order_Exception, - UNICODE_PROP_Noncharacter_Code_Point, - UNICODE_PROP_Pattern_Syntax, - UNICODE_PROP_Pattern_White_Space, - UNICODE_PROP_Quotation_Mark, - UNICODE_PROP_Radical, - UNICODE_PROP_Regional_Indicator, - UNICODE_PROP_Sentence_Terminal, - UNICODE_PROP_Soft_Dotted, - UNICODE_PROP_Terminal_Punctuation, - UNICODE_PROP_Unified_Ideograph, - UNICODE_PROP_Variation_Selector, - UNICODE_PROP_White_Space, - UNICODE_PROP_Bidi_Mirrored, - UNICODE_PROP_Emoji, - UNICODE_PROP_Emoji_Component, - UNICODE_PROP_Emoji_Modifier, - UNICODE_PROP_Emoji_Modifier_Base, - UNICODE_PROP_Emoji_Presentation, - UNICODE_PROP_Extended_Pictographic, - UNICODE_PROP_Default_Ignorable_Code_Point, - UNICODE_PROP_ID_Start, - UNICODE_PROP_Case_Ignorable, - UNICODE_PROP_ASCII, - UNICODE_PROP_Alphabetic, - UNICODE_PROP_Any, - UNICODE_PROP_Assigned, - UNICODE_PROP_Cased, - UNICODE_PROP_Changes_When_Casefolded, - UNICODE_PROP_Changes_When_Casemapped, - UNICODE_PROP_Changes_When_Lowercased, - UNICODE_PROP_Changes_When_NFKC_Casefolded, - UNICODE_PROP_Changes_When_Titlecased, - UNICODE_PROP_Changes_When_Uppercased, - UNICODE_PROP_Grapheme_Base, - UNICODE_PROP_Grapheme_Extend, - UNICODE_PROP_ID_Continue, - UNICODE_PROP_Lowercase, - UNICODE_PROP_Math, - UNICODE_PROP_Uppercase, - UNICODE_PROP_XID_Continue, - UNICODE_PROP_XID_Start, - UNICODE_PROP_Cased1, - UNICODE_PROP_COUNT, -} UnicodePropertyEnum; - -static const char unicode_prop_name_table[] = - "ASCII_Hex_Digit,AHex" "\0" - "Bidi_Control,Bidi_C" "\0" - "Dash" "\0" - "Deprecated,Dep" "\0" - "Diacritic,Dia" "\0" - "Extender,Ext" "\0" - "Hex_Digit,Hex" "\0" - "IDS_Binary_Operator,IDSB" "\0" - "IDS_Trinary_Operator,IDST" "\0" - "Ideographic,Ideo" "\0" - "Join_Control,Join_C" "\0" - "Logical_Order_Exception,LOE" "\0" - "Noncharacter_Code_Point,NChar" "\0" - "Pattern_Syntax,Pat_Syn" "\0" - "Pattern_White_Space,Pat_WS" "\0" - "Quotation_Mark,QMark" "\0" - "Radical" "\0" - "Regional_Indicator,RI" "\0" - "Sentence_Terminal,STerm" "\0" - "Soft_Dotted,SD" "\0" - "Terminal_Punctuation,Term" "\0" - "Unified_Ideograph,UIdeo" "\0" - "Variation_Selector,VS" "\0" - "White_Space,space" "\0" - "Bidi_Mirrored,Bidi_M" "\0" - "Emoji" "\0" - "Emoji_Component,EComp" "\0" - "Emoji_Modifier,EMod" "\0" - "Emoji_Modifier_Base,EBase" "\0" - "Emoji_Presentation,EPres" "\0" - "Extended_Pictographic,ExtPict" "\0" - "Default_Ignorable_Code_Point,DI" "\0" - "ID_Start,IDS" "\0" - "Case_Ignorable,CI" "\0" - "ASCII" "\0" - "Alphabetic,Alpha" "\0" - "Any" "\0" - "Assigned" "\0" - "Cased" "\0" - "Changes_When_Casefolded,CWCF" "\0" - "Changes_When_Casemapped,CWCM" "\0" - "Changes_When_Lowercased,CWL" "\0" - "Changes_When_NFKC_Casefolded,CWKCF" "\0" - "Changes_When_Titlecased,CWT" "\0" - "Changes_When_Uppercased,CWU" "\0" - "Grapheme_Base,Gr_Base" "\0" - "Grapheme_Extend,Gr_Ext" "\0" - "ID_Continue,IDC" "\0" - "Lowercase,Lower" "\0" - "Math" "\0" - "Uppercase,Upper" "\0" - "XID_Continue,XIDC" "\0" - "XID_Start,XIDS" "\0" -; - -static const uint8_t * const unicode_prop_table[] = { - unicode_prop_Hyphen_table, - unicode_prop_Other_Math_table, - unicode_prop_Other_Alphabetic_table, - unicode_prop_Other_Lowercase_table, - unicode_prop_Other_Uppercase_table, - unicode_prop_Other_Grapheme_Extend_table, - unicode_prop_Other_Default_Ignorable_Code_Point_table, - unicode_prop_Other_ID_Start_table, - unicode_prop_Other_ID_Continue_table, - unicode_prop_Prepended_Concatenation_Mark_table, - unicode_prop_ID_Continue1_table, - unicode_prop_XID_Start1_table, - unicode_prop_XID_Continue1_table, - unicode_prop_Changes_When_Titlecased1_table, - unicode_prop_Changes_When_Casefolded1_table, - unicode_prop_Changes_When_NFKC_Casefolded1_table, - unicode_prop_ASCII_Hex_Digit_table, - unicode_prop_Bidi_Control_table, - unicode_prop_Dash_table, - unicode_prop_Deprecated_table, - unicode_prop_Diacritic_table, - unicode_prop_Extender_table, - unicode_prop_Hex_Digit_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_Noncharacter_Code_Point_table, - unicode_prop_Pattern_Syntax_table, - unicode_prop_Pattern_White_Space_table, - unicode_prop_Quotation_Mark_table, - unicode_prop_Radical_table, - unicode_prop_Regional_Indicator_table, - unicode_prop_Sentence_Terminal_table, - unicode_prop_Soft_Dotted_table, - unicode_prop_Terminal_Punctuation_table, - unicode_prop_Unified_Ideograph_table, - unicode_prop_Variation_Selector_table, - unicode_prop_White_Space_table, - unicode_prop_Bidi_Mirrored_table, - unicode_prop_Emoji_table, - unicode_prop_Emoji_Component_table, - unicode_prop_Emoji_Modifier_table, - unicode_prop_Emoji_Modifier_Base_table, - unicode_prop_Emoji_Presentation_table, - unicode_prop_Extended_Pictographic_table, - unicode_prop_Default_Ignorable_Code_Point_table, - unicode_prop_ID_Start_table, - unicode_prop_Case_Ignorable_table, -}; - -static const uint16_t unicode_prop_len_table[] = { - countof(unicode_prop_Hyphen_table), - countof(unicode_prop_Other_Math_table), - countof(unicode_prop_Other_Alphabetic_table), - countof(unicode_prop_Other_Lowercase_table), - countof(unicode_prop_Other_Uppercase_table), - countof(unicode_prop_Other_Grapheme_Extend_table), - countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), - countof(unicode_prop_Other_ID_Start_table), - countof(unicode_prop_Other_ID_Continue_table), - countof(unicode_prop_Prepended_Concatenation_Mark_table), - countof(unicode_prop_ID_Continue1_table), - countof(unicode_prop_XID_Start1_table), - countof(unicode_prop_XID_Continue1_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_ASCII_Hex_Digit_table), - countof(unicode_prop_Bidi_Control_table), - countof(unicode_prop_Dash_table), - countof(unicode_prop_Deprecated_table), - countof(unicode_prop_Diacritic_table), - countof(unicode_prop_Extender_table), - countof(unicode_prop_Hex_Digit_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_Noncharacter_Code_Point_table), - countof(unicode_prop_Pattern_Syntax_table), - countof(unicode_prop_Pattern_White_Space_table), - countof(unicode_prop_Quotation_Mark_table), - countof(unicode_prop_Radical_table), - countof(unicode_prop_Regional_Indicator_table), - countof(unicode_prop_Sentence_Terminal_table), - countof(unicode_prop_Soft_Dotted_table), - countof(unicode_prop_Terminal_Punctuation_table), - countof(unicode_prop_Unified_Ideograph_table), - countof(unicode_prop_Variation_Selector_table), - countof(unicode_prop_White_Space_table), - countof(unicode_prop_Bidi_Mirrored_table), - countof(unicode_prop_Emoji_table), - countof(unicode_prop_Emoji_Component_table), - countof(unicode_prop_Emoji_Modifier_table), - countof(unicode_prop_Emoji_Modifier_Base_table), - countof(unicode_prop_Emoji_Presentation_table), - countof(unicode_prop_Extended_Pictographic_table), - countof(unicode_prop_Default_Ignorable_Code_Point_table), - countof(unicode_prop_ID_Start_table), - countof(unicode_prop_Case_Ignorable_table), -}; - -#endif /* CONFIG_ALL_UNICODE */ -/* 62 tables / 32261 bytes, 5 index / 345 bytes */ diff --git a/lib/quickjs/libunicode.c b/lib/quickjs/libunicode.c deleted file mode 100644 index c80d2f3d..00000000 --- a/lib/quickjs/libunicode.c +++ /dev/null @@ -1,1910 +0,0 @@ -/* - * Unicode utilities - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <assert.h> - -#include "cutils.h" -#include "libunicode.h" -#include "libunicode-table.h" - -enum { - RUN_TYPE_U, - RUN_TYPE_L, - RUN_TYPE_UF, - RUN_TYPE_LF, - RUN_TYPE_UL, - RUN_TYPE_LSU, - RUN_TYPE_U2L_399_EXT2, - RUN_TYPE_UF_D20, - RUN_TYPE_UF_D1_EXT, - RUN_TYPE_U_EXT, - RUN_TYPE_LF_EXT, - RUN_TYPE_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 - 1 = to lower - 2 = case folding (= to lower with modifications) -*/ -int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) -{ - if (c < 128) { - if (conv_type) { - if (c >= 'A' && c <= 'Z') { - c = c - 'A' + 'a'; - } - } else { - if (c >= 'a' && c <= 'z') { - c = c - 'a' + 'A'; - } - } - } else { - uint32_t v, code, 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_conv_entry(res, c, conv_type, idx, v); - } - } - } - res[0] = c; - 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) -{ - return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); -} - -#define UNICODE_INDEX_BLOCK_LEN 32 - -/* return -1 if not in table, otherwise the offset in the block */ -static int get_index_pos(uint32_t *pcode, uint32_t c, - const uint8_t *index_table, int index_table_len) -{ - uint32_t code, v; - int idx_min, idx_max, idx; - - idx_min = 0; - v = get_le24(index_table); - code = v & ((1 << 21) - 1); - if (c < code) { - *pcode = 0; - return 0; - } - idx_max = index_table_len - 1; - code = get_le24(index_table + idx_max * 3); - if (c >= code) - return -1; - /* invariant: tab[idx_min] <= c < tab2[idx_max] */ - while ((idx_max - idx_min) > 1) { - idx = (idx_max + idx_min) / 2; - v = get_le24(index_table + idx * 3); - code = v & ((1 << 21) - 1); - if (c < code) { - idx_max = idx; - } else { - idx_min = idx; - } - } - v = get_le24(index_table + idx_min * 3); - *pcode = v & ((1 << 21) - 1); - return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); -} - -static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, - const uint8_t *index_table, int index_table_len) -{ - uint32_t code, b, bit; - int pos; - const uint8_t *p; - - pos = get_index_pos(&code, c, index_table, index_table_len); - if (pos < 0) - return FALSE; /* outside the table */ - p = table + pos; - bit = 0; - /* 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) { - code += (b >> 3) + 1; - if (c < code) - return bit; - bit ^= 1; - code += (b & 7) + 1; - } else if (b >= 0x80) { - code += b - 0x80 + 1; - } else if (b < 0x60) { - code += (((b - 0x40) << 8) | p[0]) + 1; - p++; - } else { - code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; - p += 2; - } - if (c < code) - return bit; - bit ^= 1; - } -} - -BOOL lre_is_cased(uint32_t c) -{ - uint32_t v, code, len; - int idx, idx_min, idx_max; - - idx_min = 0; - idx_max = countof(case_conv_table1) - 1; - while (idx_min <= idx_max) { - idx = (unsigned)(idx_max + idx_min) / 2; - v = case_conv_table1[idx]; - code = v >> (32 - 17); - len = (v >> (32 - 17 - 7)) & 0x7f; - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - return TRUE; - } - } - return lre_is_in_table(c, unicode_prop_Cased1_table, - unicode_prop_Cased1_index, - sizeof(unicode_prop_Cased1_index) / 3); -} - -BOOL lre_is_case_ignorable(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, - unicode_prop_Case_Ignorable_index, - sizeof(unicode_prop_Case_Ignorable_index) / 3); -} - -/* character range */ - -static __maybe_unused void cr_dump(CharRange *cr) -{ - int i; - for(i = 0; i < cr->len; i++) - printf("%d: 0x%04x\n", i, cr->points[i]); -} - -static void *cr_default_realloc(void *opaque, void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) -{ - cr->len = cr->size = 0; - cr->points = NULL; - cr->mem_opaque = mem_opaque; - cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; -} - -void cr_free(CharRange *cr) -{ - cr->realloc_func(cr->mem_opaque, cr->points, 0); -} - -int cr_realloc(CharRange *cr, int size) -{ - int new_size; - uint32_t *new_buf; - - if (size > cr->size) { - new_size = max_int(size, cr->size * 3 / 2); - new_buf = cr->realloc_func(cr->mem_opaque, cr->points, - new_size * sizeof(cr->points[0])); - if (!new_buf) - return -1; - cr->points = new_buf; - cr->size = new_size; - } - return 0; -} - -int cr_copy(CharRange *cr, const CharRange *cr1) -{ - if (cr_realloc(cr, cr1->len)) - return -1; - memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); - cr->len = cr1->len; - return 0; -} - -/* merge consecutive intervals and remove empty intervals */ -static void cr_compress(CharRange *cr) -{ - int i, j, k, len; - uint32_t *pt; - - pt = cr->points; - len = cr->len; - i = 0; - j = 0; - k = 0; - while ((i + 1) < len) { - if (pt[i] == pt[i + 1]) { - /* empty interval */ - i += 2; - } else { - j = i; - while ((j + 3) < len && pt[j + 1] == pt[j + 2]) - j += 2; - /* just copy */ - pt[k] = pt[i]; - pt[k + 1] = pt[j + 1]; - k += 2; - i = j + 2; - } - } - cr->len = k; -} - -/* union or intersection */ -int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, - const uint32_t *b_pt, int b_len, int op) -{ - int a_idx, b_idx, is_in; - uint32_t v; - - a_idx = 0; - b_idx = 0; - for(;;) { - /* get one more point from a or b in increasing order */ - if (a_idx < a_len && b_idx < b_len) { - if (a_pt[a_idx] < b_pt[b_idx]) { - goto a_add; - } else if (a_pt[a_idx] == b_pt[b_idx]) { - v = a_pt[a_idx]; - a_idx++; - b_idx++; - } else { - goto b_add; - } - } else if (a_idx < a_len) { - a_add: - v = a_pt[a_idx++]; - } else if (b_idx < b_len) { - b_add: - v = b_pt[b_idx++]; - } else { - break; - } - /* add the point if the in/out status changes */ - switch(op) { - case CR_OP_UNION: - is_in = (a_idx & 1) | (b_idx & 1); - break; - case CR_OP_INTER: - is_in = (a_idx & 1) & (b_idx & 1); - break; - case CR_OP_XOR: - is_in = (a_idx & 1) ^ (b_idx & 1); - break; - default: - abort(); - } - if (is_in != (cr->len & 1)) { - if (cr_add_point(cr, v)) - return -1; - } - } - cr_compress(cr); - return 0; -} - -int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) -{ - CharRange a = *cr; - int ret; - cr->len = 0; - cr->size = 0; - cr->points = NULL; - ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); - cr_free(&a); - return ret; -} - -int cr_invert(CharRange *cr) -{ - int len; - len = cr->len; - if (cr_realloc(cr, len + 2)) - return -1; - memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); - cr->points[0] = 0; - cr->points[len + 1] = UINT32_MAX; - cr->len = len + 2; - cr_compress(cr); - return 0; -} - -#ifdef CONFIG_ALL_UNICODE - -BOOL lre_is_id_start(uint32_t c) -{ - return lre_is_in_table(c, unicode_prop_ID_Start_table, - unicode_prop_ID_Start_index, - sizeof(unicode_prop_ID_Start_index) / 3); -} - -BOOL lre_is_id_continue(uint32_t c) -{ - return lre_is_id_start(c) || - lre_is_in_table(c, unicode_prop_ID_Continue1_table, - unicode_prop_ID_Continue1_index, - sizeof(unicode_prop_ID_Continue1_index) / 3); -} - -#define UNICODE_DECOMP_LEN_MAX 18 - -typedef enum { - DECOMP_TYPE_C1, /* 16 bit char */ - DECOMP_TYPE_L1, /* 16 bit char table */ - DECOMP_TYPE_L2, - DECOMP_TYPE_L3, - DECOMP_TYPE_L4, - DECOMP_TYPE_L5, /* XXX: not used */ - DECOMP_TYPE_L6, /* XXX: could remove */ - DECOMP_TYPE_L7, /* XXX: could remove */ - DECOMP_TYPE_LL1, /* 18 bit char table */ - DECOMP_TYPE_LL2, - DECOMP_TYPE_S1, /* 8 bit char table */ - DECOMP_TYPE_S2, - DECOMP_TYPE_S3, - DECOMP_TYPE_S4, - DECOMP_TYPE_S5, - DECOMP_TYPE_I1, /* increment 16 bit char value */ - DECOMP_TYPE_I2_0, - DECOMP_TYPE_I2_1, - DECOMP_TYPE_I3_1, - DECOMP_TYPE_I3_2, - DECOMP_TYPE_I4_1, - DECOMP_TYPE_I4_2, - DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ - DECOMP_TYPE_B2, - DECOMP_TYPE_B3, - DECOMP_TYPE_B4, - DECOMP_TYPE_B5, - DECOMP_TYPE_B6, - DECOMP_TYPE_B7, - DECOMP_TYPE_B8, - DECOMP_TYPE_B18, - DECOMP_TYPE_LS2, - DECOMP_TYPE_PAT3, - DECOMP_TYPE_S2_UL, - DECOMP_TYPE_LS2_UL, -} DecompTypeEnum; - -static uint32_t unicode_get_short_code(uint32_t c) -{ - static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; - - if (c < 0x80) - return c; - else if (c < 0x80 + 0x50) - return c - 0x80 + 0x300; - else - return unicode_short_table[c - 0x80 - 0x50]; -} - -static uint32_t unicode_get_lower_simple(uint32_t c) -{ - if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) - c += 0x20; - else - c++; - return c; -} - -static uint16_t unicode_get16(const uint8_t *p) -{ - return p[0] | (p[1] << 8); -} - -static int unicode_decomp_entry(uint32_t *res, uint32_t c, - int idx, uint32_t code, uint32_t len, - uint32_t type) -{ - uint32_t c1; - int l, i, p; - const uint8_t *d; - - if (type == DECOMP_TYPE_C1) { - res[0] = unicode_decomp_table2[idx]; - return 1; - } else { - d = unicode_decomp_data + unicode_decomp_table2[idx]; - switch(type) { - case DECOMP_TYPE_L1: - case DECOMP_TYPE_L2: - case DECOMP_TYPE_L3: - case DECOMP_TYPE_L4: - case DECOMP_TYPE_L5: - case DECOMP_TYPE_L6: - case DECOMP_TYPE_L7: - l = type - DECOMP_TYPE_L1 + 1; - d += (c - code) * l * 2; - for(i = 0; i < l; i++) { - if ((res[i] = unicode_get16(d + 2 * i)) == 0) - return 0; - } - return l; - case DECOMP_TYPE_LL1: - case DECOMP_TYPE_LL2: - { - uint32_t k, p; - l = type - DECOMP_TYPE_LL1 + 1; - k = (c - code) * l; - p = len * l * 2; - for(i = 0; i < l; i++) { - c1 = unicode_get16(d + 2 * k) | - (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); - if (!c1) - return 0; - res[i] = c1; - k++; - } - } - return l; - case DECOMP_TYPE_S1: - case DECOMP_TYPE_S2: - case DECOMP_TYPE_S3: - case DECOMP_TYPE_S4: - case DECOMP_TYPE_S5: - l = type - DECOMP_TYPE_S1 + 1; - d += (c - code) * l; - for(i = 0; i < l; i++) { - if ((res[i] = unicode_get_short_code(d[i])) == 0) - return 0; - } - return l; - case DECOMP_TYPE_I1: - l = 1; - p = 0; - goto decomp_type_i; - case DECOMP_TYPE_I2_0: - case DECOMP_TYPE_I2_1: - case DECOMP_TYPE_I3_1: - case DECOMP_TYPE_I3_2: - case DECOMP_TYPE_I4_1: - case DECOMP_TYPE_I4_2: - l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); - p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); - decomp_type_i: - for(i = 0; i < l; i++) { - c1 = unicode_get16(d + 2 * i); - if (i == p) - c1 += c - code; - res[i] = c1; - } - return l; - case DECOMP_TYPE_B18: - l = 18; - goto decomp_type_b; - case DECOMP_TYPE_B1: - case DECOMP_TYPE_B2: - case DECOMP_TYPE_B3: - case DECOMP_TYPE_B4: - case DECOMP_TYPE_B5: - case DECOMP_TYPE_B6: - case DECOMP_TYPE_B7: - case DECOMP_TYPE_B8: - l = type - DECOMP_TYPE_B1 + 1; - decomp_type_b: - { - uint32_t c_min; - c_min = unicode_get16(d); - d += 2 + (c - code) * l; - for(i = 0; i < l; i++) { - c1 = d[i]; - if (c1 == 0xff) - c1 = 0x20; - else - c1 += c_min; - res[i] = c1; - } - } - return l; - case DECOMP_TYPE_LS2: - d += (c - code) * 3; - if (!(res[0] = unicode_get16(d))) - return 0; - res[1] = unicode_get_short_code(d[2]); - return 2; - case DECOMP_TYPE_PAT3: - res[0] = unicode_get16(d); - res[2] = unicode_get16(d + 2); - d += 4 + (c - code) * 2; - res[1] = unicode_get16(d); - return 3; - case DECOMP_TYPE_S2_UL: - case DECOMP_TYPE_LS2_UL: - c1 = c - code; - if (type == DECOMP_TYPE_S2_UL) { - d += c1 & ~1; - c = unicode_get_short_code(*d); - d++; - } else { - d += (c1 >> 1) * 3; - c = unicode_get16(d); - d += 2; - } - if (c1 & 1) - c = unicode_get_lower_simple(c); - res[0] = c; - res[1] = unicode_get_short_code(*d); - return 2; - } - } - return 0; -} - - -/* return the length of the decomposition (length <= - UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ -static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) -{ - uint32_t v, type, is_compat, code, len; - int idx_min, idx_max, idx; - - idx_min = 0; - idx_max = countof(unicode_decomp_table1) - 1; - while (idx_min <= idx_max) { - idx = (idx_max + idx_min) / 2; - v = unicode_decomp_table1[idx]; - code = v >> (32 - 18); - len = (v >> (32 - 18 - 7)) & 0x7f; - // printf("idx=%d code=%05x len=%d\n", idx, code, len); - if (c < code) { - idx_max = idx - 1; - } else if (c >= code + len) { - idx_min = idx + 1; - } else { - is_compat = v & 1; - if (is_compat1 < is_compat) - break; - type = (v >> (32 - 18 - 7 - 6)) & 0x3f; - return unicode_decomp_entry(res, c, idx, code, len, type); - } - } - return 0; -} - -/* return 0 if no pair found */ -static int unicode_compose_pair(uint32_t c0, uint32_t c1) -{ - uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; - int idx_min, idx_max, idx, d; - uint32_t pair[2]; - - idx_min = 0; - idx_max = countof(unicode_comp_table) - 1; - while (idx_min <= idx_max) { - idx = (idx_max + idx_min) / 2; - idx1 = unicode_comp_table[idx]; - - /* idx1 represent an entry of the decomposition table */ - d_idx = idx1 >> 6; - d_offset = idx1 & 0x3f; - v = unicode_decomp_table1[d_idx]; - code = v >> (32 - 18); - len = (v >> (32 - 18 - 7)) & 0x7f; - type = (v >> (32 - 18 - 7 - 6)) & 0x3f; - ch = code + d_offset; - unicode_decomp_entry(pair, ch, d_idx, code, len, type); - d = c0 - pair[0]; - if (d == 0) - d = c1 - pair[1]; - if (d < 0) { - idx_max = idx - 1; - } else if (d > 0) { - idx_min = idx + 1; - } else { - return ch; - } - } - return 0; -} - -/* return the combining class of character c (between 0 and 255) */ -static int unicode_get_cc(uint32_t c) -{ - uint32_t code, n, type, cc, c1, b; - int pos; - const uint8_t *p; - - pos = get_index_pos(&code, c, - unicode_cc_index, sizeof(unicode_cc_index) / 3); - if (pos < 0) - return 0; - p = unicode_cc_table + pos; - /* 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; - n = b & 0x3f; - if (n < 48) { - } else if (n < 56) { - n = (n - 48) << 8; - n |= *p++; - n += 48; - } else { - n = (n - 56) << 8; - n |= *p++ << 8; - n |= *p++; - n += 48 + (1 << 11); - } - if (type <= 1) - p++; - c1 = code + n + 1; - if (c < c1) { - switch(type) { - case 0: - cc = p[-1]; - break; - case 1: - cc = p[-1] + c - code; - break; - case 2: - cc = 0; - break; - default: - case 3: - cc = 230; - break; - } - return cc; - } - code = c1; - } -} - -static void sort_cc(int *buf, int len) -{ - int i, j, k, cc, cc1, start, ch1; - - for(i = 0; i < len; i++) { - cc = unicode_get_cc(buf[i]); - if (cc != 0) { - start = i; - j = i + 1; - while (j < len) { - ch1 = buf[j]; - cc1 = unicode_get_cc(ch1); - if (cc1 == 0) - break; - k = j - 1; - while (k >= start) { - if (unicode_get_cc(buf[k]) <= cc1) - break; - buf[k + 1] = buf[k]; - k--; - } - buf[k + 1] = ch1; - j++; - } -#if 0 - printf("cc:"); - for(k = start; k < j; k++) { - printf(" %3d", unicode_get_cc(buf[k])); - } - printf("\n"); -#endif - i = j; - } - } -} - -static void to_nfd_rec(DynBuf *dbuf, - const int *src, int src_len, int is_compat) -{ - uint32_t c, v; - int i, l; - uint32_t res[UNICODE_DECOMP_LEN_MAX]; - - for(i = 0; i < src_len; i++) { - c = src[i]; - if (c >= 0xac00 && c < 0xd7a4) { - /* Hangul decomposition */ - c -= 0xac00; - dbuf_put_u32(dbuf, 0x1100 + c / 588); - dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); - v = c % 28; - if (v != 0) - dbuf_put_u32(dbuf, 0x11a7 + v); - } else { - l = unicode_decomp_char(res, c, is_compat); - if (l) { - to_nfd_rec(dbuf, (int *)res, l, is_compat); - } else { - dbuf_put_u32(dbuf, c); - } - } - } -} - -/* return 0 if not found */ -static int compose_pair(uint32_t c0, uint32_t c1) -{ - /* Hangul composition */ - if (c0 >= 0x1100 && c0 < 0x1100 + 19 && - c1 >= 0x1161 && c1 < 0x1161 + 21) { - return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; - } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && - (c0 - 0xac00) % 28 == 0 && - c1 >= 0x11a7 && c1 < 0x11a7 + 28) { - return c0 + c1 - 0x11a7; - } else { - return unicode_compose_pair(c0, c1); - } -} - -int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, - UnicodeNormalizationEnum n_type, - void *opaque, DynBufReallocFunc *realloc_func) -{ - int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; - BOOL is_compat; - DynBuf dbuf_s, *dbuf = &dbuf_s; - - is_compat = n_type >> 1; - - dbuf_init2(dbuf, opaque, realloc_func); - if (dbuf_realloc(dbuf, sizeof(int) * src_len)) - goto fail; - - /* common case: latin1 is unaffected by NFC */ - if (n_type == UNICODE_NFC) { - for(i = 0; i < src_len; i++) { - if (src[i] >= 0x100) - goto not_latin1; - } - buf = (int *)dbuf->buf; - memcpy(buf, src, src_len * sizeof(int)); - *pdst = (uint32_t *)buf; - return src_len; - not_latin1: ; - } - - to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); - if (dbuf_error(dbuf)) { - fail: - *pdst = NULL; - return -1; - } - buf = (int *)dbuf->buf; - buf_len = dbuf->size / sizeof(int); - - sort_cc(buf, buf_len); - - if (buf_len <= 1 || (n_type & 1) != 0) { - /* NFD / NFKD */ - *pdst = (uint32_t *)buf; - return buf_len; - } - - i = 1; - out_len = 1; - while (i < buf_len) { - /* find the starter character and test if it is blocked from - the character at 'i' */ - last_cc = unicode_get_cc(buf[i]); - starter_pos = out_len - 1; - while (starter_pos >= 0) { - cc = unicode_get_cc(buf[starter_pos]); - if (cc == 0) - break; - if (cc >= last_cc) - goto next; - last_cc = 256; - starter_pos--; - } - if (starter_pos >= 0 && - (p = compose_pair(buf[starter_pos], buf[i])) != 0) { - buf[starter_pos] = p; - i++; - } else { - next: - buf[out_len++] = buf[i++]; - } - } - *pdst = (uint32_t *)buf; - return out_len; -} - -/* char ranges for various unicode properties */ - -static int unicode_find_name(const char *name_table, const char *name) -{ - const char *p, *r; - int pos; - size_t name_len, len; - - p = name_table; - pos = 0; - name_len = strlen(name); - while (*p) { - for(;;) { - r = strchr(p, ','); - if (!r) - len = strlen(p); - else - len = r - p; - if (len == name_len && !memcmp(p, name, name_len)) - return pos; - p += len + 1; - if (!r) - break; - } - pos++; - } - return -1; -} - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_script(CharRange *cr, - const char *script_name, BOOL is_ext) -{ - int script_idx; - const uint8_t *p, *p_end; - uint32_t c, c1, b, n, v, v_len, i, type; - CharRange cr1_s, *cr1; - CharRange cr2_s, *cr2 = &cr2_s; - BOOL is_common; - - script_idx = unicode_find_name(unicode_script_name_table, script_name); - if (script_idx < 0) - return -2; - /* Note: we remove the "Unknown" Script */ - script_idx += UNICODE_SCRIPT_Unknown + 1; - - is_common = (script_idx == UNICODE_SCRIPT_Common || - script_idx == UNICODE_SCRIPT_Inherited); - if (is_ext) { - cr1 = &cr1_s; - cr_init(cr1, cr->mem_opaque, cr->realloc_func); - cr_init(cr2, cr->mem_opaque, cr->realloc_func); - } else { - cr1 = cr; - } - - p = unicode_script_table; - p_end = unicode_script_table + countof(unicode_script_table); - c = 0; - while (p < p_end) { - b = *p++; - type = b >> 7; - n = b & 0x7f; - if (n < 96) { - } else if (n < 112) { - n = (n - 96) << 8; - n |= *p++; - n += 96; - } else { - n = (n - 112) << 16; - n |= *p++ << 8; - n |= *p++; - n += 96 + (1 << 12); - } - if (type == 0) - v = 0; - else - v = *p++; - c1 = c + n + 1; - if (v == script_idx) { - if (cr_add_interval(cr1, c, c1)) - goto fail; - } - c = c1; - } - - if (is_ext) { - /* add the script extensions */ - p = unicode_script_ext_table; - p_end = unicode_script_ext_table + countof(unicode_script_ext_table); - c = 0; - while (p < p_end) { - b = *p++; - if (b < 128) { - n = b; - } else if (b < 128 + 64) { - n = (b - 128) << 8; - n |= *p++; - n += 128; - } else { - n = (b - 128 - 64) << 16; - n |= *p++ << 8; - n |= *p++; - n += 128 + (1 << 14); - } - c1 = c + n + 1; - v_len = *p++; - if (is_common) { - if (v_len != 0) { - if (cr_add_interval(cr2, c, c1)) - goto fail; - } - } else { - for(i = 0; i < v_len; i++) { - if (p[i] == script_idx) { - if (cr_add_interval(cr2, c, c1)) - goto fail; - break; - } - } - } - p += v_len; - c = c1; - } - if (is_common) { - /* remove all the characters with script extensions */ - if (cr_invert(cr2)) - goto fail; - if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, - CR_OP_INTER)) - goto fail; - } else { - if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, - CR_OP_UNION)) - goto fail; - } - cr_free(cr1); - cr_free(cr2); - } - return 0; - fail: - if (is_ext) { - cr_free(cr1); - cr_free(cr2); - } - goto fail; -} - -#define M(id) (1U << UNICODE_GC_ ## id) - -static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) -{ - const uint8_t *p, *p_end; - uint32_t c, c0, b, n, v; - - p = unicode_gc_table; - p_end = unicode_gc_table + countof(unicode_gc_table); - c = 0; - /* 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; - v = b & 0x1f; - if (n == 7) { - n = *p++; - if (n < 128) { - n += 7; - } else if (n < 128 + 64) { - n = (n - 128) << 8; - n |= *p++; - n += 7 + 128; - } else { - n = (n - 128 - 64) << 16; - n |= *p++ << 8; - n |= *p++; - n += 7 + 128 + (1 << 14); - } - } - c0 = c; - c += n + 1; - if (v == 31) { - /* run of Lu / Ll */ - b = gc_mask & (M(Lu) | M(Ll)); - if (b != 0) { - if (b == (M(Lu) | M(Ll))) { - goto add_range; - } else { - c0 += ((gc_mask & M(Ll)) != 0); - for(; c0 < c; c0 += 2) { - if (cr_add_interval(cr, c0, c0 + 1)) - return -1; - } - } - } - } else if ((gc_mask >> v) & 1) { - add_range: - if (cr_add_interval(cr, c0, c)) - return -1; - } - } - return 0; -} - -static int unicode_prop1(CharRange *cr, int prop_idx) -{ - const uint8_t *p, *p_end; - uint32_t c, c0, b, bit; - - p = unicode_prop_table[prop_idx]; - p_end = p + unicode_prop_len_table[prop_idx]; - c = 0; - bit = 0; - /* 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++; - if (b < 64) { - c += (b >> 3) + 1; - if (bit) { - if (cr_add_interval(cr, c0, c)) - return -1; - } - bit ^= 1; - c0 = c; - c += (b & 7) + 1; - } else if (b >= 0x80) { - c += b - 0x80 + 1; - } else if (b < 0x60) { - c += (((b - 0x40) << 8) | p[0]) + 1; - p++; - } else { - c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; - p += 2; - } - if (bit) { - if (cr_add_interval(cr, c0, c)) - return -1; - } - bit ^= 1; - } - return 0; -} - -#define CASE_U (1 << 0) -#define CASE_L (1 << 1) -#define CASE_F (1 << 2) - -/* use the case conversion table to generate range of characters. - CASE_U: set char if modified by uppercasing, - CASE_L: set char if modified by lowercasing, - CASE_F: set char if modified by case folding, - */ -static int unicode_case1(CharRange *cr, int case_mask) -{ -#define MR(x) (1 << RUN_TYPE_ ## x) - const uint32_t tab_run_mask[3] = { - MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | - MR(UF_D1_EXT) | MR(U_EXT) | MR(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; -} - -typedef enum { - POP_GC, - POP_PROP, - POP_CASE, - POP_UNION, - POP_INTER, - POP_XOR, - POP_INVERT, - POP_END, -} PropOPEnum; - -#define POP_STACK_LEN_MAX 4 - -static int unicode_prop_ops(CharRange *cr, ...) -{ - va_list ap; - CharRange stack[POP_STACK_LEN_MAX]; - int stack_len, op, ret, i; - uint32_t a; - - va_start(ap, cr); - stack_len = 0; - for(;;) { - op = va_arg(ap, int); - switch(op) { - case POP_GC: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_general_category1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_PROP: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_prop1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_CASE: - assert(stack_len < POP_STACK_LEN_MAX); - a = va_arg(ap, int); - cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); - if (unicode_case1(&stack[stack_len - 1], a)) - goto fail; - break; - case POP_UNION: - case POP_INTER: - case POP_XOR: - { - CharRange *cr1, *cr2, *cr3; - assert(stack_len >= 2); - assert(stack_len < POP_STACK_LEN_MAX); - cr1 = &stack[stack_len - 2]; - cr2 = &stack[stack_len - 1]; - cr3 = &stack[stack_len++]; - cr_init(cr3, cr->mem_opaque, cr->realloc_func); - if (cr_op(cr3, cr1->points, cr1->len, - cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) - goto fail; - cr_free(cr1); - cr_free(cr2); - *cr1 = *cr3; - stack_len -= 2; - } - break; - case POP_INVERT: - assert(stack_len >= 1); - if (cr_invert(&stack[stack_len - 1])) - goto fail; - break; - case POP_END: - goto done; - default: - abort(); - } - } - done: - assert(stack_len == 1); - ret = cr_copy(cr, &stack[0]); - cr_free(&stack[0]); - return ret; - fail: - for(i = 0; i < stack_len; i++) - cr_free(&stack[i]); - return -1; -} - -static const uint32_t unicode_gc_mask_table[] = { - M(Lu) | M(Ll) | M(Lt), /* LC */ - M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ - M(Mn) | M(Mc) | M(Me), /* M */ - M(Nd) | M(Nl) | M(No), /* N */ - M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ - M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ - M(Zs) | M(Zl) | M(Zp), /* Z */ - M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ -}; - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_general_category(CharRange *cr, const char *gc_name) -{ - int gc_idx; - uint32_t gc_mask; - - gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); - if (gc_idx < 0) - return -2; - if (gc_idx <= UNICODE_GC_Co) { - gc_mask = (uint64_t)1 << gc_idx; - } else { - gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; - } - return unicode_general_category1(cr, gc_mask); -} - - -/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 - if not found */ -int unicode_prop(CharRange *cr, const char *prop_name) -{ - int prop_idx, ret; - - prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); - if (prop_idx < 0) - return -2; - prop_idx += UNICODE_PROP_ASCII_Hex_Digit; - - ret = 0; - switch(prop_idx) { - case UNICODE_PROP_ASCII: - if (cr_add_interval(cr, 0x00, 0x7f + 1)) - return -1; - break; - case UNICODE_PROP_Any: - if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) - return -1; - break; - case UNICODE_PROP_Assigned: - ret = unicode_prop_ops(cr, - POP_GC, M(Cn), - POP_INVERT, - POP_END); - break; - case UNICODE_PROP_Math: - ret = unicode_prop_ops(cr, - POP_GC, M(Sm), - POP_PROP, UNICODE_PROP_Other_Math, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Lowercase: - ret = unicode_prop_ops(cr, - POP_GC, M(Ll), - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Uppercase: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Cased: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Alphabetic: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_Uppercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Lowercase, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_Alphabetic, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_Grapheme_Base: - ret = unicode_prop_ops(cr, - POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), - POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, - POP_UNION, - POP_INVERT, - POP_END); - break; - case UNICODE_PROP_Grapheme_Extend: - ret = unicode_prop_ops(cr, - POP_GC, M(Me) | M(Mn), - POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, - POP_UNION, - POP_END); - break; - case UNICODE_PROP_XID_Start: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_PROP, UNICODE_PROP_XID_Start1, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_XID_Continue: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | - M(Mn) | M(Mc) | M(Nd) | M(Pc), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_ID_Continue, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_PROP, UNICODE_PROP_XID_Continue1, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_Changes_When_Uppercased: - ret = unicode_case1(cr, CASE_U); - break; - case UNICODE_PROP_Changes_When_Lowercased: - ret = unicode_case1(cr, CASE_L); - break; - case UNICODE_PROP_Changes_When_Casemapped: - ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); - break; - case UNICODE_PROP_Changes_When_Titlecased: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_U, - POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, - POP_XOR, - POP_END); - break; - case UNICODE_PROP_Changes_When_Casefolded: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_F, - POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, - POP_XOR, - POP_END); - break; - case UNICODE_PROP_Changes_When_NFKC_Casefolded: - ret = unicode_prop_ops(cr, - POP_CASE, CASE_F, - POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, - POP_XOR, - POP_END); - break; -#if 0 - case UNICODE_PROP_ID_Start: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_ID_Continue: - ret = unicode_prop_ops(cr, - POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | - M(Mn) | M(Mc) | M(Nd) | M(Pc), - POP_PROP, UNICODE_PROP_Other_ID_Start, - POP_UNION, - POP_PROP, UNICODE_PROP_Other_ID_Continue, - POP_UNION, - POP_PROP, UNICODE_PROP_Pattern_Syntax, - POP_PROP, UNICODE_PROP_Pattern_White_Space, - POP_UNION, - POP_INVERT, - POP_INTER, - POP_END); - break; - case UNICODE_PROP_Case_Ignorable: - ret = unicode_prop_ops(cr, - POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk), - POP_PROP, UNICODE_PROP_Case_Ignorable1, - POP_XOR, - POP_END); - break; -#else - /* we use the existing tables */ - case UNICODE_PROP_ID_Continue: - ret = unicode_prop_ops(cr, - POP_PROP, UNICODE_PROP_ID_Start, - POP_PROP, UNICODE_PROP_ID_Continue1, - POP_XOR, - POP_END); - break; -#endif - default: - if (prop_idx >= countof(unicode_prop_table)) - return -2; - ret = unicode_prop1(cr, prop_idx); - break; - } - return ret; -} - -#endif /* CONFIG_ALL_UNICODE */ - -/*---- 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; -} diff --git a/lib/quickjs/libunicode.h b/lib/quickjs/libunicode.h deleted file mode 100644 index 999ab583..00000000 --- a/lib/quickjs/libunicode.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Unicode utilities - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIBUNICODE_H -#define LIBUNICODE_H - -#include <stdint.h> - -/* define it to include all the unicode tables (40KB larger) */ -#define CONFIG_ALL_UNICODE - -#define LRE_CC_RES_LEN_MAX 3 - -/* char ranges */ - -typedef struct { - int len; /* in points, always even */ - int size; - uint32_t *points; /* points sorted by increasing value */ - void *mem_opaque; - void *(*realloc_func)(void *opaque, void *ptr, size_t size); -} CharRange; - -typedef enum { - CR_OP_UNION, - CR_OP_INTER, - CR_OP_XOR, -} CharRangeOpEnum; - -void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); -void cr_free(CharRange *cr); -int cr_realloc(CharRange *cr, int size); -int cr_copy(CharRange *cr, const CharRange *cr1); - -static inline int cr_add_point(CharRange *cr, uint32_t v) -{ - if (cr->len >= cr->size) { - if (cr_realloc(cr, cr->len + 1)) - return -1; - } - cr->points[cr->len++] = v; - return 0; -} - -static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) -{ - if ((cr->len + 2) > cr->size) { - if (cr_realloc(cr, cr->len + 2)) - return -1; - } - cr->points[cr->len++] = c1; - cr->points[cr->len++] = c2; - return 0; -} - -int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); - -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); -} - -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); - -int cr_regexp_canonicalize(CharRange *cr, int is_unicode); - -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, - void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); - -/* Unicode character range functions */ - -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); - -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 - } -} - -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 1; -#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/lib/quickjs/list.h b/lib/quickjs/list.h deleted file mode 100644 index 80983111..00000000 --- a/lib/quickjs/list.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Linux klist like system - * - * Copyright (c) 2016-2017 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIST_H -#define LIST_H - -#ifndef NULL -#include <stddef.h> -#endif - -struct list_head { - struct list_head *prev; - struct list_head *next; -}; - -#define LIST_HEAD_INIT(el) { &(el), &(el) } - -/* return the pointer of type 'type *' containing 'el' as field 'member' */ -#define list_entry(el, type, member) container_of(el, type, member) - -static inline void init_list_head(struct list_head *head) -{ - head->prev = head; - head->next = head; -} - -/* insert 'el' between 'prev' and 'next' */ -static inline void __list_add(struct list_head *el, - struct list_head *prev, struct list_head *next) -{ - prev->next = el; - el->prev = prev; - el->next = next; - next->prev = el; -} - -/* add 'el' at the head of the list 'head' (= after element head) */ -static inline void list_add(struct list_head *el, struct list_head *head) -{ - __list_add(el, head, head->next); -} - -/* add 'el' at the end of the list 'head' (= before element head) */ -static inline void list_add_tail(struct list_head *el, struct list_head *head) -{ - __list_add(el, head->prev, head); -} - -static inline void list_del(struct list_head *el) -{ - struct list_head *prev, *next; - prev = el->prev; - next = el->next; - prev->next = next; - next->prev = prev; - el->prev = NULL; /* fail safe */ - el->next = NULL; /* fail safe */ -} - -static inline int list_empty(struct list_head *el) -{ - return el->next == el; -} - -#define list_for_each(el, head) \ - for(el = (head)->next; el != (head); el = el->next) - -#define list_for_each_safe(el, el1, head) \ - for(el = (head)->next, el1 = el->next; el != (head); \ - el = el1, el1 = el->next) - -#define list_for_each_prev(el, head) \ - for(el = (head)->prev; el != (head); el = el->prev) - -#define list_for_each_prev_safe(el, el1, head) \ - for(el = (head)->prev, el1 = el->prev; el != (head); \ - el = el1, el1 = el->prev) - -#endif /* LIST_H */ diff --git a/lib/quickjs/quickjs-atom.h b/lib/quickjs/quickjs-atom.h deleted file mode 100644 index f4d5838d..00000000 --- a/lib/quickjs/quickjs-atom.h +++ /dev/null @@ -1,273 +0,0 @@ -/* - * QuickJS atom definitions - * - * Copyright (c) 2017-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef DEF - -/* Note: first atoms are considered as keywords in the parser */ -DEF(null, "null") /* must be first */ -DEF(false, "false") -DEF(true, "true") -DEF(if, "if") -DEF(else, "else") -DEF(return, "return") -DEF(var, "var") -DEF(this, "this") -DEF(delete, "delete") -DEF(void, "void") -DEF(typeof, "typeof") -DEF(new, "new") -DEF(in, "in") -DEF(instanceof, "instanceof") -DEF(do, "do") -DEF(while, "while") -DEF(for, "for") -DEF(break, "break") -DEF(continue, "continue") -DEF(switch, "switch") -DEF(case, "case") -DEF(default, "default") -DEF(throw, "throw") -DEF(try, "try") -DEF(catch, "catch") -DEF(finally, "finally") -DEF(function, "function") -DEF(debugger, "debugger") -DEF(with, "with") -/* FutureReservedWord */ -DEF(class, "class") -DEF(const, "const") -DEF(enum, "enum") -DEF(export, "export") -DEF(extends, "extends") -DEF(import, "import") -DEF(super, "super") -/* FutureReservedWords when parsing strict mode code */ -DEF(implements, "implements") -DEF(interface, "interface") -DEF(let, "let") -DEF(package, "package") -DEF(private, "private") -DEF(protected, "protected") -DEF(public, "public") -DEF(static, "static") -DEF(yield, "yield") -DEF(await, "await") - -/* empty string */ -DEF(empty_string, "") -/* identifiers */ -DEF(length, "length") -DEF(fileName, "fileName") -DEF(lineNumber, "lineNumber") -DEF(message, "message") -DEF(cause, "cause") -DEF(errors, "errors") -DEF(stack, "stack") -DEF(name, "name") -DEF(toString, "toString") -DEF(toLocaleString, "toLocaleString") -DEF(valueOf, "valueOf") -DEF(eval, "eval") -DEF(prototype, "prototype") -DEF(constructor, "constructor") -DEF(configurable, "configurable") -DEF(writable, "writable") -DEF(enumerable, "enumerable") -DEF(value, "value") -DEF(get, "get") -DEF(set, "set") -DEF(of, "of") -DEF(__proto__, "__proto__") -DEF(undefined, "undefined") -DEF(number, "number") -DEF(boolean, "boolean") -DEF(string, "string") -DEF(object, "object") -DEF(symbol, "symbol") -DEF(integer, "integer") -DEF(unknown, "unknown") -DEF(arguments, "arguments") -DEF(callee, "callee") -DEF(caller, "caller") -DEF(_eval_, "<eval>") -DEF(_ret_, "<ret>") -DEF(_var_, "<var>") -DEF(_arg_var_, "<arg_var>") -DEF(_with_, "<with>") -DEF(lastIndex, "lastIndex") -DEF(target, "target") -DEF(index, "index") -DEF(input, "input") -DEF(defineProperties, "defineProperties") -DEF(apply, "apply") -DEF(join, "join") -DEF(concat, "concat") -DEF(split, "split") -DEF(construct, "construct") -DEF(getPrototypeOf, "getPrototypeOf") -DEF(setPrototypeOf, "setPrototypeOf") -DEF(isExtensible, "isExtensible") -DEF(preventExtensions, "preventExtensions") -DEF(has, "has") -DEF(deleteProperty, "deleteProperty") -DEF(defineProperty, "defineProperty") -DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") -DEF(ownKeys, "ownKeys") -DEF(add, "add") -DEF(done, "done") -DEF(next, "next") -DEF(values, "values") -DEF(source, "source") -DEF(flags, "flags") -DEF(global, "global") -DEF(unicode, "unicode") -DEF(raw, "raw") -DEF(new_target, "new.target") -DEF(this_active_func, "this.active_func") -DEF(home_object, "<home_object>") -DEF(computed_field, "<computed_field>") -DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */ -DEF(class_fields_init, "<class_fields_init>") -DEF(brand, "<brand>") -DEF(hash_constructor, "#constructor") -DEF(as, "as") -DEF(from, "from") -DEF(meta, "meta") -DEF(_default_, "*default*") -DEF(_star_, "*") -DEF(Module, "Module") -DEF(then, "then") -DEF(resolve, "resolve") -DEF(reject, "reject") -DEF(promise, "promise") -DEF(proxy, "proxy") -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 -/* the following 3 atoms are only used with CONFIG_ATOMICS */ -DEF(not_equal, "not-equal") -DEF(timed_out, "timed-out") -DEF(ok, "ok") -/* */ -DEF(toJSON, "toJSON") -/* class names */ -DEF(Object, "Object") -DEF(Array, "Array") -DEF(Error, "Error") -DEF(Number, "Number") -DEF(String, "String") -DEF(Boolean, "Boolean") -DEF(Symbol, "Symbol") -DEF(Arguments, "Arguments") -DEF(Math, "Math") -DEF(JSON, "JSON") -DEF(Date, "Date") -DEF(Function, "Function") -DEF(GeneratorFunction, "GeneratorFunction") -DEF(ForInIterator, "ForInIterator") -DEF(RegExp, "RegExp") -DEF(ArrayBuffer, "ArrayBuffer") -DEF(SharedArrayBuffer, "SharedArrayBuffer") -/* must keep same order as class IDs for typed arrays */ -DEF(Uint8ClampedArray, "Uint8ClampedArray") -DEF(Int8Array, "Int8Array") -DEF(Uint8Array, "Uint8Array") -DEF(Int16Array, "Int16Array") -DEF(Uint16Array, "Uint16Array") -DEF(Int32Array, "Int32Array") -DEF(Uint32Array, "Uint32Array") -DEF(BigInt64Array, "BigInt64Array") -DEF(BigUint64Array, "BigUint64Array") -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(Map, "Map") -DEF(Set, "Set") /* Map + 1 */ -DEF(WeakMap, "WeakMap") /* Map + 2 */ -DEF(WeakSet, "WeakSet") /* Map + 3 */ -DEF(Map_Iterator, "Map Iterator") -DEF(Set_Iterator, "Set Iterator") -DEF(Array_Iterator, "Array Iterator") -DEF(String_Iterator, "String Iterator") -DEF(RegExp_String_Iterator, "RegExp String Iterator") -DEF(Generator, "Generator") -DEF(Proxy, "Proxy") -DEF(Promise, "Promise") -DEF(PromiseResolveFunction, "PromiseResolveFunction") -DEF(PromiseRejectFunction, "PromiseRejectFunction") -DEF(AsyncFunction, "AsyncFunction") -DEF(AsyncFunctionResolve, "AsyncFunctionResolve") -DEF(AsyncFunctionReject, "AsyncFunctionReject") -DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") -DEF(AsyncGenerator, "AsyncGenerator") -DEF(EvalError, "EvalError") -DEF(RangeError, "RangeError") -DEF(ReferenceError, "ReferenceError") -DEF(SyntaxError, "SyntaxError") -DEF(TypeError, "TypeError") -DEF(URIError, "URIError") -DEF(InternalError, "InternalError") -/* private symbols */ -DEF(Private_brand, "<brand>") -/* symbols */ -DEF(Symbol_toPrimitive, "Symbol.toPrimitive") -DEF(Symbol_iterator, "Symbol.iterator") -DEF(Symbol_match, "Symbol.match") -DEF(Symbol_matchAll, "Symbol.matchAll") -DEF(Symbol_replace, "Symbol.replace") -DEF(Symbol_search, "Symbol.search") -DEF(Symbol_split, "Symbol.split") -DEF(Symbol_toStringTag, "Symbol.toStringTag") -DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") -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/lib/quickjs/quickjs-opcode.h b/lib/quickjs/quickjs-opcode.h deleted file mode 100644 index 1e182125..00000000 --- a/lib/quickjs/quickjs-opcode.h +++ /dev/null @@ -1,372 +0,0 @@ -/* - * QuickJS opcode definitions - * - * Copyright (c) 2017-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef FMT -FMT(none) -FMT(none_int) -FMT(none_loc) -FMT(none_arg) -FMT(none_var_ref) -FMT(u8) -FMT(i8) -FMT(loc8) -FMT(const8) -FMT(label8) -FMT(u16) -FMT(i16) -FMT(label16) -FMT(npop) -FMT(npopx) -FMT(npop_u16) -FMT(loc) -FMT(arg) -FMT(var_ref) -FMT(u32) -FMT(i32) -FMT(const) -FMT(label) -FMT(atom) -FMT(atom_u8) -FMT(atom_u16) -FMT(atom_label_u8) -FMT(atom_label_u16) -FMT(label_u16) -#undef FMT -#endif /* FMT */ - -#ifdef DEF - -#ifndef def -#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) -#endif - -DEF(invalid, 1, 0, 0, none) /* never emitted */ - -/* push values */ -DEF( push_i32, 5, 0, 1, i32) -DEF( push_const, 5, 0, 1, const) -DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ -DEF(push_atom_value, 5, 0, 1, atom) -DEF( private_symbol, 5, 0, 1, atom) -DEF( undefined, 1, 0, 1, none) -DEF( null, 1, 0, 1, none) -DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ -DEF( push_false, 1, 0, 1, none) -DEF( push_true, 1, 0, 1, none) -DEF( object, 1, 0, 1, none) -DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ -DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ - -DEF( drop, 1, 1, 0, none) /* a -> */ -DEF( nip, 1, 2, 1, none) /* a b -> b */ -DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ -DEF( dup, 1, 1, 2, none) /* a -> a a */ -DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ -DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ -DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ -DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ -DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ -DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ -DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ -DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ -DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ -DEF( swap, 1, 2, 2, none) /* a b -> b a */ -DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ -DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ -DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ -DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ -DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ - -DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ -DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ -DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ -DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ -DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ -DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ -DEF( apply, 3, 3, 1, u16) -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( 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) -DEF( throw, 1, 1, 0, none) -DEF( throw_error, 6, 0, 0, atom_u8) -DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ -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( 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 */ -DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ -DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ -DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ -DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ - -DEF( get_ref_value, 1, 2, 3, none) -DEF( put_ref_value, 1, 3, 0, none) - -DEF( define_var, 6, 0, 0, atom_u8) -DEF(check_define_var, 6, 0, 0, atom_u8) -DEF( define_func, 6, 1, 0, atom_u8) -DEF( get_field, 5, 1, 1, atom) -DEF( get_field2, 5, 1, 2, atom) -DEF( put_field, 5, 2, 0, atom) -DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ -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( 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 -> */ -DEF( define_field, 5, 2, 1, atom) -DEF( set_name, 5, 1, 1, atom) -DEF(set_name_computed, 1, 2, 2, none) -DEF( set_proto, 1, 2, 1, none) -DEF(set_home_object, 1, 2, 2, none) -DEF(define_array_el, 1, 3, 2, none) -DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ -DEF(copy_data_properties, 2, 3, 3, u8) -DEF( define_method, 6, 2, 1, atom_u8) -DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ -DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ -DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ - -DEF( get_loc, 3, 0, 1, loc) -DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ -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( 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_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) -DEF( if_false, 5, 1, 0, label) -DEF( if_true, 5, 1, 0, label) /* must come after if_false */ -DEF( goto, 5, 0, 0, label) /* must come after if_true */ -DEF( catch, 5, 0, 1, label) -DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ -DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ -DEF( 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) -DEF(make_var_ref_ref, 7, 0, 2, atom_u16) -DEF( make_var_ref, 5, 0, 2, atom) - -DEF( for_in_start, 1, 1, 1, none) -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(iterator_check_object, 1, 1, 1, none) -DEF(iterator_get_value_done, 1, 1, 2, none) -DEF( iterator_close, 1, 3, 0, none) -DEF( iterator_next, 1, 4, 4, none) -DEF( iterator_call, 2, 4, 5, u8) -DEF( initial_yield, 1, 0, 0, none) -DEF( yield, 1, 1, 2, none) -DEF( yield_star, 1, 1, 2, none) -DEF(async_yield_star, 1, 1, 2, none) -DEF( await, 1, 1, 1, none) - -/* arithmetic/logic operations */ -DEF( neg, 1, 1, 1, none) -DEF( plus, 1, 1, 1, none) -DEF( dec, 1, 1, 1, none) -DEF( inc, 1, 1, 1, none) -DEF( post_dec, 1, 1, 2, none) -DEF( post_inc, 1, 1, 2, none) -DEF( dec_loc, 2, 0, 0, loc8) -DEF( inc_loc, 2, 0, 0, loc8) -DEF( add_loc, 2, 1, 0, loc8) -DEF( not, 1, 1, 1, none) -DEF( lnot, 1, 1, 1, none) -DEF( typeof, 1, 1, 1, none) -DEF( delete, 1, 2, 1, none) -DEF( delete_var, 5, 0, 1, atom) - -DEF( mul, 1, 2, 1, none) -DEF( div, 1, 2, 1, none) -DEF( mod, 1, 2, 1, none) -DEF( add, 1, 2, 1, none) -DEF( sub, 1, 2, 1, none) -DEF( pow, 1, 2, 1, none) -DEF( shl, 1, 2, 1, none) -DEF( sar, 1, 2, 1, none) -DEF( shr, 1, 2, 1, none) -DEF( lt, 1, 2, 1, none) -DEF( lte, 1, 2, 1, none) -DEF( gt, 1, 2, 1, none) -DEF( gte, 1, 2, 1, none) -DEF( instanceof, 1, 2, 1, none) -DEF( in, 1, 2, 1, none) -DEF( eq, 1, 2, 1, none) -DEF( neq, 1, 2, 1, none) -DEF( strict_eq, 1, 2, 1, none) -DEF( strict_neq, 1, 2, 1, none) -DEF( and, 1, 2, 1, none) -DEF( xor, 1, 2, 1, none) -DEF( or, 1, 2, 1, none) -DEF(is_undefined_or_null, 1, 1, 1, none) -DEF( private_in, 1, 2, 1, none) -#ifdef CONFIG_BIGNUM -DEF( mul_pow10, 1, 2, 1, none) -DEF( math_mod, 1, 2, 1, none) -#endif -/* must be the last non short and non temporary opcode */ -DEF( nop, 1, 0, 0, none) - -/* temporary opcodes: never emitted in the final bytecode */ - -def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ -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 */ -def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ -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 -DEF( push_minus1, 1, 0, 1, none_int) -DEF( push_0, 1, 0, 1, none_int) -DEF( push_1, 1, 0, 1, none_int) -DEF( push_2, 1, 0, 1, none_int) -DEF( push_3, 1, 0, 1, none_int) -DEF( push_4, 1, 0, 1, none_int) -DEF( push_5, 1, 0, 1, none_int) -DEF( push_6, 1, 0, 1, none_int) -DEF( push_7, 1, 0, 1, none_int) -DEF( push_i8, 2, 0, 1, i8) -DEF( push_i16, 3, 0, 1, i16) -DEF( push_const8, 2, 0, 1, const8) -DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ -DEF(push_empty_string, 1, 0, 1, none) - -DEF( get_loc8, 2, 0, 1, loc8) -DEF( put_loc8, 2, 1, 0, loc8) -DEF( set_loc8, 2, 1, 1, loc8) - -DEF( get_loc0, 1, 0, 1, none_loc) -DEF( get_loc1, 1, 0, 1, none_loc) -DEF( get_loc2, 1, 0, 1, none_loc) -DEF( get_loc3, 1, 0, 1, none_loc) -DEF( put_loc0, 1, 1, 0, none_loc) -DEF( put_loc1, 1, 1, 0, none_loc) -DEF( put_loc2, 1, 1, 0, none_loc) -DEF( put_loc3, 1, 1, 0, none_loc) -DEF( set_loc0, 1, 1, 1, none_loc) -DEF( set_loc1, 1, 1, 1, none_loc) -DEF( set_loc2, 1, 1, 1, none_loc) -DEF( set_loc3, 1, 1, 1, none_loc) -DEF( get_arg0, 1, 0, 1, none_arg) -DEF( get_arg1, 1, 0, 1, none_arg) -DEF( get_arg2, 1, 0, 1, none_arg) -DEF( get_arg3, 1, 0, 1, none_arg) -DEF( put_arg0, 1, 1, 0, none_arg) -DEF( put_arg1, 1, 1, 0, none_arg) -DEF( put_arg2, 1, 1, 0, none_arg) -DEF( put_arg3, 1, 1, 0, none_arg) -DEF( set_arg0, 1, 1, 1, none_arg) -DEF( set_arg1, 1, 1, 1, none_arg) -DEF( set_arg2, 1, 1, 1, none_arg) -DEF( set_arg3, 1, 1, 1, none_arg) -DEF( get_var_ref0, 1, 0, 1, none_var_ref) -DEF( get_var_ref1, 1, 0, 1, none_var_ref) -DEF( get_var_ref2, 1, 0, 1, none_var_ref) -DEF( get_var_ref3, 1, 0, 1, none_var_ref) -DEF( put_var_ref0, 1, 1, 0, none_var_ref) -DEF( put_var_ref1, 1, 1, 0, none_var_ref) -DEF( put_var_ref2, 1, 1, 0, none_var_ref) -DEF( put_var_ref3, 1, 1, 0, none_var_ref) -DEF( set_var_ref0, 1, 1, 1, none_var_ref) -DEF( set_var_ref1, 1, 1, 1, none_var_ref) -DEF( set_var_ref2, 1, 1, 1, none_var_ref) -DEF( set_var_ref3, 1, 1, 1, none_var_ref) - -DEF( get_length, 1, 1, 1, none) - -DEF( if_false8, 2, 1, 0, label8) -DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ -DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ -DEF( goto16, 3, 0, 0, label16) - -DEF( call0, 1, 1, 1, npopx) -DEF( call1, 1, 1, 1, npopx) -DEF( call2, 1, 1, 1, npopx) -DEF( call3, 1, 1, 1, npopx) - -DEF( is_undefined, 1, 1, 1, none) -DEF( is_null, 1, 1, 1, none) -DEF(typeof_is_undefined, 1, 1, 1, none) -DEF( typeof_is_function, 1, 1, 1, none) -#endif - -#undef DEF -#undef def -#endif /* DEF */ diff --git a/lib/quickjs/quickjs.c b/lib/quickjs/quickjs.c deleted file mode 100644 index 427b36df..00000000 --- a/lib/quickjs/quickjs.c +++ /dev/null @@ -1,56105 +0,0 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <inttypes.h> -#include <string.h> -#include <assert.h> -#include <sys/time.h> -#include <time.h> -#include <fenv.h> -#include <math.h> -#if defined(__APPLE__) -#include <malloc/malloc.h> -#elif defined(__linux__) -#include <malloc.h> -#elif defined(__FreeBSD__) -#include <malloc_np.h> -#endif - -#include "cutils.h" -#include "list.h" -#include "quickjs.h" -#include "libregexp.h" -#include "libunicode.h" -#include "libbf.h" - -#define OPTIMIZE 1 -#define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) -#define DIRECT_DISPATCH 0 -#else -#define DIRECT_DISPATCH 1 -#endif - -#if defined(__APPLE__) -#define MALLOC_OVERHEAD 0 -#else -#define MALLOC_OVERHEAD 8 -#endif - -#if !defined(_WIN32) -/* define it if printf uses the RNDN rounding mode instead of RNDNA */ -#define CONFIG_PRINTF_RNDN -#endif - -/* define to include Atomics.* operations which depend on the OS - threads */ -#if !defined(EMSCRIPTEN) -#define CONFIG_ATOMICS -#endif - -#if !defined(EMSCRIPTEN) -/* enable stack limitation */ -#define CONFIG_STACK_CHECK -#endif - - -/* dump object free */ -//#define DUMP_FREE -//#define DUMP_CLOSURE -/* dump the bytecode of the compiled functions: combination of bits - 1: dump pass 3 final byte code - 2: dump pass 2 code - 4: dump pass 1 code - 8: dump stdlib functions - 16: dump bytecode in hex - 32: dump line number table - 64: dump compute_stack_size - */ -//#define DUMP_BYTECODE (1) -/* dump the occurence of the automatic GC */ -//#define DUMP_GC -/* dump objects freed by the garbage collector */ -//#define DUMP_GC_FREE -/* dump objects leaking when freeing the runtime */ -//#define DUMP_LEAKS 1 -/* dump memory usage before running the garbage collector */ -//#define DUMP_MEM -//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ -//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ -//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ -//#define DUMP_MODULE_RESOLVE -//#define DUMP_PROMISE -//#define DUMP_READ_OBJECT - -/* test the GC by forcing it before each object allocation */ -//#define FORCE_GC_AT_MALLOC - -#ifdef CONFIG_ATOMICS -#include <pthread.h> -#include <stdatomic.h> -#include <errno.h> -#endif - -enum { - /* classid tag */ /* union usage | properties */ - JS_CLASS_OBJECT = 1, /* must be first */ - JS_CLASS_ARRAY, /* u.array | length */ - JS_CLASS_ERROR, - JS_CLASS_NUMBER, /* u.object_data */ - JS_CLASS_STRING, /* u.object_data */ - JS_CLASS_BOOLEAN, /* u.object_data */ - JS_CLASS_SYMBOL, /* u.object_data */ - JS_CLASS_ARGUMENTS, /* u.array | length */ - JS_CLASS_MAPPED_ARGUMENTS, /* | length */ - JS_CLASS_DATE, /* u.object_data */ - JS_CLASS_MODULE_NS, - JS_CLASS_C_FUNCTION, /* u.cfunc */ - JS_CLASS_BYTECODE_FUNCTION, /* u.func */ - JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ - JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ - JS_CLASS_GENERATOR_FUNCTION, /* u.func */ - JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ - JS_CLASS_REGEXP, /* u.regexp */ - JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ - JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ - JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ - JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ - JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ - JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ - JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ - JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ - JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ - JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ - JS_CLASS_BIG_UINT64_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 */ - JS_CLASS_WEAKSET, /* u.map_state */ - JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ - JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ - JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ - JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ - JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ - JS_CLASS_GENERATOR, /* u.generator_data */ - JS_CLASS_PROXY, /* u.proxy_data */ - JS_CLASS_PROMISE, /* u.promise_data */ - JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ - JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ - JS_CLASS_ASYNC_FUNCTION, /* u.func */ - JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ - JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ - JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ - JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ - JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ - - JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ -}; - -/* number of typed array types */ -#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) -static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; -#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) - -typedef enum JSErrorEnum { - JS_EVAL_ERROR, - JS_RANGE_ERROR, - JS_REFERENCE_ERROR, - JS_SYNTAX_ERROR, - JS_TYPE_ERROR, - JS_URI_ERROR, - JS_INTERNAL_ERROR, - JS_AGGREGATE_ERROR, - - JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ -} JSErrorEnum; - -#define JS_MAX_LOCAL_VARS 65535 -#define JS_STACK_SIZE_MAX 65534 -#define JS_STRING_LEN_MAX ((1 << 30) - 1) - -#define __exception __attribute__((warn_unused_result)) - -typedef struct JSShape JSShape; -typedef struct JSString JSString; -typedef struct JSString JSAtomStruct; - -typedef enum { - JS_GC_PHASE_NONE, - JS_GC_PHASE_DECREF, - JS_GC_PHASE_REMOVE_CYCLES, -} JSGCPhaseEnum; - -typedef enum OPCodeEnum OPCodeEnum; - -/* 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; - const char *rt_info; - - int atom_hash_size; /* power of two */ - int atom_count; - int atom_size; - int atom_count_resize; /* resize hash table at this count */ - uint32_t *atom_hash; - JSAtomStruct **atom_array; - int atom_free_index; /* 0 = none */ - - int class_count; /* size of class_array */ - JSClass *class_array; - - struct list_head context_list; /* list of JSContext.link */ - /* list of JSGCObjectHeader.link. List of allocated GC objects (used - by the garbage collector) */ - struct list_head gc_obj_list; - /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ - struct list_head gc_zero_ref_count_list; - struct list_head tmp_obj_list; /* used during GC */ - /* used during GC (for keeping track of objects with a can_destroy hook) */ - struct list_head tmp_hook_obj_list; - JSGCPhaseEnum gc_phase : 8; - size_t malloc_gc_threshold; -#ifdef DUMP_LEAKS - struct list_head string_list; /* list of JSString.link */ -#endif - /* stack limitation */ - 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 inside an out of memory error, to avoid recursing */ - BOOL in_out_of_memory : 8; - - struct JSStackFrame *current_stack_frame; - - JSInterruptHandler *interrupt_handler; - void *interrupt_opaque; - - JSHostPromiseRejectionTracker *host_promise_rejection_tracker; - void *host_promise_rejection_tracker_opaque; - - struct list_head job_list; /* list of JSJobEntry.link */ - - JSModuleNormalizeFunc *module_normalize_func; - JSModuleLoaderFunc *module_loader_func; - void *module_loader_opaque; - /* 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; - - /* 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; - JSRuntimeCleanUpFunc *user_cleanup; -}; - -struct JSClass { - uint32_t class_id; /* 0 means free entry */ - JSAtom class_name; - JSClassFinalizer *finalizer; - JSClassGCMark *gc_mark; - JSClassCall *call; - /* pointers for exotic behavior, can be NULL if none are present */ - const JSClassExoticMethods *exotic; - /* called before object would be destroyed */ - JSClassCanDestroy *can_destroy; -}; - -#define JS_MODE_STRICT (1 << 0) -#define JS_MODE_STRIP (1 << 1) -#define JS_MODE_MATH (1 << 2) -#define JS_MODE_ASYNC (1 << 3) /* async function */ - -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.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; /* for C functions, only JS_MODE_MATH may be set */ - /* only used in generators. Current stack pointer value. NULL if - the function is running. */ - JSValue *cur_sp; -} JSStackFrame; - -typedef enum { - JS_GC_OBJ_TYPE_JS_OBJECT, - JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, - JS_GC_OBJ_TYPE_SHAPE, - JS_GC_OBJ_TYPE_VAR_REF, - JS_GC_OBJ_TYPE_ASYNC_FUNCTION, - JS_GC_OBJ_TYPE_JS_CONTEXT, -} JSGCObjectTypeEnum; - -/* header for GC objects. GC objects are C data structures with a - reference count that can reference other GC objects. JS Objects are - a particular type of GC object. */ -struct JSGCObjectHeader { - int ref_count; /* must come first, 32-bit */ - JSGCObjectTypeEnum gc_obj_type : 4; - uint8_t mark : 4; /* used by the GC */ - uint8_t dummy1; /* not used by the GC */ - uint16_t dummy2; /* not used by the GC */ - struct list_head link; -}; - -typedef struct JSVarRef { - union { - JSGCObjectHeader header; /* must come first */ - struct { - int __gc_ref_count; /* corresponds to header.ref_count */ - uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - uint8_t is_detached : 1; - uint8_t is_arg : 1; - uint16_t var_idx; /* index of the corresponding function variable on - the stack */ - }; - }; - JSValue *pvalue; /* pointer to the value, either on the stack or - to 'value' */ - 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; - -#ifdef CONFIG_BIGNUM -typedef struct JSFloatEnv { - limb_t prec; - bf_flags_t flags; - unsigned int status; -} JSFloatEnv; - -typedef struct JSBigDecimal { - JSRefCountHeader header; /* must come first, 32-bit */ - bfdec_t num; -} JSBigDecimal; -#endif - -typedef enum { - JS_AUTOINIT_ID_PROTOTYPE, - JS_AUTOINIT_ID_MODULE_NS, - JS_AUTOINIT_ID_PROP, -} JSAutoInitIDEnum; - -/* must be large enough to have a negligible runtime cost and small - enough to call the interrupt callback often. */ -#define JS_INTERRUPT_COUNTER_INIT 10000 - -struct JSContext { - JSGCObjectHeader header; /* must come first */ - JSRuntime *rt; - struct list_head link; - - uint16_t binary_object_count; - int binary_object_size; - - JSShape *array_shape; /* initial shape for Array objects */ - - JSValue *class_proto; - JSValue function_proto; - JSValue function_ctor; - JSValue array_ctor; - JSValue regexp_ctor; - JSValue promise_ctor; - JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; - JSValue iterator_proto; - JSValue async_iterator_proto; - JSValue array_proto_values; - JSValue throw_type_error; - JSValue eval_obj; - - JSValue global_obj; /* global object */ - JSValue global_var_obj; /* contains the global let/const definitions */ - - uint64_t random_state; - 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; - - struct list_head loaded_modules; /* list of JSModuleDef.link */ - - /* if NULL, RegExp compilation is not supported */ - JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, - JSValueConst flags); - /* if NULL, eval is not supported */ - JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int flags, int scope_idx); - void *user_opaque; -}; - -typedef union JSFloat64Union { - double d; - uint64_t u64; - uint32_t u32[2]; -} JSFloat64Union; - -enum { - JS_ATOM_TYPE_STRING = 1, - JS_ATOM_TYPE_GLOBAL_SYMBOL, - JS_ATOM_TYPE_SYMBOL, - JS_ATOM_TYPE_PRIVATE, -}; - -enum { - JS_ATOM_HASH_SYMBOL, - JS_ATOM_HASH_PRIVATE, -}; - -typedef enum { - JS_ATOM_KIND_STRING, - JS_ATOM_KIND_SYMBOL, - JS_ATOM_KIND_PRIVATE, -} JSAtomKindEnum; - -#define JS_ATOM_HASH_MASK ((1 << 30) - 1) - -struct JSString { - JSRefCountHeader header; /* must come first, 32-bit */ - uint32_t len : 31; - uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ - /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, - for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 - XXX: could change encoding to have one more bit in hash */ - uint32_t hash : 30; - uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ - uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ -#ifdef DUMP_LEAKS - struct list_head link; /* string list */ -#endif - union { - uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ - uint16_t str16[0]; - } u; -}; - -typedef struct JSClosureVar { - uint8_t is_local : 1; - uint8_t is_arg : 1; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* 8 bits available */ - uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the - parent function. otherwise: index to a closure - variable of the parent function */ - JSAtom var_name; -} JSClosureVar; - -#define ARG_SCOPE_INDEX 1 -#define ARG_SCOPE_END (-2) - -typedef struct JSVarScope { - int parent; /* index into fd->scopes of the enclosing scope */ - int first; /* index into fd->vars of the last variable in this scope */ -} JSVarScope; - -typedef enum { - /* XXX: add more variable kinds here instead of using bit fields */ - JS_VAR_NORMAL, - JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ - JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator - function declaration */ - JS_VAR_CATCH, - JS_VAR_FUNCTION_NAME, /* function expression name */ - JS_VAR_PRIVATE_FIELD, - JS_VAR_PRIVATE_METHOD, - JS_VAR_PRIVATE_GETTER, - JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ - JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ -} JSVarKindEnum; - -/* XXX: could use a different structure in bytecode functions to save - memory */ -typedef struct JSVarDef { - JSAtom var_name; - /* index into fd->scopes of this variable lexical scope */ - int scope_level; - /* 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 - in a bytecode function: - index into fd->vars of the next - variable in the same or enclosing lexical scope - */ - int scope_next; - uint8_t is_const : 1; - uint8_t is_lexical : 1; - uint8_t is_captured : 1; - uint8_t is_static_private : 1; /* only used during private class field parsing */ - uint8_t var_kind : 4; /* see JSVarKindEnum */ - /* only used during compilation: function pool index for lexical - variables with var_kind = - JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of - the definition of the 'var' variables (they have scope_level = - 0) */ - int func_pool_idx : 24; /* only used during compilation : index in - the constant pool for hoisted function - definition */ -} JSVarDef; - -/* for the encoding of the pc2line table */ -#define PC2LINE_BASE (-1) -#define PC2LINE_RANGE 5 -#define PC2LINE_OP_FIRST 1 -#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) - -typedef enum JSFunctionKindEnum { - JS_FUNC_NORMAL = 0, - JS_FUNC_GENERATOR = (1 << 0), - JS_FUNC_ASYNC = (1 << 1), - JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), -} JSFunctionKindEnum; - -typedef struct JSFunctionBytecode { - JSGCObjectHeader header; /* must come first */ - uint8_t js_mode; - uint8_t has_prototype : 1; /* true if a prototype field is necessary */ - uint8_t has_simple_parameter_list : 1; - uint8_t is_derived_class_constructor : 1; - /* true if home_object needs to be initialized */ - uint8_t need_home_object : 1; - uint8_t func_kind : 2; - uint8_t new_target_allowed : 1; - uint8_t super_call_allowed : 1; - uint8_t super_allowed : 1; - uint8_t arguments_allowed : 1; - uint8_t has_debug : 1; - uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ - uint8_t read_only_bytecode : 1; - 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; - JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ - JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ - uint16_t arg_count; - uint16_t var_count; - uint16_t defined_arg_count; /* for length function property */ - uint16_t stack_size; /* maximum stack size */ - JSContext *realm; /* function realm */ - JSValue *cpool; /* constant pool (self pointer) */ - int cpool_count; - int closure_var_count; - struct { - /* debug info, move to separate structure to save memory? */ - JSAtom filename; - int line_num; - int source_len; - int pc2line_len; - uint8_t *pc2line_buf; - char *source; - } debug; -} JSFunctionBytecode; - -typedef struct JSBoundFunction { - JSValue func_obj; - JSValue this_val; - int argc; - JSValue argv[0]; -} JSBoundFunction; - -typedef enum JSIteratorKindEnum { - JS_ITERATOR_KIND_KEY, - JS_ITERATOR_KIND_VALUE, - JS_ITERATOR_KIND_KEY_AND_VALUE, -} JSIteratorKindEnum; - -typedef struct JSForInIterator { - JSValue obj; - 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 { - JSString *pattern; - JSString *bytecode; /* also contains the flags */ -} JSRegExp; - -typedef struct JSProxyData { - JSValue target; - JSValue handler; - uint8_t is_func; - uint8_t is_revoked; -} JSProxyData; - -typedef struct JSArrayBuffer { - int byte_length; /* 0 if detached */ - uint8_t detached; - uint8_t shared; /* if shared, the array buffer cannot be detached */ - uint8_t *data; /* NULL if detached */ - struct list_head array_list; - void *opaque; - JSFreeArrayBufferDataFunc *free_func; -} JSArrayBuffer; - -typedef struct JSTypedArray { - struct list_head link; /* link to arraybuffer */ - JSObject *obj; /* back pointer to the TypedArray/DataView object */ - JSObject *buffer; /* based array buffer */ - uint32_t offset; /* offset in the array buffer */ - uint32_t length; /* length in the array buffer */ -} JSTypedArray; - -typedef struct JSAsyncFunctionState { - 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; - -typedef enum { - /* binary operators */ - JS_OVOP_ADD, - JS_OVOP_SUB, - JS_OVOP_MUL, - JS_OVOP_DIV, - JS_OVOP_MOD, - JS_OVOP_POW, - JS_OVOP_OR, - JS_OVOP_AND, - JS_OVOP_XOR, - JS_OVOP_SHL, - JS_OVOP_SAR, - JS_OVOP_SHR, - JS_OVOP_EQ, - JS_OVOP_LESS, - - JS_OVOP_BINARY_COUNT, - /* unary operators */ - JS_OVOP_POS = JS_OVOP_BINARY_COUNT, - JS_OVOP_NEG, - JS_OVOP_INC, - JS_OVOP_DEC, - JS_OVOP_NOT, - - JS_OVOP_COUNT, -} JSOverloadableOperatorEnum; - -typedef struct { - uint32_t operator_index; - JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ -} JSBinaryOperatorDefEntry; - -typedef struct { - int count; - JSBinaryOperatorDefEntry *tab; -} JSBinaryOperatorDef; - -typedef struct { - uint32_t operator_counter; - BOOL is_primitive; /* OperatorSet for a primitive type */ - /* NULL if no operator is defined */ - JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ - JSBinaryOperatorDef left; - JSBinaryOperatorDef right; -} JSOperatorSetData; - -typedef struct JSReqModuleEntry { - JSAtom module_name; - JSModuleDef *module; /* used using resolution */ -} JSReqModuleEntry; - -typedef enum JSExportTypeEnum { - JS_EXPORT_TYPE_LOCAL, - JS_EXPORT_TYPE_INDIRECT, -} JSExportTypeEnum; - -typedef struct JSExportEntry { - union { - struct { - int var_idx; /* closure variable index */ - JSVarRef *var_ref; /* if != NULL, reference to the variable */ - } local; /* for local export */ - int req_module_idx; /* module for indirect export */ - } u; - JSExportTypeEnum export_type; - JSAtom local_name; /* '*' if export ns from. not used for local - export after compilation */ - JSAtom export_name; /* exported variable name */ -} JSExportEntry; - -typedef struct JSStarExportEntry { - int req_module_idx; /* in req_module_entries */ -} JSStarExportEntry; - -typedef struct JSImportEntry { - int var_idx; /* closure variable index */ - JSAtom import_name; - int req_module_idx; /* in req_module_entries */ -} JSImportEntry; - -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; - struct list_head link; - - JSReqModuleEntry *req_module_entries; - int req_module_entries_count; - int req_module_entries_size; - - JSExportEntry *export_entries; - int export_entries_count; - int export_entries_size; - - JSStarExportEntry *star_export_entries; - int star_export_entries_count; - int star_export_entries_size; - - JSImportEntry *import_entries; - int import_entries_count; - int import_entries_size; - - JSValue module_ns; - JSValue func_obj; /* only used for JS modules */ - JSModuleInitFunc *init_func; /* only used for C modules */ - BOOL has_tla : 8; /* true if func_obj contains await */ - BOOL resolved : 8; - BOOL func_created : 8; - 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; - JSValue eval_exception; - JSValue meta_obj; /* for import.meta */ -}; - -typedef struct JSJobEntry { - struct list_head link; - JSContext *ctx; - JSJobFunc *job_func; - int argc; - JSValue argv[0]; -} JSJobEntry; - -typedef struct JSProperty { - union { - JSValue value; /* JS_PROP_NORMAL */ - struct { /* JS_PROP_GETSET */ - JSObject *getter; /* NULL if undefined */ - JSObject *setter; /* NULL if undefined */ - } getset; - JSVarRef *var_ref; /* JS_PROP_VARREF */ - struct { /* JS_PROP_AUTOINIT */ - /* in order to use only 2 pointers, we compress the realm - and the init function pointer */ - uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) - in the 2 low bits */ - void *opaque; - } init; - } u; -} JSProperty; - -#define JS_PROP_INITIAL_SIZE 2 -#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ -#define JS_ARRAY_INITIAL_SIZE 2 - -typedef struct JSShapeProperty { - uint32_t hash_next : 26; /* 0 if last in list */ - uint32_t flags : 6; /* JS_PROP_XXX */ - JSAtom atom; /* JS_ATOM_NULL = free property entry */ -} JSShapeProperty; - -struct JSShape { - /* hash table of size hash_mask + 1 before the start of the - structure (see prop_hash_end()). */ - JSGCObjectHeader header; - /* true if the shape is inserted in the shape hash table. If not, - JSShape.hash is not valid */ - uint8_t is_hashed; - /* If true, the shape may have small array index properties 'n' with 0 - <= n <= 2^31-1. If false, the shape is guaranteed not to have - small array index properties */ - uint8_t has_small_array_index; - uint32_t hash; /* current hash value */ - uint32_t prop_hash_mask; - int prop_size; /* allocated properties */ - int prop_count; /* include deleted properties */ - int deleted_prop_count; - JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ - JSObject *proto; - JSShapeProperty prop[0]; /* prop_size elements */ -}; - -struct JSObject { - union { - JSGCObjectHeader header; - struct { - int __gc_ref_count; /* corresponds to header.ref_count */ - uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ - - uint8_t extensible : 1; - uint8_t free_mark : 1; /* only used when freeing objects with cycles */ - uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ - uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (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 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 */ - JSShape *shape; /* prototype and property names + flag */ - JSProperty *prop; /* array of properties */ - /* byte offsets: 24/40 */ - struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ - /* byte offsets: 28/48 */ - union { - void *opaque; - struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ - struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ - struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ - struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ - struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ -#ifdef CONFIG_BIGNUM - struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ - struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ -#endif - struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ - struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ - struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ - struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ - struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ - struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ - struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ - struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ - struct 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 */ - /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ - struct JSFunctionBytecode *function_bytecode; - JSVarRef **var_refs; - JSObject *home_object; /* for 'super' access */ - } func; - struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ - JSContext *realm; - JSCFunctionType c_function; - uint8_t length; - uint8_t cproto; - int16_t magic; - } cfunc; - /* array part for fast arrays and typed arrays */ - struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - union { - uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - } u1; - union { - JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ - int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ - uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ - int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ - uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ - int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ - uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ - int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ - uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ - float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ - double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ - } u; - uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ - } array; /* 12/20 bytes */ - JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ - JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ - } u; - /* byte sizes: 40/48/72 */ -}; - -enum { - __JS_ATOM_NULL = JS_ATOM_NULL, -#define DEF(name, str) JS_ATOM_ ## name, -#include "quickjs-atom.h" -#undef DEF - JS_ATOM_END, -}; -#define JS_ATOM_LAST_KEYWORD JS_ATOM_super -#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield - -static const char js_atom_init[] = -#define DEF(name, str) str "\0" -#include "quickjs-atom.h" -#undef DEF -; - -typedef enum OPCodeFormat { -#define FMT(f) OP_FMT_ ## f, -#define DEF(id, size, n_pop, n_push, f) -#include "quickjs-opcode.h" -#undef DEF -#undef FMT -} OPCodeFormat; - -enum OPCodeEnum { -#define FMT(f) -#define DEF(id, size, n_pop, n_push, f) OP_ ## id, -#define def(id, size, n_pop, n_push, f) -#include "quickjs-opcode.h" -#undef def -#undef DEF -#undef FMT - OP_COUNT, /* excluding temporary opcodes */ - /* temporary opcodes : overlap with the short opcodes */ - OP_TEMP_START = OP_nop + 1, - OP___dummy = OP_TEMP_START - 1, -#define FMT(f) -#define DEF(id, size, n_pop, n_push, f) -#define def(id, size, n_pop, n_push, f) OP_ ## id, -#include "quickjs-opcode.h" -#undef def -#undef DEF -#undef FMT - OP_TEMP_END, -}; - -static int JS_InitAtoms(JSRuntime *rt); -static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, - int atom_type); -static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); -static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); -static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags); -static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags); -static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, JSValueConst new_target, - int argc, JSValue *argv, int flags); -static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValue *argv, int flags); -static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv); -static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValueConst *argv); -static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, BOOL is_array_ctor); -static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, - JSValueConst val, int flags, int scope_idx); -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); -static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); -static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); -static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); -static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); -static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); -static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val); -static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); -static __maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val); -static __maybe_unused void JS_DumpShapes(JSRuntime *rt); -static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -static void js_array_finalizer(JSRuntime *rt, JSValue val); -static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_object_data_finalizer(JSRuntime *rt, JSValue val); -static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_c_function_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); -static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_regexp_finalizer(JSRuntime *rt, JSValue val); -static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); -static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_proxy_finalizer(JSRuntime *rt, JSValue val); -static void js_proxy_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_finalizer(JSRuntime *rt, JSValue val); -static void js_map_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_generator_finalizer(JSRuntime *rt, JSValue obj); -static void js_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_promise_finalizer(JSRuntime *rt, JSValue val); -static void js_promise_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); -static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -#ifdef CONFIG_BIGNUM -static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); -static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -#endif - -#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_compile_regexp(JSContext *ctx, JSValueConst pattern, - JSValueConst flags); -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc); -static void gc_decref(JSRuntime *rt); -static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, - const JSClassDef *class_def, JSAtom name); - -typedef enum JSStrictEqModeEnum { - JS_EQ_STRICT, - JS_EQ_SAME_VALUE, - JS_EQ_SAME_VALUE_ZERO, -} JSStrictEqModeEnum; - -static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, - JSStrictEqModeEnum eq_mode); -static BOOL js_strict_eq(JSContext *ctx, 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_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); -static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); -static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); -static int JS_CreateProperty(JSContext *ctx, JSObject *p, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); -static void reset_weak_ref(JSRuntime *rt, JSObject *p); -static JSValue js_array_buffer_constructor3(JSContext *ctx, - JSValueConst new_target, - uint64_t len, JSClassID class_id, - uint8_t *buf, - JSFreeArrayBufferDataFunc *free_func, - void *opaque, BOOL alloc_flag); -static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); -static JSValue js_typed_array_constructor(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int classid); -static 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, - int flags); -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); -static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int flags, int scope_idx); -static void js_free_module_def(JSContext *ctx, JSModuleDef *m); -static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, - JS_MarkFunc *mark_func); -static JSValue js_import_meta(JSContext *ctx); -static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); -static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); -static JSValue js_new_promise_capability(JSContext *ctx, - JSValue *resolving_funcs, - JSValueConst ctor); -static __exception int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs); -static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -static 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); -static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop, JSValue val, int flags); -static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); -static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); -static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); -static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, - JSObject *p, JSAtom prop); -static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); -static void JS_AddIntrinsicBasicObjects(JSContext *ctx); -static void js_free_shape(JSRuntime *rt, JSShape *sh); -static void js_free_shape_null(JSRuntime *rt, JSShape *sh); -static int js_shape_prepare_update(JSContext *ctx, JSObject *p, - JSShapeProperty **pprs); -static int init_shape_hash(JSRuntime *rt); -static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValueConst obj); -static __exception int js_get_length64(JSContext *ctx, int64_t *pres, - JSValueConst obj); -static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); -static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, - JSValueConst array_arg); -static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, - JSValue **arrpp, uint32_t *countp); -static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, - JSValueConst sync_iter); -static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); -static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv, int flags); -static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); -static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, - JSGCObjectTypeEnum type); -static void remove_gc_object(JSGCObjectHeader *h); -static 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); -static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_map); - -static const JSClassExoticMethods js_arguments_exotic_methods; -static const JSClassExoticMethods js_string_exotic_methods; -static const JSClassExoticMethods js_proxy_exotic_methods; -static const JSClassExoticMethods js_module_ns_exotic_methods; -static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; - -static void js_trigger_gc(JSRuntime *rt, size_t size) -{ - BOOL force_gc; -#ifdef FORCE_GC_AT_MALLOC - force_gc = TRUE; -#else - force_gc = ((rt->malloc_state.malloc_size + size) > - rt->malloc_gc_threshold); -#endif - if (force_gc) { -#ifdef DUMP_GC - printf("GC: size=%" PRIu64 "\n", - (uint64_t)rt->malloc_state.malloc_size); -#endif - JS_RunGC(rt); - rt->malloc_gc_threshold = rt->malloc_state.malloc_size + - (rt->malloc_state.malloc_size >> 1); - } -} - -static size_t js_malloc_usable_size_unknown(const void *ptr) -{ - return 0; -} - -void *js_malloc_rt(JSRuntime *rt, size_t size) -{ - return rt->mf.js_malloc(&rt->malloc_state, size); -} - -void js_free_rt(JSRuntime *rt, void *ptr) -{ - rt->mf.js_free(&rt->malloc_state, ptr); -} - -void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) -{ - return rt->mf.js_realloc(&rt->malloc_state, ptr, size); -} - -size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) -{ - return rt->mf.js_malloc_usable_size(ptr); -} - -void *js_mallocz_rt(JSRuntime *rt, size_t size) -{ - void *ptr; - ptr = js_malloc_rt(rt, size); - if (!ptr) - return NULL; - return memset(ptr, 0, size); -} - -/* 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) -{ - void *ptr; - ptr = js_malloc_rt(ctx->rt, size); - if (unlikely(!ptr)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return ptr; -} - -/* Throw out of memory in case of error */ -void *js_mallocz(JSContext *ctx, size_t size) -{ - void *ptr; - ptr = js_mallocz_rt(ctx->rt, size); - if (unlikely(!ptr)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return ptr; -} - -void js_free(JSContext *ctx, void *ptr) -{ - js_free_rt(ctx->rt, ptr); -} - -/* Throw out of memory in case of error */ -void *js_realloc(JSContext *ctx, void *ptr, size_t size) -{ - void *ret; - ret = js_realloc_rt(ctx->rt, ptr, size); - if (unlikely(!ret && size != 0)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return ret; -} - -/* store extra allocated size in *pslack if successful */ -void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) -{ - void *ret; - ret = js_realloc_rt(ctx->rt, ptr, size); - if (unlikely(!ret && size != 0)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - if (pslack) { - size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); - *pslack = (new_size > size) ? new_size - size : 0; - } - return ret; -} - -size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) -{ - return js_malloc_usable_size_rt(ctx->rt, ptr); -} - -/* Throw out of memory exception in case of error */ -char *js_strndup(JSContext *ctx, const char *s, size_t n) -{ - char *ptr; - ptr = js_malloc(ctx, n + 1); - if (ptr) { - memcpy(ptr, s, n); - ptr[n] = '\0'; - } - return ptr; -} - -char *js_strdup(JSContext *ctx, const char *str) -{ - return js_strndup(ctx, str, strlen(str)); -} - -static no_inline int js_realloc_array(JSContext *ctx, void **parray, - int elem_size, int *psize, int req_size) -{ - int new_size; - size_t slack; - void *new_array; - /* XXX: potential arithmetic overflow */ - new_size = max_int(req_size, *psize * 3 / 2); - new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); - if (!new_array) - return -1; - new_size += slack / elem_size; - *psize = new_size; - *parray = new_array; - return 0; -} - -/* resize the array and update its size if req_size > *psize */ -static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, - int *psize, int req_size) -{ - if (unlikely(req_size > *psize)) - return js_realloc_array(ctx, parray, elem_size, psize, req_size); - else - return 0; -} - -static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) -{ - dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); -} - -static inline int is_digit(int c) { - return c >= '0' && c <= '9'; -} - -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; - JSClassGCMark *gc_mark; -} JSClassShortDef; - -static JSClassShortDef const js_std_class_def[] = { - { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ - { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ - { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ - { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ - { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ - { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ - { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ - { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ - { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ - { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ - { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ - { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ - { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ - { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ - { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ - { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ - { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ - { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ - { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ - { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ - { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ - { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ - { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ - { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ - { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ - { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ - { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ - { 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_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 */ - { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ - { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ - { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ - { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ - { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ - { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ - { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ -}; - -static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, - int start, int count) -{ - JSClassDef cm_s, *cm = &cm_s; - int i, class_id; - - for(i = 0; i < count; i++) { - class_id = i + start; - memset(cm, 0, sizeof(*cm)); - cm->finalizer = tab[i].finalizer; - cm->gc_mark = tab[i].gc_mark; - if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) - return -1; - } - return 0; -} - -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) -{ - return 0; -} - -static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -{ - return FALSE; -} -#else -/* Note: OS and CPU dependent */ -static inline uintptr_t js_get_stack_pointer(void) -{ - return (uintptr_t)__builtin_frame_address(0); -} - -static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -{ - uintptr_t sp; - sp = js_get_stack_pointer() - alloca_size; - return unlikely(sp < rt->stack_limit); -} -#endif - -JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) -{ - JSRuntime *rt; - JSMallocState ms; - - memset(&ms, 0, sizeof(ms)); - ms.opaque = opaque; - ms.malloc_limit = -1; - - rt = mf->js_malloc(&ms, sizeof(JSRuntime)); - if (!rt) - return NULL; - memset(rt, 0, sizeof(*rt)); - rt->mf = *mf; - if (!rt->mf.js_malloc_usable_size) { - /* use dummy function if none provided */ - rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; - } - rt->malloc_state = ms; - rt->malloc_gc_threshold = 256 * 1024; - - 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; - -#ifdef DUMP_LEAKS - init_list_head(&rt->string_list); -#endif - init_list_head(&rt->job_list); - - if (JS_InitAtoms(rt)) - goto fail; - - /* create the object, array and function classes */ - if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, - countof(js_std_class_def)) < 0) - goto fail; - rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; - rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; - rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; - - rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; - rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; - rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; - rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; - if (init_shape_hash(rt)) - goto fail; - - rt->stack_size = JS_DEFAULT_STACK_SIZE; - JS_UpdateStackTop(rt); - - rt->current_exception = JS_NULL; - - return rt; - fail: - JS_FreeRuntime(rt); - return NULL; -} - -void *JS_GetRuntimeOpaque(JSRuntime *rt) -{ - return rt->user_opaque; -} - -void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) -{ - rt->user_opaque = opaque; -} - -void JS_SetRuntimeCleanUpFunc(JSRuntime *rt, JSRuntimeCleanUpFunc cleanup_func) -{ - rt->user_cleanup = cleanup_func; -} - -/* default memory allocation functions with memory limitation */ -static size_t js_def_malloc_usable_size(const void *ptr) -{ -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize((void *)ptr); -#elif defined(EMSCRIPTEN) || defined(__OpenBSD__) - return 0; -#elif defined(__linux__) - return malloc_usable_size((void *)ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size((void *)ptr); -#endif -} - -static void *js_def_malloc(JSMallocState *s, size_t size) -{ - void *ptr; - - /* Do not allocate zero bytes: behavior is platform dependent */ - assert(size != 0); - - if (unlikely(s->malloc_size + size > s->malloc_limit)) - return NULL; - - ptr = malloc(size); - if (!ptr) - return NULL; - - s->malloc_count++; - s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - return ptr; -} - -static void js_def_free(JSMallocState *s, void *ptr) -{ - if (!ptr) - return; - - s->malloc_count--; - s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); -} - -static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) -{ - size_t old_size; - - if (!ptr) { - if (size == 0) - return NULL; - return js_def_malloc(s, size); - } - old_size = js_def_malloc_usable_size(ptr); - if (size == 0) { - s->malloc_count--; - s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); - return NULL; - } - if (s->malloc_size + size - old_size > s->malloc_limit) - return NULL; - - ptr = realloc(ptr, size); - if (!ptr) - return NULL; - - s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; - return ptr; -} - -static const JSMallocFunctions def_malloc_funcs = { - js_def_malloc, - js_def_free, - js_def_realloc, - js_def_malloc_usable_size, -}; - -JSRuntime *JS_NewRuntime(void) -{ - return JS_NewRuntime2(&def_malloc_funcs, NULL); -} - -void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) -{ - rt->malloc_state.malloc_limit = limit; -} - -/* use -1 to disable automatic GC */ -void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) -{ - rt->malloc_gc_threshold = gc_threshold; -} - -#define malloc(s) malloc_is_forbidden(s) -#define free(p) free_is_forbidden(p) -#define realloc(p,s) realloc_is_forbidden(p,s) - -void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) -{ - rt->interrupt_handler = cb; - rt->interrupt_opaque = opaque; -} - -void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) -{ - rt->can_block = can_block; -} - -void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, - const JSSharedArrayBufferFunctions *sf) -{ - rt->sab_funcs = *sf; -} - -/* return 0 if OK, < 0 if exception */ -int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = ctx->rt; - JSJobEntry *e; - int i; - - e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); - if (!e) - return -1; - e->ctx = ctx; - e->job_func = job_func; - e->argc = argc; - for(i = 0; i < argc; i++) { - e->argv[i] = JS_DupValue(ctx, argv[i]); - } - list_add_tail(&e->link, &rt->job_list); - return 0; -} - -BOOL JS_IsJobPending(JSRuntime *rt) -{ - return !list_empty(&rt->job_list); -} - -/* return < 0 if exception, 0 if no job pending, 1 if a job was - executed successfully. the context of the job is stored in '*pctx' */ -int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) -{ - JSContext *ctx; - JSJobEntry *e; - JSValue res; - int i, ret; - - if (list_empty(&rt->job_list)) { - *pctx = NULL; - return 0; - } - - /* get the first pending job and execute it */ - e = list_entry(rt->job_list.next, JSJobEntry, link); - list_del(&e->link); - ctx = e->ctx; - res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv); - for(i = 0; i < e->argc; i++) - JS_FreeValue(ctx, e->argv[i]); - if (JS_IsException(res)) - ret = -1; - else - ret = 1; - JS_FreeValue(ctx, res); - js_free(ctx, e); - *pctx = ctx; - return ret; -} - -static inline uint32_t atom_get_free(const JSAtomStruct *p) -{ - return (uintptr_t)p >> 1; -} - -static inline BOOL atom_is_free(const JSAtomStruct *p) -{ - return (uintptr_t)p & 1; -} - -static inline JSAtomStruct *atom_set_free(uint32_t v) -{ - return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); -} - -/* Note: the string contents are uninitialized */ -static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) -{ - JSString *str; - str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); - if (unlikely(!str)) - return NULL; - str->header.ref_count = 1; - str->is_wide_char = is_wide_char; - str->len = max_len; - str->atom_type = 0; - str->hash = 0; /* optional but costless */ - str->hash_next = 0; /* optional */ -#ifdef DUMP_LEAKS - list_add_tail(&str->link, &rt->string_list); -#endif - return str; -} - -static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) -{ - JSString *p; - p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); - if (unlikely(!p)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return p; -} - -/* same as JS_FreeValueRT() but faster */ -static inline void js_free_string(JSRuntime *rt, JSString *str) -{ - if (--str->header.ref_count <= 0) { - if (str->atom_type) { - JS_FreeAtomStruct(rt, str); - } else { -#ifdef DUMP_LEAKS - list_del(&str->link); -#endif - js_free_rt(rt, str); - } - } -} - -void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) -{ - if (rt) - rt->rt_info = s; -} - -void JS_FreeRuntime(JSRuntime *rt) -{ - struct list_head *el, *el1; - int i; - - JS_FreeValueRT(rt, rt->current_exception); - - list_for_each_safe(el, el1, &rt->job_list) { - JSJobEntry *e = list_entry(el, JSJobEntry, link); - for(i = 0; i < e->argc; i++) - JS_FreeValueRT(rt, e->argv[i]); - js_free_rt(rt, e); - } - init_list_head(&rt->job_list); - - JS_RunGC(rt); - - rt->user_cleanup(rt); - -#ifdef DUMP_LEAKS - /* leaking objects */ - { - BOOL header_done; - JSGCObjectHeader *p; - int count; - - /* remove the internal refcounts to display only the object - referenced externally */ - list_for_each(el, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - p->mark = 0; - } - gc_decref(rt); - - header_done = FALSE; - list_for_each(el, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - if (p->ref_count != 0) { - if (!header_done) { - printf("Object leaks:\n"); - JS_DumpObjectHeader(rt); - header_done = TRUE; - } - JS_DumpGCObject(rt, p); - } - } - - count = 0; - list_for_each(el, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - if (p->ref_count == 0) { - count++; - } - } - if (count != 0) - printf("Secondary object leaks: %d\n", count); - } -#endif - assert(list_empty(&rt->gc_obj_list)); - - /* free the classes */ - for(i = 0; i < rt->class_count; i++) { - JSClass *cl = &rt->class_array[i]; - if (cl->class_id != 0) { - JS_FreeAtomRT(rt, cl->class_name); - } - } - js_free_rt(rt, rt->class_array); - - bf_context_end(&rt->bf_ctx); - -#ifdef DUMP_LEAKS - /* only the atoms defined in JS_InitAtoms() should be left */ - { - BOOL header_done = FALSE; - - for(i = 0; i < rt->atom_size; i++) { - JSAtomStruct *p = rt->atom_array[i]; - if (!atom_is_free(p) /* && p->str*/) { - if (i >= JS_ATOM_END || p->header.ref_count != 1) { - if (!header_done) { - header_done = TRUE; - if (rt->rt_info) { - printf("%s:1: atom leakage:", rt->rt_info); - } else { - printf("Atom leaks:\n" - " %6s %6s %s\n", - "ID", "REFCNT", "NAME"); - } - } - if (rt->rt_info) { - printf(" "); - } else { - printf(" %6u %6u ", i, p->header.ref_count); - } - switch (p->atom_type) { - case JS_ATOM_TYPE_STRING: - JS_DumpString(rt, p); - break; - case JS_ATOM_TYPE_GLOBAL_SYMBOL: - printf("Symbol.for("); - JS_DumpString(rt, p); - printf(")"); - break; - case JS_ATOM_TYPE_SYMBOL: - if (p->hash == JS_ATOM_HASH_SYMBOL) { - printf("Symbol("); - JS_DumpString(rt, p); - printf(")"); - } else { - printf("Private("); - JS_DumpString(rt, p); - printf(")"); - } - break; - } - if (rt->rt_info) { - printf(":%u", p->header.ref_count); - } else { - printf("\n"); - } - } - } - } - if (rt->rt_info && header_done) - printf("\n"); - } -#endif - - /* free the atoms */ - for(i = 0; i < rt->atom_size; i++) { - JSAtomStruct *p = rt->atom_array[i]; - if (!atom_is_free(p)) { -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - } - } - js_free_rt(rt, rt->atom_array); - js_free_rt(rt, rt->atom_hash); - js_free_rt(rt, rt->shape_hash); -#ifdef DUMP_LEAKS - if (!list_empty(&rt->string_list)) { - if (rt->rt_info) { - printf("%s:1: string leakage:", rt->rt_info); - } else { - printf("String leaks:\n" - " %6s %s\n", - "REFCNT", "VALUE"); - } - list_for_each_safe(el, el1, &rt->string_list) { - JSString *str = list_entry(el, JSString, link); - if (rt->rt_info) { - printf(" "); - } else { - printf(" %6u ", str->header.ref_count); - } - JS_DumpString(rt, str); - if (rt->rt_info) { - printf(":%u", str->header.ref_count); - } else { - printf("\n"); - } - list_del(&str->link); - js_free_rt(rt, str); - } - if (rt->rt_info) - printf("\n"); - } - { - JSMallocState *s = &rt->malloc_state; - if (s->malloc_count > 1) { - if (rt->rt_info) - printf("%s:1: ", rt->rt_info); - printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", - (uint64_t)(s->malloc_size - sizeof(JSRuntime)), - (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); - } - } -#endif - - { - JSMallocState ms = rt->malloc_state; - rt->mf.js_free(&ms, rt); - } -} - -JSContext *JS_NewContextRaw(JSRuntime *rt) -{ - JSContext *ctx; - int i; - - ctx = js_mallocz_rt(rt, sizeof(JSContext)); - if (!ctx) - return NULL; - ctx->header.ref_count = 1; - add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); - - ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * - rt->class_count); - if (!ctx->class_proto) { - js_free_rt(rt, ctx); - return NULL; - } - ctx->rt = rt; - list_add_tail(&ctx->link, &rt->context_list); - 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; - ctx->regexp_ctor = JS_NULL; - ctx->promise_ctor = JS_NULL; - init_list_head(&ctx->loaded_modules); - - JS_AddIntrinsicBasicObjects(ctx); - return ctx; -} - -JSContext *JS_NewContext(JSRuntime *rt) -{ - JSContext *ctx; - - ctx = JS_NewContextRaw(rt); - if (!ctx) - return NULL; - - JS_AddIntrinsicBaseObjects(ctx); - JS_AddIntrinsicDate(ctx); - JS_AddIntrinsicEval(ctx); - JS_AddIntrinsicStringNormalize(ctx); - JS_AddIntrinsicRegExp(ctx); - JS_AddIntrinsicJSON(ctx); - JS_AddIntrinsicProxy(ctx); - JS_AddIntrinsicMapSet(ctx); - JS_AddIntrinsicTypedArrays(ctx); - JS_AddIntrinsicPromise(ctx); - JS_AddIntrinsicBigInt(ctx); - return ctx; -} - -void *JS_GetContextOpaque(JSContext *ctx) -{ - return ctx->user_opaque; -} - -void JS_SetContextOpaque(JSContext *ctx, void *opaque) -{ - ctx->user_opaque = opaque; -} - -/* set the new value and free the old value after (freeing the value - can reallocate the object data) */ -static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) -{ - JSValue old_val; - old_val = *pval; - *pval = new_val; - JS_FreeValue(ctx, old_val); -} - -void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) -{ - JSRuntime *rt = ctx->rt; - assert(class_id < rt->class_count); - set_value(ctx, &ctx->class_proto[class_id], obj); -} - -JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) -{ - JSRuntime *rt = ctx->rt; - assert(class_id < rt->class_count); - return JS_DupValue(ctx, ctx->class_proto[class_id]); -} - -typedef enum JSFreeModuleEnum { - JS_FREE_MODULE_ALL, - JS_FREE_MODULE_NOT_RESOLVED, -} JSFreeModuleEnum; - -/* XXX: would be more efficient with separate module lists */ -static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) -{ - struct list_head *el, *el1; - list_for_each_safe(el, el1, &ctx->loaded_modules) { - JSModuleDef *m = list_entry(el, JSModuleDef, link); - if (flag == JS_FREE_MODULE_ALL || - (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { - js_free_module_def(ctx, m); - } - } -} - -JSContext *JS_DupContext(JSContext *ctx) -{ - ctx->header.ref_count++; - return ctx; -} - -/* used by the GC */ -static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, - JS_MarkFunc *mark_func) -{ - int i; - struct list_head *el; - - /* modules are not seen by the GC, so we directly mark the objects - referenced by each module */ - list_for_each(el, &ctx->loaded_modules) { - JSModuleDef *m = list_entry(el, JSModuleDef, link); - js_mark_module_def(rt, m, mark_func); - } - - JS_MarkValue(rt, ctx->global_obj, mark_func); - JS_MarkValue(rt, ctx->global_var_obj, mark_func); - - JS_MarkValue(rt, ctx->throw_type_error, mark_func); - JS_MarkValue(rt, ctx->eval_obj, mark_func); - - JS_MarkValue(rt, ctx->array_proto_values, mark_func); - for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { - JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); - } - for(i = 0; i < rt->class_count; i++) { - JS_MarkValue(rt, ctx->class_proto[i], mark_func); - } - JS_MarkValue(rt, ctx->iterator_proto, mark_func); - JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); - JS_MarkValue(rt, ctx->promise_ctor, mark_func); - JS_MarkValue(rt, ctx->array_ctor, mark_func); - JS_MarkValue(rt, ctx->regexp_ctor, mark_func); - JS_MarkValue(rt, ctx->function_ctor, mark_func); - JS_MarkValue(rt, ctx->function_proto, mark_func); - - if (ctx->array_shape) - mark_func(rt, &ctx->array_shape->header); -} - -void JS_FreeContext(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - int i; - - if (--ctx->header.ref_count > 0) - return; - assert(ctx->header.ref_count == 0); - -#ifdef DUMP_ATOMS - JS_DumpAtoms(ctx->rt); -#endif -#ifdef DUMP_SHAPES - JS_DumpShapes(ctx->rt); -#endif -#ifdef DUMP_OBJECTS - { - struct list_head *el; - JSGCObjectHeader *p; - printf("JSObjects: {\n"); - JS_DumpObjectHeader(ctx->rt); - list_for_each(el, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - JS_DumpGCObject(rt, p); - } - printf("}\n"); - } -#endif -#ifdef DUMP_MEM - { - JSMemoryUsage stats; - JS_ComputeMemoryUsage(rt, &stats); - JS_DumpMemoryUsage(stdout, &stats, rt); - } -#endif - - js_free_modules(ctx, JS_FREE_MODULE_ALL); - - JS_FreeValue(ctx, ctx->global_obj); - JS_FreeValue(ctx, ctx->global_var_obj); - - JS_FreeValue(ctx, ctx->throw_type_error); - JS_FreeValue(ctx, ctx->eval_obj); - - JS_FreeValue(ctx, ctx->array_proto_values); - for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { - JS_FreeValue(ctx, ctx->native_error_proto[i]); - } - for(i = 0; i < rt->class_count; i++) { - JS_FreeValue(ctx, ctx->class_proto[i]); - } - js_free_rt(rt, ctx->class_proto); - JS_FreeValue(ctx, ctx->iterator_proto); - JS_FreeValue(ctx, ctx->async_iterator_proto); - JS_FreeValue(ctx, ctx->promise_ctor); - JS_FreeValue(ctx, ctx->array_ctor); - JS_FreeValue(ctx, ctx->regexp_ctor); - JS_FreeValue(ctx, ctx->function_ctor); - JS_FreeValue(ctx, ctx->function_proto); - - js_free_shape_null(ctx->rt, ctx->array_shape); - - list_del(&ctx->link); - remove_gc_object(&ctx->header); - js_free_rt(ctx->rt, ctx); -} - -JSRuntime *JS_GetRuntime(JSContext *ctx) -{ - return ctx->rt; -} - -static void update_stack_limit(JSRuntime *rt) -{ - if (rt->stack_size == 0) { - rt->stack_limit = 0; /* no limit */ - } else { - rt->stack_limit = rt->stack_top - rt->stack_size; - } -} - -void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) -{ - rt->stack_size = stack_size; - update_stack_limit(rt); -} - -void JS_UpdateStackTop(JSRuntime *rt) -{ - rt->stack_top = js_get_stack_pointer(); - update_stack_limit(rt); -} - -static inline BOOL is_strict_mode(JSContext *ctx) -{ - JSStackFrame *sf = ctx->rt->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_STRICT)); -} - -#ifdef CONFIG_BIGNUM -static inline BOOL is_math_mode(JSContext *ctx) -{ - JSStackFrame *sf = ctx->rt->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_MATH)); -} -#else -static inline BOOL is_math_mode(JSContext *ctx) -{ - return FALSE; -} -#endif - -/* JSAtom support */ - -#define JS_ATOM_TAG_INT (1U << 31) -#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) -#define JS_ATOM_MAX ((1U << 30) - 1) - -/* return the max count from the hash size */ -#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) - -static inline BOOL __JS_AtomIsConst(JSAtom v) -{ -#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 - return (int32_t)v <= 0; -#else - return (int32_t)v < JS_ATOM_END; -#endif -} - -static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) -{ - return (v & JS_ATOM_TAG_INT) != 0; -} - -static inline JSAtom __JS_AtomFromUInt32(uint32_t v) -{ - return v | JS_ATOM_TAG_INT; -} - -static inline uint32_t __JS_AtomToUInt32(JSAtom atom) -{ - return atom & ~JS_ATOM_TAG_INT; -} - -static inline int is_num(int c) -{ - return c >= '0' && c <= '9'; -} - -/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ -static inline BOOL is_num_string(uint32_t *pval, const JSString *p) -{ - uint32_t n; - uint64_t n64; - int c, i, len; - - len = p->len; - if (len == 0 || len > 10) - return FALSE; - c = string_get(p, 0); - if (is_num(c)) { - if (c == '0') { - if (len != 1) - return FALSE; - n = 0; - } else { - n = c - '0'; - for(i = 1; i < len; i++) { - c = string_get(p, i); - if (!is_num(c)) - return FALSE; - n64 = (uint64_t)n * 10 + (c - '0'); - if ((n64 >> 32) != 0) - return FALSE; - n = n64; - } - } - *pval = n; - return TRUE; - } else { - return FALSE; - } -} - -/* XXX: could use faster version ? */ -static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) -{ - size_t i; - - for(i = 0; i < len; i++) - h = h * 263 + str[i]; - return h; -} - -static inline uint32_t hash_string16(const uint16_t *str, - size_t len, uint32_t h) -{ - size_t i; - - for(i = 0; i < len; i++) - h = h * 263 + str[i]; - return h; -} - -static uint32_t hash_string(const JSString *str, uint32_t h) -{ - if (str->is_wide_char) - h = hash_string16(str->u.str16, str->len, h); - else - h = hash_string8(str->u.str8, str->len, h); - return h; -} - -static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep) -{ - 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); - } -} - -static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) -{ - int i, sep; - - if (p == NULL) { - printf("<null>"); - return; - } - printf("%d", p->header.ref_count); - sep = (p->header.ref_count == 1) ? '\"' : '\''; - putchar(sep); - for(i = 0; i < p->len; i++) { - JS_DumpChar(rt, string_get(p, i), sep); - } - putchar(sep); -} - -static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) -{ - JSAtomStruct *p; - int h, i; - /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ - printf("JSAtom count=%d size=%d hash_size=%d:\n", - rt->atom_count, rt->atom_size, rt->atom_hash_size); - printf("JSAtom hash table: {\n"); - for(i = 0; i < rt->atom_hash_size; i++) { - h = rt->atom_hash[i]; - if (h) { - printf(" %d:", i); - while (h) { - p = rt->atom_array[h]; - printf(" "); - JS_DumpString(rt, p); - h = p->hash_next; - } - printf("\n"); - } - } - printf("}\n"); - printf("JSAtom table: {\n"); - for(i = 0; i < rt->atom_size; i++) { - p = rt->atom_array[i]; - if (!atom_is_free(p)) { - printf(" %d: { %d %08x ", i, p->atom_type, p->hash); - if (!(p->len == 0 && p->is_wide_char != 0)) - JS_DumpString(rt, p); - printf(" %d }\n", p->hash_next); - } - } - printf("}\n"); -} - -static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) -{ - JSAtomStruct *p; - uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; - - assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ - new_hash_mask = new_hash_size - 1; - new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); - if (!new_hash) - return -1; - for(i = 0; i < rt->atom_hash_size; i++) { - h = rt->atom_hash[i]; - while (h != 0) { - p = rt->atom_array[h]; - hash_next1 = p->hash_next; - /* add in new hash table */ - j = p->hash & new_hash_mask; - p->hash_next = new_hash[j]; - new_hash[j] = h; - h = hash_next1; - } - } - js_free_rt(rt, rt->atom_hash); - rt->atom_hash = new_hash; - rt->atom_hash_size = new_hash_size; - rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); - // JS_DumpAtoms(rt); - return 0; -} - -static int JS_InitAtoms(JSRuntime *rt) -{ - int i, len, atom_type; - const char *p; - - rt->atom_hash_size = 0; - rt->atom_hash = NULL; - rt->atom_count = 0; - rt->atom_size = 0; - rt->atom_free_index = 0; - if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ - return -1; - - p = js_atom_init; - for(i = 1; i < JS_ATOM_END; i++) { - if (i == JS_ATOM_Private_brand) - atom_type = JS_ATOM_TYPE_PRIVATE; - else if (i >= JS_ATOM_Symbol_toPrimitive) - atom_type = JS_ATOM_TYPE_SYMBOL; - else - atom_type = JS_ATOM_TYPE_STRING; - len = strlen(p); - if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) - return -1; - p = p + len + 1; - } - return 0; -} - -static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) -{ - JSAtomStruct *p; - - if (!__JS_AtomIsConst(v)) { - p = rt->atom_array[v]; - p->header.ref_count++; - } - return v; -} - -JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) -{ - JSRuntime *rt; - JSAtomStruct *p; - - if (!__JS_AtomIsConst(v)) { - rt = ctx->rt; - p = rt->atom_array[v]; - p->header.ref_count++; - } - return v; -} - -static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) -{ - JSRuntime *rt; - JSAtomStruct *p; - - rt = ctx->rt; - if (__JS_AtomIsTaggedInt(v)) - return JS_ATOM_KIND_STRING; - p = rt->atom_array[v]; - switch(p->atom_type) { - case JS_ATOM_TYPE_STRING: - return JS_ATOM_KIND_STRING; - case JS_ATOM_TYPE_GLOBAL_SYMBOL: - return JS_ATOM_KIND_SYMBOL; - case JS_ATOM_TYPE_SYMBOL: - switch(p->hash) { - case JS_ATOM_HASH_SYMBOL: - return JS_ATOM_KIND_SYMBOL; - case JS_ATOM_HASH_PRIVATE: - return JS_ATOM_KIND_PRIVATE; - default: - abort(); - } - default: - abort(); - } -} - -static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) -{ - return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; -} - -static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) -{ - uint32_t i = p->hash_next; /* atom_index */ - if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { - JSAtomStruct *p1; - - i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; - p1 = rt->atom_array[i]; - while (p1 != p) { - assert(i != 0); - i = p1->hash_next; - p1 = rt->atom_array[i]; - } - } - return i; -} - -/* string case (internal). Return JS_ATOM_NULL if error. 'str' is - freed. */ -static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) -{ - uint32_t h, h1, i; - JSAtomStruct *p; - int len; - -#if 0 - printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); -#endif - if (atom_type < JS_ATOM_TYPE_SYMBOL) { - /* str is not NULL */ - if (str->atom_type == atom_type) { - /* str is the atom, return its index */ - i = js_get_atom_index(rt, str); - /* reduce string refcount and increase atom's unless constant */ - if (__JS_AtomIsConst(i)) - str->header.ref_count--; - return i; - } - /* try and locate an already registered atom */ - len = str->len; - h = hash_string(str, atom_type); - h &= JS_ATOM_HASH_MASK; - h1 = h & (rt->atom_hash_size - 1); - i = rt->atom_hash[h1]; - while (i != 0) { - p = rt->atom_array[i]; - if (p->hash == h && - p->atom_type == atom_type && - p->len == len && - js_string_memcmp(p, str, len) == 0) { - if (!__JS_AtomIsConst(i)) - p->header.ref_count++; - goto done; - } - i = p->hash_next; - } - } else { - h1 = 0; /* avoid warning */ - if (atom_type == JS_ATOM_TYPE_SYMBOL) { - h = JS_ATOM_HASH_SYMBOL; - } else { - h = JS_ATOM_HASH_PRIVATE; - atom_type = JS_ATOM_TYPE_SYMBOL; - } - } - - if (rt->atom_free_index == 0) { - /* allow new atom entries */ - uint32_t new_size, start; - JSAtomStruct **new_array; - - /* alloc new with size progression 3/2: - 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 - preallocating space for predefined atoms (at least 195). - */ - new_size = max_int(211, rt->atom_size * 3 / 2); - if (new_size > JS_ATOM_MAX) - goto fail; - /* XXX: should use realloc2 to use slack space */ - new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); - if (!new_array) - goto fail; - /* Note: the atom 0 is not used */ - start = rt->atom_size; - if (start == 0) { - /* JS_ATOM_NULL entry */ - p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); - if (!p) { - js_free_rt(rt, new_array); - goto fail; - } - p->header.ref_count = 1; /* not refcounted */ - p->atom_type = JS_ATOM_TYPE_SYMBOL; -#ifdef DUMP_LEAKS - list_add_tail(&p->link, &rt->string_list); -#endif - new_array[0] = p; - rt->atom_count++; - start = 1; - } - rt->atom_size = new_size; - rt->atom_array = new_array; - rt->atom_free_index = start; - for(i = start; i < new_size; i++) { - uint32_t next; - if (i == (new_size - 1)) - next = 0; - else - next = i + 1; - rt->atom_array[i] = atom_set_free(next); - } - } - - if (str) { - if (str->atom_type == 0) { - p = str; - p->atom_type = atom_type; - } else { - p = js_malloc_rt(rt, sizeof(JSString) + - (str->len << str->is_wide_char) + - 1 - str->is_wide_char); - if (unlikely(!p)) - goto fail; - p->header.ref_count = 1; - p->is_wide_char = str->is_wide_char; - p->len = str->len; -#ifdef DUMP_LEAKS - list_add_tail(&p->link, &rt->string_list); -#endif - memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + - 1 - str->is_wide_char); - js_free_string(rt, str); - } - } else { - p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ - if (!p) - return JS_ATOM_NULL; - p->header.ref_count = 1; - p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ - p->len = 0; -#ifdef DUMP_LEAKS - list_add_tail(&p->link, &rt->string_list); -#endif - } - - /* use an already free entry */ - i = rt->atom_free_index; - rt->atom_free_index = atom_get_free(rt->atom_array[i]); - rt->atom_array[i] = p; - - p->hash = h; - p->hash_next = i; /* atom_index */ - p->atom_type = atom_type; - - rt->atom_count++; - - if (atom_type != JS_ATOM_TYPE_SYMBOL) { - p->hash_next = rt->atom_hash[h1]; - rt->atom_hash[h1] = i; - if (unlikely(rt->atom_count >= rt->atom_count_resize)) - JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); - } - - // JS_DumpAtoms(rt); - return i; - - fail: - i = JS_ATOM_NULL; - done: - if (str) - js_free_string(rt, str); - return i; -} - -/* only works with zero terminated 8 bit strings */ -static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, - int atom_type) -{ - JSString *p; - p = js_alloc_string_rt(rt, len, 0); - if (!p) - return JS_ATOM_NULL; - memcpy(p->u.str8, str, len); - p->u.str8[len] = '\0'; - return __JS_NewAtom(rt, p, atom_type); -} - -/* Warning: str must be ASCII only */ -static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, - int atom_type) -{ - uint32_t h, h1, i; - JSAtomStruct *p; - - h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); - h &= JS_ATOM_HASH_MASK; - h1 = h & (rt->atom_hash_size - 1); - i = rt->atom_hash[h1]; - while (i != 0) { - p = rt->atom_array[i]; - if (p->hash == h && - p->atom_type == JS_ATOM_TYPE_STRING && - p->len == len && - p->is_wide_char == 0 && - memcmp(p->u.str8, str, len) == 0) { - if (!__JS_AtomIsConst(i)) - p->header.ref_count++; - return i; - } - i = p->hash_next; - } - return JS_ATOM_NULL; -} - -static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) -{ -#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ - if (unlikely(i == JS_ATOM_NULL)) { - p->header.ref_count = INT32_MAX / 2; - return; - } -#endif - uint32_t i = p->hash_next; /* atom_index */ - if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { - JSAtomStruct *p0, *p1; - uint32_t h0; - - h0 = p->hash & (rt->atom_hash_size - 1); - i = rt->atom_hash[h0]; - p1 = rt->atom_array[i]; - if (p1 == p) { - rt->atom_hash[h0] = p1->hash_next; - } else { - for(;;) { - assert(i != 0); - p0 = p1; - i = p1->hash_next; - p1 = rt->atom_array[i]; - if (p1 == p) { - p0->hash_next = p1->hash_next; - break; - } - } - } - } - /* insert in free atom list */ - rt->atom_array[i] = atom_set_free(rt->atom_free_index); - rt->atom_free_index = i; - /* free the string structure */ -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - rt->atom_count--; - assert(rt->atom_count >= 0); -} - -static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) -{ - JSAtomStruct *p; - - p = rt->atom_array[i]; - if (--p->header.ref_count > 0) - return; - JS_FreeAtomStruct(rt, p); -} - -/* Warning: 'p' is freed */ -static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) -{ - JSRuntime *rt = ctx->rt; - uint32_t n; - if (is_num_string(&n, p)) { - if (n <= JS_ATOM_MAX_INT) { - js_free_string(rt, p); - return __JS_AtomFromUInt32(n); - } - } - /* XXX: should generate an exception */ - return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); -} - -/* str is UTF-8 encoded */ -JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) -{ - JSValue val; - - if (len == 0 || !is_digit(*str)) { - // XXX: this will not work if UTF-8 encoded str contains non ASCII bytes - JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); - if (atom) - return atom; - } - val = JS_NewStringLen(ctx, str, len); - if (JS_IsException(val)) - return JS_ATOM_NULL; - return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); -} - -JSAtom JS_NewAtom(JSContext *ctx, const char *str) -{ - return JS_NewAtomLen(ctx, str, strlen(str)); -} - -JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) -{ - if (n <= JS_ATOM_MAX_INT) { - return __JS_AtomFromUInt32(n); - } else { - char buf[11]; - JSValue val; - snprintf(buf, sizeof(buf), "%u", n); - val = JS_NewString(ctx, buf); - if (JS_IsException(val)) - return JS_ATOM_NULL; - return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), - JS_ATOM_TYPE_STRING); - } -} - -static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) -{ - if ((uint64_t)n <= JS_ATOM_MAX_INT) { - return __JS_AtomFromUInt32((uint32_t)n); - } else { - char buf[24]; - JSValue val; - snprintf(buf, sizeof(buf), "%" PRId64 , n); - val = JS_NewString(ctx, buf); - if (JS_IsException(val)) - return JS_ATOM_NULL; - return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), - JS_ATOM_TYPE_STRING); - } -} - -/* 'p' is freed */ -static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) -{ - JSRuntime *rt = ctx->rt; - JSAtom atom; - atom = __JS_NewAtom(rt, p, atom_type); - if (atom == JS_ATOM_NULL) - return JS_ThrowOutOfMemory(ctx); - return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); -} - -/* descr must be a non-numeric string atom */ -static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, - int atom_type) -{ - JSRuntime *rt = ctx->rt; - JSString *p; - - assert(!__JS_AtomIsTaggedInt(descr)); - assert(descr < rt->atom_size); - p = rt->atom_array[descr]; - JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - return JS_NewSymbol(ctx, p, atom_type); -} - -#define ATOM_GET_STR_BUF_SIZE 64 - -/* Should only be used for debug. */ -static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, - JSAtom atom) -{ - if (__JS_AtomIsTaggedInt(atom)) { - snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); - } else { - JSAtomStruct *p; - assert(atom < rt->atom_size); - if (atom == JS_ATOM_NULL) { - snprintf(buf, buf_size, "<null>"); - } else { - int i, c; - char *q; - JSString *str; - - q = buf; - p = rt->atom_array[atom]; - assert(!atom_is_free(p)); - str = p; - if (str) { - if (!str->is_wide_char) { - /* special case ASCII strings */ - c = 0; - for(i = 0; i < str->len; i++) { - c |= str->u.str8[i]; - } - if (c < 0x80) - return (const char *)str->u.str8; - } - for(i = 0; i < str->len; i++) { - c = string_get(str, i); - if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) - break; - if (c < 128) { - *q++ = c; - } else { - q += unicode_to_utf8((uint8_t *)q, c); - } - } - } - *q = '\0'; - } - } - return buf; -} - -static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) -{ - return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); -} - -static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - - if (__JS_AtomIsTaggedInt(atom)) { - snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); - return JS_NewString(ctx, buf); - } else { - JSRuntime *rt = ctx->rt; - JSAtomStruct *p; - assert(atom < rt->atom_size); - p = rt->atom_array[atom]; - if (p->atom_type == JS_ATOM_TYPE_STRING) { - goto ret_string; - } else if (force_string) { - if (p->len == 0 && p->is_wide_char != 0) { - /* no description string */ - p = rt->atom_array[JS_ATOM_empty_string]; - } - ret_string: - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - } else { - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p)); - } - } -} - -JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) -{ - return __JS_AtomToValue(ctx, atom, FALSE); -} - -JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) -{ - return __JS_AtomToValue(ctx, atom, TRUE); -} - -/* return TRUE if the atom is an array index (i.e. 0 <= index <= - 2^32-2 and return its value */ -static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) -{ - if (__JS_AtomIsTaggedInt(atom)) { - *pval = __JS_AtomToUInt32(atom); - return TRUE; - } else { - JSRuntime *rt = ctx->rt; - JSAtomStruct *p; - uint32_t val; - - assert(atom < rt->atom_size); - p = rt->atom_array[atom]; - if (p->atom_type == JS_ATOM_TYPE_STRING && - is_num_string(&val, p) && val != -1) { - *pval = val; - return TRUE; - } else { - *pval = 0; - return FALSE; - } - } -} - -/* This test must be fast if atom is not a numeric index (e.g. a - method name). Return JS_UNDEFINED if not a numeric - index. JS_EXCEPTION can also be returned. */ -static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) -{ - JSRuntime *rt = ctx->rt; - JSAtomStruct *p1; - JSString *p; - int c, len, ret; - JSValue num, str; - - if (__JS_AtomIsTaggedInt(atom)) - return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); - assert(atom < rt->atom_size); - p1 = rt->atom_array[atom]; - if (p1->atom_type != JS_ATOM_TYPE_STRING) - return JS_UNDEFINED; - p = p1; - len = p->len; - if (p->is_wide_char) { - const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; - if (r >= r_end) - return JS_UNDEFINED; - c = *r; - if (c == '-') { - if (r >= r_end) - return JS_UNDEFINED; - r++; - c = *r; - /* -0 case is specific */ - if (c == '0' && len == 2) - goto minus_zero; - } - /* XXX: should test NaN, but the tests do not check it */ - if (!is_num(c)) { - /* XXX: String should be normalized, therefore 8-bit only */ - const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; - if (!(c =='I' && (r_end - r) == 8 && - !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) - return JS_UNDEFINED; - } - } else { - const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; - if (r >= r_end) - return JS_UNDEFINED; - c = *r; - if (c == '-') { - if (r >= r_end) - return JS_UNDEFINED; - r++; - c = *r; - /* -0 case is specific */ - if (c == '0' && len == 2) { - minus_zero: - return __JS_NewFloat64(ctx, -0.0); - } - } - if (!is_num(c)) { - if (!(c =='I' && (r_end - r) == 8 && - !memcmp(r + 1, "nfinity", 7))) - return JS_UNDEFINED; - } - } - /* XXX: bignum: would be better to only accept integer to avoid - relying on current floating point precision */ - /* this is ECMA CanonicalNumericIndexString primitive */ - num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); - if (JS_IsException(num)) - return num; - str = JS_ToString(ctx, num); - if (JS_IsException(str)) { - JS_FreeValue(ctx, num); - return str; - } - ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); - JS_FreeValue(ctx, str); - if (ret == 0) { - return num; - } else { - JS_FreeValue(ctx, num); - return JS_UNDEFINED; - } -} - -/* return -1 if exception or TRUE/FALSE */ -static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) -{ - JSValue num; - num = JS_AtomIsNumericIndex1(ctx, atom); - if (likely(JS_IsUndefined(num))) - return FALSE; - if (JS_IsException(num)) - return -1; - JS_FreeValue(ctx, num); - return TRUE; -} - -void JS_FreeAtom(JSContext *ctx, JSAtom v) -{ - if (!__JS_AtomIsConst(v)) - __JS_FreeAtom(ctx->rt, v); -} - -void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) -{ - if (!__JS_AtomIsConst(v)) - __JS_FreeAtom(rt, v); -} - -/* return TRUE if 'v' is a symbol with a string description */ -static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) -{ - JSRuntime *rt; - JSAtomStruct *p; - - rt = ctx->rt; - if (__JS_AtomIsTaggedInt(v)) - return FALSE; - p = rt->atom_array[v]; - return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && - p->hash == JS_ATOM_HASH_SYMBOL) || - p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && - !(p->len == 0 && p->is_wide_char != 0)); -} - -static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - const char *p; - int i; - - /* XXX: should handle embedded null characters */ - /* XXX: should move encoding code to JS_AtomGetStr */ - p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); - for (i = 0; p[i]; i++) { - int c = (unsigned char)p[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || - (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) - break; - } - if (i > 0 && p[i] == '\0') { - printf("%s", p); - } else { - putchar('"'); - printf("%.*s", i, p); - for (; p[i]; i++) { - int c = (unsigned char)p[i]; - if (c == '\"' || c == '\\') { - putchar('\\'); - putchar(c); - } else if (c >= ' ' && c <= 126) { - putchar(c); - } else if (c == '\n') { - putchar('\\'); - putchar('n'); - } else { - printf("\\u%04x", c); - } - } - putchar('\"'); - } -} - -/* free with JS_FreeCString() */ -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) -{ - JSValue str; - const char *cstr; - - str = JS_AtomToString(ctx, atom); - if (JS_IsException(str)) - return NULL; - cstr = JS_ToCString(ctx, str); - JS_FreeValue(ctx, str); - return cstr; -} - -/* return a string atom containing name concatenated with str1 */ -static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) -{ - JSValue str; - JSAtom atom; - const char *cstr; - char *cstr2; - size_t len, len1; - - str = JS_AtomToString(ctx, name); - if (JS_IsException(str)) - return JS_ATOM_NULL; - cstr = JS_ToCStringLen(ctx, &len, str); - if (!cstr) - goto fail; - len1 = strlen(str1); - cstr2 = js_malloc(ctx, len + len1 + 1); - if (!cstr2) - goto fail; - memcpy(cstr2, cstr, len); - memcpy(cstr2 + len, str1, len1); - cstr2[len + len1] = '\0'; - atom = JS_NewAtomLen(ctx, cstr2, len + len1); - js_free(ctx, cstr2); - JS_FreeCString(ctx, cstr); - JS_FreeValue(ctx, str); - return atom; - fail: - JS_FreeCString(ctx, cstr); - JS_FreeValue(ctx, str); - return JS_ATOM_NULL; -} - -static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) -{ - char buf[16]; - snprintf(buf, sizeof(buf), "%u", n); - return js_atom_concat_str(ctx, name, buf); -} - -static inline BOOL JS_IsEmptyString(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; -} - -/* JSClass support */ - -#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; -#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 && - rt->class_array[class_id].class_id != 0); -} - -/* create a new object internal class. Return -1 if error, 0 if - OK. The finalizer can be NULL if none is needed. */ -static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, - const JSClassDef *class_def, JSAtom name) -{ - int new_size, i; - JSClass *cl, *new_class_array; - struct list_head *el; - - if (class_id >= (1 << 16)) - return -1; - if (class_id < rt->class_count && - rt->class_array[class_id].class_id != 0) - return -1; - - if (class_id >= rt->class_count) { - new_size = max_int(JS_CLASS_INIT_COUNT, - max_int(class_id + 1, rt->class_count * 3 / 2)); - - /* reallocate the context class prototype array, if any */ - list_for_each(el, &rt->context_list) { - JSContext *ctx = list_entry(el, JSContext, link); - JSValue *new_tab; - new_tab = js_realloc_rt(rt, ctx->class_proto, - sizeof(ctx->class_proto[0]) * new_size); - if (!new_tab) - return -1; - for(i = rt->class_count; i < new_size; i++) - new_tab[i] = JS_NULL; - ctx->class_proto = new_tab; - } - /* reallocate the class array */ - new_class_array = js_realloc_rt(rt, rt->class_array, - sizeof(JSClass) * new_size); - if (!new_class_array) - return -1; - memset(new_class_array + rt->class_count, 0, - (new_size - rt->class_count) * sizeof(JSClass)); - rt->class_array = new_class_array; - rt->class_count = new_size; - } - cl = &rt->class_array[class_id]; - cl->class_id = class_id; - cl->class_name = JS_DupAtomRT(rt, name); - cl->finalizer = class_def->finalizer; - cl->gc_mark = class_def->gc_mark; - cl->call = class_def->call; - cl->exotic = class_def->exotic; - cl->can_destroy = class_def->can_destroy; - return 0; -} - -int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) -{ - int ret, len; - JSAtom name; - - len = strlen(class_def->class_name); - name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); - if (name == JS_ATOM_NULL) { - name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); - if (name == JS_ATOM_NULL) - return -1; - } - ret = JS_NewClass1(rt, class_id, class_def, name); - JS_FreeAtomRT(rt, name); - return ret; -} - -static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) -{ - JSString *str; - - if (len <= 0) { - return JS_AtomToString(ctx, JS_ATOM_empty_string); - } - str = js_alloc_string(ctx, len, 0); - if (!str) - return JS_EXCEPTION; - memcpy(str->u.str8, buf, len); - str->u.str8[len] = '\0'; - return JS_MKPTR(JS_TAG_STRING, str); -} - -static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len) -{ - JSString *str; - str = js_alloc_string(ctx, len, 1); - if (!str) - return JS_EXCEPTION; - memcpy(str->u.str16, buf, len * 2); - return JS_MKPTR(JS_TAG_STRING, str); -} - -static JSValue js_new_string_char(JSContext *ctx, uint16_t c) -{ - if (c < 0x100) { - uint8_t ch8 = c; - return js_new_string8(ctx, &ch8, 1); - } else { - uint16_t ch16 = c; - return js_new_string16(ctx, &ch16, 1); - } -} - -static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) -{ - int len = end - start; - if (start == 0 && end == p->len) { - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); - } - if (p->is_wide_char && len > 0) { - JSString *str; - int i; - uint16_t c = 0; - for (i = start; i < end; i++) { - c |= p->u.str16[i]; - } - if (c > 0xFF) - return js_new_string16(ctx, p->u.str16 + start, len); - - str = js_alloc_string(ctx, len, 0); - if (!str) - return JS_EXCEPTION; - for (i = 0; i < len; i++) { - str->u.str8[i] = p->u.str16[start + i]; - } - str->u.str8[len] = '\0'; - return JS_MKPTR(JS_TAG_STRING, str); - } else { - return js_new_string8(ctx, p->u.str8 + start, len); - } -} - -typedef struct StringBuffer { - JSContext *ctx; - JSString *str; - int len; - int size; - int is_wide_char; - int error_status; -} StringBuffer; - -/* It is valid to call string_buffer_end() and all string_buffer functions even - if string_buffer_init() or another string_buffer function returns an error. - If the error_status is set, string_buffer_end() returns JS_EXCEPTION. - */ -static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, - int is_wide) -{ - s->ctx = ctx; - s->size = size; - s->len = 0; - s->is_wide_char = is_wide; - s->error_status = 0; - s->str = js_alloc_string(ctx, size, is_wide); - if (unlikely(!s->str)) { - s->size = 0; - return s->error_status = -1; - } -#ifdef DUMP_LEAKS - /* the StringBuffer may reallocate the JSString, only link it at the end */ - list_del(&s->str->link); -#endif - return 0; -} - -static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) -{ - return string_buffer_init2(ctx, s, size, 0); -} - -static void string_buffer_free(StringBuffer *s) -{ - js_free(s->ctx, s->str); - s->str = NULL; -} - -static int string_buffer_set_error(StringBuffer *s) -{ - js_free(s->ctx, s->str); - s->str = NULL; - s->size = 0; - s->len = 0; - return s->error_status = -1; -} - -static no_inline int string_buffer_widen(StringBuffer *s, int size) -{ - JSString *str; - size_t slack; - int i; - - if (s->error_status) - return -1; - - str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); - if (!str) - return string_buffer_set_error(s); - size += slack >> 1; - for(i = s->len; i-- > 0;) { - str->u.str16[i] = str->u.str8[i]; - } - s->is_wide_char = 1; - s->size = size; - s->str = str; - return 0; -} - -static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) -{ - JSString *new_str; - int new_size; - size_t new_size_bytes, slack; - - if (s->error_status) - return -1; - - if (new_len > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(s->ctx, "string too long"); - return string_buffer_set_error(s); - } - new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); - if (!s->is_wide_char && c >= 0x100) { - return string_buffer_widen(s, new_size); - } - new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; - new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); - if (!new_str) - return string_buffer_set_error(s); - new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); - s->size = new_size; - s->str = new_str; - return 0; -} - -static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) -{ - if (unlikely(s->len >= s->size)) { - if (string_buffer_realloc(s, s->len + 1, c)) - return -1; - } - if (s->is_wide_char) { - s->str->u.str16[s->len++] = c; - } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; - } else { - if (string_buffer_widen(s, s->size)) - return -1; - s->str->u.str16[s->len++] = c; - } - return 0; -} - -/* 0 <= c <= 0xff */ -static int string_buffer_putc8(StringBuffer *s, uint32_t c) -{ - if (unlikely(s->len >= s->size)) { - if (string_buffer_realloc(s, s->len + 1, c)) - return -1; - } - if (s->is_wide_char) { - s->str->u.str16[s->len++] = c; - } else { - s->str->u.str8[s->len++] = c; - } - return 0; -} - -/* 0 <= c <= 0xffff */ -static int string_buffer_putc16(StringBuffer *s, uint32_t c) -{ - if (likely(s->len < s->size)) { - if (s->is_wide_char) { - s->str->u.str16[s->len++] = c; - return 0; - } else if (c < 0x100) { - s->str->u.str8[s->len++] = c; - return 0; - } - } - return string_buffer_putc_slow(s, c); -} - -/* 0 <= c <= 0x10ffff */ -static int string_buffer_putc(StringBuffer *s, uint32_t c) -{ - if (unlikely(c >= 0x10000)) { - /* surrogate pair */ - if (string_buffer_putc16(s, get_hi_surrogate(c))) - return -1; - c = get_lo_surrogate(c); - } - return string_buffer_putc16(s, c); -} - -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 (is_hi_surrogate(c) && idx < p->len) { - c1 = p->u.str16[idx]; - if (is_lo_surrogate(c1)) { - c = from_surrogate(c, c1); - idx++; - } - } - } else { - c = p->u.str8[idx++]; - } - *pidx = idx; - return c; -} - -static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) -{ - int i; - - if (s->len + len > s->size) { - if (string_buffer_realloc(s, s->len + len, 0)) - return -1; - } - if (s->is_wide_char) { - for (i = 0; i < len; i++) { - s->str->u.str16[s->len + i] = p[i]; - } - s->len += len; - } else { - memcpy(&s->str->u.str8[s->len], p, len); - s->len += len; - } - return 0; -} - -static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) -{ - int c = 0, i; - - for (i = 0; i < len; i++) { - c |= p[i]; - } - if (s->len + len > s->size) { - if (string_buffer_realloc(s, s->len + len, c)) - return -1; - } else if (!s->is_wide_char && c >= 0x100) { - if (string_buffer_widen(s, s->size)) - return -1; - } - if (s->is_wide_char) { - memcpy(&s->str->u.str16[s->len], p, len << 1); - s->len += len; - } else { - for (i = 0; i < len; i++) { - s->str->u.str8[s->len + i] = p[i]; - } - s->len += len; - } - return 0; -} - -/* appending an ASCII string */ -static int string_buffer_puts8(StringBuffer *s, const char *str) -{ - return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); -} - -static int string_buffer_concat(StringBuffer *s, const JSString *p, - uint32_t from, uint32_t to) -{ - if (to <= from) - return 0; - if (p->is_wide_char) - return string_buffer_write16(s, p->u.str16 + from, to - from); - else - return string_buffer_write8(s, p->u.str8 + from, to - from); -} - -static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) -{ - JSString *p; - JSValue v1; - int res; - - if (s->error_status) { - /* prevent exception overload */ - return -1; - } - if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { - v1 = JS_ToString(s->ctx, v); - if (JS_IsException(v1)) - return string_buffer_set_error(s); - p = JS_VALUE_GET_STRING(v1); - res = string_buffer_concat(s, p, 0, p->len); - JS_FreeValue(s->ctx, v1); - return res; - } - p = JS_VALUE_GET_STRING(v); - return string_buffer_concat(s, p, 0, p->len); -} - -static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) -{ - JSString *p; - int res; - - if (s->error_status) { - /* prevent exception overload */ - JS_FreeValue(s->ctx, v); - return -1; - } - if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { - v = JS_ToStringFree(s->ctx, v); - if (JS_IsException(v)) - return string_buffer_set_error(s); - } - p = JS_VALUE_GET_STRING(v); - res = string_buffer_concat(s, p, 0, p->len); - JS_FreeValue(s->ctx, v); - return res; -} - -static int string_buffer_fill(StringBuffer *s, int c, int count) -{ - /* XXX: optimize */ - if (s->len + count > s->size) { - if (string_buffer_realloc(s, s->len + count, c)) - return -1; - } - while (count-- > 0) { - if (string_buffer_putc16(s, c)) - return -1; - } - return 0; -} - -static JSValue string_buffer_end(StringBuffer *s) -{ - JSString *str; - str = s->str; - if (s->error_status) - return JS_EXCEPTION; - if (s->len == 0) { - js_free(s->ctx, str); - s->str = NULL; - return JS_AtomToString(s->ctx, JS_ATOM_empty_string); - } - if (s->len < s->size) { - /* smaller size so js_realloc should not fail, but OK if it does */ - /* XXX: should add some slack to avoid unnecessary calls */ - /* XXX: might need to use malloc+free to ensure smaller size */ - str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + - (s->len << s->is_wide_char) + 1 - s->is_wide_char); - if (str == NULL) - str = s->str; - s->str = str; - } - if (!s->is_wide_char) - str->u.str8[s->len] = 0; -#ifdef DUMP_LEAKS - list_add_tail(&str->link, &s->ctx->rt->string_list); -#endif - str->is_wide_char = s->is_wide_char; - str->len = s->len; - s->str = NULL; - return JS_MKPTR(JS_TAG_STRING, str); -} - -/* create a string from a UTF-8 buffer */ -JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) -{ - const uint8_t *p, *p_end, *p_start, *p_next; - uint32_t c; - StringBuffer b_s, *b = &b_s; - size_t len1; - - p_start = (const uint8_t *)buf; - p_end = p_start + buf_len; - p = p_start; - while (p < p_end && *p < 128) - p++; - len1 = p - p_start; - if (len1 > JS_STRING_LEN_MAX) - return JS_ThrowInternalError(ctx, "string too long"); - if (p == p_end) { - /* ASCII string */ - return js_new_string8(ctx, (const uint8_t *)buf, buf_len); - } else { - if (string_buffer_init(ctx, b, buf_len)) - goto fail; - string_buffer_write8(b, p_start, len1); - while (p < p_end) { - if (*p < 128) { - string_buffer_putc8(b, *p++); - } else { - /* parse utf-8 sequence, return 0xFFFFFFFF for error */ - c = unicode_from_utf8(p, p_end - p, &p_next); - if (c < 0x10000) { - p = p_next; - } else if (c <= 0x10FFFF) { - p = p_next; - /* surrogate pair */ - string_buffer_putc16(b, get_hi_surrogate(c)); - c = get_lo_surrogate(c); - } else { - /* invalid char */ - c = 0xfffd; - /* skip the invalid chars */ - /* XXX: seems incorrect. Why not just use c = *p++; ? */ - while (p < p_end && (*p >= 0x80 && *p < 0xc0)) - p++; - if (p < p_end) { - p++; - while (p < p_end && (*p >= 0x80 && *p < 0xc0)) - p++; - } - } - string_buffer_putc16(b, c); - } - } - } - return string_buffer_end(b); - - fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, - JSValue str2, const char *str3) -{ - StringBuffer b_s, *b = &b_s; - int len1, len3; - JSString *p; - - if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { - str2 = JS_ToStringFree(ctx, str2); - if (JS_IsException(str2)) - goto fail; - } - p = JS_VALUE_GET_STRING(str2); - len1 = strlen(str1); - len3 = strlen(str3); - - if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) - goto fail; - - string_buffer_write8(b, (const uint8_t *)str1, len1); - string_buffer_concat(b, p, 0, p->len); - string_buffer_write8(b, (const uint8_t *)str3, len3); - - JS_FreeValue(ctx, str2); - return string_buffer_end(b); - - fail: - JS_FreeValue(ctx, str2); - return JS_EXCEPTION; -} - -JSValue JS_NewString(JSContext *ctx, const char *str) -{ - return JS_NewStringLen(ctx, str, strlen(str)); -} - -JSValue JS_NewAtomString(JSContext *ctx, const char *str) -{ - JSAtom atom = JS_NewAtom(ctx, str); - if (atom == JS_ATOM_NULL) - return JS_EXCEPTION; - JSValue val = JS_AtomToString(ctx, atom); - JS_FreeAtom(ctx, atom); - return val; -} - -/* return (NULL, 0) if exception. */ -/* return pointer into a JSString with a live ref_count */ -/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ -const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8) -{ - JSValue val; - JSString *str, *str_new; - int pos, len, c, c1; - uint8_t *q; - - if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { - val = JS_ToString(ctx, val1); - if (JS_IsException(val)) - goto fail; - } else { - val = JS_DupValue(ctx, val1); - } - - str = JS_VALUE_GET_STRING(val); - len = str->len; - if (!str->is_wide_char) { - const uint8_t *src = str->u.str8; - int count; - - /* count the number of non-ASCII characters */ - /* Scanning the whole string is required for ASCII strings, - and computing the number of non-ASCII bytes is less expensive - than testing each byte, hence this method is faster for ASCII - strings, which is the most common case. - */ - count = 0; - for (pos = 0; pos < len; pos++) { - count += src[pos] >> 7; - } - if (count == 0) { - if (plen) - *plen = len; - return (const char *)src; - } - str_new = js_alloc_string(ctx, len + count, 0); - if (!str_new) - goto fail; - q = str_new->u.str8; - for (pos = 0; pos < len; pos++) { - c = src[pos]; - if (c < 0x80) { - *q++ = c; - } else { - *q++ = (c >> 6) | 0xc0; - *q++ = (c & 0x3f) | 0x80; - } - } - } else { - const uint16_t *src = str->u.str16; - /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may - produce 4 bytes but use 2 code points. - */ - str_new = js_alloc_string(ctx, len * 3, 0); - if (!str_new) - goto fail; - q = str_new->u.str8; - pos = 0; - while (pos < len) { - c = src[pos++]; - if (c < 0x80) { - *q++ = c; - } else { - if (is_hi_surrogate(c)) { - if (pos < len && !cesu8) { - c1 = src[pos]; - if (is_lo_surrogate(c1)) { - pos++; - c = from_surrogate(c, c1); - } else { - /* Keep unmatched surrogate code points */ - /* c = 0xfffd; */ /* error */ - } - } else { - /* Keep unmatched surrogate code points */ - /* c = 0xfffd; */ /* error */ - } - } - q += unicode_to_utf8(q, c); - } - } - } - - *q = '\0'; - str_new->len = q - str_new->u.str8; - JS_FreeValue(ctx, val); - if (plen) - *plen = str_new->len; - return (const char *)str_new->u.str8; - fail: - if (plen) - *plen = 0; - return NULL; -} - -void JS_FreeCString(JSContext *ctx, const char *ptr) -{ - JSString *p; - if (!ptr) - return; - /* purposely removing constness */ - p = container_of(ptr, JSString, u); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); -} - -/* create a string from a narrow Unicode (latin-1) buffer */ -JSValue JS_NewNarrowStringLen(JSContext *ctx, const char *buf, size_t buf_len) -{ - if (buf_len > JS_STRING_LEN_MAX) - return JS_ThrowInternalError(ctx, "string too long"); - return js_new_string8(ctx, (const uint8_t *)buf, buf_len); -} - -JS_BOOL JS_IsStringWideChar(JSValue value) -{ - if (unlikely(JS_VALUE_GET_TAG(value) != JS_TAG_STRING)) - return FALSE; - return JS_VALUE_GET_STRING(value)->is_wide_char; -} - -uint8_t *JS_GetNarrowStringBuffer(JSValue value) -{ - if (unlikely(JS_VALUE_GET_TAG(value) != JS_TAG_STRING)) - return NULL; - return JS_VALUE_GET_STRING(value)->u.str8; -} - -uint32_t JS_GetStringLength(JSValue value) -{ - if (unlikely(JS_VALUE_GET_TAG(value) != JS_TAG_STRING)) - return 0; - return JS_VALUE_GET_STRING(value)->len; -} - -static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) -{ - int c, i; - for(i = 0; i < len; i++) { - c = src1[i] - src2[i]; - if (c != 0) - return c; - } - return 0; -} - -static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) -{ - int c, i; - for(i = 0; i < len; i++) { - c = src1[i] - src2[i]; - if (c != 0) - return c; - } - return 0; -} - -static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) -{ - int res; - - if (likely(!p1->is_wide_char)) { - if (likely(!p2->is_wide_char)) - res = memcmp(p1->u.str8, p2->u.str8, len); - else - res = -memcmp16_8(p2->u.str16, p1->u.str8, len); - } else { - if (!p2->is_wide_char) - res = memcmp16_8(p1->u.str16, p2->u.str8, len); - else - res = memcmp16(p1->u.str16, p2->u.str16, len); - } - return res; -} - -/* return < 0, 0 or > 0 */ -static int js_string_compare(JSContext *ctx, - const JSString *p1, const JSString *p2) -{ - int res, len; - len = min_int(p1->len, p2->len); - res = js_string_memcmp(p1, p2, len); - if (res == 0) { - if (p1->len == p2->len) - res = 0; - else if (p1->len < p2->len) - res = -1; - else - res = 1; - } - return res; -} - -static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) -{ - if (p->is_wide_char) { - memcpy(dst, p->u.str16 + offset, len * 2); - } else { - const uint8_t *src1 = p->u.str8 + offset; - int i; - - for(i = 0; i < len; i++) - dst[i] = src1[i]; - } -} - -static JSValue JS_ConcatString1(JSContext *ctx, - const JSString *p1, const JSString *p2) -{ - JSString *p; - uint32_t len; - int is_wide_char; - - len = p1->len + p2->len; - if (len > JS_STRING_LEN_MAX) - return JS_ThrowInternalError(ctx, "string too long"); - is_wide_char = p1->is_wide_char | p2->is_wide_char; - p = js_alloc_string(ctx, len, is_wide_char); - if (!p) - return JS_EXCEPTION; - if (!is_wide_char) { - memcpy(p->u.str8, p1->u.str8, p1->len); - memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); - p->u.str8[len] = '\0'; - } else { - copy_str16(p->u.str16, p1, 0, p1->len); - copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); - } - return JS_MKPTR(JS_TAG_STRING, p); -} - -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; -} - -/* 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)) { - op1 = JS_ToStringFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - } - if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { - op2 = JS_ToStringFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - } - p1 = JS_VALUE_GET_STRING(op1); - 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; -} - -/* Shape support */ - -static inline size_t get_shape_size(size_t hash_size, size_t prop_size) -{ - return hash_size * sizeof(uint32_t) + sizeof(JSShape) + - prop_size * sizeof(JSShapeProperty); -} - -static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) -{ - return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); -} - -static inline uint32_t *prop_hash_end(JSShape *sh) -{ - return (uint32_t *)sh; -} - -static inline void *get_alloc_from_shape(JSShape *sh) -{ - return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); -} - -static inline JSShapeProperty *get_shape_prop(JSShape *sh) -{ - return sh->prop; -} - -static int init_shape_hash(JSRuntime *rt) -{ - rt->shape_hash_bits = 4; /* 16 shapes */ - rt->shape_hash_size = 1 << rt->shape_hash_bits; - rt->shape_hash_count = 0; - rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * - rt->shape_hash_size); - if (!rt->shape_hash) - return -1; - return 0; -} - -/* same magic hash multiplier as the Linux kernel */ -static uint32_t shape_hash(uint32_t h, uint32_t val) -{ - return (h + val) * 0x9e370001; -} - -/* truncate the shape hash to 'hash_bits' bits */ -static uint32_t get_shape_hash(uint32_t h, int hash_bits) -{ - return h >> (32 - hash_bits); -} - -static uint32_t shape_initial_hash(JSObject *proto) -{ - uint32_t h; - h = shape_hash(1, (uintptr_t)proto); - if (sizeof(proto) > 4) - h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); - return h; -} - -static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) -{ - int new_shape_hash_size, i; - uint32_t h; - JSShape **new_shape_hash, *sh, *sh_next; - - new_shape_hash_size = 1 << new_shape_hash_bits; - new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * - new_shape_hash_size); - if (!new_shape_hash) - return -1; - for(i = 0; i < rt->shape_hash_size; i++) { - for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { - sh_next = sh->shape_hash_next; - h = get_shape_hash(sh->hash, new_shape_hash_bits); - sh->shape_hash_next = new_shape_hash[h]; - new_shape_hash[h] = sh; - } - } - js_free_rt(rt, rt->shape_hash); - rt->shape_hash_bits = new_shape_hash_bits; - rt->shape_hash_size = new_shape_hash_size; - rt->shape_hash = new_shape_hash; - return 0; -} - -static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) -{ - uint32_t h; - h = get_shape_hash(sh->hash, rt->shape_hash_bits); - sh->shape_hash_next = rt->shape_hash[h]; - rt->shape_hash[h] = sh; - rt->shape_hash_count++; -} - -static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) -{ - uint32_t h; - JSShape **psh; - - h = get_shape_hash(sh->hash, rt->shape_hash_bits); - psh = &rt->shape_hash[h]; - while (*psh != sh) - psh = &(*psh)->shape_hash_next; - *psh = sh->shape_hash_next; - rt->shape_hash_count--; -} - -/* create a new empty shape with prototype 'proto' */ -static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, - int hash_size, int prop_size) -{ - JSRuntime *rt = ctx->rt; - void *sh_alloc; - JSShape *sh; - - /* resize the shape hash table if necessary */ - if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { - resize_shape_hash(rt, rt->shape_hash_bits + 1); - } - - sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); - if (!sh_alloc) - return NULL; - sh = get_shape_from_alloc(sh_alloc, hash_size); - sh->header.ref_count = 1; - add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); - if (proto) - JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); - sh->proto = proto; - memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * - hash_size); - sh->prop_hash_mask = hash_size - 1; - sh->prop_size = prop_size; - sh->prop_count = 0; - sh->deleted_prop_count = 0; - - /* insert in the hash table */ - sh->hash = shape_initial_hash(proto); - sh->is_hashed = TRUE; - sh->has_small_array_index = FALSE; - js_shape_hash_link(ctx->rt, sh); - return sh; -} - -static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) -{ - return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, - JS_PROP_INITIAL_SIZE); -} - -/* The shape is cloned. The new shape is not inserted in the shape - hash table */ -static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) -{ - JSShape *sh; - void *sh_alloc, *sh_alloc1; - size_t size; - JSShapeProperty *pr; - uint32_t i, hash_size; - - hash_size = sh1->prop_hash_mask + 1; - size = get_shape_size(hash_size, sh1->prop_size); - sh_alloc = js_malloc(ctx, size); - if (!sh_alloc) - return NULL; - sh_alloc1 = get_alloc_from_shape(sh1); - memcpy(sh_alloc, sh_alloc1, size); - sh = get_shape_from_alloc(sh_alloc, hash_size); - sh->header.ref_count = 1; - add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); - sh->is_hashed = FALSE; - if (sh->proto) { - JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); - } - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { - JS_DupAtom(ctx, pr->atom); - } - return sh; -} - -static JSShape *js_dup_shape(JSShape *sh) -{ - sh->header.ref_count++; - return sh; -} - -static void js_free_shape0(JSRuntime *rt, JSShape *sh) -{ - uint32_t i; - JSShapeProperty *pr; - - assert(sh->header.ref_count == 0); - if (sh->is_hashed) - js_shape_hash_unlink(rt, sh); - if (sh->proto != NULL) { - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); - } - pr = get_shape_prop(sh); - for(i = 0; i < sh->prop_count; i++) { - JS_FreeAtomRT(rt, pr->atom); - pr++; - } - remove_gc_object(&sh->header); - js_free_rt(rt, get_alloc_from_shape(sh)); -} - -static void js_free_shape(JSRuntime *rt, JSShape *sh) -{ - if (unlikely(--sh->header.ref_count <= 0)) { - js_free_shape0(rt, sh); - } -} - -static void js_free_shape_null(JSRuntime *rt, JSShape *sh) -{ - if (sh) - js_free_shape(rt, sh); -} - -/* make space to hold at least 'count' properties */ -static no_inline int resize_properties(JSContext *ctx, JSShape **psh, - JSObject *p, uint32_t count) -{ - JSShape *sh; - uint32_t new_size, new_hash_size, new_hash_mask, i; - JSShapeProperty *pr; - void *sh_alloc; - intptr_t h; - JSShape *old_sh; - - sh = *psh; - new_size = max_int(count, sh->prop_size * 3 / 2); - /* Reallocate prop array first to avoid crash or size inconsistency - in case of memory allocation failure */ - if (p) { - JSProperty *new_prop; - new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); - if (unlikely(!new_prop)) - return -1; - p->prop = new_prop; - } - new_hash_size = sh->prop_hash_mask + 1; - while (new_hash_size < new_size) - new_hash_size = 2 * new_hash_size; - /* 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)) { - /* resize the hash table and the properties */ - new_hash_mask = new_hash_size - 1; - sh->prop_hash_mask = new_hash_mask; - memset(prop_hash_end(sh) - new_hash_size, 0, - sizeof(prop_hash_end(sh)[0]) * new_hash_size); - for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { - if (pr->atom != JS_ATOM_NULL) { - h = ((uintptr_t)pr->atom & new_hash_mask); - pr->hash_next = prop_hash_end(sh)[-h - 1]; - prop_hash_end(sh)[-h - 1] = i + 1; - } - } - } else { - /* 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; -} - -/* remove the deleted properties. */ -static int compact_properties(JSContext *ctx, JSObject *p) -{ - JSShape *sh, *old_sh; - void *sh_alloc; - intptr_t h; - uint32_t new_hash_size, i, j, new_hash_mask, new_size; - JSShapeProperty *old_pr, *pr; - JSProperty *prop, *new_prop; - - sh = p->shape; - assert(!sh->is_hashed); - - new_size = max_int(JS_PROP_INITIAL_SIZE, - sh->prop_count - sh->deleted_prop_count); - assert(new_size <= sh->prop_size); - - new_hash_size = sh->prop_hash_mask + 1; - while ((new_hash_size / 2) >= new_size) - new_hash_size = new_hash_size / 2; - new_hash_mask = new_hash_size - 1; - - /* resize the hash table and the properties */ - old_sh = sh; - sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); - if (!sh_alloc) - return -1; - sh = get_shape_from_alloc(sh_alloc, new_hash_size); - list_del(&old_sh->header.link); - memcpy(sh, old_sh, sizeof(JSShape)); - list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - - memset(prop_hash_end(sh) - new_hash_size, 0, - sizeof(prop_hash_end(sh)[0]) * new_hash_size); - - j = 0; - old_pr = old_sh->prop; - pr = sh->prop; - prop = p->prop; - for(i = 0; i < sh->prop_count; i++) { - if (old_pr->atom != JS_ATOM_NULL) { - pr->atom = old_pr->atom; - pr->flags = old_pr->flags; - h = ((uintptr_t)old_pr->atom & new_hash_mask); - pr->hash_next = prop_hash_end(sh)[-h - 1]; - prop_hash_end(sh)[-h - 1] = j + 1; - prop[j] = prop[i]; - j++; - pr++; - } - old_pr++; - } - assert(j == (sh->prop_count - sh->deleted_prop_count)); - sh->prop_hash_mask = new_hash_mask; - sh->prop_size = new_size; - sh->deleted_prop_count = 0; - sh->prop_count = j; - - p->shape = sh; - js_free(ctx, get_alloc_from_shape(old_sh)); - - /* reduce the size of the object properties */ - new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); - if (new_prop) - p->prop = new_prop; - return 0; -} - -static int add_shape_property(JSContext *ctx, JSShape **psh, - JSObject *p, JSAtom atom, int prop_flags) -{ - JSRuntime *rt = ctx->rt; - JSShape *sh = *psh; - JSShapeProperty *pr, *prop; - uint32_t hash_mask, new_shape_hash = 0; - intptr_t h; - - /* update the shape hash */ - if (sh->is_hashed) { - js_shape_hash_unlink(rt, sh); - new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); - } - - if (unlikely(sh->prop_count >= sh->prop_size)) { - if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { - /* in case of error, reinsert in the hash table. - sh is still valid if resize_properties() failed */ - if (sh->is_hashed) - js_shape_hash_link(rt, sh); - return -1; - } - sh = *psh; - } - if (sh->is_hashed) { - sh->hash = new_shape_hash; - js_shape_hash_link(rt, sh); - } - /* Initialize the new shape property. - The object property at p->prop[sh->prop_count] is uninitialized */ - prop = get_shape_prop(sh); - pr = &prop[sh->prop_count++]; - pr->atom = JS_DupAtom(ctx, atom); - pr->flags = prop_flags; - sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); - /* add in hash table */ - hash_mask = sh->prop_hash_mask; - h = atom & hash_mask; - pr->hash_next = prop_hash_end(sh)[-h - 1]; - prop_hash_end(sh)[-h - 1] = sh->prop_count; - return 0; -} - -/* find a hashed empty shape matching the prototype. Return NULL if - not found */ -static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) -{ - JSShape *sh1; - uint32_t h, h1; - - h = shape_initial_hash(proto); - h1 = get_shape_hash(h, rt->shape_hash_bits); - for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { - if (sh1->hash == h && - sh1->proto == proto && - sh1->prop_count == 0) { - return sh1; - } - } - return NULL; -} - -/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if - not found */ -static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, - JSAtom atom, int prop_flags) -{ - JSShape *sh1; - uint32_t h, h1, i, n; - - h = sh->hash; - h = shape_hash(h, atom); - h = shape_hash(h, prop_flags); - h1 = get_shape_hash(h, rt->shape_hash_bits); - for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { - /* we test the hash first so that the rest is done only if the - shapes really match */ - if (sh1->hash == h && - sh1->proto == sh->proto && - sh1->prop_count == ((n = sh->prop_count) + 1)) { - for(i = 0; i < n; i++) { - if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || - unlikely(sh1->prop[i].flags != sh->prop[i].flags)) - goto next; - } - if (unlikely(sh1->prop[n].atom != atom) || - unlikely(sh1->prop[n].flags != prop_flags)) - goto next; - return sh1; - } - next: ; - } - return NULL; -} - -static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) -{ - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - int j; - - /* XXX: should output readable class prototype */ - printf("%5d %3d%c %14p %5d %5d", i, - sh->header.ref_count, " *"[sh->is_hashed], - (void *)sh->proto, sh->prop_size, sh->prop_count); - for(j = 0; j < sh->prop_count; j++) { - printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), - sh->prop[j].atom)); - } - printf("\n"); -} - -static __maybe_unused void JS_DumpShapes(JSRuntime *rt) -{ - int i; - JSShape *sh; - struct list_head *el; - JSObject *p; - JSGCObjectHeader *gp; - - printf("JSShapes: {\n"); - printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); - for(i = 0; i < rt->shape_hash_size; i++) { - for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { - JS_DumpShape(rt, i, sh); - assert(sh->is_hashed); - } - } - /* dump non-hashed shapes */ - list_for_each(el, &rt->gc_obj_list) { - gp = list_entry(el, JSGCObjectHeader, link); - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - p = (JSObject *)gp; - if (!p->shape->is_hashed) { - JS_DumpShape(rt, -1, p->shape); - } - } - } - printf("}\n"); -} - -static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) -{ - JSObject *p; - - js_trigger_gc(ctx->rt, sizeof(JSObject)); - p = js_malloc(ctx, sizeof(JSObject)); - if (unlikely(!p)) - goto fail; - p->class_id = class_id; - p->extensible = TRUE; - p->free_mark = 0; - p->is_exotic = 0; - p->fast_array = 0; - p->is_constructor = 0; - p->is_uncatchable_error = 0; - p->tmp_mark = 0; - p->is_HTMLDDA = 0; - p->first_weak_ref = NULL; - p->u.opaque = NULL; - p->shape = sh; - p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); - if (unlikely(!p->prop)) { - js_free(ctx, p); - fail: - js_free_shape(ctx->rt, sh); - return JS_EXCEPTION; - } - - switch(class_id) { - case JS_CLASS_OBJECT: - break; - case JS_CLASS_ARRAY: - { - JSProperty *pr; - p->is_exotic = 1; - p->fast_array = 1; - p->u.array.u.values = NULL; - p->u.array.count = 0; - p->u.array.u1.size = 0; - /* the length property is always the first one */ - if (likely(sh == ctx->array_shape)) { - pr = &p->prop[0]; - } else { - /* only used for the first array */ - /* cannot fail */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_LENGTH); - } - pr->u.value = JS_NewInt32(ctx, 0); - } - break; - case JS_CLASS_C_FUNCTION: - p->prop[0].u.value = JS_UNDEFINED; - break; - case JS_CLASS_ARGUMENTS: - case JS_CLASS_UINT8C_ARRAY: - 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: - p->is_exotic = 1; - p->fast_array = 1; - p->u.array.u.ptr = NULL; - p->u.array.count = 0; - break; - case JS_CLASS_DATAVIEW: - p->u.array.u.ptr = NULL; - p->u.array.count = 0; - break; - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: - case JS_CLASS_SYMBOL: - case JS_CLASS_DATE: - 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: - p->u.regexp.pattern = NULL; - p->u.regexp.bytecode = NULL; - goto set_exotic; - default: - set_exotic: - if (ctx->rt->class_array[class_id].exotic) { - p->is_exotic = 1; - } - break; - } - p->header.ref_count = 1; - add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); - return JS_MKPTR(JS_TAG_OBJECT, p); -} - -static JSObject *get_proto_obj(JSValueConst proto_val) -{ - if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) - return NULL; - else - return JS_VALUE_GET_OBJ(proto_val); -} - -/* WARNING: proto must be an object or JS_NULL */ -JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, - JSClassID class_id) -{ - JSShape *sh; - JSObject *proto; - - proto = get_proto_obj(proto_val); - sh = find_hashed_shape_proto(ctx->rt, proto); - if (likely(sh)) { - sh = js_dup_shape(sh); - } else { - sh = js_new_shape(ctx, proto); - if (!sh) - return JS_EXCEPTION; - } - return JS_NewObjectFromShape(ctx, sh, class_id); -} - -#if 0 -static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - switch(p->class_id) { - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: - case JS_CLASS_SYMBOL: - case JS_CLASS_DATE: - 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); - } - } - return JS_UNDEFINED; -} -#endif - -static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) -{ - JSObject *p; - - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - switch(p->class_id) { - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: - case JS_CLASS_SYMBOL: - case JS_CLASS_DATE: - 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; - return 0; - } - } - JS_FreeValue(ctx, val); - if (!JS_IsException(obj)) - JS_ThrowTypeError(ctx, "invalid object type"); - return -1; -} - -JSValue JS_NewObjectClass(JSContext *ctx, int class_id) -{ - return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); -} - -JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) -{ - return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); -} - -JSValue JS_NewArray(JSContext *ctx) -{ - return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), - JS_CLASS_ARRAY); -} - -JSValue JS_NewObject(JSContext *ctx) -{ - /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ - return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); -} - -static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int len) -{ - /* ES6 feature non compatible with ES5.1: length is configurable */ - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), - JS_PROP_CONFIGURABLE); - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, - JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); -} - -static BOOL js_class_has_bytecode(JSClassID class_id) -{ - return (class_id == JS_CLASS_BYTECODE_FUNCTION || - class_id == JS_CLASS_GENERATOR_FUNCTION || - class_id == JS_CLASS_ASYNC_FUNCTION || - class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); -} - -/* return NULL without exception if not a function or no bytecode */ -static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return NULL; - p = JS_VALUE_GET_OBJ(val); - if (!js_class_has_bytecode(p->class_id)) - return NULL; - return p->u.func.function_bytecode; -} - -static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, - JSValueConst home_obj) -{ - JSObject *p, *p1; - JSFunctionBytecode *b; - - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(func_obj); - if (!js_class_has_bytecode(p->class_id)) - return; - b = p->u.func.function_bytecode; - if (b->need_home_object) { - p1 = p->u.func.home_object; - if (p1) { - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) - p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); - else - p1 = NULL; - p->u.func.home_object = p1; - } -} - -static JSValue js_get_function_name(JSContext *ctx, JSAtom name) -{ - JSValue name_str; - - name_str = JS_AtomToString(ctx, name); - if (JS_AtomSymbolHasDescription(ctx, name)) { - name_str = JS_ConcatString3(ctx, "[", name_str, "]"); - } - return name_str; -} - -/* Modify the name of a method according to the atom and - 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and - JS_PROP_HAS_SET. Also set the home object of the method. - Return < 0 if exception. */ -static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int flags, JSValueConst home_obj) -{ - JSValue name_str; - - name_str = js_get_function_name(ctx, name); - if (flags & JS_PROP_HAS_GET) { - name_str = JS_ConcatString3(ctx, "get ", name_str, ""); - } else if (flags & JS_PROP_HAS_SET) { - name_str = JS_ConcatString3(ctx, "set ", name_str, ""); - } - if (JS_IsException(name_str)) - return -1; - if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, - JS_PROP_CONFIGURABLE) < 0) - return -1; - js_method_set_home_object(ctx, func_obj, home_obj); - return 0; -} - -/* Note: at least 'length' arguments will be readable in 'argv' */ -static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic, - JSValueConst proto_val) -{ - JSValue func_obj; - JSObject *p; - JSAtom name_atom; - - func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); - if (JS_IsException(func_obj)) - return func_obj; - p = JS_VALUE_GET_OBJ(func_obj); - p->u.cfunc.realm = JS_DupContext(ctx); - p->u.cfunc.c_function.generic = func; - p->u.cfunc.length = length; - p->u.cfunc.cproto = cproto; - p->u.cfunc.magic = magic; - p->is_constructor = (cproto == JS_CFUNC_constructor || - cproto == JS_CFUNC_constructor_magic || - cproto == JS_CFUNC_constructor_or_func || - cproto == JS_CFUNC_constructor_or_func_magic); - if (!name) - name = ""; - name_atom = JS_NewAtom(ctx, name); - js_function_set_properties(ctx, func_obj, name_atom, length); - JS_FreeAtom(ctx, name_atom); - return func_obj; -} - -/* Note: at least 'length' arguments will be readable in 'argv' */ -JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic) -{ - return JS_NewCFunction3(ctx, func, name, length, cproto, magic, - ctx->function_proto); -} - -typedef struct JSCFunctionDataRecord { - JSCFunctionData *func; - uint8_t length; - uint8_t data_len; - uint16_t magic; - JSValue data[0]; -} JSCFunctionDataRecord; - -static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) -{ - JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); - int i; - - if (s) { - for(i = 0; i < s->data_len; i++) { - JS_FreeValueRT(rt, s->data[i]); - } - js_free_rt(rt, s); - } -} - -static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); - int i; - - if (s) { - for(i = 0; i < s->data_len; i++) { - JS_MarkValue(rt, s->data[i], mark_func); - } - } -} - -static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv, int flags) -{ - JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); - JSValueConst *arg_buf; - int i; - - /* XXX: could add the function on the stack for debug */ - if (unlikely(argc < s->length)) { - arg_buf = alloca(sizeof(arg_buf[0]) * s->length); - for(i = 0; i < argc; i++) - arg_buf[i] = argv[i]; - for(i = argc; i < s->length; i++) - arg_buf[i] = JS_UNDEFINED; - } else { - arg_buf = argv; - } - - return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); -} - -JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, - int length, int magic, int data_len, - JSValueConst *data) -{ - JSCFunctionDataRecord *s; - JSValue func_obj; - int i; - - func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_C_FUNCTION_DATA); - if (JS_IsException(func_obj)) - return func_obj; - s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); - if (!s) { - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; - } - s->func = func; - s->length = length; - s->data_len = data_len; - s->magic = magic; - for(i = 0; i < data_len; i++) - s->data[i] = JS_DupValue(ctx, data[i]); - JS_SetOpaque(func_obj, s); - js_function_set_properties(ctx, func_obj, - JS_ATOM_empty_string, length); - return func_obj; -} - -static JSContext *js_autoinit_get_realm(JSProperty *pr) -{ - return (JSContext *)(pr->u.init.realm_and_id & ~3); -} - -static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) -{ - return pr->u.init.realm_and_id & 3; -} - -static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) -{ - JS_FreeContext(js_autoinit_get_realm(pr)); -} - -static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, - JS_MarkFunc *mark_func) -{ - mark_func(rt, &js_autoinit_get_realm(pr)->header); -} - -static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) -{ - if (unlikely(prop_flags & JS_PROP_TMASK)) { - if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (pr->u.getset.getter) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - free_var_ref(rt, pr->u.var_ref); - } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - js_autoinit_free(rt, pr); - } - } else { - JS_FreeValueRT(rt, pr->u.value); - } -} - -static force_inline JSShapeProperty *find_own_property1(JSObject *p, - JSAtom atom) -{ - JSShape *sh; - JSShapeProperty *pr, *prop; - intptr_t h; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - return pr; - } - h = pr->hash_next; - } - return NULL; -} - -static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, - JSObject *p, - JSAtom atom) -{ - JSShape *sh; - JSShapeProperty *pr, *prop; - intptr_t h; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - *ppr = &p->prop[h - 1]; - /* the compiler should be able to assume that pr != NULL here */ - return pr; - } - h = pr->hash_next; - } - *ppr = NULL; - return NULL; -} - -/* indicate that the object may be part of a function prototype cycle */ -static void set_cycle_flag(JSContext *ctx, JSValueConst obj) -{ -} - -static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) -{ - if (var_ref) { - assert(var_ref->header.ref_count > 0); - if (--var_ref->header.ref_count == 0) { - if (var_ref->is_detached) { - JS_FreeValueRT(rt, var_ref->value); - } else { - 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); - } - } -} - -static void js_array_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - int i; - - for(i = 0; i < p->u.array.count; i++) { - JS_FreeValueRT(rt, p->u.array.u.values[i]); - } - js_free_rt(rt, p->u.array.u.values); -} - -static void js_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - int i; - - for(i = 0; i < p->u.array.count; i++) { - JS_MarkValue(rt, p->u.array.u.values[i], mark_func); - } -} - -static void js_object_data_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JS_FreeValueRT(rt, p->u.object_data); - p->u.object_data = JS_UNDEFINED; -} - -static void js_object_data_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JS_MarkValue(rt, p->u.object_data, mark_func); -} - -static void js_c_function_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - - if (p->u.cfunc.realm) - JS_FreeContext(p->u.cfunc.realm); -} - -static void js_c_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - - if (p->u.cfunc.realm) - mark_func(rt, &p->u.cfunc.realm->header); -} - -static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p1, *p = JS_VALUE_GET_OBJ(val); - JSFunctionBytecode *b; - JSVarRef **var_refs; - int i; - - p1 = p->u.func.home_object; - if (p1) { - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - b = p->u.func.function_bytecode; - if (b) { - var_refs = p->u.func.var_refs; - if (var_refs) { - for(i = 0; i < b->closure_var_count; i++) - free_var_ref(rt, var_refs[i]); - js_free_rt(rt, var_refs); - } - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); - } -} - -static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSVarRef **var_refs = p->u.func.var_refs; - JSFunctionBytecode *b = p->u.func.function_bytecode; - int i; - - if (p->u.func.home_object) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), - mark_func); - } - if (b) { - if (var_refs) { - for(i = 0; i < b->closure_var_count; i++) { - JSVarRef *var_ref = var_refs[i]; - if (var_ref) { - mark_func(rt, &var_ref->header); - } - } - } - /* must mark the function bytecode because template objects may be - part of a cycle */ - JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); - } -} - -static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSBoundFunction *bf = p->u.bound_function; - int i; - - JS_FreeValueRT(rt, bf->func_obj); - JS_FreeValueRT(rt, bf->this_val); - for(i = 0; i < bf->argc; i++) { - JS_FreeValueRT(rt, bf->argv[i]); - } - js_free_rt(rt, bf); -} - -static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSBoundFunction *bf = p->u.bound_function; - int i; - - JS_MarkValue(rt, bf->func_obj, mark_func); - JS_MarkValue(rt, bf->this_val, mark_func); - for(i = 0; i < bf->argc; i++) - JS_MarkValue(rt, bf->argv[i], mark_func); -} - -static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSForInIterator *it = p->u.for_in_iterator; - 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); -} - -static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSForInIterator *it = p->u.for_in_iterator; - JS_MarkValue(rt, it->obj, mark_func); -} - -static void free_object(JSRuntime *rt, JSObject *p) -{ - int i; - JSClassFinalizer *finalizer; - JSShape *sh; - JSShapeProperty *pr; - - p->free_mark = 1; /* used to tell the object is invalid when - freeing cycles */ - /* free all the fields */ - sh = p->shape; - pr = get_shape_prop(sh); - for(i = 0; i < sh->prop_count; i++) { - free_property(rt, &p->prop[i], pr->flags); - pr++; - } - js_free_rt(rt, p->prop); - /* as an optimization we destroy the shape immediately without - putting it in gc_zero_ref_count_list */ - js_free_shape(rt, sh); - - /* fail safe */ - p->shape = NULL; - p->prop = NULL; - - if (unlikely(p->first_weak_ref)) { - reset_weak_ref(rt, p); - } - - finalizer = rt->class_array[p->class_id].finalizer; - if (finalizer) - (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); - - /* fail safe */ - p->class_id = 0; - p->u.opaque = NULL; - p->u.func.var_refs = NULL; - p->u.func.home_object = NULL; - - remove_gc_object(&p->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { - list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); - } else { - js_free_rt(rt, p); - } -} - -static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) -{ - switch(gp->gc_obj_type) { - case JS_GC_OBJ_TYPE_JS_OBJECT: - free_object(rt, (JSObject *)gp); - break; - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - free_function_bytecode(rt, (JSFunctionBytecode *)gp); - break; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - __async_func_free(rt, (JSAsyncFunctionState *)gp); - break; - default: - abort(); - } -} - -/* Check if object has a can_destroy hook. */ -static int gc_has_can_destroy_hook(JSRuntime *rt, JSGCObjectHeader *p) -{ - JSObject *obj; - - if (p->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) - return 0; - obj = (JSObject *)p; - return rt->class_array[obj->class_id].can_destroy != NULL; -} - -/* User-defined override for object destruction. */ -static int gc_can_destroy(JSRuntime *rt, JSGCObjectHeader *p) -{ - JSClassCanDestroy *can_destroy; - JSObject *obj; - - obj = (JSObject *)p; - can_destroy = rt->class_array[obj->class_id].can_destroy; - if (!((*can_destroy)(rt, JS_MKPTR(JS_TAG_OBJECT, obj)))) - return 0; - return 1; -} - -static void free_zero_refcount(JSRuntime *rt) -{ - struct list_head *el; - JSGCObjectHeader *p; - - rt->gc_phase = JS_GC_PHASE_DECREF; - for(;;) { - el = rt->gc_zero_ref_count_list.next; - if (el == &rt->gc_zero_ref_count_list) - break; - p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count == 0); - free_gc_object(rt, p); - } - rt->gc_phase = JS_GC_PHASE_NONE; -} - -/* called with the ref_count of 'v' reaches zero. */ -void __JS_FreeValueRT(JSRuntime *rt, JSValue v) -{ - uint32_t tag = JS_VALUE_GET_TAG(v); - -#ifdef DUMP_FREE - { - printf("Freeing "); - if (tag == JS_TAG_OBJECT) { - JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); - } else { - JS_DumpValueShort(rt, v); - printf("\n"); - } - } -#endif - - switch(tag) { - case JS_TAG_STRING: - { - JSString *p = JS_VALUE_GET_STRING(v); - if (p->atom_type) { - JS_FreeAtomStruct(rt, p); - } else { -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - } - } - break; - case JS_TAG_OBJECT: - case JS_TAG_FUNCTION_BYTECODE: - { - JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); - if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { - if (gc_has_can_destroy_hook(rt, p) && !gc_can_destroy(rt, p)) { - p->ref_count++; - break; - } - list_del(&p->link); - list_add(&p->link, &rt->gc_zero_ref_count_list); - if (rt->gc_phase == JS_GC_PHASE_NONE) { - free_zero_refcount(rt); - } - } - } - break; - case JS_TAG_MODULE: - abort(); /* never freed here */ - break; - 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); - } - break; -#endif - case JS_TAG_SYMBOL: - { - JSAtomStruct *p = JS_VALUE_GET_PTR(v); - JS_FreeAtomStruct(rt, p); - } - break; - default: - printf("__JS_FreeValue: unknown tag=%d\n", tag); - abort(); - } -} - -void __JS_FreeValue(JSContext *ctx, JSValue v) -{ - __JS_FreeValueRT(ctx->rt, v); -} - -/* garbage collection */ - -static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, - JSGCObjectTypeEnum type) -{ - h->mark = 0; - h->gc_obj_type = type; - list_add_tail(&h->link, &rt->gc_obj_list); -} - -static void remove_gc_object(JSGCObjectHeader *h) -{ - list_del(&h->link); -} - -void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) -{ - if (JS_VALUE_HAS_REF_COUNT(val)) { - switch(JS_VALUE_GET_TAG(val)) { - case JS_TAG_OBJECT: - case JS_TAG_FUNCTION_BYTECODE: - mark_func(rt, JS_VALUE_GET_PTR(val)); - break; - default: - break; - } - } -} - -static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, - JS_MarkFunc *mark_func) -{ - switch(gp->gc_obj_type) { - case JS_GC_OBJ_TYPE_JS_OBJECT: - { - JSObject *p = (JSObject *)gp; - JSShapeProperty *prs; - JSShape *sh; - int i; - sh = p->shape; - mark_func(rt, &sh->header); - /* mark all the fields */ - prs = get_shape_prop(sh); - for(i = 0; i < sh->prop_count; i++) { - JSProperty *pr = &p->prop[i]; - if (prs->atom != JS_ATOM_NULL) { - if (prs->flags & JS_PROP_TMASK) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (pr->u.getset.getter) - mark_func(rt, &pr->u.getset.getter->header); - if (pr->u.getset.setter) - mark_func(rt, &pr->u.getset.setter->header); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - /* Note: the tag does not matter - provided it is a GC object */ - mark_func(rt, &pr->u.var_ref->header); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - js_autoinit_mark(rt, pr, mark_func); - } - } else { - JS_MarkValue(rt, pr->u.value, mark_func); - } - } - prs++; - } - - if (p->class_id != JS_CLASS_OBJECT) { - JSClassGCMark *gc_mark; - gc_mark = rt->class_array[p->class_id].gc_mark; - if (gc_mark) - gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); - } - } - break; - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - /* the template objects can be part of a cycle */ - { - JSFunctionBytecode *b = (JSFunctionBytecode *)gp; - int i; - for(i = 0; i < b->cpool_count; i++) { - JS_MarkValue(rt, b->cpool[i], mark_func); - } - if (b->realm) - mark_func(rt, &b->realm->header); - } - break; - case JS_GC_OBJ_TYPE_VAR_REF: - { - JSVarRef *var_ref = (JSVarRef *)gp; - 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: - { - 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); - } - break; - case JS_GC_OBJ_TYPE_SHAPE: - { - JSShape *sh = (JSShape *)gp; - if (sh->proto != NULL) { - mark_func(rt, &sh->proto->header); - } - } - break; - case JS_GC_OBJ_TYPE_JS_CONTEXT: - { - JSContext *ctx = (JSContext *)gp; - JS_MarkContext(rt, ctx, mark_func); - } - break; - default: - abort(); - } -} - -static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) -{ - assert(p->ref_count > 0); - p->ref_count--; - if (p->ref_count == 0 && p->mark == 1) { - list_del(&p->link); - if (gc_has_can_destroy_hook(rt, p)) - list_add_tail(&p->link, &rt->tmp_hook_obj_list); - else - list_add_tail(&p->link, &rt->tmp_obj_list); - } -} - -static void gc_decref(JSRuntime *rt) -{ - struct list_head *el, *el1; - JSGCObjectHeader *p; - - init_list_head(&rt->tmp_obj_list); - init_list_head(&rt->tmp_hook_obj_list); - - /* decrement the refcount of all the children of all the GC - objects and move the GC objects with zero refcount to - tmp_obj_list */ - list_for_each_safe(el, el1, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->mark == 0); - mark_children(rt, p, gc_decref_child); - p->mark = 1; - if (p->ref_count == 0) { - list_del(&p->link); - if (gc_has_can_destroy_hook(rt, p)) - list_add_tail(&p->link, &rt->tmp_hook_obj_list); - else - list_add_tail(&p->link, &rt->tmp_obj_list); - } - } -} - -static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) -{ - p->ref_count++; - if (p->ref_count == 1) { - /* ref_count was 0: remove from tmp_obj_list and add at the - end of gc_obj_list */ - list_del(&p->link); - list_add_tail(&p->link, &rt->gc_obj_list); - p->mark = 0; /* reset the mark for the next GC call */ - } -} - -static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) -{ - p->ref_count++; -} - -static void gc_scan(JSRuntime *rt) -{ - struct list_head *el, *el1, *gc_tail; - JSGCObjectHeader *p; - int redo; - - /* keep the objects with a refcount > 0 and their children. */ - list_for_each(el, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count > 0); - p->mark = 0; /* reset the mark for the next GC call */ - mark_children(rt, p, gc_scan_incref_child); - } - - /* restore objects whose can_destroy hook returns 0 and their children. */ - do { - /* save previous tail position of gc_obj_list */ - gc_tail = rt->gc_obj_list.prev; - redo = 0; - list_for_each_safe(el, el1, &rt->tmp_hook_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - list_del(&p->link); - /* gc_has_can_destroy_hook is the condition for objects to be - placed in tmp_hook_obj_list, so it is true here. */ - if (gc_can_destroy(rt, p)) { - /* object can be destroyed; move to tmp_obj_list. */ - list_add_tail(&p->link, &rt->tmp_obj_list); - } else { - /* hook says we cannot destroy yet; move back to gc_obj_list. */ - p->ref_count++; - list_add_tail(&p->link, &rt->gc_obj_list); - redo = 1; - break; - } - } - /* if redo, restore object and all its descendants. - Note: we must do this outside the previous loop, because el/el1 - might get moved into gc_obj_list here. */ - for (el = gc_tail->next; el != &rt->gc_obj_list; el = el->next) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count > 0); - p->mark = 0; /* reset the mark for the next GC call */ - mark_children(rt, p, gc_scan_incref_child); - } - } while(redo); - - /* restore the refcount of the objects to be deleted. */ - list_for_each(el, &rt->tmp_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - mark_children(rt, p, gc_scan_incref_child2); - } -} - -static void gc_free_cycles(JSRuntime *rt) -{ - struct list_head *el, *el1; - JSGCObjectHeader *p; -#ifdef DUMP_GC_FREE - BOOL header_done = FALSE; -#endif - - rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; - - for(;;) { - el = rt->tmp_obj_list.next; - if (el == &rt->tmp_obj_list) - break; - p = list_entry(el, JSGCObjectHeader, link); - /* Only need to free the GC object associated with JS values - 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"); - JS_DumpObjectHeader(rt); - header_done = TRUE; - } - JS_DumpGCObject(rt, p); -#endif - free_gc_object(rt, p); - break; - default: - list_del(&p->link); - list_add_tail(&p->link, &rt->gc_zero_ref_count_list); - break; - } - } - rt->gc_phase = JS_GC_PHASE_NONE; - - list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || - p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || - p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION); - js_free_rt(rt, p); - } - - init_list_head(&rt->gc_zero_ref_count_list); -} - -void JS_RunGC(JSRuntime *rt) -{ - /* decrement the reference of the children of each object. mark = - 1 after this pass. */ - gc_decref(rt); - - /* keep the GC objects with a non zero refcount and their childs */ - gc_scan(rt); - - /* free the GC objects in a cycle */ - gc_free_cycles(rt); -} - -/* Return false if not an object or if the object has already been - freed (zombie objects are visible in finalizers when freeing - cycles). */ -BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) -{ - JSObject *p; - if (!JS_IsObject(obj)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - return !p->free_mark; -} - -/* Compute memory used by various object types */ -/* XXX: poor man's approach to handling multiply referenced objects */ -typedef struct JSMemoryUsage_helper { - double memory_used_count; - double str_count; - double str_size; - int64_t js_func_count; - double js_func_size; - int64_t js_func_code_size; - int64_t js_func_pc2line_count; - int64_t js_func_pc2line_size; -} JSMemoryUsage_helper; - -static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp); - -static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) -{ - if (!str->atom_type) { /* atoms are handled separately */ - double s_ref_count = str->header.ref_count; - hp->str_count += 1 / s_ref_count; - hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + - 1 - str->is_wide_char) / s_ref_count); - } -} - -static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) -{ - int memory_used_count, js_func_size, i; - - memory_used_count = 0; - js_func_size = offsetof(JSFunctionBytecode, debug); - if (b->vardefs) { - js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); - } - if (b->cpool) { - js_func_size += b->cpool_count * sizeof(*b->cpool); - for (i = 0; i < b->cpool_count; i++) { - JSValueConst val = b->cpool[i]; - compute_value_size(val, hp); - } - } - if (b->closure_var) { - js_func_size += b->closure_var_count * sizeof(*b->closure_var); - } - if (!b->read_only_bytecode && b->byte_code_buf) { - hp->js_func_code_size += b->byte_code_len; - } - if (b->has_debug) { - js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); - if (b->debug.source) { - memory_used_count++; - js_func_size += b->debug.source_len + 1; - } - if (b->debug.pc2line_len) { - memory_used_count++; - hp->js_func_pc2line_count += 1; - hp->js_func_pc2line_size += b->debug.pc2line_len; - } - } - hp->js_func_size += js_func_size; - hp->js_func_count += 1; - hp->memory_used_count += memory_used_count; -} - -static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) -{ - switch(JS_VALUE_GET_TAG(val)) { - case JS_TAG_STRING: - compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); - break; - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: -#endif - /* should track JSBigFloat usage */ - break; - } -} - -void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) -{ - struct list_head *el, *el1; - int i; - JSMemoryUsage_helper mem = { 0 }, *hp = &mem; - - memset(s, 0, sizeof(*s)); - s->malloc_count = rt->malloc_state.malloc_count; - s->malloc_size = rt->malloc_state.malloc_size; - s->malloc_limit = rt->malloc_state.malloc_limit; - - s->memory_used_count = 2; /* rt + rt->class_array */ - s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count; - - list_for_each(el, &rt->context_list) { - JSContext *ctx = list_entry(el, JSContext, link); - JSShape *sh = ctx->array_shape; - s->memory_used_count += 2; /* ctx + ctx->class_proto */ - s->memory_used_size += sizeof(JSContext) + - sizeof(JSValue) * rt->class_count; - s->binary_object_count += ctx->binary_object_count; - s->binary_object_size += ctx->binary_object_size; - - /* the hashed shapes are counted separately */ - if (sh && !sh->is_hashed) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size(hash_size, sh->prop_size); - } - list_for_each(el1, &ctx->loaded_modules) { - JSModuleDef *m = list_entry(el1, JSModuleDef, link); - s->memory_used_count += 1; - s->memory_used_size += sizeof(*m); - if (m->req_module_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); - } - if (m->export_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); - for (i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { - /* potential multiple count */ - s->memory_used_count += 1; - compute_value_size(me->u.local.var_ref->value, hp); - } - } - } - if (m->star_export_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); - } - if (m->import_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); - } - compute_value_size(m->module_ns, hp); - compute_value_size(m->func_obj, hp); - } - } - - list_for_each(el, &rt->gc_obj_list) { - JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); - JSObject *p; - JSShape *sh; - JSShapeProperty *prs; - - /* XXX: could count the other GC object types too */ - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { - compute_bytecode_size((JSFunctionBytecode *)gp, hp); - continue; - } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { - continue; - } - p = (JSObject *)gp; - sh = p->shape; - s->obj_count++; - if (p->prop) { - s->memory_used_count++; - s->prop_size += sh->prop_size * sizeof(*p->prop); - s->prop_count += sh->prop_count; - prs = get_shape_prop(sh); - for(i = 0; i < sh->prop_count; i++) { - JSProperty *pr = &p->prop[i]; - if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { - compute_value_size(pr->u.value, hp); - } - prs++; - } - } - /* the hashed shapes are counted separately */ - if (!sh->is_hashed) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size(hash_size, sh->prop_size); - } - - switch(p->class_id) { - case JS_CLASS_ARRAY: /* u.array | length */ - case JS_CLASS_ARGUMENTS: /* u.array | length */ - s->array_count++; - if (p->fast_array) { - s->fast_array_count++; - if (p->u.array.u.values) { - s->memory_used_count++; - s->memory_used_size += p->u.array.count * - sizeof(*p->u.array.u.values); - s->fast_array_elements += p->u.array.count; - for (i = 0; i < p->u.array.count; i++) { - compute_value_size(p->u.array.u.values[i], hp); - } - } - } - break; - case JS_CLASS_NUMBER: /* u.object_data */ - case JS_CLASS_STRING: /* u.object_data */ - case JS_CLASS_BOOLEAN: /* u.object_data */ - case JS_CLASS_SYMBOL: /* u.object_data */ - case JS_CLASS_DATE: /* u.object_data */ - 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 */ - s->c_func_count++; - break; - case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ - { - JSFunctionBytecode *b = p->u.func.function_bytecode; - JSVarRef **var_refs = p->u.func.var_refs; - /* home_object: object will be accounted for in list scan */ - if (var_refs) { - s->memory_used_count++; - s->js_func_size += b->closure_var_count * sizeof(*var_refs); - for (i = 0; i < b->closure_var_count; i++) { - if (var_refs[i]) { - double ref_count = var_refs[i]->header.ref_count; - s->memory_used_count += 1 / ref_count; - s->js_func_size += sizeof(*var_refs[i]) / ref_count; - /* handle non object closed values */ - if (var_refs[i]->pvalue == &var_refs[i]->value) { - /* potential multiple count */ - compute_value_size(var_refs[i]->value, hp); - } - } - } - } - } - break; - case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ - { - JSBoundFunction *bf = p->u.bound_function; - /* func_obj and this_val are objects */ - for (i = 0; i < bf->argc; i++) { - compute_value_size(bf->argv[i], hp); - } - s->memory_used_count += 1; - s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); - } - break; - case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ - { - JSCFunctionDataRecord *fd = p->u.c_function_data_record; - if (fd) { - for (i = 0; i < fd->data_len; i++) { - compute_value_size(fd->data[i], hp); - } - s->memory_used_count += 1; - s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); - } - } - break; - case JS_CLASS_REGEXP: /* u.regexp */ - compute_jsstring_size(p->u.regexp.pattern, hp); - compute_jsstring_size(p->u.regexp.bytecode, hp); - break; - - case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ - { - JSForInIterator *it = p->u.for_in_iterator; - if (it) { - compute_value_size(it->obj, hp); - s->memory_used_count += 1; - s->memory_used_size += sizeof(*it); - } - } - break; - case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ - case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ - { - JSArrayBuffer *abuf = p->u.array_buffer; - if (abuf) { - s->memory_used_count += 1; - s->memory_used_size += sizeof(*abuf); - if (abuf->data) { - s->memory_used_count += 1; - s->memory_used_size += abuf->byte_length; - } - } - } - break; - case JS_CLASS_GENERATOR: /* u.generator_data */ - case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ - 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_FLOAT32_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_DATAVIEW: /* u.typed_array */ -#ifdef CONFIG_BIGNUM - case JS_CLASS_FLOAT_ENV: /* u.float_env */ -#endif - case JS_CLASS_MAP: /* u.map_state */ - case JS_CLASS_SET: /* u.map_state */ - case JS_CLASS_WEAKMAP: /* u.map_state */ - case JS_CLASS_WEAKSET: /* u.map_state */ - case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ - case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ - case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ - case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ - case JS_CLASS_PROXY: /* u.proxy_data */ - case JS_CLASS_PROMISE: /* u.promise_data */ - case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ - case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ - case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ - case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ - case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ - case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ - /* TODO */ - default: - /* XXX: class definition should have an opaque block size */ - if (p->u.opaque) { - s->memory_used_count += 1; - } - break; - } - } - s->obj_size += s->obj_count * sizeof(JSObject); - - /* hashed shapes */ - s->memory_used_count++; /* rt->shape_hash */ - s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; - for(i = 0; i < rt->shape_hash_size; i++) { - JSShape *sh; - for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size(hash_size, sh->prop_size); - } - } - - /* atoms */ - s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ - s->atom_count = rt->atom_count; - s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + - sizeof(rt->atom_hash[0]) * rt->atom_hash_size; - for(i = 0; i < rt->atom_size; i++) { - JSAtomStruct *p = rt->atom_array[i]; - if (!atom_is_free(p)) { - s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + - 1 - p->is_wide_char); - } - } - s->str_count = round(mem.str_count); - s->str_size = round(mem.str_size); - s->js_func_count = mem.js_func_count; - s->js_func_size = round(mem.js_func_size); - s->js_func_code_size = mem.js_func_code_size; - s->js_func_pc2line_count = mem.js_func_pc2line_count; - s->js_func_pc2line_size = mem.js_func_pc2line_size; - s->memory_used_count += round(mem.memory_used_count) + - s->atom_count + s->str_count + - s->obj_count + s->shape_count + - s->js_func_count + s->js_func_pc2line_count; - s->memory_used_size += s->atom_size + s->str_size + - s->obj_size + s->prop_size + s->shape_size + - s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; -} - -void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) -{ - fprintf(fp, "QuickJS memory usage -- " -#ifdef CONFIG_BIGNUM - "BigNum " -#endif - CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", - (int)sizeof(void *) * 8, s->malloc_limit); -#if 1 - if (rt) { - static const struct { - const char *name; - size_t size; - } object_types[] = { - { "JSRuntime", sizeof(JSRuntime) }, - { "JSContext", sizeof(JSContext) }, - { "JSObject", sizeof(JSObject) }, - { "JSString", sizeof(JSString) }, - { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, - }; - int i, usage_size_ok = 0; - for(i = 0; i < countof(object_types); i++) { - unsigned int size = object_types[i].size; - void *p = js_malloc_rt(rt, size); - if (p) { - unsigned int size1 = js_malloc_usable_size_rt(rt, p); - if (size1 >= size) { - usage_size_ok = 1; - fprintf(fp, " %3u + %-2u %s\n", - size, size1 - size, object_types[i].name); - } - js_free_rt(rt, p); - } - } - if (!usage_size_ok) { - fprintf(fp, " malloc_usable_size unavailable\n"); - } - { - int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; - int class_id; - struct list_head *el; - list_for_each(el, &rt->gc_obj_list) { - JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); - JSObject *p; - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - p = (JSObject *)gp; - obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; - } - } - fprintf(fp, "\n" "JSObject classes\n"); - if (obj_classes[0]) - fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); - for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { - if (obj_classes[class_id] && 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), rt->class_array[class_id].class_name)); - } - } - if (obj_classes[JS_CLASS_INIT_COUNT]) - fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); - } - fprintf(fp, "\n"); - } -#endif - fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); - - if (s->malloc_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", - "memory allocated", s->malloc_count, s->malloc_size, - (double)s->malloc_size / s->malloc_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", - "memory used", s->memory_used_count, s->memory_used_size, - MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / - s->memory_used_count)); - } - if (s->atom_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", - "atoms", s->atom_count, s->atom_size, - (double)s->atom_size / s->atom_count); - } - if (s->str_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", - "strings", s->str_count, s->str_size, - (double)s->str_size / s->str_count); - } - if (s->obj_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", - "objects", s->obj_count, s->obj_size, - (double)s->obj_size / s->obj_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", - " properties", s->prop_count, s->prop_size, - (double)s->prop_count / s->obj_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", - " shapes", s->shape_count, s->shape_size, - (double)s->shape_size / s->shape_count); - } - if (s->js_func_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", - "bytecode functions", s->js_func_count, s->js_func_size); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", - " bytecode", s->js_func_count, s->js_func_code_size, - (double)s->js_func_code_size / s->js_func_count); - if (s->js_func_pc2line_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", - " pc2line", s->js_func_pc2line_count, - s->js_func_pc2line_size, - (double)s->js_func_pc2line_size / s->js_func_pc2line_count); - } - } - if (s->c_func_count) { - fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); - } - if (s->array_count) { - fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); - if (s->fast_array_count) { - fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", - " elements", s->fast_array_elements, - s->fast_array_elements * (int)sizeof(JSValue), - (double)s->fast_array_elements / s->fast_array_count); - } - } - if (s->binary_object_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", - "binary objects", s->binary_object_count, s->binary_object_size); - } -} - -JSValue JS_GetGlobalObject(JSContext *ctx) -{ - return JS_DupValue(ctx, ctx->global_obj); -} - -/* WARNING: obj is freed */ -JSValue JS_Throw(JSContext *ctx, JSValue obj) -{ - JSRuntime *rt = ctx->rt; - JS_FreeValue(ctx, rt->current_exception); - rt->current_exception = obj; - return JS_EXCEPTION; -} - -/* return the pending exception (cannot be called twice). */ -JSValue JS_GetException(JSContext *ctx) -{ - JSValue val; - JSRuntime *rt = ctx->rt; - val = rt->current_exception; - rt->current_exception = JS_NULL; - return val; -} - -JS_BOOL JS_HasException(JSContext *ctx) -{ - return !JS_IsNull(ctx->rt->current_exception); -} - -static void dbuf_put_leb128(DynBuf *s, uint32_t v) -{ - uint32_t a; - for(;;) { - a = v & 0x7f; - v >>= 7; - if (v != 0) { - dbuf_putc(s, a | 0x80); - } else { - dbuf_putc(s, a); - break; - } - } -} - -static void dbuf_put_sleb128(DynBuf *s, int32_t v1) -{ - uint32_t v = v1; - dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); -} - -static int get_leb128(uint32_t *pval, const uint8_t *buf, - const uint8_t *buf_end) -{ - const uint8_t *ptr = buf; - uint32_t v, a, i; - v = 0; - for(i = 0; i < 5; i++) { - if (unlikely(ptr >= buf_end)) - break; - a = *ptr++; - v |= (a & 0x7f) << (i * 7); - if (!(a & 0x80)) { - *pval = v; - return ptr - buf; - } - } - *pval = 0; - return -1; -} - -static int get_sleb128(int32_t *pval, const uint8_t *buf, - const uint8_t *buf_end) -{ - int ret; - uint32_t val; - ret = get_leb128(&val, buf, buf_end); - if (ret < 0) { - *pval = 0; - return -1; - } - *pval = (val >> 1) ^ -(val & 1); - return ret; -} - -static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, - uint32_t pc_value) -{ - const uint8_t *p_end, *p; - int new_line_num, line_num, pc, v, ret; - unsigned int op; - - if (!b->has_debug || !b->debug.pc2line_buf) { - /* function was stripped */ - return -1; - } - - p = b->debug.pc2line_buf; - p_end = p + b->debug.pc2line_len; - pc = 0; - line_num = b->debug.line_num; - while (p < p_end) { - op = *p++; - if (op == 0) { - uint32_t val; - ret = get_leb128(&val, p, p_end); - if (ret < 0) - goto fail; - pc += val; - p += ret; - ret = get_sleb128(&v, p, p_end); - if (ret < 0) { - fail: - /* should never happen */ - return b->debug.line_num; - } - p += ret; - new_line_num = line_num + v; - } else { - op -= PC2LINE_OP_FIRST; - pc += (op / PC2LINE_RANGE); - new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; - } - if (pc_value < pc) - return line_num; - line_num = new_line_num; - } - return line_num; -} - -/* in order to avoid executing arbitrary code during the stack trace - generation, we only look at simple 'name' properties containing a - string. */ -static const char *get_func_name(JSContext *ctx, JSValueConst func) -{ - JSProperty *pr; - JSShapeProperty *prs; - JSValueConst val; - - if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) - return NULL; - prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); - if (!prs) - return NULL; - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) - return NULL; - val = pr->u.value; - if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) - return NULL; - return JS_ToCString(ctx, val); -} - -#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) -/* only taken into account if filename is provided */ -#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) - -/* if filename != NULL, an additional level is added with the filename - and line number information (used for parse error). */ -static void build_backtrace(JSContext *ctx, JSValueConst error_obj, - const char *filename, int line_num, - int backtrace_flags) -{ - JSStackFrame *sf; - JSValue str; - DynBuf dbuf; - const char *func_name_str; - const char *str1; - JSObject *p; - BOOL backtrace_barrier; - - js_dbuf_init(ctx, &dbuf); - if (filename) { - dbuf_printf(&dbuf, " at %s", filename); - if (line_num != -1) - dbuf_printf(&dbuf, ":%d", line_num); - dbuf_putc(&dbuf, '\n'); - str = JS_NewString(ctx, filename); - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL) - goto done; - } - for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { - if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { - backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; - continue; - } - func_name_str = get_func_name(ctx, sf->cur_func); - if (!func_name_str || func_name_str[0] == '\0') - str1 = "<anonymous>"; - else - str1 = func_name_str; - dbuf_printf(&dbuf, " at %s", str1); - JS_FreeCString(ctx, func_name_str); - - p = JS_VALUE_GET_OBJ(sf->cur_func); - backtrace_barrier = FALSE; - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b; - const char *atom_str; - int line_num1; - - b = p->u.func.function_bytecode; - backtrace_barrier = b->backtrace_barrier; - if (b->has_debug) { - line_num1 = find_line_num(ctx, b, - sf->cur_pc - b->byte_code_buf - 1); - atom_str = JS_AtomToCString(ctx, b->debug.filename); - dbuf_printf(&dbuf, " (%s", - atom_str ? atom_str : "<null>"); - JS_FreeCString(ctx, atom_str); - if (line_num1 != -1) - dbuf_printf(&dbuf, ":%d", line_num1); - dbuf_putc(&dbuf, ')'); - } - } else { - dbuf_printf(&dbuf, " (native)"); - } - dbuf_putc(&dbuf, '\n'); - /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ - if (backtrace_barrier) - break; - } - done: - dbuf_putc(&dbuf, '\0'); - if (dbuf_error(&dbuf)) - str = JS_NULL; - else - str = JS_NewString(ctx, (char *)dbuf.buf); - dbuf_free(&dbuf); - JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); -} - -/* Note: it is important that no exception is returned by this function */ -static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ERROR) - return FALSE; - if (find_own_property1(p, JS_ATOM_stack)) - return FALSE; - return TRUE; -} - -JSValue JS_NewError(JSContext *ctx) -{ - return JS_NewObjectClass(ctx, JS_CLASS_ERROR); -} - -static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap, BOOL add_backtrace) -{ - char buf[256]; - JSValue obj, ret; - - vsnprintf(buf, sizeof(buf), fmt, ap); - obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], - JS_CLASS_ERROR); - if (unlikely(JS_IsException(obj))) { - /* out of memory: throw JS_NULL to avoid recursing */ - obj = JS_NULL; - } else { - JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, - JS_NewString(ctx, buf), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - } - if (add_backtrace) { - build_backtrace(ctx, obj, NULL, 0, 0); - } - ret = JS_Throw(ctx, obj); - return ret; -} - -static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap) -{ - JSRuntime *rt = ctx->rt; - JSStackFrame *sf; - BOOL add_backtrace; - - /* the backtrace is added later if called from a bytecode function */ - sf = rt->current_stack_frame; - add_backtrace = !rt->in_out_of_memory && - (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); - return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) -{ - va_list ap; - - if ((flags & JS_PROP_THROW) || - ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - va_start(ap, fmt); - JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); - va_end(ap); - return -1; - } else { - return FALSE; - } -} - -/* never use it directly */ -static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowTypeError(ctx, fmt, - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); -} - -/* never use it directly */ -static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowSyntaxError(ctx, fmt, - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); -} - -/* %s is replaced by 'atom'. The macro is used so that gcc can check - the format string. */ -#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") -#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") - -static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) -{ - if ((flags & JS_PROP_THROW) || - ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); - return -1; - } else { - return FALSE; - } -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) -{ - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue JS_ThrowOutOfMemory(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - if (!rt->in_out_of_memory) { - rt->in_out_of_memory = TRUE; - JS_ThrowInternalError(ctx, "out of memory"); - rt->in_out_of_memory = FALSE; - } - return JS_EXCEPTION; -} - -static JSValue JS_ThrowStackOverflow(JSContext *ctx) -{ - return JS_ThrowInternalError(ctx, "stack overflow"); -} - -static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "not an object"); -} - -static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "not a symbol"); -} - -static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowReferenceError(ctx, "'%s' is not defined", - JS_AtomGetStr(ctx, buf, sizeof(buf), name)); -} - -static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowReferenceError(ctx, "%s is not initialized", - name == JS_ATOM_NULL ? "lexical variable" : - JS_AtomGetStr(ctx, buf, sizeof(buf), name)); -} - -static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, - JSFunctionBytecode *b, - int idx, BOOL is_ref) -{ - JSAtom atom = JS_ATOM_NULL; - if (is_ref) { - atom = b->closure_var[idx].var_name; - } else { - /* not present if the function is stripped and contains no eval() */ - if (b->vardefs) - atom = b->vardefs[b->arg_count + idx].var_name; - } - return JS_ThrowReferenceErrorUninitialized(ctx, atom); -} - -static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) -{ - JSRuntime *rt = ctx->rt; - JSAtom name; - name = rt->class_array[class_id].class_name; - return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); -} - -static no_inline __exception int __js_poll_interrupts(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; - if (rt->interrupt_handler) { - if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { - /* XXX: should set a specific flag to avoid catching */ - JS_ThrowInternalError(ctx, "interrupted"); - JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); - return -1; - } - } - return 0; -} - -static inline __exception int js_poll_interrupts(JSContext *ctx) -{ - if (unlikely(--ctx->interrupt_counter <= 0)) { - return __js_poll_interrupts(ctx); - } else { - return 0; - } -} - -/* return -1 (exception) or TRUE/FALSE */ -static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, - BOOL throw_flag) -{ - JSObject *proto, *p, *p1; - JSShape *sh; - - if (throw_flag) { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || - JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) - goto not_obj; - } else { - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - goto not_obj; - } - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { - if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { - not_obj: - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - proto = NULL; - } else { - proto = JS_VALUE_GET_OBJ(proto_val); - } - - if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return TRUE; - - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); - sh = p->shape; - if (sh->proto == proto) - return TRUE; - if (!p->extensible) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "object is not extensible"); - return -1; - } else { - return FALSE; - } - } - if (proto) { - /* check if there is a cycle */ - p1 = proto; - do { - if (p1 == p) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "circular prototype chain"); - return -1; - } else { - return FALSE; - } - } - /* Note: for Proxy objects, proto is NULL */ - p1 = p1->shape->proto; - } while (p1 != NULL); - JS_DupValue(ctx, proto_val); - } - - if (js_shape_prepare_update(ctx, p, NULL)) - return -1; - sh = p->shape; - if (sh->proto) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); - sh->proto = proto; - return TRUE; -} - -/* return -1 (exception) or TRUE/FALSE */ -int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) -{ - return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE); -} - -/* Only works for primitive types, otherwise return JS_NULL. */ -static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) -{ - switch(JS_VALUE_GET_NORM_TAG(val)) { - 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]; - break; - case JS_TAG_BOOL: - val = ctx->class_proto[JS_CLASS_BOOLEAN]; - break; - case JS_TAG_STRING: - val = ctx->class_proto[JS_CLASS_STRING]; - break; - case JS_TAG_SYMBOL: - val = ctx->class_proto[JS_CLASS_SYMBOL]; - break; - case JS_TAG_OBJECT: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - val = JS_NULL; - break; - } - return val; -} - -/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ -JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) -{ - JSValue val; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p; - p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) { - val = js_proxy_getPrototypeOf(ctx, obj); - } else { - p = p->shape->proto; - if (!p) - val = JS_NULL; - else - val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - } - } else { - val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj)); - } - return val; -} - -static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) -{ - JSValue obj1; - obj1 = JS_GetPrototype(ctx, obj); - JS_FreeValue(ctx, obj); - return obj1; -} - -/* return TRUE, FALSE or (-1) in case of exception */ -static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, - JSValueConst obj) -{ - JSValue obj_proto; - JSObject *proto; - const JSObject *p, *proto1; - BOOL ret; - - if (!JS_IsFunction(ctx, obj)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_BOUND_FUNCTION) { - JSBoundFunction *s = p->u.bound_function; - return JS_IsInstanceOf(ctx, val, s->func_obj); - } - - /* Only explicitly boxed values are instances of constructors */ - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); - if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { - if (!JS_IsException(obj_proto)) - JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); - ret = -1; - goto done; - } - proto = JS_VALUE_GET_OBJ(obj_proto); - p = JS_VALUE_GET_OBJ(val); - for(;;) { - proto1 = p->shape->proto; - if (!proto1) { - /* slow case if proxy in the prototype chain */ - if (unlikely(p->class_id == JS_CLASS_PROXY)) { - JSValue obj1; - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); - for(;;) { - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsException(obj1)) { - ret = -1; - break; - } - if (JS_IsNull(obj1)) { - ret = FALSE; - break; - } - if (proto == JS_VALUE_GET_OBJ(obj1)) { - JS_FreeValue(ctx, obj1); - ret = TRUE; - break; - } - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) { - JS_FreeValue(ctx, obj1); - ret = -1; - break; - } - } - } else { - ret = FALSE; - } - break; - } - p = proto1; - if (proto == p) { - ret = TRUE; - break; - } - } -done: - JS_FreeValue(ctx, obj_proto); - return ret; -} - -/* return TRUE, FALSE or (-1) in case of exception */ -int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) -{ - JSValue method; - - if (!JS_IsObject(obj)) - goto fail; - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); - if (JS_IsException(method)) - return -1; - if (!JS_IsNull(method) && !JS_IsUndefined(method)) { - JSValue ret; - ret = JS_CallFree(ctx, method, obj, 1, &val); - return JS_ToBoolFree(ctx, ret); - } - - /* legacy case */ - if (!JS_IsFunction(ctx, obj)) { - fail: - JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); - return -1; - } - return JS_OrdinaryIsInstanceOf(ctx, val, obj); -} - -/* return the value associated to the autoinit property or an exception */ -typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); - -static JSAutoInitFunc *js_autoinit_func_table[] = { - js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ - js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ - JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ -}; - -/* warning: 'prs' is reallocated after it */ -static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, - JSProperty *pr, JSShapeProperty *prs) -{ - JSValue val; - JSContext *realm; - JSAutoInitFunc *func; - - 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)]; - /* 'func' shall not modify the object properties 'pr' */ - val = func(realm, p, prop, pr->u.init.opaque); - js_autoinit_free(ctx->rt, pr); - prs->flags &= ~JS_PROP_TMASK; - pr->u.value = JS_UNDEFINED; - if (JS_IsException(val)) - return -1; - pr->u.value = val; - return 0; -} - -JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst this_obj, - BOOL throw_ref_error) -{ - JSObject *p; - JSProperty *pr; - JSShapeProperty *prs; - uint32_t tag; - - tag = JS_VALUE_GET_TAG(obj); - if (unlikely(tag != JS_TAG_OBJECT)) { - switch(tag) { - case JS_TAG_NULL: - return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); - case JS_TAG_UNDEFINED: - return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); - case JS_TAG_EXCEPTION: - return JS_EXCEPTION; - case JS_TAG_STRING: - { - JSString *p1 = JS_VALUE_GET_STRING(obj); - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx, ch; - idx = __JS_AtomToUInt32(prop); - if (idx < p1->len) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; - else - ch = p1->u.str8[idx]; - return js_new_string_char(ctx, ch); - } - } else if (prop == JS_ATOM_length) { - return JS_NewInt32(ctx, p1->len); - } - } - break; - default: - break; - } - /* cannot raise an exception */ - p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); - if (!p) - return JS_UNDEFINED; - } else { - p = JS_VALUE_GET_OBJ(obj); - } - - for(;;) { - prs = find_own_property(&pr, p, prop); - if (prs) { - /* found */ - if (unlikely(prs->flags & JS_PROP_TMASK)) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (unlikely(!pr->u.getset.getter)) { - return JS_UNDEFINED; - } else { - JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); - /* Note: the field could be removed in the getter */ - func = JS_DupValue(ctx, func); - return JS_CallFree(ctx, func, this_obj, 0, NULL); - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSValue val = *pr->u.var_ref->pvalue; - if (unlikely(JS_IsUninitialized(val))) - return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return JS_DupValue(ctx, val); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) - return JS_EXCEPTION; - continue; - } - } else { - return JS_DupValue(ctx, pr->u.value); - } - } - if (unlikely(p->is_exotic)) { - /* exotic behaviors */ - if (p->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - /* we avoid duplicating the code */ - return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - return JS_UNDEFINED; - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - int ret; - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) - return JS_EXCEPTION; - return JS_UNDEFINED; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em) { - if (em->get_property) { - JSValue obj1, retval; - /* XXX: should pass throw_ref_error */ - /* Note: if 'p' is a prototype, it can be - freed in the called function */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - retval = em->get_property(ctx, obj1, prop, this_obj); - JS_FreeValue(ctx, obj1); - return retval; - } - if (em->get_own_property) { - JSPropertyDescriptor desc; - int ret; - JSValue obj1; - - /* Note: if 'p' is a prototype, it can be - freed in the called function */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = em->get_own_property(ctx, &desc, obj1, prop); - JS_FreeValue(ctx, obj1); - if (ret < 0) - return JS_EXCEPTION; - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JS_FreeValue(ctx, desc.setter); - return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); - } else { - return desc.value; - } - } - } - } - } - } - p = p->shape->proto; - if (!p) - break; - } - if (unlikely(throw_ref_error)) { - return JS_ThrowReferenceErrorNotDefined(ctx, prop); - } else { - return JS_UNDEFINED; - } -} - -static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) -{ - return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", - atom); -} - -/* Private fields can be added even on non extensible objects or - Proxies */ -static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name, JSValue val) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail; - } - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", - prop); - goto fail; - } - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { - fail: - JS_FreeValue(ctx, val); - return -1; - } - pr->u.value = val; - return 0; -} - -static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return JS_ThrowTypeErrorNotAnObject(ctx); - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) - return JS_ThrowTypeErrorNotASymbol(ctx); - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - JS_ThrowTypeErrorPrivateNotFound(ctx, prop); - return JS_EXCEPTION; - } - return JS_DupValue(ctx, pr->u.value); -} - -static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name, JSValue val) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail; - } - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - JS_ThrowTypeErrorPrivateNotFound(ctx, prop); - fail: - JS_FreeValue(ctx, val); - return -1; - } - set_value(ctx, &pr->u.value, val); - return 0; -} - -/* 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; - JSShapeProperty *prs; - JSProperty *pr; - JSValue brand; - JSAtom brand_atom; - - if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p = JS_VALUE_GET_OBJ(home_obj); - prs = find_own_property(&pr, p, JS_ATOM_Private_brand); - if (!prs) { - /* 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; - pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); - if (!pr) { - JS_FreeValue(ctx, brand); - return -1; - } - pr->u.value = JS_DupValue(ctx, brand); - } else { - brand = JS_DupValue(ctx, pr->u.value); - } - brand_atom = js_symbol_to_atom(ctx, brand); - - if (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 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)) - goto not_obj; - p1 = JS_VALUE_GET_OBJ(func); - if (!js_class_has_bytecode(p1->class_id)) - goto not_obj; - home_obj = p1->u.func.home_object; - if (!home_obj) - goto not_obj; - prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); - if (!prs) { - JS_ThrowTypeError(ctx, "expecting <brand> private field"); - return -1; - } - brand = pr->u.value; - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) - goto not_obj; - - /* get the brand array of 'obj' */ - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - not_obj: - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)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); - len = p1->len; - } - return len; -} - -static int num_keys_cmp(const void *p1, const void *p2, void *opaque) -{ - JSContext *ctx = opaque; - JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; - JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; - uint32_t v1, v2; - BOOL atom1_is_integer, atom2_is_integer; - - atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); - atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); - assert(atom1_is_integer && atom2_is_integer); - if (v1 < v2) - return -1; - else if (v1 == v2) - return 0; - else - return 1; -} - -static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) -{ - uint32_t i; - if (tab) { - for(i = 0; i < len; i++) - JS_FreeAtom(ctx, tab[i].atom); - js_free(ctx, tab); - } -} - -/* return < 0 in case if exception, 0 if OK. ptab and its atoms must - be freed by the user. */ -static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSObject *p, int flags) -{ - int i, j; - JSShape *sh; - JSShapeProperty *prs; - JSPropertyEnum *tab_atom, *tab_exotic; - JSAtom atom; - uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; - uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; - BOOL is_enumerable, num_sorted; - uint32_t num_key; - JSAtomKindEnum kind; - - /* clear pointer for consistency in case of failure */ - *ptab = NULL; - *plen = 0; - - /* compute the number of returned properties */ - num_keys_count = 0; - str_keys_count = 0; - sym_keys_count = 0; - exotic_keys_count = 0; - exotic_count = 0; - tab_exotic = NULL; - sh = p->shape; - for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - atom = prs->atom; - if (atom != JS_ATOM_NULL) { - is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && - ((flags >> kind) & 1) != 0) { - /* need to raise an exception in case of the module - name space (implicit GetOwnProperty) */ - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && - (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { - JSVarRef *var_ref = p->prop[i].u.var_ref; - if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - } - if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { - num_keys_count++; - } else if (kind == JS_ATOM_KIND_STRING) { - str_keys_count++; - } else { - sym_keys_count++; - } - } - } - } - - if (p->is_exotic) { - if (p->fast_array) { - if (flags & JS_GPN_STRING_MASK) { - num_keys_count += p->u.array.count; - } - } else if (p->class_id == JS_CLASS_STRING) { - if (flags & JS_GPN_STRING_MASK) { - num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->get_own_property_names) { - if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, - JS_MKPTR(JS_TAG_OBJECT, p))) - return -1; - for(i = 0; i < exotic_count; i++) { - atom = tab_exotic[i].atom; - kind = JS_AtomGetKind(ctx, atom); - if (((flags >> kind) & 1) != 0) { - is_enumerable = FALSE; - if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { - JSPropertyDescriptor desc; - int res; - /* set the "is_enumerable" field if necessary */ - res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); - if (res < 0) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); - return -1; - } - if (res) { - is_enumerable = - ((desc.flags & JS_PROP_ENUMERABLE) != 0); - js_free_desc(ctx, &desc); - } - tab_exotic[i].is_enumerable = is_enumerable; - } - if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { - exotic_keys_count++; - } - } - } - } - } - } - - /* fill them */ - - atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; - /* avoid allocating 0 bytes */ - tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); - if (!tab_atom) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); - return -1; - } - - num_index = 0; - str_index = num_keys_count; - sym_index = str_index + str_keys_count; - - num_sorted = TRUE; - sh = p->shape; - for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - atom = prs->atom; - if (atom != JS_ATOM_NULL) { - is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && - ((flags >> kind) & 1) != 0) { - if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { - j = num_index++; - num_sorted = FALSE; - } else if (kind == JS_ATOM_KIND_STRING) { - j = str_index++; - } else { - j = sym_index++; - } - tab_atom[j].atom = JS_DupAtom(ctx, atom); - tab_atom[j].is_enumerable = is_enumerable; - } - } - } - - if (p->is_exotic) { - int len; - if (p->fast_array) { - if (flags & JS_GPN_STRING_MASK) { - len = p->u.array.count; - goto add_array_keys; - } - } else if (p->class_id == JS_CLASS_STRING) { - if (flags & JS_GPN_STRING_MASK) { - len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - add_array_keys: - 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); - return -1; - } - tab_atom[num_index].is_enumerable = TRUE; - num_index++; - } - } - } else { - /* Note: exotic keys are not reordered and comes after the object own properties. */ - for(i = 0; i < exotic_count; i++) { - atom = tab_exotic[i].atom; - is_enumerable = tab_exotic[i].is_enumerable; - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && - ((flags >> kind) & 1) != 0) { - tab_atom[sym_index].atom = atom; - tab_atom[sym_index].is_enumerable = is_enumerable; - sym_index++; - } else { - JS_FreeAtom(ctx, atom); - } - } - js_free(ctx, tab_exotic); - } - } - - assert(num_index == num_keys_count); - assert(str_index == num_keys_count + str_keys_count); - assert(sym_index == atom_count); - - if (num_keys_count != 0 && !num_sorted) { - rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, - ctx); - } - *ptab = tab_atom; - *plen = atom_count; - return 0; -} - -int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSValueConst obj, int flags) -{ - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, - JS_VALUE_GET_OBJ(obj), flags); -} - -/* Return -1 if exception, - FALSE if the property does not exist, TRUE if it exists. If TRUE is - returned, the property descriptor 'desc' is filled present. */ -static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, - JSObject *p, JSAtom prop) -{ - JSShapeProperty *prs; - JSProperty *pr; - -retry: - prs = find_own_property(&pr, p, prop); - if (prs) { - if (desc) { - desc->flags = prs->flags & JS_PROP_C_W_E; - desc->getter = JS_UNDEFINED; - desc->setter = JS_UNDEFINED; - desc->value = JS_UNDEFINED; - if (unlikely(prs->flags & JS_PROP_TMASK)) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - desc->flags |= JS_PROP_GETSET; - if (pr->u.getset.getter) - desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSValue val = *pr->u.var_ref->pvalue; - if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - desc->value = JS_DupValue(ctx, val); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) - return -1; - goto retry; - } - } else { - desc->value = JS_DupValue(ctx, pr->u.value); - } - } else { - /* for consistency, send the exception even if desc is NULL */ - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { - if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* nothing to do: delay instantiation until actual value and/or attributes are read */ - } - } - return TRUE; - } - if (p->is_exotic) { - if (p->fast_array) { - /* specific case for fast arrays */ - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx; - idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - if (desc) { - desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | - JS_PROP_CONFIGURABLE; - desc->getter = JS_UNDEFINED; - desc->setter = JS_UNDEFINED; - desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); - } - return TRUE; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->get_own_property) { - return em->get_own_property(ctx, desc, - JS_MKPTR(JS_TAG_OBJECT, p), prop); - } - } - } - return FALSE; -} - -int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop) -{ - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); -} - -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ -int JS_IsExtensible(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_isExtensible(ctx, obj); - else - return p->extensible; -} - -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ -int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_preventExtensions(ctx, obj); - p->extensible = FALSE; - return TRUE; -} - -/* return -1 if exception otherwise TRUE or FALSE */ -int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) -{ - JSObject *p; - int ret; - JSValue obj1; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - for(;;) { - if (p->is_exotic) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->has_property) { - /* has_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = em->has_property(ctx, obj1, prop); - JS_FreeValue(ctx, obj1); - return ret; - } - } - /* JS_GetOwnPropertyInternal can free the prototype */ - JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - if (ret != 0) - return ret; - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) - return -1; - return FALSE; - } - } - p = p->shape->proto; - if (!p) - break; - } - return FALSE; -} - -/* val must be a symbol */ -static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) -{ - JSAtomStruct *p = JS_VALUE_GET_PTR(val); - return js_get_atom_index(ctx->rt, p); -} - -/* return JS_ATOM_NULL in case of exception */ -JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) -{ - JSAtom atom; - uint32_t tag; - tag = JS_VALUE_GET_TAG(val); - if (tag == JS_TAG_INT && - (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { - /* fast path for integer values */ - atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); - } else if (tag == JS_TAG_SYMBOL) { - JSAtomStruct *p = JS_VALUE_GET_PTR(val); - atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); - } else { - JSValue str; - str = JS_ToPropertyKey(ctx, val); - if (JS_IsException(str)) - return JS_ATOM_NULL; - if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { - atom = js_symbol_to_atom(ctx, str); - } else { - atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); - } - } - return atom; -} - -static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop) -{ - JSAtom atom; - JSValue ret; - - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && - JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { - JSObject *p; - uint32_t idx; - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - idx = JS_VALUE_GET_INT(prop); - switch(p->class_id) { - case JS_CLASS_ARRAY: - 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_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: - atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_GetProperty(ctx, this_obj, atom); - JS_FreeAtom(ctx, atom); - return ret; - } -} - -JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx) -{ - return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); -} - -/* Check if an object has a generalized numeric property. Return value: - -1 for exception, - TRUE if property exists, stored into *pval, - FALSE if proprty does not exist. - */ -static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) -{ - JSValue val = JS_UNDEFINED; - JSAtom prop; - int present; - - if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { - /* fast path */ - present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); - if (present > 0) { - val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - if (unlikely(JS_IsException(val))) - present = -1; - } - } else { - prop = JS_NewAtomInt64(ctx, idx); - present = -1; - if (likely(prop != JS_ATOM_NULL)) { - present = JS_HasProperty(ctx, obj, prop); - if (present > 0) { - val = JS_GetProperty(ctx, obj, prop); - if (unlikely(JS_IsException(val))) - present = -1; - } - JS_FreeAtom(ctx, prop); - } - } - *pval = val; - return present; -} - -static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) -{ - JSAtom prop; - JSValue val; - - if ((uint64_t)idx <= INT32_MAX) { - /* fast path for fast arrays */ - return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) - return JS_EXCEPTION; - - val = JS_GetProperty(ctx, obj, prop); - JS_FreeAtom(ctx, prop); - return val; -} - -JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, - const char *prop) -{ - JSAtom atom; - JSValue ret; - atom = JS_NewAtom(ctx, prop); - ret = JS_GetProperty(ctx, this_obj, atom); - JS_FreeAtom(ctx, atom); - return ret; -} - -/* Note: the property value is not initialized. Return NULL if memory - error. */ -static JSProperty *add_property(JSContext *ctx, - JSObject *p, JSAtom prop, int prop_flags) -{ - JSShape *sh, *new_sh; - - sh = p->shape; - if (sh->is_hashed) { - /* try to find an existing shape */ - new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); - if (new_sh) { - /* matching shape found: use it */ - /* the property array may need to be resized */ - if (new_sh->prop_size != sh->prop_size) { - JSProperty *new_prop; - new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * - new_sh->prop_size); - if (!new_prop) - return NULL; - p->prop = new_prop; - } - p->shape = js_dup_shape(new_sh); - js_free_shape(ctx->rt, sh); - return &p->prop[new_sh->prop_count - 1]; - } else if (sh->header.ref_count != 1) { - /* if the shape is shared, clone it */ - new_sh = js_clone_shape(ctx, sh); - if (!new_sh) - return NULL; - /* hash the cloned shape */ - new_sh->is_hashed = TRUE; - js_shape_hash_link(ctx->rt, new_sh); - js_free_shape(ctx->rt, p->shape); - p->shape = new_sh; - } - } - assert(p->shape->header.ref_count == 1); - if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) - return NULL; - return &p->prop[p->shape->prop_count - 1]; -} - -/* can be called on Array or Arguments objects. return < 0 if - memory alloc error. */ -static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, - JSObject *p) -{ - JSProperty *pr; - JSShape *sh; - JSValue *tab; - uint32_t i, len, new_count; - - if (js_shape_prepare_update(ctx, p, NULL)) - return -1; - len = p->u.array.count; - /* resize the properties once to simplify the error handling */ - sh = p->shape; - new_count = sh->prop_count + len; - if (new_count > sh->prop_size) { - if (resize_properties(ctx, &p->shape, p, new_count)) - return -1; - } - - tab = p->u.array.u.values; - for(i = 0; i < len; i++) { - /* add_property cannot fail here but - __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); - pr->u.value = *tab++; - } - js_free(ctx, p->u.array.u.values); - p->u.array.count = 0; - p->u.array.u.values = NULL; /* fail safe */ - p->u.array.u1.size = 0; - p->fast_array = 0; - return 0; -} - -static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) -{ - JSShape *sh; - JSShapeProperty *pr, *lpr, *prop; - JSProperty *pr1; - uint32_t lpr_idx; - intptr_t h, h1; - - redo: - sh = p->shape; - h1 = atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h1 - 1]; - prop = get_shape_prop(sh); - lpr = NULL; - lpr_idx = 0; /* prevent warning */ - while (h != 0) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - /* found ! */ - if (!(pr->flags & JS_PROP_CONFIGURABLE)) - return FALSE; - /* realloc the shape if needed */ - if (lpr) - lpr_idx = lpr - get_shape_prop(sh); - if (js_shape_prepare_update(ctx, p, &pr)) - return -1; - sh = p->shape; - /* remove property */ - if (lpr) { - lpr = get_shape_prop(sh) + lpr_idx; - lpr->hash_next = pr->hash_next; - } else { - prop_hash_end(sh)[-h1 - 1] = pr->hash_next; - } - sh->deleted_prop_count++; - /* free the entry */ - pr1 = &p->prop[h - 1]; - free_property(ctx->rt, pr1, pr->flags); - JS_FreeAtom(ctx, pr->atom); - /* put default values */ - pr->flags = 0; - pr->atom = JS_ATOM_NULL; - pr1->u.value = JS_UNDEFINED; - - /* compact the properties if too many deleted properties */ - if (sh->deleted_prop_count >= 8 && - sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { - compact_properties(ctx, p); - } - return TRUE; - } - lpr = pr; - h = pr->hash_next; - } - - if (p->is_exotic) { - if (p->fast_array) { - uint32_t idx; - if (JS_AtomIsArrayIndex(ctx, &idx, atom) && - idx < p->u.array.count) { - if (p->class_id == JS_CLASS_ARRAY || - p->class_id == JS_CLASS_ARGUMENTS) { - /* Special case deleting the last element of a fast Array */ - if (idx == p->u.array.count - 1) { - JS_FreeValue(ctx, p->u.array.u.values[idx]); - p->u.array.count = idx; - return TRUE; - } - if (convert_fast_array_to_array(ctx, p)) - return -1; - goto redo; - } else { - return FALSE; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->delete_property) { - return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); - } - } - } - /* not found */ - return TRUE; -} - -static int call_setter(JSContext *ctx, JSObject *setter, - JSValueConst this_obj, JSValue val, int flags) -{ - JSValue ret, func; - if (likely(setter)) { - func = JS_MKPTR(JS_TAG_OBJECT, setter); - /* Note: the field could be removed in the setter */ - func = JS_DupValue(ctx, func); - ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val); - JS_FreeValue(ctx, val); - if (JS_IsException(ret)) - return -1; - JS_FreeValue(ctx, ret); - return TRUE; - } else { - JS_FreeValue(ctx, val); - if ((flags & JS_PROP_THROW) || - ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeError(ctx, "no setter for property"); - return -1; - } - return FALSE; - } -} - -/* set the array length and remove the array elements if necessary. */ -static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, - int flags) -{ - uint32_t len, idx, cur_len; - int i, ret; - - /* Note: this call can reallocate the properties of 'p' */ - ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); - if (ret) - return -1; - /* JS_ToArrayLengthFree() must be done before the read-only test */ - if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - - if (likely(p->fast_array)) { - uint32_t old_len = p->u.array.count; - if (len < old_len) { - for(i = len; i < old_len; i++) { - JS_FreeValue(ctx, p->u.array.u.values[i]); - } - p->u.array.count = len; - } - p->prop[0].u.value = JS_NewUint32(ctx, len); - } else { - /* Note: length is always a uint32 because the object is an - array */ - JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); - if (len < cur_len) { - uint32_t d; - JSShape *sh; - JSShapeProperty *pr; - - d = cur_len - len; - sh = p->shape; - if (d <= sh->prop_count) { - JSAtom atom; - - /* faster to iterate */ - while (cur_len > len) { - atom = JS_NewAtomUInt32(ctx, cur_len - 1); - ret = delete_property(ctx, p, atom); - JS_FreeAtom(ctx, atom); - if (unlikely(!ret)) { - /* unlikely case: property is not - configurable */ - break; - } - cur_len--; - } - } else { - /* faster to iterate thru all the properties. Need two - passes in case one of the property is not - configurable */ - cur_len = len; - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; - i++, pr++) { - if (pr->atom != JS_ATOM_NULL && - JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { - if (idx >= cur_len && - !(pr->flags & JS_PROP_CONFIGURABLE)) { - cur_len = idx + 1; - } - } - } - - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; - i++, pr++) { - if (pr->atom != JS_ATOM_NULL && - JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { - if (idx >= cur_len) { - /* remove the property */ - delete_property(ctx, p, pr->atom); - /* WARNING: the shape may have been modified */ - sh = p->shape; - pr = get_shape_prop(sh) + i; - } - } - } - } - } else { - cur_len = len; - } - set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); - if (unlikely(cur_len > len)) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); - } - } - return TRUE; -} - -/* return -1 if exception */ -static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) -{ - uint32_t new_size; - size_t slack; - JSValue *new_array_prop; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); - new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); - if (!new_array_prop) - return -1; - new_size += slack / sizeof(*new_array_prop); - p->u.array.u.values = new_array_prop; - p->u.array.u1.size = new_size; - return 0; -} - -/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = - TRUE and p->extensible = TRUE */ -static int add_fast_array_element(JSContext *ctx, JSObject *p, - JSValue val, int flags) -{ - uint32_t new_len, array_len; - /* extend the array by one */ - /* XXX: convert to slow array if new_len > 2^31-1 elements */ - new_len = p->u.array.count + 1; - /* update the length if necessary. We assume that if the length is - not an integer, then if it >= 2^31. */ - if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { - array_len = JS_VALUE_GET_INT(p->prop[0].u.value); - if (new_len > array_len) { - if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - } - p->prop[0].u.value = JS_NewInt32(ctx, new_len); - } - } - if (unlikely(new_len > p->u.array.u1.size)) { - if (expand_fast_array(ctx, p, new_len)) { - JS_FreeValue(ctx, val); - return -1; - } - } - p->u.array.u.values[new_len - 1] = val; - p->u.array.count = new_len; - 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); - JS_FreeValue(ctx, desc->setter); - JS_FreeValue(ctx, desc->value); -} - -/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is - freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, - JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, - the new property is not added and an error is raised. 'this_obj' is - the receiver. If obj != this_obj, then obj must be an object - (Reflect.set case). */ -int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValue val, JSValueConst this_obj, int flags) -{ - JSObject *p, *p1; - JSShapeProperty *prs; - JSProperty *pr; - uint32_t tag; - JSPropertyDescriptor desc; - int ret; -#if 0 - printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); -#endif - tag = JS_VALUE_GET_TAG(this_obj); - if (unlikely(tag != JS_TAG_OBJECT)) { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = NULL; - p1 = JS_VALUE_GET_OBJ(obj); - goto prototype_lookup; - } else { - switch(tag) { - case JS_TAG_NULL: - JS_FreeValue(ctx, val); - JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); - return -1; - case JS_TAG_UNDEFINED: - JS_FreeValue(ctx, val); - JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); - return -1; - default: - /* even on a primitive type we can have setters on the prototype */ - p = NULL; - p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); - goto prototype_lookup; - } - } - } else { - p = JS_VALUE_GET_OBJ(this_obj); - p1 = JS_VALUE_GET_OBJ(obj); - if (unlikely(p != p1)) - goto retry2; - } - - /* fast path if obj == this_obj */ - retry: - prs = find_own_property(&pr, p1, prop); - if (prs) { - if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | - JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { - /* fast case */ - set_value(ctx, &pr->u.value, val); - return TRUE; - } else if (prs->flags & JS_PROP_LENGTH) { - assert(p->class_id == JS_CLASS_ARRAY); - assert(prop == JS_ATOM_length); - return set_array_length(ctx, p, val, flags); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - /* JS_PROP_WRITABLE is always true for variable - references, but they are write protected in module name - spaces. */ - if (p->class_id == JS_CLASS_MODULE_NS) - goto read_only_prop; - set_value(ctx, pr->u.var_ref->pvalue, val); - return TRUE; - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry (potentially useless) */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { - JS_FreeValue(ctx, val); - return -1; - } - goto retry; - } else { - goto read_only_prop; - } - } - - for(;;) { - if (p1->is_exotic) { - if (p1->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx < p1->u.array.count) { - if (unlikely(p == p1)) - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); - else - break; - } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && - p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { - goto typed_array_oob; - } - } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && - p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) { - JS_FreeValue(ctx, val); - return -1; - } - typed_array_oob: - /* 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; - } - return TRUE; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; - if (em) { - JSValue obj1; - if (em->set_property) { - /* set_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - ret = em->set_property(ctx, obj1, prop, - val, this_obj, flags); - JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, val); - return ret; - } - if (em->get_own_property) { - /* get_own_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - ret = em->get_own_property(ctx, &desc, - obj1, prop); - JS_FreeValue(ctx, obj1); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JSObject *setter; - if (JS_IsUndefined(desc.setter)) - setter = NULL; - else - setter = JS_VALUE_GET_OBJ(desc.setter); - ret = call_setter(ctx, setter, this_obj, val, flags); - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - return ret; - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE)) - goto read_only_prop; - if (likely(p == p1)) { - ret = JS_DefineProperty(ctx, this_obj, prop, val, - JS_UNDEFINED, JS_UNDEFINED, - JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; - } else { - break; - } - } - } - } - } - } - } - p1 = p1->shape->proto; - prototype_lookup: - if (!p1) - break; - - retry2: - prs = find_own_property(&pr, p1, prop); - if (prs) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry (potentially useless) */ - if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) - return -1; - goto retry2; - } else if (!(prs->flags & JS_PROP_WRITABLE)) { - goto read_only_prop; - } - } - } - - if (unlikely(flags & JS_PROP_NO_ADD)) { - JS_FreeValue(ctx, val); - JS_ThrowReferenceErrorNotDefined(ctx, prop); - return -1; - } - - if (unlikely(!p)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); - } - - if (unlikely(!p->extensible)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); - } - - if (likely(p == JS_VALUE_GET_OBJ(obj))) { - if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY && p->fast_array && - __JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - /* fast case */ - return add_fast_array_element(ctx, p, val, flags); - } else { - goto generic_create_prop; - } - } else { - goto generic_create_prop; - } - } else { - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { - JS_FreeValue(ctx, val); - return -1; - } - pr->u.value = val; - return TRUE; - } - } else { - /* generic case: modify the property in this_obj if it already exists */ - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE) || - p->class_id == JS_CLASS_MODULE_NS) { - read_only_prop: - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); - } - } - ret = JS_DefineProperty(ctx, this_obj, prop, val, - JS_UNDEFINED, JS_UNDEFINED, - JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; - } else { - generic_create_prop: - ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, - flags | - JS_PROP_HAS_VALUE | - JS_PROP_HAS_ENUMERABLE | - JS_PROP_HAS_WRITABLE | - JS_PROP_HAS_CONFIGURABLE | - JS_PROP_C_W_E); - JS_FreeValue(ctx, val); - return ret; - } - } -} - -/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ -static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop, JSValue val, int flags) -{ - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && - JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { - JSObject *p; - uint32_t idx; - double d; - int32_t v; - - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - idx = JS_VALUE_GET_INT(prop); - switch(p->class_id) { - case JS_CLASS_ARRAY: - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - JSObject *p1; - JSShape *sh1; - - /* fast path to add an element to the array */ - if (idx != (uint32_t)p->u.array.count || - !p->fast_array || !p->extensible) - goto slow_path; - /* check if prototype chain has a numeric property */ - p1 = p->shape->proto; - while (p1 != NULL) { - sh1 = p1->shape; - if (p1->class_id == JS_CLASS_ARRAY) { - if (unlikely(!p1->fast_array)) - goto slow_path; - } else if (p1->class_id == JS_CLASS_OBJECT) { - if (unlikely(sh1->has_small_array_index)) - goto slow_path; - } else { - goto slow_path; - } - p1 = sh1->proto; - } - /* add element */ - return add_fast_array_element(ctx, p, val, flags); - } - set_value(ctx, &p->u.array.u.values[idx], val); - break; - case JS_CLASS_ARGUMENTS: - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto slow_path; - set_value(ctx, &p->u.array.u.values[idx], val); - break; - case JS_CLASS_UINT8C_ARRAY: - if (JS_ToUint8ClampFree(ctx, &v, val)) - return -1; - /* Note: the conversion can detach the typed array, so the - array bound check must be done after */ - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint8_ptr[idx] = v; - break; - case JS_CLASS_INT8_ARRAY: - case JS_CLASS_UINT8_ARRAY: - if (JS_ToInt32Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint8_ptr[idx] = v; - break; - case JS_CLASS_INT16_ARRAY: - case JS_CLASS_UINT16_ARRAY: - if (JS_ToInt32Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint16_ptr[idx] = v; - break; - case JS_CLASS_INT32_ARRAY: - case JS_CLASS_UINT32_ARRAY: - if (JS_ToInt32Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint32_ptr[idx] = v; - break; - case JS_CLASS_BIG_INT64_ARRAY: - case JS_CLASS_BIG_UINT64_ARRAY: - /* XXX: need specific conversion function */ - { - int64_t v; - if (JS_ToBigInt64Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint64_ptr[idx] = v; - } - break; - case JS_CLASS_FLOAT32_ARRAY: - if (JS_ToFloat64Free(ctx, &d, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.float_ptr[idx] = d; - break; - case JS_CLASS_FLOAT64_ARRAY: - if (JS_ToFloat64Free(ctx, &d, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - ta_out_of_bound: - return TRUE; - } - p->u.array.u.double_ptr[idx] = d; - break; - default: - goto slow_path; - } - return TRUE; - } else { - JSAtom atom; - int ret; - slow_path: - atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, val); - return -1; - } - ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags); - JS_FreeAtom(ctx, atom); - return ret; - } -} - -int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx, JSValue val) -{ - return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, - JS_PROP_THROW); -} - -int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, - int64_t idx, JSValue val) -{ - JSAtom prop; - int res; - - if ((uint64_t)idx <= INT32_MAX) { - /* fast path for fast arrays */ - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, - JS_PROP_THROW); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) { - JS_FreeValue(ctx, val); - return -1; - } - res = JS_SetProperty(ctx, this_obj, prop, val); - JS_FreeAtom(ctx, prop); - return res; -} - -int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, - const char *prop, JSValue val) -{ - JSAtom atom; - int ret; - atom = JS_NewAtom(ctx, prop); - ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW); - JS_FreeAtom(ctx, atom); - return ret; -} - -/* compute the property flags. For each flag: (JS_PROP_HAS_x forces - it, otherwise def_flags is used) - Note: makes assumption about the bit pattern of the flags -*/ -static int get_prop_flags(int flags, int def_flags) -{ - int mask; - mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; - return (flags & mask) | (def_flags & ~mask); -} - -static int JS_CreateProperty(JSContext *ctx, JSObject *p, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags) -{ - JSProperty *pr; - int ret, prop_flags; - - /* add a new property or modify an existing exotic one */ - if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY) { - uint32_t idx, len; - - if (p->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - if (!p->extensible) - goto not_extensible; - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) - goto convert_to_array; - prop_flags = get_prop_flags(flags, 0); - if (prop_flags != JS_PROP_C_W_E) - goto convert_to_array; - return add_fast_array_element(ctx, p, - JS_DupValue(ctx, val), flags); - } else { - goto convert_to_array; - } - } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { - /* convert the fast array to normal array */ - convert_to_array: - if (convert_fast_array_to_array(ctx, p)) - return -1; - goto generic_array; - } - } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { - JSProperty *plen; - JSShapeProperty *pslen; - generic_array: - /* update the length field */ - plen = &p->prop[0]; - JS_ToUint32(ctx, &len, plen->u.value); - if ((idx + 1) > len) { - pslen = get_shape_prop(p->shape); - if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) - return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); - /* XXX: should update the length after defining - the property */ - len = idx + 1; - set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len)); - } - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) - return -1; - return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); - } - } else if (!(flags & JS_PROP_NO_EXOTIC)) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em) { - if (em->define_own_property) { - return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), - prop, val, getter, setter, flags); - } - ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - if (ret < 0) - return -1; - if (!ret) - goto not_extensible; - } - } - } - - if (!p->extensible) { - not_extensible: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); - } - - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | - JS_PROP_GETSET; - } else { - prop_flags = flags & JS_PROP_C_W_E; - } - pr = add_property(ctx, p, prop, prop_flags); - if (unlikely(!pr)) - return -1; - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - pr->u.getset.getter = NULL; - if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { - pr->u.getset.getter = - JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter)); - } - pr->u.getset.setter = NULL; - if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { - pr->u.getset.setter = - JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter)); - } - } else { - if (flags & JS_PROP_HAS_VALUE) { - pr->u.value = JS_DupValue(ctx, val); - } else { - pr->u.value = JS_UNDEFINED; - } - } - return TRUE; -} - -/* return FALSE if not OK */ -static BOOL check_define_prop_flags(int prop_flags, int flags) -{ - BOOL has_accessor, is_getset; - - if (!(prop_flags & JS_PROP_CONFIGURABLE)) { - if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == - (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { - return FALSE; - } - if ((flags & JS_PROP_HAS_ENUMERABLE) && - (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) - return FALSE; - } - if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | - JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if (!(prop_flags & JS_PROP_CONFIGURABLE)) { - has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); - is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); - if (has_accessor != is_getset) - return FALSE; - if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { - /* not writable: cannot set the writable bit */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == - (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) - return FALSE; - } - } - } - return TRUE; -} - -/* ensure that the shape can be safely modified */ -static int js_shape_prepare_update(JSContext *ctx, JSObject *p, - JSShapeProperty **pprs) -{ - JSShape *sh; - uint32_t idx = 0; /* prevent warning */ - - sh = p->shape; - if (sh->is_hashed) { - if (sh->header.ref_count != 1) { - if (pprs) - idx = *pprs - get_shape_prop(sh); - /* clone the shape (the resulting one is no longer hashed) */ - sh = js_clone_shape(ctx, sh); - if (!sh) - return -1; - js_free_shape(ctx->rt, p->shape); - p->shape = sh; - if (pprs) - *pprs = get_shape_prop(sh) + idx; - } else { - js_shape_hash_unlink(ctx->rt, sh); - sh->is_hashed = FALSE; - } - } - return 0; -} - -static int js_update_property_flags(JSContext *ctx, JSObject *p, - JSShapeProperty **pprs, int flags) -{ - if (flags != (*pprs)->flags) { - if (js_shape_prepare_update(ctx, p, pprs)) - return -1; - (*pprs)->flags = flags; - } - return 0; -} - -/* allowed flags: - JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE - JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, - JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, - JS_PROP_THROW, JS_PROP_NO_EXOTIC. - If JS_PROP_THROW is set, return an exception instead of FALSE. - if JS_PROP_NO_EXOTIC is set, do not call the exotic - define_own_property callback. - return -1 (exception), FALSE or TRUE. -*/ -int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, int flags) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - int mask, res; - - if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p = JS_VALUE_GET_OBJ(this_obj); - - redo_prop_update: - prs = find_own_property(&pr, p, prop); - if (prs) { - /* the range of the Array length property is always tested before */ - if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { - uint32_t array_length; - if (JS_ToArrayLengthFree(ctx, &array_length, - JS_DupValue(ctx, val), FALSE)) { - return -1; - } - /* this code relies on the fact that Uint32 are never allocated */ - val = (JSValueConst)JS_NewUint32(ctx, array_length); - /* prs may have been modified */ - prs = find_own_property(&pr, p, prop); - assert(prs != NULL); - } - /* property already exists */ - if (!check_define_prop_flags(prs->flags, flags)) { - not_configurable: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); - } - - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) - return -1; - goto redo_prop_update; - } - - if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | - JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - JSObject *new_getter, *new_setter; - - if (JS_IsFunction(ctx, getter)) { - new_getter = JS_VALUE_GET_OBJ(getter); - } else { - new_getter = NULL; - } - if (JS_IsFunction(ctx, setter)) { - new_setter = JS_VALUE_GET_OBJ(setter); - } else { - new_setter = NULL; - } - - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - /* convert to getset */ - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - free_var_ref(ctx->rt, pr->u.var_ref); - } else { - JS_FreeValue(ctx, pr->u.value); - } - prs->flags = (prs->flags & - (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | - JS_PROP_GETSET; - pr->u.getset.getter = NULL; - pr->u.getset.setter = NULL; - } else { - if (!(prs->flags & JS_PROP_CONFIGURABLE)) { - if ((flags & JS_PROP_HAS_GET) && - new_getter != pr->u.getset.getter) { - goto not_configurable; - } - if ((flags & JS_PROP_HAS_SET) && - new_setter != pr->u.getset.setter) { - goto not_configurable; - } - } - } - if (flags & JS_PROP_HAS_GET) { - if (pr->u.getset.getter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (new_getter) - JS_DupValue(ctx, getter); - pr->u.getset.getter = new_getter; - } - if (flags & JS_PROP_HAS_SET) { - if (pr->u.getset.setter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - if (new_setter) - JS_DupValue(ctx, setter); - pr->u.getset.setter = new_setter; - } - } else { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - /* convert to data descriptor */ - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - if (pr->u.getset.getter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); - pr->u.value = JS_UNDEFINED; - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - /* Note: JS_PROP_VARREF is always writable */ - } else { - if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && - (flags & JS_PROP_HAS_VALUE)) { - if (!js_same_value(ctx, val, pr->u.value)) { - goto not_configurable; - } else { - return TRUE; - } - } - } - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - if (flags & JS_PROP_HAS_VALUE) { - if (p->class_id == JS_CLASS_MODULE_NS) { - /* JS_PROP_WRITABLE is always true for variable - references, but they are write protected in module name - spaces. */ - if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) - goto not_configurable; - } else { - /* 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); - free_var_ref(ctx->rt, pr->u.var_ref); - pr->u.value = val1; - prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); - } - } else if (prs->flags & JS_PROP_LENGTH) { - if (flags & JS_PROP_HAS_VALUE) { - /* Note: no JS code is executable because - 'val' is guaranted to be a Uint32 */ - res = set_array_length(ctx, p, JS_DupValue(ctx, val), - flags); - } else { - res = TRUE; - } - /* still need to reset the writable flag if - needed. The JS_PROP_LENGTH is kept because the - Uint32 test is still done if the length - property is read-only. */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == - JS_PROP_HAS_WRITABLE) { - prs = get_shape_prop(p->shape); - if (js_update_property_flags(ctx, p, &prs, - prs->flags & ~JS_PROP_WRITABLE)) - return -1; - } - return res; - } else { - if (flags & JS_PROP_HAS_VALUE) { - JS_FreeValue(ctx, pr->u.value); - pr->u.value = JS_DupValue(ctx, val); - } - if (flags & JS_PROP_HAS_WRITABLE) { - if (js_update_property_flags(ctx, p, &prs, - (prs->flags & ~JS_PROP_WRITABLE) | - (flags & JS_PROP_WRITABLE))) - return -1; - } - } - } - } - mask = 0; - if (flags & JS_PROP_HAS_CONFIGURABLE) - mask |= JS_PROP_CONFIGURABLE; - if (flags & JS_PROP_HAS_ENUMERABLE) - mask |= JS_PROP_ENUMERABLE; - if (js_update_property_flags(ctx, p, &prs, - (prs->flags & ~mask) | (flags & mask))) - return -1; - return TRUE; - } - - /* handle modification of fast array elements */ - if (p->fast_array) { - uint32_t idx; - uint32_t prop_flags; - if (p->class_id == JS_CLASS_ARRAY) { - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); - if (prop_flags != JS_PROP_C_W_E) - goto convert_to_slow_array; - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - convert_to_slow_array: - if (convert_fast_array_to_array(ctx, p)) - return -1; - else - goto redo_prop_update; - } - if (flags & JS_PROP_HAS_VALUE) { - set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); - } - return TRUE; - } - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - JSValue num; - int ret; - - if (!__JS_AtomIsTaggedInt(prop)) { - /* slow path with to handle all numeric indexes */ - num = JS_AtomIsNumericIndex1(ctx, prop); - if (JS_IsUndefined(num)) - goto typed_array_done; - if (JS_IsException(num)) - return -1; - ret = JS_NumberIsInteger(ctx, num); - if (ret < 0) { - JS_FreeValue(ctx, num); - return -1; - } - if (!ret) { - JS_FreeValue(ctx, num); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); - } - ret = JS_NumberIsNegativeOrMinusZero(ctx, num); - JS_FreeValue(ctx, num); - if (ret) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); - } - if (!__JS_AtomIsTaggedInt(prop)) - goto typed_array_oob; - } - idx = __JS_AtomToUInt32(prop); - /* if the typed array is detached, p->u.array.count = 0 */ - if (idx >= p->u.array.count) { - typed_array_oob: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); - } - prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || - prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); - } - if (flags & JS_PROP_HAS_VALUE) { - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags); - } - return TRUE; - typed_array_done: ; - } - } - - return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); -} - -static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSAutoInitIDEnum id, - void *opaque, int flags) -{ - JSObject *p; - JSProperty *pr; - - if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) - return FALSE; - - p = JS_VALUE_GET_OBJ(this_obj); - - if (find_own_property(&pr, p, prop)) { - /* property already exists */ - abort(); - return FALSE; - } - - /* Specialized CreateProperty */ - pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); - if (unlikely(!pr)) - return -1; - pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); - assert((pr->u.init.realm_and_id & 3) == 0); - assert(id <= 3); - pr->u.init.realm_and_id |= id; - pr->u.init.opaque = opaque; - return TRUE; -} - -/* shortcut to add or redefine a new property value */ -int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val, int flags) -{ - int ret; - ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, - flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); - JS_FreeValue(ctx, val); - return ret; -} - -int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, - JSValue prop, JSValue val, int flags) -{ - JSAtom atom; - int ret; - atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, val); - return -1; - } - ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); - JS_FreeAtom(ctx, atom); - return ret; -} - -int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx, JSValue val, int flags) -{ - return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx), - val, flags); -} - -int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj, - int64_t idx, JSValue val, int flags) -{ - return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), - val, flags); -} - -int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, - const char *prop, JSValue val, int flags) -{ - JSAtom atom; - int ret; - atom = JS_NewAtom(ctx, prop); - ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); - JS_FreeAtom(ctx, atom); - return ret; -} - -/* shortcut to add getter & setter */ -int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue getter, JSValue setter, - int flags) -{ - int ret; - ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, - flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); - JS_FreeValue(ctx, getter); - JS_FreeValue(ctx, setter); - return ret; -} - -static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, - int64_t idx, JSValue val, int flags) -{ - return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), - val, flags | JS_PROP_CONFIGURABLE | - JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); -} - - -/* return TRUE if 'obj' has a non empty 'name' string */ -static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj) -{ - JSProperty *pr; - JSShapeProperty *prs; - JSValueConst val; - JSString *p; - - prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); - if (!prs) - return FALSE; - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) - return TRUE; - val = pr->u.value; - if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) - return TRUE; - p = JS_VALUE_GET_STRING(val); - return (p->len != 0); -} - -static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj, - JSAtom name, int flags) -{ - if (name != JS_ATOM_NULL - && JS_IsObject(obj) - && !js_object_has_name(ctx, obj) - && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { - return -1; - } - return 0; -} - -static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, - JSValueConst str, int flags) -{ - if (JS_IsObject(obj) && - !js_object_has_name(ctx, obj)) { - JSAtom prop; - JSValue name_str; - prop = JS_ValueToAtom(ctx, str); - if (prop == JS_ATOM_NULL) - return -1; - name_str = js_get_function_name(ctx, prop); - JS_FreeAtom(ctx, prop); - if (JS_IsException(name_str)) - return -1; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) - return -1; - } - return 0; -} - -#define DEFINE_GLOBAL_LEX_VAR (1 << 7) -#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) - -static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) -{ - return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); -} - -/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ -/* XXX: could support exotic global object. */ -static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) -{ - JSObject *p; - JSShapeProperty *prs; - - p = JS_VALUE_GET_OBJ(ctx->global_obj); - prs = find_own_property1(p, prop); - /* XXX: should handle JS_PROP_AUTOINIT */ - if (flags & DEFINE_GLOBAL_LEX_VAR) { - if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) - goto fail_redeclaration; - } else { - if (!prs && !p->extensible) - goto define_error; - if (flags & DEFINE_GLOBAL_FUNC_VAR) { - if (prs) { - if (!(prs->flags & JS_PROP_CONFIGURABLE) && - ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || - ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != - (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { - define_error: - JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", - prop); - return -1; - } - } - } - } - /* check if there already is a lexical declaration */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property1(p, prop); - if (prs) { - fail_redeclaration: - JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); - return -1; - } - return 0; -} - -/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ -/* XXX: could support exotic global object. */ -static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - JSValue val; - int flags; - - if (def_flags & DEFINE_GLOBAL_LEX_VAR) { - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | - JS_PROP_CONFIGURABLE; - val = JS_UNINITIALIZED; - } else { - p = JS_VALUE_GET_OBJ(ctx->global_obj); - flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | - (def_flags & JS_PROP_CONFIGURABLE); - val = JS_UNDEFINED; - } - prs = find_own_property1(p, prop); - if (prs) - return 0; - if (!p->extensible) - return 0; - pr = add_property(ctx, p, prop, flags); - if (unlikely(!pr)) - return -1; - pr->u.value = val; - return 0; -} - -/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ -/* XXX: could support exotic global object. */ -static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, - JSValueConst func, int def_flags) -{ - - JSObject *p; - JSShapeProperty *prs; - int flags; - - p = JS_VALUE_GET_OBJ(ctx->global_obj); - prs = find_own_property1(p, prop); - flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; - if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { - flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; - } - if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, - JS_UNDEFINED, JS_UNDEFINED, flags) < 0) - return -1; - return 0; -} - -static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, - BOOL throw_ref_error) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_TMASK properties */ - if (unlikely(JS_IsUninitialized(pr->u.value))) - return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return JS_DupValue(ctx, pr->u.value); - } - return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, - ctx->global_obj, throw_ref_error); -} - -/* construct a reference to a global variable */ -static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_AUTOINIT properties? */ - /* XXX: conformance: do these tests in - OP_put_var_ref/OP_get_var_ref ? */ - if (unlikely(JS_IsUninitialized(pr->u.value))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { - return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); - } - sp[0] = JS_DupValue(ctx, ctx->global_var_obj); - } else { - int ret; - ret = JS_HasProperty(ctx, ctx->global_obj, prop); - if (ret < 0) - return -1; - if (ret) { - sp[0] = JS_DupValue(ctx, ctx->global_obj); - } else { - sp[0] = JS_UNDEFINED; - } - } - sp[1] = JS_AtomToValue(ctx, prop); - return 0; -} - -/* use for strict variable access: test if the variable exists */ -static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) -{ - JSObject *p; - JSShapeProperty *prs; - int ret; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property1(p, prop); - if (prs) { - ret = TRUE; - } else { - ret = JS_HasProperty(ctx, ctx->global_obj, prop); - if (ret < 0) - return -1; - } - return ret; -} - -/* flag = 0: normal variable write - flag = 1: initialize lexical variable - flag = 2: normal variable write, strict check was done before -*/ -static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, - int flag) -{ - JSObject *p; - JSShapeProperty *prs; - JSProperty *pr; - int flags; - - /* no exotic behavior is possible in global_var_obj */ - p = JS_VALUE_GET_OBJ(ctx->global_var_obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - /* XXX: should handle JS_PROP_AUTOINIT properties? */ - if (flag != 1) { - if (unlikely(JS_IsUninitialized(pr->u.value))) { - JS_FreeValue(ctx, val); - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); - } - } - set_value(ctx, &pr->u.value, val); - return 0; - } - flags = JS_PROP_THROW_STRICT; - if (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. return FALSE if not configurable or - invalid object. return -1 in case of exception. - flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ -int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) -{ - JSValue obj1; - JSObject *p; - int res; - - obj1 = JS_ToObject(ctx, obj); - if (JS_IsException(obj1)) - return -1; - p = JS_VALUE_GET_OBJ(obj1); - res = delete_property(ctx, p, prop); - JS_FreeValue(ctx, obj1); - if (res != FALSE) - return res; - if ((flags & JS_PROP_THROW) || - ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeError(ctx, "could not delete property"); - return -1; - } - return FALSE; -} - -int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) -{ - JSAtom prop; - int res; - - if ((uint64_t)idx <= JS_ATOM_MAX_INT) { - /* fast path for fast arrays */ - return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) - return -1; - res = JS_DeleteProperty(ctx, obj, prop, flags); - JS_FreeAtom(ctx, prop); - return res; -} - -BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - switch(p->class_id) { - case JS_CLASS_BYTECODE_FUNCTION: - return TRUE; - case JS_CLASS_PROXY: - return p->u.proxy_data->is_func; - default: - return (ctx->rt->class_array[p->class_id].call != NULL); - } -} - -BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_C_FUNCTION) - return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); - else - return FALSE; -} - -BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return p->is_constructor; -} - -BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(func_obj); - p->is_constructor = val; - return TRUE; -} - -BOOL JS_IsError(JSContext *ctx, JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return (p->class_id == JS_CLASS_ERROR); -} - -/* used to avoid catching interrupt exceptions */ -BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; -} - -void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_ERROR) - p->is_uncatchable_error = flag; -} - -void JS_ResetUncatchableError(JSContext *ctx) -{ - JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); -} - -void JS_SetOpaque(JSValue obj, void *opaque) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - p->u.opaque = opaque; - } -} - -/* return NULL if not an object of class class_id */ -void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return NULL; - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != class_id) - return NULL; - return p->u.opaque; -} - -void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) -{ - void *p = JS_GetOpaque(obj, class_id); - if (unlikely(!p)) { - JS_ThrowTypeErrorInvalidClass(ctx, class_id); - } - return p; -} - -static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) -{ - int i; - BOOL force_ordinary; - - JSAtom method_name; - JSValue method, ret; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return val; - force_ordinary = hint & HINT_FORCE_ORDINARY; - hint &= ~HINT_FORCE_ORDINARY; - if (!force_ordinary) { - method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); - if (JS_IsException(method)) - goto exception; - /* ECMA says *If exoticToPrim is not undefined* but tests in - test262 use null as a non callable converter */ - if (!JS_IsUndefined(method) && !JS_IsNull(method)) { - JSAtom atom; - JSValue arg; - switch(hint) { - case HINT_STRING: - atom = JS_ATOM_string; - break; - case HINT_NUMBER: - atom = JS_ATOM_number; - break; - default: - case HINT_NONE: - atom = JS_ATOM_default; - break; - } - arg = JS_AtomToString(ctx, atom); - ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); - JS_FreeValue(ctx, arg); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, val); - if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) - return ret; - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "toPrimitive"); - } - } - if (hint != HINT_STRING) - hint = HINT_NUMBER; - for(i = 0; i < 2; i++) { - if ((i ^ hint) == 0) { - method_name = JS_ATOM_toString; - } else { - method_name = JS_ATOM_valueOf; - } - method = JS_GetProperty(ctx, val, method_name); - if (JS_IsException(method)) - goto exception; - if (JS_IsFunction(ctx, method)) { - ret = JS_CallFree(ctx, method, val, 0, NULL); - if (JS_IsException(ret)) - goto exception; - if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { - JS_FreeValue(ctx, val); - return ret; - } - JS_FreeValue(ctx, ret); - } else { - JS_FreeValue(ctx, method); - } - } - JS_ThrowTypeError(ctx, "toPrimitive"); -exception: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) -{ - return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); -} - -void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(obj); - p->is_HTMLDDA = TRUE; -} - -static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return FALSE; - 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); - switch(tag) { - case JS_TAG_INT: - return JS_VALUE_GET_INT(val) != 0; - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - return JS_VALUE_GET_INT(val); - case JS_TAG_EXCEPTION: - return -1; - case JS_TAG_STRING: - { - BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; - JS_FreeValue(ctx, val); - return ret; - } - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - BOOL ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; - JS_FreeValue(ctx, val); - return ret; - } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - BOOL ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; - JS_FreeValue(ctx, val); - return ret; - } -#endif - case JS_TAG_OBJECT: - { - JSObject *p = JS_VALUE_GET_OBJ(val); - BOOL ret; - ret = !p->is_HTMLDDA; - JS_FreeValue(ctx, val); - return ret; - } - break; - default: - if (JS_TAG_IS_FLOAT64(tag)) { - double d = JS_VALUE_GET_FLOAT64(val); - return !isnan(d) && d != 0; - } else { - JS_FreeValue(ctx, val); - return TRUE; - } - } -} - -int JS_ToBool(JSContext *ctx, JSValueConst val) -{ - return JS_ToBoolFree(ctx, JS_DupValue(ctx, val)); -} - -static int skip_spaces(const char *pc) -{ - const uint8_t *p, *p_next, *p_start; - uint32_t c; - - p = p_start = (const uint8_t *)pc; - for (;;) { - c = *p; - if (c < 128) { - if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) - break; - p++; - } else { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (!lre_is_space(c)) - break; - p = p_next; - } - } - return p - p_start; -} - -static inline int to_digit(int c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'A' && c <= 'Z') - return c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - return c - 'a' + 10; - else - return 36; -} - -/* XXX: remove */ -static double js_strtod(const char *str, int radix, BOOL is_float) -{ - double d; - int c; - - if (!is_float || radix != 10) { - const char *p = str; - uint64_t n_max, n; - int int_exp, is_neg; - - is_neg = 0; - if (*p == '-') { - is_neg = 1; - p++; - } - - /* skip leading zeros */ - while (*p == '0') - p++; - n = 0; - if (radix == 10) - n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ - else - n_max = ((uint64_t)-1 - (radix - 1)) / radix; - /* XXX: could be more precise */ - int_exp = 0; - while (*p != '\0') { - c = to_digit((uint8_t)*p); - if (c >= radix) - break; - if (n <= n_max) { - n = n * radix + c; - } else { - if (radix == 10) - goto strtod_case; - int_exp++; - } - p++; - } - d = n; - if (int_exp != 0) { - d *= pow(radix, int_exp); - } - if (is_neg) - d = -d; - } else { - strtod_case: - d = strtod(str, NULL); - } - return d; -} - -#define ATOD_INT_ONLY (1 << 0) -/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ -#define ATOD_ACCEPT_BIN_OCT (1 << 2) -/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ -#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) -/* accept _ between digits as a digit separator */ -#define ATOD_ACCEPT_UNDERSCORES (1 << 5) -/* allow a suffix to override the type */ -#define ATOD_ACCEPT_SUFFIX (1 << 6) -/* default type */ -#define ATOD_TYPE_MASK (3 << 7) -#define ATOD_TYPE_FLOAT64 (0 << 7) -#define ATOD_TYPE_BIG_INT (1 << 7) -#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 JSValue js_string_to_bigint(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - bf_t a_s, *a = &a_s; - int ret; - JSValue val; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } -#ifdef CONFIG_BIGNUM - val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); -#else - val = JS_CompactBigInt1(ctx, val, FALSE); -#endif - return val; -} - -#ifdef CONFIG_BIGNUM -static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - bf_t *a; - int ret; - JSValue val; - - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigFloat(val); - if (flags & ATOD_ACCEPT_SUFFIX) { - /* return the exponent to get infinite precision */ - ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, - BF_RNDZ | BF_ATOF_EXPONENT); - } else { - ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec, - ctx->fp_env.flags); - } - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - return val; -} - -static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, - int radix, int flags, slimb_t *pexponent) -{ - bfdec_t *a; - int ret; - JSValue val; - - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigDecimal(val); - ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, - BF_RNDZ | BF_ATOF_NO_NAN_INF); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - return val; -} -#endif - -/* return an exception in case of memory error. Return JS_NAN if - invalid syntax */ -#ifdef CONFIG_BIGNUM -static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, - int radix, int flags, slimb_t *pexponent) -#else -static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - int radix, int flags) -#endif -{ - const char *p, *p_start; - int sep, is_neg; - BOOL is_float, has_legacy_octal; - int atod_type = flags & ATOD_TYPE_MASK; - char buf1[64], *buf; - int i, j, len; - BOOL buf_allocated = FALSE; - JSValue val; - - /* optional separator between digits */ - sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - has_legacy_octal = FALSE; - - p = str; - p_start = p; - is_neg = 0; - if (p[0] == '+') { - p++; - p_start++; - if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) - goto no_radix_prefix; - } else if (p[0] == '-') { - p++; - p_start++; - is_neg = 1; - if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) - goto no_radix_prefix; - } - if (p[0] == '0') { - if ((p[1] == 'x' || p[1] == 'X') && - (radix == 0 || radix == 16)) { - p += 2; - radix = 16; - } else if ((p[1] == 'o' || p[1] == 'O') && - radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 8; - } else if ((p[1] == 'b' || p[1] == 'B') && - radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 2; - } else if ((p[1] >= '0' && p[1] <= '9') && - radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { - int i; - has_legacy_octal = TRUE; - sep = 256; - for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) - continue; - if (p[i] == '8' || p[i] == '9') - goto no_prefix; - p += 1; - radix = 8; - } else { - goto no_prefix; - } - /* there must be a digit after the prefix */ - if (to_digit((uint8_t)*p) >= radix) - goto fail; - no_prefix: ; - } else { - no_radix_prefix: - if (!(flags & ATOD_INT_ONLY) && - (atod_type == ATOD_TYPE_FLOAT64 -#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; - } - } - 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 == '.' && radix == 10 && - (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 -#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; - } - } 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; - } - } - } - - 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(); - } - -done: - if (buf_allocated) - js_free_rt(ctx->rt, buf); - if (pp) - *pp = p; - return val; - fail: - val = JS_NAN; - goto done; - mem_error: - val = JS_ThrowOutOfMemory(ctx); - goto done; -} - -#ifdef CONFIG_BIGNUM -static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - int radix, int flags) -{ - return js_atof2(ctx, str, pp, radix, flags, NULL); -} -#endif - -typedef enum JSToNumberHintEnum { - TON_FLAG_NUMBER, - TON_FLAG_NUMERIC, -} JSToNumberHintEnum; - -static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, - JSToNumberHintEnum flag) -{ - uint32_t tag; - JSValue ret; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - 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; - } - return ret; -} - -static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) -{ - return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); -} - -static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) -{ - return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); -} - -static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) -{ - return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); -} - -static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, - JSValue val) -{ - double d; - uint32_t tag; - - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = JS_FLOAT64_NAN; - return -1; - } - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - d = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: - d = JS_VALUE_GET_FLOAT64(val); - break; - 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) -{ - uint32_t tag; - - tag = JS_VALUE_GET_TAG(val); - if (tag <= JS_TAG_NULL) { - *pres = JS_VALUE_GET_INT(val); - return 0; - } else if (JS_TAG_IS_FLOAT64(tag)) { - *pres = JS_VALUE_GET_FLOAT64(val); - return 0; - } else { - return __JS_ToFloat64Free(ctx, pres, val); - } -} - -int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) -{ - return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); -} - -static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) -{ - return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); -} - -/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ -static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) -{ - uint32_t tag; - JSValue ret; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = JS_NewInt32(ctx, 0); - } else { - /* convert -0 to +0 */ - d = trunc(d) + 0.0; - ret = JS_NewFloat64(ctx, d); - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - bf_t a_s, *a, r_s, *r = &r_s; - BOOL is_nan; - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!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; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return val; - goto redo; - } - return ret; -} - -/* Note: the integer value is satured to 32 bits */ -static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) -{ - uint32_t tag; - int ret; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_EXCEPTION: - *pres = 0; - return -1; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = 0; - } else { - if (d < INT32_MIN) - ret = INT32_MIN; - else if (d > INT32_MAX) - ret = INT32_MAX; - else - ret = (int)d; - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int32(&ret, &p->num, 0); - JS_FreeValue(ctx, val); - } - break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = ret; - return 0; -} - -int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) -{ - return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, - int min, int max, int min_offset) -{ - int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); - if (res == 0) { - if (*pres < min) { - *pres += min_offset; - if (*pres < min) - *pres = min; - } else { - if (*pres > max) - *pres = max; - } - } - return res; -} - -static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) -{ - uint32_t tag; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - *pres = JS_VALUE_GET_INT(val); - return 0; - case JS_TAG_EXCEPTION: - *pres = 0; - return -1; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - *pres = 0; - } else { - if (d < INT64_MIN) - *pres = INT64_MIN; - else if (d >= 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; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int64(pres, &p->num, 0); - JS_FreeValue(ctx, val); - } - return 0; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } -} - -int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, - int64_t min, int64_t max, int64_t neg_offset) -{ - int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); - if (res == 0) { - if (*pres < 0) - *pres += neg_offset; - if (*pres < min) - *pres = min; - else if (*pres > max) - *pres = max; - } - return res; -} - -/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) - in case of exception */ -static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) -{ - uint32_t tag; - int64_t ret; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: - { - JSFloat64Union u; - double d; - int e; - d = JS_VALUE_GET_FLOAT64(val); - u.d = d; - /* we avoid doing fmod(x, 2^64) */ - e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 62))) { - /* fast case */ - ret = (int64_t)d; - } else if (e <= (1023 + 62 + 53)) { - uint64_t v; - /* remainder modulo 2^64 */ - v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); - ret = v << ((e - 1023) - 52); - /* take the sign into account */ - if (u.u64 >> 63) - ret = -ret; - } else { - ret = 0; /* also handles NaN and +inf */ - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); - JS_FreeValue(ctx, val); - } - break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = ret; - return 0; -} - -int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - if (JS_IsBigInt(ctx, val)) - return JS_ToBigInt64(ctx, pres, val); - else - return JS_ToInt64(ctx, pres, val); -} - -/* return (<0, 0) in case of exception */ -static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) -{ - uint32_t tag; - int32_t ret; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: - { - JSFloat64Union u; - double d; - int e; - d = JS_VALUE_GET_FLOAT64(val); - u.d = d; - /* we avoid doing fmod(x, 2^32) */ - e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 30))) { - /* fast case */ - ret = (int32_t)d; - } else if (e <= (1023 + 30 + 53)) { - uint64_t v; - /* remainder modulo 2^32 */ - v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); - v = v << ((e - 1023) - 52 + 32); - ret = v >> 32; - /* take the sign into account */ - if (u.u64 >> 63) - ret = -ret; - } else { - ret = 0; /* also handles NaN and +inf */ - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); - JS_FreeValue(ctx, val); - } - break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = ret; - return 0; -} - -int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) -{ - return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); -} - -static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) -{ - return JS_ToInt32Free(ctx, (int32_t *)pres, val); -} - -static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) -{ - uint32_t tag; - int res; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - res = JS_VALUE_GET_INT(val); -#ifdef CONFIG_BIGNUM - int_clamp: -#endif - res = max_int(0, min_int(255, res)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - res = 0; - } else { - if (d < 0) - res = 0; - else if (d > 255) - res = 255; - else - res = lrint(d); - } - } - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - bf_rint(r, BF_RNDN); - bf_get_int32(&res, r, 0); - bf_delete(r); - JS_FreeValue(ctx, val); - } - goto int_clamp; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = res; - return 0; -} - -static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, 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; - 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; - } - 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_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); - } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return p->num.sign; - } - break; - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - return p->num.sign; - } - break; -#endif - default: - return FALSE; - } -} - -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); -} - -#ifdef CONFIG_BIGNUM - -static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, - limb_t prec, bf_flags_t flags) -{ - JSValue val, ret; - bf_t a_s, *a; - char *str; - int saved_sign; - - val = JS_ToNumeric(ctx, val1); - if (JS_IsException(val)) - return val; - a = JS_ToBigFloat(ctx, &a_s, val); - 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)); - } else { - prec = 53; - flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; - } - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_round(r, prec, flags1 | BF_RNDN); - str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); - bf_delete(r); - } else { - str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); - } - } else { - str = bf_ftoa(NULL, a, radix, prec, flags); - } - a->sign = saved_sign; - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, val); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; -} - -static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) -{ - return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); -} - -static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, - limb_t prec, int flags) -{ - JSValue ret; - bfdec_t *a; - char *str; - int saved_sign; - - a = JS_ToBigDecimal(ctx, val); - 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; -} - -static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) -{ - return js_bigdecimal_to_string1(ctx, val, 0, - BF_RNDZ | BF_FTOA_FORMAT_FREE); -} - -#endif /* CONFIG_BIGNUM */ - -/* 2 <= base <= 36 */ -static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; - -static char *i64toa(char *buf_end, int64_t n, unsigned int base) -{ - char *q = buf_end; - int digit, is_neg; - - is_neg = 0; - if (n < 0) { - is_neg = 1; - n = -n; - } - *--q = '\0'; - 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); - } - if (is_neg) - *--q = '-'; - return q; -} - -/* buf1 contains the printf result */ -static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf, - int rounding_mode, char *buf1, int buf1_size) -{ - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - *sign = (buf1[0] == '-'); - /* mantissa */ - buf[0] = buf1[1]; - if (n_digits > 1) - memcpy(buf + 1, buf1 + 3, n_digits - 1); - buf[n_digits] = '\0'; - /* exponent */ - *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; -} - -/* maximum buffer size for js_dtoa */ -#define JS_DTOA_BUF_SIZE 128 - -/* needed because ecvt usually limits the number of digits to - 17. Return the number of digits. */ -static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, - BOOL is_fixed) -{ - int rounding_mode; - char buf_tmp[JS_DTOA_BUF_SIZE]; - - if (!is_fixed) { - unsigned int n_digits_min, n_digits_max; - /* find the minimum amount of digits (XXX: inefficient but simple) */ - n_digits_min = 1; - n_digits_max = 17; - while (n_digits_min < n_digits_max) { - n_digits = (n_digits_min + n_digits_max) / 2; - js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); - if (strtod(buf_tmp, NULL) == d) { - /* no need to keep the trailing zeros */ - while (n_digits >= 2 && buf[n_digits - 1] == '0') - n_digits--; - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; - } - } - n_digits = n_digits_max; - rounding_mode = FE_TONEAREST; - } else { - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; - int decpt1, sign1, decpt2, sign2; - /* The JS rounding is specified as round to nearest ties away - from zero (RNDNA), but in printf the "ties" case is not - specified (for example it is RNDN for glibc, RNDNA for - Windows), so we must round manually. */ - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n_digits] == '5') { - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, - buf_tmp, sizeof(buf_tmp)); - js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, - buf_tmp, sizeof(buf_tmp)); - if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { - /* exact result: round away from zero */ - if (sign1) - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ - } - js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, - buf_tmp, sizeof(buf_tmp)); - return n_digits; -} - -static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits, - int rounding_mode) -{ - int n; - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - assert(n < sizeof(*buf)); - return n; -} - -static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits) -{ - int rounding_mode; - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - int n1, n2; - char buf1[JS_DTOA_BUF_SIZE]; - char buf2[JS_DTOA_BUF_SIZE]; - - /* The JS rounding is specified as round to nearest ties away from - zero (RNDNA), but in printf the "ties" case is not specified - (for example it is RNDN for glibc, RNDNA for Windows), so we - must round manually. */ - n1 = js_fcvt1(&buf1, 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, d, n_digits + 1, FE_DOWNWARD); - n2 = js_fcvt1(&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, d, n_digits, rounding_mode); -} - -/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ -/* use as many digits as necessary */ -#define JS_DTOA_VAR_FORMAT (0 << 0) -/* use n_digits significant digits (1 <= n_digits <= 101) */ -#define JS_DTOA_FIXED_FORMAT (1 << 0) -/* force fractional format: [-]dd.dd with n_digits fractional digits */ -#define JS_DTOA_FRAC_FORMAT (2 << 0) -/* force exponential notation either in fixed or variable format */ -#define JS_DTOA_FORCE_EXP (1 << 2) - -/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. - XXX: radix != 10 is only supported for small integers -*/ -static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d, - int radix, int n_digits, int flags) -{ - char *q; - - if (!isfinite(d)) { - if (isnan(d)) { - pstrcpy(*buf, sizeof(*buf), "NaN"); - } else if (d < 0) { - pstrcpy(*buf, sizeof(*buf), "-Infinity"); - } else { - pstrcpy(*buf, sizeof(*buf), "Infinity"); - } - } else if (flags == JS_DTOA_VAR_FORMAT) { - int64_t i64; - char buf1[70], *ptr; - if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER) - goto generic_conv; - i64 = (int64_t)d; - if (d != i64) - goto generic_conv; - /* fast path for integers */ - ptr = i64toa(buf1 + sizeof(buf1), i64, radix); - pstrcpy(*buf, sizeof(*buf), ptr); - } else { - if (d == 0.0) - d = 0.0; /* convert -0 to 0 */ - if (flags == JS_DTOA_FRAC_FORMAT) { - js_fcvt(buf, d, n_digits); - } else { - char buf1[JS_DTOA_BUF_SIZE]; - int sign, decpt, k, n, i, p, n_max; - BOOL is_fixed; - generic_conv: - is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); - if (is_fixed) { - n_max = n_digits; - } else { - n_max = 21; - } - /* the number has k digits (k >= 1) */ - k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); - n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ - q = *buf; - if (sign) - *q++ = '-'; - if (flags & JS_DTOA_FORCE_EXP) - goto force_exp; - if (n >= 1 && n <= n_max) { - if (k <= n) { - memcpy(q, buf1, k); - q += k; - for(i = 0; i < (n - k); i++) - *q++ = '0'; - *q = '\0'; - } else { - /* k > n */ - memcpy(q, buf1, n); - q += n; - *q++ = '.'; - for(i = 0; i < (k - n); i++) - *q++ = buf1[n + i]; - *q = '\0'; - } - } else if (n >= -5 && n <= 0) { - *q++ = '0'; - *q++ = '.'; - for(i = 0; i < -n; i++) - *q++ = '0'; - memcpy(q, buf1, k); - q += k; - *q = '\0'; - } else { - force_exp: - /* exponential notation */ - *q++ = buf1[0]; - if (k > 1) { - *q++ = '.'; - for(i = 1; i < k; i++) - *q++ = buf1[i]; - } - *q++ = 'e'; - p = n - 1; - if (p >= 0) - *q++ = '+'; - snprintf(q, *buf + sizeof(*buf) - q, "%d", p); - } - } - } -} - -static JSValue js_dtoa(JSContext *ctx, - double d, int radix, int n_digits, int flags) -{ - char buf[JS_DTOA_BUF_SIZE]; - js_dtoa1(&buf, d, radix, n_digits, flags); - return JS_NewString(ctx, buf); -} - -static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix) -{ - char buf[2200], *ptr, *ptr2; - /* d is finite */ - int sign = d < 0; - int digit; - double frac, d0; - int64_t n0 = 0; - d = fabs(d); - d0 = trunc(d); - frac = d - d0; - ptr = buf + 1100; - *ptr = '\0'; - if (d0 <= MAX_SAFE_INTEGER) { - int64_t n = n0 = (int64_t)d0; - while (n >= radix) { - digit = n % radix; - n = n / radix; - *--ptr = digits[digit]; - } - *--ptr = digits[(int)n]; - } else { - /* no decimals */ - while (d0 >= radix) { - digit = fmod(d0, radix); - d0 = trunc(d0 / radix); - if (d0 >= MAX_SAFE_INTEGER) - digit = 0; - *--ptr = digits[digit]; - } - *--ptr = digits[(int)d0]; - goto done; - } - if (frac != 0) { - double log2_radix = log2(radix); - double prec = 1023 + 51; // handle subnormals - ptr2 = buf + 1100; - *ptr2++ = '.'; - while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) { - frac *= radix; - digit = trunc(frac); - frac -= digit; - *ptr2++ = digits[digit]; - n0 = n0 * radix + digit; - prec -= log2_radix; - } - *ptr2 = '\0'; - if (frac * radix >= radix / 2) { - char nine = digits[radix - 1]; - // round to closest - while (ptr2[-1] == nine) - *--ptr2 = '\0'; - if (ptr2[-1] == '.') { - *--ptr2 = '\0'; - while (ptr2[-1] == nine) - *--ptr2 = '0'; - } - if (ptr2 - 1 == ptr) - *--ptr = '1'; - else - ptr2[-1] += 1; - } else { - while (ptr2[-1] == '0') - *--ptr2 = '\0'; - if (ptr2[-1] == '.') - *--ptr2 = '\0'; - } - } -done: - ptr[-1] = '-'; - ptr -= sign; - return JS_NewString(ctx, ptr); -} - -JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) -{ - uint32_t tag; - const char *str; - char buf[32]; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_STRING: - return JS_DupValue(ctx, val); - case JS_TAG_INT: - snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); - str = buf; - goto new_string; - case JS_TAG_BOOL: - return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? - JS_ATOM_true : JS_ATOM_false); - case JS_TAG_NULL: - return JS_AtomToString(ctx, JS_ATOM_null); - case JS_TAG_UNDEFINED: - return JS_AtomToString(ctx, JS_ATOM_undefined); - case JS_TAG_EXCEPTION: - return JS_EXCEPTION; - case JS_TAG_OBJECT: - { - JSValue val1, ret; - val1 = JS_ToPrimitive(ctx, val, HINT_STRING); - if (JS_IsException(val1)) - return val1; - ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); - JS_FreeValue(ctx, val1); - return ret; - } - break; - case JS_TAG_FUNCTION_BYTECODE: - str = "[function bytecode]"; - goto new_string; - case JS_TAG_SYMBOL: - if (is_ToPropertyKey) { - return JS_DupValue(ctx, val); - } else { - return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); - } - case JS_TAG_FLOAT64: - return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, - JS_DTOA_VAR_FORMAT); - 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 - default: - str = "[unsupported type]"; - new_string: - return JS_NewString(ctx, str); - } -} - -JSValue JS_ToString(JSContext *ctx, JSValueConst val) -{ - return JS_ToStringInternal(ctx, val, FALSE); -} - -static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) -{ - JSValue ret; - ret = JS_ToString(ctx, val); - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) -{ - if (JS_IsUndefined(val) || JS_IsNull(val)) - return JS_ToStringFree(ctx, val); - return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); -} - -JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) -{ - return JS_ToStringInternal(ctx, val, TRUE); -} - -static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) -{ - uint32_t tag = JS_VALUE_GET_TAG(val); - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) - return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); - return JS_ToString(ctx, val); -} - -static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) -{ - JSValue val; - JSString *p; - int i; - uint32_t c; - StringBuffer b_s, *b = &b_s; - char buf[16]; - - val = JS_ToStringCheckObject(ctx, val1); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - - if (string_buffer_init(ctx, b, p->len + 2)) - goto fail; - - if (string_buffer_putc8(b, '\"')) - goto fail; - for(i = 0; i < p->len; ) { - c = string_getc(p, &i); - switch(c) { - case '\t': - c = 't'; - goto quote; - case '\r': - c = 'r'; - goto quote; - case '\n': - c = 'n'; - goto quote; - case '\b': - c = 'b'; - goto quote; - case '\f': - c = 'f'; - goto quote; - case '\"': - case '\\': - quote: - if (string_buffer_putc8(b, '\\')) - goto fail; - if (string_buffer_putc8(b, c)) - goto fail; - break; - default: - if (c < 32 || is_surrogate(c)) { - snprintf(buf, sizeof(buf), "\\u%04x", c); - if (string_buffer_puts8(b, buf)) - goto fail; - } else { - if (string_buffer_putc(b, c)) - goto fail; - } - break; - } - } - if (string_buffer_putc8(b, '\"')) - goto fail; - JS_FreeValue(ctx, val); - return string_buffer_end(b); - fail: - JS_FreeValue(ctx, val); - string_buffer_free(b); - return JS_EXCEPTION; -} - -static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) -{ - printf("%14s %4s %4s %14s %10s %s\n", - "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); -} - -/* for debug only: dump an object without side effect */ -static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) -{ - uint32_t i; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - JSShape *sh; - JSShapeProperty *prs; - JSProperty *pr; - BOOL is_first = TRUE; - - /* XXX: should encode atoms with special characters */ - sh = p->shape; /* the shape can be NULL while freeing an object */ - printf("%14p %4d ", - (void *)p, - p->header.ref_count); - if (sh) { - printf("%3d%c %14p ", - sh->header.ref_count, - " *"[sh->is_hashed], - (void *)sh->proto); - } else { - printf("%3s %14s ", "-", "-"); - } - printf("%10s ", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); - if (p->is_exotic && p->fast_array) { - printf("[ "); - for(i = 0; i < p->u.array.count; i++) { - if (i != 0) - printf(", "); - switch (p->class_id) { - case JS_CLASS_ARRAY: - case JS_CLASS_ARGUMENTS: - JS_DumpValueShort(rt, p->u.array.u.values[i]); - break; - case JS_CLASS_UINT8C_ARRAY: - 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(" ] "); - } - - if (sh) { - printf("{ "); - for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - if (prs->atom != JS_ATOM_NULL) { - pr = &p->prop[i]; - if (!is_first) - printf(", "); - printf("%s: ", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - printf("[getset %p %p]", (void *)pr->u.getset.getter, - (void *)pr->u.getset.setter); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - printf("[varref %p]", (void *)pr->u.var_ref); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - printf("[autoinit %p %d %p]", - (void *)js_autoinit_get_realm(pr), - js_autoinit_get_id(pr), - (void *)pr->u.init.opaque); - } else { - JS_DumpValueShort(rt, pr->u.value); - } - is_first = FALSE; - } - } - printf(" }"); - } - - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b = p->u.func.function_bytecode; - JSVarRef **var_refs; - if (b->closure_var_count) { - var_refs = p->u.func.var_refs; - printf(" Closure:"); - for(i = 0; i < b->closure_var_count; i++) { - printf(" "); - JS_DumpValueShort(rt, var_refs[i]->value); - } - if (p->u.func.home_object) { - printf(" HomeObject: "); - JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); - } - } - } - printf("\n"); -} - -static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) -{ - if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - JS_DumpObject(rt, (JSObject *)p); - } else { - printf("%14p %4d ", - (void *)p, - p->ref_count); - switch(p->gc_obj_type) { - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - printf("[function bytecode]"); - break; - case JS_GC_OBJ_TYPE_SHAPE: - printf("[shape]"); - break; - case JS_GC_OBJ_TYPE_VAR_REF: - printf("[var_ref]"); - break; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - printf("[async_function]"); - break; - case JS_GC_OBJ_TYPE_JS_CONTEXT: - printf("[js_context]"); - break; - default: - printf("[unknown %d]", p->gc_obj_type); - break; - } - printf("\n"); - } -} - -static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val) -{ - uint32_t tag = JS_VALUE_GET_NORM_TAG(val); - const char *str; - - switch(tag) { - case JS_TAG_INT: - printf("%d", JS_VALUE_GET_INT(val)); - break; - case JS_TAG_BOOL: - if (JS_VALUE_GET_BOOL(val)) - str = "true"; - else - str = "false"; - goto print_str; - case JS_TAG_NULL: - str = "null"; - goto print_str; - case JS_TAG_EXCEPTION: - str = "exception"; - goto print_str; - case JS_TAG_UNINITIALIZED: - str = "uninitialized"; - goto print_str; - case JS_TAG_UNDEFINED: - str = "undefined"; - print_str: - printf("%s", str); - break; - case JS_TAG_FLOAT64: - printf("%.14g", JS_VALUE_GET_FLOAT64(val)); - break; - 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); - } - break; - case JS_TAG_BIG_DECIMAL: - { - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - char *str; - str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF, - BF_RNDZ | BF_FTOA_FORMAT_FREE); - printf("%sm", str); - bf_free(&rt->bf_ctx, str); - } - break; -#endif - case JS_TAG_STRING: - { - JSString *p; - p = JS_VALUE_GET_STRING(val); - JS_DumpString(rt, p); - } - break; - case JS_TAG_FUNCTION_BYTECODE: - { - JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); - } - break; - case JS_TAG_OBJECT: - { - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAtom atom = rt->class_array[p->class_id].class_name; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - printf("[%s %p]", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); - } - break; - case JS_TAG_SYMBOL: - { - JSAtomStruct *p = JS_VALUE_GET_PTR(val); - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - printf("Symbol(%s)", - JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); - } - break; - case JS_TAG_MODULE: - printf("[module]"); - break; - default: - printf("[unknown tag %d]", tag); - break; - } -} - -static __maybe_unused void JS_DumpValue(JSContext *ctx, - JSValueConst val) -{ - JS_DumpValueShort(ctx->rt, val); -} - -static __maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val) -{ - printf("%s=", str); - JS_DumpValueShort(ctx->rt, val); - printf("\n"); -} - -/* return -1 if exception (proxy case) or TRUE/FALSE */ -// TODO: should take flags to make proxy resolution and exceptions optional -int JS_IsArray(JSContext *ctx, JSValueConst val) -{ - if (js_resolve_proxy(ctx, &val, TRUE)) - return -1; - if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ARRAY; - } else { - return FALSE; - } -} - -static double js_pow(double a, double b) -{ - if (unlikely(!isfinite(b)) && fabs(a) == 1) { - /* not compatible with IEEE 754 */ - return JS_FLOAT64_NAN; - } else { - return pow(a, b); - } -} - -JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) -{ - JSValue val; - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_si(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - return val; -} - -JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) -{ - if (is_math_mode(ctx) && - v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { - return JS_NewInt64(ctx, v); - } else { - return JS_NewBigInt64_1(ctx, v); - } -} - -JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) -{ - JSValue val; - if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { - val = JS_NewInt64(ctx, v); - } else { - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_ui(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - } - return val; -} - -/* return NaN if bad bigint literal */ -static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) -{ - const char *str, *p; - size_t len; - int flags; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - val = JS_NewBigInt64(ctx, 0); - } else { - flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; -#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)) { - if ((p - str) != len) { - JS_FreeValue(ctx, val); - val = JS_NAN; - } - } - } - JS_FreeCString(ctx, str); - return val; -} - -static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) -{ - val = JS_StringToBigInt(ctx, val); - if (JS_VALUE_IS_NAN(val)) - return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); - return val; -} - -/* if the returned bigfloat is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */ -static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) -{ - uint32_t tag; - bf_t *r; - JSBigFloat *p; - - redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - if (!is_math_mode(ctx)) - goto fail; - /* fall tru */ - case JS_TAG_BOOL: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (!is_math_mode(ctx)) - goto fail; - if (!isfinite(d)) - goto fail; - r = buf; - bf_init(ctx->bf_ctx, r); - d = trunc(d); - bf_set_float64(r, d); - } - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; -#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); - break; -#endif - case JS_TAG_STRING: - val = JS_StringToBigIntErr(ctx, val); - if (JS_IsException(val)) - return NULL; - goto redo; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - return NULL; - goto redo; - default: - fail: - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "cannot convert to bigint"); - return NULL; - } - return r; -} - -static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val)); -} - -static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { - return val; - } else { - bf_t a_s, *a, *r; - int ret; - JSValue res; - - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - r = JS_GetBigInt(res); - ret = bf_set(r, a); - JS_FreeBigInt(ctx, a, &a_s); - if (ret) { - JS_FreeValue(ctx, res); - return JS_ThrowOutOfMemory(ctx); - } - return JS_CompactBigInt(ctx, res); - } -} - -/* free the bf_t allocated by JS_ToBigInt */ -static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) -{ - if (a == buf) { - bf_delete(a); - } else { - JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - - offsetof(JSBigFloat, num)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); - } -} - -/* XXX: merge with JS_ToInt64Free with a specific flag */ -static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) -{ - bf_t a_s, *a; - - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { - *pres = 0; - return -1; - } - bf_get_int64(pres, a, BF_GET_INT_MOD); - JS_FreeBigInt(ctx, a, &a_s); - return 0; -} - -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); -} - -static JSBigFloat *js_new_bf(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return NULL; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return p; -} - -static JSValue JS_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) -{ - uint32_t tag; - bf_t *r; - JSBigFloat *p; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_si(r, JS_VALUE_GET_INT(val))) - goto fail; - break; - case JS_TAG_FLOAT64: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { - fail: - bf_delete(r); - return NULL; - } - break; - case JS_TAG_BIG_INT: -#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; - 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: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - d += v; - break; - case OP_plus: - break; - case OP_neg: - d = -d; - break; - default: - abort(); - } - sp[-1] = __JS_NewFloat64(ctx, d); - } - break; - } - return 0; - exception: - sp[-1] = JS_UNDEFINED; - return -1; -} - -static __exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) -{ - JSValue op1; - - /* XXX: allow custom operators */ - op1 = sp[-1]; - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - sp[-1] = JS_UNDEFINED; - return -1; - } - sp[-1] = op1; - sp[0] = JS_DupValue(ctx, op1); - return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); -} - -static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1; - - 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)) - goto exception; - } else { - int32_t v1; - if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) - goto exception; - sp[-1] = JS_NewInt32(ctx, ~v1); - } - return 0; - exception: - sp[-1] = JS_UNDEFINED; - return -1; -} - -static int js_binary_arith_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) -{ - JSValue op1, op2; - uint32_t tag1, tag2; - double d1, d2; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - /* fast path for float operations */ - if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - d2 = JS_VALUE_GET_FLOAT64(op2); - goto handle_float64; - } - -#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; - } - } - } -#endif - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { - int32_t v1, v2; - int64_t v; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - switch(op) { - case OP_sub: - v = (int64_t)v1 - (int64_t)v2; - break; - case OP_mul: - v = (int64_t)v1 * (int64_t)v2; - if (is_math_mode(ctx) && - (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) - goto handle_bigint; - if (v == 0 && (v1 | v2) < 0) { - sp[-2] = __JS_NewFloat64(ctx, -0.0); - return 0; - } - break; - case OP_div: - if (is_math_mode(ctx)) - goto handle_bigint; - sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); - return 0; -#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)); - return 0; - } else { - v = (int64_t)v1 % (int64_t)v2; - } - break; - case OP_pow: - if (!is_math_mode(ctx)) { - sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); - return 0; - } else { - goto handle_bigint; - } - break; - default: - abort(); - } - sp[-2] = JS_NewInt64(ctx, v); - } else -#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; - switch(op) { - case OP_sub: - dr = d1 - d2; - break; - case OP_mul: - dr = d1 * d2; - break; - case OP_div: - dr = d1 / d2; - break; - case OP_mod: - dr = fmod(d1, d2); - break; -#ifdef CONFIG_BIGNUM - case OP_math_mod: - d2 = fabs(d2); - 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; - default: - abort(); - } - sp[-2] = __JS_NewFloat64(ctx, dr); - } - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - uint32_t tag1, tag2; - - op1 = sp[-2]; - op2 = sp[-1]; - - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - /* fast path for float64 */ - if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { - double d1, d2; - d1 = JS_VALUE_GET_FLOAT64(op1); - d2 = JS_VALUE_GET_FLOAT64(op2); - sp[-2] = __JS_NewFloat64(ctx, d1 + d2); - return 0; - } - - if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { -#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; - } - } - } -#endif - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - } - - if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { - sp[-2] = JS_ConcatString(ctx, op1, op2); - if (JS_IsException(sp[-2])) - goto exception; - return 0; - } - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { - int32_t v1, v2; - int64_t v; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - v = (int64_t)v1 + (int64_t)v2; - sp[-2] = JS_NewInt64(ctx, v); - } else -#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)) - goto exception; - } else { - double d1, d2; - /* float64 result */ - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) - goto handle_bigint; - sp[-2] = __JS_NewFloat64(ctx, d1 + d2); - } - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2; - uint32_t tag1, tag2; - uint32_t v1, v2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - -#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; - } - } - } -#endif - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - - if (is_math_mode(ctx)) - goto bigint_op; - - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } else { - bigint_op: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } - } else { - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) - goto exception; - switch(op) { - case OP_shl: - r = v1 << (v2 & 0x1f); - break; - case OP_sar: - r = (int)v1 >> (v2 & 0x1f); - break; - case OP_and: - r = v1 & v2; - break; - case OP_or: - r = v1 | v2; - break; - case OP_xor: - r = v1 ^ v2; - break; - default: - abort(); - } - sp[-2] = JS_NewInt32(ctx, r); - } - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -/* Note: also used for bigint */ -static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2) -{ - bf_t a_s, b_s, *a, *b; - int res; - - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) { - JS_FreeValue(ctx, op2); - return -1; - } - b = JS_ToBigFloat(ctx, &b_s, op2); - if (!b) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - return -1; - } - switch(op) { - case OP_lt: - res = bf_cmp_lt(a, b); /* if NaN return false */ - break; - case OP_lte: - res = bf_cmp_le(a, b); /* if NaN return false */ - break; - case OP_gt: - res = bf_cmp_lt(b, a); /* if NaN return false */ - break; - case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ - break; - case OP_eq: - res = bf_cmp_eq(a, b); /* if NaN return false */ - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return res; -} - -#ifdef CONFIG_BIGNUM -static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2) -{ - bfdec_t *a, *b; - int res; - - /* Note: binary floats are converted to bigdecimal with - toString(). It is not mathematically correct but is consistent - with the BigDecimal() constructor behavior */ - op1 = JS_ToBigDecimalFree(ctx, op1, TRUE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - return -1; - } - op2 = JS_ToBigDecimalFree(ctx, op2, TRUE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return -1; - } - a = JS_ToBigDecimal(ctx, op1); /* cannot fail */ - b = JS_ToBigDecimal(ctx, op2); /* cannot fail */ - - switch(op) { - case OP_lt: - res = bfdec_cmp_lt(a, b); /* if NaN return false */ - break; - case OP_lte: - res = bfdec_cmp_le(a, b); /* if NaN return false */ - break; - case OP_gt: - res = bfdec_cmp_lt(b, a); /* if NaN return false */ - break; - case OP_gte: - res = bfdec_cmp_le(b, a); /* if NaN return false */ - break; - case OP_eq: - res = bfdec_cmp_eq(a, b); /* if NaN return false */ - break; - default: - abort(); - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return res; -} -#endif /* !CONFIG_BIGNUM */ - -static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2; - int res; - uint32_t tag1, tag2; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); -#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); - goto exception; - } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { - JSString *p1, *p2; - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = js_string_compare(ctx, p1, p2); - switch(op) { - case OP_lt: - res = (res < 0); - break; - case OP_lte: - res = (res <= 0); - break; - case OP_gt: - res = (res > 0); - break; - default: - case OP_gte: - res = (res >= 0); - break; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && - (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { - /* 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) { - op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) - goto invalid_bigint_string; - } - if (tag2 == JS_TAG_STRING) { - op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { - invalid_bigint_string: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - res = FALSE; - goto done; - } - } - } else { - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - } - - 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; - } else { - double d1, d2; - - float64_compare: - /* can use floating point comparison */ - if (tag1 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - } else { - d1 = JS_VALUE_GET_INT(op1); - } - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else { - d2 = JS_VALUE_GET_INT(op2); - } - switch(op) { - case OP_lt: - res = (d1 < d2); /* if NaN return false */ - break; - case OP_lte: - res = (d1 <= d2); /* if NaN return false */ - break; - case OP_gt: - res = (d1 > d2); /* if NaN return false */ - break; - default: - case OP_gte: - res = (d1 >= d2); /* if NaN return false */ - break; - } - } - } - done: - sp[-2] = JS_NewBool(ctx, res); - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static BOOL tag_is_number(uint32_t tag) -{ - return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_FLOAT64 -#ifdef CONFIG_BIGNUM - || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL -#endif - ); -} - -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; - - op1 = sp[-2]; - op2 = sp[-1]; - redo: - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag_is_number(tag1) && tag_is_number(tag2)) { - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { - res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); - } else if ((tag1 == JS_TAG_FLOAT64 && - (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || - (tag2 == JS_TAG_FLOAT64 && - (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { - double d1, d2; - if (tag1 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - } else { - d1 = JS_VALUE_GET_INT(op1); - } - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else { - d2 = JS_VALUE_GET_INT(op2); - } - res = (d1 == d2); - } else -#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 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))) { - - if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - !is_math_mode(ctx)) { - if (tag1 == JS_TAG_STRING) { - op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) - goto invalid_bigint_string; - } - if (tag2 == JS_TAG_STRING) { - op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { - invalid_bigint_string: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - res = FALSE; - goto done; - } - } - } else { - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - } - res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); - } else if (tag1 == JS_TAG_BOOL) { - op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); - goto redo; - } else if (tag2 == JS_TAG_BOOL) { - op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); - goto redo; - } else if ((tag1 == JS_TAG_OBJECT && - (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || - (tag2 == JS_TAG_OBJECT && - (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { -#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 - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - goto redo; - } else { - /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ - if ((JS_IsHTMLDDA(ctx, op1) && - (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || - (JS_IsHTMLDDA(ctx, op2) && - (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { - res = TRUE; - } else { - res = FALSE; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - } - done: - sp[-2] = JS_NewBool(ctx, res ^ is_neq); - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - uint32_t v1, v2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - /* XXX: could forbid >>> in bignum mode */ - if (!is_math_mode(ctx) && - (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { - JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - goto exception; - } - /* cannot give an exception */ - JS_ToUint32Free(ctx, &v1, op1); - JS_ToUint32Free(ctx, &v2, op2); - r = v1 >> (v2 & 0x1f); - sp[-2] = JS_NewUint32(ctx, r); - return 0; - exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -#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) -{ - BOOL res; - int tag1, tag2; - double d1, d2; - - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - switch(tag1) { - case JS_TAG_BOOL: - if (tag1 != tag2) { - res = FALSE; - } else { - res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); - goto done_no_free; - } - break; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - res = (tag1 == tag2); - break; - case JS_TAG_STRING: - { - JSString *p1, *p2; - if (tag1 != tag2) { - res = FALSE; - } else { - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = (js_string_compare(ctx, p1, p2) == 0); - } - } - break; - case JS_TAG_SYMBOL: - { - JSAtomStruct *p1, *p2; - if (tag1 != tag2) { - res = FALSE; - } else { - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - res = (p1 == p2); - } - } - break; - case JS_TAG_OBJECT: - if (tag1 != tag2) - res = FALSE; - else - res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); - break; - case JS_TAG_INT: - d1 = JS_VALUE_GET_INT(op1); - if (tag2 == JS_TAG_INT) { - d2 = JS_VALUE_GET_INT(op2); - goto number_test; - } else if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - goto number_test; - } else { - res = FALSE; - } - break; - case JS_TAG_FLOAT64: - d1 = JS_VALUE_GET_FLOAT64(op1); - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else if (tag2 == JS_TAG_INT) { - d2 = JS_VALUE_GET_INT(op2); - } else { - res = FALSE; - break; - } - number_test: - if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { - JSFloat64Union u1, u2; - /* NaN is not always normalized, so this test is necessary */ - if (isnan(d1) || isnan(d2)) { - res = isnan(d1) == isnan(d2); - } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { - res = (d1 == d2); /* +0 == -0 */ - } else { - u1.d = d1; - u2.d = d2; - res = (u1.u64 == u2.u64); /* +0 != -0 */ - } - } else { - res = (d1 == d2); /* if NaN return false and +0 == -0 */ - } - goto done_no_free; - 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) { - res = FALSE; - break; - } - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - a = &p1->num; - b = &p2->num; - res = bfdec_cmp_eq(a, b); - } - break; -#endif - default: - res = FALSE; - break; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - done_no_free: - return res; -} - -static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2) -{ - 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) -{ - return js_strict_eq2(ctx, - JS_DupValue(ctx, op1), JS_DupValue(ctx, 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, - JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), - 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_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT); - sp[-2] = JS_NewBool(ctx, res ^ is_neq); - return 0; -} - -static __exception int js_operator_in(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - JSAtom atom; - int ret; - - op1 = sp[-2]; - op2 = sp[-1]; - - if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { - JS_ThrowTypeError(ctx, "invalid 'in' operand"); - return -1; - } - atom = JS_ValueToAtom(ctx, op1); - if (unlikely(atom == JS_ATOM_NULL)) - return -1; - ret = JS_HasProperty(ctx, op2, atom); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return -1; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -static __exception int js_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) -{ - JSValue arr, val; - int ret; - - arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); - if (JS_IsException(arr)) - return -1; - ret = 0; - if (JS_IsObject(arr)) { - val = JS_GetProperty(ctx, arr, atom); - ret = JS_ToBoolFree(ctx, val); - } - JS_FreeValue(ctx, arr); - return ret; -} - -static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - BOOL ret; - - op1 = sp[-2]; - op2 = sp[-1]; - ret = JS_IsInstanceOf(ctx, op1, op2); - if (ret < 0) - return ret; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) -{ - JSAtom atom; - uint32_t tag; - - tag = JS_VALUE_GET_NORM_TAG(op1); - switch(tag) { - 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; - break; - case JS_TAG_UNDEFINED: - atom = JS_ATOM_undefined; - break; - case JS_TAG_BOOL: - atom = JS_ATOM_boolean; - break; - case JS_TAG_STRING: - atom = JS_ATOM_string; - break; - case JS_TAG_OBJECT: - { - JSObject *p; - p = JS_VALUE_GET_OBJ(op1); - if (unlikely(p->is_HTMLDDA)) - atom = JS_ATOM_undefined; - else if (JS_IsFunction(ctx, op1)) - atom = JS_ATOM_function; - else - goto obj_type; - } - break; - case JS_TAG_NULL: - obj_type: - atom = JS_ATOM_object; - break; - case JS_TAG_SYMBOL: - atom = JS_ATOM_symbol; - break; - default: - atom = JS_ATOM_unknown; - break; - } - return atom; -} - -static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - JSAtom atom; - int ret; - - op1 = sp[-2]; - op2 = sp[-1]; - atom = JS_ValueToAtom(ctx, op2); - if (unlikely(atom == JS_ATOM_NULL)) - return -1; - ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - if (unlikely(ret < 0)) - return -1; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ThrowTypeError(ctx, "invalid property access"); -} - -/* XXX: not 100% compatible, but mozilla seems to use a similar - implementation to ensure that caller in non strict mode does not - throw (ES5 compatibility) */ -static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); - if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { - return js_throw_type_error(ctx, this_val, 0, NULL); - } - return JS_UNDEFINED; -} - -static JSValue js_function_proto_fileName(JSContext *ctx, - JSValueConst this_val) -{ - JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); - if (b && b->has_debug) { - return JS_AtomToString(ctx, b->debug.filename); - } - return JS_UNDEFINED; -} - -static JSValue js_function_proto_lineNumber(JSContext *ctx, - JSValueConst this_val) -{ - JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); - if (b && b->has_debug) { - return JS_NewInt32(ctx, b->debug.line_num); - } - return JS_UNDEFINED; -} - -static int js_arguments_define_own_property(JSContext *ctx, - JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, int flags) -{ - JSObject *p; - uint32_t idx; - p = JS_VALUE_GET_OBJ(this_obj); - /* convert to normal array when redefining an existing numeric field */ - if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && - idx < p->u.array.count) { - if (convert_fast_array_to_array(ctx, p)) - return -1; - } - /* run the default define own property */ - return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, - flags | JS_PROP_NO_EXOTIC); -} - -static const JSClassExoticMethods js_arguments_exotic_methods = { - .define_own_property = js_arguments_define_own_property, -}; - -static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) -{ - JSValue val, *tab; - JSProperty *pr; - JSObject *p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - pr->u.value = JS_NewInt32(ctx, argc); - - /* initialize the fast array part */ - tab = NULL; - if (argc > 0) { - tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - for(i = 0; i < argc; i++) { - tab[i] = JS_DupValue(ctx, argv[i]); - } - } - p->u.array.u.values = tab; - p->u.array.count = argc; - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, - JS_DupValue(ctx, ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* add callee property to throw a TypeError in strict mode */ - JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, - ctx->throw_type_error, ctx->throw_type_error, - JS_PROP_HAS_GET | JS_PROP_HAS_SET); - return val; -} - -#define GLOBAL_VAR_OFFSET 0x40000000 -#define ARGUMENT_VAR_OFFSET 0x20000000 - -/* legacy arguments object: add references to the function arguments */ -static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, - JSValueConst *argv, - JSStackFrame *sf, int arg_count) -{ - JSValue val; - JSProperty *pr; - JSObject *p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_MAPPED_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - pr->u.value = JS_NewInt32(ctx, argc); - - for(i = 0; i < arg_count; i++) { - JSVarRef *var_ref; - var_ref = get_var_ref(ctx, sf, i, TRUE); - if (!var_ref) - goto fail; - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); - if (!pr) { - free_var_ref(ctx->rt, var_ref); - goto fail; - } - pr->u.var_ref = var_ref; - } - - /* the arguments not mapped to the arguments of the function can - be normal properties */ - for(i = arg_count; i < argc; i++) { - if (JS_DefinePropertyValueUint32(ctx, val, i, - JS_DupValue(ctx, argv[i]), - JS_PROP_C_W_E) < 0) - goto fail; - } - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, - JS_DupValue(ctx, ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* callee returns this function in non strict mode */ - JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, - JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - return val; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv) -{ - JSValue val; - int i, ret; - - val = JS_NewArray(ctx); - if (JS_IsException(val)) - return val; - for (i = first; i < argc; i++) { - ret = JS_DefinePropertyValueUint32(ctx, val, i - first, - JS_DupValue(ctx, argv[i]), - JS_PROP_C_W_E); - if (ret < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - } - return val; -} - -static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) -{ - JSObject *p, *p1; - JSPropertyEnum *tab_atom; - int i; - JSValue enum_obj; - JSForInIterator *it; - uint32_t tag, tab_atom_count; - - tag = JS_VALUE_GET_TAG(obj); - if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { - obj = JS_ToObjectFree(ctx, obj); - } - - it = js_malloc(ctx, sizeof(*it)); - if (!it) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); - if (JS_IsException(enum_obj)) { - js_free(ctx, it); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - it->is_array = FALSE; - it->obj = obj; - it->idx = 0; - 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; - - p = JS_VALUE_GET_OBJ(obj); - if (p->fast_array) { - JSShape *sh; - JSShapeProperty *prs; - /* check that there are no enumerable normal fields */ - sh = p->shape; - for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - if (prs->flags & JS_PROP_ENUMERABLE) - goto normal_case; - } - /* for fast arrays, we only store the number of elements */ - it->is_array = TRUE; - 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_SET_ENUM)) { - JS_FreeValue(ctx, enum_obj); - return JS_EXCEPTION; - } - it->tab_atom = tab_atom; - it->atom_count = tab_atom_count; - } - return enum_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_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; - } - } - JS_FreeValue(ctx, obj1); - return 1; - - 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; - } - - 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 */ -static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) -{ - JSValueConst enum_obj; - JSObject *p; - JSAtom prop; - JSForInIterator *it; - JSPropertyEnum *tab_atom; - uint32_t tab_atom_count; - int ret; - - enum_obj = sp[-1]; - /* fail safe */ - if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) - goto done; - p = JS_VALUE_GET_OBJ(enum_obj); - if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) - goto done; - it = p->u.for_in_iterator; - - for(;;) { - if (it->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_free_prop_enum(ctx, it->tab_atom, it->atom_count); - it->tab_atom = tab_atom; - it->atom_count = tab_atom_count; - it->idx = 0; - } else { - 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; - } - } - /* return the property */ - sp[0] = JS_AtomToValue(ctx, prop); - sp[1] = JS_FALSE; - return 0; - done: - /* return the end */ - sp[0] = JS_UNDEFINED; - sp[1] = JS_TRUE; - return 0; -} - -static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, - JSValueConst method) -{ - JSValue enum_obj; - - enum_obj = JS_Call(ctx, method, obj, 0, NULL); - if (JS_IsException(enum_obj)) - return enum_obj; - if (!JS_IsObject(enum_obj)) { - JS_FreeValue(ctx, enum_obj); - return JS_ThrowTypeErrorNotAnObject(ctx); - } - return enum_obj; -} - -static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) -{ - JSValue method, ret, sync_iter; - - if (is_async) { - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); - if (JS_IsException(method)) - return method; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); - if (JS_IsException(method)) - return method; - sync_iter = JS_GetIterator2(ctx, obj, method); - JS_FreeValue(ctx, method); - if (JS_IsException(sync_iter)) - return sync_iter; - ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); - JS_FreeValue(ctx, sync_iter); - return ret; - } - } else { - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); - if (JS_IsException(method)) - return method; - } - if (!JS_IsFunction(ctx, method)) { - JS_FreeValue(ctx, method); - return JS_ThrowTypeError(ctx, "value is not iterable"); - } - ret = JS_GetIterator2(ctx, obj, method); - JS_FreeValue(ctx, method); - return ret; -} - -/* return *pdone = 2 if the iterator object is not parsed */ -static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, - JSValueConst method, - int argc, JSValueConst *argv, int *pdone) -{ - JSValue obj; - - /* fast path for the built-in iterators (avoid creating the - intermediate result object) */ - if (JS_IsObject(method)) { - JSObject *p = JS_VALUE_GET_OBJ(method); - if (p->class_id == JS_CLASS_C_FUNCTION && - p->u.cfunc.cproto == JS_CFUNC_iterator_next) { - JSCFunctionType func; - JSValueConst args[1]; - - /* in case the function expects one argument */ - if (argc == 0) { - args[0] = JS_UNDEFINED; - argv = args; - } - func = p->u.cfunc.c_function; - return func.iterator_next(ctx, enum_obj, argc, argv, - pdone, p->u.cfunc.magic); - } - } - obj = JS_Call(ctx, method, enum_obj, argc, argv); - if (JS_IsException(obj)) - goto fail; - if (!JS_IsObject(obj)) { - JS_FreeValue(ctx, obj); - JS_ThrowTypeError(ctx, "iterator must return an object"); - goto fail; - } - *pdone = 2; - return obj; - fail: - *pdone = FALSE; - return JS_EXCEPTION; -} - -static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, - JSValueConst method, - int argc, JSValueConst *argv, BOOL *pdone) -{ - JSValue obj, value, done_val; - int done; - - obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); - if (JS_IsException(obj)) - goto fail; - if (done != 2) { - *pdone = done; - return obj; - } else { - done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); - if (JS_IsException(done_val)) - goto fail; - *pdone = JS_ToBoolFree(ctx, done_val); - value = JS_UNDEFINED; - if (!*pdone) { - value = JS_GetProperty(ctx, obj, JS_ATOM_value); - } - JS_FreeValue(ctx, obj); - return value; - } - fail: - JS_FreeValue(ctx, obj); - *pdone = FALSE; - return JS_EXCEPTION; -} - -/* return < 0 in case of exception */ -static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, - BOOL is_exception_pending) -{ - JSValue method, ret, ex_obj; - int res; - - if (is_exception_pending) { - ex_obj = ctx->rt->current_exception; - ctx->rt->current_exception = JS_NULL; - res = -1; - } else { - ex_obj = JS_UNDEFINED; - res = 0; - } - method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); - if (JS_IsException(method)) { - res = -1; - goto done; - } - if (JS_IsUndefined(method) || JS_IsNull(method)) { - goto done; - } - ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); - if (!is_exception_pending) { - if (JS_IsException(ret)) { - res = -1; - } else if (!JS_IsObject(ret)) { - JS_ThrowTypeErrorNotAnObject(ctx); - res = -1; - } - } - JS_FreeValue(ctx, ret); - done: - if (is_exception_pending) { - JS_Throw(ctx, ex_obj); - } - return res; -} - -/* obj -> enum_rec (3 slots) */ -static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, - BOOL is_async) -{ - JSValue op1, obj, method; - op1 = sp[-1]; - obj = JS_GetIterator(ctx, op1, is_async); - if (JS_IsException(obj)) - return -1; - JS_FreeValue(ctx, op1); - sp[-1] = obj; - method = JS_GetProperty(ctx, obj, JS_ATOM_next); - if (JS_IsException(method)) - return -1; - sp[0] = method; - return 0; -} - -/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' - objs. If 'done' is true or in case of exception, 'enum_rec' is set - to undefined. If 'done' is true, 'value' is always set to - undefined. */ -static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) -{ - JSValue value = JS_UNDEFINED; - int done = 1; - - if (likely(!JS_IsUndefined(sp[offset]))) { - value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); - if (JS_IsException(value)) - done = -1; - if (done) { - /* value is JS_UNDEFINED or JS_EXCEPTION */ - /* replace the iteration object with undefined */ - JS_FreeValue(ctx, sp[offset]); - sp[offset] = JS_UNDEFINED; - if (done < 0) { - return -1; - } else { - JS_FreeValue(ctx, value); - value = JS_UNDEFINED; - } - } - } - sp[0] = value; - sp[1] = JS_NewBool(ctx, done); - return 0; -} - -static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, - BOOL *pdone) -{ - JSValue done_val, value; - BOOL done; - done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); - if (JS_IsException(done_val)) - goto fail; - done = JS_ToBoolFree(ctx, done_val); - value = JS_GetProperty(ctx, obj, JS_ATOM_value); - if (JS_IsException(value)) - goto fail; - *pdone = done; - return value; - fail: - *pdone = FALSE; - return JS_EXCEPTION; -} - -static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) -{ - JSValue obj, value; - BOOL done; - obj = sp[-1]; - if (!JS_IsObject(obj)) { - JS_ThrowTypeError(ctx, "iterator must return an object"); - return -1; - } - value = JS_IteratorGetCompleteValue(ctx, obj, &done); - if (JS_IsException(value)) - return -1; - JS_FreeValue(ctx, obj); - sp[-1] = value; - sp[0] = JS_NewBool(ctx, done); - return 0; -} - -static JSValue js_create_iterator_result(JSContext *ctx, - JSValue val, - BOOL done) -{ - JSValue obj; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, val); - return obj; - } - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, - val, JS_PROP_C_W_E) < 0) { - goto fail; - } - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, - JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) { - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic); - -static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); - -static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) -{ - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - return TRUE; - } - } - return FALSE; -} - -/* Access an Array's internal JSValue array if available */ -static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, - JSValue **arrpp, uint32_t *countp) -{ - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - *countp = p->u.array.count; - *arrpp = p->u.array.u.values; - return TRUE; - } - } - return FALSE; -} - -static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) -{ - JSValue iterator, enumobj, method, value; - int 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; - } - - pos = JS_VALUE_GET_INT(sp[-2]); - - /* XXX: further optimisations: - - use ctx->array_proto_values? - - check if array_iterator_prototype next method is built-in and - avoid constructing actual iterator object? - - build this into js_for_of_start and use in all `for (x of o)` loops - */ - iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); - if (JS_IsException(iterator)) - return -1; - is_array_iterator = JS_IsCFunction(ctx, iterator, - (JSCFunction *)js_create_array_iterator, - JS_ITERATOR_KIND_VALUE); - JS_FreeValue(ctx, iterator); - - enumobj = JS_GetIterator(ctx, sp[-1], FALSE); - if (JS_IsException(enumobj)) - return -1; - method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); - if (JS_IsException(method)) { - JS_FreeValue(ctx, enumobj); - return -1; - } - if (is_array_iterator - && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) - && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { - uint32_t len; - if (js_get_length32(ctx, &len, sp[-1])) - goto exception; - /* if len > count32, the elements >= count32 might be read in - the prototypes and might have side effects */ - if (len != count32) - goto general_case; - /* Handle fast arrays explicitly */ - for (i = 0; i < count32; i++) { - if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, - JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) - goto exception; - } - } else { - general_case: - for (;;) { - BOOL done; - value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); - if (JS_IsException(value)) - goto exception; - if (done) { - /* value is JS_UNDEFINED */ - break; - } - if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) - goto exception; - } - } - /* Note: could raise an error if too many elements */ - sp[-2] = JS_NewInt32(ctx, pos); - JS_FreeValue(ctx, enumobj); - JS_FreeValue(ctx, method); - return 0; - -exception: - JS_IteratorClose(ctx, enumobj, TRUE); - JS_FreeValue(ctx, enumobj); - JS_FreeValue(ctx, method); - return -1; -} - -static __exception int JS_CopyDataProperties(JSContext *ctx, - JSValueConst target, - JSValueConst source, - JSValueConst excluded, - BOOL setprop) -{ - JSPropertyEnum *tab_atom; - JSValue val; - uint32_t i, tab_atom_count; - JSObject *p; - JSObject *pexcl = NULL; - int ret, gpn_flags; - JSPropertyDescriptor desc; - BOOL is_enumerable; - - if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) - return 0; - - if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) - pexcl = JS_VALUE_GET_OBJ(excluded); - - p = JS_VALUE_GET_OBJ(source); - - gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; - if (p->is_exotic) { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it - introduces a visible change */ - if (em && em->get_own_property_names) { - gpn_flags &= ~JS_GPN_ENUM_ONLY; - } - } - 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); - if (ret) { - if (ret < 0) - goto exception; - continue; - } - } - if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { - /* test if the property is enumerable */ - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); - if (ret < 0) - goto exception; - if (!ret) - continue; - is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; - js_free_desc(ctx, &desc); - if (!is_enumerable) - continue; - } - val = JS_GetProperty(ctx, source, tab_atom[i].atom); - if (JS_IsException(val)) - goto exception; - if (setprop) - ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); - else - ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, - JS_PROP_C_W_E); - if (ret < 0) - goto exception; - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - return 0; - exception: - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - return -1; -} - -/* only valid inside C functions */ -static JSValueConst JS_GetActiveFunction(JSContext *ctx) -{ - return ctx->rt->current_stack_frame->cur_func; -} - -static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, - int var_idx, BOOL is_arg) -{ - JSVarRef *var_ref; - struct list_head *el; - - list_for_each(el, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, var_ref_link); - if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { - var_ref->header.ref_count++; - return var_ref; - } - } - /* create a new one */ - var_ref = js_malloc(ctx, sizeof(JSVarRef)); - if (!var_ref) - return NULL; - var_ref->header.ref_count = 1; - 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->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; - } - if (is_arg) - var_ref->pvalue = &sf->arg_buf[var_idx]; - else - var_ref->pvalue = &sf->var_buf[var_idx]; - return var_ref; -} - -static JSValue js_closure2(JSContext *ctx, JSValue func_obj, - JSFunctionBytecode *b, - JSVarRef **cur_var_refs, - JSStackFrame *sf) -{ - JSObject *p; - JSVarRef **var_refs; - int i; - - p = JS_VALUE_GET_OBJ(func_obj); - p->u.func.function_bytecode = b; - p->u.func.home_object = NULL; - p->u.func.var_refs = NULL; - if (b->closure_var_count) { - var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); - if (!var_refs) - goto fail; - p->u.func.var_refs = var_refs; - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JSVarRef *var_ref; - if (cv->is_local) { - /* reuse the existing variable reference if it already exists */ - var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); - if (!var_ref) - goto fail; - } else { - var_ref = cur_var_refs[cv->var_idx]; - var_ref->header.ref_count++; - } - var_refs[i] = var_ref; - } - } - return func_obj; - fail: - /* bfunc is freed when func_obj is freed */ - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) -{ - JSValue obj, this_val; - int ret; - - this_val = JS_MKPTR(JS_TAG_OBJECT, p); - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - set_cycle_flag(ctx, obj); - set_cycle_flag(ctx, this_val); - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, - JS_DupValue(ctx, this_val), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (ret < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -static const uint16_t func_kind_to_class_id[] = { - [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, - [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, - [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, - [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, -}; - -static JSValue js_closure(JSContext *ctx, JSValue bfunc, - JSVarRef **cur_var_refs, - JSStackFrame *sf) -{ - JSFunctionBytecode *b; - JSValue func_obj; - JSAtom name_atom; - - b = JS_VALUE_GET_PTR(bfunc); - func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); - if (JS_IsException(func_obj)) { - JS_FreeValue(ctx, bfunc); - return JS_EXCEPTION; - } - func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); - if (JS_IsException(func_obj)) { - /* bfunc has been freed */ - goto fail; - } - name_atom = b->func_name; - if (name_atom == JS_ATOM_NULL) - name_atom = JS_ATOM_empty_string; - js_function_set_properties(ctx, func_obj, name_atom, - b->defined_arg_count); - - if (b->func_kind & JS_FUNC_GENERATOR) { - JSValue proto; - int proto_class_id; - /* generators have a prototype field which is used as - prototype for the generator object */ - if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) - proto_class_id = JS_CLASS_ASYNC_GENERATOR; - else - proto_class_id = JS_CLASS_GENERATOR; - proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); - if (JS_IsException(proto)) - goto fail; - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, - JS_PROP_WRITABLE); - } else if (b->has_prototype) { - /* add the 'prototype' property: delay instantiation to avoid - creating cycles for every javascript function. The prototype - object is created on the fly when first accessed */ - JS_SetConstructorBit(ctx, func_obj, TRUE); - JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, - JS_AUTOINIT_ID_PROTOTYPE, NULL, - JS_PROP_WRITABLE); - } - return func_obj; - fail: - /* bfunc is freed when func_obj is freed */ - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) - -static int js_op_define_class(JSContext *ctx, JSValue *sp, - JSAtom class_name, int class_flags, - JSVarRef **cur_var_refs, - JSStackFrame *sf, BOOL is_computed_name) -{ - JSValue bfunc, parent_class, proto = JS_UNDEFINED; - JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; - JSFunctionBytecode *b; - - parent_class = sp[-2]; - bfunc = sp[-1]; - - if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { - if (JS_IsNull(parent_class)) { - parent_proto = JS_NULL; - parent_class = JS_DupValue(ctx, ctx->function_proto); - } else { - if (!JS_IsConstructor(ctx, parent_class)) { - JS_ThrowTypeError(ctx, "parent class must be constructor"); - goto fail; - } - parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); - if (JS_IsException(parent_proto)) - goto fail; - if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { - JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); - goto fail; - } - } - } else { - /* parent_class is JS_UNDEFINED in this case */ - parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); - parent_class = JS_DupValue(ctx, ctx->function_proto); - } - proto = JS_NewObjectProto(ctx, parent_proto); - if (JS_IsException(proto)) - goto fail; - - b = JS_VALUE_GET_PTR(bfunc); - assert(b->func_kind == JS_FUNC_NORMAL); - ctor = JS_NewObjectProtoClass(ctx, parent_class, - JS_CLASS_BYTECODE_FUNCTION); - if (JS_IsException(ctor)) - goto fail; - ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); - bfunc = JS_UNDEFINED; - if (JS_IsException(ctor)) - goto fail; - js_method_set_home_object(ctx, ctor, proto); - JS_SetConstructorBit(ctx, ctor, TRUE); - - JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, - JS_NewInt32(ctx, b->defined_arg_count), - JS_PROP_CONFIGURABLE); - - if (is_computed_name) { - if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], - JS_PROP_CONFIGURABLE) < 0) - goto fail; - } else { - if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) - goto fail; - } - - /* the constructor property must be first. It can be overriden by - computed property names */ - if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, - JS_DupValue(ctx, ctor), - JS_PROP_CONFIGURABLE | - JS_PROP_WRITABLE | JS_PROP_THROW) < 0) - goto fail; - /* set the prototype property */ - if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, - JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) - goto fail; - set_cycle_flag(ctx, ctor); - set_cycle_flag(ctx, proto); - - JS_FreeValue(ctx, parent_proto); - JS_FreeValue(ctx, parent_class); - - sp[-2] = ctor; - sp[-1] = proto; - return 0; - fail: - JS_FreeValue(ctx, parent_class); - JS_FreeValue(ctx, parent_proto); - JS_FreeValue(ctx, bfunc); - JS_FreeValue(ctx, proto); - JS_FreeValue(ctx, ctor); - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) -{ - struct list_head *el, *el1; - JSVarRef *var_ref; - int var_idx; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, 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_idx = var_ref->var_idx; - if (var_ref->is_arg) - var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); - else - var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - } -} - -static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) -{ - struct list_head *el, *el1; - JSVarRef *var_ref; - int var_idx = idx; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, var_ref_link); - if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { - 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, sf->var_buf[var_idx]); - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - } - } -} - -#define JS_CALL_FLAG_COPY_ARGV (1 << 1) -#define JS_CALL_FLAG_GENERATOR (1 << 2) - -static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSRuntime *rt = ctx->rt; - JSCFunctionType func; - JSObject *p; - JSStackFrame sf_s, *sf = &sf_s, *prev_sf; - JSValue ret_val; - JSValueConst *arg_buf; - int arg_count, i; - JSCFunctionEnum cproto; - - p = JS_VALUE_GET_OBJ(func_obj); - cproto = p->u.cfunc.cproto; - arg_count = p->u.cfunc.length; - - /* better to always check stack overflow */ - if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) - return JS_ThrowStackOverflow(ctx); - - prev_sf = rt->current_stack_frame; - sf->prev_frame = prev_sf; - rt->current_stack_frame = sf; - ctx = p->u.cfunc.realm; /* change the current realm */ - -#ifdef CONFIG_BIGNUM - /* we only propagate the bignum mode as some runtime functions - test it */ - if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; - else - sf->js_mode = 0; -#else - sf->js_mode = 0; -#endif - sf->cur_func = (JSValue)func_obj; - sf->arg_count = argc; - arg_buf = argv; - - if (unlikely(argc < arg_count)) { - /* ensure that at least argc_count arguments are readable */ - arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); - for(i = 0; i < argc; i++) - arg_buf[i] = argv[i]; - for(i = argc; i < arg_count; i++) - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = arg_count; - } - sf->arg_buf = (JSValue*)arg_buf; - - func = p->u.cfunc.c_function; - switch(cproto) { - case JS_CFUNC_constructor: - case JS_CFUNC_constructor_or_func: - if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { - if (cproto == JS_CFUNC_constructor) { - not_a_constructor: - ret_val = JS_ThrowTypeError(ctx, "must be called with new"); - break; - } else { - this_obj = JS_UNDEFINED; - } - } - /* here this_obj is new_target */ - /* fall thru */ - case JS_CFUNC_generic: - ret_val = func.generic(ctx, this_obj, argc, arg_buf); - break; - case JS_CFUNC_constructor_magic: - case JS_CFUNC_constructor_or_func_magic: - if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { - if (cproto == JS_CFUNC_constructor_magic) { - goto not_a_constructor; - } else { - this_obj = JS_UNDEFINED; - } - } - /* fall thru */ - case JS_CFUNC_generic_magic: - ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, - p->u.cfunc.magic); - break; - case JS_CFUNC_getter: - ret_val = func.getter(ctx, this_obj); - break; - case JS_CFUNC_setter: - ret_val = func.setter(ctx, this_obj, arg_buf[0]); - break; - case JS_CFUNC_getter_magic: - ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); - break; - case JS_CFUNC_setter_magic: - ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); - break; - case JS_CFUNC_f_f: - { - double d1; - - if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { - ret_val = JS_EXCEPTION; - break; - } - ret_val = JS_NewFloat64(ctx, func.f_f(d1)); - } - break; - case JS_CFUNC_f_f_f: - { - double d1, d2; - - if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { - ret_val = JS_EXCEPTION; - break; - } - if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { - ret_val = JS_EXCEPTION; - break; - } - ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); - } - break; - case JS_CFUNC_iterator_next: - { - int done; - ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, - &done, p->u.cfunc.magic); - if (!JS_IsException(ret_val) && done != 2) { - ret_val = js_create_iterator_result(ctx, ret_val, done); - } - } - break; - default: - abort(); - } - - rt->current_stack_frame = sf->prev_frame; - return ret_val; -} - -static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSObject *p; - JSBoundFunction *bf; - JSValueConst *arg_buf, new_target; - int arg_count, i; - - p = JS_VALUE_GET_OBJ(func_obj); - bf = p->u.bound_function; - arg_count = bf->argc + argc; - if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) - return JS_ThrowStackOverflow(ctx); - arg_buf = alloca(sizeof(JSValue) * arg_count); - for(i = 0; i < bf->argc; i++) { - arg_buf[i] = bf->argv[i]; - } - for(i = 0; i < argc; i++) { - arg_buf[bf->argc + i] = argv[i]; - } - if (flags & JS_CALL_FLAG_CONSTRUCTOR) { - new_target = this_obj; - if (js_same_value(ctx, func_obj, new_target)) - new_target = bf->func_obj; - return JS_CallConstructor2(ctx, bf->func_obj, new_target, - arg_count, arg_buf); - } else { - return JS_Call(ctx, bf->func_obj, bf->this_val, - arg_count, arg_buf); - } -} - -/* argument of OP_special_object */ -typedef enum { - OP_SPECIAL_OBJECT_ARGUMENTS, - OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, - OP_SPECIAL_OBJECT_THIS_FUNC, - OP_SPECIAL_OBJECT_NEW_TARGET, - OP_SPECIAL_OBJECT_HOME_OBJECT, - OP_SPECIAL_OBJECT_VAR_OBJECT, - OP_SPECIAL_OBJECT_IMPORT_META, -} OPSpecialObjectEnum; - -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 -#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, - JSValueConst this_obj, JSValueConst new_target, - int argc, JSValue *argv, int flags) -{ - JSRuntime *rt = caller_ctx->rt; - JSContext *ctx; - JSObject *p; - JSFunctionBytecode *b; - JSStackFrame sf_s, *sf = &sf_s; - const uint8_t *pc; - int opcode, arg_allocated_size, i; - JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; - JSVarRef **var_refs; - size_t alloca_size; - -#if !DIRECT_DISPATCH -#define SWITCH(pc) switch (opcode = *pc++) -#define CASE(op) case op -#define DEFAULT default -#define BREAK break -#else - static const void * const dispatch_table[256] = { -#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, -#if SHORT_OPCODES -#define def(id, size, n_pop, n_push, f) -#else -#define def(id, size, n_pop, n_push, f) && case_default, -#endif -#include "quickjs-opcode.h" - [ OP_COUNT ... 255 ] = &&case_default - }; -#define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; -#define CASE(op) case_ ## op -#define DEFAULT case_default -#define BREAK SWITCH(pc) -#endif - - if (js_poll_interrupts(caller_ctx)) - return JS_EXCEPTION; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { - if (flags & JS_CALL_FLAG_GENERATOR) { - JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); - /* func_obj get contains a pointer to JSFuncAsyncState */ - /* the stack frame is already allocated */ - sf = &s->frame; - p = JS_VALUE_GET_OBJ(sf->cur_func); - b = p->u.func.function_bytecode; - ctx = b->realm; - var_refs = p->u.func.var_refs; - local_buf = arg_buf = sf->arg_buf; - var_buf = sf->var_buf; - stack_buf = sf->var_buf + b->var_count; - sp = sf->cur_sp; - sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ - pc = sf->cur_pc; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - if (s->throw_flag) - goto exception; - else - goto restart; - } else { - goto not_a_function; - } - } - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall *call_func; - call_func = rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(caller_ctx, "not a function"); - } - return call_func(caller_ctx, func_obj, this_obj, argc, - (JSValueConst *)argv, flags); - } - b = p->u.func.function_bytecode; - - if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { - arg_allocated_size = b->arg_count; - } else { - arg_allocated_size = 0; - } - - alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + - b->stack_size); - if (js_check_stack_overflow(rt, alloca_size)) - return JS_ThrowStackOverflow(caller_ctx); - - sf->js_mode = b->js_mode; - arg_buf = argv; - sf->arg_count = argc; - sf->cur_func = (JSValue)func_obj; - init_list_head(&sf->var_ref_list); - var_refs = p->u.func.var_refs; - - local_buf = alloca(alloca_size); - if (unlikely(arg_allocated_size)) { - int n = min_int(argc, b->arg_count); - arg_buf = local_buf; - for(i = 0; i < n; i++) - arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); - for(; i < b->arg_count; i++) - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = b->arg_count; - } - var_buf = local_buf + arg_allocated_size; - sf->var_buf = var_buf; - sf->arg_buf = arg_buf; - - for(i = 0; i < b->var_count; i++) - var_buf[i] = JS_UNDEFINED; - - stack_buf = var_buf + b->var_count; - sp = stack_buf; - pc = b->byte_code_buf; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - ctx = b->realm; /* set the current realm */ - - restart: - for(;;) { - int call_argc; - JSValue *call_argv; - - SWITCH(pc) { - CASE(OP_push_i32): - *sp++ = JS_NewInt32(ctx, get_u32(pc)); - pc += 4; - BREAK; - CASE(OP_push_const): - *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - BREAK; -#if SHORT_OPCODES - CASE(OP_push_minus1): - CASE(OP_push_0): - CASE(OP_push_1): - CASE(OP_push_2): - CASE(OP_push_3): - CASE(OP_push_4): - CASE(OP_push_5): - CASE(OP_push_6): - CASE(OP_push_7): - *sp++ = JS_NewInt32(ctx, opcode - OP_push_0); - BREAK; - CASE(OP_push_i8): - *sp++ = JS_NewInt32(ctx, get_i8(pc)); - pc += 1; - BREAK; - CASE(OP_push_i16): - *sp++ = JS_NewInt32(ctx, get_i16(pc)); - pc += 2; - BREAK; - CASE(OP_push_const8): - *sp++ = JS_DupValue(ctx, b->cpool[*pc++]); - BREAK; - CASE(OP_fclosure8): - *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_push_empty_string): - *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); - BREAK; - CASE(OP_get_length): - { - JSValue val; - - val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; -#endif - CASE(OP_push_atom_value): - *sp++ = JS_AtomToValue(ctx, get_u32(pc)); - pc += 4; - BREAK; - CASE(OP_undefined): - *sp++ = JS_UNDEFINED; - BREAK; - CASE(OP_null): - *sp++ = JS_NULL; - BREAK; - CASE(OP_push_this): - /* OP_push_this is only called at the start of a function */ - { - JSValue val; - if (!(b->js_mode & JS_MODE_STRICT)) { - uint32_t tag = JS_VALUE_GET_TAG(this_obj); - if (likely(tag == JS_TAG_OBJECT)) - goto normal_this; - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { - val = JS_DupValue(ctx, ctx->global_obj); - } else { - val = JS_ToObject(ctx, this_obj); - if (JS_IsException(val)) - goto exception; - } - } else { - normal_this: - val = JS_DupValue(ctx, this_obj); - } - *sp++ = val; - } - BREAK; - CASE(OP_push_false): - *sp++ = JS_FALSE; - BREAK; - CASE(OP_push_true): - *sp++ = JS_TRUE; - BREAK; - CASE(OP_object): - *sp++ = JS_NewObject(ctx); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_special_object): - { - int arg = *pc++; - switch(arg) { - case OP_SPECIAL_OBJECT_ARGUMENTS: - *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: - *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, - sf, min_int(argc, b->arg_count)); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_THIS_FUNC: - *sp++ = JS_DupValue(ctx, sf->cur_func); - break; - case OP_SPECIAL_OBJECT_NEW_TARGET: - *sp++ = JS_DupValue(ctx, new_target); - break; - case OP_SPECIAL_OBJECT_HOME_OBJECT: - { - JSObject *p1; - p1 = p->u.func.home_object; - if (unlikely(!p1)) - *sp++ = JS_UNDEFINED; - else - *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - break; - case OP_SPECIAL_OBJECT_VAR_OBJECT: - *sp++ = JS_NewObjectProto(ctx, JS_NULL); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_IMPORT_META: - *sp++ = js_import_meta(ctx); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - default: - abort(); - } - } - BREAK; - CASE(OP_rest): - { - int first = get_u16(pc); - pc += 2; - *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; - - CASE(OP_drop): - JS_FreeValue(ctx, sp[-1]); - sp--; - BREAK; - CASE(OP_nip): - JS_FreeValue(ctx, sp[-2]); - sp[-2] = sp[-1]; - sp--; - BREAK; - CASE(OP_nip1): /* a b c -> b c */ - JS_FreeValue(ctx, sp[-3]); - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp--; - BREAK; - CASE(OP_dup): - sp[0] = JS_DupValue(ctx, sp[-1]); - sp++; - BREAK; - CASE(OP_dup2): /* a b -> a b a b */ - sp[0] = JS_DupValue(ctx, sp[-2]); - sp[1] = JS_DupValue(ctx, sp[-1]); - sp += 2; - BREAK; - CASE(OP_dup3): /* a b c -> a b c a b c */ - sp[0] = JS_DupValue(ctx, sp[-3]); - sp[1] = JS_DupValue(ctx, sp[-2]); - sp[2] = JS_DupValue(ctx, sp[-1]); - sp += 3; - BREAK; - CASE(OP_dup1): /* a b -> a a b */ - sp[0] = sp[-1]; - sp[-1] = JS_DupValue(ctx, sp[-2]); - sp++; - BREAK; - CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_perm3): /* obj a b -> a obj b (213) */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = tmp; - } - BREAK; - CASE(OP_rot3l): /* x a b -> a b x (231) */ - { - JSValue tmp; - tmp = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot4l): /* x a b c -> a b c x */ - { - JSValue tmp; - tmp = sp[-4]; - sp[-4] = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot5l): /* x a b c d -> a b c d x */ - { - JSValue tmp; - tmp = sp[-5]; - sp[-5] = sp[-4]; - sp[-4] = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot3r): /* a b x -> x a b (312) */ - { - JSValue tmp; - tmp = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = tmp; - } - BREAK; - CASE(OP_perm4): /* obj prop a b -> a obj prop b */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = tmp; - } - BREAK; - CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = sp[-5]; - sp[-5] = tmp; - } - BREAK; - CASE(OP_swap): /* a b -> b a */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_swap2): /* a b c d -> c d a b */ - { - JSValue tmp1, tmp2; - tmp1 = sp[-4]; - tmp2 = sp[-3]; - sp[-4] = sp[-2]; - sp[-3] = sp[-1]; - sp[-2] = tmp1; - sp[-1] = tmp2; - } - BREAK; - - CASE(OP_fclosure): - { - JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - *sp++ = js_closure(ctx, bfunc, var_refs, sf); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_call0): - CASE(OP_call1): - CASE(OP_call2): - CASE(OP_call3): - call_argc = opcode - OP_call0; - goto has_call_argc; -#endif - CASE(OP_call): - CASE(OP_tail_call): - { - call_argc = get_u16(pc); - pc += 2; - goto has_call_argc; - has_call_argc: - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, - JS_UNDEFINED, call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - if (opcode == OP_tail_call) - goto done; - for(i = -1; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 1; - *sp++ = ret_val; - } - BREAK; - CASE(OP_call_constructor): - { - call_argc = get_u16(pc); - pc += 2; - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], - call_argv[-1], - call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - for(i = -2; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 2; - *sp++ = ret_val; - } - BREAK; - CASE(OP_call_method): - CASE(OP_tail_call_method): - { - call_argc = get_u16(pc); - pc += 2; - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], - JS_UNDEFINED, call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - if (opcode == OP_tail_call_method) - goto done; - for(i = -2; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 2; - *sp++ = ret_val; - } - BREAK; - CASE(OP_array_from): - { - int i, ret; - - call_argc = get_u16(pc); - pc += 2; - ret_val = JS_NewArray(ctx); - if (unlikely(JS_IsException(ret_val))) - goto exception; - call_argv = sp - call_argc; - for(i = 0; i < call_argc; i++) { - ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], - JS_PROP_C_W_E | JS_PROP_THROW); - call_argv[i] = JS_UNDEFINED; - if (ret < 0) { - JS_FreeValue(ctx, ret_val); - goto exception; - } - } - sp -= call_argc; - *sp++ = ret_val; - } - BREAK; - - CASE(OP_apply): - { - int magic; - magic = get_u16(pc); - pc += 2; - - ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 3; - *sp++ = ret_val; - } - BREAK; - CASE(OP_return): - ret_val = *--sp; - goto done; - CASE(OP_return_undef): - ret_val = JS_UNDEFINED; - goto done; - - CASE(OP_check_ctor_return): - /* return TRUE if 'this' should be returned */ - if (!JS_IsObject(sp[-1])) { - if (!JS_IsUndefined(sp[-1])) { - JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); - goto exception; - } - sp[0] = JS_TRUE; - } else { - sp[0] = JS_FALSE; - } - sp++; - BREAK; - CASE(OP_check_ctor): - if (JS_IsUndefined(new_target)) { - JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); - goto exception; - } - BREAK; - CASE(OP_check_brand): - { - 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) - goto exception; - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - BREAK; - - CASE(OP_throw): - JS_Throw(ctx, *--sp); - goto exception; - - CASE(OP_throw_error): -#define JS_THROW_VAR_RO 0 -#define JS_THROW_VAR_REDECL 1 -#define JS_THROW_VAR_UNINITIALIZED 2 -#define JS_THROW_ERROR_DELETE_SUPER 3 -#define JS_THROW_ERROR_ITERATOR_THROW 4 - { - JSAtom atom; - int type; - atom = get_u32(pc); - type = pc[4]; - pc += 5; - if (type == JS_THROW_VAR_RO) - JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); - else - if (type == JS_THROW_VAR_REDECL) - JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); - else - if (type == JS_THROW_VAR_UNINITIALIZED) - JS_ThrowReferenceErrorUninitialized(ctx, atom); - else - if (type == JS_THROW_ERROR_DELETE_SUPER) - JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); - else - if (type == JS_THROW_ERROR_ITERATOR_THROW) - JS_ThrowTypeError(ctx, "iterator does not have a throw method"); - else - JS_ThrowInternalError(ctx, "invalid throw var type %d", type); - } - goto exception; - - CASE(OP_eval): - { - JSValueConst obj; - int scope_idx; - call_argc = get_u16(pc); - scope_idx = get_u16(pc + 2) - 1; - pc += 4; - call_argv = sp - call_argc; - sf->cur_pc = pc; - if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { - if (call_argc >= 1) - obj = call_argv[0]; - else - obj = JS_UNDEFINED; - ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, - JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, - JS_UNDEFINED, call_argc, call_argv, 0); - } - if (unlikely(JS_IsException(ret_val))) - goto exception; - for(i = -1; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 1; - *sp++ = ret_val; - } - BREAK; - /* could merge with OP_apply */ - CASE(OP_apply_eval): - { - int scope_idx; - uint32_t len; - JSValue *tab; - JSValueConst obj; - - scope_idx = get_u16(pc) - 1; - pc += 2; - tab = build_arg_list(ctx, &len, sp[-1]); - if (!tab) - goto exception; - if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { - if (len >= 1) - obj = tab[0]; - else - obj = JS_UNDEFINED; - ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, - JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, - (JSValueConst *)tab); - } - free_arg_list(ctx, tab, len); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - *sp++ = ret_val; - } - BREAK; - - CASE(OP_regexp): - { - sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, - sp[-2], sp[-1]); - sp--; - } - BREAK; - - CASE(OP_get_super): - { - JSValue proto; - proto = JS_GetPrototype(ctx, sp[-1]); - if (JS_IsException(proto)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = proto; - } - BREAK; - - CASE(OP_import): - { - JSValue val; - val = js_dynamic_import(ctx, sp[-1]); - if (JS_IsException(val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_check_var): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_CheckGlobalVar(ctx, atom); - if (ret < 0) - goto exception; - *sp++ = JS_NewBool(ctx, ret); - } - BREAK; - - CASE(OP_get_var_undef): - CASE(OP_get_var): - { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_var): - CASE(OP_put_var_init): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); - sp--; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_var_strict): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - /* sp[-2] is JS_TRUE or JS_FALSE */ - if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - goto exception; - } - ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_check_define_var): - { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_CheckDefineGlobalVar(ctx, atom, flags)) - goto exception; - } - BREAK; - CASE(OP_define_var): - { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_DefineGlobalVar(ctx, atom, flags)) - goto exception; - } - BREAK; - CASE(OP_define_func): - { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp--; - } - BREAK; - - CASE(OP_get_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, var_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, arg_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &arg_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_arg): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); - } - BREAK; - -#if SHORT_OPCODES - CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK; - CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; - CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK; - - CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; - CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; - CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; - CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK; - CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; - CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; - CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; - CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; - CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; - CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; - CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; - CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK; - CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; - CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; - CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; - CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; - CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; - CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; - CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; - CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK; - CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; - CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; - CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; - CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; - CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; - CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; -#endif - - CASE(OP_get_var_ref): - { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - sp[0] = JS_DupValue(ctx, val); - sp++; - } - BREAK; - CASE(OP_put_var_ref): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_var_ref): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_var_ref_check): - { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - sp[0] = JS_DupValue(ctx, val); - sp++; - } - BREAK; - CASE(OP_put_var_ref_check): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_put_var_ref_check_init): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_loc_uninitialized): - { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); - } - BREAK; - CASE(OP_get_loc_check): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); - goto exception; - } - sp[0] = JS_DupValue(ctx, var_buf[idx]); - 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; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); - goto exception; - } - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_put_loc_check_init): - { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); - goto exception; - } - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_close_loc): - { - int idx; - idx = get_u16(pc); - pc += 2; - close_lexical_var(ctx, sf, idx, FALSE); - } - BREAK; - - CASE(OP_make_loc_ref): - CASE(OP_make_arg_ref): - CASE(OP_make_var_ref_ref): - { - JSVarRef *var_ref; - JSProperty *pr; - JSAtom atom; - int idx; - atom = get_u32(pc); - idx = get_u16(pc + 4); - pc += 6; - *sp++ = JS_NewObjectProto(ctx, JS_NULL); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - if (opcode == OP_make_var_ref_ref) { - var_ref = var_refs[idx]; - var_ref->header.ref_count++; - } else { - var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); - if (!var_ref) - goto exception; - } - pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, - JS_PROP_WRITABLE | JS_PROP_VARREF); - if (!pr) { - free_var_ref(rt, var_ref); - goto exception; - } - pr->u.var_ref = var_ref; - *sp++ = JS_AtomToValue(ctx, atom); - } - BREAK; - CASE(OP_make_var_ref): - { - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - if (JS_GetGlobalVarRef(ctx, atom, sp)) - goto exception; - sp += 2; - } - BREAK; - - CASE(OP_goto): - pc += (int32_t)get_u32(pc); - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; -#if SHORT_OPCODES - CASE(OP_goto16): - pc += (int16_t)get_u16(pc); - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; - CASE(OP_goto8): - pc += (int8_t)pc[0]; - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; -#endif - CASE(OP_if_true): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 4; - /* 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 { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (res) { - pc += (int32_t)get_u32(pc - 4) - 4; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; - CASE(OP_if_false): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 4; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (!res) { - pc += (int32_t)get_u32(pc - 4) - 4; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_if_true8): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 1; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (res) { - pc += (int8_t)pc[-1] - 1; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; - CASE(OP_if_false8): - { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 1; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (!res) { - pc += (int8_t)pc[-1] - 1; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; -#endif - CASE(OP_catch): - { - int32_t diff; - diff = get_u32(pc); - sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); - sp++; - pc += 4; - } - BREAK; - CASE(OP_gosub): - { - int32_t diff; - diff = get_u32(pc); - /* XXX: should have a different tag to avoid security flaw */ - sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); - sp++; - pc += diff; - } - BREAK; - CASE(OP_ret): - { - JSValue op1; - uint32_t pos; - op1 = sp[-1]; - if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) - goto ret_fail; - pos = JS_VALUE_GET_INT(op1); - if (unlikely(pos >= b->byte_code_len)) { - ret_fail: - JS_ThrowInternalError(ctx, "invalid ret value"); - goto exception; - } - sp--; - pc = b->byte_code_buf + pos; - } - BREAK; - - CASE(OP_for_in_start): - if (js_for_in_start(ctx, sp)) - goto exception; - BREAK; - CASE(OP_for_in_next): - if (js_for_in_next(ctx, sp)) - goto exception; - sp += 2; - BREAK; - CASE(OP_for_of_start): - if (js_for_of_start(ctx, sp, FALSE)) - goto exception; - sp += 1; - *sp++ = JS_NewCatchOffset(ctx, 0); - BREAK; - CASE(OP_for_of_next): - { - int offset = -3 - pc[0]; - pc += 1; - if (js_for_of_next(ctx, sp, offset)) - goto exception; - sp += 2; - } - BREAK; - CASE(OP_for_await_of_start): - if (js_for_of_start(ctx, sp, TRUE)) - goto exception; - sp += 1; - *sp++ = JS_NewCatchOffset(ctx, 0); - BREAK; - CASE(OP_iterator_get_value_done): - if (js_iterator_get_value_done(ctx, sp)) - goto exception; - sp += 1; - BREAK; - CASE(OP_iterator_check_object): - if (unlikely(!JS_IsObject(sp[-1]))) { - JS_ThrowTypeError(ctx, "iterator must return an object"); - goto exception; - } - BREAK; - - CASE(OP_iterator_close): - /* iter_obj next catch_offset -> */ - sp--; /* drop the catch offset to avoid getting caught by exception */ - JS_FreeValue(ctx, sp[-1]); /* drop the next method */ - sp--; - if (!JS_IsUndefined(sp[-1])) { - if (JS_IteratorClose(ctx, sp[-1], FALSE)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - } - sp--; - BREAK; - CASE(OP_nip_catch): - { - JSValue ret_val; - /* 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)) { - JS_ThrowInternalError(ctx, "nip_catch"); - JS_FreeValue(ctx, ret_val); - goto exception; - } - sp[-1] = ret_val; - } - BREAK; - - CASE(OP_iterator_next): - /* stack: iter_obj next catch_offset val */ - { - JSValue ret; - ret = JS_Call(ctx, sp[-3], sp[-4], - 1, (JSValueConst *)(sp - 1)); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - } - BREAK; - - CASE(OP_iterator_call): - /* stack: iter_obj next catch_offset val */ - { - JSValue method, ret; - BOOL ret_flag; - int flags; - flags = *pc++; - method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? - JS_ATOM_throw : JS_ATOM_return); - if (JS_IsException(method)) - goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - ret_flag = TRUE; - } else { - if (flags & 2) { - /* no argument */ - ret = JS_CallFree(ctx, method, sp[-4], - 0, NULL); - } else { - ret = JS_CallFree(ctx, method, sp[-4], - 1, (JSValueConst *)(sp - 1)); - } - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - ret_flag = FALSE; - } - sp[0] = JS_NewBool(ctx, ret_flag); - sp += 1; - } - BREAK; - - CASE(OP_lnot): - { - int res; - JSValue op1; - - op1 = sp[-1]; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1) != 0; - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp[-1] = JS_NewBool(ctx, !res); - } - BREAK; - - CASE(OP_get_field): - { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetProperty(ctx, sp[-1], atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_get_field2): - { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetProperty(ctx, sp[-1], atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_field): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2], - JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_private_symbol): - { - JSAtom atom; - JSValue val; - - atom = get_u32(pc); - pc += 4; - val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); - if (JS_IsException(val)) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_get_private_field): - { - JSValue val; - - val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp[-2] = val; - sp--; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_put_private_field): - { - int ret; - ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-1]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_private_field): - { - int ret; - ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_field): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], - JS_PROP_C_W_E | JS_PROP_THROW); - sp--; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_set_name): - { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - CASE(OP_set_name_computed): - { - int ret; - ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - CASE(OP_set_proto): - { - JSValue proto; - proto = sp[-1]; - if (JS_IsObject(proto) || JS_IsNull(proto)) { - if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) - goto exception; - } - JS_FreeValue(ctx, proto); - sp--; - } - BREAK; - CASE(OP_set_home_object): - js_method_set_home_object(ctx, sp[-1], sp[-2]); - BREAK; - CASE(OP_define_method): - CASE(OP_define_method_computed): - { - JSValue getter, setter, value; - JSValueConst obj; - JSAtom atom; - int flags, ret, op_flags; - BOOL is_computed; -#define OP_DEFINE_METHOD_METHOD 0 -#define OP_DEFINE_METHOD_GETTER 1 -#define OP_DEFINE_METHOD_SETTER 2 -#define OP_DEFINE_METHOD_ENUMERABLE 4 - - is_computed = (opcode == OP_define_method_computed); - if (is_computed) { - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - opcode += OP_define_method - OP_define_method_computed; - } else { - atom = get_u32(pc); - pc += 4; - } - op_flags = *pc++; - - obj = sp[-2 - is_computed]; - flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | - JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; - if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) - flags |= JS_PROP_ENUMERABLE; - op_flags &= 3; - value = JS_UNDEFINED; - getter = JS_UNDEFINED; - setter = JS_UNDEFINED; - if (op_flags == OP_DEFINE_METHOD_METHOD) { - value = sp[-1]; - flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; - } else if (op_flags == OP_DEFINE_METHOD_GETTER) { - getter = sp[-1]; - flags |= JS_PROP_HAS_GET; - } else { - setter = sp[-1]; - flags |= JS_PROP_HAS_SET; - } - ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); - if (ret >= 0) { - ret = JS_DefineProperty(ctx, obj, atom, value, - getter, setter, flags); - } - JS_FreeValue(ctx, sp[-1]); - if (is_computed) { - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-2]); - } - sp -= 1 + is_computed; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_class): - CASE(OP_define_class_computed): - { - int class_flags; - JSAtom atom; - - atom = get_u32(pc); - class_flags = pc[4]; - pc += 5; - if (js_op_define_class(ctx, sp, atom, class_flags, - var_refs, sf, - (opcode == OP_define_class_computed)) < 0) - goto exception; - } - BREAK; - - CASE(OP_get_array_el): - { - JSValue val; - - val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp[-2] = val; - sp--; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_get_array_el2): - { - JSValue val; - - val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); - sp[-1] = val; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_get_ref_value): - { - JSValue val; - if (unlikely(JS_IsUndefined(sp[-2]))) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } - goto exception; - } - val = JS_GetPropertyValue(ctx, sp[-2], - JS_DupValue(ctx, sp[-1])); - if (unlikely(JS_IsException(val))) - goto exception; - sp[0] = val; - sp++; - } - BREAK; - - CASE(OP_get_super_value): - { - JSValue val; - JSAtom atom; - atom = JS_ValueToAtom(ctx, sp[-1]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); - JS_FreeAtom(ctx, atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-3]); - sp[-3] = val; - sp -= 2; - } - BREAK; - - CASE(OP_put_array_el): - { - int ret; - - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_ref_value): - { - int ret, flags; - flags = JS_PROP_THROW_STRICT; - if (unlikely(JS_IsUndefined(sp[-3]))) { - if (is_strict_mode(ctx)) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } - goto exception; - } else { - sp[-3] = JS_DupValue(ctx, ctx->global_obj); - } - } else { - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; - } - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_super_value): - { - int ret; - JSAtom atom; - if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto exception; - } - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4], - JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-4]); - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-2]); - sp -= 4; - if (ret < 0) - goto exception; - } - BREAK; - - CASE(OP_define_array_el): - { - int ret; - ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], - JS_PROP_C_W_E | JS_PROP_THROW); - sp -= 1; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_append): /* array pos enumobj -- array pos */ - { - if (js_append_enumerate(ctx, sp)) - goto exception; - JS_FreeValue(ctx, *--sp); - } - BREAK; - - CASE(OP_copy_data_properties): /* target source excludeList */ - { - /* stack offsets (-1 based): - 2 bits for target, - 3 bits for source, - 2 bits for exclusionList */ - int mask; - - mask = *pc++; - if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], - sp[-1 - ((mask >> 2) & 7)], - sp[-1 - ((mask >> 5) & 7)], 0)) - goto exception; - } - BREAK; - - CASE(OP_add): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) - goto add_slow; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else 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: - if (js_add_slow(ctx, sp)) - goto exception; - sp--; - } - } - 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, op2))) { - int64_t r; - 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) { - sp--; - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) - goto exception; - 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 */ - ops[0] = JS_DupValue(ctx, *pv); - ops[1] = op2; - sp--; - if (js_add_slow(ctx, ops + 2)) - goto exception; - set_value(ctx, pv, ops[0]); - } - } - BREAK; - CASE(OP_sub): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) - goto binary_arith_slow; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_mul): - { - JSValue op1, op2; - double d; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int32_t v1, v2; - int64_t r; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - r = (int64_t)v1 * v2; - if (unlikely((int)r != r)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH) && - (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) - goto binary_arith_slow; -#endif - d = (double)r; - goto mul_fp_res; - } - /* need to test zero case for -0 result */ - if (unlikely(r == 0 && (v1 | v2) < 0)) { - d = -0.0; - goto mul_fp_res; - } - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; -#endif - d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); - mul_fp_res: - sp[-2] = __JS_NewFloat64(ctx, d); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_div): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int v1, v2; - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_mod): -#ifdef CONFIG_BIGNUM - CASE(OP_math_mod): -#endif - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int v1, v2, r; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = - -1 and the cases where the result is -0. */ - if (unlikely(v1 < 0 || v2 <= 0)) - goto binary_arith_slow; - r = v1 % v2; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_pow): - binary_arith_slow: - if (js_binary_arith_slow(ctx, sp, opcode)) - goto exception; - sp--; - BREAK; - - CASE(OP_plus): - { - JSValue op1; - uint32_t tag; - op1 = sp[-1]; - tag = JS_VALUE_GET_TAG(op1); - if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_neg): - { - JSValue op1; - uint32_t tag; - int val; - double d; - op1 = sp[-1]; - tag = JS_VALUE_GET_TAG(op1); - if (tag == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - /* Note: -0 cannot be expressed as integer */ - if (unlikely(val == 0)) { - d = -0.0; - goto neg_fp_res; - } - if (unlikely(val == INT32_MIN)) { - d = -(double)val; - goto neg_fp_res; - } - sp[-1] = JS_NewInt32(ctx, -val); - } else if (JS_TAG_IS_FLOAT64(tag)) { - d = -JS_VALUE_GET_FLOAT64(op1); - neg_fp_res: - sp[-1] = __JS_NewFloat64(ctx, d); - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_inc): - { - JSValue op1; - int val; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MAX)) - goto inc_slow; - sp[-1] = JS_NewInt32(ctx, val + 1); - } else { - inc_slow: - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_dec): - { - JSValue op1; - int val; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MIN)) - goto dec_slow; - sp[-1] = JS_NewInt32(ctx, val - 1); - } else { - dec_slow: - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_post_inc): - CASE(OP_post_dec): - if (js_post_inc_slow(ctx, sp, opcode)) - goto exception; - sp++; - BREAK; - CASE(OP_inc_loc): - { - JSValue op1; - int val; - int idx; - idx = *pc; - pc += 1; - - op1 = var_buf[idx]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MAX)) - goto inc_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, val + 1); - } else { - inc_loc_slow: - /* must duplicate otherwise the variable value may - be destroyed before JS code accesses it */ - op1 = JS_DupValue(ctx, op1); - if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) - goto exception; - set_value(ctx, &var_buf[idx], op1); - } - } - BREAK; - CASE(OP_dec_loc): - { - JSValue op1; - int val; - int idx; - idx = *pc; - pc += 1; - - op1 = var_buf[idx]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MIN)) - goto dec_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, val - 1); - } else { - dec_loc_slow: - /* must duplicate otherwise the variable value may - be destroyed before JS code accesses it */ - op1 = JS_DupValue(ctx, op1); - if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) - goto exception; - set_value(ctx, &var_buf[idx], op1); - } - } - BREAK; - CASE(OP_not): - { - JSValue op1; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); - } else { - if (js_not_slow(ctx, sp)) - goto exception; - } - } - BREAK; - - CASE(OP_shl): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v1, v2; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - { - int64_t r; - if (unlikely(sf->js_mode & JS_MODE_MATH)) { - if (v2 > 0x1f) - goto shl_slow; - r = (int64_t)v1 << v2; - if ((int)r != r) - goto shl_slow; - } else { - v2 &= 0x1f; - } - } -#else - v2 &= 0x1f; -#endif - sp[-2] = JS_NewInt32(ctx, v1 << v2); - sp--; - } else { -#ifdef CONFIG_BIGNUM - shl_slow: -#endif - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_shr): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v2; - v2 = JS_VALUE_GET_INT(op2); - /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ - v2 &= 0x1f; - sp[-2] = JS_NewUint32(ctx, - (uint32_t)JS_VALUE_GET_INT(op1) >> - v2); - sp--; - } else { - if (js_shr_slow(ctx, sp)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_sar): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v2; - v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - if (unlikely(v2 > 0x1f)) { - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto sar_slow; - else - v2 &= 0x1f; - } -#else - v2 &= 0x1f; -#endif - sp[-2] = JS_NewInt32(ctx, - (int)JS_VALUE_GET_INT(op1) >> v2); - sp--; - } else { -#ifdef CONFIG_BIGNUM - sar_slow: -#endif - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_and): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, - JS_VALUE_GET_INT(op1) & - JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_or): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, - JS_VALUE_GET_INT(op1) | - JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_xor): - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, - JS_VALUE_GET_INT(op1) ^ - JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - - -#define OP_CMP(opcode, binary_op, slow_call) \ - CASE(opcode): \ - { \ - JSValue op1, op2; \ - op1 = sp[-2]; \ - op2 = sp[-1]; \ - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ - sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ - sp--; \ - } else { \ - if (slow_call) \ - goto exception; \ - sp--; \ - } \ - } \ - BREAK - - OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); - OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); - OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); - OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); - -#ifdef CONFIG_BIGNUM - CASE(OP_mul_pow10): - if (rt->bigfloat_ops.mul_pow10(ctx, sp)) - goto exception; - sp--; - BREAK; -#endif - CASE(OP_in): - if (js_operator_in(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_private_in): - if (js_operator_private_in(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_instanceof): - if (js_operator_instanceof(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_typeof): - { - JSValue op1; - JSAtom atom; - - op1 = sp[-1]; - atom = js_operator_typeof(ctx, op1); - JS_FreeValue(ctx, op1); - sp[-1] = JS_AtomToString(ctx, atom); - } - BREAK; - CASE(OP_delete): - if (js_operator_delete(ctx, sp)) - goto exception; - sp--; - BREAK; - CASE(OP_delete_var): - { - JSAtom atom; - int ret; - - atom = get_u32(pc); - pc += 4; - - ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); - if (unlikely(ret < 0)) - goto exception; - *sp++ = JS_NewBool(ctx, ret); - } - BREAK; - - CASE(OP_to_object): - if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { - ret_val = JS_ToObject(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; - - CASE(OP_to_propkey): - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; - - CASE(OP_to_propkey2): - /* must be tested first */ - if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { - JS_ThrowTypeError(ctx, "value has no property"); - goto exception; - } - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; -#if 0 - CASE(OP_to_string): - if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { - ret_val = JS_ToString(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; -#endif - CASE(OP_with_get_var): - CASE(OP_with_put_var): - CASE(OP_with_delete_var): - CASE(OP_with_make_ref): - CASE(OP_with_get_ref): - CASE(OP_with_get_ref_undef): - { - JSAtom atom; - int32_t diff; - JSValue obj, val; - int ret, is_with; - atom = get_u32(pc); - diff = get_u32(pc + 4); - is_with = pc[8]; - pc += 9; - - obj = sp[-1]; - ret = JS_HasProperty(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (ret) { - if (is_with) { - ret = js_has_unscopable(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (ret) - goto no_with; - } - switch (opcode) { - case OP_with_get_var: - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - set_value(ctx, &sp[-1], val); - break; - case OP_with_put_var: - /* XXX: check if strict mode */ - ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj, - JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - break; - case OP_with_delete_var: - ret = JS_DeleteProperty(ctx, obj, atom, 0); - if (unlikely(ret < 0)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_NewBool(ctx, ret); - break; - case OP_with_make_ref: - /* produce a pair object/propname on the stack */ - *sp++ = JS_AtomToValue(ctx, atom); - break; - case OP_with_get_ref: - /* produce a pair object/method on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - break; - case OP_with_get_ref_undef: - /* produce a pair undefined/function on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_UNDEFINED; - *sp++ = val; - break; - } - pc += diff - 5; - } else { - no_with: - /* if not jumping, drop the object argument */ - JS_FreeValue(ctx, sp[-1]); - sp--; - } - } - BREAK; - - CASE(OP_await): - ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); - goto done_generator; - CASE(OP_yield): - ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); - goto done_generator; - CASE(OP_yield_star): - CASE(OP_async_yield_star): - ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); - goto done_generator; - CASE(OP_return_async): - 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; - CASE(OP_is_undefined_or_null): - if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || - JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { - goto set_true; - } else { - goto free_and_set_false; - } -#if SHORT_OPCODES - CASE(OP_is_undefined): - if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { - goto set_true; - } else { - goto free_and_set_false; - } - CASE(OP_is_null): - if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { - goto set_true; - } else { - goto free_and_set_false; - } - /* XXX: could merge to a single opcode */ - CASE(OP_typeof_is_undefined): - /* different from OP_is_undefined because of isHTMLDDA */ - if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { - goto free_and_set_true; - } else { - goto free_and_set_false; - } - CASE(OP_typeof_is_function): - if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { - goto free_and_set_true; - } else { - goto free_and_set_false; - } - free_and_set_true: - JS_FreeValue(ctx, sp[-1]); -#endif - set_true: - sp[-1] = JS_TRUE; - BREAK; - free_and_set_false: - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_FALSE; - BREAK; - CASE(OP_invalid): - DEFAULT: - JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", - (int)(pc - b->byte_code_buf - 1), opcode); - goto exception; - } - } - exception: - if (is_backtrace_needed(ctx, rt->current_exception)) { - /* add the backtrace information now (it is not done - before if the exception happens in a bytecode - operation */ - sf->cur_pc = pc; - build_backtrace(ctx, rt->current_exception, NULL, 0, 0); - } - if (!JS_IsUncatchableError(ctx, rt->current_exception)) { - while (sp > stack_buf) { - JSValue val = *--sp; - JS_FreeValue(ctx, val); - if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { - int pos = JS_VALUE_GET_INT(val); - if (pos == 0) { - /* enumerator: close it with a throw */ - JS_FreeValue(ctx, sp[-1]); /* drop the next method */ - sp--; - JS_IteratorClose(ctx, sp[-1], TRUE); - } else { - *sp++ = rt->current_exception; - rt->current_exception = JS_NULL; - pc = b->byte_code_buf + pos; - goto restart; - } - } - } - } - ret_val = JS_EXCEPTION; - /* the local variables are freed by the caller in the generator - case. Hence the label 'done' should never be reached in a - generator function. */ - if (b->func_kind != JS_FUNC_NORMAL) { - done_generator: - sf->cur_pc = pc; - sf->cur_sp = sp; - } else { - done: - if (unlikely(!list_empty(&sf->var_ref_list))) { - /* variable references reference the stack: must close them */ - close_var_refs(rt, sf); - } - /* free the local variables and stack */ - for(pval = local_buf; pval < sp; pval++) { - JS_FreeValue(ctx, *pval); - } - } - rt->current_stack_frame = sf->prev_frame; - return ret_val; -} - -JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, - argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); -} - -static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, - argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); - JS_FreeValue(ctx, func_obj); - return res; -} - -/* warning: the refcount of the context is not incremented. Return - NULL in case of exception (case of revoked proxy only) */ -static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) -{ - JSObject *p; - JSContext *realm; - - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return ctx; - p = JS_VALUE_GET_OBJ(func_obj); - switch(p->class_id) { - case JS_CLASS_C_FUNCTION: - realm = p->u.cfunc.realm; - break; - case JS_CLASS_BYTECODE_FUNCTION: - case JS_CLASS_GENERATOR_FUNCTION: - case JS_CLASS_ASYNC_FUNCTION: - case JS_CLASS_ASYNC_GENERATOR_FUNCTION: - { - JSFunctionBytecode *b; - b = p->u.func.function_bytecode; - realm = b->realm; - } - break; - case JS_CLASS_PROXY: - { - JSProxyData *s = p->u.opaque; - if (!s) - return ctx; - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return NULL; - } else { - realm = JS_GetFunctionRealm(ctx, s->target); - } - } - break; - case JS_CLASS_BOUND_FUNCTION: - { - JSBoundFunction *bf = p->u.bound_function; - realm = JS_GetFunctionRealm(ctx, bf->func_obj); - } - break; - default: - realm = ctx; - break; - } - return realm; -} - -static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, - int class_id) -{ - JSValue proto, obj; - JSContext *realm; - - if (JS_IsUndefined(ctor)) { - proto = JS_DupValue(ctx, ctx->class_proto[class_id]); - } else { - proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); - if (JS_IsException(proto)) - return proto; - if (!JS_IsObject(proto)) { - JS_FreeValue(ctx, proto); - realm = JS_GetFunctionRealm(ctx, ctor); - if (!realm) - return JS_EXCEPTION; - proto = JS_DupValue(ctx, realm->class_proto[class_id]); - } - } - obj = JS_NewObjectProtoClass(ctx, proto, class_id); - JS_FreeValue(ctx, proto); - return obj; -} - -JSValue JS_NewObjectFromCtor(JSContext *ctx, JSValueConst ctor, - JSClassID class_id) -{ - return js_create_from_ctor(ctx, ctor, class_id); -} - -/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ -static JSValue JS_CallConstructorInternal(JSContext *ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValue *argv, int flags) -{ - JSObject *p; - JSFunctionBytecode *b; - - if (js_poll_interrupts(ctx)) - return JS_EXCEPTION; - flags |= JS_CALL_FLAG_CONSTRUCTOR; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) - goto not_a_function; - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(!p->is_constructor)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall *call_func; - call_func = ctx->rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(ctx, "not a function"); - } - return call_func(ctx, func_obj, new_target, argc, - (JSValueConst *)argv, flags); - } - - b = p->u.func.function_bytecode; - if (b->is_derived_class_constructor) { - return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); - } else { - JSValue obj, ret; - /* legacy constructor behavior */ - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); - if (JS_IsException(obj)) - return JS_EXCEPTION; - ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); - if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || - JS_IsException(ret)) { - JS_FreeValue(ctx, obj); - return ret; - } else { - JS_FreeValue(ctx, ret); - return obj; - } - } -} - -JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - return JS_CallConstructorInternal(ctx, func_obj, new_target, - argc, (JSValue *)argv, - JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, - int argc, JSValueConst *argv) -{ - return JS_CallConstructorInternal(ctx, func_obj, func_obj, - argc, (JSValue *)argv, - JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, - int argc, JSValueConst *argv) -{ - JSValue func_obj; - func_obj = JS_GetProperty(ctx, this_val, atom); - if (JS_IsException(func_obj)) - return func_obj; - return JS_CallFree(ctx, func_obj, this_val, argc, argv); -} - -static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValueConst *argv) -{ - JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); - JS_FreeValue(ctx, this_val); - return res; -} - -/* JSAsyncFunctionState (used by generator and async functions) */ -static 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 | 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) { - 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; - sf->arg_count = arg_buf_len; - sf->var_buf = sf->arg_buf + arg_buf_len; - sf->cur_sp = sf->var_buf + b->var_count; - for(i = 0; i < argc; i++) - sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); - n = arg_buf_len + b->var_count; - for(i = argc; i < n; i++) - sf->arg_buf[i] = JS_UNDEFINED; - s->resolving_funcs[0] = JS_UNDEFINED; - s->resolving_funcs[1] = JS_UNDEFINED; - s->is_completed = FALSE; - return s; -} - -static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s) -{ - JSStackFrame *sf = &s->frame; - JSValue *sp; - - if (sf->arg_buf) { - /* cannot free the function if it is running */ - assert(sf->cur_sp != NULL); - for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { - JS_FreeValueRT(rt, *sp); - } - js_free_rt(rt, sf->arg_buf); - sf->arg_buf = NULL; - } - JS_FreeValueRT(rt, sf->cur_func); - JS_FreeValueRT(rt, s->this_val); -} - -static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) -{ - JSRuntime *rt = ctx->rt; - JSStackFrame *sf = &s->frame; - JSValue func_obj, ret; - - 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); - - 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 */ - -typedef enum JSGeneratorStateEnum { - JS_GENERATOR_STATE_SUSPENDED_START, - JS_GENERATOR_STATE_SUSPENDED_YIELD, - JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, - JS_GENERATOR_STATE_EXECUTING, - JS_GENERATOR_STATE_COMPLETED, -} JSGeneratorStateEnum; - -typedef struct JSGeneratorData { - JSGeneratorStateEnum state; - JSAsyncFunctionState *func_state; -} JSGeneratorData; - -static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) -{ - if (s->state == JS_GENERATOR_STATE_COMPLETED) - return; - if (s->func_state) { - async_func_free(rt, s->func_state); - s->func_state = NULL; - } - s->state = JS_GENERATOR_STATE_COMPLETED; -} - -static void js_generator_finalizer(JSRuntime *rt, JSValue obj) -{ - JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); - - if (s) { - free_generator_stack_rt(rt, s); - js_free_rt(rt, s); - } -} - -static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) -{ - free_generator_stack_rt(ctx->rt, s); -} - -static void js_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSGeneratorData *s = p->u.generator_data; - - if (!s || !s->func_state) - return; - mark_func(rt, &s->func_state->header); -} - -/* XXX: use enum */ -#define GEN_MAGIC_NEXT 0 -#define GEN_MAGIC_RETURN 1 -#define GEN_MAGIC_THROW 2 - -static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); - JSStackFrame *sf; - JSValue ret, func_ret; - - *pdone = TRUE; - if (!s) - return JS_ThrowTypeError(ctx, "not a generator"); - 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 { - free_generator_stack(ctx, s); - goto done; - } - 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; - } 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->state = JS_GENERATOR_STATE_EXECUTING; - func_ret = async_func_resume(ctx, s->func_state); - s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; - if (s->func_state->is_completed) { - /* finalize the execution in case of exception or normal return */ - free_generator_stack(ctx, s); - return func_ret; - } 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; - if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { - s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - /* return (value, done) object */ - *pdone = 2; - } else { - *pdone = FALSE; - } - } - break; - case JS_GENERATOR_STATE_COMPLETED: - done: - /* execution is finished */ - switch(magic) { - default: - case GEN_MAGIC_NEXT: - ret = JS_UNDEFINED; - break; - case GEN_MAGIC_RETURN: - ret = JS_DupValue(ctx, argv[0]); - break; - case GEN_MAGIC_THROW: - ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); - break; - } - break; - case JS_GENERATOR_STATE_EXECUTING: - ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); - break; - } - return ret; -} - -static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSValue obj, func_ret; - JSGeneratorData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->state = JS_GENERATOR_STATE_SUSPENDED_START; - 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); - if (JS_IsException(func_ret)) - goto fail; - JS_FreeValue(ctx, func_ret); - - obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); - if (JS_IsException(obj)) - goto fail; - JS_SetOpaque(obj, s); - return obj; - fail: - free_generator_stack_rt(ctx->rt, s); - js_free(ctx, s); - return JS_EXCEPTION; -} - -/* AsyncFunction */ - -static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionState *s = p->u.async_function_data; - if (s) { - async_func_free(rt, s); - } -} - -static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionState *s = p->u.async_function_data; - if (s) { - mark_func(rt, &s->header); - } -} - -static int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionState *s, - JSValue *resolving_funcs) -{ - int i; - JSObject *p; - - for(i = 0; i < 2; i++) { - resolving_funcs[i] = - JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); - if (JS_IsException(resolving_funcs[i])) { - if (i == 1) - JS_FreeValue(ctx, resolving_funcs[0]); - return -1; - } - p = JS_VALUE_GET_OBJ(resolving_funcs[i]); - s->header.ref_count++; - p->u.async_function_data = s; - } - return 0; -} - -static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s) -{ - JSValue func_ret, ret2; - - 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 ? */ - } else { - /* 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; - - value = s->frame.cur_sp[-1]; - s->frame.cur_sp[-1] = JS_UNDEFINED; - - /* await */ - JS_FreeValue(ctx, func_ret); /* not used */ - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - JS_FreeValue(ctx, value); - if (JS_IsException(promise)) - goto fail; - if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { - JS_FreeValue(ctx, promise); - goto fail; - } - - /* Note: no need to create 'thrownawayCapability' as in - the spec */ - for(i = 0; i < 2; i++) - resolving_funcs1[i] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs, - (JSValueConst *)resolving_funcs1); - JS_FreeValue(ctx, promise); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; - } -} - -static JSValue js_async_function_resolve_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSAsyncFunctionState *s = p->u.async_function_data; - BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; - JSValueConst arg; - - if (argc > 0) - arg = argv[0]; - else - arg = JS_UNDEFINED; - s->throw_flag = is_reject; - if (is_reject) { - JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - /* return value of await */ - s->frame.cur_sp[-1] = JS_DupValue(ctx, arg); - } - js_async_function_resume(ctx, s); - return JS_UNDEFINED; -} - -static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSValue promise; - JSAsyncFunctionState *s; - - s = async_func_init(ctx, func_obj, this_obj, argc, argv); - if (!s) - return JS_EXCEPTION; - - promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); - if (JS_IsException(promise)) { - async_func_free(ctx->rt, s); - return JS_EXCEPTION; - } - - js_async_function_resume(ctx, s); - - async_func_free(ctx->rt, s); - - return promise; -} - -/* AsyncGenerator */ - -typedef enum JSAsyncGeneratorStateEnum { - JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, - JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, - JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, - JS_ASYNC_GENERATOR_STATE_EXECUTING, - JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, - JS_ASYNC_GENERATOR_STATE_COMPLETED, -} JSAsyncGeneratorStateEnum; - -typedef struct JSAsyncGeneratorRequest { - struct list_head link; - /* completion */ - int completion_type; /* GEN_MAGIC_x */ - JSValue result; - /* promise capability */ - JSValue promise; - JSValue resolving_funcs[2]; -} JSAsyncGeneratorRequest; - -typedef struct JSAsyncGeneratorData { - JSObject *generator; /* back pointer to the object (const) */ - JSAsyncGeneratorStateEnum state; - /* func_state is NULL is state AWAITING_RETURN and COMPLETED */ - JSAsyncFunctionState *func_state; - struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ -} JSAsyncGeneratorData; - -static void js_async_generator_free(JSRuntime *rt, - JSAsyncGeneratorData *s) -{ - struct list_head *el, *el1; - JSAsyncGeneratorRequest *req; - - list_for_each_safe(el, el1, &s->queue) { - req = list_entry(el, JSAsyncGeneratorRequest, link); - JS_FreeValueRT(rt, req->result); - JS_FreeValueRT(rt, req->promise); - JS_FreeValueRT(rt, req->resolving_funcs[0]); - JS_FreeValueRT(rt, req->resolving_funcs[1]); - js_free_rt(rt, req); - } - if (s->func_state) - async_func_free(rt, s->func_state); - js_free_rt(rt, s); -} - -static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); - - if (s) { - js_async_generator_free(rt, s); - } -} - -static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); - struct list_head *el; - JSAsyncGeneratorRequest *req; - if (s) { - list_for_each(el, &s->queue) { - req = list_entry(el, JSAsyncGeneratorRequest, link); - JS_MarkValue(rt, req->result, mark_func); - JS_MarkValue(rt, req->promise, mark_func); - JS_MarkValue(rt, req->resolving_funcs[0], mark_func); - JS_MarkValue(rt, req->resolving_funcs[1], mark_func); - } - if (s->func_state) { - mark_func(rt, &s->func_state->header); - } - } -} - -static JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int magic, JSValue *func_data); - -static int js_async_generator_resolve_function_create(JSContext *ctx, - JSValueConst generator, - JSValue *resolving_funcs, - BOOL is_resume_next) -{ - int i; - JSValue func; - - for(i = 0; i < 2; i++) { - func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, - i + is_resume_next * 2, 1, &generator); - if (JS_IsException(func)) { - if (i == 1) - JS_FreeValue(ctx, resolving_funcs[0]); - return -1; - } - resolving_funcs[i] = func; - } - return 0; -} - -static int js_async_generator_await(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value) -{ - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; - - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &value, 0); - if (JS_IsException(promise)) - goto fail; - - if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), - resolving_funcs, FALSE)) { - JS_FreeValue(ctx, promise); - goto fail; - } - - /* Note: no need to create 'thrownawayCapability' as in - the spec */ - for(i = 0; i < 2; i++) - resolving_funcs1[i] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs, - (JSValueConst *)resolving_funcs1); - JS_FreeValue(ctx, promise); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; - return 0; - fail: - return -1; -} - -static void js_async_generator_resolve_or_reject(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst result, - int is_reject) -{ - JSAsyncGeneratorRequest *next; - JSValue ret; - - next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); - list_del(&next->link); - ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, - &result); - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, next->result); - JS_FreeValue(ctx, next->promise); - JS_FreeValue(ctx, next->resolving_funcs[0]); - JS_FreeValue(ctx, next->resolving_funcs[1]); - js_free(ctx, next); -} - -static void js_async_generator_resolve(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value, - BOOL done) -{ - JSValue result; - result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done); - /* XXX: better exception handling ? */ - js_async_generator_resolve_or_reject(ctx, s, result, 0); - JS_FreeValue(ctx, result); - } - -static void js_async_generator_reject(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst exception) -{ - js_async_generator_resolve_or_reject(ctx, s, exception, 1); -} - -static void js_async_generator_complete(JSContext *ctx, - JSAsyncGeneratorData *s) -{ - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - async_func_free(ctx->rt, s->func_state); - s->func_state = NULL; - } -} - -static int js_async_generator_completed_return(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value) -{ - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int res; - - // 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, - TRUE)) { - JS_FreeValue(ctx, promise); - return -1; - } - resolving_funcs[0] = JS_UNDEFINED; - resolving_funcs[1] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs1, - (JSValueConst *)resolving_funcs); - JS_FreeValue(ctx, resolving_funcs1[0]); - JS_FreeValue(ctx, resolving_funcs1[1]); - JS_FreeValue(ctx, promise); - return res; -} - -static void js_async_generator_resume_next(JSContext *ctx, - JSAsyncGeneratorData *s) -{ - JSAsyncGeneratorRequest *next; - JSValue func_ret, value; - - for(;;) { - if (list_empty(&s->queue)) - break; - next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); - switch(s->state) { - case JS_ASYNC_GENERATOR_STATE_EXECUTING: - /* only happens when restarting execution after await() */ - goto resume_exec; - case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: - goto done; - case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: - if (next->completion_type == GEN_MAGIC_NEXT) { - goto exec_no_arg; - } else { - js_async_generator_complete(ctx, s); - } - break; - case JS_ASYNC_GENERATOR_STATE_COMPLETED: - if (next->completion_type == GEN_MAGIC_NEXT) { - js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); - } else if (next->completion_type == GEN_MAGIC_RETURN) { - s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; - js_async_generator_completed_return(ctx, s, next->result); - } else { - js_async_generator_reject(ctx, s, next->result); - } - goto done; - case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: - case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: - value = JS_DupValue(ctx, next->result); - if (next->completion_type == GEN_MAGIC_THROW && - s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { - JS_Throw(ctx, value); - s->func_state->throw_flag = TRUE; - } else { - /* 'yield' returns a value. 'yield *' also returns a value - in case the 'throw' method is called */ - s->func_state->frame.cur_sp[-1] = value; - s->func_state->frame.cur_sp[0] = - JS_NewInt32(ctx, next->completion_type); - s->func_state->frame.cur_sp++; - exec_no_arg: - s->func_state->throw_flag = FALSE; - } - s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; - resume_exec: - func_ret = async_func_resume(ctx, s->func_state); - if (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: - if (func_ret_code == FUNC_RET_YIELD_STAR) - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - else - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; - js_async_generator_resolve(ctx, s, value, FALSE); - JS_FreeValue(ctx, value); - break; - case FUNC_RET_AWAIT: - 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(); - } - } - break; - default: - abort(); - } - } - done: ; -} - -static JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - BOOL is_reject = magic & 1; - JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); - JSValueConst arg = argv[0]; - - /* XXX: what if s == NULL */ - - if (magic >= 2) { - /* resume next case in AWAITING_RETURN state */ - assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || - s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - if (is_reject) { - js_async_generator_reject(ctx, s, arg); - } else { - js_async_generator_resolve(ctx, s, arg, TRUE); - } - } else { - /* restart function execution after await() */ - assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); - s->func_state->throw_flag = is_reject; - if (is_reject) { - JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - /* return value of await */ - s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg); - } - js_async_generator_resume_next(ctx, s); - } - return JS_UNDEFINED; -} - -/* magic = GEN_MAGIC_x */ -static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); - JSValue promise, resolving_funcs[2]; - JSAsyncGeneratorRequest *req; - - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - if (!s) { - JSValue err, res2; - JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); - err = JS_GetException(ctx); - res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - } - req = js_mallocz(ctx, sizeof(*req)); - if (!req) - goto fail; - req->completion_type = magic; - req->result = JS_DupValue(ctx, argv[0]); - req->promise = JS_DupValue(ctx, promise); - req->resolving_funcs[0] = resolving_funcs[0]; - req->resolving_funcs[1] = resolving_funcs[1]; - list_add_tail(&req->link, &s->queue); - if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { - js_async_generator_resume_next(ctx, s); - } - return promise; - fail: - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; -} - -static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSValue obj, func_ret; - JSAsyncGeneratorData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; - init_list_head(&s->queue); - 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); - if (JS_IsException(func_ret)) - goto fail; - JS_FreeValue(ctx, func_ret); - - obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); - if (JS_IsException(obj)) - goto fail; - s->generator = JS_VALUE_GET_OBJ(obj); - JS_SetOpaque(obj, s); - return obj; - fail: - js_async_generator_free(ctx->rt, s); - return JS_EXCEPTION; -} - -/* JS parser */ - -enum { - TOK_NUMBER = -128, - TOK_STRING, - TOK_TEMPLATE, - TOK_IDENT, - TOK_REGEXP, - /* warning: order matters (see js_parse_assign_expr) */ - TOK_MUL_ASSIGN, - TOK_DIV_ASSIGN, - TOK_MOD_ASSIGN, - TOK_PLUS_ASSIGN, - TOK_MINUS_ASSIGN, - TOK_SHL_ASSIGN, - TOK_SAR_ASSIGN, - TOK_SHR_ASSIGN, - TOK_AND_ASSIGN, - TOK_XOR_ASSIGN, - TOK_OR_ASSIGN, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW_ASSIGN, -#endif - TOK_POW_ASSIGN, - TOK_LAND_ASSIGN, - TOK_LOR_ASSIGN, - TOK_DOUBLE_QUESTION_MARK_ASSIGN, - TOK_DEC, - TOK_INC, - TOK_SHL, - TOK_SAR, - TOK_SHR, - TOK_LT, - TOK_LTE, - TOK_GT, - TOK_GTE, - TOK_EQ, - TOK_STRICT_EQ, - TOK_NEQ, - TOK_STRICT_NEQ, - TOK_LAND, - TOK_LOR, -#ifdef CONFIG_BIGNUM - TOK_MATH_POW, -#endif - TOK_POW, - TOK_ARROW, - TOK_ELLIPSIS, - TOK_DOUBLE_QUESTION_MARK, - TOK_QUESTION_MARK_DOT, - TOK_ERROR, - TOK_PRIVATE_NAME, - TOK_EOF, - /* keywords: WARNING: same order as atoms */ - TOK_NULL, /* must be first */ - TOK_FALSE, - TOK_TRUE, - TOK_IF, - TOK_ELSE, - TOK_RETURN, - TOK_VAR, - TOK_THIS, - TOK_DELETE, - TOK_VOID, - TOK_TYPEOF, - TOK_NEW, - TOK_IN, - TOK_INSTANCEOF, - TOK_DO, - TOK_WHILE, - TOK_FOR, - TOK_BREAK, - TOK_CONTINUE, - TOK_SWITCH, - TOK_CASE, - TOK_DEFAULT, - TOK_THROW, - TOK_TRY, - TOK_CATCH, - TOK_FINALLY, - TOK_FUNCTION, - TOK_DEBUGGER, - TOK_WITH, - /* FutureReservedWord */ - TOK_CLASS, - TOK_CONST, - TOK_ENUM, - TOK_EXPORT, - TOK_EXTENDS, - TOK_IMPORT, - TOK_SUPER, - /* FutureReservedWords when parsing strict mode code */ - TOK_IMPLEMENTS, - TOK_INTERFACE, - TOK_LET, - TOK_PACKAGE, - TOK_PRIVATE, - TOK_PROTECTED, - TOK_PUBLIC, - TOK_STATIC, - TOK_YIELD, - TOK_AWAIT, /* must be last */ - TOK_OF, /* only used for js_parse_skip_parens_token() */ -}; - -#define TOK_FIRST_KEYWORD TOK_NULL -#define TOK_LAST_KEYWORD TOK_AWAIT - -/* unicode code points */ -#define CP_NBSP 0x00a0 -#define CP_BOM 0xfeff - -#define CP_LS 0x2028 -#define CP_PS 0x2029 - -typedef struct BlockEnv { - struct BlockEnv *prev; - JSAtom label_name; /* JS_ATOM_NULL if none */ - int label_break; /* -1 if none */ - int label_cont; /* -1 if none */ - int drop_count; /* number of stack elements to drop */ - int label_finally; /* -1 if none */ - int scope_level; - int has_iterator; -} BlockEnv; - -typedef struct JSGlobalVar { - int cpool_idx; /* if >= 0, index in the constant pool for hoisted - function defintion*/ - uint8_t force_init : 1; /* force initialization to undefined */ - uint8_t is_lexical : 1; /* global let/const definition */ - uint8_t is_const : 1; /* const definition */ - int scope_level; /* scope of definition */ - JSAtom var_name; /* variable name */ -} JSGlobalVar; - -typedef struct RelocEntry { - struct RelocEntry *next; - uint32_t addr; /* address to patch */ - int size; /* address size: 1, 2 or 4 bytes */ -} RelocEntry; - -typedef struct JumpSlot { - int op; - int size; - int pos; - int label; -} JumpSlot; - -typedef struct LabelSlot { - int ref_count; - int pos; /* phase 1 address, -1 means not resolved yet */ - int pos2; /* phase 2 address, -1 means not resolved yet */ - int addr; /* phase 3 address, -1 means not resolved yet */ - RelocEntry *first_reloc; -} LabelSlot; - -typedef struct LineNumberSlot { - uint32_t pc; - int line_num; -} LineNumberSlot; - -typedef enum JSParseFunctionEnum { - JS_PARSE_FUNC_STATEMENT, - JS_PARSE_FUNC_VAR, - JS_PARSE_FUNC_EXPR, - JS_PARSE_FUNC_ARROW, - JS_PARSE_FUNC_GETTER, - JS_PARSE_FUNC_SETTER, - JS_PARSE_FUNC_METHOD, - JS_PARSE_FUNC_CLASS_STATIC_INIT, - JS_PARSE_FUNC_CLASS_CONSTRUCTOR, - JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, -} JSParseFunctionEnum; - -typedef enum JSParseExportEnum { - JS_PARSE_EXPORT_NONE, - JS_PARSE_EXPORT_NAMED, - JS_PARSE_EXPORT_DEFAULT, -} JSParseExportEnum; - -typedef struct JSFunctionDef { - JSContext *ctx; - struct JSFunctionDef *parent; - int parent_cpool_idx; /* index in the constant pool of the parent - or -1 if none */ - int parent_scope_level; /* scope level in parent at point of definition */ - struct list_head child_list; /* list of JSFunctionDef.link */ - struct list_head link; - - BOOL is_eval; /* TRUE if eval code */ - int eval_type; /* only valid if is_eval = TRUE */ - BOOL is_global_var; /* TRUE if variables are not defined locally: - eval global, eval module or non strict eval */ - BOOL is_func_expr; /* TRUE if function expression */ - BOOL has_home_object; /* TRUE if the home object is available */ - BOOL has_prototype; /* true if a prototype field is necessary */ - BOOL has_simple_parameter_list; - BOOL has_parameter_expressions; /* if true, an argument scope is created */ - BOOL has_use_strict; /* to reject directive in special cases */ - BOOL has_eval_call; /* true if the function contains a call to eval() */ - BOOL has_arguments_binding; /* true if the 'arguments' binding is - available in the function */ - BOOL has_this_binding; /* true if the 'this' and new.target binding are - available in the function */ - BOOL new_target_allowed; /* true if the 'new.target' does not - throw a syntax error */ - BOOL super_call_allowed; /* true if super() is allowed */ - BOOL super_allowed; /* true if super. or super[] is allowed */ - BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ - BOOL is_derived_class_constructor; - BOOL in_function_body; - BOOL backtrace_barrier; - JSFunctionKindEnum func_kind : 8; - JSParseFunctionEnum func_type : 8; - uint8_t js_mode; /* bitmap of JS_MODE_x */ - JSAtom func_name; /* JS_ATOM_NULL if no name */ - - JSVarDef *vars; - int var_size; /* allocated size for vars[] */ - int var_count; - JSVarDef *args; - int arg_size; /* allocated size for args[] */ - int arg_count; /* number of arguments */ - int defined_arg_count; - int var_object_idx; /* -1 if none */ - int 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, - -1 if none */ - int func_var_idx; /* variable containing the current function (-1 - if none, only used if is_func_expr is true) */ - int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ - int this_var_idx; /* variable containg the 'this' value, -1 if none */ - int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ - int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ - int home_object_var_idx; - BOOL need_home_object; - - int scope_level; /* index into fd->scopes if the current lexical scope */ - int scope_first; /* index into vd->vars of first lexically scoped variable */ - int scope_size; /* allocated size of fd->scopes array */ - int scope_count; /* number of entries used in the fd->scopes array */ - JSVarScope *scopes; - JSVarScope def_scope_array[4]; - int body_scope; /* scope of the body of the function or eval */ - - int global_var_count; - int global_var_size; - JSGlobalVar *global_vars; - - DynBuf byte_code; - int last_opcode_pos; /* -1 if no last opcode */ - int last_opcode_line_num; - BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ - - LabelSlot *label_slots; - int label_size; /* allocated size for label_slots[] */ - int label_count; - BlockEnv *top_break; /* break/continue label stack */ - - /* constant pool (strings, functions, numbers) */ - JSValue *cpool; - int cpool_count; - int cpool_size; - - /* list of variables in the closure */ - int closure_var_count; - int closure_var_size; - JSClosureVar *closure_var; - - JumpSlot *jump_slots; - int jump_size; - int jump_count; - - LineNumberSlot *line_number_slots; - int line_number_size; - int line_number_count; - int line_number_last; - int line_number_last_pc; - - /* pc2line table */ - JSAtom filename; - int line_num; - DynBuf pc2line; - - char *source; /* raw source, utf-8 encoded */ - int source_len; - - JSModuleDef *module; /* != NULL when parsing a module */ - 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; - union { - struct { - JSValue str; - int sep; - } str; - struct { - JSValue val; -#ifdef CONFIG_BIGNUM - slimb_t exponent; /* may be != 0 only if val is a float */ -#endif - } num; - struct { - JSAtom atom; - BOOL has_escape; - BOOL is_reserved; - } ident; - struct { - JSValue body; - JSValue flags; - } regexp; - } u; -} JSToken; - -typedef struct JSParseState { - JSContext *ctx; - int last_line_num; /* line number of last token */ - int line_num; /* line number of current offset */ - const char *filename; - JSToken token; - BOOL got_lf; /* true if got line feed before the current token */ - const uint8_t *last_ptr; - const uint8_t *buf_ptr; - const uint8_t *buf_end; - - /* current function code */ - JSFunctionDef *cur_func; - BOOL is_module; /* parsing a module */ - BOOL allow_html_comments; - BOOL ext_json; /* true if accepting JSON superset */ -} JSParseState; - -typedef struct JSOpCode { -#ifdef DUMP_BYTECODE - const char *name; -#endif - uint8_t size; /* in bytes */ - /* the opcodes remove n_pop items from the top of the stack, then - pushes n_push items */ - uint8_t n_pop; - uint8_t n_push; - uint8_t fmt; -} JSOpCode; - -static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { -#define FMT(f) -#ifdef DUMP_BYTECODE -#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, -#else -#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, -#endif -#include "quickjs-opcode.h" -#undef DEF -#undef FMT -}; - -#if SHORT_OPCODES -/* After the final compilation pass, short opcodes are used. Their - opcodes overlap with the temporary opcodes which cannot appear in - the final bytecode. Their description is after the temporary - opcodes in opcode_info[]. */ -#define short_opcode_info(op) \ - opcode_info[(op) >= OP_TEMP_START ? \ - (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] -#else -#define short_opcode_info(op) opcode_info[op] -#endif - -static __exception int next_token(JSParseState *s); - -static void free_token(JSParseState *s, JSToken *token) -{ - switch(token->val) { - case TOK_NUMBER: - JS_FreeValue(s->ctx, token->u.num.val); - break; - case TOK_STRING: - case TOK_TEMPLATE: - JS_FreeValue(s->ctx, token->u.str.str); - break; - case TOK_REGEXP: - JS_FreeValue(s->ctx, token->u.regexp.body); - JS_FreeValue(s->ctx, token->u.regexp.flags); - break; - case TOK_IDENT: - case TOK_PRIVATE_NAME: - JS_FreeAtom(s->ctx, token->u.ident.atom); - break; - default: - if (token->val >= TOK_FIRST_KEYWORD && - token->val <= TOK_LAST_KEYWORD) { - JS_FreeAtom(s->ctx, token->u.ident.atom); - } - break; - } -} - -static void __attribute((unused)) dump_token(JSParseState *s, - const JSToken *token) -{ - switch(token->val) { - case TOK_NUMBER: - { - double d; - JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ - printf("number: %.14g\n", d); - } - break; - case TOK_IDENT: - dump_atom: - { - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("ident: '%s'\n", - JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); - } - break; - case TOK_STRING: - { - const char *str; - /* XXX: quote the string */ - str = JS_ToCString(s->ctx, token->u.str.str); - printf("string: '%s'\n", str); - JS_FreeCString(s->ctx, str); - } - break; - case TOK_TEMPLATE: - { - const char *str; - str = JS_ToCString(s->ctx, token->u.str.str); - printf("template: `%s`\n", str); - JS_FreeCString(s->ctx, str); - } - break; - case TOK_REGEXP: - { - const char *str, *str2; - str = JS_ToCString(s->ctx, token->u.regexp.body); - str2 = JS_ToCString(s->ctx, token->u.regexp.flags); - printf("regexp: '%s' '%s'\n", str, str2); - JS_FreeCString(s->ctx, str); - JS_FreeCString(s->ctx, str2); - } - break; - case TOK_EOF: - printf("eof\n"); - break; - default: - if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { - goto dump_atom; - } else if (s->token.val >= 256) { - printf("token: %d\n", token->val); - } else { - printf("token: '%c'\n", token->val); - } - break; - } -} - -int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) -{ - JSContext *ctx = s->ctx; - va_list ap; - int backtrace_flags; - - va_start(ap, fmt); - JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); - va_end(ap); - backtrace_flags = 0; - if (s->cur_func && s->cur_func->backtrace_barrier) - backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; - build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num, - backtrace_flags); - return -1; -} - -static int js_parse_expect(JSParseState *s, int tok) -{ - if (s->token.val != tok) { - /* XXX: dump token correctly in all cases */ - return js_parse_error(s, "expecting '%c'", tok); - } - return next_token(s); -} - -static int js_parse_expect_semi(JSParseState *s) -{ - if (s->token.val != ';') { - /* automatic insertion of ';' */ - if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { - return 0; - } - return js_parse_error(s, "expecting '%c'", ';'); - } - return next_token(s); -} - -static int js_parse_error_reserved_identifier(JSParseState *s) -{ - char buf1[ATOM_GET_STR_BUF_SIZE]; - return js_parse_error(s, "'%s' is a reserved identifier", - JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), - s->token.u.ident.atom)); -} - -static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) -{ - uint32_t c; - StringBuffer b_s, *b = &b_s; - - /* p points to the first byte of the template part */ - if (string_buffer_init(s->ctx, b, 32)) - goto fail; - for(;;) { - if (p >= s->buf_end) - goto unexpected_eof; - c = *p++; - if (c == '`') { - /* template end part */ - break; - } - if (c == '$' && *p == '{') { - /* template start or middle part */ - p++; - break; - } - if (c == '\\') { - if (string_buffer_putc8(b, c)) - goto fail; - if (p >= s->buf_end) - goto unexpected_eof; - c = *p++; - } - /* newline sequences are normalized as single '\n' bytes */ - if (c == '\r') { - if (*p == '\n') - p++; - c = '\n'; - } - if (c == '\n') { - s->line_num++; - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - } - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - s->token.val = TOK_TEMPLATE; - s->token.u.str.sep = c; - s->token.u.str.str = string_buffer_end(b); - s->buf_ptr = p; - return 0; - - unexpected_eof: - js_parse_error(s, "unexpected end of string"); - fail: - string_buffer_free(b); - return -1; -} - -static __exception int js_parse_string(JSParseState *s, int sep, - BOOL do_throw, const uint8_t *p, - JSToken *token, const uint8_t **pp) -{ - int ret; - uint32_t c; - StringBuffer b_s, *b = &b_s; - - /* string */ - if (string_buffer_init(s->ctx, b, 32)) - goto fail; - for(;;) { - if (p >= s->buf_end) - goto invalid_char; - c = *p; - if (c < 0x20) { - if (!s->cur_func) { - if (do_throw) - js_parse_error(s, "invalid character in a JSON string"); - goto fail; - } - if (sep == '`') { - if (c == '\r') { - if (p[1] == '\n') - p++; - c = '\n'; - } - /* do not update s->line_num */ - } else if (c == '\n' || c == '\r') - goto invalid_char; - } - p++; - if (c == sep) - break; - if (c == '$' && *p == '{' && sep == '`') { - /* template start or middle part */ - p++; - break; - } - if (c == '\\') { - c = *p; - /* XXX: need a specific JSON case to avoid - accepting invalid escapes */ - switch(c) { - case '\0': - if (p >= s->buf_end) - goto invalid_char; - p++; - break; - case '\'': - case '\"': - case '\\': - p++; - break; - case '\r': /* accept DOS and MAC newline sequences */ - if (p[1] == '\n') { - p++; - } - /* fall thru */ - case '\n': - /* ignore escaped newline sequence */ - p++; - if (sep != '`') - s->line_num++; - continue; - default: - if (c >= '0' && c <= '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')) { - p++; - c = '\0'; - } else { - if (c >= '8' || sep == '`') { - /* Note: according to ES2021, \8 and \9 are not - accepted in strict mode or in templates. */ - goto invalid_escape; - } else { - if (do_throw) - js_parse_error(s, "octal escape sequences are not allowed in strict mode"); - } - goto fail; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - /* LS or PS are skipped */ - if (c == CP_LS || c == CP_PS) - continue; - } else { - parse_escape: - ret = lre_parse_escape(&p, TRUE); - if (ret == -1) { - invalid_escape: - if (do_throw) - js_parse_error(s, "malformed escape sequence in string literal"); - goto fail; - } else if (ret < 0) { - /* ignore the '\' (could output a warning) */ - p++; - } else { - c = ret; - } - } - break; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) - goto invalid_utf8; - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - token->val = TOK_STRING; - token->u.str.sep = c; - token->u.str.str = string_buffer_end(b); - *pp = p; - return 0; - - invalid_utf8: - if (do_throw) - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - invalid_char: - if (do_throw) - js_parse_error(s, "unexpected end of string"); - fail: - string_buffer_free(b); - return -1; -} - -static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { - return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && - !s->token.u.ident.has_escape; -} - -static __exception int js_parse_regexp(JSParseState *s) -{ - const uint8_t *p; - BOOL in_class; - StringBuffer b_s, *b = &b_s; - StringBuffer b2_s, *b2 = &b2_s; - uint32_t c; - - p = s->buf_ptr; - p++; - in_class = FALSE; - if (string_buffer_init(s->ctx, b, 32)) - return -1; - if (string_buffer_init(s->ctx, b2, 1)) - goto fail; - for(;;) { - if (p >= s->buf_end) { - eof_error: - js_parse_error(s, "unexpected end of regexp"); - goto fail; - } - c = *p++; - if (c == '\n' || c == '\r') { - goto eol_error; - } else if (c == '/') { - if (!in_class) - break; - } else if (c == '[') { - in_class = TRUE; - } else if (c == ']') { - /* XXX: incorrect as the first character in a class */ - in_class = FALSE; - } else if (c == '\\') { - if (string_buffer_putc8(b, c)) - goto fail; - c = *p++; - if (c == '\n' || c == '\r') - goto eol_error; - else if (c == '\0' && p >= s->buf_end) - goto eof_error; - else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - if (c == CP_LS || c == CP_PS) - goto eol_error; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - invalid_utf8: - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - } - p = p_next; - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - eol_error: - js_parse_error(s, "unexpected line terminator in regexp"); - goto fail; - } - } - if (string_buffer_putc(b, c)) - goto fail; - } - - /* flags */ - for(;;) { - const uint8_t *p_next = p; - c = *p_next++; - if (c >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - } - if (!lre_js_is_ident_next(c)) - break; - if (string_buffer_putc(b2, c)) - goto fail; - p = p_next; - } - - s->token.val = TOK_REGEXP; - s->token.u.regexp.body = string_buffer_end(b); - s->token.u.regexp.flags = string_buffer_end(b2); - s->buf_ptr = p; - return 0; - fail: - string_buffer_free(b); - string_buffer_free(b2); - return -1; -} - -static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, - char *static_buf) -{ - char *buf, *new_buf; - size_t size, new_size; - - buf = *pbuf; - size = *psize; - if (size >= (SIZE_MAX / 3) * 2) - new_size = SIZE_MAX; - else - new_size = size + (size >> 1); - if (buf == static_buf) { - new_buf = js_malloc(ctx, new_size); - if (!new_buf) - return -1; - memcpy(new_buf, buf, size); - } else { - new_buf = js_realloc(ctx, buf, new_size); - if (!new_buf) - return -1; - } - *pbuf = new_buf; - *psize = new_size; - return 0; -} - -/* 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) -{ - const uint8_t *p, *p1; - char ident_buf[128], *buf; - size_t ident_size, ident_pos; - JSAtom atom; - - p = *pp; - buf = ident_buf; - ident_size = sizeof(ident_buf); - ident_pos = 0; - if (is_private) - buf[ident_pos++] = '#'; - for(;;) { - p1 = p; - - if (c < 128) { - buf[ident_pos++] = c; - } else { - ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c); - } - c = *p1++; - if (c == '\\' && *p1 == 'u') { - c = lre_parse_escape(&p1, TRUE); - *pident_has_escape = TRUE; - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - } - if (!lre_js_is_ident_next(c)) - break; - p = p1; - if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { - if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { - atom = JS_ATOM_NULL; - goto done; - } - } - } - atom = JS_NewAtomLen(s->ctx, buf, ident_pos); - done: - if (unlikely(buf != ident_buf)) - js_free(s->ctx, buf); - *pp = p; - return atom; -} - - -static __exception int next_token(JSParseState *s) -{ - const uint8_t *p; - int c; - BOOL ident_has_escape; - JSAtom atom; - - if (js_check_stack_overflow(s->ctx->rt, 0)) { - return js_parse_error(s, "stack overflow"); - } - - free_token(s, &s->token); - - p = s->last_ptr = s->buf_ptr; - s->got_lf = FALSE; - s->last_line_num = s->token.line_num; - redo: - s->token.line_num = s->line_num; - s->token.ptr = p; - c = *p; - switch(c) { - case 0: - if (p >= s->buf_end) { - s->token.val = TOK_EOF; - } else { - goto def_token; - } - break; - case '`': - if (js_parse_template_part(s, p + 1)) - goto fail; - p = s->buf_ptr; - break; - case '\'': - case '\"': - if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) - goto fail; - break; - case '\r': /* accept DOS and MAC newline sequences */ - if (p[1] == '\n') { - p++; - } - /* fall thru */ - case '\n': - p++; - line_terminator: - s->got_lf = TRUE; - s->line_num++; - goto redo; - case '\f': - case '\v': - case ' ': - case '\t': - p++; - goto redo; - case '/': - if (p[1] == '*') { - /* comment */ - p += 2; - for(;;) { - if (*p == '\0' && p >= s->buf_end) { - js_parse_error(s, "unexpected end of comment"); - goto fail; - } - if (p[0] == '*' && p[1] == '/') { - p += 2; - break; - } - if (*p == '\n') { - s->line_num++; - s->got_lf = TRUE; /* considered as LF for ASI */ - p++; - } else if (*p == '\r') { - s->got_lf = TRUE; /* considered as LF for ASI */ - p++; - } else if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == CP_LS || c == CP_PS) { - s->got_lf = TRUE; /* considered as LF for ASI */ - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - goto redo; - } else if (p[1] == '/') { - /* line comment */ - p += 2; - skip_line_comment: - for(;;) { - if (*p == '\0' && p >= s->buf_end) - break; - if (*p == '\r' || *p == '\n') - break; - if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - break; - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - goto redo; - } else if (p[1] == '=') { - p += 2; - s->token.val = TOK_DIV_ASSIGN; - } else { - p++; - s->token.val = c; - } - break; - case '\\': - if (p[1] == 'u') { - const uint8_t *p1 = p + 1; - int c1 = lre_parse_escape(&p1, TRUE); - if (c1 >= 0 && lre_js_is_ident_first(c1)) { - c = c1; - p = p1; - ident_has_escape = TRUE; - goto has_ident; - } else { - /* XXX: syntax error? */ - } - } - goto def_token; - case 'a': 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 '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 '_': - case '$': - /* identifier */ - p++; - ident_has_escape = FALSE; - has_ident: - atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); - if (atom == JS_ATOM_NULL) - goto fail; - s->token.u.ident.atom = atom; - s->token.u.ident.has_escape = ident_has_escape; - s->token.u.ident.is_reserved = FALSE; - s->token.val = TOK_IDENT; - update_token_ident(s); - break; - case '#': - /* private name */ - { - const uint8_t *p1; - p++; - p1 = p; - c = *p1++; - if (c == '\\' && *p1 == 'u') { - c = lre_parse_escape(&p1, TRUE); - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - } - if (!lre_js_is_ident_first(c)) { - js_parse_error(s, "invalid first character of private name"); - goto fail; - } - p = p1; - ident_has_escape = FALSE; /* not used */ - atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); - if (atom == JS_ATOM_NULL) - goto fail; - s->token.u.ident.atom = atom; - s->token.val = TOK_PRIVATE_NAME; - } - break; - case '.': - if (p[1] == '.' && p[2] == '.') { - p += 3; - s->token.val = TOK_ELLIPSIS; - break; - } - if (p[1] >= '0' && p[1] <= '9') { - goto parse_number; - } else { - goto def_token; - } - break; - case '0': - /* in strict mode, octal literals are not accepted */ - if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) { - js_parse_error(s, "octal literals are deprecated in strict mode"); - goto fail; - } - goto parse_number; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': - case '9': - /* number */ - parse_number: - { - JSValue ret; - const uint8_t *p1; - int flags, radix; - flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | - ATOD_ACCEPT_UNDERSCORES; - 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, - flags); -#endif - if (JS_IsException(ret)) - goto fail; - /* reject `10instanceof Number` */ - if (JS_VALUE_IS_NAN(ret) || - lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) { - JS_FreeValue(s->ctx, ret); - js_parse_error(s, "invalid number literal"); - goto fail; - } - s->token.val = TOK_NUMBER; - s->token.u.num.val = ret; - } - break; - case '*': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_MUL_ASSIGN; - } else if (p[1] == '*') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_POW_ASSIGN; - } else { - p += 2; - s->token.val = TOK_POW; - } - } else { - goto def_token; - } - break; - case '%': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_MOD_ASSIGN; - } else { - goto def_token; - } - break; - case '+': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_PLUS_ASSIGN; - } else if (p[1] == '+') { - p += 2; - s->token.val = TOK_INC; - } else { - goto def_token; - } - break; - case '-': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_MINUS_ASSIGN; - } else if (p[1] == '-') { - if (s->allow_html_comments && - p[2] == '>' && s->last_line_num != s->line_num) { - /* Annex B: `-->` at beginning of line is an html comment end. - It extends to the end of the line. - */ - goto skip_line_comment; - } - p += 2; - s->token.val = TOK_DEC; - } else { - goto def_token; - } - break; - case '<': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_LTE; - } else if (p[1] == '<') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_SHL_ASSIGN; - } else { - p += 2; - s->token.val = TOK_SHL; - } - } else if (s->allow_html_comments && - p[1] == '!' && p[2] == '-' && p[3] == '-') { - /* Annex B: handle `<!--` single line html comments */ - goto skip_line_comment; - } else { - goto def_token; - } - break; - case '>': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_GTE; - } else if (p[1] == '>') { - if (p[2] == '>') { - if (p[3] == '=') { - p += 4; - s->token.val = TOK_SHR_ASSIGN; - } else { - p += 3; - s->token.val = TOK_SHR; - } - } else if (p[2] == '=') { - p += 3; - s->token.val = TOK_SAR_ASSIGN; - } else { - p += 2; - s->token.val = TOK_SAR; - } - } else { - goto def_token; - } - break; - case '=': - if (p[1] == '=') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_STRICT_EQ; - } else { - p += 2; - s->token.val = TOK_EQ; - } - } else if (p[1] == '>') { - p += 2; - s->token.val = TOK_ARROW; - } else { - goto def_token; - } - break; - case '!': - if (p[1] == '=') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_STRICT_NEQ; - } else { - p += 2; - s->token.val = TOK_NEQ; - } - } else { - goto def_token; - } - break; - case '&': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_AND_ASSIGN; - } else if (p[1] == '&') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_LAND_ASSIGN; - } else { - p += 2; - s->token.val = TOK_LAND; - } - } else { - goto def_token; - } - break; -#ifdef CONFIG_BIGNUM - /* in math mode, '^' is the power operator. '^^' is always the - xor operator and '**' is always the power operator */ - case '^': - if (p[1] == '=') { - p += 2; - if (s->cur_func->js_mode & JS_MODE_MATH) - s->token.val = TOK_MATH_POW_ASSIGN; - else - s->token.val = TOK_XOR_ASSIGN; - } else if (p[1] == '^') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_XOR_ASSIGN; - } else { - p += 2; - s->token.val = '^'; - } - } else { - p++; - if (s->cur_func->js_mode & JS_MODE_MATH) - s->token.val = TOK_MATH_POW; - else - s->token.val = '^'; - } - break; -#else - case '^': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_XOR_ASSIGN; - } else { - goto def_token; - } - break; -#endif - case '|': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_OR_ASSIGN; - } else if (p[1] == '|') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_LOR_ASSIGN; - } else { - p += 2; - s->token.val = TOK_LOR; - } - } else { - goto def_token; - } - break; - case '?': - if (p[1] == '?') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; - } else { - p += 2; - s->token.val = TOK_DOUBLE_QUESTION_MARK; - } - } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { - p += 2; - s->token.val = TOK_QUESTION_MARK_DOT; - } else { - goto def_token; - } - break; - default: - if (c >= 128) { - /* unicode value */ - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - switch(c) { - case CP_PS: - case CP_LS: - /* XXX: should avoid incrementing line_number, but - needed to handle HTML comments */ - goto line_terminator; - default: - if (lre_is_space(c)) { - goto redo; - } else if (lre_js_is_ident_first(c)) { - ident_has_escape = FALSE; - goto has_ident; - } else { - js_parse_error(s, "unexpected character"); - goto fail; - } - } - } - def_token: - s->token.val = c; - p++; - break; - } - s->buf_ptr = p; - - // dump_token(s, &s->token); - return 0; - - fail: - s->token.val = TOK_ERROR; - return -1; -} - -/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ -static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) -{ - const uint8_t *p; - char ident_buf[128], *buf; - size_t ident_size, ident_pos; - JSAtom atom; - - p = *pp; - buf = ident_buf; - ident_size = sizeof(ident_buf); - ident_pos = 0; - for(;;) { - buf[ident_pos++] = c; - c = *p; - if (c >= 128 || !lre_is_id_continue_byte(c)) - break; - p++; - if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { - if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { - atom = JS_ATOM_NULL; - goto done; - } - } - } - atom = JS_NewAtomLen(s->ctx, buf, ident_pos); - done: - if (unlikely(buf != ident_buf)) - js_free(s->ctx, buf); - *pp = p; - return atom; -} - -static __exception int json_next_token(JSParseState *s) -{ - const uint8_t *p; - int c; - JSAtom atom; - - if (js_check_stack_overflow(s->ctx->rt, 0)) { - return js_parse_error(s, "stack overflow"); - } - - free_token(s, &s->token); - - p = s->last_ptr = s->buf_ptr; - s->last_line_num = s->token.line_num; - redo: - s->token.line_num = s->line_num; - s->token.ptr = p; - c = *p; - switch(c) { - case 0: - if (p >= s->buf_end) { - s->token.val = TOK_EOF; - } else { - goto def_token; - } - break; - case '\'': - if (!s->ext_json) { - /* JSON does not accept single quoted strings */ - goto def_token; - } - /* fall through */ - case '\"': - if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) - goto fail; - break; - case '\r': /* accept DOS and MAC newline sequences */ - if (p[1] == '\n') { - p++; - } - /* fall thru */ - case '\n': - p++; - s->line_num++; - goto redo; - case '\f': - case '\v': - if (!s->ext_json) { - /* JSONWhitespace does not match <VT>, nor <FF> */ - goto def_token; - } - /* fall through */ - case ' ': - case '\t': - p++; - goto redo; - case '/': - if (!s->ext_json) { - /* JSON does not accept comments */ - goto def_token; - } - if (p[1] == '*') { - /* comment */ - p += 2; - for(;;) { - if (*p == '\0' && p >= s->buf_end) { - js_parse_error(s, "unexpected end of comment"); - goto fail; - } - if (p[0] == '*' && p[1] == '/') { - p += 2; - break; - } - if (*p == '\n') { - s->line_num++; - p++; - } else if (*p == '\r') { - p++; - } else if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - goto redo; - } else if (p[1] == '/') { - /* line comment */ - p += 2; - for(;;) { - if (*p == '\0' && p >= s->buf_end) - break; - if (*p == '\r' || *p == '\n') - break; - if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - break; - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - goto redo; - } else { - goto def_token; - } - break; - case 'a': 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 '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 '_': - case '$': - /* identifier : only pure ascii characters are accepted */ - p++; - atom = json_parse_ident(s, &p, c); - if (atom == JS_ATOM_NULL) - goto fail; - s->token.u.ident.atom = atom; - s->token.u.ident.has_escape = FALSE; - s->token.u.ident.is_reserved = FALSE; - s->token.val = TOK_IDENT; - break; - case '+': - if (!s->ext_json || !is_digit(p[1])) - goto def_token; - goto parse_number; - case '0': - if (is_digit(p[1])) - goto def_token; - goto parse_number; - case '-': - if (!is_digit(p[1])) - goto def_token; - goto parse_number; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': - 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; - } - break; - default: - if (c >= 128) { - js_parse_error(s, "unexpected character"); - goto fail; - } - def_token: - s->token.val = c; - p++; - break; - } - s->buf_ptr = p; - - // dump_token(s, &s->token); - return 0; - - fail: - s->token.val = TOK_ERROR; - return -1; -} - -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 (;;) { - switch(c = *p++) { - case '\r': - case '\n': - if (no_line_terminator) - return '\n'; - continue; - case ' ': - case '\t': - case '\v': - case '\f': - continue; - case '/': - if (*p == '/') { - if (no_line_terminator) - return '\n'; - while (*p && *p != '\r' && *p != '\n') - p++; - continue; - } - if (*p == '*') { - while (*++p) { - if ((*p == '\r' || *p == '\n') && no_line_terminator) - return '\n'; - if (*p == '*' && p[1] == '/') { - p += 2; - break; - } - } - continue; - } - break; - case '=': - if (*p == '>') - return TOK_ARROW; - break; - 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 (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; - } -} - -static int peek_token(JSParseState *s, BOOL no_line_terminator) -{ - const uint8_t *p = s->buf_ptr; - return simple_next_token(&p, no_line_terminator); -} - -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. - - Heuristic: skip comments and expect 'import' keyword not followed - by '(' or '.' or export keyword. -*/ -BOOL JS_DetectModule(const char *input, size_t input_len) -{ - const uint8_t *p = (const uint8_t *)input; - int tok; - - skip_shebang(&p, p + input_len); - switch(simple_next_token(&p, FALSE)) { - case TOK_IMPORT: - tok = simple_next_token(&p, FALSE); - return (tok != '.' && tok != '('); - case TOK_EXPORT: - return TRUE; - default: - return FALSE; - } -} - -static inline int get_prev_opcode(JSFunctionDef *fd) { - if (fd->last_opcode_pos < 0) - return OP_invalid; - else - return fd->byte_code.buf[fd->last_opcode_pos]; -} - -static BOOL js_is_live_code(JSParseState *s) { - switch (get_prev_opcode(s->cur_func)) { - case OP_tail_call: - case OP_tail_call_method: - case OP_return: - case OP_return_undef: - case OP_return_async: - case OP_throw: - case OP_throw_error: - case OP_goto: -#if SHORT_OPCODES - case OP_goto8: - case OP_goto16: -#endif - case OP_ret: - return FALSE; - default: - return TRUE; - } -} - -static void emit_u8(JSParseState *s, uint8_t val) -{ - dbuf_putc(&s->cur_func->byte_code, val); -} - -static void emit_u16(JSParseState *s, uint16_t val) -{ - dbuf_put_u16(&s->cur_func->byte_code, val); -} - -static void emit_u32(JSParseState *s, uint32_t val) -{ - dbuf_put_u32(&s->cur_func->byte_code, val); -} - -static void emit_op(JSParseState *s, uint8_t val) -{ - JSFunctionDef *fd = s->cur_func; - DynBuf *bc = &fd->byte_code; - - /* Use the line number of the last token used, not the next token, - nor the current offset in the source file. - */ - if (unlikely(fd->last_opcode_line_num != s->last_line_num)) { - dbuf_putc(bc, OP_line_num); - dbuf_put_u32(bc, s->last_line_num); - fd->last_opcode_line_num = s->last_line_num; - } - fd->last_opcode_pos = bc->size; - dbuf_putc(bc, val); -} - -static void emit_atom(JSParseState *s, JSAtom name) -{ - emit_u32(s, JS_DupAtom(s->ctx, name)); -} - -static int update_label(JSFunctionDef *s, int label, int delta) -{ - LabelSlot *ls; - - assert(label >= 0 && label < s->label_count); - ls = &s->label_slots[label]; - ls->ref_count += delta; - assert(ls->ref_count >= 0); - return ls->ref_count; -} - -static int new_label_fd(JSFunctionDef *fd, int label) -{ - LabelSlot *ls; - - if (label < 0) { - if (js_resize_array(fd->ctx, (void *)&fd->label_slots, - sizeof(fd->label_slots[0]), - &fd->label_size, fd->label_count + 1)) - return -1; - label = fd->label_count++; - ls = &fd->label_slots[label]; - ls->ref_count = 0; - ls->pos = -1; - ls->pos2 = -1; - ls->addr = -1; - ls->first_reloc = NULL; - } - return label; -} - -static int new_label(JSParseState *s) -{ - return new_label_fd(s->cur_func, -1); -} - -/* 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 */ -static int emit_label(JSParseState *s, int label) -{ - if (label >= 0) { - emit_op(s, OP_label); - emit_u32(s, label); - s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; - return s->cur_func->byte_code.size - 4; - } else { - return -1; - } -} - -/* return label or -1 if dead code */ -static int emit_goto(JSParseState *s, int opcode, int label) -{ - if (js_is_live_code(s)) { - if (label < 0) - label = new_label(s); - emit_op(s, opcode); - emit_u32(s, label); - s->cur_func->label_slots[label].ref_count++; - return label; - } - return -1; -} - -/* return the constant pool index. 'val' is not duplicated. */ -static int cpool_add(JSParseState *s, JSValue val) -{ - JSFunctionDef *fd = s->cur_func; - - if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), - &fd->cpool_size, fd->cpool_count + 1)) - return -1; - fd->cpool[fd->cpool_count++] = val; - return fd->cpool_count - 1; -} - -static __exception int emit_push_const(JSParseState *s, JSValueConst val, - BOOL as_atom) -{ - int idx; - - if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) { - JSAtom atom; - /* warning: JS_NewAtomStr frees the string value */ - JS_DupValue(s->ctx, val); - atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); - if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { - emit_op(s, OP_push_atom_value); - emit_u32(s, atom); - return 0; - } - } - - idx = cpool_add(s, JS_DupValue(s->ctx, val)); - if (idx < 0) - return -1; - emit_op(s, OP_push_const); - emit_u32(s, idx); - return 0; -} - -/* return the variable index or -1 if not found, - add ARGUMENT_VAR_OFFSET for argument variables */ -static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) -{ - int i; - for(i = fd->arg_count; i-- > 0;) { - if (fd->args[i].var_name == name) - return i | ARGUMENT_VAR_OFFSET; - } - return -1; -} - -static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) -{ - int i; - for(i = fd->var_count; i-- > 0;) { - if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0) - return i; - } - return find_arg(ctx, fd, name); -} - -/* find a variable declaration in a given scope */ -static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_level) -{ - int scope_idx; - for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0; - scope_idx = fd->vars[scope_idx].scope_next) { - if (fd->vars[scope_idx].scope_level != scope_level) - break; - if (fd->vars[scope_idx].var_name == name) - return scope_idx; - } - return -1; -} - -/* return true if scope == parent_scope or if scope is a child of - parent_scope */ -static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd, - int scope, int parent_scope) -{ - while (scope >= 0) { - if (scope == parent_scope) - return TRUE; - scope = fd->scopes[scope].parent; - } - return FALSE; -} - -/* find a 'var' declaration in the same scope or a child scope */ -static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_level) -{ - int i; - for(i = 0; i < fd->var_count; i++) { - JSVarDef *vd = &fd->vars[i]; - if (vd->var_name == name && vd->scope_level == 0) { - if (is_child_scope(ctx, fd, vd->scope_next, - scope_level)) - return i; - } - } - return -1; -} - - -static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) -{ - int i; - for(i = 0; i < fd->global_var_count; i++) { - JSGlobalVar *hf = &fd->global_vars[i]; - if (hf->var_name == name) - return hf; - } - return NULL; - -} - -static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) -{ - JSGlobalVar *hf = find_global_var(fd, name); - if (hf && hf->is_lexical) - return hf; - else - return NULL; -} - -static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, - int scope_idx, BOOL check_catch_var) -{ - while (scope_idx >= 0) { - JSVarDef *vd = &fd->vars[scope_idx]; - if (vd->var_name == name && - (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH && - check_catch_var))) - return scope_idx; - scope_idx = vd->scope_next; - } - - if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { - if (find_lexical_global_var(fd, name)) - return GLOBAL_VAR_OFFSET; - } - return -1; -} - -static int push_scope(JSParseState *s) { - if (s->cur_func) { - JSFunctionDef *fd = s->cur_func; - int scope = fd->scope_count; - /* XXX: should check for scope overflow */ - if ((fd->scope_count + 1) > fd->scope_size) { - int new_size; - size_t slack; - JSVarScope *new_buf; - /* XXX: potential arithmetic overflow */ - new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2); - if (fd->scopes == fd->def_scope_array) { - new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack); - if (!new_buf) - return -1; - memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes)); - } else { - new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack); - if (!new_buf) - return -1; - } - new_size += slack / sizeof(*new_buf); - fd->scopes = new_buf; - fd->scope_size = new_size; - } - fd->scope_count++; - fd->scopes[scope].parent = fd->scope_level; - fd->scopes[scope].first = fd->scope_first; - emit_op(s, OP_enter_scope); - emit_u16(s, scope); - return fd->scope_level = scope; - } - return 0; -} - -static int get_first_lexical_var(JSFunctionDef *fd, int scope) -{ - while (scope >= 0) { - int scope_idx = fd->scopes[scope].first; - if (scope_idx >= 0) - return scope_idx; - scope = fd->scopes[scope].parent; - } - return -1; -} - -static void pop_scope(JSParseState *s) { - if (s->cur_func) { - /* disable scoped variables */ - JSFunctionDef *fd = s->cur_func; - int scope = fd->scope_level; - emit_op(s, OP_leave_scope); - emit_u16(s, scope); - fd->scope_level = fd->scopes[scope].parent; - fd->scope_first = get_first_lexical_var(fd, fd->scope_level); - } -} - -static void close_scopes(JSParseState *s, int scope, int scope_stop) -{ - while (scope > scope_stop) { - emit_op(s, OP_leave_scope); - emit_u16(s, scope); - scope = s->cur_func->scopes[scope].parent; - } -} - -/* return the variable index or -1 if error */ -static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) -{ - JSVarDef *vd; - - /* the local variable indexes are currently stored on 16 bits */ - if (fd->var_count >= JS_MAX_LOCAL_VARS) { - JS_ThrowInternalError(ctx, "too many local variables"); - return -1; - } - if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]), - &fd->var_size, fd->var_count + 1)) - return -1; - vd = &fd->vars[fd->var_count++]; - memset(vd, 0, sizeof(*vd)); - vd->var_name = JS_DupAtom(ctx, name); - vd->func_pool_idx = -1; - return fd->var_count - 1; -} - -static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name, - JSVarKindEnum var_kind) -{ - int idx = add_var(ctx, fd, name); - if (idx >= 0) { - JSVarDef *vd = &fd->vars[idx]; - vd->var_kind = var_kind; - vd->scope_level = fd->scope_level; - vd->scope_next = fd->scope_first; - fd->scopes[fd->scope_level].first = idx; - fd->scope_first = idx; - } - return idx; -} - -static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) -{ - int idx = fd->func_var_idx; - if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { - fd->func_var_idx = idx; - fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME; - if (fd->js_mode & JS_MODE_STRICT) - fd->vars[idx].is_const = TRUE; - } - return idx; -} - -static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) -{ - int idx = fd->arguments_var_idx; - if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) { - fd->arguments_var_idx = idx; - } - return idx; -} - -/* add an argument definition in the argument scope. Only needed when - "eval()" may be called in the argument scope. Return 0 if OK. */ -static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd) -{ - int idx; - if (fd->arguments_arg_idx < 0) { - idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX); - if (idx < 0) { - /* XXX: the scope links are not fully updated. May be an - issue if there are child scopes of the argument - scope */ - idx = add_var(ctx, fd, JS_ATOM_arguments); - if (idx < 0) - return -1; - fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first; - fd->scopes[ARG_SCOPE_INDEX].first = idx; - fd->vars[idx].scope_level = ARG_SCOPE_INDEX; - fd->vars[idx].is_lexical = TRUE; - - fd->arguments_arg_idx = idx; - } - } - return 0; -} - -static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) -{ - JSVarDef *vd; - - /* the local variable indexes are currently stored on 16 bits */ - if (fd->arg_count >= JS_MAX_LOCAL_VARS) { - JS_ThrowInternalError(ctx, "too many arguments"); - return -1; - } - if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), - &fd->arg_size, fd->arg_count + 1)) - return -1; - vd = &fd->args[fd->arg_count++]; - memset(vd, 0, sizeof(*vd)); - vd->var_name = JS_DupAtom(ctx, name); - vd->func_pool_idx = -1; - return fd->arg_count - 1; -} - -/* add a global variable definition */ -static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, - JSAtom name) -{ - JSGlobalVar *hf; - - if (js_resize_array(ctx, (void **)&s->global_vars, - sizeof(s->global_vars[0]), - &s->global_var_size, s->global_var_count + 1)) - return NULL; - hf = &s->global_vars[s->global_var_count++]; - hf->cpool_idx = -1; - hf->force_init = FALSE; - hf->is_lexical = FALSE; - hf->is_const = FALSE; - hf->scope_level = s->scope_level; - hf->var_name = JS_DupAtom(ctx, name); - return hf; -} - -typedef enum { - JS_VAR_DEF_WITH, - JS_VAR_DEF_LET, - JS_VAR_DEF_CONST, - JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ - JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ - JS_VAR_DEF_CATCH, - JS_VAR_DEF_VAR, -} JSVarDefEnum; - -static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, - JSVarDefEnum var_def_type) -{ - JSContext *ctx = s->ctx; - JSVarDef *vd; - int idx; - - switch (var_def_type) { - case JS_VAR_DEF_WITH: - idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL); - break; - - case JS_VAR_DEF_LET: - case JS_VAR_DEF_CONST: - case JS_VAR_DEF_FUNCTION_DECL: - case JS_VAR_DEF_NEW_FUNCTION_DECL: - idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE); - if (idx >= 0) { - if (idx < GLOBAL_VAR_OFFSET) { - if (fd->vars[idx].scope_level == fd->scope_level) { - /* same scope: in non strict mode, functions - can be redefined (annex B.3.3.4). */ - if (!(!(fd->js_mode & JS_MODE_STRICT) && - var_def_type == JS_VAR_DEF_FUNCTION_DECL && - fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) { - goto redef_lex_error; - } - } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) { - goto redef_lex_error; - } - } else { - if (fd->scope_level == fd->body_scope) { - redef_lex_error: - /* redefining a scoped var in the same scope: error */ - return js_parse_error(s, "invalid redefinition of lexical identifier"); - } - } - } - if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && - var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && - fd->scope_level == fd->body_scope && - find_arg(ctx, fd, name) >= 0) { - /* lexical variable redefines a parameter name */ - return js_parse_error(s, "invalid redefinition of parameter name"); - } - - if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) { - return js_parse_error(s, "invalid redefinition of a variable"); - } - - if (fd->is_global_var) { - JSGlobalVar *hf; - hf = find_global_var(fd, name); - if (hf && is_child_scope(ctx, fd, hf->scope_level, - fd->scope_level)) { - return js_parse_error(s, "invalid redefinition of global identifier"); - } - } - - if (fd->is_eval && - (fd->eval_type == JS_EVAL_TYPE_GLOBAL || - fd->eval_type == JS_EVAL_TYPE_MODULE) && - fd->scope_level == fd->body_scope) { - JSGlobalVar *hf; - hf = add_global_var(s->ctx, fd, name); - if (!hf) - return -1; - hf->is_lexical = TRUE; - hf->is_const = (var_def_type == JS_VAR_DEF_CONST); - idx = GLOBAL_VAR_OFFSET; - } else { - JSVarKindEnum var_kind; - if (var_def_type == JS_VAR_DEF_FUNCTION_DECL) - var_kind = JS_VAR_FUNCTION_DECL; - else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL) - var_kind = JS_VAR_NEW_FUNCTION_DECL; - else - var_kind = JS_VAR_NORMAL; - idx = add_scope_var(ctx, fd, name, var_kind); - if (idx >= 0) { - vd = &fd->vars[idx]; - vd->is_lexical = 1; - vd->is_const = (var_def_type == JS_VAR_DEF_CONST); - } - } - break; - - case JS_VAR_DEF_CATCH: - idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH); - break; - - case JS_VAR_DEF_VAR: - if (find_lexical_decl(ctx, fd, name, fd->scope_first, - FALSE) >= 0) { - invalid_lexical_redefinition: - /* error to redefine a var that inside a lexical scope */ - return js_parse_error(s, "invalid redefinition of lexical identifier"); - } - if (fd->is_global_var) { - JSGlobalVar *hf; - hf = find_global_var(fd, name); - if (hf && hf->is_lexical && hf->scope_level == fd->scope_level && - fd->eval_type == JS_EVAL_TYPE_MODULE) { - goto invalid_lexical_redefinition; - } - hf = add_global_var(s->ctx, fd, name); - if (!hf) - return -1; - idx = GLOBAL_VAR_OFFSET; - } else { - /* if the variable already exists, don't add it again */ - idx = find_var(ctx, fd, name); - if (idx >= 0) - break; - idx = add_var(ctx, fd, name); - if (idx >= 0) { - if (name == JS_ATOM_arguments && fd->has_arguments_binding) - fd->arguments_var_idx = idx; - fd->vars[idx].scope_next = fd->scope_level; - } - } - break; - default: - abort(); - } - return idx; -} - -/* add a private field variable in the current scope */ -static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, - JSAtom name, JSVarKindEnum var_kind, BOOL is_static) -{ - JSContext *ctx = s->ctx; - JSVarDef *vd; - int idx; - - idx = add_scope_var(ctx, fd, name, var_kind); - if (idx < 0) - return idx; - vd = &fd->vars[idx]; - vd->is_lexical = 1; - vd->is_const = 1; - vd->is_static_private = is_static; - return idx; -} - -static __exception int js_parse_expr(JSParseState *s); -static __exception int js_parse_function_decl(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, const uint8_t *ptr, - int start_line); -static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); -static __exception int js_parse_function_decl2(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, - const uint8_t *ptr, - int function_line_num, - JSParseExportEnum export_flag, - JSFunctionDef **pfd); -static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); -static __exception int js_parse_assign_expr(JSParseState *s); -static __exception int js_parse_unary(JSParseState *s, int parse_flags); -static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, - JSAtom label_name, - int label_break, int label_cont, - int drop_count); -static void pop_break_entry(JSFunctionDef *fd); -static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, - JSAtom local_name, JSAtom export_name, - JSExportTypeEnum export_type); - -/* Note: all the fields are already sealed except length */ -static int seal_template_obj(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - JSShapeProperty *prs; - - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property1(p, JS_ATOM_length); - if (prs) { - if (js_update_property_flags(ctx, p, &prs, - prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE))) - return -1; - } - p->extensible = FALSE; - return 0; -} - -static __exception int js_parse_template(JSParseState *s, int call, int *argc) -{ - JSContext *ctx = s->ctx; - JSValue raw_array, template_object; - JSToken cooked; - int depth, ret; - - raw_array = JS_UNDEFINED; /* avoid warning */ - template_object = JS_UNDEFINED; /* avoid warning */ - if (call) { - /* Create a template object: an array of cooked strings */ - /* Create an array of raw strings and store it to the raw property */ - template_object = JS_NewArray(ctx); - if (JS_IsException(template_object)) - return -1; - // pool_idx = s->cur_func->cpool_count; - ret = emit_push_const(s, template_object, 0); - JS_FreeValue(ctx, template_object); - if (ret) - return -1; - raw_array = JS_NewArray(ctx); - if (JS_IsException(raw_array)) - return -1; - if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw, - raw_array, JS_PROP_THROW) < 0) { - return -1; - } - } - - depth = 0; - while (s->token.val == TOK_TEMPLATE) { - const uint8_t *p = s->token.ptr + 1; - cooked = s->token; - if (call) { - if (JS_DefinePropertyValueUint32(ctx, raw_array, depth, - JS_DupValue(ctx, s->token.u.str.str), - JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { - return -1; - } - /* re-parse the string with escape sequences but do not throw a - syntax error if it contains invalid sequences - */ - if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) { - cooked.u.str.str = JS_UNDEFINED; - } - if (JS_DefinePropertyValueUint32(ctx, template_object, depth, - cooked.u.str.str, - JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { - return -1; - } - } else { - JSString *str; - /* re-parse the string with escape sequences and throw a - syntax error if it contains invalid sequences - */ - JS_FreeValue(ctx, s->token.u.str.str); - s->token.u.str.str = JS_UNDEFINED; - if (js_parse_string(s, '`', TRUE, p, &cooked, &p)) - return -1; - str = JS_VALUE_GET_STRING(cooked.u.str.str); - if (str->len != 0 || depth == 0) { - ret = emit_push_const(s, cooked.u.str.str, 1); - JS_FreeValue(s->ctx, cooked.u.str.str); - if (ret) - return -1; - if (depth == 0) { - if (s->token.u.str.sep == '`') - goto done1; - emit_op(s, OP_get_field2); - emit_atom(s, JS_ATOM_concat); - } - depth++; - } else { - JS_FreeValue(s->ctx, cooked.u.str.str); - } - } - if (s->token.u.str.sep == '`') - goto done; - if (next_token(s)) - return -1; - if (js_parse_expr(s)) - return -1; - depth++; - if (s->token.val != '}') { - return js_parse_error(s, "expected '}' after template expression"); - } - /* XXX: should convert to string at this stage? */ - free_token(s, &s->token); - /* Resume TOK_TEMPLATE parsing (s->token.line_num and - * s->token.ptr are OK) */ - s->got_lf = FALSE; - s->last_line_num = s->token.line_num; - if (js_parse_template_part(s, s->buf_ptr)) - return -1; - } - return js_parse_expect(s, TOK_TEMPLATE); - - done: - if (call) { - /* Seal the objects */ - seal_template_obj(ctx, raw_array); - seal_template_obj(ctx, template_object); - *argc = depth + 1; - } else { - emit_op(s, OP_call_method); - emit_u16(s, depth - 1); - } - done1: - return next_token(s); -} - - -#define PROP_TYPE_IDENT 0 -#define PROP_TYPE_VAR 1 -#define PROP_TYPE_GET 2 -#define PROP_TYPE_SET 3 -#define PROP_TYPE_STAR 4 -#define PROP_TYPE_ASYNC 5 -#define PROP_TYPE_ASYNC_STAR 6 - -#define PROP_TYPE_PRIVATE (1 << 4) - -static BOOL token_is_ident(int tok) -{ - /* Accept keywords and reserved words as property names */ - return (tok == TOK_IDENT || - (tok >= TOK_FIRST_KEYWORD && - tok <= TOK_LAST_KEYWORD)); -} - -/* if the property is an expression, name = JS_ATOM_NULL */ -static int __exception js_parse_property_name(JSParseState *s, - JSAtom *pname, - BOOL allow_method, BOOL allow_var, - BOOL allow_private) -{ - int is_private = 0; - BOOL is_non_reserved_ident; - JSAtom name; - int prop_type; - - prop_type = PROP_TYPE_IDENT; - if (allow_method) { - if (token_is_pseudo_keyword(s, JS_ATOM_get) - || token_is_pseudo_keyword(s, JS_ATOM_set)) { - /* get x(), set x() */ - name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) - goto fail1; - if (s->token.val == ':' || s->token.val == ',' || - s->token.val == '}' || s->token.val == '(' || - s->token.val == '=') { - is_non_reserved_ident = TRUE; - goto ident_found; - } - prop_type = PROP_TYPE_GET + (name == JS_ATOM_set); - JS_FreeAtom(s->ctx, name); - } else if (s->token.val == '*') { - if (next_token(s)) - goto fail; - prop_type = PROP_TYPE_STAR; - } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) != '\n') { - name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) - goto fail1; - if (s->token.val == ':' || s->token.val == ',' || - s->token.val == '}' || s->token.val == '(' || - s->token.val == '=') { - is_non_reserved_ident = TRUE; - goto ident_found; - } - JS_FreeAtom(s->ctx, name); - if (s->token.val == '*') { - if (next_token(s)) - goto fail; - prop_type = PROP_TYPE_ASYNC_STAR; - } else { - prop_type = PROP_TYPE_ASYNC; - } - } - } - - if (token_is_ident(s->token.val)) { - /* variable can only be a non-reserved identifier */ - is_non_reserved_ident = - (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved); - /* keywords and reserved words have a valid atom */ - name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) - goto fail1; - ident_found: - if (is_non_reserved_ident && - prop_type == PROP_TYPE_IDENT && allow_var) { - if (!(s->token.val == ':' || - (s->token.val == '(' && allow_method))) { - prop_type = PROP_TYPE_VAR; - } - } - } else if (s->token.val == TOK_STRING) { - name = JS_ValueToAtom(s->ctx, s->token.u.str.str); - if (name == JS_ATOM_NULL) - goto fail; - if (next_token(s)) - goto fail1; - } else if (s->token.val == TOK_NUMBER) { - JSValue val; - val = s->token.u.num.val; -#ifdef CONFIG_BIGNUM - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - val = s->ctx->rt->bigfloat_ops. - mul_pow10_to_float64(s->ctx, &p->num, - s->token.u.num.exponent); - if (JS_IsException(val)) - goto fail; - name = JS_ValueToAtom(s->ctx, val); - JS_FreeValue(s->ctx, val); - } else -#endif - { - name = JS_ValueToAtom(s->ctx, val); - } - if (name == JS_ATOM_NULL) - goto fail; - if (next_token(s)) - goto fail1; - } else if (s->token.val == '[') { - if (next_token(s)) - goto fail; - if (js_parse_expr(s)) - goto fail; - if (js_parse_expect(s, ']')) - goto fail; - name = JS_ATOM_NULL; - } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) { - name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) - goto fail1; - is_private = PROP_TYPE_PRIVATE; - } else { - goto invalid_prop; - } - if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR && - s->token.val != '(') { - JS_FreeAtom(s->ctx, name); - invalid_prop: - js_parse_error(s, "invalid property name"); - goto fail; - } - *pname = name; - return prop_type | is_private; - fail1: - JS_FreeAtom(s->ctx, name); - fail: - *pname = JS_ATOM_NULL; - return -1; -} - -typedef struct JSParsePos { - int last_line_num; - int line_num; - BOOL got_lf; - const uint8_t *ptr; -} JSParsePos; - -static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) -{ - sp->last_line_num = s->last_line_num; - sp->line_num = s->token.line_num; - sp->ptr = s->token.ptr; - sp->got_lf = s->got_lf; - return 0; -} - -static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) -{ - s->token.line_num = sp->last_line_num; - s->line_num = sp->line_num; - s->buf_ptr = sp->ptr; - s->got_lf = sp->got_lf; - return next_token(s); -} - -/* return TRUE if a regexp literal is allowed after this token */ -static BOOL is_regexp_allowed(int tok) -{ - switch (tok) { - case TOK_NUMBER: - case TOK_STRING: - case TOK_REGEXP: - case TOK_DEC: - case TOK_INC: - case TOK_NULL: - case TOK_FALSE: - case TOK_TRUE: - case TOK_THIS: - case ')': - case ']': - case '}': /* XXX: regexp may occur after */ - case TOK_IDENT: - return FALSE; - default: - return TRUE; - } -} - -#define SKIP_HAS_SEMI (1 << 0) -#define SKIP_HAS_ELLIPSIS (1 << 1) -#define SKIP_HAS_ASSIGNMENT (1 << 2) - -/* XXX: improve speed with early bailout */ -/* XXX: no longer works if regexps are present. Could use previous - regexp parsing heuristics to handle most cases */ -static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator) -{ - char state[256]; - size_t level = 0; - JSParsePos pos; - int last_tok, tok = TOK_EOF; - int c, tok_len, bits = 0; - - /* protect from underflow */ - state[level++] = 0; - - js_parse_get_pos(s, &pos); - last_tok = 0; - for (;;) { - switch(s->token.val) { - case '(': - case '[': - case '{': - if (level >= sizeof(state)) - goto done; - state[level++] = s->token.val; - break; - case ')': - if (state[--level] != '(') - goto done; - break; - case ']': - if (state[--level] != '[') - goto done; - break; - case '}': - c = state[--level]; - if (c == '`') { - /* continue the parsing of the template */ - free_token(s, &s->token); - /* Resume TOK_TEMPLATE parsing (s->token.line_num and - * s->token.ptr are OK) */ - s->got_lf = FALSE; - s->last_line_num = s->token.line_num; - if (js_parse_template_part(s, s->buf_ptr)) - goto done; - goto handle_template; - } else if (c != '{') { - goto done; - } - break; - case TOK_TEMPLATE: - handle_template: - if (s->token.u.str.sep != '`') { - /* '${' inside the template : closing '}' and continue - parsing the template */ - if (level >= sizeof(state)) - goto done; - state[level++] = '`'; - } - break; - case TOK_EOF: - goto done; - case ';': - if (level == 2) { - bits |= SKIP_HAS_SEMI; - } - break; - case TOK_ELLIPSIS: - if (level == 2) { - bits |= SKIP_HAS_ELLIPSIS; - } - break; - case '=': - bits |= SKIP_HAS_ASSIGNMENT; - break; - - case TOK_DIV_ASSIGN: - tok_len = 2; - goto parse_regexp; - case '/': - tok_len = 1; - parse_regexp: - if (is_regexp_allowed(last_tok)) { - s->buf_ptr -= tok_len; - if (js_parse_regexp(s)) { - /* XXX: should clear the exception */ - goto done; - } - } - break; - } - /* last_tok is only used to recognize regexps */ - if (s->token.val == TOK_IDENT && - (token_is_pseudo_keyword(s, JS_ATOM_of) || - token_is_pseudo_keyword(s, JS_ATOM_yield))) { - last_tok = TOK_OF; - } else { - last_tok = s->token.val; - } - if (next_token(s)) { - /* XXX: should clear the exception generated by next_token() */ - break; - } - if (level <= 1) { - tok = s->token.val; - if (token_is_pseudo_keyword(s, JS_ATOM_of)) - tok = TOK_OF; - if (no_line_terminator && s->last_line_num != s->token.line_num) - tok = '\n'; - break; - } - } - done: - if (pbits) { - *pbits = bits; - } - if (js_parse_seek_token(s, &pos)) - return -1; - return tok; -} - -static void set_object_name(JSParseState *s, JSAtom name) -{ - JSFunctionDef *fd = s->cur_func; - int opcode; - - opcode = get_prev_opcode(fd); - if (opcode == OP_set_name) { - /* XXX: should free atom after OP_set_name? */ - fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; - emit_op(s, OP_set_name); - emit_atom(s, name); - } else if (opcode == OP_set_class_name) { - int define_class_pos; - JSAtom atom; - define_class_pos = fd->last_opcode_pos + 1 - - get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); - assert(fd->byte_code.buf[define_class_pos] == OP_define_class); - /* for consistency we free the previous atom which is - JS_ATOM_empty_string */ - atom = get_u32(fd->byte_code.buf + define_class_pos + 1); - JS_FreeAtom(s->ctx, atom); - put_u32(fd->byte_code.buf + define_class_pos + 1, - JS_DupAtom(s->ctx, name)); - fd->last_opcode_pos = -1; - } -} - -static void set_object_name_computed(JSParseState *s) -{ - JSFunctionDef *fd = s->cur_func; - int opcode; - - opcode = get_prev_opcode(fd); - if (opcode == OP_set_name) { - /* XXX: should free atom after OP_set_name? */ - fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; - emit_op(s, OP_set_name_computed); - } else if (opcode == OP_set_class_name) { - int define_class_pos; - define_class_pos = fd->last_opcode_pos + 1 - - get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); - assert(fd->byte_code.buf[define_class_pos] == OP_define_class); - fd->byte_code.buf[define_class_pos] = OP_define_class_computed; - fd->last_opcode_pos = -1; - } -} - -static __exception int js_parse_object_literal(JSParseState *s) -{ - JSAtom name = JS_ATOM_NULL; - const uint8_t *start_ptr; - int start_line, prop_type; - BOOL has_proto; - - if (next_token(s)) - goto fail; - /* XXX: add an initial length that will be patched back */ - emit_op(s, OP_object); - has_proto = FALSE; - while (s->token.val != '}') { - /* specific case for getter/setter */ - start_ptr = s->token.ptr; - start_line = s->token.line_num; - - if (s->token.val == TOK_ELLIPSIS) { - if (next_token(s)) - return -1; - if (js_parse_assign_expr(s)) - return -1; - emit_op(s, OP_null); /* dummy excludeList */ - emit_op(s, OP_copy_data_properties); - emit_u8(s, 2 | (1 << 2) | (0 << 5)); - emit_op(s, OP_drop); /* pop excludeList */ - emit_op(s, OP_drop); /* pop src object */ - goto next; - } - - prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE); - if (prop_type < 0) - goto fail; - - if (prop_type == PROP_TYPE_VAR) { - /* shortcut for x: x */ - emit_op(s, OP_scope_get_var); - emit_atom(s, name); - emit_u16(s, s->cur_func->scope_level); - emit_op(s, OP_define_field); - emit_atom(s, name); - } else if (s->token.val == '(') { - BOOL is_getset = (prop_type == PROP_TYPE_GET || - prop_type == PROP_TYPE_SET); - JSParseFunctionEnum func_type; - JSFunctionKindEnum func_kind; - int op_flags; - - func_kind = JS_FUNC_NORMAL; - if (is_getset) { - func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET; - } else { - func_type = JS_PARSE_FUNC_METHOD; - if (prop_type == PROP_TYPE_STAR) - func_kind = JS_FUNC_GENERATOR; - else if (prop_type == PROP_TYPE_ASYNC) - func_kind = JS_FUNC_ASYNC; - else if (prop_type == PROP_TYPE_ASYNC_STAR) - func_kind = JS_FUNC_ASYNC_GENERATOR; - } - if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL, - start_ptr, start_line)) - goto fail; - if (name == JS_ATOM_NULL) { - emit_op(s, OP_define_method_computed); - } else { - emit_op(s, OP_define_method); - emit_atom(s, name); - } - if (is_getset) { - op_flags = OP_DEFINE_METHOD_GETTER + - prop_type - PROP_TYPE_GET; - } else { - op_flags = OP_DEFINE_METHOD_METHOD; - } - emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE); - } else { - if (js_parse_expect(s, ':')) - goto fail; - if (js_parse_assign_expr(s)) - goto fail; - if (name == JS_ATOM_NULL) { - set_object_name_computed(s); - emit_op(s, OP_define_array_el); - emit_op(s, OP_drop); - } else if (name == JS_ATOM___proto__) { - if (has_proto) { - js_parse_error(s, "duplicate __proto__ property name"); - goto fail; - } - emit_op(s, OP_set_proto); - has_proto = TRUE; - } else { - set_object_name(s, name); - emit_op(s, OP_define_field); - emit_atom(s, name); - } - } - JS_FreeAtom(s->ctx, name); - next: - name = JS_ATOM_NULL; - if (s->token.val != ',') - break; - if (next_token(s)) - goto fail; - } - if (js_parse_expect(s, '}')) - goto fail; - return 0; - fail: - JS_FreeAtom(s->ctx, name); - return -1; -} - -/* allow the 'in' binary operator */ -#define PF_IN_ACCEPTED (1 << 0) -/* allow function calls parsing in js_parse_postfix_expr() */ -#define PF_POSTFIX_CALL (1 << 1) -/* allow the exponentiation operator in js_parse_unary() */ -#define PF_POW_ALLOWED (1 << 2) -/* forbid the exponentiation operator in js_parse_unary() */ -#define PF_POW_FORBIDDEN (1 << 3) - -static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); - -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); - if (has_super) { - /* spec change: no argument evaluation */ - str = "(){super(...arguments);}"; - func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; - } else { - str = "(){}"; - func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; - } - line_num = s->token.line_num; - saved_buf_end = s->buf_end; - s->buf_ptr = (uint8_t *)str; - s->buf_end = (uint8_t *)(str + strlen(str)); - ret = next_token(s); - if (!ret) { - ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL, - JS_ATOM_NULL, (uint8_t *)str, - line_num, JS_PARSE_EXPORT_NONE, pfd); - } - s->buf_end = saved_buf_end; - ret |= js_parse_seek_token(s, &pos); - return ret; -} - -/* find field in the current scope */ -static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_level) -{ - int idx; - idx = fd->scopes[scope_level].first; - while (idx != -1) { - if (fd->vars[idx].scope_level != scope_level) - break; - if (fd->vars[idx].var_name == name) - return idx; - idx = fd->vars[idx].scope_next; - } - return -1; -} - -/* initialize the class fields, called by the constructor. Note: - super() can be called in an arrow function, so <this> and - <class_fields_init> can be variable references */ -static void emit_class_field_init(JSParseState *s) -{ - int label_next; - - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_class_fields_init); - emit_u16(s, s->cur_func->scope_level); - - /* no need to call the class field initializer if not defined */ - emit_op(s, OP_dup); - label_next = emit_goto(s, OP_if_false, -1); - - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - - emit_op(s, OP_swap); - - emit_op(s, OP_call_method); - emit_u16(s, 0); - - emit_label(s, label_next); - emit_op(s, OP_drop); -} - -/* build a private setter function name from the private getter name */ -static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name) -{ - return js_atom_concat_str(ctx, name, "<set>"); -} - -typedef struct { - JSFunctionDef *fields_init_fd; - int computed_fields_count; - BOOL 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; - - 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); - - 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); - emit_u32(s, cpool_idx); - emit_op(s, OP_set_home_object); -} - - -static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, - JSParseExportEnum export_flag) -{ - JSContext *ctx = s->ctx; - JSFunctionDef *fd = s->cur_func; - JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1; - JSAtom class_var_name = JS_ATOM_NULL; - JSFunctionDef *method_fd, *ctor_fd; - int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset; - int class_flags = 0, i, define_class_offset; - BOOL is_static, is_private; - const uint8_t *class_start_ptr = s->token.ptr; - const uint8_t *start_ptr; - ClassFieldsDef class_fields[2]; - - /* classes are parsed and executed in strict mode */ - saved_js_mode = fd->js_mode; - fd->js_mode |= JS_MODE_STRICT; - if (next_token(s)) - goto fail; - if (s->token.val == TOK_IDENT) { - if (s->token.u.ident.is_reserved) { - js_parse_error_reserved_identifier(s); - goto fail; - } - class_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) - goto fail; - } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) { - js_parse_error(s, "class statement requires a name"); - goto fail; - } - if (!is_class_expr) { - if (class_name == JS_ATOM_NULL) - class_var_name = JS_ATOM__default_; /* export default */ - else - class_var_name = class_name; - class_var_name = JS_DupAtom(ctx, class_var_name); - } - - push_scope(s); - - if (s->token.val == TOK_EXTENDS) { - class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; - if (next_token(s)) - goto fail; - if (js_parse_left_hand_side_expr(s)) - goto fail; - } else { - emit_op(s, OP_undefined); - } - - /* add a 'const' definition for the class name */ - if (class_name != JS_ATOM_NULL) { - class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST); - if (class_name_var_idx < 0) - goto fail; - } - - if (js_parse_expect(s, '{')) - goto fail; - - /* this scope contains the private fields */ - push_scope(s); - - emit_op(s, OP_push_const); - ctor_cpool_offset = fd->byte_code.size; - emit_u32(s, 0); /* will be patched at the end of the class parsing */ - - if (class_name == JS_ATOM_NULL) { - if (class_var_name != JS_ATOM_NULL) - class_name1 = JS_ATOM_default; - else - class_name1 = JS_ATOM_empty_string; - } else { - class_name1 = class_name; - } - - emit_op(s, OP_define_class); - emit_atom(s, class_name1); - emit_u8(s, class_flags); - define_class_offset = fd->last_opcode_pos; - - for(i = 0; i < 2; i++) { - ClassFieldsDef *cf = &class_fields[i]; - cf->fields_init_fd = NULL; - cf->computed_fields_count = 0; - cf->need_brand = FALSE; - cf->is_static = i; - } - - ctor_fd = NULL; - while (s->token.val != '}') { - if (s->token.val == ';') { - if (next_token(s)) - goto fail; - continue; - } - 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: <empty> - if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num, - 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: <empty> - 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; - name = JS_DupAtom(ctx, JS_ATOM_static); - prop_type = PROP_TYPE_IDENT; - } - } - if (is_static) - emit_op(s, OP_swap); - start_ptr = s->token.ptr; - if (prop_type < 0) { - prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); - if (prop_type < 0) - goto fail; - } - is_private = prop_type & PROP_TYPE_PRIVATE; - prop_type &= ~PROP_TYPE_PRIVATE; - - if ((name == JS_ATOM_constructor && !is_static && - prop_type != PROP_TYPE_IDENT) || - (name == JS_ATOM_prototype && is_static) || - name == JS_ATOM_hash_constructor) { - js_parse_error(s, "invalid method name"); - goto fail; - } - if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) { - BOOL is_set = prop_type - PROP_TYPE_GET; - JSFunctionDef *method_fd; - - if (is_private) { - int idx, var_kind, is_static1; - idx = find_private_class_field(ctx, fd, name, fd->scope_level); - if (idx >= 0) { - var_kind = fd->vars[idx].var_kind; - is_static1 = fd->vars[idx].is_static_private; - if (var_kind == JS_VAR_PRIVATE_FIELD || - var_kind == JS_VAR_PRIVATE_METHOD || - var_kind == JS_VAR_PRIVATE_GETTER_SETTER || - var_kind == (JS_VAR_PRIVATE_GETTER + is_set) || - (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) && - is_static != is_static1)) { - goto private_field_already_defined; - } - fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER; - } else { - if (add_private_class_field(s, fd, name, - JS_VAR_PRIVATE_GETTER + is_set, 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, - JS_PARSE_EXPORT_NONE, &method_fd)) - goto fail; - if (is_private) { - method_fd->need_home_object = TRUE; /* needed for brand check */ - emit_op(s, OP_set_home_object); - /* XXX: missing function name */ - emit_op(s, OP_scope_put_var_init); - if (is_set) { - JSAtom setter_name; - int ret; - - setter_name = get_private_setter_name(ctx, name); - if (setter_name == JS_ATOM_NULL) - goto fail; - emit_atom(s, setter_name); - ret = add_private_class_field(s, fd, setter_name, - JS_VAR_PRIVATE_SETTER, is_static); - JS_FreeAtom(ctx, setter_name); - if (ret < 0) - goto fail; - } else { - emit_atom(s, name); - } - emit_u16(s, s->cur_func->scope_level); - } else { - if (name == JS_ATOM_NULL) { - emit_op(s, OP_define_method_computed); - } else { - emit_op(s, OP_define_method); - emit_atom(s, name); - } - emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set); - } - } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') { - ClassFieldsDef *cf = &class_fields[is_static]; - JSAtom field_var_name = JS_ATOM_NULL; - - /* class field */ - - /* XXX: spec: not consistent with method name checks */ - if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) { - js_parse_error(s, "invalid field name"); - goto fail; - } - - if (is_private) { - if (find_private_class_field(ctx, fd, name, - fd->scope_level) >= 0) { - goto private_field_already_defined; - } - if (add_private_class_field(s, fd, name, - JS_VAR_PRIVATE_FIELD, is_static) < 0) - goto fail; - emit_op(s, OP_private_symbol); - emit_atom(s, name); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, name); - emit_u16(s, s->cur_func->scope_level); - } - - if (!cf->fields_init_fd) { - if (emit_class_init_start(s, cf)) - goto fail; - } - if (name == JS_ATOM_NULL ) { - /* save the computed field name into a variable */ - field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count); - if (field_var_name == JS_ATOM_NULL) - goto fail; - if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) { - JS_FreeAtom(ctx, field_var_name); - goto fail; - } - emit_op(s, OP_to_propkey); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, field_var_name); - emit_u16(s, s->cur_func->scope_level); - } - s->cur_func = cf->fields_init_fd; - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - - if (name == JS_ATOM_NULL) { - emit_op(s, OP_scope_get_var); - emit_atom(s, field_var_name); - emit_u16(s, s->cur_func->scope_level); - cf->computed_fields_count++; - JS_FreeAtom(ctx, field_var_name); - } else if (is_private) { - emit_op(s, OP_scope_get_var); - emit_atom(s, name); - emit_u16(s, s->cur_func->scope_level); - } - - if (s->token.val == '=') { - if (next_token(s)) - goto fail; - if (js_parse_assign_expr(s)) - goto fail; - } else { - emit_op(s, OP_undefined); - } - if (is_private) { - set_object_name_computed(s); - emit_op(s, OP_define_private_field); - } else if (name == JS_ATOM_NULL) { - set_object_name_computed(s); - emit_op(s, OP_define_array_el); - emit_op(s, OP_drop); - } else { - set_object_name(s, name); - emit_op(s, OP_define_field); - emit_atom(s, name); - } - s->cur_func = s->cur_func->parent; - if (js_parse_expect_semi(s)) - goto fail; - } else { - JSParseFunctionEnum func_type; - JSFunctionKindEnum func_kind; - - func_type = JS_PARSE_FUNC_METHOD; - func_kind = JS_FUNC_NORMAL; - if (prop_type == PROP_TYPE_STAR) { - func_kind = JS_FUNC_GENERATOR; - } else if (prop_type == PROP_TYPE_ASYNC) { - func_kind = JS_FUNC_ASYNC; - } else if (prop_type == PROP_TYPE_ASYNC_STAR) { - func_kind = JS_FUNC_ASYNC_GENERATOR; - } else if (name == JS_ATOM_constructor && !is_static) { - if (ctor_fd) { - js_parse_error(s, "property constructor appears more than once"); - goto fail; - } - if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) - func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; - else - func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; - } - if (is_private) { - 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)) - goto fail; - if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR || - func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { - ctor_fd = method_fd; - } else if (is_private) { - method_fd->need_home_object = TRUE; /* needed for brand check */ - if (find_private_class_field(ctx, fd, name, - fd->scope_level) >= 0) { - private_field_already_defined: - js_parse_error(s, "private class field is already defined"); - goto fail; - } - if (add_private_class_field(s, fd, name, - JS_VAR_PRIVATE_METHOD, is_static) < 0) - goto fail; - emit_op(s, OP_set_home_object); - emit_op(s, OP_set_name); - emit_atom(s, name); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, name); - emit_u16(s, s->cur_func->scope_level); - } else { - if (name == JS_ATOM_NULL) { - emit_op(s, OP_define_method_computed); - } else { - emit_op(s, OP_define_method); - emit_atom(s, name); - } - emit_u8(s, OP_DEFINE_METHOD_METHOD); - } - } - if (is_static) - emit_op(s, OP_swap); - JS_FreeAtom(ctx, name); - name = JS_ATOM_NULL; - } - - if (s->token.val != '}') { - js_parse_error(s, "expecting '%c'", '}'); - goto fail; - } - - if (!ctor_fd) { - if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd)) - goto fail; - } - /* patch the constant pool index for the constructor */ - put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx); - - /* store the class source code in the constructor. */ - if (!(fd->js_mode & JS_MODE_STRIP)) { - js_free(ctx, ctor_fd->source); - ctor_fd->source_len = s->buf_ptr - class_start_ptr; - ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr, - ctor_fd->source_len); - if (!ctor_fd->source) - goto fail; - } - - /* consume the '}' */ - if (next_token(s)) - goto fail; - - { - 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) - goto fail; - if (cf->fields_init_fd) { - emit_class_init_end(s, cf); - } else { - emit_op(s, OP_undefined); - } - emit_op(s, OP_scope_put_var_init); - emit_atom(s, JS_ATOM_class_fields_init); - emit_u16(s, s->cur_func->scope_level); - } - - /* drop the prototype */ - emit_op(s, OP_drop); - - if (class_fields[1].need_brand) { - /* add a private brand to the class */ - emit_op(s, OP_dup); - 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 - definition) */ - emit_op(s, OP_dup); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, class_name); - emit_u16(s, fd->scope_level); - } - - /* 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); - - /* the class statements have a block level scope */ - if (class_var_name != JS_ATOM_NULL) { - if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0) - goto fail; - emit_op(s, OP_scope_put_var_init); - emit_atom(s, class_var_name); - emit_u16(s, fd->scope_level); - } else { - if (class_name == JS_ATOM_NULL) { - /* cannot use OP_set_name because the name of the class - must be defined before the static initializers are - executed */ - emit_op(s, OP_set_class_name); - emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset); - } - } - - if (export_flag != JS_PARSE_EXPORT_NONE) { - if (!add_export_entry(s, fd->module, - class_var_name, - export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default, - JS_EXPORT_TYPE_LOCAL)) - goto fail; - } - - JS_FreeAtom(ctx, class_name); - JS_FreeAtom(ctx, class_var_name); - fd->js_mode = saved_js_mode; - return 0; - fail: - JS_FreeAtom(ctx, name); - JS_FreeAtom(ctx, class_name); - JS_FreeAtom(ctx, class_var_name); - fd->js_mode = saved_js_mode; - return -1; -} - -static __exception int js_parse_array_literal(JSParseState *s) -{ - uint32_t idx; - BOOL need_length; - - if (next_token(s)) - return -1; - /* small regular arrays are created on the stack */ - idx = 0; - while (s->token.val != ']' && idx < 32) { - if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) - break; - if (js_parse_assign_expr(s)) - return -1; - idx++; - /* accept trailing comma */ - if (s->token.val == ',') { - if (next_token(s)) - return -1; - } else - if (s->token.val != ']') - goto done; - } - emit_op(s, OP_array_from); - emit_u16(s, idx); - - /* larger arrays and holes are handled with explicit indices */ - need_length = FALSE; - while (s->token.val != ']' && idx < 0x7fffffff) { - if (s->token.val == TOK_ELLIPSIS) - break; - need_length = TRUE; - if (s->token.val != ',') { - if (js_parse_assign_expr(s)) - return -1; - emit_op(s, OP_define_field); - emit_u32(s, __JS_AtomFromUInt32(idx)); - need_length = FALSE; - } - idx++; - /* accept trailing comma */ - if (s->token.val == ',') { - if (next_token(s)) - return -1; - } - } - if (s->token.val == ']') { - if (need_length) { - /* Set the length: Cannot use OP_define_field because - length is not configurable */ - emit_op(s, OP_dup); - emit_op(s, OP_push_i32); - emit_u32(s, idx); - emit_op(s, OP_put_field); - emit_atom(s, JS_ATOM_length); - } - goto done; - } - - /* huge arrays and spread elements require a dynamic index on the stack */ - emit_op(s, OP_push_i32); - emit_u32(s, idx); - - /* stack has array, index */ - while (s->token.val != ']') { - if (s->token.val == TOK_ELLIPSIS) { - if (next_token(s)) - return -1; - if (js_parse_assign_expr(s)) - return -1; -#if 1 - emit_op(s, OP_append); -#else - int label_next, label_done; - label_next = new_label(s); - label_done = new_label(s); - /* enumerate object */ - emit_op(s, OP_for_of_start); - emit_op(s, OP_rot5l); - emit_op(s, OP_rot5l); - emit_label(s, label_next); - /* on stack: enum_rec array idx */ - emit_op(s, OP_for_of_next); - emit_u8(s, 2); - emit_goto(s, OP_if_true, label_done); - /* append element */ - /* enum_rec array idx val -> enum_rec array new_idx */ - emit_op(s, OP_define_array_el); - emit_op(s, OP_inc); - emit_goto(s, OP_goto, label_next); - emit_label(s, label_done); - /* close enumeration */ - emit_op(s, OP_drop); /* drop undef val */ - emit_op(s, OP_nip1); /* drop enum_rec */ - emit_op(s, OP_nip1); - emit_op(s, OP_nip1); -#endif - } else { - need_length = TRUE; - if (s->token.val != ',') { - if (js_parse_assign_expr(s)) - return -1; - /* a idx val */ - emit_op(s, OP_define_array_el); - need_length = FALSE; - } - emit_op(s, OP_inc); - } - if (s->token.val != ',') - break; - if (next_token(s)) - return -1; - } - if (need_length) { - /* Set the length: cannot use OP_define_field because - length is not configurable */ - emit_op(s, OP_dup1); /* array length - array array length */ - emit_op(s, OP_put_field); - emit_atom(s, JS_ATOM_length); - } else { - emit_op(s, OP_drop); /* array length - array */ - } -done: - return js_parse_expect(s, ']'); -} - -/* XXX: remove */ -static BOOL has_with_scope(JSFunctionDef *s, int scope_level) -{ - /* check if scope chain contains a with statement */ - while (s) { - int scope_idx = s->scopes[scope_level].first; - while (scope_idx >= 0) { - JSVarDef *vd = &s->vars[scope_idx]; - - if (vd->var_name == JS_ATOM__with_) - return TRUE; - scope_idx = vd->scope_next; - } - /* check parent scopes */ - scope_level = s->parent_scope_level; - s = s->parent; - } - return FALSE; -} - -static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, - JSAtom *pname, int *plabel, int *pdepth, BOOL keep, - int tok) -{ - JSFunctionDef *fd; - int opcode, scope, label, depth; - JSAtom name; - - /* we check the last opcode to get the lvalue type */ - fd = s->cur_func; - scope = 0; - name = JS_ATOM_NULL; - label = -1; - depth = 0; - switch(opcode = get_prev_opcode(fd)) { - case OP_scope_get_var: - name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); - scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); - if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) && - (fd->js_mode & JS_MODE_STRICT)) { - return js_parse_error(s, "invalid lvalue in strict mode"); - } - if (name == JS_ATOM_this || name == JS_ATOM_new_target) - goto invalid_lvalue; - depth = 2; /* will generate OP_get_ref_value */ - break; - case OP_get_field: - name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); - depth = 1; - break; - case OP_scope_get_private_field: - name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); - scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); - depth = 1; - break; - case OP_get_array_el: - depth = 2; - break; - case OP_get_super_value: - depth = 3; - break; - default: - invalid_lvalue: - if (tok == TOK_FOR) { - return js_parse_error(s, "invalid for in/of left hand-side"); - } else if (tok == TOK_INC || tok == TOK_DEC) { - return js_parse_error(s, "invalid increment/decrement operand"); - } else if (tok == '[' || tok == '{') { - return js_parse_error(s, "invalid destructuring target"); - } else { - return js_parse_error(s, "invalid assignment left-hand side"); - } - } - /* remove the last opcode */ - fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; - - if (keep) { - /* get the value but keep the object/fields on the stack */ - switch(opcode) { - case OP_scope_get_var: - label = new_label(s); - emit_op(s, OP_scope_make_ref); - emit_atom(s, name); - emit_u32(s, label); - emit_u16(s, scope); - update_label(fd, label, 1); - emit_op(s, OP_get_ref_value); - opcode = OP_get_ref_value; - break; - case OP_get_field: - emit_op(s, OP_get_field2); - emit_atom(s, name); - break; - case OP_scope_get_private_field: - emit_op(s, OP_scope_get_private_field2); - emit_atom(s, name); - emit_u16(s, scope); - break; - case OP_get_array_el: - /* XXX: replace by a single opcode ? */ - emit_op(s, OP_to_propkey2); - emit_op(s, OP_dup2); - emit_op(s, OP_get_array_el); - break; - case OP_get_super_value: - emit_op(s, OP_to_propkey); - emit_op(s, OP_dup3); - emit_op(s, OP_get_super_value); - break; - default: - abort(); - } - } else { - switch(opcode) { - case OP_scope_get_var: - label = new_label(s); - emit_op(s, OP_scope_make_ref); - emit_atom(s, name); - emit_u32(s, label); - emit_u16(s, scope); - update_label(fd, label, 1); - opcode = OP_get_ref_value; - break; - case OP_get_array_el: - emit_op(s, OP_to_propkey2); - break; - case OP_get_super_value: - emit_op(s, OP_to_propkey); - break; - } - } - - *popcode = opcode; - *pscope = scope; - /* name has refcount for OP_get_field and OP_get_ref_value, - and JS_ATOM_NULL for other opcodes */ - *pname = name; - *plabel = label; - if (pdepth) - *pdepth = depth; - return 0; -} - -typedef enum { - PUT_LVALUE_NOKEEP, /* [depth] v -> */ - PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently - just disable optimizations) */ - PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ - PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ - PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ -} PutLValueEnum; - -/* name has a live reference. 'is_let' is only used with opcode = - OP_scope_get_var which is never generated by get_lvalue(). */ -static void put_lvalue(JSParseState *s, int opcode, int scope, - JSAtom name, int label, PutLValueEnum special, - BOOL is_let) -{ - switch(opcode) { - case OP_get_field: - case OP_scope_get_private_field: - /* depth = 1 */ - switch(special) { - case PUT_LVALUE_NOKEEP: - case PUT_LVALUE_NOKEEP_DEPTH: - break; - case PUT_LVALUE_KEEP_TOP: - emit_op(s, OP_insert2); /* obj v -> v obj v */ - break; - case PUT_LVALUE_KEEP_SECOND: - emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ - break; - case PUT_LVALUE_NOKEEP_BOTTOM: - emit_op(s, OP_swap); - break; - default: - abort(); - } - break; - case OP_get_array_el: - case OP_get_ref_value: - /* depth = 2 */ - if (opcode == OP_get_ref_value) { - JS_FreeAtom(s->ctx, name); - emit_label(s, label); - } - switch(special) { - case PUT_LVALUE_NOKEEP: - emit_op(s, OP_nop); /* will trigger optimization */ - break; - case PUT_LVALUE_NOKEEP_DEPTH: - break; - case PUT_LVALUE_KEEP_TOP: - emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ - break; - case PUT_LVALUE_KEEP_SECOND: - emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ - break; - case PUT_LVALUE_NOKEEP_BOTTOM: - emit_op(s, OP_rot3l); - break; - default: - abort(); - } - break; - case OP_get_super_value: - /* depth = 3 */ - switch(special) { - case PUT_LVALUE_NOKEEP: - case PUT_LVALUE_NOKEEP_DEPTH: - break; - case PUT_LVALUE_KEEP_TOP: - emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ - break; - case PUT_LVALUE_KEEP_SECOND: - emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ - break; - case PUT_LVALUE_NOKEEP_BOTTOM: - emit_op(s, OP_rot4l); - break; - default: - abort(); - } - break; - default: - break; - } - - switch(opcode) { - case OP_scope_get_var: /* val -- */ - assert(special == PUT_LVALUE_NOKEEP || - special == PUT_LVALUE_NOKEEP_DEPTH); - emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); - emit_u32(s, name); /* has refcount */ - emit_u16(s, scope); - break; - case OP_get_field: - emit_op(s, OP_put_field); - emit_u32(s, name); /* name has refcount */ - break; - case OP_scope_get_private_field: - emit_op(s, OP_scope_put_private_field); - emit_u32(s, name); /* name has refcount */ - emit_u16(s, scope); - break; - case OP_get_array_el: - emit_op(s, OP_put_array_el); - break; - case OP_get_ref_value: - emit_op(s, OP_put_ref_value); - break; - case OP_get_super_value: - emit_op(s, OP_put_super_value); - break; - default: - abort(); - } -} - -static __exception int js_parse_expr_paren(JSParseState *s) -{ - if (js_parse_expect(s, '(')) - return -1; - if (js_parse_expr(s)) - return -1; - if (js_parse_expect(s, ')')) - return -1; - return 0; -} - -static int js_unsupported_keyword(JSParseState *s, JSAtom atom) -{ - char buf[ATOM_GET_STR_BUF_SIZE]; - return js_parse_error(s, "unsupported keyword: %s", - JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); -} - -static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) -{ - JSFunctionDef *fd = s->cur_func; - JSVarDefEnum var_def_type; - - if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { - return js_parse_error(s, "yield is a reserved identifier"); - } - if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) - && (fd->js_mode & JS_MODE_STRICT)) { - return js_parse_error(s, "invalid variable name in strict mode"); - } - if ((name == JS_ATOM_let || name == JS_ATOM_undefined) - && (tok == TOK_LET || tok == TOK_CONST)) { - return js_parse_error(s, "invalid lexical variable name"); - } - switch(tok) { - case TOK_LET: - var_def_type = JS_VAR_DEF_LET; - break; - case TOK_CONST: - var_def_type = JS_VAR_DEF_CONST; - break; - case TOK_VAR: - var_def_type = JS_VAR_DEF_VAR; - break; - case TOK_CATCH: - var_def_type = JS_VAR_DEF_CATCH; - break; - default: - abort(); - } - if (define_var(s, fd, name, var_def_type) < 0) - return -1; - return 0; -} - -static void js_emit_spread_code(JSParseState *s, int depth) -{ - int label_rest_next, label_rest_done; - - /* XXX: could check if enum object is an actual array and optimize - slice extraction. enumeration record and target array are in a - different order from OP_append case. */ - /* enum_rec xxx -- enum_rec xxx array 0 */ - emit_op(s, OP_array_from); - emit_u16(s, 0); - emit_op(s, OP_push_i32); - emit_u32(s, 0); - emit_label(s, label_rest_next = new_label(s)); - emit_op(s, OP_for_of_next); - emit_u8(s, 2 + depth); - label_rest_done = emit_goto(s, OP_if_true, -1); - /* array idx val -- array idx */ - emit_op(s, OP_define_array_el); - emit_op(s, OP_inc); - emit_goto(s, OP_goto, label_rest_next); - emit_label(s, label_rest_done); - /* enum_rec xxx array idx undef -- enum_rec xxx array */ - emit_op(s, OP_drop); - emit_op(s, OP_drop); -} - -static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) -{ - /* Check for duplicate parameter names */ - JSFunctionDef *fd = s->cur_func; - int i; - for (i = 0; i < fd->arg_count; i++) { - if (fd->args[i].var_name == name) - goto duplicate; - } - for (i = 0; i < fd->var_count; i++) { - if (fd->vars[i].var_name == name) - goto duplicate; - } - return 0; - -duplicate: - return js_parse_error(s, "duplicate parameter names not allowed in this context"); -} - -static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) -{ - JSAtom name; - - if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) - || ((s->cur_func->js_mode & JS_MODE_STRICT) && - (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) { - js_parse_error(s, "invalid destructuring target"); - return JS_ATOM_NULL; - } - name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (is_arg && js_parse_check_duplicate_parameter(s, name)) - goto fail; - if (next_token(s)) - goto fail; - - return name; -fail: - JS_FreeAtom(s->ctx, name); - return JS_ATOM_NULL; -} - -/* Return -1 if error, 0 if no initializer, 1 if an initializer is - 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) -{ - 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); - has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS; - } - - label_parse = new_label(s); - label_assign = new_label(s); - - start_addr = s->cur_func->byte_code.size; - if (hasval) { - /* consume value from the stack */ - emit_op(s, OP_dup); - emit_op(s, OP_undefined); - emit_op(s, OP_strict_eq); - emit_goto(s, OP_if_true, label_parse); - emit_label(s, label_assign); - } else { - emit_goto(s, OP_goto, label_parse); - emit_label(s, label_assign); - /* leave value on the stack */ - emit_op(s, OP_dup); - } - assign_addr = s->cur_func->byte_code.size; - if (s->token.val == '{') { - if (next_token(s)) - return -1; - /* throw an exception if the value cannot be converted to an object */ - emit_op(s, OP_to_object); - if (has_ellipsis) { - /* add excludeList on stack just below src object */ - emit_op(s, OP_object); - emit_op(s, OP_swap); - } - while (s->token.val != '}') { - int prop_type; - if (s->token.val == TOK_ELLIPSIS) { - if (!has_ellipsis) { - JS_ThrowInternalError(s->ctx, "unexpected ellipsis token"); - return -1; - } - if (next_token(s)) - return -1; - if (tok) { - var_name = js_parse_destructuring_var(s, tok, is_arg); - if (var_name == JS_ATOM_NULL) - return -1; - opcode = OP_scope_get_var; - scope = s->cur_func->scope_level; - label_lvalue = -1; - depth_lvalue = 0; - } else { - if (js_parse_left_hand_side_expr(s)) - return -1; - - if (get_lvalue(s, &opcode, &scope, &var_name, - &label_lvalue, &depth_lvalue, FALSE, '{')) - return -1; - } - if (s->token.val != '}') { - js_parse_error(s, "assignment rest property must be last"); - goto var_error; - } - emit_op(s, OP_object); /* target */ - emit_op(s, OP_copy_data_properties); - emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5)); - goto set_val; - } - prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE); - if (prop_type < 0) - return -1; - var_name = JS_ATOM_NULL; - opcode = OP_scope_get_var; - scope = s->cur_func->scope_level; - label_lvalue = -1; - depth_lvalue = 0; - if (prop_type == PROP_TYPE_IDENT) { - if (next_token(s)) - goto prop_error; - if ((s->token.val == '[' || s->token.val == '{') - && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || - tok1 == '=' || tok1 == '}')) { - if (prop_name == JS_ATOM_NULL) { - /* computed property name on stack */ - if (has_ellipsis) { - /* define the property in excludeList */ - emit_op(s, OP_to_propkey); /* avoid calling ToString twice */ - emit_op(s, OP_perm3); /* TOS: src excludeList prop */ - emit_op(s, OP_null); /* TOS: src excludeList prop null */ - emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */ - emit_op(s, OP_perm3); /* TOS: excludeList src prop */ - } - /* get the computed property from the source object */ - emit_op(s, OP_get_array_el2); - } else { - /* named property */ - if (has_ellipsis) { - /* define the property in excludeList */ - emit_op(s, OP_swap); /* TOS: src excludeList */ - emit_op(s, OP_null); /* TOS: src excludeList null */ - emit_op(s, OP_define_field); /* TOS: src excludeList */ - emit_atom(s, prop_name); - emit_op(s, OP_swap); /* TOS: excludeList src */ - } - /* get the named property from the source object */ - emit_op(s, OP_get_field2); - emit_u32(s, prop_name); - } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) - return -1; - if (s->token.val == '}') - break; - /* accept a trailing comma before the '}' */ - if (js_parse_expect(s, ',')) - return -1; - continue; - } - if (prop_name == JS_ATOM_NULL) { - emit_op(s, OP_to_propkey2); - if (has_ellipsis) { - /* define the property in excludeList */ - emit_op(s, OP_perm3); - emit_op(s, OP_null); - emit_op(s, OP_define_array_el); - emit_op(s, OP_perm3); - } - /* source prop -- source source prop */ - emit_op(s, OP_dup1); - } else { - if (has_ellipsis) { - /* define the property in excludeList */ - emit_op(s, OP_swap); - emit_op(s, OP_null); - emit_op(s, OP_define_field); - emit_atom(s, prop_name); - emit_op(s, OP_swap); - } - /* source -- source source */ - emit_op(s, OP_dup); - } - if (tok) { - var_name = js_parse_destructuring_var(s, tok, is_arg); - if (var_name == JS_ATOM_NULL) - goto prop_error; - } else { - if (js_parse_left_hand_side_expr(s)) - goto prop_error; - lvalue: - if (get_lvalue(s, &opcode, &scope, &var_name, - &label_lvalue, &depth_lvalue, FALSE, '{')) - goto prop_error; - /* swap ref and lvalue object if any */ - if (prop_name == JS_ATOM_NULL) { - switch(depth_lvalue) { - case 1: - /* source prop x -> x source prop */ - emit_op(s, OP_rot3r); - break; - case 2: - /* source prop x y -> x y source prop */ - emit_op(s, OP_swap2); /* t p2 s p1 */ - break; - case 3: - /* source prop x y z -> x y z source prop */ - emit_op(s, OP_rot5l); - emit_op(s, OP_rot5l); - break; - } - } else { - switch(depth_lvalue) { - case 1: - /* source x -> x source */ - emit_op(s, OP_swap); - break; - case 2: - /* source x y -> x y source */ - emit_op(s, OP_rot3l); - break; - case 3: - /* source x y z -> x y z source */ - emit_op(s, OP_rot4l); - break; - } - } - } - if (prop_name == JS_ATOM_NULL) { - /* computed property name on stack */ - /* XXX: should have OP_get_array_el2x with depth */ - /* source prop -- val */ - emit_op(s, OP_get_array_el); - } else { - /* named property */ - /* XXX: should have OP_get_field2x with depth */ - /* source -- val */ - emit_op(s, OP_get_field); - emit_u32(s, prop_name); - } - } else { - /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ - if (is_arg && js_parse_check_duplicate_parameter(s, prop_name)) - goto prop_error; - if ((s->cur_func->js_mode & JS_MODE_STRICT) && - (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) { - js_parse_error(s, "invalid destructuring target"); - goto prop_error; - } - if (has_ellipsis) { - /* define the property in excludeList */ - emit_op(s, OP_swap); - emit_op(s, OP_null); - emit_op(s, OP_define_field); - emit_atom(s, prop_name); - emit_op(s, OP_swap); - } - if (!tok || tok == TOK_VAR) { - /* generate reference */ - /* source -- source source */ - emit_op(s, OP_dup); - emit_op(s, OP_scope_get_var); - emit_atom(s, prop_name); - emit_u16(s, s->cur_func->scope_level); - goto lvalue; - } - var_name = JS_DupAtom(s->ctx, prop_name); - /* source -- source val */ - emit_op(s, OP_get_field2); - emit_u32(s, prop_name); - } - set_val: - if (tok) { - if (js_define_var(s, var_name, tok)) - goto var_error; - scope = s->cur_func->scope_level; - } - if (s->token.val == '=') { /* handle optional default value */ - int label_hasval; - emit_op(s, OP_dup); - emit_op(s, OP_undefined); - emit_op(s, OP_strict_eq); - label_hasval = emit_goto(s, OP_if_false, -1); - if (next_token(s)) - goto var_error; - emit_op(s, OP_drop); - if (js_parse_assign_expr(s)) - goto var_error; - if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) - set_object_name(s, var_name); - emit_label(s, label_hasval); - } - /* store value into lvalue object */ - put_lvalue(s, opcode, scope, var_name, label_lvalue, - PUT_LVALUE_NOKEEP_DEPTH, - (tok == TOK_CONST || tok == TOK_LET)); - if (s->token.val == '}') - break; - /* accept a trailing comma before the '}' */ - if (js_parse_expect(s, ',')) - return -1; - } - /* drop the source object */ - emit_op(s, OP_drop); - if (has_ellipsis) { - emit_op(s, OP_drop); /* pop excludeList */ - } - if (next_token(s)) - return -1; - } else if (s->token.val == '[') { - BOOL has_spread; - int enum_depth; - BlockEnv block_env; - - if (next_token(s)) - return -1; - /* the block environment is only needed in generators in case - 'yield' triggers a 'return' */ - push_break_entry(s->cur_func, &block_env, - JS_ATOM_NULL, -1, -1, 2); - block_env.has_iterator = TRUE; - emit_op(s, OP_for_of_start); - has_spread = FALSE; - while (s->token.val != ']') { - /* get the next value */ - if (s->token.val == TOK_ELLIPSIS) { - if (next_token(s)) - return -1; - if (s->token.val == ',' || s->token.val == ']') - return js_parse_error(s, "missing binding pattern..."); - has_spread = TRUE; - } - if (s->token.val == ',') { - /* do nothing, skip the value, has_spread is false */ - emit_op(s, OP_for_of_next); - emit_u8(s, 0); - emit_op(s, OP_drop); - emit_op(s, OP_drop); - } else if ((s->token.val == '[' || s->token.val == '{') - && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || - tok1 == '=' || tok1 == ']')) { - if (has_spread) { - if (tok1 == '=') - return js_parse_error(s, "rest element cannot have a default value"); - js_emit_spread_code(s, 0); - } else { - emit_op(s, OP_for_of_next); - emit_u8(s, 0); - emit_op(s, OP_drop); - } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 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; - } else { - if (js_parse_left_hand_side_expr(s)) - return -1; - if (get_lvalue(s, &opcode, &scope, &var_name, - &label_lvalue, &enum_depth, FALSE, '[')) { - return -1; - } - } - if (has_spread) { - js_emit_spread_code(s, enum_depth); - } else { - emit_op(s, OP_for_of_next); - emit_u8(s, enum_depth); - emit_op(s, OP_drop); - } - if (s->token.val == '=' && !has_spread) { - /* handle optional default value */ - int label_hasval; - emit_op(s, OP_dup); - emit_op(s, OP_undefined); - emit_op(s, OP_strict_eq); - label_hasval = emit_goto(s, OP_if_false, -1); - if (next_token(s)) - goto var_error; - emit_op(s, OP_drop); - if (js_parse_assign_expr(s)) - goto var_error; - if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) - set_object_name(s, var_name); - emit_label(s, label_hasval); - } - /* store value into lvalue object */ - put_lvalue(s, opcode, scope, var_name, - label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, - (tok == TOK_CONST || tok == TOK_LET)); - } - if (s->token.val == ']') - break; - if (has_spread) - return js_parse_error(s, "rest element must be the last one"); - /* accept a trailing comma before the ']' */ - if (js_parse_expect(s, ',')) - return -1; - } - /* close iterator object: - if completed, enum_obj has been replaced by undefined */ - emit_op(s, OP_iterator_close); - pop_break_entry(s->cur_func); - if (next_token(s)) - return -1; - } else { - return js_parse_error(s, "invalid assignment syntax"); - } - if (s->token.val == '=' && allow_initializer) { - label_done = emit_goto(s, OP_goto, -1); - if (next_token(s)) - return -1; - emit_label(s, label_parse); - if (hasval) - emit_op(s, OP_drop); - if (js_parse_assign_expr(s)) - return -1; - emit_goto(s, OP_goto, label_assign); - emit_label(s, label_done); - has_initializer = TRUE; - } else { - /* normally hasval is true except if - js_parse_skip_parens_token() was wrong in the parsing */ - // assert(hasval); - if (!hasval) { - js_parse_error(s, "too complicated destructuring expression"); - return -1; - } - /* remove test and decrement label ref count */ - memset(s->cur_func->byte_code.buf + start_addr, OP_nop, - assign_addr - start_addr); - s->cur_func->label_slots[label_parse].ref_count--; - has_initializer = FALSE; - } - return has_initializer; - - prop_error: - JS_FreeAtom(s->ctx, prop_name); - var_error: - JS_FreeAtom(s->ctx, var_name); - return -1; -} - -typedef enum FuncCallType { - FUNC_CALL_NORMAL, - FUNC_CALL_NEW, - FUNC_CALL_SUPER_CTOR, - FUNC_CALL_TEMPLATE, -} FuncCallType; - -static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, - int drop_count) -{ - int label_next, i; - if (*poptional_chaining_label < 0) - *poptional_chaining_label = new_label(s); - /* XXX: could be more efficient with a specific opcode */ - emit_op(s, OP_dup); - emit_op(s, OP_is_undefined_or_null); - label_next = emit_goto(s, OP_if_false, -1); - for(i = 0; i < drop_count; i++) - emit_op(s, OP_drop); - emit_op(s, OP_undefined); - emit_goto(s, OP_goto, *poptional_chaining_label); - emit_label(s, label_next); -} - -/* 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; - - call_type = FUNC_CALL_NORMAL; - switch(s->token.val) { - case TOK_NUMBER: - { - JSValue val; - val = s->token.u.num.val; - - if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { - emit_op(s, OP_push_i32); - emit_u32(s, JS_VALUE_GET_INT(val)); - } else -#ifdef CONFIG_BIGNUM - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - slimb_t e; - int ret; - - /* need a runtime conversion */ - /* XXX: could add a cache and/or do it once at - the start of the function */ - if (emit_push_const(s, val, 0) < 0) - return -1; - e = s->token.u.num.exponent; - if (e == (int32_t)e) { - emit_op(s, OP_push_i32); - emit_u32(s, e); - } else { - val = JS_NewBigInt64_1(s->ctx, e); - if (JS_IsException(val)) - return -1; - ret = emit_push_const(s, val, 0); - JS_FreeValue(s->ctx, val); - if (ret < 0) - return -1; - } - emit_op(s, OP_mul_pow10); - } else -#endif - { - if (emit_push_const(s, val, 0) < 0) - return -1; - } - } - if (next_token(s)) - return -1; - break; - case TOK_TEMPLATE: - if (js_parse_template(s, 0, NULL)) - return -1; - break; - case TOK_STRING: - if (emit_push_const(s, s->token.u.str.str, 1)) - return -1; - if (next_token(s)) - return -1; - break; - - case TOK_DIV_ASSIGN: - s->buf_ptr -= 2; - goto parse_regexp; - case '/': - s->buf_ptr--; - parse_regexp: - { - JSValue str; - int ret, backtrace_flags; - if (!s->ctx->compile_regexp) - return js_parse_error(s, "RegExp are not supported"); - /* the previous token is '/' or '/=', so no need to free */ - if (js_parse_regexp(s)) - return -1; - ret = emit_push_const(s, s->token.u.regexp.body, 0); - str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body, - s->token.u.regexp.flags); - if (JS_IsException(str)) { - /* add the line number info */ - backtrace_flags = 0; - if (s->cur_func && s->cur_func->backtrace_barrier) - backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; - build_backtrace(s->ctx, s->ctx->rt->current_exception, - s->filename, s->token.line_num, - backtrace_flags); - return -1; - } - ret = emit_push_const(s, str, 0); - JS_FreeValue(s->ctx, str); - if (ret) - return -1; - /* we use a specific opcode to be sure the correct - function is called (otherwise the bytecode would have - to be verified by the RegExp constructor) */ - emit_op(s, OP_regexp); - if (next_token(s)) - return -1; - } - break; - case '(': - if (js_parse_expr_paren(s)) - return -1; - break; - case TOK_FUNCTION: - if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - return -1; - break; - case TOK_CLASS: - if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE)) - return -1; - break; - case TOK_NULL: - if (next_token(s)) - return -1; - emit_op(s, OP_null); - break; - case TOK_THIS: - if (next_token(s)) - return -1; - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - break; - case TOK_FALSE: - if (next_token(s)) - return -1; - emit_op(s, OP_push_false); - break; - case TOK_TRUE: - if (next_token(s)) - return -1; - emit_op(s, OP_push_true); - break; - case TOK_IDENT: - { - JSAtom name; - if (s->token.u.ident.is_reserved) { - return js_parse_error_reserved_identifier(s); - } - if (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) != '\n') { - const uint8_t *source_ptr; - int source_line_num; - - source_ptr = s->token.ptr; - source_line_num = s->token.line_num; - if (next_token(s)) - return -1; - if (s->token.val == TOK_FUNCTION) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, - JS_FUNC_ASYNC, JS_ATOM_NULL, - source_ptr, source_line_num)) - return -1; - } else { - name = JS_DupAtom(s->ctx, JS_ATOM_async); - goto do_get_var; - } - } else { - if (s->token.u.ident.atom == JS_ATOM_arguments && - !s->cur_func->arguments_allowed) { - js_parse_error(s, "'arguments' identifier is not allowed in class field initializer"); - return -1; - } - name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) { /* update line number before emitting code */ - JS_FreeAtom(s->ctx, name); - return -1; - } - do_get_var: - emit_op(s, OP_scope_get_var); - emit_u32(s, name); - emit_u16(s, s->cur_func->scope_level); - } - } - break; - case '{': - case '[': - { - int skip_bits; - if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { - if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 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; - } - } - } - break; - case TOK_NEW: - if (next_token(s)) - return -1; - if (s->token.val == '.') { - if (next_token(s)) - return -1; - if (!token_is_pseudo_keyword(s, JS_ATOM_target)) - return js_parse_error(s, "expecting target"); - if (!s->cur_func->new_target_allowed) - return js_parse_error(s, "new.target only allowed within functions"); - if (next_token(s)) - return -1; - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_new_target); - emit_u16(s, 0); - } else { - if (js_parse_postfix_expr(s, 0)) - return -1; - accept_lparen = TRUE; - if (s->token.val != '(') { - /* new operator on an object */ - emit_op(s, OP_dup); - emit_op(s, OP_call_constructor); - emit_u16(s, 0); - } else { - call_type = FUNC_CALL_NEW; - } - } - break; - case TOK_SUPER: - if (next_token(s)) - return -1; - if (s->token.val == '(') { - if (!s->cur_func->super_call_allowed) - return js_parse_error(s, "super() is only valid in a derived class constructor"); - call_type = FUNC_CALL_SUPER_CTOR; - } else if (s->token.val == '.' || s->token.val == '[') { - if (!s->cur_func->super_allowed) - return js_parse_error(s, "'super' is only valid in a method"); - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_home_object); - emit_u16(s, 0); - emit_op(s, OP_get_super); - } else { - return js_parse_error(s, "invalid use of 'super'"); - } - break; - case TOK_IMPORT: - if (next_token(s)) - return -1; - if (s->token.val == '.') { - if (next_token(s)) - return -1; - if (!token_is_pseudo_keyword(s, JS_ATOM_meta)) - return js_parse_error(s, "meta expected"); - if (!s->is_module) - return js_parse_error(s, "import.meta only valid in module code"); - if (next_token(s)) - return -1; - emit_op(s, OP_special_object); - emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META); - } else { - if (js_parse_expect(s, '(')) - return -1; - if (!accept_lparen) - return js_parse_error(s, "invalid use of 'import()'"); - if (js_parse_assign_expr(s)) - return -1; - if (js_parse_expect(s, ')')) - return -1; - emit_op(s, OP_import); - } - break; - default: - return js_parse_error(s, "unexpected token in expression: '%.*s'", - (int)(s->buf_ptr - s->token.ptr), s->token.ptr); - } - - optional_chaining_label = -1; - for(;;) { - JSFunctionDef *fd = s->cur_func; - BOOL has_optional_chain = FALSE; - - if (s->token.val == TOK_QUESTION_MARK_DOT) { - /* optional chaining */ - if (next_token(s)) - return -1; - has_optional_chain = TRUE; - if (s->token.val == '(' && accept_lparen) { - goto parse_func_call; - } else if (s->token.val == '[') { - goto parse_array_access; - } else { - goto parse_property; - } - } else if (s->token.val == TOK_TEMPLATE && - call_type == FUNC_CALL_NORMAL) { - if (optional_chaining_label >= 0) { - return js_parse_error(s, "template literal cannot appear in an optional chain"); - } - call_type = FUNC_CALL_TEMPLATE; - goto parse_func_call2; - } else if (s->token.val == '(' && accept_lparen) { - int opcode, arg_count, drop_count; - - /* function call */ - parse_func_call: - if (next_token(s)) - return -1; - - if (call_type == FUNC_CALL_NORMAL) { - parse_func_call2: - switch(opcode = get_prev_opcode(fd)) { - case OP_get_field: - /* keep the object on the stack */ - fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; - drop_count = 2; - break; - case OP_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; - drop_count = 2; - break; - case OP_get_array_el: - /* keep the object on the stack */ - fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; - drop_count = 2; - break; - case OP_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; - int scope; - name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); - scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); - if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { - /* direct 'eval' */ - opcode = OP_eval; - } else { - /* verify if function name resolves to a simple - get_loc/get_arg: a function call inside a `with` - statement can resolve to a method call of the - `with` context object - */ - /* XXX: always generate the OP_scope_get_ref - and remove it in variable resolution - pass ? */ - if (has_with_scope(fd, scope)) { - opcode = OP_scope_get_ref; - fd->byte_code.buf[fd->last_opcode_pos] = opcode; - } - } - drop_count = 1; - } - break; - case OP_get_super_value: - fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el; - /* on stack: this func_obj */ - opcode = OP_get_array_el; - drop_count = 2; - break; - default: - opcode = OP_invalid; - drop_count = 1; - break; - } - if (has_optional_chain) { - optional_chain_test(s, &optional_chaining_label, - drop_count); - } - } else { - opcode = OP_invalid; - } - - if (call_type == FUNC_CALL_TEMPLATE) { - if (js_parse_template(s, 1, &arg_count)) - return -1; - goto emit_func_call; - } else if (call_type == FUNC_CALL_SUPER_CTOR) { - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this_active_func); - emit_u16(s, 0); - - emit_op(s, OP_get_super); - - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_new_target); - emit_u16(s, 0); - } else if (call_type == FUNC_CALL_NEW) { - emit_op(s, OP_dup); /* new.target = function */ - } - - /* parse arguments */ - arg_count = 0; - while (s->token.val != ')') { - if (arg_count >= 65535) { - return js_parse_error(s, "Too many call arguments"); - } - if (s->token.val == TOK_ELLIPSIS) - break; - if (js_parse_assign_expr(s)) - return -1; - arg_count++; - if (s->token.val == ')') - break; - /* accept a trailing comma before the ')' */ - if (js_parse_expect(s, ',')) - return -1; - } - if (s->token.val == TOK_ELLIPSIS) { - emit_op(s, OP_array_from); - emit_u16(s, arg_count); - emit_op(s, OP_push_i32); - emit_u32(s, arg_count); - - /* on stack: array idx */ - while (s->token.val != ')') { - if (s->token.val == TOK_ELLIPSIS) { - if (next_token(s)) - return -1; - if (js_parse_assign_expr(s)) - return -1; -#if 1 - /* XXX: could pass is_last indicator? */ - emit_op(s, OP_append); -#else - int label_next, label_done; - label_next = new_label(s); - label_done = new_label(s); - /* push enumerate object below array/idx pair */ - emit_op(s, OP_for_of_start); - emit_op(s, OP_rot5l); - emit_op(s, OP_rot5l); - emit_label(s, label_next); - /* on stack: enum_rec array idx */ - emit_op(s, OP_for_of_next); - emit_u8(s, 2); - emit_goto(s, OP_if_true, label_done); - /* append element */ - /* enum_rec array idx val -> enum_rec array new_idx */ - emit_op(s, OP_define_array_el); - emit_op(s, OP_inc); - emit_goto(s, OP_goto, label_next); - emit_label(s, label_done); - /* close enumeration, drop enum_rec and idx */ - emit_op(s, OP_drop); /* drop undef */ - emit_op(s, OP_nip1); /* drop enum_rec */ - emit_op(s, OP_nip1); - emit_op(s, OP_nip1); -#endif - } else { - if (js_parse_assign_expr(s)) - return -1; - /* array idx val */ - emit_op(s, OP_define_array_el); - emit_op(s, OP_inc); - } - if (s->token.val == ')') - break; - /* accept a trailing comma before the ')' */ - if (js_parse_expect(s, ',')) - return -1; - } - if (next_token(s)) - return -1; - /* drop the index */ - emit_op(s, OP_drop); - - /* apply function call */ - switch(opcode) { - case OP_get_field: - case OP_scope_get_private_field: - case OP_get_array_el: - case OP_scope_get_ref: - /* obj func array -> func obj array */ - emit_op(s, OP_perm3); - emit_op(s, OP_apply); - emit_u16(s, call_type == FUNC_CALL_NEW); - break; - case OP_eval: - emit_op(s, OP_apply_eval); - emit_u16(s, fd->scope_level); - fd->has_eval_call = TRUE; - break; - default: - if (call_type == FUNC_CALL_SUPER_CTOR) { - emit_op(s, OP_apply); - emit_u16(s, 1); - /* set the 'this' value */ - emit_op(s, OP_dup); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - - emit_class_field_init(s); - } else if (call_type == FUNC_CALL_NEW) { - /* obj func array -> func obj array */ - emit_op(s, OP_perm3); - emit_op(s, OP_apply); - emit_u16(s, 1); - } else { - /* func array -> func undef array */ - emit_op(s, OP_undefined); - emit_op(s, OP_swap); - emit_op(s, OP_apply); - emit_u16(s, 0); - } - break; - } - } else { - if (next_token(s)) - return -1; - emit_func_call: - switch(opcode) { - case OP_get_field: - case OP_scope_get_private_field: - case OP_get_array_el: - case OP_scope_get_ref: - emit_op(s, OP_call_method); - emit_u16(s, arg_count); - break; - case OP_eval: - emit_op(s, OP_eval); - emit_u16(s, arg_count); - emit_u16(s, fd->scope_level); - fd->has_eval_call = TRUE; - break; - default: - if (call_type == FUNC_CALL_SUPER_CTOR) { - emit_op(s, OP_call_constructor); - emit_u16(s, arg_count); - - /* set the 'this' value */ - emit_op(s, OP_dup); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); - - emit_class_field_init(s); - } else if (call_type == FUNC_CALL_NEW) { - emit_op(s, OP_call_constructor); - emit_u16(s, arg_count); - } else { - emit_op(s, OP_call); - emit_u16(s, arg_count); - } - break; - } - } - call_type = FUNC_CALL_NORMAL; - } else if (s->token.val == '.') { - if (next_token(s)) - return -1; - parse_property: - if (s->token.val == TOK_PRIVATE_NAME) { - /* private class field */ - if (get_prev_opcode(fd) == OP_get_super) { - return js_parse_error(s, "private class field forbidden after super"); - } - if (has_optional_chain) { - optional_chain_test(s, &optional_chaining_label, 1); - } - emit_op(s, OP_scope_get_private_field); - emit_atom(s, s->token.u.ident.atom); - emit_u16(s, s->cur_func->scope_level); - } else { - if (!token_is_ident(s->token.val)) { - return js_parse_error(s, "expecting field name"); - } - if (get_prev_opcode(fd) == OP_get_super) { - JSValue val; - int ret; - val = JS_AtomToValue(s->ctx, s->token.u.ident.atom); - ret = emit_push_const(s, val, 1); - JS_FreeValue(s->ctx, val); - if (ret) - return -1; - emit_op(s, OP_get_super_value); - } else { - if (has_optional_chain) { - optional_chain_test(s, &optional_chaining_label, 1); - } - emit_op(s, OP_get_field); - emit_atom(s, s->token.u.ident.atom); - } - } - if (next_token(s)) - return -1; - } else if (s->token.val == '[') { - int prev_op; - - parse_array_access: - prev_op = get_prev_opcode(fd); - if (has_optional_chain) { - optional_chain_test(s, &optional_chaining_label, 1); - } - if (next_token(s)) - return -1; - if (js_parse_expr(s)) - return -1; - if (js_parse_expect(s, ']')) - return -1; - if (prev_op == OP_get_super) { - emit_op(s, OP_get_super_value); - } else { - emit_op(s, OP_get_array_el); - } - } else { - break; - } - } - if (optional_chaining_label >= 0) { - 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; -} - -static __exception int js_parse_delete(JSParseState *s) -{ - JSFunctionDef *fd = s->cur_func; - JSAtom name; - int opcode; - - if (next_token(s)) - return -1; - if (js_parse_unary(s, PF_POW_FORBIDDEN)) - return -1; - switch(opcode = get_prev_opcode(fd)) { - case OP_get_field: - case OP_get_field_opt_chain: - { - JSValue val; - 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; - 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; - } - break; - case OP_get_array_el: - fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; - 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); - if (name == JS_ATOM_this || name == JS_ATOM_new_target) - goto ret_true; - if (fd->js_mode & JS_MODE_STRICT) { - return js_parse_error(s, "cannot delete a direct reference in strict mode"); - } else { - fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var; - } - break; - case OP_scope_get_private_field: - return js_parse_error(s, "cannot delete a private class field"); - case OP_get_super_value: - fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; - emit_op(s, OP_throw_error); - emit_atom(s, JS_ATOM_NULL); - emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); - break; - default: - ret_true: - emit_op(s, OP_drop); - emit_op(s, OP_push_true); - break; - } - return 0; -} - -/* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */ -static __exception int js_parse_unary(JSParseState *s, int parse_flags) -{ - int op; - - switch(s->token.val) { - case '+': - case '-': - case '!': - case '~': - case TOK_VOID: - op = s->token.val; - if (next_token(s)) - return -1; - if (js_parse_unary(s, PF_POW_FORBIDDEN)) - return -1; - switch(op) { - case '-': - emit_op(s, OP_neg); - break; - case '+': - emit_op(s, OP_plus); - break; - case '!': - emit_op(s, OP_lnot); - break; - case '~': - emit_op(s, OP_not); - break; - case TOK_VOID: - emit_op(s, OP_drop); - emit_op(s, OP_undefined); - break; - default: - abort(); - } - parse_flags = 0; - break; - case TOK_DEC: - case TOK_INC: - { - int opcode, op, scope, label; - JSAtom name; - op = s->token.val; - if (next_token(s)) - return -1; - if (js_parse_unary(s, 0)) - return -1; - if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) - return -1; - emit_op(s, OP_dec + op - TOK_DEC); - put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, - FALSE); - } - break; - case TOK_TYPEOF: - { - JSFunctionDef *fd; - if (next_token(s)) - return -1; - if (js_parse_unary(s, PF_POW_FORBIDDEN)) - return -1; - /* reference access should not return an exception, so we - patch the get_var */ - fd = s->cur_func; - if (get_prev_opcode(fd) == OP_scope_get_var) { - fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; - } - emit_op(s, OP_typeof); - parse_flags = 0; - } - break; - case TOK_DELETE: - if (js_parse_delete(s)) - return -1; - parse_flags = 0; - break; - case TOK_AWAIT: - if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) - return js_parse_error(s, "unexpected 'await' keyword"); - if (!s->cur_func->in_function_body) - return js_parse_error(s, "await in default expression"); - if (next_token(s)) - return -1; - if (js_parse_unary(s, 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, 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; - if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) - return -1; - emit_op(s, OP_post_dec + op - TOK_DEC); - put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, - FALSE); - if (next_token(s)) - 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 - regarding the precedence of prefix operators and the - postifx exponential, ES7 specifies that -2**2 is a - syntax error. */ - 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); - } -#endif - } - return 0; -} - -/* allowed parse_flags: PF_IN_ACCEPTED */ -static __exception int js_parse_expr_binary(JSParseState *s, int level, - int parse_flags) -{ - int op, opcode; - - if (level == 0) { - 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; - } - for(;;) { - op = s->token.val; - switch(level) { - case 1: - switch(op) { - case '*': - opcode = OP_mul; - break; - case '/': - opcode = OP_div; - break; - case '%': -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) - opcode = OP_math_mod; - else -#endif - opcode = OP_mod; - break; - default: - return 0; - } - break; - case 2: - switch(op) { - case '+': - opcode = OP_add; - break; - case '-': - opcode = OP_sub; - break; - default: - return 0; - } - break; - case 3: - switch(op) { - case TOK_SHL: - opcode = OP_shl; - break; - case TOK_SAR: - opcode = OP_sar; - break; - case TOK_SHR: - opcode = OP_shr; - break; - default: - return 0; - } - break; - case 4: - switch(op) { - case '<': - opcode = OP_lt; - break; - case '>': - opcode = OP_gt; - break; - case TOK_LTE: - opcode = OP_lte; - break; - case TOK_GTE: - opcode = OP_gte; - break; - case TOK_INSTANCEOF: - opcode = OP_instanceof; - break; - case TOK_IN: - if (parse_flags & PF_IN_ACCEPTED) { - opcode = OP_in; - } else { - return 0; - } - break; - default: - return 0; - } - break; - case 5: - switch(op) { - case TOK_EQ: - opcode = OP_eq; - break; - case TOK_NEQ: - opcode = OP_neq; - break; - case TOK_STRICT_EQ: - opcode = OP_strict_eq; - break; - case TOK_STRICT_NEQ: - opcode = OP_strict_neq; - break; - default: - return 0; - } - break; - case 6: - switch(op) { - case '&': - opcode = OP_and; - break; - default: - return 0; - } - break; - case 7: - switch(op) { - case '^': - opcode = OP_xor; - break; - default: - return 0; - } - break; - case 8: - switch(op) { - case '|': - opcode = OP_or; - break; - default: - return 0; - } - break; - default: - abort(); - } - if (next_token(s)) - return -1; - if (js_parse_expr_binary(s, level - 1, parse_flags)) - return -1; - emit_op(s, opcode); - } - return 0; -} - -/* allowed parse_flags: PF_IN_ACCEPTED */ -static __exception int js_parse_logical_and_or(JSParseState *s, int op, - int parse_flags) -{ - int label1; - - if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, parse_flags)) - return -1; - } else { - if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) - return -1; - } - if (s->token.val == op) { - label1 = new_label(s); - - for(;;) { - if (next_token(s)) - return -1; - emit_op(s, OP_dup); - emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1); - emit_op(s, OP_drop); - - if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, parse_flags)) - return -1; - } else { - if (js_parse_logical_and_or(s, TOK_LAND, - parse_flags)) - return -1; - } - if (s->token.val != op) { - if (s->token.val == TOK_DOUBLE_QUESTION_MARK) - return js_parse_error(s, "cannot mix ?? with && or ||"); - break; - } - } - - emit_label(s, label1); - } - return 0; -} - -static __exception int js_parse_coalesce_expr(JSParseState *s, 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) { - label1 = new_label(s); - for(;;) { - if (next_token(s)) - return -1; - - emit_op(s, OP_dup); - emit_op(s, OP_is_undefined_or_null); - emit_goto(s, OP_if_false, label1); - emit_op(s, OP_drop); - - if (js_parse_expr_binary(s, 8, parse_flags)) - return -1; - if (s->token.val != TOK_DOUBLE_QUESTION_MARK) - break; - } - emit_label(s, label1); - } - return 0; -} - -/* allowed parse_flags: PF_IN_ACCEPTED */ -static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) -{ - int label1, label2; - - if (js_parse_coalesce_expr(s, parse_flags)) - return -1; - if (s->token.val == '?') { - if (next_token(s)) - return -1; - label1 = emit_goto(s, OP_if_false, -1); - - if (js_parse_assign_expr(s)) - return -1; - if (js_parse_expect(s, ':')) - return -1; - - label2 = emit_goto(s, OP_goto, -1); - - emit_label(s, label1); - - if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) - return -1; - - emit_label(s, label2); - } - 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; - 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) - return js_parse_error(s, "yield in default expression"); - if (next_token(s)) - return -1; - /* XXX: is there a better method to detect 'yield' without - parameters ? */ - if (s->token.val != ';' && s->token.val != ')' && - s->token.val != ']' && s->token.val != '}' && - s->token.val != ',' && s->token.val != ':' && !s->got_lf) { - if (s->token.val == '*') { - is_star = TRUE; - if (next_token(s)) - return -1; - } - if (js_parse_assign_expr2(s, parse_flags)) - return -1; - } else { - emit_op(s, OP_undefined); - } - is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); - - if (is_star) { - int label_loop, label_return, label_next; - int label_return1, label_yield, label_throw, label_throw1; - int label_throw2; - - label_loop = new_label(s); - label_yield = new_label(s); - - emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); - - /* remove the catch offset (XXX: could avoid pushing back - undefined) */ - emit_op(s, OP_drop); - emit_op(s, OP_undefined); - - emit_op(s, OP_undefined); /* initial value */ - - emit_label(s, label_loop); - emit_op(s, OP_iterator_next); - if (is_async) - emit_op(s, OP_await); - emit_op(s, OP_iterator_check_object); - emit_op(s, OP_get_field2); - emit_atom(s, JS_ATOM_done); - label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ - emit_label(s, label_yield); - if (is_async) { - /* 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_async_yield_star); - } else { - /* OP_yield_star takes (value, done) as parameter */ - emit_op(s, OP_yield_star); - } - emit_op(s, OP_dup); - label_return = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_drop); - emit_goto(s, OP_goto, label_loop); - - emit_label(s, label_return); - emit_op(s, OP_push_i32); - emit_u32(s, 2); - emit_op(s, OP_strict_eq); - label_throw = emit_goto(s, OP_if_true, -1); - - /* return handling */ - if (is_async) - emit_op(s, OP_await); - emit_op(s, OP_iterator_call); - emit_u8(s, 0); - label_return1 = emit_goto(s, OP_if_true, -1); - if (is_async) - emit_op(s, OP_await); - emit_op(s, OP_iterator_check_object); - emit_op(s, OP_get_field2); - emit_atom(s, JS_ATOM_done); - emit_goto(s, OP_if_false, label_yield); - - 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); - emit_u8(s, 1); - label_throw1 = emit_goto(s, OP_if_true, -1); - if (is_async) - emit_op(s, OP_await); - emit_op(s, OP_iterator_check_object); - emit_op(s, OP_get_field2); - emit_atom(s, JS_ATOM_done); - emit_goto(s, OP_if_false, label_yield); - emit_goto(s, OP_goto, label_next); - /* close the iterator and throw a type error exception */ - emit_label(s, label_throw1); - emit_op(s, OP_iterator_call); - emit_u8(s, 2); - label_throw2 = emit_goto(s, OP_if_true, -1); - if (is_async) - emit_op(s, OP_await); - emit_label(s, label_throw2); - - 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); - emit_op(s, OP_nip); /* keep the value associated with - done = true */ - emit_op(s, OP_nip); - emit_op(s, OP_nip); - } else { - int label_next; - - if (is_async) - emit_op(s, OP_await); - emit_op(s, OP_yield); - label_next = emit_goto(s, OP_if_false, -1); - emit_return(s, TRUE); - emit_label(s, label_next); - } - 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, s->token.line_num); - } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) { - const uint8_t *source_ptr; - int source_line_num, tok; - JSParsePos pos; - - /* fast test */ - tok = peek_token(s, TRUE); - if (tok == TOK_FUNCTION || tok == '\n') - goto next; - - source_ptr = s->token.ptr; - source_line_num = s->token.line_num; - 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, source_line_num); - } 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, s->token.line_num); - } - 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)) - return -1; - - op = s->token.val; - if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) { - int label; - if (next_token(s)) - return -1; - if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) - return -1; - - if (js_parse_assign_expr2(s, parse_flags)) { - JS_FreeAtom(s->ctx, name); - return -1; - } - - if (op == '=') { - if (opcode == OP_get_ref_value && name == name0) { - set_object_name(s, name); - } - } else { - static const uint8_t assign_opcodes[] = { - OP_mul, OP_div, OP_mod, OP_add, OP_sub, - OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or, -#ifdef CONFIG_BIGNUM - OP_pow, -#endif - OP_pow, - }; - op = assign_opcodes[op - TOK_MUL_ASSIGN]; -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) { - if (op == OP_mod) - op = OP_math_mod; - } -#endif - emit_op(s, op); - } - put_lvalue(s, opcode, scope, name, label, 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, - &depth_lvalue, TRUE, op) < 0) - return -1; - - emit_op(s, OP_dup); - if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) - emit_op(s, OP_is_undefined_or_null); - 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; - } - - if (opcode == OP_get_ref_value && name == name0) { - set_object_name(s, name); - } - - switch(depth_lvalue) { - case 1: - emit_op(s, OP_insert2); - break; - case 2: - emit_op(s, OP_insert3); - break; - case 3: - emit_op(s, OP_insert4); - break; - default: - abort(); - } - - /* XXX: we disable the OP_put_ref_value optimization by not - using put_lvalue() otherwise depth_lvalue is not correct */ - 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 */ - while (depth_lvalue != 0) { - emit_op(s, OP_nip); - depth_lvalue--; - } - - emit_label(s, label2); - } - return 0; -} - -static __exception int js_parse_assign_expr(JSParseState *s) -{ - return js_parse_assign_expr2(s, PF_IN_ACCEPTED); -} - -/* allowed parse_flags: PF_IN_ACCEPTED */ -static __exception int js_parse_expr2(JSParseState *s, int parse_flags) -{ - BOOL comma = FALSE; - for(;;) { - if (js_parse_assign_expr2(s, parse_flags)) - return -1; - if (comma) { - /* prevent get_lvalue from using the last expression - as an lvalue. This also prevents the conversion of - of get_var to get_ref for method lookup in function - call inside `with` statement. - */ - s->cur_func->last_opcode_pos = -1; - } - if (s->token.val != ',') - break; - comma = TRUE; - if (next_token(s)) - return -1; - emit_op(s, OP_drop); - } - return 0; -} - -static __exception int js_parse_expr(JSParseState *s) -{ - return js_parse_expr2(s, PF_IN_ACCEPTED); -} - -static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, - JSAtom label_name, - int label_break, int label_cont, - int drop_count) -{ - be->prev = fd->top_break; - fd->top_break = be; - be->label_name = label_name; - be->label_break = label_break; - be->label_cont = label_cont; - be->drop_count = drop_count; - be->label_finally = -1; - be->scope_level = fd->scope_level; - be->has_iterator = FALSE; -} - -static void pop_break_entry(JSFunctionDef *fd) -{ - BlockEnv *be; - be = fd->top_break; - fd->top_break = be->prev; -} - -static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) -{ - BlockEnv *top; - int i, scope_level; - - scope_level = s->cur_func->scope_level; - top = s->cur_func->top_break; - while (top != NULL) { - close_scopes(s, scope_level, top->scope_level); - scope_level = top->scope_level; - if (is_cont && - top->label_cont != -1 && - (name == JS_ATOM_NULL || top->label_name == name)) { - /* continue stays inside the same block */ - emit_goto(s, OP_goto, top->label_cont); - return 0; - } - if (!is_cont && - top->label_break != -1 && - (name == JS_ATOM_NULL || top->label_name == name)) { - emit_goto(s, OP_goto, top->label_break); - return 0; - } - i = 0; - if (top->has_iterator) { - emit_op(s, OP_iterator_close); - i += 3; - } - for(; i < top->drop_count; i++) - emit_op(s, OP_drop); - if (top->label_finally != -1) { - /* must push dummy value to keep same stack depth */ - emit_op(s, OP_undefined); - emit_goto(s, OP_gosub, top->label_finally); - emit_op(s, OP_drop); - } - top = top->prev; - } - if (name == JS_ATOM_NULL) { - if (is_cont) - return js_parse_error(s, "continue must be inside loop"); - else - return js_parse_error(s, "break must be inside loop or switch"); - } else { - return js_parse_error(s, "break/continue label not found"); - } -} - -/* execute the finally blocks before return */ -static void emit_return(JSParseState *s, BOOL hasval) -{ - BlockEnv *top; - - 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) { - if (top->has_iterator || top->label_finally != -1) { - if (!hasval) { - emit_op(s, OP_undefined); - hasval = TRUE; - } - /* 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 { - /* execute the "finally" block */ - emit_goto(s, OP_gosub, top->label_finally); - } - } - top = top->prev; - } - if (s->cur_func->is_derived_class_constructor) { - int label_return; - - /* 'this' can be uninitialized, so it may be accessed only if - the derived class constructor does not return an object */ - if (hasval) { - emit_op(s, OP_check_ctor_return); - label_return = emit_goto(s, OP_if_false, -1); - emit_op(s, OP_drop); - } else { - label_return = -1; - } - - /* 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) { - emit_op(s, OP_return_async); - } else { - emit_op(s, hasval ? OP_return : OP_return_undef); - } -} - -#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */ -/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */ -#define DECL_MASK_FUNC_WITH_LABEL (1 << 1) -#define DECL_MASK_OTHER (1 << 2) /* all other declarations */ -#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) - -static __exception int js_parse_statement_or_decl(JSParseState *s, - int decl_mask); - -static __exception int js_parse_statement(JSParseState *s) -{ - return js_parse_statement_or_decl(s, 0); -} - -static __exception int js_parse_block(JSParseState *s) -{ - if (js_parse_expect(s, '{')) - return -1; - if (s->token.val != '}') { - push_scope(s); - for(;;) { - if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) - return -1; - if (s->token.val == '}') - break; - } - pop_scope(s); - } - if (next_token(s)) - return -1; - return 0; -} - -/* allowed parse_flags: PF_IN_ACCEPTED */ -static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, - BOOL export_flag) -{ - JSContext *ctx = s->ctx; - JSFunctionDef *fd = s->cur_func; - JSAtom name = JS_ATOM_NULL; - - for (;;) { - if (s->token.val == TOK_IDENT) { - if (s->token.u.ident.is_reserved) { - return js_parse_error_reserved_identifier(s); - } - name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) { - js_parse_error(s, "'let' is not a valid lexical identifier"); - goto var_error; - } - if (next_token(s)) - goto var_error; - if (js_define_var(s, name, tok)) - goto var_error; - if (export_flag) { - if (!add_export_entry(s, s->cur_func->module, name, name, - JS_EXPORT_TYPE_LOCAL)) - goto var_error; - } - - if (s->token.val == '=') { - if (next_token(s)) - goto var_error; - if (tok == TOK_VAR) { - /* Must make a reference for proper `with` semantics */ - int opcode, scope, label; - JSAtom name1; - - emit_op(s, OP_scope_get_var); - emit_atom(s, name); - emit_u16(s, fd->scope_level); - if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) - goto var_error; - if (js_parse_assign_expr2(s, parse_flags)) { - JS_FreeAtom(ctx, name1); - goto var_error; - } - set_object_name(s, name); - put_lvalue(s, opcode, scope, name1, label, - PUT_LVALUE_NOKEEP, FALSE); - } else { - if (js_parse_assign_expr2(s, parse_flags)) - goto var_error; - set_object_name(s, name); - emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? - OP_scope_put_var_init : OP_scope_put_var); - emit_atom(s, name); - emit_u16(s, fd->scope_level); - } - } else { - if (tok == TOK_CONST) { - js_parse_error(s, "missing initializer for const variable"); - goto var_error; - } - if (tok == TOK_LET) { - /* initialize lexical variable upon entering its scope */ - emit_op(s, OP_undefined); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, name); - emit_u16(s, fd->scope_level); - } - } - JS_FreeAtom(ctx, name); - } else { - int skip_bits; - if ((s->token.val == '[' || s->token.val == '{') - && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { - emit_op(s, OP_undefined); - if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) - return -1; - } else { - return js_parse_error(s, "variable name expected"); - } - } - if (s->token.val != ',') - break; - if (next_token(s)) - return -1; - } - return 0; - - var_error: - JS_FreeAtom(ctx, name); - return -1; -} - -/* test if the current token is a label. Use simplistic look-ahead scanner */ -static BOOL is_label(JSParseState *s) -{ - return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && - peek_token(s, FALSE) == ':'); -} - -/* test if the current token is a let keyword. Use simplistic look-ahead scanner */ -static int is_let(JSParseState *s, int decl_mask) -{ - int res = FALSE; - - if (token_is_pseudo_keyword(s, JS_ATOM_let)) { - JSParsePos pos; - js_parse_get_pos(s, &pos); - for (;;) { - if (next_token(s)) { - res = -1; - break; - } - if (s->token.val == '[') { - /* let [ is a syntax restriction: - it never introduces an ExpressionStatement */ - res = TRUE; - break; - } - if (s->token.val == '{' || - (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) || - s->token.val == TOK_LET || - s->token.val == TOK_YIELD || - s->token.val == TOK_AWAIT) { - /* Check for possible ASI if not scanning for Declaration */ - /* XXX: should also check that `{` introduces a BindingPattern, - but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */ - if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) { - res = TRUE; - break; - } - break; - } - break; - } - if (js_parse_seek_token(s, &pos)) { - res = -1; - } - } - return res; -} - -/* XXX: handle IteratorClose when exiting the loop before the - enumeration is done */ -static __exception int js_parse_for_in_of(JSParseState *s, int label_name, - BOOL is_async) -{ - JSContext *ctx = s->ctx; - JSFunctionDef *fd = s->cur_func; - JSAtom var_name; - BOOL has_initializer, is_for_of, has_destructuring; - int tok, tok1, opcode, scope, block_scope_level; - int label_next, label_expr, label_cont, label_body, label_break; - int pos_next, pos_expr; - BlockEnv break_entry; - - has_initializer = FALSE; - has_destructuring = FALSE; - is_for_of = FALSE; - block_scope_level = fd->scope_level; - label_cont = new_label(s); - label_body = new_label(s); - label_break = new_label(s); - label_next = new_label(s); - - /* create scope for the lexical variables declared in the enumeration - expressions. XXX: Not completely correct because of weird capturing - semantics in `for (i of o) a.push(function(){return i})` */ - push_scope(s); - - /* local for_in scope starts here so individual elements - can be closed in statement. */ - push_break_entry(s->cur_func, &break_entry, - label_name, label_break, label_cont, 1); - break_entry.scope_level = block_scope_level; - - label_expr = emit_goto(s, OP_goto, -1); - - pos_next = s->cur_func->byte_code.size; - emit_label(s, label_next); - - tok = s->token.val; - switch (is_let(s, DECL_MASK_OTHER)) { - case TRUE: - tok = TOK_LET; - break; - case FALSE: - break; - default: - return -1; - } - if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { - if (next_token(s)) - return -1; - - if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { - if (s->token.val == '[' || s->token.val == '{') { - if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) - return -1; - has_destructuring = TRUE; - } else { - return js_parse_error(s, "variable name expected"); - } - var_name = JS_ATOM_NULL; - } else { - var_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) { - JS_FreeAtom(s->ctx, var_name); - return -1; - } - if (js_define_var(s, var_name, tok)) { - JS_FreeAtom(s->ctx, var_name); - return -1; - } - emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? - OP_scope_put_var_init : OP_scope_put_var); - emit_atom(s, var_name); - emit_u16(s, fd->scope_level); - } - } else 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) - return -1; - } else { - int lvalue_label; - if (js_parse_left_hand_side_expr(s)) - return -1; - if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, - NULL, FALSE, TOK_FOR)) - return -1; - put_lvalue(s, opcode, scope, var_name, lvalue_label, - PUT_LVALUE_NOKEEP_BOTTOM, FALSE); - } - var_name = JS_ATOM_NULL; - } - emit_goto(s, OP_goto, label_body); - - pos_expr = s->cur_func->byte_code.size; - emit_label(s, label_expr); - if (s->token.val == '=') { - /* XXX: potential scoping issue if inside `with` statement */ - has_initializer = TRUE; - /* parse and evaluate initializer prior to evaluating the - object (only used with "for in" with a non lexical variable - in non strict mode */ - if (next_token(s) || js_parse_assign_expr2(s, 0)) { - JS_FreeAtom(ctx, var_name); - return -1; - } - if (var_name != JS_ATOM_NULL) { - emit_op(s, OP_scope_put_var); - emit_atom(s, var_name); - emit_u16(s, fd->scope_level); - } - } - JS_FreeAtom(ctx, var_name); - - if (token_is_pseudo_keyword(s, JS_ATOM_of)) { - break_entry.has_iterator = is_for_of = TRUE; - break_entry.drop_count += 2; - if (has_initializer) - goto initializer_error; - } else if (s->token.val == TOK_IN) { - if (is_async) - return js_parse_error(s, "'for await' loop should be used with 'of'"); - if (has_initializer && - (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) || - has_destructuring)) { - initializer_error: - return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer", - is_for_of ? "of" : "in"); - } - } else { - return js_parse_error(s, "expected 'of' or 'in' in for control expression"); - } - if (next_token(s)) - return -1; - if (is_for_of) { - if (js_parse_assign_expr(s)) - return -1; - } else { - if (js_parse_expr(s)) - return -1; - } - /* close the scope after having evaluated the expression so that - the TDZ values are in the closures */ - close_scopes(s, s->cur_func->scope_level, block_scope_level); - if (is_for_of) { - if (is_async) - emit_op(s, OP_for_await_of_start); - else - emit_op(s, OP_for_of_start); - /* on stack: enum_rec */ - } else { - emit_op(s, OP_for_in_start); - /* on stack: enum_obj */ - } - emit_goto(s, OP_goto, label_cont); - - if (js_parse_expect(s, ')')) - return -1; - - if (OPTIMIZE) { - /* move the `next` code here */ - DynBuf *bc = &s->cur_func->byte_code; - int chunk_size = pos_expr - pos_next; - int offset = bc->size - pos_next; - int i; - dbuf_realloc(bc, bc->size + chunk_size); - dbuf_put(bc, bc->buf + pos_next, chunk_size); - memset(bc->buf + pos_next, OP_nop, chunk_size); - /* `next` part ends with a goto */ - s->cur_func->last_opcode_pos = bc->size - 5; - /* relocate labels */ - for (i = label_cont; i < s->cur_func->label_count; i++) { - LabelSlot *ls = &s->cur_func->label_slots[i]; - if (ls->pos >= pos_next && ls->pos < pos_expr) - ls->pos += offset; - } - } - - emit_label(s, label_body); - if (js_parse_statement(s)) - return -1; - - close_scopes(s, s->cur_func->scope_level, block_scope_level); - - emit_label(s, label_cont); - if (is_for_of) { - if (is_async) { - /* call the next method */ - /* 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); - /* get the result of the promise */ - emit_op(s, OP_await); - /* unwrap the value and done values */ - emit_op(s, OP_iterator_get_value_done); - } else { - emit_op(s, OP_for_of_next); - emit_u8(s, 0); - } - } else { - emit_op(s, OP_for_in_next); - } - /* on stack: enum_rec / enum_obj value bool */ - emit_goto(s, OP_if_false, label_next); - /* drop the undefined value from for_xx_next */ - emit_op(s, OP_drop); - - emit_label(s, label_break); - if (is_for_of) { - /* close and drop enum_rec */ - emit_op(s, OP_iterator_close); - } else { - emit_op(s, OP_drop); - } - pop_break_entry(s->cur_func); - pop_scope(s); - return 0; -} - -static void set_eval_ret_undefined(JSParseState *s) -{ - if (s->cur_func->eval_ret_idx >= 0) { - emit_op(s, OP_undefined); - emit_op(s, OP_put_loc); - emit_u16(s, s->cur_func->eval_ret_idx); - } -} - -static __exception int js_parse_statement_or_decl(JSParseState *s, - int decl_mask) -{ - JSContext *ctx = s->ctx; - JSAtom label_name; - int tok; - - /* specific label handling */ - /* XXX: support multiple labels on loop statements */ - label_name = JS_ATOM_NULL; - if (is_label(s)) { - BlockEnv *be; - - label_name = JS_DupAtom(ctx, s->token.u.ident.atom); - - for (be = s->cur_func->top_break; be; be = be->prev) { - if (be->label_name == label_name) { - js_parse_error(s, "duplicate label name"); - goto fail; - } - } - - if (next_token(s)) - goto fail; - if (js_parse_expect(s, ':')) - goto fail; - if (s->token.val != TOK_FOR - && s->token.val != TOK_DO - && s->token.val != TOK_WHILE) { - /* labelled regular statement */ - int label_break, mask; - BlockEnv break_entry; - - label_break = new_label(s); - push_break_entry(s->cur_func, &break_entry, - label_name, label_break, -1, 0); - if (!(s->cur_func->js_mode & JS_MODE_STRICT) && - (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { - mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; - } else { - mask = 0; - } - if (js_parse_statement_or_decl(s, mask)) - goto fail; - emit_label(s, label_break); - pop_break_entry(s->cur_func); - goto done; - } - } - - switch(tok = s->token.val) { - case '{': - if (js_parse_block(s)) - goto fail; - break; - case TOK_RETURN: - if (s->cur_func->is_eval) { - js_parse_error(s, "return not in a function"); - goto fail; - } - if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { - js_parse_error(s, "return in a static initializer block"); - goto fail; - } - if (next_token(s)) - goto fail; - if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { - if (js_parse_expr(s)) - goto fail; - emit_return(s, TRUE); - } else { - emit_return(s, FALSE); - } - if (js_parse_expect_semi(s)) - goto fail; - break; - case TOK_THROW: - if (next_token(s)) - goto fail; - if (s->got_lf) { - js_parse_error(s, "line terminator not allowed after throw"); - goto fail; - } - if (js_parse_expr(s)) - goto fail; - emit_op(s, OP_throw); - if (js_parse_expect_semi(s)) - goto fail; - break; - case TOK_LET: - case TOK_CONST: - haslet: - if (!(decl_mask & DECL_MASK_OTHER)) { - js_parse_error(s, "lexical declarations can't appear in single-statement context"); - goto fail; - } - /* fall thru */ - case TOK_VAR: - if (next_token(s)) - goto fail; - if (js_parse_var(s, TRUE, tok, FALSE)) - goto fail; - if (js_parse_expect_semi(s)) - goto fail; - break; - case TOK_IF: - { - int label1, label2, mask; - if (next_token(s)) - goto fail; - /* create a new scope for `let f;if(1) function f(){}` */ - push_scope(s); - set_eval_ret_undefined(s); - if (js_parse_expr_paren(s)) - goto fail; - label1 = emit_goto(s, OP_if_false, -1); - if (s->cur_func->js_mode & JS_MODE_STRICT) - mask = 0; - else - mask = DECL_MASK_FUNC; /* Annex B.3.4 */ - - if (js_parse_statement_or_decl(s, mask)) - goto fail; - - if (s->token.val == TOK_ELSE) { - label2 = emit_goto(s, OP_goto, -1); - if (next_token(s)) - goto fail; - - emit_label(s, label1); - if (js_parse_statement_or_decl(s, mask)) - goto fail; - - label1 = label2; - } - emit_label(s, label1); - pop_scope(s); - } - break; - case TOK_WHILE: - { - int label_cont, label_break; - BlockEnv break_entry; - - label_cont = new_label(s); - label_break = new_label(s); - - push_break_entry(s->cur_func, &break_entry, - label_name, label_break, label_cont, 0); - - if (next_token(s)) - goto fail; - - set_eval_ret_undefined(s); - - emit_label(s, label_cont); - if (js_parse_expr_paren(s)) - goto fail; - emit_goto(s, OP_if_false, label_break); - - if (js_parse_statement(s)) - goto fail; - emit_goto(s, OP_goto, label_cont); - - emit_label(s, label_break); - - pop_break_entry(s->cur_func); - } - break; - case TOK_DO: - { - int label_cont, label_break, label1; - BlockEnv break_entry; - - label_cont = new_label(s); - label_break = new_label(s); - label1 = new_label(s); - - push_break_entry(s->cur_func, &break_entry, - label_name, label_break, label_cont, 0); - - if (next_token(s)) - goto fail; - - emit_label(s, label1); - - set_eval_ret_undefined(s); - - if (js_parse_statement(s)) - goto fail; - - emit_label(s, label_cont); - if (js_parse_expect(s, TOK_WHILE)) - goto fail; - if (js_parse_expr_paren(s)) - goto fail; - /* Insert semicolon if missing */ - if (s->token.val == ';') { - if (next_token(s)) - goto fail; - } - emit_goto(s, OP_if_true, label1); - - emit_label(s, label_break); - - pop_break_entry(s->cur_func); - } - break; - case TOK_FOR: - { - int label_cont, label_break, label_body, label_test; - int pos_cont, pos_body, block_scope_level; - BlockEnv break_entry; - int tok, bits; - BOOL is_async; - - if (next_token(s)) - goto fail; - - set_eval_ret_undefined(s); - bits = 0; - is_async = FALSE; - if (s->token.val == '(') { - js_parse_skip_parens_token(s, &bits, FALSE); - } else if (s->token.val == TOK_AWAIT) { - if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) { - js_parse_error(s, "for await is only valid in asynchronous functions"); - goto fail; - } - is_async = TRUE; - if (next_token(s)) - goto fail; - s->cur_func->has_await = TRUE; - } - if (js_parse_expect(s, '(')) - goto fail; - - if (!(bits & SKIP_HAS_SEMI)) { - /* parse for/in or for/of */ - if (js_parse_for_in_of(s, label_name, is_async)) - goto fail; - break; - } - block_scope_level = s->cur_func->scope_level; - - /* create scope for the lexical variables declared in the initial, - test and increment expressions */ - push_scope(s); - /* initial expression */ - tok = s->token.val; - if (tok != ';') { - switch (is_let(s, DECL_MASK_OTHER)) { - case TRUE: - tok = TOK_LET; - break; - case FALSE: - break; - default: - goto fail; - } - if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { - if (next_token(s)) - goto fail; - if (js_parse_var(s, FALSE, tok, FALSE)) - goto fail; - } else { - if (js_parse_expr2(s, FALSE)) - goto fail; - emit_op(s, OP_drop); - } - - /* close the closures before the first iteration */ - close_scopes(s, s->cur_func->scope_level, block_scope_level); - } - if (js_parse_expect(s, ';')) - goto fail; - - label_test = new_label(s); - label_cont = new_label(s); - label_body = new_label(s); - label_break = new_label(s); - - push_break_entry(s->cur_func, &break_entry, - label_name, label_break, label_cont, 0); - - /* test expression */ - if (s->token.val == ';') { - /* no test expression */ - label_test = label_body; - } else { - emit_label(s, label_test); - if (js_parse_expr(s)) - goto fail; - emit_goto(s, OP_if_false, label_break); - } - if (js_parse_expect(s, ';')) - goto fail; - - if (s->token.val == ')') { - /* no end expression */ - break_entry.label_cont = label_cont = label_test; - pos_cont = 0; /* avoid warning */ - } else { - /* skip the end expression */ - emit_goto(s, OP_goto, label_body); - - pos_cont = s->cur_func->byte_code.size; - emit_label(s, label_cont); - if (js_parse_expr(s)) - goto fail; - emit_op(s, OP_drop); - if (label_test != label_body) - emit_goto(s, OP_goto, label_test); - } - if (js_parse_expect(s, ')')) - goto fail; - - pos_body = s->cur_func->byte_code.size; - emit_label(s, label_body); - if (js_parse_statement(s)) - goto fail; - - /* close the closures before the next iteration */ - /* XXX: check continue case */ - close_scopes(s, s->cur_func->scope_level, block_scope_level); - - if (OPTIMIZE && label_test != label_body && label_cont != label_test) { - /* move the increment code here */ - DynBuf *bc = &s->cur_func->byte_code; - int chunk_size = pos_body - pos_cont; - int offset = bc->size - pos_cont; - int i; - dbuf_realloc(bc, bc->size + chunk_size); - dbuf_put(bc, bc->buf + pos_cont, chunk_size); - memset(bc->buf + pos_cont, OP_nop, chunk_size); - /* increment part ends with a goto */ - s->cur_func->last_opcode_pos = bc->size - 5; - /* relocate labels */ - for (i = label_cont; i < s->cur_func->label_count; i++) { - LabelSlot *ls = &s->cur_func->label_slots[i]; - if (ls->pos >= pos_cont && ls->pos < pos_body) - ls->pos += offset; - } - } else { - emit_goto(s, OP_goto, label_cont); - } - - emit_label(s, label_break); - - pop_break_entry(s->cur_func); - pop_scope(s); - } - break; - case TOK_BREAK: - case TOK_CONTINUE: - { - int is_cont = s->token.val - TOK_BREAK; - int label; - - if (next_token(s)) - goto fail; - if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) - label = s->token.u.ident.atom; - else - label = JS_ATOM_NULL; - if (emit_break(s, label, is_cont)) - goto fail; - if (label != JS_ATOM_NULL) { - if (next_token(s)) - goto fail; - } - if (js_parse_expect_semi(s)) - goto fail; - } - break; - case TOK_SWITCH: - { - int label_case, label_break, label1; - int default_label_pos; - BlockEnv break_entry; - - if (next_token(s)) - goto fail; - - set_eval_ret_undefined(s); - if (js_parse_expr_paren(s)) - goto fail; - - push_scope(s); - label_break = new_label(s); - push_break_entry(s->cur_func, &break_entry, - label_name, label_break, -1, 1); - - if (js_parse_expect(s, '{')) - goto fail; - - default_label_pos = -1; - label_case = -1; - while (s->token.val != '}') { - if (s->token.val == TOK_CASE) { - label1 = -1; - if (label_case >= 0) { - /* skip the case if needed */ - label1 = emit_goto(s, OP_goto, -1); - } - emit_label(s, label_case); - label_case = -1; - for (;;) { - /* parse a sequence of case clauses */ - if (next_token(s)) - goto fail; - emit_op(s, OP_dup); - if (js_parse_expr(s)) - goto fail; - if (js_parse_expect(s, ':')) - goto fail; - emit_op(s, OP_strict_eq); - if (s->token.val == TOK_CASE) { - label1 = emit_goto(s, OP_if_true, label1); - } else { - label_case = emit_goto(s, OP_if_false, -1); - emit_label(s, label1); - break; - } - } - } else if (s->token.val == TOK_DEFAULT) { - if (next_token(s)) - goto fail; - if (js_parse_expect(s, ':')) - goto fail; - if (default_label_pos >= 0) { - js_parse_error(s, "duplicate default"); - goto fail; - } - if (label_case < 0) { - /* falling thru direct from switch expression */ - label_case = emit_goto(s, OP_goto, -1); - } - /* Emit a dummy label opcode. Label will be patched after - the end of the switch body. Do not use emit_label(s, 0) - because it would clobber label 0 address, preventing - proper optimizer operation. - */ - emit_op(s, OP_label); - emit_u32(s, 0); - default_label_pos = s->cur_func->byte_code.size - 4; - } else { - if (label_case < 0) { - /* falling thru direct from switch expression */ - js_parse_error(s, "invalid switch statement"); - goto fail; - } - if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) - goto fail; - } - } - if (js_parse_expect(s, '}')) - goto fail; - if (default_label_pos >= 0) { - /* Ugly patch for the the `default` label, shameful and risky */ - put_u32(s->cur_func->byte_code.buf + default_label_pos, - label_case); - s->cur_func->label_slots[label_case].pos = default_label_pos + 4; - } else { - emit_label(s, label_case); - } - emit_label(s, label_break); - emit_op(s, OP_drop); /* drop the switch expression */ - - pop_break_entry(s->cur_func); - pop_scope(s); - } - break; - case TOK_TRY: - { - int label_catch, label_catch2, label_finally, label_end; - JSAtom name; - BlockEnv block_env; - - set_eval_ret_undefined(s); - if (next_token(s)) - goto fail; - label_catch = new_label(s); - label_catch2 = new_label(s); - label_finally = new_label(s); - label_end = new_label(s); - - emit_goto(s, OP_catch, label_catch); - - push_break_entry(s->cur_func, &block_env, - JS_ATOM_NULL, -1, -1, 1); - block_env.label_finally = label_finally; - - if (js_parse_block(s)) - goto fail; - - pop_break_entry(s->cur_func); - - if (js_is_live_code(s)) { - /* drop the catch offset */ - emit_op(s, OP_drop); - /* must push dummy value to keep same stack size */ - emit_op(s, OP_undefined); - emit_goto(s, OP_gosub, label_finally); - emit_op(s, OP_drop); - - emit_goto(s, OP_goto, label_end); - } - - if (s->token.val == TOK_CATCH) { - if (next_token(s)) - goto fail; - - push_scope(s); /* catch variable */ - emit_label(s, label_catch); - - if (s->token.val == '{') { - /* support optional-catch-binding feature */ - emit_op(s, OP_drop); /* pop the exception object */ - } else { - if (js_parse_expect(s, '(')) - goto fail; - if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { - if (s->token.val == '[' || s->token.val == '{') { - /* XXX: TOK_LET is not completely correct */ - if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) - goto fail; - } else { - js_parse_error(s, "identifier expected"); - goto fail; - } - } else { - name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s) - || js_define_var(s, name, TOK_CATCH) < 0) { - JS_FreeAtom(ctx, name); - goto fail; - } - /* store the exception value in the catch variable */ - emit_op(s, OP_scope_put_var); - emit_u32(s, name); - emit_u16(s, s->cur_func->scope_level); - } - if (js_parse_expect(s, ')')) - goto fail; - } - /* XXX: should keep the address to nop it out if there is no finally block */ - emit_goto(s, OP_catch, label_catch2); - - push_scope(s); /* catch block */ - push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, - -1, -1, 1); - block_env.label_finally = label_finally; - - if (js_parse_block(s)) - goto fail; - - pop_break_entry(s->cur_func); - pop_scope(s); /* catch block */ - pop_scope(s); /* catch variable */ - - if (js_is_live_code(s)) { - /* drop the catch2 offset */ - emit_op(s, OP_drop); - /* XXX: should keep the address to nop it out if there is no finally block */ - /* must push dummy value to keep same stack size */ - emit_op(s, OP_undefined); - emit_goto(s, OP_gosub, label_finally); - emit_op(s, OP_drop); - emit_goto(s, OP_goto, label_end); - } - /* catch exceptions thrown in the catch block to execute the - * finally clause and rethrow the exception */ - emit_label(s, label_catch2); - /* catch value is at TOS, no need to push undefined */ - emit_goto(s, OP_gosub, label_finally); - emit_op(s, OP_throw); - - } else if (s->token.val == TOK_FINALLY) { - /* finally without catch : execute the finally clause - * and rethrow the exception */ - emit_label(s, label_catch); - /* catch value is at TOS, no need to push undefined */ - emit_goto(s, OP_gosub, label_finally); - emit_op(s, OP_throw); - } else { - js_parse_error(s, "expecting catch or finally"); - goto fail; - } - emit_label(s, label_finally); - if (s->token.val == TOK_FINALLY) { - int saved_eval_ret_idx = 0; /* avoid warning */ - - if (next_token(s)) - goto fail; - /* on the stack: ret_value gosub_ret_value */ - push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, - -1, -1, 2); - - if (s->cur_func->eval_ret_idx >= 0) { - /* 'finally' updates eval_ret only if not a normal - termination */ - saved_eval_ret_idx = - add_var(s->ctx, s->cur_func, JS_ATOM__ret_); - if (saved_eval_ret_idx < 0) - goto fail; - emit_op(s, OP_get_loc); - emit_u16(s, s->cur_func->eval_ret_idx); - emit_op(s, OP_put_loc); - emit_u16(s, saved_eval_ret_idx); - set_eval_ret_undefined(s); - } - - if (js_parse_block(s)) - goto fail; - - if (s->cur_func->eval_ret_idx >= 0) { - emit_op(s, OP_get_loc); - emit_u16(s, saved_eval_ret_idx); - emit_op(s, OP_put_loc); - emit_u16(s, s->cur_func->eval_ret_idx); - } - pop_break_entry(s->cur_func); - } - emit_op(s, OP_ret); - emit_label(s, label_end); - } - break; - case ';': - /* empty statement */ - if (next_token(s)) - goto fail; - break; - case TOK_WITH: - if (s->cur_func->js_mode & JS_MODE_STRICT) { - js_parse_error(s, "invalid keyword: with"); - goto fail; - } else { - int with_idx; - - if (next_token(s)) - goto fail; - - if (js_parse_expr_paren(s)) - goto fail; - - push_scope(s); - with_idx = define_var(s, s->cur_func, JS_ATOM__with_, - JS_VAR_DEF_WITH); - if (with_idx < 0) - goto fail; - emit_op(s, OP_to_object); - emit_op(s, OP_put_loc); - emit_u16(s, with_idx); - - set_eval_ret_undefined(s); - if (js_parse_statement(s)) - goto fail; - - /* Popping scope drops lexical context for the with object variable */ - pop_scope(s); - } - break; - case TOK_FUNCTION: - /* ES6 Annex B.3.2 and B.3.3 semantics */ - if (!(decl_mask & DECL_MASK_FUNC)) - goto func_decl_error; - if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*') - goto func_decl_error; - goto parse_func_var; - case TOK_IDENT: - if (s->token.u.ident.is_reserved) { - js_parse_error_reserved_identifier(s); - goto fail; - } - /* Determine if `let` introduces a Declaration or an ExpressionStatement */ - switch (is_let(s, decl_mask)) { - case TRUE: - tok = TOK_LET; - goto haslet; - case FALSE: - break; - default: - goto fail; - } - if (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) == TOK_FUNCTION) { - if (!(decl_mask & DECL_MASK_OTHER)) { - func_decl_error: - js_parse_error(s, "function declarations can't appear in single-statement context"); - goto fail; - } - parse_func_var: - if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - goto fail; - break; - } - goto hasexpr; - - case TOK_CLASS: - if (!(decl_mask & DECL_MASK_OTHER)) { - js_parse_error(s, "class declarations can't appear in single-statement context"); - goto fail; - } - if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE)) - return -1; - break; - - case TOK_DEBUGGER: - /* currently no debugger, so just skip the keyword */ - if (next_token(s)) - goto fail; - if (js_parse_expect_semi(s)) - goto fail; - break; - - case TOK_ENUM: - case TOK_EXPORT: - case TOK_EXTENDS: - js_unsupported_keyword(s, s->token.u.ident.atom); - goto fail; - - default: - hasexpr: - if (js_parse_expr(s)) - goto fail; - if (s->cur_func->eval_ret_idx >= 0) { - /* store the expression value so that it can be returned - by eval() */ - emit_op(s, OP_put_loc); - emit_u16(s, s->cur_func->eval_ret_idx); - } else { - emit_op(s, OP_drop); /* drop the result */ - } - if (js_parse_expect_semi(s)) - goto fail; - break; - } -done: - JS_FreeAtom(ctx, label_name); - return 0; -fail: - JS_FreeAtom(ctx, label_name); - return -1; -} - -/* 'name' is freed */ -static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) -{ - JSModuleDef *m; - m = js_mallocz(ctx, sizeof(*m)); - if (!m) { - JS_FreeAtom(ctx, name); - return NULL; - } - m->header.ref_count = 1; - m->module_name = name; - m->module_ns = JS_UNDEFINED; - m->func_obj = JS_UNDEFINED; - m->eval_exception = JS_UNDEFINED; - m->meta_obj = JS_UNDEFINED; - m->promise = JS_UNDEFINED; - m->resolving_funcs[0] = JS_UNDEFINED; - m->resolving_funcs[1] = JS_UNDEFINED; - list_add_tail(&m->link, &ctx->loaded_modules); - return m; -} - -static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, - JS_MarkFunc *mark_func) -{ - int i; - - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL && - me->u.local.var_ref) { - mark_func(rt, &me->u.local.var_ref->header); - } - } - - JS_MarkValue(rt, m->module_ns, mark_func); - JS_MarkValue(rt, m->func_obj, mark_func); - JS_MarkValue(rt, m->eval_exception, mark_func); - JS_MarkValue(rt, m->meta_obj, mark_func); - 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); -} - -static void js_free_module_def(JSContext *ctx, JSModuleDef *m) -{ - int i; - - JS_FreeAtom(ctx, m->module_name); - - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - JS_FreeAtom(ctx, rme->module_name); - } - js_free(ctx, m->req_module_entries); - - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) - free_var_ref(ctx->rt, me->u.local.var_ref); - JS_FreeAtom(ctx, me->export_name); - JS_FreeAtom(ctx, me->local_name); - } - js_free(ctx, m->export_entries); - - js_free(ctx, m->star_export_entries); - - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - JS_FreeAtom(ctx, mi->import_name); - } - js_free(ctx, m->import_entries); - js_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]); - list_del(&m->link); - js_free(ctx, m); -} - -static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, - JSAtom module_name) -{ - JSReqModuleEntry *rme; - int i; - - /* no need to add the module request if it is already present */ - for(i = 0; i < m->req_module_entries_count; i++) { - rme = &m->req_module_entries[i]; - if (rme->module_name == module_name) - return i; - } - - if (js_resize_array(ctx, (void **)&m->req_module_entries, - sizeof(JSReqModuleEntry), - &m->req_module_entries_size, - m->req_module_entries_count + 1)) - return -1; - rme = &m->req_module_entries[m->req_module_entries_count++]; - rme->module_name = JS_DupAtom(ctx, module_name); - rme->module = NULL; - return i; -} - -static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, - JSAtom export_name) -{ - JSExportEntry *me; - int i; - for(i = 0; i < m->export_entries_count; i++) { - me = &m->export_entries[i]; - if (me->export_name == export_name) - return me; - } - return NULL; -} - -static JSExportEntry *add_export_entry2(JSContext *ctx, - JSParseState *s, JSModuleDef *m, - JSAtom local_name, JSAtom export_name, - JSExportTypeEnum export_type) -{ - JSExportEntry *me; - - if (find_export_entry(ctx, m, export_name)) { - char buf1[ATOM_GET_STR_BUF_SIZE]; - if (s) { - js_parse_error(s, "duplicate exported name '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); - } else { - JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name); - } - return NULL; - } - - if (js_resize_array(ctx, (void **)&m->export_entries, - sizeof(JSExportEntry), - &m->export_entries_size, - m->export_entries_count + 1)) - return NULL; - me = &m->export_entries[m->export_entries_count++]; - memset(me, 0, sizeof(*me)); - me->local_name = JS_DupAtom(ctx, local_name); - me->export_name = JS_DupAtom(ctx, export_name); - me->export_type = export_type; - return me; -} - -static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, - JSAtom local_name, JSAtom export_name, - JSExportTypeEnum export_type) -{ - return add_export_entry2(s->ctx, s, m, local_name, export_name, - export_type); -} - -static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, - int req_module_idx) -{ - JSStarExportEntry *se; - - if (js_resize_array(ctx, (void **)&m->star_export_entries, - sizeof(JSStarExportEntry), - &m->star_export_entries_size, - m->star_export_entries_count + 1)) - return -1; - se = &m->star_export_entries[m->star_export_entries_count++]; - se->req_module_idx = req_module_idx; - return 0; -} - -/* create a C module */ -JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, - JSModuleInitFunc *func) -{ - JSModuleDef *m; - JSAtom name; - name = JS_NewAtom(ctx, name_str); - if (name == JS_ATOM_NULL) - return NULL; - m = js_new_module_def(ctx, name); - m->init_func = func; - return m; -} - -int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name) -{ - JSExportEntry *me; - JSAtom name; - name = JS_NewAtom(ctx, export_name); - if (name == JS_ATOM_NULL) - return -1; - me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name, - JS_EXPORT_TYPE_LOCAL); - JS_FreeAtom(ctx, name); - if (!me) - return -1; - else - return 0; -} - -int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, - JSValue val) -{ - JSExportEntry *me; - JSAtom name; - name = JS_NewAtom(ctx, export_name); - if (name == JS_ATOM_NULL) - goto fail; - me = find_export_entry(ctx, m, name); - JS_FreeAtom(ctx, name); - if (!me) - goto fail; - set_value(ctx, me->u.local.var_ref->pvalue, val); - return 0; - fail: - JS_FreeValue(ctx, val); - return -1; -} - -void JS_SetModuleLoaderFunc(JSRuntime *rt, - JSModuleNormalizeFunc *module_normalize, - JSModuleLoaderFunc *module_loader, void *opaque) -{ - rt->module_normalize_func = module_normalize; - rt->module_loader_func = module_loader; - rt->module_loader_opaque = opaque; -} - -/* default module filename normalizer */ -static char *js_default_module_normalize_name(JSContext *ctx, - const char *base_name, - const char *name) -{ - char *filename, *p; - const char *r; - int cap; - int len; - - if (name[0] != '.') { - /* if no initial dot, the module name is not modified */ - return js_strdup(ctx, name); - } - - p = strrchr(base_name, '/'); - if (p) - len = p - base_name; - else - len = 0; - - cap = len + strlen(name) + 1 + 1; - filename = js_malloc(ctx, cap); - if (!filename) - return NULL; - memcpy(filename, base_name, len); - filename[len] = '\0'; - - /* we only normalize the leading '..' or '.' */ - r = name; - for(;;) { - if (r[0] == '.' && r[1] == '/') { - r += 2; - } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') { - /* remove the last path element of filename, except if "." - or ".." */ - if (filename[0] == '\0') - break; - p = strrchr(filename, '/'); - if (!p) - p = filename; - else - p++; - if (!strcmp(p, ".") || !strcmp(p, "..")) - break; - if (p > filename) - p--; - *p = '\0'; - r += 3; - } else { - break; - } - } - if (filename[0] != '\0') - pstrcat(filename, cap, "/"); - pstrcat(filename, cap, r); - // printf("normalize: %s %s -> %s\n", base_name, name, filename); - return filename; -} - -static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) -{ - struct list_head *el; - JSModuleDef *m; - - /* first look at the loaded modules */ - list_for_each(el, &ctx->loaded_modules) { - m = list_entry(el, JSModuleDef, link); - if (m->module_name == name) - return m; - } - return NULL; -} - -/* return NULL in case of exception (e.g. module could not be loaded) */ -static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, - const char *base_cname, - const char *cname1) -{ - JSRuntime *rt = ctx->rt; - JSModuleDef *m; - char *cname; - JSAtom module_name; - - if (!rt->module_normalize_func) { - cname = js_default_module_normalize_name(ctx, base_cname, cname1); - } else { - cname = rt->module_normalize_func(ctx, base_cname, cname1, - rt->module_loader_opaque); - } - if (!cname) - return NULL; - - module_name = JS_NewAtom(ctx, cname); - if (module_name == JS_ATOM_NULL) { - js_free(ctx, cname); - return NULL; - } - - /* first look at the loaded modules */ - m = js_find_loaded_module(ctx, module_name); - if (m) { - js_free(ctx, cname); - JS_FreeAtom(ctx, module_name); - return m; - } - - JS_FreeAtom(ctx, module_name); - - /* load the module */ - if (!rt->module_loader_func) { - /* XXX: use a syntax error ? */ - JS_ThrowReferenceError(ctx, "could not load module '%s'", - cname); - js_free(ctx, cname); - return NULL; - } - - m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque); - js_free(ctx, cname); - return m; -} - -static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, - JSAtom base_module_name, - JSAtom module_name1) -{ - const char *base_cname, *cname; - JSModuleDef *m; - - base_cname = JS_AtomToCString(ctx, base_module_name); - if (!base_cname) - return NULL; - cname = JS_AtomToCString(ctx, module_name1); - if (!cname) { - JS_FreeCString(ctx, base_cname); - return NULL; - } - m = js_host_resolve_imported_module(ctx, base_cname, cname); - JS_FreeCString(ctx, base_cname); - JS_FreeCString(ctx, cname); - return m; -} - -typedef struct JSResolveEntry { - JSModuleDef *module; - JSAtom name; -} JSResolveEntry; - -typedef struct JSResolveState { - JSResolveEntry *array; - int size; - int count; -} JSResolveState; - -static int find_resolve_entry(JSResolveState *s, - JSModuleDef *m, JSAtom name) -{ - int i; - for(i = 0; i < s->count; i++) { - JSResolveEntry *re = &s->array[i]; - if (re->module == m && re->name == name) - return i; - } - return -1; -} - -static int add_resolve_entry(JSContext *ctx, JSResolveState *s, - JSModuleDef *m, JSAtom name) -{ - JSResolveEntry *re; - - if (js_resize_array(ctx, (void **)&s->array, - sizeof(JSResolveEntry), - &s->size, s->count + 1)) - return -1; - re = &s->array[s->count++]; - re->module = m; - re->name = JS_DupAtom(ctx, name); - return 0; -} - -typedef enum JSResolveResultEnum { - JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */ - JS_RESOLVE_RES_FOUND = 0, - JS_RESOLVE_RES_NOT_FOUND, - JS_RESOLVE_RES_CIRCULAR, - JS_RESOLVE_RES_AMBIGUOUS, -} JSResolveResultEnum; - -static JSResolveResultEnum js_resolve_export1(JSContext *ctx, - JSModuleDef **pmodule, - JSExportEntry **pme, - JSModuleDef *m, - JSAtom export_name, - JSResolveState *s) -{ - JSExportEntry *me; - - *pmodule = NULL; - *pme = NULL; - if (find_resolve_entry(s, m, export_name) >= 0) - return JS_RESOLVE_RES_CIRCULAR; - if (add_resolve_entry(ctx, s, m, export_name) < 0) - return JS_RESOLVE_RES_EXCEPTION; - me = find_export_entry(ctx, m, export_name); - if (me) { - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - /* local export */ - *pmodule = m; - *pme = me; - return JS_RESOLVE_RES_FOUND; - } else { - /* indirect export */ - JSModuleDef *m1; - m1 = m->req_module_entries[me->u.req_module_idx].module; - if (me->local_name == JS_ATOM__star_) { - /* export ns from */ - *pmodule = m; - *pme = me; - return JS_RESOLVE_RES_FOUND; - } else { - return js_resolve_export1(ctx, pmodule, pme, m1, - me->local_name, s); - } - } - } else { - if (export_name != JS_ATOM_default) { - /* not found in direct or indirect exports: try star exports */ - int i; - - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - JSModuleDef *m1, *res_m; - JSExportEntry *res_me; - JSResolveResultEnum ret; - - m1 = m->req_module_entries[se->req_module_idx].module; - ret = js_resolve_export1(ctx, &res_m, &res_me, m1, - export_name, s); - if (ret == JS_RESOLVE_RES_AMBIGUOUS || - ret == JS_RESOLVE_RES_EXCEPTION) { - return ret; - } else if (ret == JS_RESOLVE_RES_FOUND) { - if (*pme != NULL) { - if (*pmodule != res_m || - res_me->local_name != (*pme)->local_name) { - *pmodule = NULL; - *pme = NULL; - return JS_RESOLVE_RES_AMBIGUOUS; - } - } else { - *pmodule = res_m; - *pme = res_me; - } - } - } - if (*pme != NULL) - return JS_RESOLVE_RES_FOUND; - } - return JS_RESOLVE_RES_NOT_FOUND; - } -} - -/* If the return value is JS_RESOLVE_RES_FOUND, return the module - (*pmodule) and the corresponding local export entry - (*pme). Otherwise return (NULL, NULL) */ -static JSResolveResultEnum js_resolve_export(JSContext *ctx, - JSModuleDef **pmodule, - JSExportEntry **pme, - JSModuleDef *m, - JSAtom export_name) -{ - JSResolveState ss, *s = &ss; - int i; - JSResolveResultEnum ret; - - s->array = NULL; - s->size = 0; - s->count = 0; - - ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s); - - for(i = 0; i < s->count; i++) - JS_FreeAtom(ctx, s->array[i].name); - js_free(ctx, s->array); - - return ret; -} - -static void js_resolve_export_throw_error(JSContext *ctx, - JSResolveResultEnum res, - JSModuleDef *m, JSAtom export_name) -{ - char buf1[ATOM_GET_STR_BUF_SIZE]; - char buf2[ATOM_GET_STR_BUF_SIZE]; - switch(res) { - case JS_RESOLVE_RES_EXCEPTION: - break; - default: - case JS_RESOLVE_RES_NOT_FOUND: - JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - case JS_RESOLVE_RES_CIRCULAR: - JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - case JS_RESOLVE_RES_AMBIGUOUS: - JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - } -} - - -typedef enum { - EXPORTED_NAME_AMBIGUOUS, - EXPORTED_NAME_NORMAL, - EXPORTED_NAME_NS, -} ExportedNameEntryEnum; - -typedef struct ExportedNameEntry { - JSAtom export_name; - ExportedNameEntryEnum export_type; - union { - JSExportEntry *me; /* using when the list is built */ - JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ - JSModuleDef *module; /* for EXPORTED_NAME_NS */ - } u; -} ExportedNameEntry; - -typedef struct GetExportNamesState { - JSModuleDef **modules; - int modules_size; - int modules_count; - - ExportedNameEntry *exported_names; - int exported_names_size; - int exported_names_count; -} GetExportNamesState; - -static int find_exported_name(GetExportNamesState *s, JSAtom name) -{ - int i; - for(i = 0; i < s->exported_names_count; i++) { - if (s->exported_names[i].export_name == name) - return i; - } - return -1; -} - -static __exception int get_exported_names(JSContext *ctx, - GetExportNamesState *s, - JSModuleDef *m, BOOL from_star) -{ - ExportedNameEntry *en; - int i, j; - - /* check circular reference */ - for(i = 0; i < s->modules_count; i++) { - if (s->modules[i] == m) - return 0; - } - if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), - &s->modules_size, s->modules_count + 1)) - return -1; - s->modules[s->modules_count++] = m; - - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (from_star && me->export_name == JS_ATOM_default) - continue; - j = find_exported_name(s, me->export_name); - if (j < 0) { - if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), - &s->exported_names_size, - s->exported_names_count + 1)) - return -1; - en = &s->exported_names[s->exported_names_count++]; - en->export_name = me->export_name; - /* avoid a second lookup for simple module exports */ - if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) - en->u.me = NULL; - else - en->u.me = me; - } else { - en = &s->exported_names[j]; - en->u.me = NULL; - } - } - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - JSModuleDef *m1; - m1 = m->req_module_entries[se->req_module_idx].module; - if (get_exported_names(ctx, s, m1, TRUE)) - return -1; - } - return 0; -} - -/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ -static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom) -{ - return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); -} - -static const JSClassExoticMethods js_module_ns_exotic_methods = { - .has_property = js_module_ns_has, -}; - -static int exported_names_cmp(const void *p1, const void *p2, void *opaque) -{ - JSContext *ctx = opaque; - const ExportedNameEntry *me1 = p1; - const ExportedNameEntry *me2 = p2; - JSValue str1, str2; - int ret; - - /* XXX: should avoid allocation memory in atom comparison */ - str1 = JS_AtomToString(ctx, me1->export_name); - str2 = JS_AtomToString(ctx, me2->export_name); - if (JS_IsException(str1) || JS_IsException(str2)) { - /* XXX: raise an error ? */ - ret = 0; - } else { - ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1), - JS_VALUE_GET_STRING(str2)); - } - JS_FreeValue(ctx, str1); - JS_FreeValue(ctx, str2); - return ret; -} - -static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, - void *opaque) -{ - JSModuleDef *m = opaque; - return JS_GetModuleNamespace(ctx, m); -} - -static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) -{ - JSValue obj; - JSObject *p; - GetExportNamesState s_s, *s = &s_s; - int i, ret; - JSProperty *pr; - - obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS); - if (JS_IsException(obj)) - return obj; - p = JS_VALUE_GET_OBJ(obj); - - memset(s, 0, sizeof(*s)); - ret = get_exported_names(ctx, s, m, FALSE); - js_free(ctx, s->modules); - if (ret) - goto fail; - - /* Resolve the exported names. The ambiguous exports are removed */ - for(i = 0; i < s->exported_names_count; i++) { - ExportedNameEntry *en = &s->exported_names[i]; - JSResolveResultEnum res; - JSExportEntry *res_me; - JSModuleDef *res_m; - - if (en->u.me) { - res_me = en->u.me; /* fast case: no resolution needed */ - res_m = m; - res = JS_RESOLVE_RES_FOUND; - } else { - res = js_resolve_export(ctx, &res_m, &res_me, m, - en->export_name); - } - if (res != JS_RESOLVE_RES_FOUND) { - if (res != JS_RESOLVE_RES_AMBIGUOUS) { - js_resolve_export_throw_error(ctx, res, m, en->export_name); - goto fail; - } - en->export_type = EXPORTED_NAME_AMBIGUOUS; - } else { - if (res_me->local_name == JS_ATOM__star_) { - en->export_type = EXPORTED_NAME_NS; - en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module; - } else { - en->export_type = EXPORTED_NAME_NORMAL; - if (res_me->u.local.var_ref) { - en->u.var_ref = res_me->u.local.var_ref; - } else { - JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; - } - } - } - } - - /* sort the exported names */ - rqsort(s->exported_names, s->exported_names_count, - sizeof(s->exported_names[0]), exported_names_cmp, ctx); - - for(i = 0; i < s->exported_names_count; i++) { - ExportedNameEntry *en = &s->exported_names[i]; - switch(en->export_type) { - case EXPORTED_NAME_NORMAL: - { - JSVarRef *var_ref = en->u.var_ref; - pr = add_property(ctx, p, en->export_name, - JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | - JS_PROP_VARREF); - if (!pr) - goto fail; - var_ref->header.ref_count++; - pr->u.var_ref = var_ref; - } - break; - case EXPORTED_NAME_NS: - /* the exported namespace must be created on demand */ - if (JS_DefineAutoInitProperty(ctx, obj, - en->export_name, - JS_AUTOINIT_ID_MODULE_NS, - en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - goto fail; - break; - default: - break; - } - } - - js_free(ctx, s->exported_names); - - JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag, - JS_AtomToString(ctx, JS_ATOM_Module), - 0); - - p->extensible = FALSE; - return obj; - fail: - js_free(ctx, s->exported_names); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) -{ - if (JS_IsUndefined(m->module_ns)) { - JSValue val; - val = js_build_module_ns(ctx, m); - if (JS_IsException(val)) - return JS_EXCEPTION; - m->module_ns = val; - } - return JS_DupValue(ctx, m->module_ns); -} - -/* Load all the required modules for module 'm' */ -static int js_resolve_module(JSContext *ctx, JSModuleDef *m) -{ - int i; - JSModuleDef *m1; - - if (m->resolved) - return 0; -#ifdef DUMP_MODULE_RESOLVE - { - char buf1[ATOM_GET_STR_BUF_SIZE]; - printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); - } -#endif - m->resolved = TRUE; - /* resolve each requested module */ - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, - rme->module_name); - if (!m1) - return -1; - rme->module = m1; - /* already done in js_host_resolve_imported_module() except if - the module was loaded with JS_EvalBinary() */ - if (js_resolve_module(ctx, m1) < 0) - return -1; - } - return 0; -} - -static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) -{ - JSVarRef *var_ref; - var_ref = js_malloc(ctx, sizeof(JSVarRef)); - if (!var_ref) - return NULL; - var_ref->header.ref_count = 1; - if (is_lexical) - var_ref->value = JS_UNINITIALIZED; - else - var_ref->value = JS_UNDEFINED; - var_ref->pvalue = &var_ref->value; - var_ref->is_detached = TRUE; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - return var_ref; -} - -/* Create the <eval> function associated with the module */ -static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) -{ - JSFunctionBytecode *b; - int i; - JSVarRef **var_refs; - JSValue func_obj, bfunc; - JSObject *p; - - bfunc = m->func_obj; - func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_BYTECODE_FUNCTION); - - if (JS_IsException(func_obj)) - return -1; - b = JS_VALUE_GET_PTR(bfunc); - - p = JS_VALUE_GET_OBJ(func_obj); - p->u.func.function_bytecode = b; - b->header.ref_count++; - p->u.func.home_object = NULL; - p->u.func.var_refs = NULL; - if (b->closure_var_count) { - var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); - if (!var_refs) - goto fail; - p->u.func.var_refs = var_refs; - - /* create the global variables. The other variables are - imported from other modules */ - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JSVarRef *var_ref; - if (cv->is_local) { - var_ref = js_create_module_var(ctx, cv->is_lexical); - if (!var_ref) - goto fail; -#ifdef DUMP_MODULE_RESOLVE - printf("local %d: %p\n", i, var_ref); -#endif - var_refs[i] = var_ref; - } - } - } - m->func_obj = func_obj; - JS_FreeValue(ctx, bfunc); - return 0; - fail: - JS_FreeValue(ctx, func_obj); - return -1; -} - -/* must be done before js_link_module() because of cyclic references */ -static int js_create_module_function(JSContext *ctx, JSModuleDef *m) -{ - BOOL is_c_module; - int i; - JSVarRef *var_ref; - - if (m->func_created) - return 0; - - is_c_module = (m->init_func != NULL); - - if (is_c_module) { - /* initialize the exported variables */ - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - var_ref = js_create_module_var(ctx, FALSE); - if (!var_ref) - return -1; - me->u.local.var_ref = var_ref; - } - } - } else { - if (js_create_module_bytecode_function(ctx, m)) - return -1; - } - m->func_created = TRUE; - - /* do it on the dependencies */ - - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (js_create_module_function(ctx, rme->module) < 0) - return -1; - } - - return 0; -} - - -/* Prepare a module to be executed by resolving all the imported - variables. */ -static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, - JSModuleDef **pstack_top, int index) -{ - int i; - JSImportEntry *mi; - JSModuleDef *m1; - JSVarRef **var_refs, *var_ref; - JSObject *p; - BOOL is_c_module; - JSValue ret_val; - - 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("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]; - 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 - { - char buf1[ATOM_GET_STR_BUF_SIZE]; - printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); - } -#endif - /* check the indirect exports */ - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_INDIRECT && - me->local_name != JS_ATOM__star_) { - JSResolveResultEnum ret; - JSExportEntry *res_me; - JSModuleDef *res_m, *m1; - m1 = m->req_module_entries[me->u.req_module_idx].module; - ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); - if (ret != JS_RESOLVE_RES_FOUND) { - js_resolve_export_throw_error(ctx, ret, m, me->export_name); - goto fail; - } - } - } - -#ifdef DUMP_MODULE_RESOLVE - { - printf("exported bindings:\n"); - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - printf(" name="); print_atom(ctx, me->export_name); - printf(" local="); print_atom(ctx, me->local_name); - printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx); - } - } -#endif - - is_c_module = (m->init_func != NULL); - - if (!is_c_module) { - p = JS_VALUE_GET_OBJ(m->func_obj); - var_refs = p->u.func.var_refs; - - for(i = 0; i < m->import_entries_count; i++) { - mi = &m->import_entries[i]; -#ifdef DUMP_MODULE_RESOLVE - printf("import var_idx=%d name=", mi->var_idx); - print_atom(ctx, mi->import_name); - printf(": "); -#endif - m1 = m->req_module_entries[mi->req_module_idx].module; - if (mi->import_name == JS_ATOM__star_) { - JSValue val; - /* name space import */ - val = JS_GetModuleNamespace(ctx, m1); - if (JS_IsException(val)) - goto fail; - set_value(ctx, &var_refs[mi->var_idx]->value, val); -#ifdef DUMP_MODULE_RESOLVE - printf("namespace\n"); -#endif - } else { - JSResolveResultEnum ret; - JSExportEntry *res_me; - JSModuleDef *res_m; - JSObject *p1; - - ret = js_resolve_export(ctx, &res_m, - &res_me, m1, mi->import_name); - if (ret != JS_RESOLVE_RES_FOUND) { - js_resolve_export_throw_error(ctx, ret, m1, mi->import_name); - goto fail; - } - if (res_me->local_name == JS_ATOM__star_) { - JSValue val; - JSModuleDef *m2; - /* name space import from */ - m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; - val = JS_GetModuleNamespace(ctx, m2); - if (JS_IsException(val)) - goto fail; - var_ref = js_create_module_var(ctx, TRUE); - if (!var_ref) { - JS_FreeValue(ctx, val); - goto fail; - } - set_value(ctx, &var_ref->value, val); - var_refs[mi->var_idx] = var_ref; -#ifdef DUMP_MODULE_RESOLVE - printf("namespace from\n"); -#endif - } else { - var_ref = res_me->u.local.var_ref; - if (!var_ref) { - p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; - } - var_ref->header.ref_count++; - var_refs[mi->var_idx] = var_ref; -#ifdef DUMP_MODULE_RESOLVE - printf("local export (var_ref=%p)\n", var_ref); -#endif - } - } - } - - /* keep the exported variables in the module export entries (they - are used when the eval function is deleted and cannot be - initialized before in case imports are exported) */ - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - var_ref = var_refs[me->u.local.var_idx]; - var_ref->header.ref_count++; - me->u.local.var_ref = var_ref; - } - } - - /* initialize the global variables */ - ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); - if (JS_IsException(ret_val)) - goto fail; - 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("js_inner_module_linking done\n"); -#endif - 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) -{ - JSStackFrame *sf; - JSFunctionBytecode *b; - JSObject *p; - /* XXX: currently we just use the filename of the englobing - 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; - while (n_stack_levels-- > 0) { - sf = sf->prev_frame; - if (!sf) - return JS_ATOM_NULL; - } - 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) -{ - return JS_DupAtom(ctx, m->module_name); -} - -JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m) -{ - JSValue obj; - /* allocate meta_obj only if requested to save memory */ - obj = m->meta_obj; - if (JS_IsUndefined(obj)) { - obj = JS_NewObjectProto(ctx, JS_NULL); - if (JS_IsException(obj)) - return JS_EXCEPTION; - m->meta_obj = obj; - } - return JS_DupValue(ctx, obj); -} - -static JSValue js_import_meta(JSContext *ctx) -{ - JSAtom filename; - JSModuleDef *m; - - filename = JS_GetScriptOrModuleName(ctx, 0); - if (filename == JS_ATOM_NULL) - goto fail; - - /* XXX: inefficient, need to add a module or script pointer in - JSFunctionBytecode */ - m = js_find_loaded_module(ctx, filename); - JS_FreeAtom(ctx, filename); - if (!m) { - fail: - JS_ThrowTypeError(ctx, "import.meta not supported in this context"); - return JS_EXCEPTION; - } - return JS_GetImportMeta(ctx, m); -} - -static JSValue JS_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) -{ - JSValue evaluate_promise; - JSModuleDef *m; - JSValue ret, err, func_obj, evaluate_resolving_funcs[2]; - JSValueConst func_data[3]; - - m = js_host_resolve_imported_module(ctx, basename, filename); - if (!m) - goto fail; - - if (js_resolve_module(ctx, m) < 0) { - js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - goto fail; - } - - /* Evaluate the module code */ - 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); - 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_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; -} - -static JSValue js_dynamic_import_job(JSContext *ctx, - int argc, JSValueConst *argv) -{ - JSValueConst *resolving_funcs = argv; - JSValueConst basename_val = argv[2]; - JSValueConst specifier = argv[3]; - const char *basename = NULL, *filename; - JSValue ret, err; - - if (!JS_IsString(basename_val)) { - JS_ThrowTypeError(ctx, "no function filename for import()"); - goto exception; - } - basename = JS_ToCString(ctx, basename_val); - if (!basename) - goto exception; - - filename = JS_ToCString(ctx, specifier); - if (!filename) - goto exception; - - JS_LoadModuleInternal(ctx, basename, filename, - resolving_funcs); - 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); - JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, err); - JS_FreeCString(ctx, basename); - return JS_UNDEFINED; -} - -static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) -{ - JSAtom basename; - JSValue promise, resolving_funcs[2], basename_val; - JSValueConst args[4]; - - basename = JS_GetScriptOrModuleName(ctx, 0); - if (basename == JS_ATOM_NULL) - basename_val = JS_NULL; - else - basename_val = JS_AtomToValue(ctx, basename); - 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; - } - - args[0] = resolving_funcs[0]; - args[1] = resolving_funcs[1]; - args[2] = basename_val; - args[3] = specifier; - - /* cannot run JS_LoadModuleInternal synchronously because it would - cause an unexpected recursion in js_evaluate_module() */ - JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); - - JS_FreeValue(ctx, basename_val); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; -} - -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; - - 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->status == JS_MODULE_STATUS_EVALUATING_ASYNC || - m->status == JS_MODULE_STATUS_EVALUATED) { - if (m->eval_has_exception) { - *pvalue = JS_DupValue(ctx, m->eval_exception); - return -1; - } else { - *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->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; - 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; - } - } - 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->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 { - if (js_execute_sync_module(ctx, m, pvalue) < 0) - return -1; - } - - 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; - } - } - *pvalue = JS_UNDEFINED; - return index; -} - -/* Run the <eval> 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 JSAtom js_parse_from_clause(JSParseState *s) -{ - JSAtom module_name; - if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { - js_parse_error(s, "from clause expected"); - return JS_ATOM_NULL; - } - if (next_token(s)) - return JS_ATOM_NULL; - if (s->token.val != TOK_STRING) { - js_parse_error(s, "string expected"); - return JS_ATOM_NULL; - } - module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); - if (module_name == JS_ATOM_NULL) - return JS_ATOM_NULL; - if (next_token(s)) { - JS_FreeAtom(s->ctx, module_name); - return JS_ATOM_NULL; - } - return module_name; -} - -static __exception int js_parse_export(JSParseState *s) -{ - JSContext *ctx = s->ctx; - JSModuleDef *m = s->cur_func->module; - JSAtom local_name, export_name; - int first_export, idx, i, tok; - JSAtom module_name; - JSExportEntry *me; - - if (next_token(s)) - return -1; - - tok = s->token.val; - if (tok == TOK_CLASS) { - return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED); - } else if (tok == TOK_FUNCTION || - (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) == TOK_FUNCTION)) { - return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num, - JS_PARSE_EXPORT_NAMED, NULL); - } - - if (next_token(s)) - return -1; - - switch(tok) { - case '{': - first_export = m->export_entries_count; - while (s->token.val != '}') { - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; - } - local_name = JS_DupAtom(ctx, s->token.u.ident.atom); - export_name = JS_ATOM_NULL; - if (next_token(s)) - goto fail; - if (token_is_pseudo_keyword(s, JS_ATOM_as)) { - if (next_token(s)) - goto fail; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - goto fail; - } - export_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) { - fail: - JS_FreeAtom(ctx, local_name); - fail1: - JS_FreeAtom(ctx, export_name); - return -1; - } - } else { - export_name = JS_DupAtom(ctx, local_name); - } - me = add_export_entry(s, m, local_name, export_name, - JS_EXPORT_TYPE_LOCAL); - JS_FreeAtom(ctx, local_name); - JS_FreeAtom(ctx, export_name); - if (!me) - return -1; - if (s->token.val != ',') - break; - if (next_token(s)) - return -1; - } - if (js_parse_expect(s, '}')) - return -1; - if (token_is_pseudo_keyword(s, JS_ATOM_from)) { - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - return -1; - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); - if (idx < 0) - return -1; - for(i = first_export; i < m->export_entries_count; i++) { - me = &m->export_entries[i]; - me->export_type = JS_EXPORT_TYPE_INDIRECT; - me->u.req_module_idx = idx; - } - } - break; - case '*': - if (token_is_pseudo_keyword(s, JS_ATOM_as)) { - /* export ns from */ - if (next_token(s)) - return -1; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; - } - export_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) - goto fail1; - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - goto fail1; - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); - if (idx < 0) - goto fail1; - me = add_export_entry(s, m, JS_ATOM__star_, export_name, - JS_EXPORT_TYPE_INDIRECT); - JS_FreeAtom(ctx, export_name); - if (!me) - return -1; - me->u.req_module_idx = idx; - } else { - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - return -1; - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); - if (idx < 0) - return -1; - if (add_star_export_entry(ctx, m, idx) < 0) - return -1; - } - break; - case TOK_DEFAULT: - if (s->token.val == TOK_CLASS) { - return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT); - } else if (s->token.val == TOK_FUNCTION || - (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) == TOK_FUNCTION)) { - return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num, - JS_PARSE_EXPORT_DEFAULT, NULL); - } else { - if (js_parse_assign_expr(s)) - return -1; - } - /* set the name of anonymous functions */ - set_object_name(s, JS_ATOM_default); - - /* store the value in the _default_ global variable and export - it */ - local_name = JS_ATOM__default_; - if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0) - return -1; - emit_op(s, OP_scope_put_var_init); - emit_atom(s, local_name); - emit_u16(s, 0); - - if (!add_export_entry(s, m, local_name, JS_ATOM_default, - JS_EXPORT_TYPE_LOCAL)) - return -1; - break; - case TOK_VAR: - case TOK_LET: - case TOK_CONST: - return js_parse_var(s, TRUE, tok, TRUE); - default: - return js_parse_error(s, "invalid export syntax"); - } - return js_parse_expect_semi(s); -} - -static int add_closure_var(JSContext *ctx, JSFunctionDef *s, - BOOL is_local, BOOL is_arg, - int var_idx, JSAtom var_name, - BOOL is_const, BOOL is_lexical, - JSVarKindEnum var_kind); - -static int add_import(JSParseState *s, JSModuleDef *m, - JSAtom local_name, JSAtom import_name) -{ - JSContext *ctx = s->ctx; - int i, var_idx; - JSImportEntry *mi; - BOOL is_local; - - if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval) - return js_parse_error(s, "invalid import binding"); - - if (local_name != JS_ATOM_default) { - for (i = 0; i < s->cur_func->closure_var_count; i++) { - if (s->cur_func->closure_var[i].var_name == local_name) - return js_parse_error(s, "duplicate import binding"); - } - } - - is_local = (import_name == JS_ATOM__star_); - var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE, - m->import_entries_count, - local_name, TRUE, TRUE, FALSE); - if (var_idx < 0) - return -1; - if (js_resize_array(ctx, (void **)&m->import_entries, - sizeof(JSImportEntry), - &m->import_entries_size, - m->import_entries_count + 1)) - return -1; - mi = &m->import_entries[m->import_entries_count++]; - mi->import_name = JS_DupAtom(ctx, import_name); - mi->var_idx = var_idx; - return 0; -} - -static __exception int js_parse_import(JSParseState *s) -{ - JSContext *ctx = s->ctx; - JSModuleDef *m = s->cur_func->module; - JSAtom local_name, import_name, module_name; - int first_import, i, idx; - - if (next_token(s)) - return -1; - - first_import = m->import_entries_count; - if (s->token.val == TOK_STRING) { - module_name = JS_ValueToAtom(ctx, s->token.u.str.str); - if (module_name == JS_ATOM_NULL) - return -1; - if (next_token(s)) { - JS_FreeAtom(ctx, module_name); - return -1; - } - } else { - if (s->token.val == TOK_IDENT) { - if (s->token.u.ident.is_reserved) { - return js_parse_error_reserved_identifier(s); - } - /* "default" import */ - local_name = JS_DupAtom(ctx, s->token.u.ident.atom); - import_name = JS_ATOM_default; - if (next_token(s)) - goto fail; - if (add_import(s, m, local_name, import_name)) - goto fail; - JS_FreeAtom(ctx, local_name); - - if (s->token.val != ',') - goto end_import_clause; - if (next_token(s)) - return -1; - } - - if (s->token.val == '*') { - /* name space import */ - if (next_token(s)) - return -1; - if (!token_is_pseudo_keyword(s, JS_ATOM_as)) - return js_parse_error(s, "expecting 'as'"); - if (next_token(s)) - return -1; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; - } - local_name = JS_DupAtom(ctx, s->token.u.ident.atom); - import_name = JS_ATOM__star_; - if (next_token(s)) - goto fail; - if (add_import(s, m, local_name, import_name)) - goto fail; - JS_FreeAtom(ctx, local_name); - } else if (s->token.val == '{') { - if (next_token(s)) - return -1; - - while (s->token.val != '}') { - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - return -1; - } - import_name = JS_DupAtom(ctx, s->token.u.ident.atom); - local_name = JS_ATOM_NULL; - if (next_token(s)) - goto fail; - if (token_is_pseudo_keyword(s, JS_ATOM_as)) { - if (next_token(s)) - goto fail; - if (!token_is_ident(s->token.val)) { - js_parse_error(s, "identifier expected"); - goto fail; - } - local_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) { - fail: - JS_FreeAtom(ctx, local_name); - JS_FreeAtom(ctx, import_name); - return -1; - } - } else { - local_name = JS_DupAtom(ctx, import_name); - } - if (add_import(s, m, local_name, import_name)) - goto fail; - JS_FreeAtom(ctx, local_name); - JS_FreeAtom(ctx, import_name); - if (s->token.val != ',') - break; - if (next_token(s)) - return -1; - } - if (js_parse_expect(s, '}')) - return -1; - } - end_import_clause: - module_name = js_parse_from_clause(s); - if (module_name == JS_ATOM_NULL) - return -1; - } - idx = add_req_module_entry(ctx, m, module_name); - JS_FreeAtom(ctx, module_name); - if (idx < 0) - return -1; - for(i = first_import; i < m->import_entries_count; i++) - m->import_entries[i].req_module_idx = idx; - - return js_parse_expect_semi(s); -} - -static __exception int js_parse_source_element(JSParseState *s) -{ - JSFunctionDef *fd = s->cur_func; - int tok; - - if (s->token.val == TOK_FUNCTION || - (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) == TOK_FUNCTION)) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - return -1; - } else if (s->token.val == TOK_EXPORT && fd->module) { - if (js_parse_export(s)) - return -1; - } else if (s->token.val == TOK_IMPORT && fd->module && - ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) { - /* the peek_token is needed to avoid confusion with ImportCall - (dynamic import) or import.meta */ - if (js_parse_import(s)) - return -1; - } else { - if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) - return -1; - } - return 0; -} - -static JSFunctionDef *js_new_function_def(JSContext *ctx, - JSFunctionDef *parent, - BOOL is_eval, - BOOL is_func_expr, - const char *filename, int line_num) -{ - JSFunctionDef *fd; - - fd = js_mallocz(ctx, sizeof(*fd)); - if (!fd) - return NULL; - - fd->ctx = ctx; - init_list_head(&fd->child_list); - - /* insert in parent list */ - fd->parent = parent; - fd->parent_cpool_idx = -1; - if (parent) { - list_add_tail(&fd->link, &parent->child_list); - fd->js_mode = parent->js_mode; - fd->parent_scope_level = parent->scope_level; - } - - fd->is_eval = is_eval; - fd->is_func_expr = is_func_expr; - js_dbuf_init(ctx, &fd->byte_code); - fd->last_opcode_pos = -1; - fd->func_name = JS_ATOM_NULL; - fd->var_object_idx = -1; - fd->arg_var_object_idx = -1; - fd->arguments_var_idx = -1; - fd->arguments_arg_idx = -1; - fd->func_var_idx = -1; - fd->eval_ret_idx = -1; - fd->this_var_idx = -1; - fd->new_target_var_idx = -1; - fd->this_active_func_var_idx = -1; - fd->home_object_var_idx = -1; - - /* XXX: should distinguish arg, var and var object and body scopes */ - fd->scopes = fd->def_scope_array; - fd->scope_size = countof(fd->def_scope_array); - fd->scope_count = 1; - fd->scopes[0].first = -1; - fd->scopes[0].parent = -1; - fd->scope_level = 0; /* 0: var/arg scope */ - fd->scope_first = -1; - fd->body_scope = -1; - - fd->filename = JS_NewAtom(ctx, filename); - fd->line_num = line_num; - - js_dbuf_init(ctx, &fd->pc2line); - //fd->pc2line_last_line_num = line_num; - //fd->pc2line_last_pc = 0; - fd->last_opcode_line_num = line_num; - - return fd; -} - -static void free_bytecode_atoms(JSRuntime *rt, - const uint8_t *bc_buf, int bc_len, - BOOL use_short_opcodes) -{ - int pos, len, op; - JSAtom atom; - const JSOpCode *oi; - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - if (use_short_opcodes) - oi = &short_opcode_info(op); - else - oi = &opcode_info[op]; - - len = oi->size; - switch(oi->fmt) { - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - atom = get_u32(bc_buf + pos + 1); - JS_FreeAtomRT(rt, atom); - break; - default: - break; - } - pos += len; - } -} - -static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) -{ - int i; - struct list_head *el, *el1; - - /* free the child functions */ - list_for_each_safe(el, el1, &fd->child_list) { - JSFunctionDef *fd1; - fd1 = list_entry(el, JSFunctionDef, link); - js_free_function_def(ctx, fd1); - } - - free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size, - fd->use_short_opcodes); - dbuf_free(&fd->byte_code); - js_free(ctx, fd->jump_slots); - js_free(ctx, fd->label_slots); - js_free(ctx, fd->line_number_slots); - - for(i = 0; i < fd->cpool_count; i++) { - JS_FreeValue(ctx, fd->cpool[i]); - } - js_free(ctx, fd->cpool); - - JS_FreeAtom(ctx, fd->func_name); - - for(i = 0; i < fd->var_count; i++) { - JS_FreeAtom(ctx, fd->vars[i].var_name); - } - js_free(ctx, fd->vars); - for(i = 0; i < fd->arg_count; i++) { - JS_FreeAtom(ctx, fd->args[i].var_name); - } - js_free(ctx, fd->args); - - for(i = 0; i < fd->global_var_count; i++) { - JS_FreeAtom(ctx, fd->global_vars[i].var_name); - } - js_free(ctx, fd->global_vars); - - for(i = 0; i < fd->closure_var_count; i++) { - JSClosureVar *cv = &fd->closure_var[i]; - JS_FreeAtom(ctx, cv->var_name); - } - js_free(ctx, fd->closure_var); - - if (fd->scopes != fd->def_scope_array) - js_free(ctx, fd->scopes); - - JS_FreeAtom(ctx, fd->filename); - dbuf_free(&fd->pc2line); - - js_free(ctx, fd->source); - - if (fd->parent) { - /* remove in parent list */ - list_del(&fd->link); - } - js_free(ctx, fd); -} - -#ifdef DUMP_BYTECODE -static const char *skip_lines(const char *p, int n) { - while (n-- > 0 && *p) { - while (*p && *p++ != '\n') - continue; - } - return p; -} - -static void print_lines(const char *source, int line, int line1) { - const char *s = source; - const char *p = skip_lines(s, line); - if (*p) { - while (line++ < line1) { - p = skip_lines(s = p, 1); - printf(";; %.*s", (int)(p - s), s); - if (!*p) { - if (p[-1] != '\n') - printf("\n"); - break; - } - } - } -} - -static void dump_byte_code(JSContext *ctx, int pass, - const uint8_t *tab, int len, - const JSVarDef *args, int arg_count, - const JSVarDef *vars, int var_count, - const JSClosureVar *closure_var, int closure_var_count, - const JSValue *cpool, uint32_t cpool_count, - const char *source, int line_num, - const LabelSlot *label_slots, JSFunctionBytecode *b) -{ - const JSOpCode *oi; - int pos, pos_next, op, size, idx, addr, line, line1, in_source; - uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits)); - BOOL use_short_opcodes = (b != NULL); - - /* scan for jump targets */ - for (pos = 0; pos < len; pos = pos_next) { - op = tab[pos]; - if (use_short_opcodes) - oi = &short_opcode_info(op); - else - oi = &opcode_info[op]; - pos_next = pos + oi->size; - if (op < OP_COUNT) { - switch (oi->fmt) { -#if SHORT_OPCODES - case OP_FMT_label8: - pos++; - addr = (int8_t)tab[pos]; - goto has_addr; - case OP_FMT_label16: - pos++; - addr = (int16_t)get_u16(tab + pos); - goto has_addr; -#endif - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - pos += 4; - /* fall thru */ - case OP_FMT_label: - case OP_FMT_label_u16: - pos++; - addr = get_u32(tab + pos); - goto has_addr; - has_addr: - if (pass == 1) - addr = label_slots[addr].pos; - if (pass == 2) - addr = label_slots[addr].pos2; - if (pass == 3) - addr += pos; - if (addr >= 0 && addr < len) - bits[addr] |= 1; - break; - } - } - } - in_source = 0; - if (source) { - /* Always print first line: needed if single line */ - print_lines(source, 0, 1); - in_source = 1; - } - line1 = line = 1; - pos = 0; - while (pos < len) { - op = tab[pos]; - if (source) { - if (b) { - line1 = find_line_num(ctx, b, pos) - line_num + 1; - } else if (op == OP_line_num) { - line1 = get_u32(tab + pos + 1) - line_num + 1; - } - if (line1 > line) { - if (!in_source) - printf("\n"); - in_source = 1; - print_lines(source, line, line1); - line = line1; - //bits[pos] |= 2; - } - } - if (in_source) - printf("\n"); - in_source = 0; - if (op >= OP_COUNT) { - printf("invalid opcode (0x%02x)\n", op); - pos++; - continue; - } - if (use_short_opcodes) - oi = &short_opcode_info(op); - else - oi = &opcode_info[op]; - size = oi->size; - if (pos + size > len) { - printf("truncated opcode (0x%02x)\n", op); - break; - } -#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16) - { - int i, x, x0; - x = x0 = printf("%5d ", pos); - for (i = 0; i < size; i++) { - if (i == 6) { - printf("\n%*s", x = x0, ""); - } - x += printf(" %02X", tab[pos + i]); - } - printf("%*s", x0 + 20 - x, ""); - } -#endif - if (bits[pos]) { - printf("%5d: ", pos); - } else { - printf(" "); - } - printf("%s", oi->name); - pos++; - switch(oi->fmt) { - case OP_FMT_none_int: - printf(" %d", op - OP_push_0); - break; - case OP_FMT_npopx: - printf(" %d", op - OP_call0); - break; - case OP_FMT_u8: - printf(" %u", get_u8(tab + pos)); - break; - case OP_FMT_i8: - printf(" %d", get_i8(tab + pos)); - break; - case OP_FMT_u16: - case OP_FMT_npop: - printf(" %u", get_u16(tab + pos)); - break; - case OP_FMT_npop_u16: - printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2)); - break; - case OP_FMT_i16: - printf(" %d", get_i16(tab + pos)); - break; - case OP_FMT_i32: - printf(" %d", get_i32(tab + pos)); - break; - case OP_FMT_u32: - printf(" %u", get_u32(tab + pos)); - break; -#if SHORT_OPCODES - case OP_FMT_label8: - addr = get_i8(tab + pos); - goto has_addr1; - case OP_FMT_label16: - addr = get_i16(tab + pos); - goto has_addr1; -#endif - case OP_FMT_label: - addr = get_u32(tab + pos); - goto has_addr1; - has_addr1: - if (pass == 1) - printf(" %u:%u", addr, label_slots[addr].pos); - if (pass == 2) - printf(" %u:%u", addr, label_slots[addr].pos2); - if (pass == 3) - printf(" %u", addr + pos); - break; - case OP_FMT_label_u16: - addr = get_u32(tab + pos); - if (pass == 1) - printf(" %u:%u", addr, label_slots[addr].pos); - if (pass == 2) - printf(" %u:%u", addr, label_slots[addr].pos2); - if (pass == 3) - printf(" %u", addr + pos); - printf(",%u", get_u16(tab + pos + 4)); - break; -#if SHORT_OPCODES - case OP_FMT_const8: - idx = get_u8(tab + pos); - goto has_pool_idx; -#endif - case OP_FMT_const: - idx = get_u32(tab + pos); - goto has_pool_idx; - has_pool_idx: - printf(" %u: ", idx); - if (idx < cpool_count) { - JS_DumpValue(ctx, cpool[idx]); - } - break; - case OP_FMT_atom: - printf(" "); - print_atom(ctx, get_u32(tab + pos)); - break; - case OP_FMT_atom_u8: - printf(" "); - print_atom(ctx, get_u32(tab + pos)); - printf(",%d", get_u8(tab + pos + 4)); - break; - case OP_FMT_atom_u16: - printf(" "); - print_atom(ctx, get_u32(tab + pos)); - printf(",%d", get_u16(tab + pos + 4)); - break; - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - printf(" "); - print_atom(ctx, get_u32(tab + pos)); - addr = get_u32(tab + pos + 4); - if (pass == 1) - printf(",%u:%u", addr, label_slots[addr].pos); - if (pass == 2) - printf(",%u:%u", addr, label_slots[addr].pos2); - if (pass == 3) - printf(",%u", addr + pos + 4); - if (oi->fmt == OP_FMT_atom_label_u8) - printf(",%u", get_u8(tab + pos + 8)); - else - printf(",%u", get_u16(tab + pos + 8)); - break; - case OP_FMT_none_loc: - idx = (op - OP_get_loc0) % 4; - goto has_loc; - case OP_FMT_loc8: - idx = get_u8(tab + pos); - goto has_loc; - case OP_FMT_loc: - idx = get_u16(tab + pos); - has_loc: - printf(" %d: ", idx); - if (idx < var_count) { - print_atom(ctx, vars[idx].var_name); - } - break; - case OP_FMT_none_arg: - idx = (op - OP_get_arg0) % 4; - goto has_arg; - case OP_FMT_arg: - idx = get_u16(tab + pos); - has_arg: - printf(" %d: ", idx); - if (idx < arg_count) { - print_atom(ctx, args[idx].var_name); - } - break; - case OP_FMT_none_var_ref: - idx = (op - OP_get_var_ref0) % 4; - goto has_var_ref; - case OP_FMT_var_ref: - idx = get_u16(tab + pos); - has_var_ref: - printf(" %d: ", idx); - if (idx < closure_var_count) { - print_atom(ctx, closure_var[idx].var_name); - } - break; - default: - break; - } - printf("\n"); - pos += oi->size - 1; - } - if (source) { - if (!in_source) - printf("\n"); - print_lines(source, line, INT32_MAX); - } - js_free(ctx, bits); -} - -static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len, - int line_num) -{ - const uint8_t *p_end, *p_next, *p; - int pc, v; - unsigned int op; - - if (len <= 0) - return; - - printf("%5s %5s\n", "PC", "LINE"); - - p = buf; - p_end = buf + len; - pc = 0; - while (p < p_end) { - op = *p++; - if (op == 0) { - v = unicode_from_utf8(p, p_end - p, &p_next); - if (v < 0) - goto fail; - pc += v; - p = p_next; - v = unicode_from_utf8(p, p_end - p, &p_next); - if (v < 0) { - fail: - printf("invalid pc2line encode pos=%d\n", (int)(p - buf)); - return; - } - if (!(v & 1)) { - v = v >> 1; - } else { - v = -(v >> 1) - 1; - } - line_num += v; - p = p_next; - } else { - op -= PC2LINE_OP_FIRST; - pc += (op / PC2LINE_RANGE); - line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE; - } - printf("%5d %5d\n", pc, line_num); - } -} - -static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b) -{ - int i; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - const char *str; - - if (b->has_debug && b->debug.filename != JS_ATOM_NULL) { - str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename); - printf("%s:%d: ", str, b->debug.line_num); - } - - str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name); - printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str); - if (b->js_mode) { - printf(" mode:"); - if (b->js_mode & JS_MODE_STRICT) - printf(" strict"); -#ifdef CONFIG_BIGNUM - if (b->js_mode & JS_MODE_MATH) - printf(" math"); -#endif - printf("\n"); - } - if (b->arg_count && b->vardefs) { - printf(" args:"); - for(i = 0; i < b->arg_count; i++) { - printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), - b->vardefs[i].var_name)); - } - printf("\n"); - } - if (b->var_count && b->vardefs) { - printf(" locals:\n"); - for(i = 0; i < b->var_count; i++) { - JSVarDef *vd = &b->vardefs[b->arg_count + i]; - printf("%5d: %s %s", i, - vd->var_kind == JS_VAR_CATCH ? "catch" : - (vd->var_kind == JS_VAR_FUNCTION_DECL || - vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" : - vd->is_const ? "const" : - vd->is_lexical ? "let" : "var", - JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name)); - if (vd->scope_level) - printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next); - printf("\n"); - } - } - if (b->closure_var_count) { - printf(" closure vars:\n"); - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - printf("%5d: %s %s:%s%d %s\n", i, - JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name), - cv->is_local ? "local" : "parent", - cv->is_arg ? "arg" : "loc", cv->var_idx, - cv->is_const ? "const" : - cv->is_lexical ? "let" : "var"); - } - } - printf(" stack_size: %d\n", b->stack_size); - printf(" opcodes:\n"); - dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len, - b->vardefs, b->arg_count, - b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count, - b->closure_var, b->closure_var_count, - b->cpool, b->cpool_count, - b->has_debug ? b->debug.source : NULL, - b->has_debug ? b->debug.line_num : -1, NULL, b); -#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32) - if (b->has_debug) - dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num); -#endif - printf("\n"); -} -#endif - -static int add_closure_var(JSContext *ctx, JSFunctionDef *s, - BOOL is_local, BOOL is_arg, - int var_idx, JSAtom var_name, - BOOL is_const, BOOL is_lexical, - JSVarKindEnum var_kind) -{ - JSClosureVar *cv; - - /* the closure variable indexes are currently stored on 16 bits */ - if (s->closure_var_count >= JS_MAX_LOCAL_VARS) { - JS_ThrowInternalError(ctx, "too many closure variables"); - return -1; - } - - if (js_resize_array(ctx, (void **)&s->closure_var, - sizeof(s->closure_var[0]), - &s->closure_var_size, s->closure_var_count + 1)) - return -1; - cv = &s->closure_var[s->closure_var_count++]; - cv->is_local = is_local; - cv->is_arg = is_arg; - cv->is_const = is_const; - cv->is_lexical = is_lexical; - cv->var_kind = var_kind; - cv->var_idx = var_idx; - cv->var_name = JS_DupAtom(ctx, var_name); - return s->closure_var_count - 1; -} - -static int find_closure_var(JSContext *ctx, JSFunctionDef *s, - JSAtom var_name) -{ - int i; - for(i = 0; i < s->closure_var_count; i++) { - JSClosureVar *cv = &s->closure_var[i]; - if (cv->var_name == var_name) - return i; - } - return -1; -} - -/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a - local variable (is_local = TRUE) or a closure (is_local = FALSE) in - 'fd' */ -static int get_closure_var2(JSContext *ctx, JSFunctionDef *s, - JSFunctionDef *fd, BOOL is_local, - BOOL is_arg, int var_idx, JSAtom var_name, - BOOL is_const, BOOL is_lexical, - JSVarKindEnum var_kind) -{ - int i; - - if (fd != s->parent) { - var_idx = get_closure_var2(ctx, s->parent, fd, is_local, - is_arg, var_idx, var_name, - is_const, is_lexical, var_kind); - if (var_idx < 0) - return -1; - is_local = FALSE; - } - for(i = 0; i < s->closure_var_count; i++) { - JSClosureVar *cv = &s->closure_var[i]; - if (cv->var_idx == var_idx && cv->is_arg == is_arg && - cv->is_local == is_local) - return i; - } - return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name, - is_const, is_lexical, var_kind); -} - -static int get_closure_var(JSContext *ctx, JSFunctionDef *s, - JSFunctionDef *fd, BOOL is_arg, - int var_idx, JSAtom var_name, - BOOL is_const, BOOL is_lexical, - JSVarKindEnum var_kind) -{ - return get_closure_var2(ctx, s, fd, TRUE, is_arg, - var_idx, var_name, is_const, is_lexical, - var_kind); -} - -static int get_with_scope_opcode(int op) -{ - if (op == OP_scope_get_var_undef) - return OP_with_get_var; - else - return OP_with_get_var + (op - OP_scope_get_var); -} - -static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos) -{ - int opcode = bc_buf[pos]; - return (bc_buf[pos + 1] == OP_put_ref_value && - (opcode == OP_insert3 || - opcode == OP_perm4 || - opcode == OP_nop || - opcode == OP_rot3l)); -} - -static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos) -{ - int opcode = bc_buf[pos]; - return (bc_buf[pos + 1] == OP_put_ref_value && - (opcode == OP_insert3 || - opcode == OP_perm4 || - opcode == OP_nop || - opcode == OP_rot3l)); -} - -static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s, - DynBuf *bc, uint8_t *bc_buf, - LabelSlot *ls, int pos_next, - int get_op, int var_idx) -{ - int label_pos, end_pos, pos; - - /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)` - but only if expr does not modify `a`. - should scan the code between pos_next and label_pos - for operations that can potentially change `a`: - OP_scope_make_ref(a), function calls, jumps and gosub. - */ - /* replace the reference get/put with normal variable - accesses */ - if (bc_buf[pos_next] == OP_get_ref_value) { - dbuf_putc(bc, get_op); - dbuf_put_u16(bc, var_idx); - pos_next++; - } - /* remove the OP_label to make room for replacement */ - /* label should have a refcount of 0 anyway */ - /* XXX: should avoid this patch by inserting nops in phase 1 */ - label_pos = ls->pos; - pos = label_pos - 5; - assert(bc_buf[pos] == OP_label); - /* label points to an instruction pair: - - insert3 / put_ref_value - - perm4 / put_ref_value - - rot3l / put_ref_value - - nop / put_ref_value - */ - end_pos = label_pos + 2; - if (bc_buf[label_pos] == OP_insert3) - bc_buf[pos++] = OP_dup; - bc_buf[pos] = get_op + 1; - put_u16(bc_buf + pos + 1, var_idx); - pos += 3; - /* pad with OP_nop */ - while (pos < end_pos) - bc_buf[pos++] = OP_nop; - return pos_next; -} - -static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s, - DynBuf *bc, uint8_t *bc_buf, - LabelSlot *ls, int pos_next, - JSAtom var_name) -{ - int label_pos, end_pos, pos, op; - BOOL is_strict; - is_strict = ((s->js_mode & JS_MODE_STRICT) != 0); - - /* replace the reference get/put with normal variable - accesses */ - if (is_strict) { - /* need to check if the variable exists before evaluating the right - expression */ - /* XXX: need an extra OP_true if destructuring an array */ - dbuf_putc(bc, OP_check_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - } else { - /* XXX: need 2 extra OP_true if destructuring an array */ - } - if (bc_buf[pos_next] == OP_get_ref_value) { - dbuf_putc(bc, OP_get_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - pos_next++; - } - /* remove the OP_label to make room for replacement */ - /* label should have a refcount of 0 anyway */ - /* XXX: should have emitted several OP_nop to avoid this kludge */ - label_pos = ls->pos; - pos = label_pos - 5; - assert(bc_buf[pos] == OP_label); - end_pos = label_pos + 2; - op = bc_buf[label_pos]; - if (is_strict) { - if (op != OP_nop) { - switch(op) { - case OP_insert3: - op = OP_insert2; - break; - case OP_perm4: - op = OP_perm3; - break; - case OP_rot3l: - op = OP_swap; - break; - default: - abort(); - } - bc_buf[pos++] = op; - } - } else { - if (op == OP_insert3) - bc_buf[pos++] = OP_dup; - } - if (is_strict) { - bc_buf[pos] = OP_put_var_strict; - /* XXX: need 1 extra OP_drop if destructuring an array */ - } else { - bc_buf[pos] = OP_put_var; - /* XXX: need 2 extra OP_drop if destructuring an array */ - } - put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name)); - pos += 5; - /* pad with OP_nop */ - while (pos < end_pos) - bc_buf[pos++] = OP_nop; - return pos_next; -} - -static int add_var_this(JSContext *ctx, JSFunctionDef *fd) -{ - int idx; - idx = add_var(ctx, fd, JS_ATOM_this); - if (idx >= 0 && fd->is_derived_class_constructor) { - JSVarDef *vd = &fd->vars[idx]; - /* XXX: should have is_this flag or var type */ - vd->is_lexical = 1; /* used to trigger 'uninitialized' checks - in a derived class constructor */ - } - return idx; -} - -static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, - JSAtom var_name) -{ - int var_idx; - - if (!s->has_this_binding) - return -1; - switch(var_name) { - case JS_ATOM_home_object: - /* 'home_object' pseudo variable */ - if (s->home_object_var_idx < 0) - s->home_object_var_idx = add_var(ctx, s, var_name); - var_idx = s->home_object_var_idx; - break; - case JS_ATOM_this_active_func: - /* 'this.active_func' pseudo variable */ - if (s->this_active_func_var_idx < 0) - s->this_active_func_var_idx = add_var(ctx, s, var_name); - var_idx = s->this_active_func_var_idx; - break; - case JS_ATOM_new_target: - /* 'new.target' pseudo variable */ - if (s->new_target_var_idx < 0) - s->new_target_var_idx = add_var(ctx, s, var_name); - var_idx = s->new_target_var_idx; - break; - case JS_ATOM_this: - /* 'this' pseudo variable */ - if (s->this_var_idx < 0) - s->this_var_idx = add_var_this(ctx, s); - var_idx = s->this_var_idx; - break; - default: - var_idx = -1; - break; - } - return var_idx; -} - -/* test if 'var_name' is in the variable object on the stack. If is it - the case, handle it and jump to 'label_done' */ -static void var_object_test(JSContext *ctx, JSFunctionDef *s, - JSAtom var_name, int op, DynBuf *bc, - int *plabel_done, BOOL is_with) -{ - 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); - 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, - DynBuf *bc, uint8_t *bc_buf, - LabelSlot *ls, int pos_next) -{ - int idx, var_idx, is_put; - int label_done; - JSFunctionDef *fd; - JSVarDef *vd; - BOOL is_pseudo_var, is_arg_scope; - - label_done = -1; - - /* XXX: could be simpler to use a specific function to - resolve the pseudo variables */ - is_pseudo_var = (var_name == JS_ATOM_home_object || - var_name == JS_ATOM_this_active_func || - var_name == JS_ATOM_new_target || - var_name == JS_ATOM_this); - - /* resolve local scoped variables */ - var_idx = -1; - for (idx = s->scopes[scope_level].first; idx >= 0;) { - vd = &s->vars[idx]; - if (vd->var_name == var_name) { - if (op == OP_scope_put_var || op == OP_scope_make_ref) { - if (vd->is_const) { - dbuf_putc(bc, OP_throw_error); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_RO); - goto done; - } - } - var_idx = idx; - break; - } else - if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { - dbuf_putc(bc, OP_get_loc); - dbuf_put_u16(bc, idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, 1); - } - idx = vd->scope_next; - } - is_arg_scope = (idx == ARG_SCOPE_END); - if (var_idx < 0) { - /* argument scope: variables are not visible but pseudo - variables are visible */ - if (!is_arg_scope) { - var_idx = find_var(ctx, s, var_name); - } - - if (var_idx < 0 && is_pseudo_var) - var_idx = resolve_pseudo_var(ctx, s, var_name); - - if (var_idx < 0 && var_name == JS_ATOM_arguments && - s->has_arguments_binding) { - /* 'arguments' pseudo variable */ - var_idx = add_arguments_var(ctx, s); - } - if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) { - /* add a new variable with the function name */ - var_idx = add_func_var(ctx, s, var_name); - } - } - if (var_idx >= 0) { - if ((op == OP_scope_put_var || op == OP_scope_make_ref) && - !(var_idx & ARGUMENT_VAR_OFFSET) && - s->vars[var_idx].is_const) { - /* only happens when assigning a function expression name - in strict mode */ - dbuf_putc(bc, OP_throw_error); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_RO); - goto done; - } - /* OP_scope_put_var_init is only used to initialize a - lexical variable, so it is never used in a with or var object. It - can be used with a closure (module global variable case). */ - switch (op) { - case OP_scope_make_ref: - if (!(var_idx & ARGUMENT_VAR_OFFSET) && - s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { - /* Create a dummy object reference for the func_var */ - dbuf_putc(bc, OP_object); - dbuf_putc(bc, OP_get_loc); - dbuf_put_u16(bc, var_idx); - dbuf_putc(bc, OP_define_field); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, OP_push_atom_value); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - } else - if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) { - int get_op; - if (var_idx & ARGUMENT_VAR_OFFSET) { - get_op = OP_get_arg; - var_idx -= ARGUMENT_VAR_OFFSET; - } else { - if (s->vars[var_idx].is_lexical) - get_op = OP_get_loc_check; - else - get_op = OP_get_loc; - } - pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, - pos_next, get_op, var_idx); - } else { - /* Create a dummy object with a named slot that is - a reference to the local variable */ - if (var_idx & ARGUMENT_VAR_OFFSET) { - dbuf_putc(bc, OP_make_arg_ref); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); - } else { - dbuf_putc(bc, OP_make_loc_ref); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_put_u16(bc, var_idx); - } - } - break; - case OP_scope_get_ref: - dbuf_putc(bc, OP_undefined); - /* fall thru */ - case OP_scope_get_var_checkthis: - case OP_scope_get_var_undef: - case OP_scope_get_var: - case OP_scope_put_var: - case OP_scope_put_var_init: - is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init); - if (var_idx & ARGUMENT_VAR_OFFSET) { - dbuf_putc(bc, OP_get_arg + is_put); - dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); - } else { - if (is_put) { - if (s->vars[var_idx].is_lexical) { - if (op == OP_scope_put_var_init) { - /* 'this' can only be initialized once */ - if (var_name == JS_ATOM_this) - dbuf_putc(bc, OP_put_loc_check_init); - else - dbuf_putc(bc, OP_put_loc); - } else { - dbuf_putc(bc, OP_put_loc_check); - } - } else { - dbuf_putc(bc, OP_put_loc); - } - } else { - if (s->vars[var_idx].is_lexical) { - 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); - } - } - dbuf_put_u16(bc, var_idx); - } - break; - case OP_scope_delete_var: - dbuf_putc(bc, OP_push_false); - break; - } - goto done; - } - /* check eval object */ - if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) { - dbuf_putc(bc, OP_get_loc); - dbuf_put_u16(bc, s->var_object_idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, 0); - } - /* check eval object in argument scope */ - if (s->arg_var_object_idx >= 0 && !is_pseudo_var) { - dbuf_putc(bc, OP_get_loc); - dbuf_put_u16(bc, s->arg_var_object_idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, 0); - } - - /* check parent scopes */ - for (fd = s; fd->parent;) { - scope_level = fd->parent_scope_level; - fd = fd->parent; - for (idx = fd->scopes[scope_level].first; idx >= 0;) { - vd = &fd->vars[idx]; - if (vd->var_name == var_name) { - if (op == OP_scope_put_var || op == OP_scope_make_ref) { - if (vd->is_const) { - dbuf_putc(bc, OP_throw_error); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_RO); - goto done; - } - } - var_idx = idx; - break; - } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { - vd->is_captured = 1; - idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); - if (idx >= 0) { - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, 1); - } - } - idx = vd->scope_next; - } - 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) - break; - } - if (is_pseudo_var) { - var_idx = resolve_pseudo_var(ctx, fd, var_name); - if (var_idx >= 0) - break; - } - if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { - var_idx = add_arguments_var(ctx, fd); - break; - } - if (fd->is_func_expr && fd->func_name == var_name) { - /* add a new variable with the function name */ - var_idx = add_func_var(ctx, fd, var_name); - break; - } - - /* check eval object */ - if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { - vd = &fd->vars[fd->var_object_idx]; - vd->is_captured = 1; - idx = get_closure_var(ctx, s, fd, FALSE, - fd->var_object_idx, vd->var_name, - FALSE, FALSE, JS_VAR_NORMAL); - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, 0); - } - - /* check eval object in argument scope */ - if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { - vd = &fd->vars[fd->arg_var_object_idx]; - vd->is_captured = 1; - idx = get_closure_var(ctx, s, fd, FALSE, - fd->arg_var_object_idx, vd->var_name, - FALSE, FALSE, JS_VAR_NORMAL); - dbuf_putc(bc, OP_get_var_ref); - 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 */ - } - - /* check direct eval scope (in the closure of the eval function - which is necessarily at the top level) */ - if (!fd) - fd = s; - if (var_idx < 0 && fd->is_eval) { - int idx1; - for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) { - JSClosureVar *cv = &fd->closure_var[idx1]; - if (var_name == cv->var_name) { - if (fd != s) { - idx = get_closure_var2(ctx, s, fd, - FALSE, - cv->is_arg, idx1, - cv->var_name, cv->is_const, - cv->is_lexical, cv->var_kind); - } else { - idx = idx1; - } - goto has_idx; - } else if ((cv->var_name == JS_ATOM__var_ || - cv->var_name == JS_ATOM__arg_var_ || - cv->var_name == JS_ATOM__with_) && !is_pseudo_var) { - int is_with = (cv->var_name == JS_ATOM__with_); - if (fd != s) { - idx = get_closure_var2(ctx, s, fd, - FALSE, - cv->is_arg, idx1, - cv->var_name, FALSE, FALSE, - JS_VAR_NORMAL); - } else { - idx = idx1; - } - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); - } - } - } - - if (var_idx >= 0) { - /* find the corresponding closure variable */ - if (var_idx & ARGUMENT_VAR_OFFSET) { - fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1; - idx = get_closure_var(ctx, s, fd, - TRUE, var_idx - ARGUMENT_VAR_OFFSET, - var_name, FALSE, FALSE, JS_VAR_NORMAL); - } else { - fd->vars[var_idx].is_captured = 1; - idx = get_closure_var(ctx, s, fd, - FALSE, var_idx, - var_name, - fd->vars[var_idx].is_const, - fd->vars[var_idx].is_lexical, - fd->vars[var_idx].var_kind); - } - if (idx >= 0) { - has_idx: - if ((op == OP_scope_put_var || op == OP_scope_make_ref) && - s->closure_var[idx].is_const) { - dbuf_putc(bc, OP_throw_error); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_RO); - goto done; - } - switch (op) { - case OP_scope_make_ref: - if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { - /* Create a dummy object reference for the func_var */ - dbuf_putc(bc, OP_object); - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - dbuf_putc(bc, OP_define_field); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, OP_push_atom_value); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - } else - if (label_done == -1 && - can_opt_put_ref_value(bc_buf, ls->pos)) { - int get_op; - if (s->closure_var[idx].is_lexical) - get_op = OP_get_var_ref_check; - else - get_op = OP_get_var_ref; - pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, - pos_next, - get_op, idx); - } else { - /* Create a dummy object with a named slot that is - a reference to the closure variable */ - dbuf_putc(bc, OP_make_var_ref_ref); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_put_u16(bc, idx); - } - break; - case OP_scope_get_ref: - /* XXX: should create a dummy object with a named slot that is - a reference to the closure variable */ - dbuf_putc(bc, OP_undefined); - /* fall thru */ - case OP_scope_get_var_undef: - case OP_scope_get_var: - case OP_scope_put_var: - case OP_scope_put_var_init: - is_put = (op == OP_scope_put_var || - op == OP_scope_put_var_init); - if (is_put) { - if (s->closure_var[idx].is_lexical) { - if (op == OP_scope_put_var_init) { - /* 'this' can only be initialized once */ - if (var_name == JS_ATOM_this) - dbuf_putc(bc, OP_put_var_ref_check_init); - else - dbuf_putc(bc, OP_put_var_ref); - } else { - dbuf_putc(bc, OP_put_var_ref_check); - } - } else { - dbuf_putc(bc, OP_put_var_ref); - } - } else { - if (s->closure_var[idx].is_lexical) { - dbuf_putc(bc, OP_get_var_ref_check); - } else { - dbuf_putc(bc, OP_get_var_ref); - } - } - dbuf_put_u16(bc, idx); - break; - case OP_scope_delete_var: - dbuf_putc(bc, OP_push_false); - break; - } - goto done; - } - } - - /* global variable access */ - - switch (op) { - case OP_scope_make_ref: - if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) { - pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls, - pos_next, var_name); - } else { - dbuf_putc(bc, OP_make_var_ref); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - } - break; - case OP_scope_get_ref: - /* XXX: should create a dummy object with a named slot that is - a reference to the global variable */ - dbuf_putc(bc, OP_undefined); - dbuf_putc(bc, OP_get_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - break; - case OP_scope_get_var_undef: - case OP_scope_get_var: - case OP_scope_put_var: - dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - break; - case OP_scope_put_var_init: - dbuf_putc(bc, OP_put_var_init); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - break; - case OP_scope_delete_var: - dbuf_putc(bc, OP_delete_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - break; - } -done: - if (label_done >= 0) { - dbuf_putc(bc, OP_label); - dbuf_put_u32(bc, label_done); - s->label_slots[label_done].pos2 = bc->size; - } - return pos_next; -} - -/* search in all scopes */ -static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd, - JSAtom name, int scope_level) -{ - int idx; - - idx = fd->scopes[scope_level].first; - while (idx >= 0) { - if (fd->vars[idx].var_name == name) - return idx; - idx = fd->vars[idx].scope_next; - } - return -1; -} - -static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx) -{ - /* if the field is not initialized, the error is catched when - accessing it */ - if (is_ref) - dbuf_putc(bc, OP_get_var_ref); - else - dbuf_putc(bc, OP_get_loc); - dbuf_put_u16(bc, idx); -} - -static int resolve_scope_private_field1(JSContext *ctx, - BOOL *pis_ref, int *pvar_kind, - JSFunctionDef *s, - JSAtom var_name, int scope_level) -{ - int idx, var_kind; - JSFunctionDef *fd; - BOOL is_ref; - - fd = s; - is_ref = FALSE; - for(;;) { - idx = find_private_class_field_all(ctx, fd, var_name, scope_level); - if (idx >= 0) { - var_kind = fd->vars[idx].var_kind; - if (is_ref) { - idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name, - TRUE, TRUE, JS_VAR_NORMAL); - if (idx < 0) - return -1; - } - break; - } - scope_level = fd->parent_scope_level; - if (!fd->parent) { - if (fd->is_eval) { - /* closure of the eval function (top level) */ - for (idx = 0; idx < fd->closure_var_count; idx++) { - JSClosureVar *cv = &fd->closure_var[idx]; - if (cv->var_name == var_name) { - var_kind = cv->var_kind; - is_ref = TRUE; - if (fd != s) { - idx = get_closure_var2(ctx, s, fd, - FALSE, - cv->is_arg, idx, - cv->var_name, cv->is_const, - cv->is_lexical, - cv->var_kind); - if (idx < 0) - return -1; - } - goto done; - } - } - } - /* XXX: no line number info */ - JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'", - var_name); - return -1; - } else { - fd = fd->parent; - } - is_ref = TRUE; - } - done: - *pis_ref = is_ref; - *pvar_kind = var_kind; - return idx; -} - -/* return 0 if OK or -1 if the private field could not be resolved */ -static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, - JSAtom var_name, int scope_level, int op, - DynBuf *bc) -{ - int idx, var_kind; - BOOL is_ref; - - idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s, - var_name, scope_level); - if (idx < 0) - return -1; - assert(var_kind != JS_VAR_NORMAL); - switch (op) { - case OP_scope_get_private_field: - case OP_scope_get_private_field2: - switch(var_kind) { - case JS_VAR_PRIVATE_FIELD: - if (op == OP_scope_get_private_field2) - dbuf_putc(bc, OP_dup); - get_loc_or_ref(bc, is_ref, idx); - dbuf_putc(bc, OP_get_private_field); - break; - case JS_VAR_PRIVATE_METHOD: - get_loc_or_ref(bc, is_ref, idx); - dbuf_putc(bc, OP_check_brand); - if (op != OP_scope_get_private_field2) - dbuf_putc(bc, OP_nip); - break; - case JS_VAR_PRIVATE_GETTER: - case JS_VAR_PRIVATE_GETTER_SETTER: - if (op == OP_scope_get_private_field2) - dbuf_putc(bc, OP_dup); - get_loc_or_ref(bc, is_ref, idx); - dbuf_putc(bc, OP_check_brand); - dbuf_putc(bc, OP_call_method); - dbuf_put_u16(bc, 0); - break; - case JS_VAR_PRIVATE_SETTER: - /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_error); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_RO); - break; - default: - abort(); - } - break; - case OP_scope_put_private_field: - switch(var_kind) { - case JS_VAR_PRIVATE_FIELD: - get_loc_or_ref(bc, is_ref, idx); - dbuf_putc(bc, OP_put_private_field); - break; - case JS_VAR_PRIVATE_METHOD: - case JS_VAR_PRIVATE_GETTER: - /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_error); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_RO); - break; - case JS_VAR_PRIVATE_SETTER: - case JS_VAR_PRIVATE_GETTER_SETTER: - { - JSAtom setter_name = get_private_setter_name(ctx, var_name); - if (setter_name == JS_ATOM_NULL) - return -1; - idx = resolve_scope_private_field1(ctx, &is_ref, - &var_kind, s, - setter_name, scope_level); - JS_FreeAtom(ctx, setter_name); - if (idx < 0) - return -1; - assert(var_kind == JS_VAR_PRIVATE_SETTER); - get_loc_or_ref(bc, is_ref, idx); - dbuf_putc(bc, OP_swap); - /* obj func value */ - dbuf_putc(bc, OP_rot3r); - /* value obj func */ - dbuf_putc(bc, OP_check_brand); - dbuf_putc(bc, OP_rot3l); - /* obj func value */ - dbuf_putc(bc, OP_call_method); - dbuf_put_u16(bc, 1); - dbuf_putc(bc, OP_drop); - } - break; - default: - 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(); - } - return 0; -} - -static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, - int scope_level) -{ - int idx; - JSVarDef *vd; - - for (idx = s->scopes[scope_level].first; idx >= 0;) { - vd = &s->vars[idx]; - vd->is_captured = 1; - idx = vd->scope_next; - } -} - -/* XXX: should handle the argument scope generically */ -static BOOL is_var_in_arg_scope(const JSVarDef *vd) -{ - return (vd->var_name == JS_ATOM_home_object || - vd->var_name == JS_ATOM_this_active_func || - vd->var_name == JS_ATOM_new_target || - vd->var_name == JS_ATOM_this || - vd->var_name == JS_ATOM__arg_var_ || - vd->var_kind == JS_VAR_FUNCTION_NAME); -} - -static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) -{ - JSFunctionDef *fd; - JSVarDef *vd; - int i, scope_level, scope_idx; - BOOL has_arguments_binding, has_this_binding, is_arg_scope; - - /* in non strict mode, variables are created in the caller's - environment object */ - if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) { - s->var_object_idx = add_var(ctx, s, JS_ATOM__var_); - if (s->has_parameter_expressions) { - /* an additional variable object is needed for the - argument scope */ - s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_); - } - } - - /* eval can potentially use 'arguments' so we must define it */ - has_this_binding = s->has_this_binding; - if (has_this_binding) { - if (s->this_var_idx < 0) - s->this_var_idx = add_var_this(ctx, s); - if (s->new_target_var_idx < 0) - s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target); - if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0) - s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func); - if (s->has_home_object && s->home_object_var_idx < 0) - s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object); - } - has_arguments_binding = s->has_arguments_binding; - if (has_arguments_binding) { - add_arguments_var(ctx, s); - /* also add an arguments binding in the argument scope to - raise an error if a direct eval in the argument scope tries - to redefine it */ - if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT)) - add_arguments_arg(ctx, s); - } - if (s->is_func_expr && s->func_name != JS_ATOM_NULL) - add_func_var(ctx, s, s->func_name); - - /* eval can use all the variables of the enclosing functions, so - they must be all put in the closure. The closure variables are - ordered by scope. It works only because no closure are created - before. */ - assert(s->is_eval || s->closure_var_count == 0); - - /* XXX: inefficient, but eval performance is less critical */ - fd = s; - for(;;) { - scope_level = fd->parent_scope_level; - fd = fd->parent; - if (!fd) - break; - /* add 'this' if it was not previously added */ - if (!has_this_binding && fd->has_this_binding) { - if (fd->this_var_idx < 0) - fd->this_var_idx = add_var_this(ctx, fd); - if (fd->new_target_var_idx < 0) - fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target); - if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0) - fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func); - if (fd->has_home_object && fd->home_object_var_idx < 0) - fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object); - has_this_binding = TRUE; - } - /* add 'arguments' if it was not previously added */ - if (!has_arguments_binding && fd->has_arguments_binding) { - add_arguments_var(ctx, fd); - has_arguments_binding = TRUE; - } - /* add function name */ - if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL) - add_func_var(ctx, fd, fd->func_name); - - /* add lexical variables */ - scope_idx = fd->scopes[scope_level].first; - while (scope_idx >= 0) { - vd = &fd->vars[scope_idx]; - vd->is_captured = 1; - get_closure_var(ctx, s, fd, FALSE, scope_idx, - vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); - scope_idx = vd->scope_next; - } - is_arg_scope = (scope_idx == ARG_SCOPE_END); - if (!is_arg_scope) { - /* add unscoped variables */ - /* XXX: propagate is_const and var_kind too ? */ - for(i = 0; i < fd->arg_count; i++) { - vd = &fd->args[i]; - if (vd->var_name != JS_ATOM_NULL) { - get_closure_var(ctx, s, fd, - TRUE, i, vd->var_name, FALSE, - vd->is_lexical, JS_VAR_NORMAL); - } - } - for(i = 0; i < fd->var_count; i++) { - vd = &fd->vars[i]; - /* do not close top level last result */ - if (vd->scope_level == 0 && - vd->var_name != JS_ATOM__ret_ && - vd->var_name != JS_ATOM_NULL) { - get_closure_var(ctx, s, fd, - FALSE, i, vd->var_name, FALSE, - vd->is_lexical, JS_VAR_NORMAL); - } - } - } else { - for(i = 0; i < fd->var_count; i++) { - vd = &fd->vars[i]; - /* do not close top level last result */ - if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { - get_closure_var(ctx, s, fd, - FALSE, i, vd->var_name, FALSE, - vd->is_lexical, JS_VAR_NORMAL); - } - } - } - if (fd->is_eval) { - int idx; - /* add direct eval variables (we are necessarily at the - top level) */ - for (idx = 0; idx < fd->closure_var_count; idx++) { - JSClosureVar *cv = &fd->closure_var[idx]; - get_closure_var2(ctx, s, fd, - FALSE, cv->is_arg, - idx, cv->var_name, cv->is_const, - cv->is_lexical, cv->var_kind); - } - } - } -} - -static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, - JSVarDef *vd, int var_idx) -{ - cv->is_local = TRUE; - cv->is_arg = FALSE; - cv->is_const = vd->is_const; - cv->is_lexical = vd->is_lexical; - cv->var_kind = vd->var_kind; - cv->var_idx = var_idx; - cv->var_name = JS_DupAtom(ctx, vd->var_name); -} - -/* for direct eval compilation: add references to the variables of the - calling function */ -static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, - JSFunctionBytecode *b, int scope_idx) -{ - int i, count; - JSVarDef *vd; - BOOL is_arg_scope; - - count = b->arg_count + b->var_count + b->closure_var_count; - s->closure_var = NULL; - s->closure_var_count = 0; - s->closure_var_size = count; - if (count == 0) - return 0; - s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count); - if (!s->closure_var) - return -1; - /* Add lexical variables in scope at the point of evaluation */ - for (i = scope_idx; i >= 0;) { - vd = &b->vardefs[b->arg_count + i]; - if (vd->scope_level > 0) { - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - set_closure_from_var(ctx, cv, vd, i); - } - i = vd->scope_next; - } - is_arg_scope = (i == ARG_SCOPE_END); - if (!is_arg_scope) { - /* Add argument variables */ - for(i = 0; i < b->arg_count; i++) { - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - vd = &b->vardefs[i]; - cv->is_local = TRUE; - cv->is_arg = TRUE; - cv->is_const = FALSE; - cv->is_lexical = FALSE; - cv->var_kind = JS_VAR_NORMAL; - cv->var_idx = i; - cv->var_name = JS_DupAtom(ctx, vd->var_name); - } - /* Add local non lexical variables */ - for(i = 0; i < b->var_count; i++) { - vd = &b->vardefs[b->arg_count + i]; - if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - set_closure_from_var(ctx, cv, vd, i); - } - } - } else { - /* only add pseudo variables */ - for(i = 0; i < b->var_count; i++) { - vd = &b->vardefs[b->arg_count + i]; - if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - set_closure_from_var(ctx, cv, vd, i); - } - } - } - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv0 = &b->closure_var[i]; - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - cv->is_local = FALSE; - cv->is_arg = cv0->is_arg; - cv->is_const = cv0->is_const; - cv->is_lexical = cv0->is_lexical; - cv->var_kind = cv0->var_kind; - cv->var_idx = i; - cv->var_name = JS_DupAtom(ctx, cv0->var_name); - } - return 0; -} - -typedef struct CodeContext { - const uint8_t *bc_buf; /* code buffer */ - int bc_len; /* length of the code buffer */ - int pos; /* position past the matched code pattern */ - int line_num; /* last visited OP_line_num parameter or -1 */ - int op; - int idx; - int label; - int val; - JSAtom atom; -} CodeContext; - -#define M2(op1, op2) ((op1) | ((op2) << 8)) -#define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16)) -#define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24)) - -static BOOL code_match(CodeContext *s, int pos, ...) -{ - const uint8_t *tab = s->bc_buf; - int op, len, op1, line_num, pos_next; - va_list ap; - BOOL ret = FALSE; - - line_num = -1; - va_start(ap, pos); - - for(;;) { - op1 = va_arg(ap, int); - if (op1 == -1) { - s->pos = pos; - s->line_num = line_num; - ret = TRUE; - break; - } - for (;;) { - if (pos >= s->bc_len) - goto done; - op = tab[pos]; - len = opcode_info[op].size; - pos_next = pos + len; - if (pos_next > s->bc_len) - goto done; - if (op == OP_line_num) { - line_num = get_u32(tab + pos + 1); - pos = pos_next; - } else { - break; - } - } - if (op != op1) { - if (op1 == (uint8_t)op1 || !op) - break; - if (op != (uint8_t)op1 - && op != (uint8_t)(op1 >> 8) - && op != (uint8_t)(op1 >> 16) - && op != (uint8_t)(op1 >> 24)) { - break; - } - s->op = op; - } - - pos++; - switch(opcode_info[op].fmt) { - case OP_FMT_loc8: - case OP_FMT_u8: - { - int idx = tab[pos]; - int arg = va_arg(ap, int); - if (arg == -1) { - s->idx = idx; - } else { - if (arg != idx) - goto done; - } - break; - } - case OP_FMT_u16: - case OP_FMT_npop: - case OP_FMT_loc: - case OP_FMT_arg: - case OP_FMT_var_ref: - { - int idx = get_u16(tab + pos); - int arg = va_arg(ap, int); - if (arg == -1) { - s->idx = idx; - } else { - if (arg != idx) - goto done; - } - break; - } - case OP_FMT_i32: - case OP_FMT_u32: - case OP_FMT_label: - case OP_FMT_const: - { - s->label = get_u32(tab + pos); - break; - } - case OP_FMT_label_u16: - { - s->label = get_u32(tab + pos); - s->val = get_u16(tab + pos + 4); - break; - } - case OP_FMT_atom: - { - s->atom = get_u32(tab + pos); - break; - } - case OP_FMT_atom_u8: - { - s->atom = get_u32(tab + pos); - s->val = get_u8(tab + pos + 4); - break; - } - case OP_FMT_atom_u16: - { - s->atom = get_u32(tab + pos); - s->val = get_u16(tab + pos + 4); - break; - } - case OP_FMT_atom_label_u8: - { - s->atom = get_u32(tab + pos); - s->label = get_u32(tab + pos + 4); - s->val = get_u8(tab + pos + 8); - break; - } - default: - break; - } - pos = pos_next; - } - done: - va_end(ap); - return ret; -} - -static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) -{ - int i, idx, label_next = -1; - - /* add the hoisted functions in arguments and local variables */ - for(i = 0; i < s->arg_count; i++) { - JSVarDef *vd = &s->args[i]; - if (vd->func_pool_idx >= 0) { - dbuf_putc(bc, OP_fclosure); - dbuf_put_u32(bc, vd->func_pool_idx); - dbuf_putc(bc, OP_put_arg); - dbuf_put_u16(bc, i); - } - } - for(i = 0; i < s->var_count; i++) { - JSVarDef *vd = &s->vars[i]; - if (vd->scope_level == 0 && vd->func_pool_idx >= 0) { - dbuf_putc(bc, OP_fclosure); - dbuf_put_u32(bc, vd->func_pool_idx); - dbuf_putc(bc, OP_put_loc); - dbuf_put_u16(bc, i); - } - } - - /* the module global variables must be initialized before - 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); - - /* if 'this' is true, initialize the global variables and return */ - dbuf_putc(bc, OP_push_this); - dbuf_putc(bc, OP_if_false); - dbuf_put_u32(bc, label_next); - 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++) { - JSGlobalVar *hf = &s->global_vars[i]; - int has_closure = 0; - BOOL force_init = hf->force_init; - /* we are in an eval, so the closure contains all the - enclosing variables */ - /* If the outer function has a variable environment, - create a property for the variable there */ - for(idx = 0; idx < s->closure_var_count; idx++) { - JSClosureVar *cv = &s->closure_var[idx]; - if (cv->var_name == hf->var_name) { - has_closure = 2; - force_init = FALSE; - break; - } - if (cv->var_name == JS_ATOM__var_ || - cv->var_name == JS_ATOM__arg_var_) { - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - has_closure = 1; - force_init = TRUE; - break; - } - } - if (!has_closure) { - int flags; - - flags = 0; - if (s->eval_type != JS_EVAL_TYPE_GLOBAL) - flags |= JS_PROP_CONFIGURABLE; - if (hf->cpool_idx >= 0 && !hf->is_lexical) { - /* global function definitions need a specific handling */ - dbuf_putc(bc, OP_fclosure); - dbuf_put_u32(bc, hf->cpool_idx); - - dbuf_putc(bc, OP_define_func); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, flags); - - goto done_global_var; - } else { - if (hf->is_lexical) { - flags |= DEFINE_GLOBAL_LEX_VAR; - if (!hf->is_const) - flags |= JS_PROP_WRITABLE; - } - dbuf_putc(bc, OP_define_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, flags); - } - } - if (hf->cpool_idx >= 0 || force_init) { - if (hf->cpool_idx >= 0) { - dbuf_putc(bc, OP_fclosure); - dbuf_put_u32(bc, hf->cpool_idx); - if (hf->var_name == JS_ATOM__default_) { - /* set default export function name */ - dbuf_putc(bc, OP_set_name); - dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default)); - } - } else { - dbuf_putc(bc, OP_undefined); - } - if (has_closure == 2) { - dbuf_putc(bc, OP_put_var_ref); - dbuf_put_u16(bc, idx); - } else if (has_closure == 1) { - dbuf_putc(bc, OP_define_field); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, OP_drop); - } else { - /* XXX: Check if variable is writable and enumerable */ - dbuf_putc(bc, OP_put_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - } - } - done_global_var: - JS_FreeAtom(ctx, hf->var_name); - } - - 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; - } - - js_free(ctx, s->global_vars); - s->global_vars = NULL; - s->global_var_count = 0; - s->global_var_size = 0; -} - -static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, - int pos, int *linep) -{ - int op, len, label; - - for (; pos < bc_len; pos += len) { - op = bc_buf[pos]; - len = opcode_info[op].size; - if (op == OP_line_num) { - *linep = get_u32(bc_buf + pos + 1); - } else - if (op == OP_label) { - label = get_u32(bc_buf + pos + 1); - if (update_label(s, label, 0) > 0) - break; -#if 0 - if (s->label_slots[label].first_reloc) { - printf("line %d: unreferenced label %d:%d has relocations\n", - *linep, label, s->label_slots[label].pos2); - } -#endif - assert(s->label_slots[label].first_reloc == NULL); - } else { - /* XXX: output a warning for unreachable code? */ - JSAtom atom; - switch(opcode_info[op].fmt) { - case OP_FMT_label: - case OP_FMT_label_u16: - label = get_u32(bc_buf + pos + 1); - update_label(s, label, -1); - break; - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - label = get_u32(bc_buf + pos + 5); - update_label(s, label, -1); - /* fall thru */ - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - atom = get_u32(bc_buf + pos + 1); - JS_FreeAtom(s->ctx, atom); - break; - default: - break; - } - } - } - return pos; -} - -static int get_label_pos(JSFunctionDef *s, int label) -{ - int i, pos; - for (i = 0; i < 20; i++) { - pos = s->label_slots[label].pos; - for (;;) { - switch (s->byte_code.buf[pos]) { - case OP_line_num: - case OP_label: - pos += 5; - continue; - case OP_goto: - label = get_u32(s->byte_code.buf + pos + 1); - break; - default: - return pos; - } - break; - } - } - return pos; -} - -/* convert global variable accesses to local variables or closure - variables when necessary */ -static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) -{ - int pos, pos_next, bc_len, op, len, i, idx, line_num; - uint8_t *bc_buf; - JSAtom var_name; - DynBuf bc_out; - CodeContext cc; - int scope; - - cc.bc_buf = bc_buf = s->byte_code.buf; - cc.bc_len = bc_len = s->byte_code.size; - js_dbuf_init(ctx, &bc_out); - - /* first pass for runtime checks (must be done before the - variables are created) */ - 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]; - if (cv->var_name == hf->var_name) { - if (s->eval_type == JS_EVAL_TYPE_DIRECT && - cv->is_lexical) { - /* Check if a lexical variable is - redefined as 'var'. XXX: Could abort - compilation here, but for consistency - with the other checks, we delay the - error generation. */ - dbuf_putc(&bc_out, OP_throw_error); - dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); - } - goto next; - } - if (cv->var_name == JS_ATOM__var_ || - 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; - if (hf->is_lexical) - flags |= DEFINE_GLOBAL_LEX_VAR; - if (hf->cpool_idx >= 0) - flags |= DEFINE_GLOBAL_FUNC_VAR; - dbuf_putc(&bc_out, flags); - next: ; - } - - line_num = 0; /* avoid warning */ - for (pos = 0; pos < bc_len; pos = pos_next) { - op = bc_buf[pos]; - len = opcode_info[op].size; - pos_next = pos + len; - switch(op) { - case OP_line_num: - line_num = get_u32(bc_buf + pos + 1); - s->line_number_size++; - goto no_change; - - case OP_eval: /* convert scope index to adjusted variable index */ - { - int call_argc = get_u16(bc_buf + pos + 1); - scope = get_u16(bc_buf + pos + 1 + 2); - mark_eval_captured_variables(ctx, s, scope); - dbuf_putc(&bc_out, op); - dbuf_put_u16(&bc_out, call_argc); - dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); - } - break; - case OP_apply_eval: /* convert scope index to adjusted variable index */ - scope = get_u16(bc_buf + pos + 1); - mark_eval_captured_variables(ctx, s, scope); - dbuf_putc(&bc_out, op); - dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); - break; - case OP_scope_get_var_checkthis: - case OP_scope_get_var_undef: - case OP_scope_get_var: - case OP_scope_put_var: - case OP_scope_delete_var: - case OP_scope_get_ref: - case OP_scope_put_var_init: - var_name = get_u32(bc_buf + pos + 1); - scope = get_u16(bc_buf + pos + 5); - pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, - NULL, NULL, pos_next); - JS_FreeAtom(ctx, var_name); - break; - case OP_scope_make_ref: - { - int label; - LabelSlot *ls; - var_name = get_u32(bc_buf + pos + 1); - label = get_u32(bc_buf + pos + 5); - scope = get_u16(bc_buf + pos + 9); - ls = &s->label_slots[label]; - ls->ref_count--; /* always remove label reference */ - pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, - bc_buf, ls, pos_next); - JS_FreeAtom(ctx, var_name); - } - break; - 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); - scope = get_u16(bc_buf + pos + 5); - ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out); - if (ret < 0) - goto fail; - JS_FreeAtom(ctx, var_name); - } - break; - case OP_gosub: - s->jump_size++; - if (OPTIMIZE) { - /* remove calls to empty finalizers */ - int label; - LabelSlot *ls; - - label = get_u32(bc_buf + pos + 1); - assert(label >= 0 && label < s->label_count); - ls = &s->label_slots[label]; - if (code_match(&cc, ls->pos, OP_ret, -1)) { - ls->ref_count--; - break; - } - } - goto no_change; - case OP_drop: - if (0) { - /* remove drops before return_undef */ - /* do not perform this optimization in pass2 because - it breaks patterns recognised in resolve_labels */ - int pos1 = pos_next; - int line1 = line_num; - while (code_match(&cc, pos1, OP_drop, -1)) { - if (cc.line_num >= 0) line1 = cc.line_num; - pos1 = cc.pos; - } - if (code_match(&cc, pos1, OP_return_undef, -1)) { - pos_next = pos1; - if (line1 != -1 && line1 != line_num) { - line_num = line1; - s->line_number_size++; - dbuf_putc(&bc_out, OP_line_num); - dbuf_put_u32(&bc_out, line_num); - } - break; - } - } - goto no_change; - case OP_insert3: - if (OPTIMIZE) { - /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */ - if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) { - dbuf_putc(&bc_out, cc.op); - pos_next = cc.pos; - if (cc.line_num != -1 && cc.line_num != line_num) { - line_num = cc.line_num; - s->line_number_size++; - dbuf_putc(&bc_out, OP_line_num); - dbuf_put_u32(&bc_out, line_num); - } - break; - } - } - goto no_change; - - case OP_goto: - s->jump_size++; - /* fall thru */ - case OP_tail_call: - case OP_tail_call_method: - case OP_return: - case OP_return_undef: - case OP_throw: - case OP_throw_error: - case OP_ret: - if (OPTIMIZE) { - /* remove dead code */ - int line = -1; - dbuf_put(&bc_out, bc_buf + pos, len); - pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line); - pos_next = pos; - if (pos < bc_len && line >= 0 && line_num != line) { - line_num = line; - s->line_number_size++; - dbuf_putc(&bc_out, OP_line_num); - dbuf_put_u32(&bc_out, line_num); - } - break; - } - goto no_change; - - case OP_label: - { - int label; - LabelSlot *ls; - - label = get_u32(bc_buf + pos + 1); - assert(label >= 0 && label < s->label_count); - ls = &s->label_slots[label]; - ls->pos2 = bc_out.size + opcode_info[op].size; - } - goto no_change; - - case OP_enter_scope: - { - int scope_idx, scope = get_u16(bc_buf + pos + 1); - - if (scope == s->body_scope) { - instantiate_hoisted_definitions(ctx, s, &bc_out); - } - - for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { - JSVarDef *vd = &s->vars[scope_idx]; - if (vd->scope_level == scope) { - if (scope_idx != s->arguments_arg_idx) { - if (vd->var_kind == JS_VAR_FUNCTION_DECL || - vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { - /* Initialize lexical variable upon entering scope */ - dbuf_putc(&bc_out, OP_fclosure); - dbuf_put_u32(&bc_out, vd->func_pool_idx); - dbuf_putc(&bc_out, OP_put_loc); - dbuf_put_u16(&bc_out, scope_idx); - } else { - /* XXX: should check if variable can be used - before initialization */ - dbuf_putc(&bc_out, OP_set_loc_uninitialized); - dbuf_put_u16(&bc_out, scope_idx); - } - } - scope_idx = vd->scope_next; - } else { - break; - } - } - } - break; - - case OP_leave_scope: - { - int scope_idx, scope = get_u16(bc_buf + pos + 1); - - for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { - JSVarDef *vd = &s->vars[scope_idx]; - if (vd->scope_level == scope) { - if (vd->is_captured) { - dbuf_putc(&bc_out, OP_close_loc); - dbuf_put_u16(&bc_out, scope_idx); - } - scope_idx = vd->scope_next; - } else { - break; - } - } - } - break; - - case OP_set_name: - { - /* remove dummy set_name opcodes */ - JSAtom name = get_u32(bc_buf + pos + 1); - if (name == JS_ATOM_NULL) - break; - } - goto no_change; - - case OP_if_false: - case OP_if_true: - case OP_catch: - s->jump_size++; - goto no_change; - - case OP_dup: - if (OPTIMIZE) { - /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */ - /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */ - if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) { - int lab0, lab1, op1, pos1, line1, pos2; - lab0 = lab1 = cc.label; - assert(lab1 >= 0 && lab1 < s->label_count); - op1 = cc.op; - pos1 = cc.pos; - line1 = cc.line_num; - while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) { - lab1 = cc.label; - } - if (code_match(&cc, pos2, op1, -1)) { - s->jump_size++; - update_label(s, lab0, -1); - update_label(s, cc.label, +1); - dbuf_putc(&bc_out, op1); - dbuf_put_u32(&bc_out, cc.label); - pos_next = pos1; - if (line1 != -1 && line1 != line_num) { - line_num = line1; - s->line_number_size++; - dbuf_putc(&bc_out, OP_line_num); - dbuf_put_u32(&bc_out, line_num); - } - break; - } - } - } - goto no_change; - - case OP_nop: - /* remove erased code */ - break; - case OP_set_class_name: - /* only used during parsing */ - break; - - 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); - break; - } - } - - /* set the new byte code */ - dbuf_free(&s->byte_code); - s->byte_code = bc_out; - if (dbuf_error(&s->byte_code)) { - JS_ThrowOutOfMemory(ctx); - return -1; - } - return 0; - fail: - /* continue the copy to keep the atom refcounts consistent */ - /* XXX: find a better solution ? */ - for (; pos < bc_len; pos = pos_next) { - op = bc_buf[pos]; - len = opcode_info[op].size; - pos_next = pos + len; - dbuf_put(&bc_out, bc_buf + pos, len); - } - dbuf_free(&s->byte_code); - s->byte_code = bc_out; - return -1; -} - -/* the pc2line table gives a line number for each PC value */ -static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num) -{ - if (s->line_number_slots != NULL - && s->line_number_count < s->line_number_size - && pc >= s->line_number_last_pc - && line_num != s->line_number_last) { - s->line_number_slots[s->line_number_count].pc = pc; - s->line_number_slots[s->line_number_count].line_num = line_num; - s->line_number_count++; - s->line_number_last_pc = pc; - s->line_number_last = line_num; - } -} - -static void compute_pc2line_info(JSFunctionDef *s) -{ - if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) { - int last_line_num = s->line_num; - uint32_t last_pc = 0; - int i; - - js_dbuf_init(s->ctx, &s->pc2line); - for (i = 0; i < s->line_number_count; i++) { - uint32_t pc = s->line_number_slots[i].pc; - int line_num = s->line_number_slots[i].line_num; - int diff_pc, diff_line; - - if (line_num < 0) - continue; - - diff_pc = pc - last_pc; - diff_line = line_num - last_line_num; - if (diff_line == 0 || diff_pc < 0) - continue; - - if (diff_line >= PC2LINE_BASE && - diff_line < PC2LINE_BASE + PC2LINE_RANGE && - diff_pc <= PC2LINE_DIFF_PC_MAX) { - dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) + - diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST); - } else { - /* longer encoding */ - dbuf_putc(&s->pc2line, 0); - dbuf_put_leb128(&s->pc2line, diff_pc); - dbuf_put_sleb128(&s->pc2line, diff_line); - } - last_pc = pc; - last_line_num = line_num; - } - } -} - -static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size) -{ - RelocEntry *re; - re = js_malloc(ctx, sizeof(*re)); - if (!re) - return NULL; - re->addr = addr; - re->size = size; - re->next = ls->first_reloc; - ls->first_reloc = re; - return re; -} - -static BOOL code_has_label(CodeContext *s, int pos, int label) -{ - while (pos < s->bc_len) { - int op = s->bc_buf[pos]; - if (op == OP_line_num) { - pos += 5; - continue; - } - if (op == OP_label) { - int lab = get_u32(s->bc_buf + pos + 1); - if (lab == label) - return TRUE; - pos += 5; - continue; - } - if (op == OP_goto) { - int lab = get_u32(s->bc_buf + pos + 1); - if (lab == label) - return TRUE; - } - break; - } - return FALSE; -} - -/* return the target label, following the OP_goto jumps - the first opcode at destination is stored in *pop - */ -static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline) -{ - int i, pos, op; - - update_label(s, label, -1); - for (i = 0; i < 10; i++) { - assert(label >= 0 && label < s->label_count); - pos = s->label_slots[label].pos2; - for (;;) { - switch(op = s->byte_code.buf[pos]) { - case OP_line_num: - if (pline) - *pline = get_u32(s->byte_code.buf + pos + 1); - /* fall thru */ - case OP_label: - pos += opcode_info[op].size; - continue; - case OP_goto: - label = get_u32(s->byte_code.buf + pos + 1); - break; - case OP_drop: - /* ignore drop opcodes if followed by OP_return_undef */ - while (s->byte_code.buf[++pos] == OP_drop) - continue; - if (s->byte_code.buf[pos] == OP_return_undef) - op = OP_return_undef; - /* fall thru */ - default: - goto done; - } - break; - } - } - /* cycle detected, could issue a warning */ - done: - *pop = op; - update_label(s, label, +1); - return label; -} - -static void push_short_int(DynBuf *bc_out, int val) -{ -#if SHORT_OPCODES - if (val >= -1 && val <= 7) { - dbuf_putc(bc_out, OP_push_0 + val); - return; - } - if (val == (int8_t)val) { - dbuf_putc(bc_out, OP_push_i8); - dbuf_putc(bc_out, val); - return; - } - if (val == (int16_t)val) { - dbuf_putc(bc_out, OP_push_i16); - dbuf_put_u16(bc_out, val); - return; - } -#endif - dbuf_putc(bc_out, OP_push_i32); - dbuf_put_u32(bc_out, val); -} - -static void put_short_code(DynBuf *bc_out, int op, int idx) -{ -#if SHORT_OPCODES - if (idx < 4) { - switch (op) { - case OP_get_loc: - dbuf_putc(bc_out, OP_get_loc0 + idx); - return; - case OP_put_loc: - dbuf_putc(bc_out, OP_put_loc0 + idx); - return; - case OP_set_loc: - dbuf_putc(bc_out, OP_set_loc0 + idx); - return; - case OP_get_arg: - dbuf_putc(bc_out, OP_get_arg0 + idx); - return; - case OP_put_arg: - dbuf_putc(bc_out, OP_put_arg0 + idx); - return; - case OP_set_arg: - dbuf_putc(bc_out, OP_set_arg0 + idx); - return; - case OP_get_var_ref: - dbuf_putc(bc_out, OP_get_var_ref0 + idx); - return; - case OP_put_var_ref: - dbuf_putc(bc_out, OP_put_var_ref0 + idx); - return; - case OP_set_var_ref: - dbuf_putc(bc_out, OP_set_var_ref0 + idx); - return; - case OP_call: - dbuf_putc(bc_out, OP_call0 + idx); - return; - } - } - if (idx < 256) { - switch (op) { - case OP_get_loc: - dbuf_putc(bc_out, OP_get_loc8); - dbuf_putc(bc_out, idx); - return; - case OP_put_loc: - dbuf_putc(bc_out, OP_put_loc8); - dbuf_putc(bc_out, idx); - return; - case OP_set_loc: - dbuf_putc(bc_out, OP_set_loc8); - dbuf_putc(bc_out, idx); - return; - } - } -#endif - dbuf_putc(bc_out, op); - dbuf_put_u16(bc_out, idx); -} - -/* peephole optimizations and resolve goto/labels */ -static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) -{ - int pos, pos_next, bc_len, op, op1, len, i, line_num; - const uint8_t *bc_buf; - DynBuf bc_out; - LabelSlot *label_slots, *ls; - RelocEntry *re, *re_next; - CodeContext cc; - int label; -#if SHORT_OPCODES - JumpSlot *jp; -#endif - - label_slots = s->label_slots; - - line_num = s->line_num; - - cc.bc_buf = bc_buf = s->byte_code.buf; - cc.bc_len = bc_len = s->byte_code.size; - js_dbuf_init(ctx, &bc_out); - -#if SHORT_OPCODES - if (s->jump_size) { - s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size); - if (s->jump_slots == NULL) - return -1; - } -#endif - /* XXX: Should skip this phase if not generating SHORT_OPCODES */ - if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) { - s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size); - if (s->line_number_slots == NULL) - return -1; - s->line_number_last = s->line_num; - s->line_number_last_pc = 0; - } - - /* initialize the 'home_object' variable if needed */ - if (s->home_object_var_idx >= 0) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT); - put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx); - } - /* initialize the 'this.active_func' variable if needed */ - if (s->this_active_func_var_idx >= 0) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); - put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx); - } - /* initialize the 'new.target' variable if needed */ - if (s->new_target_var_idx >= 0) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET); - put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx); - } - /* initialize the 'this' variable if needed. In a derived class - constructor, this is initially uninitialized. */ - if (s->this_var_idx >= 0) { - if (s->is_derived_class_constructor) { - dbuf_putc(&bc_out, OP_set_loc_uninitialized); - dbuf_put_u16(&bc_out, s->this_var_idx); - } else { - dbuf_putc(&bc_out, OP_push_this); - put_short_code(&bc_out, OP_put_loc, s->this_var_idx); - } - } - /* initialize the 'arguments' variable if needed */ - if (s->arguments_var_idx >= 0) { - if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS); - } else { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); - } - if (s->arguments_arg_idx >= 0) - put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); - put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx); - } - /* initialize a reference to the current function if needed */ - if (s->func_var_idx >= 0) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); - put_short_code(&bc_out, OP_put_loc, s->func_var_idx); - } - /* initialize the variable environment object if needed */ - if (s->var_object_idx >= 0) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); - put_short_code(&bc_out, OP_put_loc, s->var_object_idx); - } - if (s->arg_var_object_idx >= 0) { - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); - put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx); - } - - for (pos = 0; pos < bc_len; pos = pos_next) { - int val; - op = bc_buf[pos]; - len = opcode_info[op].size; - pos_next = pos + len; - switch(op) { - case OP_line_num: - /* line number info (for debug). We put it in a separate - compressed table to reduce memory usage and get better - performance */ - line_num = get_u32(bc_buf + pos + 1); - break; - - case OP_label: - { - label = get_u32(bc_buf + pos + 1); - assert(label >= 0 && label < s->label_count); - ls = &label_slots[label]; - assert(ls->addr == -1); - ls->addr = bc_out.size; - /* resolve the relocation entries */ - for(re = ls->first_reloc; re != NULL; re = re_next) { - int diff = ls->addr - re->addr; - re_next = re->next; - switch (re->size) { - case 4: - put_u32(bc_out.buf + re->addr, diff); - break; - case 2: - assert(diff == (int16_t)diff); - put_u16(bc_out.buf + re->addr, diff); - break; - case 1: - assert(diff == (int8_t)diff); - put_u8(bc_out.buf + re->addr, diff); - break; - } - js_free(ctx, re); - } - ls->first_reloc = NULL; - } - break; - - case OP_call: - case OP_call_method: - { - /* detect and transform tail calls */ - int argc; - argc = get_u16(bc_buf + pos + 1); - if (code_match(&cc, pos_next, OP_return, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op + 1, argc); - pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num); - break; - } - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op, argc); - break; - } - goto no_change; - - case OP_return: - case OP_return_undef: - case OP_return_async: - case OP_throw: - case OP_throw_error: - pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); - goto no_change; - - case OP_goto: - label = get_u32(bc_buf + pos + 1); - has_goto: - if (OPTIMIZE) { - int line1 = -1; - /* Use custom matcher because multiple labels can follow */ - label = find_jump_target(s, label, &op1, &line1); - if (code_has_label(&cc, pos_next, label)) { - /* jump to next instruction: remove jump */ - update_label(s, label, -1); - break; - } - if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) { - /* jump to return/throw: remove jump, append return/throw */ - /* updating the line number obfuscates assembly listing */ - //if (line1 >= 0) line_num = line1; - update_label(s, label, -1); - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, op1); - pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); - break; - } - /* XXX: should duplicate single instructions followed by goto or return */ - /* For example, can match one of these followed by return: - push_i32 / push_const / push_atom_value / get_var / - undefined / null / push_false / push_true / get_ref_value / - get_loc / get_arg / get_var_ref - */ - } - goto has_label; - - case OP_gosub: - label = get_u32(bc_buf + pos + 1); - if (0 && OPTIMIZE) { - label = find_jump_target(s, label, &op1, NULL); - if (op1 == OP_ret) { - update_label(s, label, -1); - /* empty finally clause: remove gosub */ - break; - } - } - goto has_label; - - case OP_catch: - label = get_u32(bc_buf + pos + 1); - goto has_label; - - case OP_if_true: - case OP_if_false: - label = get_u32(bc_buf + pos + 1); - if (OPTIMIZE) { - label = find_jump_target(s, label, &op1, NULL); - /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */ - if (code_has_label(&cc, pos_next, label)) { - update_label(s, label, -1); - dbuf_putc(&bc_out, OP_drop); - break; - } - /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */ - if (code_match(&cc, pos_next, OP_goto, -1)) { - int pos1 = cc.pos; - int line1 = cc.line_num; - if (code_has_label(&cc, pos1, label)) { - if (line1 >= 0) line_num = line1; - pos_next = pos1; - update_label(s, label, -1); - label = cc.label; - op ^= OP_if_true ^ OP_if_false; - } - } - } - has_label: - add_pc2line_info(s, bc_out.size, line_num); - if (op == OP_goto) { - pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); - } - assert(label >= 0 && label < s->label_count); - ls = &label_slots[label]; -#if SHORT_OPCODES - jp = &s->jump_slots[s->jump_count++]; - jp->op = op; - jp->size = 4; - jp->pos = bc_out.size + 1; - jp->label = label; - - if (ls->addr == -1) { - int diff = ls->pos2 - pos - 1; - if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { - jp->size = 1; - jp->op = OP_if_false8 + (op - OP_if_false); - dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); - dbuf_putc(&bc_out, 0); - if (!add_reloc(ctx, ls, bc_out.size - 1, 1)) - goto fail; - break; - } - if (diff < 32768 && op == OP_goto) { - jp->size = 2; - jp->op = OP_goto16; - dbuf_putc(&bc_out, OP_goto16); - dbuf_put_u16(&bc_out, 0); - if (!add_reloc(ctx, ls, bc_out.size - 2, 2)) - goto fail; - break; - } - } else { - int diff = ls->addr - bc_out.size - 1; - if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { - jp->size = 1; - jp->op = OP_if_false8 + (op - OP_if_false); - dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); - dbuf_putc(&bc_out, diff); - break; - } - if (diff == (int16_t)diff && op == OP_goto) { - jp->size = 2; - jp->op = OP_goto16; - dbuf_putc(&bc_out, OP_goto16); - dbuf_put_u16(&bc_out, diff); - break; - } - } -#endif - dbuf_putc(&bc_out, op); - dbuf_put_u32(&bc_out, ls->addr - bc_out.size); - if (ls->addr == -1) { - /* unresolved yet: create a new relocation entry */ - if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) - goto fail; - } - break; - case OP_with_get_var: - case OP_with_put_var: - case OP_with_delete_var: - case OP_with_make_ref: - case OP_with_get_ref: - case OP_with_get_ref_undef: - { - JSAtom atom; - int is_with; - - atom = get_u32(bc_buf + pos + 1); - label = get_u32(bc_buf + pos + 5); - is_with = bc_buf[pos + 9]; - if (OPTIMIZE) { - label = find_jump_target(s, label, &op1, NULL); - } - assert(label >= 0 && label < s->label_count); - ls = &label_slots[label]; - add_pc2line_info(s, bc_out.size, line_num); -#if SHORT_OPCODES - jp = &s->jump_slots[s->jump_count++]; - jp->op = op; - jp->size = 4; - jp->pos = bc_out.size + 5; - jp->label = label; -#endif - dbuf_putc(&bc_out, op); - dbuf_put_u32(&bc_out, atom); - dbuf_put_u32(&bc_out, ls->addr - bc_out.size); - if (ls->addr == -1) { - /* unresolved yet: create a new relocation entry */ - if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) - goto fail; - } - dbuf_putc(&bc_out, is_with); - } - break; - - case OP_drop: - if (OPTIMIZE) { - /* remove useless drops before return */ - if (code_match(&cc, pos_next, OP_return_undef, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - break; - } - } - goto no_change; - - case OP_null: -#if SHORT_OPCODES - if (OPTIMIZE) { - /* transform null strict_eq into is_null */ - if (code_match(&cc, pos_next, OP_strict_eq, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_is_null); - pos_next = cc.pos; - break; - } - /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */ - if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_is_null); - pos_next = cc.pos; - label = cc.label; - op = cc.op ^ OP_if_false ^ OP_if_true; - goto has_label; - } - } -#endif - /* fall thru */ - case OP_push_false: - case OP_push_true: - if (OPTIMIZE) { - val = (op == OP_push_true); - if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { - has_constant_test: - if (cc.line_num >= 0) line_num = cc.line_num; - if (val == cc.op - OP_if_false) { - /* transform null if_false(l1) -> goto l1 */ - /* transform false if_false(l1) -> goto l1 */ - /* transform true if_true(l1) -> goto l1 */ - pos_next = cc.pos; - op = OP_goto; - label = cc.label; - goto has_goto; - } else { - /* transform null if_true(l1) -> nop */ - /* transform false if_true(l1) -> nop */ - /* transform true if_false(l1) -> nop */ - pos_next = cc.pos; - update_label(s, cc.label, -1); - break; - } - } - } - goto no_change; - - case OP_push_i32: - if (OPTIMIZE) { - /* transform i32(val) neg -> i32(-val) */ - val = get_i32(bc_buf + pos + 1); - if ((val != INT32_MIN && val != 0) - && code_match(&cc, pos_next, OP_neg, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - if (code_match(&cc, cc.pos, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - } else { - add_pc2line_info(s, bc_out.size, line_num); - push_short_int(&bc_out, -val); - } - pos_next = cc.pos; - break; - } - /* remove push/drop pairs generated by the parser */ - if (code_match(&cc, pos_next, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - pos_next = cc.pos; - break; - } - /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */ - if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { - val = (val != 0); - goto has_constant_test; - } - add_pc2line_info(s, bc_out.size, line_num); - push_short_int(&bc_out, val); - break; - } - goto no_change; - -#if SHORT_OPCODES - case OP_push_const: - case OP_fclosure: - if (OPTIMIZE) { - int idx = get_u32(bc_buf + pos + 1); - if (idx < 256) { - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const); - dbuf_putc(&bc_out, idx); - break; - } - } - goto no_change; - - case OP_get_field: - if (OPTIMIZE) { - JSAtom atom = get_u32(bc_buf + pos + 1); - if (atom == JS_ATOM_length) { - JS_FreeAtom(ctx, atom); - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_get_length); - break; - } - } - goto no_change; -#endif - case OP_push_atom_value: - if (OPTIMIZE) { - JSAtom atom = get_u32(bc_buf + pos + 1); - /* remove push/drop pairs generated by the parser */ - if (code_match(&cc, pos_next, OP_drop, -1)) { - JS_FreeAtom(ctx, atom); - if (cc.line_num >= 0) line_num = cc.line_num; - pos_next = cc.pos; - break; - } -#if SHORT_OPCODES - if (atom == JS_ATOM_empty_string) { - JS_FreeAtom(ctx, atom); - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_push_empty_string); - break; - } -#endif - } - goto no_change; - - case OP_to_propkey: - case OP_to_propkey2: - if (OPTIMIZE) { - /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */ - if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) - || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1) - || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) { - break; - } - } - goto no_change; - - case OP_undefined: - if (OPTIMIZE) { - /* remove push/drop pairs generated by the parser */ - if (code_match(&cc, pos_next, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - pos_next = cc.pos; - break; - } - /* transform undefined return -> return_undefined */ - if (code_match(&cc, pos_next, OP_return, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_return_undef); - pos_next = cc.pos; - break; - } - /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */ - if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { - val = 0; - goto has_constant_test; - } -#if SHORT_OPCODES - /* transform undefined strict_eq -> is_undefined */ - if (code_match(&cc, pos_next, OP_strict_eq, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_is_undefined); - pos_next = cc.pos; - break; - } - /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */ - if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_is_undefined); - pos_next = cc.pos; - label = cc.label; - op = cc.op ^ OP_if_false ^ OP_if_true; - goto has_label; - } -#endif - } - goto no_change; - - case OP_insert2: - if (OPTIMIZE) { - /* Transformation: - insert2 put_field(a) drop -> put_field(a) - insert2 put_var_strict(a) drop -> put_var_strict(a) - */ - if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, cc.op); - dbuf_put_u32(&bc_out, cc.atom); - pos_next = cc.pos; - break; - } - } - goto no_change; - - case OP_dup: - if (OPTIMIZE) { - /* Transformation: dup put_x(n) drop -> put_x(n) */ - int op1, line2 = -1; - /* Transformation: dup put_x(n) -> set_x(n) */ - if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - op1 = cc.op + 1; /* put_x -> set_x */ - pos_next = cc.pos; - if (code_match(&cc, cc.pos, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - op1 -= 1; /* set_x drop -> put_x */ - pos_next = cc.pos; - if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) { - line2 = cc.line_num; /* delay line number update */ - op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ - pos_next = cc.pos; - } - } - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op1, cc.idx); - if (line2 >= 0) line_num = line2; - break; - } - } - goto no_change; - - case OP_get_loc: - if (OPTIMIZE) { - /* transformation: - get_loc(n) post_dec put_loc(n) drop -> dec_loc(n) - get_loc(n) post_inc put_loc(n) drop -> inc_loc(n) - get_loc(n) dec dup put_loc(n) drop -> dec_loc(n) - get_loc(n) inc dup put_loc(n) drop -> inc_loc(n) - */ - int idx; - idx = get_u16(bc_buf + pos + 1); - if (idx >= 256) - goto no_change; - if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) || - code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc); - dbuf_putc(&bc_out, idx); - pos_next = cc.pos; - break; - } - /* transformation: - get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n) - */ - if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); -#if SHORT_OPCODES - if (cc.atom == JS_ATOM_empty_string) { - JS_FreeAtom(ctx, cc.atom); - dbuf_putc(&bc_out, OP_push_empty_string); - } else -#endif - { - dbuf_putc(&bc_out, OP_push_atom_value); - dbuf_put_u32(&bc_out, cc.atom); - } - dbuf_putc(&bc_out, OP_add_loc); - dbuf_putc(&bc_out, idx); - pos_next = cc.pos; - break; - } - /* transformation: - get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n) - */ - if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - push_short_int(&bc_out, cc.label); - dbuf_putc(&bc_out, OP_add_loc); - dbuf_putc(&bc_out, idx); - pos_next = cc.pos; - break; - } - /* transformation: XXX: also do these: - get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n) - get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n) - get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n) - */ - if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, cc.op, cc.idx); - dbuf_putc(&bc_out, OP_add_loc); - dbuf_putc(&bc_out, idx); - pos_next = cc.pos; - break; - } - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op, idx); - break; - } - goto no_change; -#if SHORT_OPCODES - case OP_get_arg: - case OP_get_var_ref: - if (OPTIMIZE) { - int idx; - idx = get_u16(bc_buf + pos + 1); - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op, idx); - break; - } - goto no_change; -#endif - case OP_put_loc: - case OP_put_arg: - case OP_put_var_ref: - if (OPTIMIZE) { - /* transformation: put_x(n) get_x(n) -> set_x(n) */ - int idx; - idx = get_u16(bc_buf + pos + 1); - if (code_match(&cc, pos_next, op - 1, idx, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op + 1, idx); - pos_next = cc.pos; - break; - } - add_pc2line_info(s, bc_out.size, line_num); - put_short_code(&bc_out, op, idx); - break; - } - goto no_change; - - case OP_post_inc: - case OP_post_dec: - if (OPTIMIZE) { - /* transformation: - post_inc put_x drop -> inc put_x - post_inc perm3 put_field drop -> inc put_field - post_inc perm3 put_var_strict drop -> inc put_var_strict - post_inc perm4 put_array_el drop -> inc put_array_el - */ - int op1, idx; - if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - op1 = cc.op; - idx = cc.idx; - pos_next = cc.pos; - if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ - pos_next = cc.pos; - } - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); - put_short_code(&bc_out, op1, idx); - break; - } - if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); - dbuf_putc(&bc_out, cc.op); - dbuf_put_u32(&bc_out, cc.atom); - pos_next = cc.pos; - break; - } - if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); - dbuf_putc(&bc_out, OP_put_array_el); - pos_next = cc.pos; - break; - } - } - goto no_change; - -#if SHORT_OPCODES - case OP_typeof: - if (OPTIMIZE) { - /* simplify typeof tests */ - if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { - if (cc.line_num >= 0) line_num = cc.line_num; - int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq; - int op2 = -1; - switch (cc.atom) { - case JS_ATOM_undefined: - op2 = OP_typeof_is_undefined; - break; - case JS_ATOM_function: - op2 = OP_typeof_is_function; - break; - } - if (op2 >= 0) { - /* transform typeof(s) == "<type>" into is_<type> */ - if (op1 == OP_strict_eq) { - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, op2); - JS_FreeAtom(ctx, cc.atom); - pos_next = cc.pos; - break; - } - if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) { - /* transform typeof(s) != "<type>" if_false into is_<type> if_true */ - if (cc.line_num >= 0) line_num = cc.line_num; - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, op2); - JS_FreeAtom(ctx, cc.atom); - pos_next = cc.pos; - label = cc.label; - op = OP_if_true; - goto has_label; - } - } - } - } - goto no_change; -#endif - - default: - no_change: - add_pc2line_info(s, bc_out.size, line_num); - dbuf_put(&bc_out, bc_buf + pos, len); - break; - } - } - - /* check that there were no missing labels */ - for(i = 0; i < s->label_count; i++) { - assert(label_slots[i].first_reloc == NULL); - } -#if SHORT_OPCODES - if (OPTIMIZE) { - /* more jump optimizations */ - int patch_offsets = 0; - for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) { - LabelSlot *ls; - JumpSlot *jp1; - int j, pos, diff, delta; - - delta = 3; - switch (op = jp->op) { - case OP_goto16: - delta = 1; - /* fall thru */ - case OP_if_false: - case OP_if_true: - case OP_goto: - pos = jp->pos; - diff = s->label_slots[jp->label].addr - pos; - if (diff >= -128 && diff <= 127 + delta) { - //put_u8(bc_out.buf + pos, diff); - jp->size = 1; - if (op == OP_goto16) { - bc_out.buf[pos - 1] = jp->op = OP_goto8; - } else { - bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false); - } - goto shrink; - } else - if (diff == (int16_t)diff && op == OP_goto) { - //put_u16(bc_out.buf + pos, diff); - jp->size = 2; - delta = 2; - bc_out.buf[pos - 1] = jp->op = OP_goto16; - shrink: - /* XXX: should reduce complexity, using 2 finger copy scheme */ - memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta, - bc_out.size - pos - jp->size - delta); - bc_out.size -= delta; - patch_offsets++; - for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) { - if (ls->addr > pos) - ls->addr -= delta; - } - for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) { - if (jp1->pos > pos) - jp1->pos -= delta; - } - for (j = 0; j < s->line_number_count; j++) { - if (s->line_number_slots[j].pc > pos) - s->line_number_slots[j].pc -= delta; - } - continue; - } - break; - } - } - if (patch_offsets) { - JumpSlot *jp1; - int j; - for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) { - int diff1 = s->label_slots[jp1->label].addr - jp1->pos; - switch (jp1->size) { - case 1: - put_u8(bc_out.buf + jp1->pos, diff1); - break; - case 2: - put_u16(bc_out.buf + jp1->pos, diff1); - break; - case 4: - put_u32(bc_out.buf + jp1->pos, diff1); - break; - } - } - } - } - js_free(ctx, s->jump_slots); - s->jump_slots = NULL; -#endif - js_free(ctx, s->label_slots); - s->label_slots = NULL; - /* XXX: should delay until copying to runtime bytecode function */ - compute_pc2line_info(s); - js_free(ctx, s->line_number_slots); - s->line_number_slots = NULL; - /* set the new byte code */ - dbuf_free(&s->byte_code); - s->byte_code = bc_out; - s->use_short_opcodes = TRUE; - if (dbuf_error(&s->byte_code)) { - JS_ThrowOutOfMemory(ctx); - return -1; - } - return 0; - fail: - /* XXX: not safe */ - dbuf_free(&bc_out); - return -1; -} - -/* compute the maximum stack size needed by the function */ - -typedef struct StackSizeState { - int 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; -} 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 catch_pos) -{ - if ((unsigned)pos >= s->bc_len) { - JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); - return -1; - } - if (stack_len > s->stack_len_max) { - s->stack_len_max = stack_len; - if (s->stack_len_max > JS_STACK_SIZE_MAX) { - JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); - return -1; - } - } - if (s->stack_level_tab[pos] != 0xffff) { - /* already explored: check that the stack size is consistent */ - if (s->stack_level_tab[pos] != stack_len) { - JS_ThrowInternalError(ctx, "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; - } - } - - /* 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]), - &s->pc_stack_size, s->pc_stack_len + 1)) - return -1; - s->pc_stack[s->pc_stack_len++] = pos; - return 0; -} - -static __exception int compute_stack_size(JSContext *ctx, - JSFunctionDef *fd, - int *pstack_size) -{ - StackSizeState s_s, *s = &s_s; - int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level; - const JSOpCode *oi; - const uint8_t *bc_buf; - - bc_buf = fd->byte_code.buf; - s->bc_len = fd->byte_code.size; - /* bc_len > 0 */ - s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * - s->bc_len); - if (!s->stack_level_tab) - return -1; - for(i = 0; i < s->bc_len; i++) - s->stack_level_tab[i] = 0xffff; - 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, -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); - goto fail; - } - n_pop = oi->n_pop; - /* call pops a variable number of arguments */ - if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) { - n_pop += get_u16(bc_buf + pos + 1); - } else { -#if SHORT_OPCODES - if (oi->fmt == OP_FMT_npopx) { - n_pop += op - OP_call0; - } -#endif - } - - if (stack_len < n_pop) { - JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); - goto fail; - } - stack_len += oi->n_push - n_pop; - if (stack_len > s->stack_len_max) { - s->stack_len_max = stack_len; - if (s->stack_len_max > JS_STACK_SIZE_MAX) { - JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); - goto fail; - } - } - switch(op) { - case OP_tail_call: - case OP_tail_call_method: - case OP_return: - case OP_return_undef: - case OP_return_async: - case OP_throw: - case OP_throw_error: - case OP_ret: - goto done_insn; - case OP_goto: - diff = get_u32(bc_buf + pos + 1); - pos_next = pos + 1 + diff; - break; -#if SHORT_OPCODES - case OP_goto16: - diff = (int16_t)get_u16(bc_buf + pos + 1); - pos_next = pos + 1 + diff; - break; - case OP_goto8: - diff = (int8_t)bc_buf[pos + 1]; - pos_next = pos + 1 + diff; - break; - case OP_if_true8: - case OP_if_false8: - diff = (int8_t)bc_buf[pos + 1]; - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) - goto fail; - break; -#endif - case OP_if_true: - case OP_if_false: - diff = get_u32(bc_buf + pos + 1); - 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, 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, 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, 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, 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, catch_pos)) - goto fail; - done_insn: ; - } - 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->pc_stack); - js_free(ctx, s->catch_pos_tab); - js_free(ctx, s->stack_level_tab); - *pstack_size = 0; - return -1; -} - -static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) -{ - int i, idx; - JSModuleDef *m = fd->module; - JSExportEntry *me; - JSGlobalVar *hf; - - /* The imported global variables were added as closure variables - in js_parse_import(). We add here the module global - variables. */ - - for(i = 0; i < fd->global_var_count; i++) { - hf = &fd->global_vars[i]; - if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const, - hf->is_lexical, FALSE) < 0) - return -1; - } - - /* resolve the variable names of the local exports */ - for(i = 0; i < m->export_entries_count; i++) { - me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - idx = find_closure_var(ctx, fd, me->local_name); - if (idx < 0) { - JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist", - me->local_name); - return -1; - } - me->u.local.var_idx = idx; - } - } - return 0; -} - -/* create a function object from a function definition. The function - definition is freed. All the child functions are also created. It - must be done this way to resolve all the variables. */ -static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) -{ - JSValue func_obj; - JSFunctionBytecode *b; - struct list_head *el, *el1; - int stack_size, scope, idx; - int function_size, byte_code_offset, cpool_offset; - int closure_var_offset, vardefs_offset; - - /* recompute scope linkage */ - for (scope = 0; scope < fd->scope_count; scope++) { - fd->scopes[scope].first = -1; - } - if (fd->has_parameter_expressions) { - /* special end of variable list marker for the argument scope */ - fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END; - } - for (idx = 0; idx < fd->var_count; idx++) { - JSVarDef *vd = &fd->vars[idx]; - vd->scope_next = fd->scopes[vd->scope_level].first; - fd->scopes[vd->scope_level].first = idx; - } - for (scope = 2; scope < fd->scope_count; scope++) { - JSVarScope *sd = &fd->scopes[scope]; - if (sd->first < 0) - sd->first = fd->scopes[sd->parent].first; - } - for (idx = 0; idx < fd->var_count; idx++) { - JSVarDef *vd = &fd->vars[idx]; - if (vd->scope_next < 0 && vd->scope_level > 1) { - scope = fd->scopes[vd->scope_level].parent; - vd->scope_next = fd->scopes[scope].first; - } - } - - /* if the function contains an eval call, the closure variables - are used to compile the eval and they must be ordered by scope, - so it is necessary to create the closure variables before any - other variable lookup is done. */ - if (fd->has_eval_call) - add_eval_variables(ctx, fd); - - /* add the module global variables in the closure */ - if (fd->module) { - if (add_module_variables(ctx, fd)) - goto fail; - } - - /* first create all the child functions */ - list_for_each_safe(el, el1, &fd->child_list) { - JSFunctionDef *fd1; - int cpool_idx; - - fd1 = list_entry(el, JSFunctionDef, link); - cpool_idx = fd1->parent_cpool_idx; - func_obj = js_create_function(ctx, fd1); - if (JS_IsException(func_obj)) - goto fail; - /* save it in the constant pool */ - assert(cpool_idx >= 0); - fd->cpool[cpool_idx] = func_obj; - } - -#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4) - if (!(fd->js_mode & JS_MODE_STRIP)) { - printf("pass 1\n"); - dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size, - fd->args, fd->arg_count, fd->vars, fd->var_count, - fd->closure_var, fd->closure_var_count, - fd->cpool, fd->cpool_count, fd->source, fd->line_num, - fd->label_slots, NULL); - printf("\n"); - } -#endif - - if (resolve_variables(ctx, fd)) - goto fail; - -#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2) - if (!(fd->js_mode & JS_MODE_STRIP)) { - printf("pass 2\n"); - dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size, - fd->args, fd->arg_count, fd->vars, fd->var_count, - fd->closure_var, fd->closure_var_count, - fd->cpool, fd->cpool_count, fd->source, fd->line_num, - fd->label_slots, NULL); - printf("\n"); - } -#endif - - if (resolve_labels(ctx, fd)) - goto fail; - - if (compute_stack_size(ctx, fd, &stack_size) < 0) - goto fail; - - if (fd->js_mode & JS_MODE_STRIP) { - function_size = offsetof(JSFunctionBytecode, debug); - } else { - function_size = sizeof(*b); - } - cpool_offset = function_size; - function_size += fd->cpool_count * sizeof(*fd->cpool); - vardefs_offset = function_size; - if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) { - function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs); - } - closure_var_offset = function_size; - function_size += fd->closure_var_count * sizeof(*fd->closure_var); - byte_code_offset = function_size; - function_size += fd->byte_code.size; - - b = js_mallocz(ctx, function_size); - if (!b) - goto fail; - b->header.ref_count = 1; - - b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset); - b->byte_code_len = fd->byte_code.size; - memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size); - js_free(ctx, fd->byte_code.buf); - fd->byte_code.buf = NULL; - - b->func_name = fd->func_name; - if (fd->arg_count + fd->var_count > 0) { - if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) { - /* Strip variable definitions not needed at runtime */ - int i; - for(i = 0; i < fd->var_count; i++) { - JS_FreeAtom(ctx, fd->vars[i].var_name); - } - for(i = 0; i < fd->arg_count; i++) { - JS_FreeAtom(ctx, fd->args[i].var_name); - } - for(i = 0; i < fd->closure_var_count; i++) { - JS_FreeAtom(ctx, fd->closure_var[i].var_name); - fd->closure_var[i].var_name = JS_ATOM_NULL; - } - } else { - b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - memcpy_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; - b->defined_arg_count = fd->defined_arg_count; - js_free(ctx, fd->args); - js_free(ctx, fd->vars); - } - b->cpool_count = fd->cpool_count; - if (b->cpool_count) { - b->cpool = (void *)((uint8_t*)b + cpool_offset); - memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool)); - } - js_free(ctx, fd->cpool); - fd->cpool = NULL; - - b->stack_size = stack_size; - - if (fd->js_mode & JS_MODE_STRIP) { - JS_FreeAtom(ctx, fd->filename); - dbuf_free(&fd->pc2line); // probably useless - } else { - /* XXX: source and pc2line info should be packed at the end of the - JSFunctionBytecode structure, avoiding allocation overhead - */ - b->has_debug = 1; - b->debug.filename = fd->filename; - b->debug.line_num = fd->line_num; - - //DynBuf pc2line; - //compute_pc2line_info(fd, &pc2line); - //js_free(ctx, fd->line_number_slots) - b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size); - if (!b->debug.pc2line_buf) - b->debug.pc2line_buf = fd->pc2line.buf; - b->debug.pc2line_len = fd->pc2line.size; - b->debug.source = fd->source; - b->debug.source_len = fd->source_len; - } - if (fd->scopes != fd->def_scope_array) - js_free(ctx, fd->scopes); - - b->closure_var_count = fd->closure_var_count; - if (b->closure_var_count) { - b->closure_var = (void *)((uint8_t*)b + closure_var_offset); - memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var)); - } - js_free(ctx, fd->closure_var); - fd->closure_var = NULL; - - b->has_prototype = fd->has_prototype; - b->has_simple_parameter_list = fd->has_simple_parameter_list; - b->js_mode = fd->js_mode; - b->is_derived_class_constructor = fd->is_derived_class_constructor; - b->func_kind = fd->func_kind; - b->need_home_object = (fd->home_object_var_idx >= 0 || - fd->need_home_object); - b->new_target_allowed = fd->new_target_allowed; - b->super_call_allowed = fd->super_call_allowed; - b->super_allowed = fd->super_allowed; - b->arguments_allowed = fd->arguments_allowed; - b->backtrace_barrier = fd->backtrace_barrier; - b->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)) { - js_dump_function_bytecode(ctx, b); - } -#endif - - if (fd->parent) { - /* remove from parent list */ - list_del(&fd->link); - } - - js_free(ctx, fd); - return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); - fail: - js_free_function_def(ctx, fd); - return JS_EXCEPTION; -} - -static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) -{ - int i; - -#if 0 - { - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("freeing %s\n", - JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); - } -#endif - free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); - - if (b->vardefs) { - for(i = 0; i < b->arg_count + b->var_count; i++) { - JS_FreeAtomRT(rt, b->vardefs[i].var_name); - } - } - for(i = 0; i < b->cpool_count; i++) - JS_FreeValueRT(rt, b->cpool[i]); - - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JS_FreeAtomRT(rt, cv->var_name); - } - if (b->realm) - JS_FreeContext(b->realm); - - JS_FreeAtomRT(rt, b->func_name); - if (b->has_debug) { - JS_FreeAtomRT(rt, b->debug.filename); - js_free_rt(rt, b->debug.pc2line_buf); - js_free_rt(rt, b->debug.source); - } - - remove_gc_object(&b->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { - list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); - } else { - js_free_rt(rt, b); - } -} - -static __exception int js_parse_directives(JSParseState *s) -{ - char str[20]; - JSParsePos pos; - BOOL has_semi; - - if (s->token.val != TOK_STRING) - return 0; - - js_parse_get_pos(s, &pos); - - while(s->token.val == TOK_STRING) { - /* Copy actual source string representation */ - snprintf(str, sizeof str, "%.*s", - (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1); - - if (next_token(s)) - return -1; - - has_semi = FALSE; - switch (s->token.val) { - case ';': - if (next_token(s)) - return -1; - has_semi = TRUE; - break; - case '}': - case TOK_EOF: - has_semi = TRUE; - break; - case TOK_NUMBER: - case TOK_STRING: - case TOK_TEMPLATE: - case TOK_IDENT: - case TOK_REGEXP: - case TOK_DEC: - case TOK_INC: - case TOK_NULL: - case TOK_FALSE: - case TOK_TRUE: - case TOK_IF: - case TOK_RETURN: - case TOK_VAR: - case TOK_THIS: - case TOK_DELETE: - case TOK_TYPEOF: - case TOK_NEW: - case TOK_DO: - case TOK_WHILE: - case TOK_FOR: - case TOK_SWITCH: - case TOK_THROW: - case TOK_TRY: - case TOK_FUNCTION: - case TOK_DEBUGGER: - case TOK_WITH: - case TOK_CLASS: - case TOK_CONST: - case TOK_ENUM: - case TOK_EXPORT: - case TOK_IMPORT: - case TOK_SUPER: - case TOK_INTERFACE: - case TOK_LET: - case TOK_PACKAGE: - case TOK_PRIVATE: - case TOK_PROTECTED: - case TOK_PUBLIC: - case TOK_STATIC: - /* automatic insertion of ';' */ - if (s->got_lf) - has_semi = TRUE; - break; - default: - break; - } - if (!has_semi) - break; - if (!strcmp(str, "use strict")) { - s->cur_func->has_use_strict = TRUE; - s->cur_func->js_mode |= JS_MODE_STRICT; - } -#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8) - else if (!strcmp(str, "use strip")) { - s->cur_func->js_mode |= JS_MODE_STRIP; - } -#endif -#ifdef CONFIG_BIGNUM - else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { - s->cur_func->js_mode |= JS_MODE_MATH; - } -#endif - } - return js_parse_seek_token(s, &pos); -} - -static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, - JSAtom func_name) -{ - JSAtom name; - int i, idx; - - if (fd->js_mode & JS_MODE_STRICT) { - if (!fd->has_simple_parameter_list && fd->has_use_strict) { - return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); - } - if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) { - return js_parse_error(s, "invalid function name in strict code"); - } - for (idx = 0; idx < fd->arg_count; idx++) { - name = fd->args[idx].var_name; - - if (name == JS_ATOM_eval || name == JS_ATOM_arguments) { - return js_parse_error(s, "invalid argument name in strict code"); - } - } - } - /* check async_generator case */ - if ((fd->js_mode & JS_MODE_STRICT) - || !fd->has_simple_parameter_list - || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC) - || fd->func_type == JS_PARSE_FUNC_ARROW - || fd->func_type == JS_PARSE_FUNC_METHOD) { - for (idx = 0; idx < fd->arg_count; idx++) { - name = fd->args[idx].var_name; - if (name != JS_ATOM_NULL) { - for (i = 0; i < idx; i++) { - if (fd->args[i].var_name == name) - goto duplicate; - } - /* Check if argument name duplicates a destructuring parameter */ - /* XXX: should have a flag for such variables */ - for (i = 0; i < fd->var_count; i++) { - if (fd->vars[i].var_name == name && - fd->vars[i].scope_level == 0) - goto duplicate; - } - } - } - } - return 0; - -duplicate: - return js_parse_error(s, "duplicate argument names not allowed in this context"); -} - -/* create a function to initialize class fields */ -static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) -{ - JSFunctionDef *fd; - - fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE, - s->filename, 0); - if (!fd) - return NULL; - fd->func_name = JS_ATOM_NULL; - fd->has_prototype = FALSE; - fd->has_home_object = TRUE; - - fd->has_arguments_binding = FALSE; - fd->has_this_binding = TRUE; - fd->is_derived_class_constructor = FALSE; - fd->new_target_allowed = TRUE; - fd->super_call_allowed = FALSE; - fd->super_allowed = fd->has_home_object; - fd->arguments_allowed = FALSE; - - fd->func_kind = JS_FUNC_NORMAL; - fd->func_type = JS_PARSE_FUNC_METHOD; - return fd; -} - -/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and - JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ -static __exception int js_parse_function_decl2(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, - const uint8_t *ptr, - int function_line_num, - JSParseExportEnum export_flag, - JSFunctionDef **pfd) -{ - JSContext *ctx = s->ctx; - JSFunctionDef *fd = s->cur_func; - BOOL is_expr; - int func_idx, lexical_func_idx = -1; - BOOL has_opt_arg; - BOOL create_func_var = FALSE; - - is_expr = (func_type != JS_PARSE_FUNC_STATEMENT && - func_type != JS_PARSE_FUNC_VAR); - - if (func_type == JS_PARSE_FUNC_STATEMENT || - func_type == JS_PARSE_FUNC_VAR || - func_type == JS_PARSE_FUNC_EXPR) { - if (func_kind == JS_FUNC_NORMAL && - token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) != '\n') { - if (next_token(s)) - return -1; - func_kind = JS_FUNC_ASYNC; - } - if (next_token(s)) - return -1; - if (s->token.val == '*') { - if (next_token(s)) - return -1; - func_kind |= JS_FUNC_GENERATOR; - } - - if (s->token.val == TOK_IDENT) { - if (s->token.u.ident.is_reserved || - (s->token.u.ident.atom == JS_ATOM_yield && - func_type == JS_PARSE_FUNC_EXPR && - (func_kind & JS_FUNC_GENERATOR)) || - (s->token.u.ident.atom == JS_ATOM_await && - ((func_type == JS_PARSE_FUNC_EXPR && - (func_kind & JS_FUNC_ASYNC)) || - func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { - return js_parse_error_reserved_identifier(s); - } - } - if (s->token.val == TOK_IDENT || - (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) || - (s->token.val == TOK_AWAIT && !s->is_module)) && - func_type == JS_PARSE_FUNC_EXPR)) { - func_name = JS_DupAtom(ctx, s->token.u.ident.atom); - if (next_token(s)) { - JS_FreeAtom(ctx, func_name); - return -1; - } - } else { - if (func_type != JS_PARSE_FUNC_EXPR && - export_flag != JS_PARSE_EXPORT_DEFAULT) { - return js_parse_error(s, "function name expected"); - } - } - } else if (func_type != JS_PARSE_FUNC_ARROW) { - func_name = JS_DupAtom(ctx, func_name); - } - - if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && - (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { - JSGlobalVar *hf; - hf = find_global_var(fd, func_name); - /* XXX: should check scope chain */ - if (hf && hf->scope_level == fd->scope_level) { - js_parse_error(s, "invalid redefinition of global identifier in module code"); - JS_FreeAtom(ctx, func_name); - return -1; - } - } - - if (func_type == JS_PARSE_FUNC_VAR) { - if (!(fd->js_mode & JS_MODE_STRICT) - && func_kind == JS_FUNC_NORMAL - && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 - && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) - && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { - create_func_var = TRUE; - } - /* Create the lexical name here so that the function closure - contains it */ - if (fd->is_eval && - (fd->eval_type == JS_EVAL_TYPE_GLOBAL || - fd->eval_type == JS_EVAL_TYPE_MODULE) && - fd->scope_level == fd->body_scope) { - /* avoid creating a lexical variable in the global - scope. XXX: check annex B */ - JSGlobalVar *hf; - hf = find_global_var(fd, func_name); - /* XXX: should check scope chain */ - if (hf && hf->scope_level == fd->scope_level) { - js_parse_error(s, "invalid redefinition of global identifier"); - JS_FreeAtom(ctx, func_name); - return -1; - } - } else { - /* Always create a lexical name, fail if at the same scope as - existing name */ - /* Lexical variable will be initialized upon entering scope */ - lexical_func_idx = define_var(s, fd, func_name, - func_kind != JS_FUNC_NORMAL ? - JS_VAR_DEF_NEW_FUNCTION_DECL : - JS_VAR_DEF_FUNCTION_DECL); - if (lexical_func_idx < 0) { - JS_FreeAtom(ctx, func_name); - return -1; - } - } - } - - fd = js_new_function_def(ctx, fd, FALSE, is_expr, - s->filename, function_line_num); - if (!fd) { - JS_FreeAtom(ctx, func_name); - return -1; - } - if (pfd) - *pfd = fd; - s->cur_func = fd; - fd->func_name = func_name; - /* XXX: test !fd->is_generator is always false */ - fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT || - func_type == JS_PARSE_FUNC_VAR || - func_type == JS_PARSE_FUNC_EXPR) && - func_kind == JS_FUNC_NORMAL; - fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD || - func_type == JS_PARSE_FUNC_GETTER || - func_type == JS_PARSE_FUNC_SETTER || - func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || - func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); - fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW && - 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) { - fd->new_target_allowed = fd->parent->new_target_allowed; - fd->super_call_allowed = fd->parent->super_call_allowed; - fd->super_allowed = fd->parent->super_allowed; - fd->arguments_allowed = fd->parent->arguments_allowed; - } else 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; - fd->super_allowed = fd->has_home_object; - fd->arguments_allowed = TRUE; - } - - /* fd->in_function_body == FALSE prevents yield/await during the parsing - of the arguments in generator/async functions. They are parsed as - regular identifiers for other function kinds. */ - fd->func_kind = func_kind; - fd->func_type = func_type; - - if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || - func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { - /* error if not invoked as a constructor */ - emit_op(s, OP_check_ctor); - } - - if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { - emit_class_field_init(s); - } - - /* parse arguments */ - fd->has_simple_parameter_list = TRUE; - fd->has_parameter_expressions = FALSE; - has_opt_arg = FALSE; - if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { - JSAtom name; - if (s->token.u.ident.is_reserved) { - js_parse_error_reserved_identifier(s); - goto fail; - } - name = s->token.u.ident.atom; - if (add_arg(ctx, fd, name) < 0) - goto fail; - fd->defined_arg_count = 1; - } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { - if (s->token.val == '(') { - int skip_bits; - /* if there is an '=' inside the parameter list, we - consider there is a parameter expression inside */ - js_parse_skip_parens_token(s, &skip_bits, FALSE); - if (skip_bits & SKIP_HAS_ASSIGNMENT) - fd->has_parameter_expressions = TRUE; - if (next_token(s)) - goto fail; - } else { - if (js_parse_expect(s, '(')) - goto fail; - } - - if (fd->has_parameter_expressions) { - fd->scope_level = -1; /* force no parent scope */ - 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) { - fd->has_simple_parameter_list = FALSE; - rest = TRUE; - if (next_token(s)) - goto fail; - } - if (s->token.val == '[' || s->token.val == '{') { - fd->has_simple_parameter_list = FALSE; - if (rest) { - emit_op(s, OP_rest); - emit_u16(s, fd->arg_count); - } else { - /* unnamed arg for destructuring */ - idx = add_arg(ctx, fd, JS_ATOM_NULL); - emit_op(s, OP_get_arg); - emit_u16(s, idx); - } - has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); - if (has_initializer < 0) - goto fail; - if (has_initializer) - has_opt_arg = TRUE; - if (!has_opt_arg) - fd->defined_arg_count++; - } else if (s->token.val == TOK_IDENT) { - if (s->token.u.ident.is_reserved) { - js_parse_error_reserved_identifier(s); - goto fail; - } - name = s->token.u.ident.atom; - if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { - js_parse_error_reserved_identifier(s); - goto fail; - } - 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; - } - /* XXX: could avoid allocating an argument if rest is true */ - idx = add_arg(ctx, fd, name); - if (idx < 0) - goto fail; - if (next_token(s)) - goto fail; - if (rest) { - emit_op(s, OP_rest); - emit_u16(s, idx); - if (fd->has_parameter_expressions) { - emit_op(s, OP_dup); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, name); - emit_u16(s, fd->scope_level); - } - emit_op(s, OP_put_arg); - emit_u16(s, idx); - fd->has_simple_parameter_list = FALSE; - has_opt_arg = TRUE; - } else if (s->token.val == '=') { - int label; - - fd->has_simple_parameter_list = FALSE; - has_opt_arg = TRUE; - - if (next_token(s)) - goto fail; - - label = new_label(s); - emit_op(s, OP_get_arg); - emit_u16(s, idx); - emit_op(s, OP_dup); - emit_op(s, OP_undefined); - emit_op(s, OP_strict_eq); - emit_goto(s, OP_if_false, label); - emit_op(s, OP_drop); - if (js_parse_assign_expr(s)) - goto fail; - set_object_name(s, name); - emit_op(s, OP_dup); - emit_op(s, OP_put_arg); - emit_u16(s, idx); - emit_label(s, label); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, name); - emit_u16(s, fd->scope_level); - } else { - if (!has_opt_arg) { - fd->defined_arg_count++; - } - if (fd->has_parameter_expressions) { - /* copy the argument to the argument scope */ - emit_op(s, OP_get_arg); - emit_u16(s, idx); - emit_op(s, OP_scope_put_var_init); - emit_atom(s, name); - emit_u16(s, fd->scope_level); - } - } - } else { - js_parse_error(s, "missing formal parameter"); - goto fail; - } - if (rest && s->token.val != ')') { - js_parse_expect(s, ')'); - goto fail; - } - if (s->token.val == ')') - break; - if (js_parse_expect(s, ',')) - goto fail; - } - if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || - (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { - js_parse_error(s, "invalid number of arguments for getter or setter"); - goto fail; - } - } - - if (fd->has_parameter_expressions) { - int idx; - - /* Copy the variables in the argument scope to the variable - scope (see FunctionDeclarationInstantiation() in spec). The - normal arguments are already present, so no need to copy - them. */ - idx = fd->scopes[fd->scope_level].first; - while (idx >= 0) { - JSVarDef *vd = &fd->vars[idx]; - if (vd->scope_level != fd->scope_level) - break; - if (find_var(ctx, fd, vd->var_name) < 0) { - if (add_var(ctx, fd, vd->var_name) < 0) - goto fail; - vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ - emit_op(s, OP_scope_get_var); - emit_atom(s, vd->var_name); - emit_u16(s, fd->scope_level); - emit_op(s, OP_scope_put_var); - emit_atom(s, vd->var_name); - emit_u16(s, 0); - } - 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); - - /* set the variable scope as the current scope */ - fd->scope_level = 0; - fd->scope_first = fd->scopes[fd->scope_level].first; - } - - if (next_token(s)) - goto fail; - - /* generator function: yield after the parameters are evaluated */ - if (func_kind == JS_FUNC_GENERATOR || - func_kind == JS_FUNC_ASYNC_GENERATOR) - emit_op(s, OP_initial_yield); - - /* in generators, yield expression is forbidden during the parsing - of the arguments */ - fd->in_function_body = TRUE; - push_scope(s); /* enter body scope */ - fd->body_scope = fd->scope_level; - - if (s->token.val == TOK_ARROW) { - if (next_token(s)) - goto fail; - - if (s->token.val != '{') { - if (js_parse_function_check_names(s, fd, func_name)) - goto fail; - - if (js_parse_assign_expr(s)) - goto fail; - - if (func_kind != JS_FUNC_NORMAL) - emit_op(s, OP_return_async); - else - emit_op(s, OP_return); - - if (!(fd->js_mode & JS_MODE_STRIP)) { - /* save the function source code */ - /* the end of the function source code is after the last - token of the function source stored into s->last_ptr */ - fd->source_len = s->last_ptr - ptr; - fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); - if (!fd->source) - goto fail; - } - goto done; - } - } - - if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { - if (js_parse_expect(s, '{')) - goto fail; - } - - if (js_parse_directives(s)) - goto fail; - - /* in strict_mode, check function and argument names */ - if (js_parse_function_check_names(s, fd, func_name)) - goto fail; - - while (s->token.val != '}') { - if (js_parse_source_element(s)) - goto fail; - } - if (!(fd->js_mode & JS_MODE_STRIP)) { - /* save the function source code */ - fd->source_len = s->buf_ptr - ptr; - fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); - if (!fd->source) - goto fail; - } - - if (next_token(s)) { - /* consume the '}' */ - goto fail; - } - - /* in case there is no return, add one */ - if (js_is_live_code(s)) { - emit_return(s, FALSE); - } - done: - s->cur_func = fd->parent; - - /* 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; - JSAtom func_name = fd->func_name; - - /* the real object will be set at the end of the compilation */ - idx = cpool_add(s, JS_NULL); - fd->parent_cpool_idx = idx; - - if (is_expr) { - /* for constructors, no code needs to be generated here */ - if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR && - func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { - /* OP_fclosure creates the function object from the bytecode - and adds the scope information */ - emit_op(s, OP_fclosure); - emit_u32(s, idx); - if (func_name == JS_ATOM_NULL) { - emit_op(s, OP_set_name); - emit_u32(s, JS_ATOM_NULL); - } - } - } else if (func_type == JS_PARSE_FUNC_VAR) { - emit_op(s, OP_fclosure); - emit_u32(s, idx); - if (create_func_var) { - if (s->cur_func->is_global_var) { - JSGlobalVar *hf; - /* the global variable must be defined at the start of the - function */ - hf = add_global_var(ctx, s->cur_func, func_name); - if (!hf) - goto fail; - /* it is considered as defined at the top level - (needed for annex B.3.3.4 and B.3.3.5 - checks) */ - hf->scope_level = 0; - hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0); - /* store directly into global var, bypass lexical scope */ - emit_op(s, OP_dup); - emit_op(s, OP_scope_put_var); - emit_atom(s, func_name); - emit_u16(s, 0); - } else { - /* do not call define_var to bypass lexical scope check */ - func_idx = find_var(ctx, s->cur_func, func_name); - if (func_idx < 0) { - func_idx = add_var(ctx, s->cur_func, func_name); - if (func_idx < 0) - goto fail; - } - /* store directly into local var, bypass lexical catch scope */ - emit_op(s, OP_dup); - emit_op(s, OP_scope_put_var); - emit_atom(s, func_name); - emit_u16(s, 0); - } - } - if (lexical_func_idx >= 0) { - /* lexical variable will be initialized upon entering scope */ - s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; - emit_op(s, OP_drop); - } else { - /* store function object into its lexical name */ - /* XXX: could use OP_put_loc directly */ - emit_op(s, OP_scope_put_var_init); - emit_atom(s, func_name); - emit_u16(s, s->cur_func->scope_level); - } - } else { - if (!s->cur_func->is_global_var) { - int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR); - - if (var_idx < 0) - goto fail; - /* the variable will be assigned at the top of the function */ - if (var_idx & ARGUMENT_VAR_OFFSET) { - s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; - } else { - s->cur_func->vars[var_idx].func_pool_idx = idx; - } - } else { - JSAtom func_var_name; - JSGlobalVar *hf; - if (func_name == JS_ATOM_NULL) - func_var_name = JS_ATOM__default_; /* export default */ - else - func_var_name = func_name; - /* the variable will be assigned at the top of the function */ - hf = add_global_var(ctx, s->cur_func, func_var_name); - if (!hf) - goto fail; - hf->cpool_idx = idx; - if (export_flag != JS_PARSE_EXPORT_NONE) { - if (!add_export_entry(s, s->cur_func->module, func_var_name, - export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) - goto fail; - } - } - } - } - return 0; - fail: - s->cur_func = fd->parent; - js_free_function_def(ctx, fd); - if (pfd) - *pfd = NULL; - return -1; -} - -static __exception int js_parse_function_decl(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, - const uint8_t *ptr, - int function_line_num) -{ - return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr, - function_line_num, JS_PARSE_EXPORT_NONE, - NULL); -} - -static __exception int js_parse_program(JSParseState *s) -{ - JSFunctionDef *fd = s->cur_func; - int idx; - - if (next_token(s)) - return -1; - - if (js_parse_directives(s)) - return -1; - - fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) || - (fd->eval_type == JS_EVAL_TYPE_MODULE) || - !(fd->js_mode & JS_MODE_STRICT); - - if (!s->is_module) { - /* hidden variable for the return value */ - fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_); - if (idx < 0) - return -1; - } - - while (s->token.val != TOK_EOF) { - if (js_parse_source_element(s)) - return -1; - } - - if (!s->is_module) { - /* return the value of the hidden variable eval_ret_idx */ - 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_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_return(s, FALSE); - } - - return 0; -} - -static void js_parse_init(JSContext *ctx, JSParseState *s, - const char *input, size_t input_len, - const char *filename) -{ - memset(s, 0, sizeof(*s)); - s->ctx = ctx; - s->filename = filename; - s->line_num = 1; - s->buf_ptr = (const uint8_t *)input; - s->buf_end = s->buf_ptr + input_len; - s->token.val = ' '; - s->token.line_num = 1; -} - -static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, - JSValueConst this_obj, - JSVarRef **var_refs, JSStackFrame *sf) -{ - JSValue ret_val; - uint32_t tag; - - tag = JS_VALUE_GET_TAG(fun_obj); - if (tag == JS_TAG_FUNCTION_BYTECODE) { - fun_obj = js_closure(ctx, fun_obj, var_refs, sf); - ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL); - } else if (tag == JS_TAG_MODULE) { - JSModuleDef *m; - m = JS_VALUE_GET_PTR(fun_obj); - /* the module refcount should be >= 2 */ - JS_FreeValue(ctx, fun_obj); - if (js_create_module_function(ctx, m) < 0) - goto fail; - if (js_link_module(ctx, m) < 0) - goto fail; - ret_val = js_evaluate_module(ctx, m); - if (JS_IsException(ret_val)) { - fail: - return JS_EXCEPTION; - } - } else { - JS_FreeValue(ctx, fun_obj); - ret_val = JS_ThrowTypeError(ctx, "bytecode function expected"); - } - return ret_val; -} - -JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) -{ - return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); -} - -/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ -static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int flags, int scope_idx) -{ - JSParseState s1, *s = &s1; - int err, js_mode, eval_type; - JSValue fun_obj, ret_val; - JSStackFrame *sf; - JSVarRef **var_refs; - JSFunctionBytecode *b; - JSFunctionDef *fd; - JSModuleDef *m; - - js_parse_init(ctx, s, input, input_len, filename); - skip_shebang(&s->buf_ptr, s->buf_end); - - eval_type = flags & JS_EVAL_TYPE_MASK; - m = NULL; - if (eval_type == JS_EVAL_TYPE_DIRECT) { - JSObject *p; - sf = ctx->rt->current_stack_frame; - assert(sf != NULL); - assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); - p = JS_VALUE_GET_OBJ(sf->cur_func); - assert(js_class_has_bytecode(p->class_id)); - b = p->u.func.function_bytecode; - var_refs = p->u.func.var_refs; - js_mode = b->js_mode; - } else { - sf = NULL; - b = NULL; - var_refs = NULL; - js_mode = 0; - if (flags & JS_EVAL_FLAG_STRICT) - js_mode |= JS_MODE_STRICT; - if (flags & JS_EVAL_FLAG_STRIP) - js_mode |= JS_MODE_STRIP; - if (eval_type == JS_EVAL_TYPE_MODULE) { - JSAtom module_name = JS_NewAtom(ctx, filename); - if (module_name == JS_ATOM_NULL) - return JS_EXCEPTION; - m = js_new_module_def(ctx, module_name); - if (!m) - return JS_EXCEPTION; - js_mode |= JS_MODE_STRICT; - } - } - fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1); - if (!fd) - goto fail1; - s->cur_func = fd; - fd->eval_type = eval_type; - fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT); - fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); - if (eval_type == JS_EVAL_TYPE_DIRECT) { - fd->new_target_allowed = b->new_target_allowed; - fd->super_call_allowed = b->super_call_allowed; - fd->super_allowed = b->super_allowed; - fd->arguments_allowed = b->arguments_allowed; - } else { - fd->new_target_allowed = FALSE; - fd->super_call_allowed = FALSE; - fd->super_allowed = FALSE; - fd->arguments_allowed = TRUE; - } - fd->js_mode = js_mode; - fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_); - if (b) { - if (add_closure_variables(ctx, fd, b, scope_idx)) - goto fail; - } - fd->module = m; - 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: - free_token(s, &s->token); - js_free_function_def(ctx, fd); - 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)) - goto fail1; - /* Could add a flag to avoid resolution if necessary */ - if (m) { - m->func_obj = fun_obj; - if (js_resolve_module(ctx, m) < 0) - goto fail1; - fun_obj = JS_NewModuleValue(ctx, m); - } - if (flags & JS_EVAL_FLAG_COMPILE_ONLY) { - ret_val = fun_obj; - } else { - ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf); - } - return ret_val; - fail1: - /* XXX: should free all the unresolved dependencies */ - if (m) - js_free_module_def(ctx, m); - return JS_EXCEPTION; -} - -/* the indirection is needed to make 'eval' optional */ -static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int flags, int scope_idx) -{ - if (unlikely(!ctx->eval_internal)) { - return JS_ThrowTypeError(ctx, "eval is not supported"); - } - return ctx->eval_internal(ctx, this_obj, input, input_len, filename, - flags, scope_idx); -} - -static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, - JSValueConst val, int flags, int scope_idx) -{ - JSValue ret; - const char *str; - size_t len; - - if (!JS_IsString(val)) - return JS_DupValue(ctx, val); - str = JS_ToCStringLen(ctx, &len, val); - if (!str) - return JS_EXCEPTION; - ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx); - JS_FreeCString(ctx, str); - return ret; - -} - -JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int eval_flags) -{ - int eval_type = eval_flags & JS_EVAL_TYPE_MASK; - JSValue ret; - - assert(eval_type == JS_EVAL_TYPE_GLOBAL || - eval_type == JS_EVAL_TYPE_MODULE); - ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, - eval_flags, -1); - return ret; -} - -JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags) -{ - return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, - eval_flags); -} - -int JS_ResolveModule(JSContext *ctx, JSValueConst obj) -{ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - JSModuleDef *m = JS_VALUE_GET_PTR(obj); - if (js_resolve_module(ctx, m) < 0) { - js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - return -1; - } - } - return 0; -} - -/*******************************************************************/ -/* object list */ - -typedef struct { - JSObject *obj; - uint32_t hash_next; /* -1 if no next entry */ -} JSObjectListEntry; - -/* XXX: reuse it to optimize weak references */ -typedef struct { - JSObjectListEntry *object_tab; - int object_count; - int object_size; - uint32_t *hash_table; - uint32_t hash_size; -} JSObjectList; - -static void js_object_list_init(JSObjectList *s) -{ - memset(s, 0, sizeof(*s)); -} - -static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) -{ - return ((uintptr_t)p * 3163) & (hash_size - 1); -} - -static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, - uint32_t new_hash_size) -{ - JSObjectListEntry *e; - uint32_t i, h, *new_hash_table; - - new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); - if (!new_hash_table) - return -1; - js_free(ctx, s->hash_table); - s->hash_table = new_hash_table; - s->hash_size = new_hash_size; - - for(i = 0; i < s->hash_size; i++) { - s->hash_table[i] = -1; - } - for(i = 0; i < s->object_count; i++) { - e = &s->object_tab[i]; - h = js_object_list_get_hash(e->obj, s->hash_size); - e->hash_next = s->hash_table[h]; - s->hash_table[h] = i; - } - return 0; -} - -/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if - memory error */ -static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) -{ - JSObjectListEntry *e; - uint32_t h, new_hash_size; - - if (js_resize_array(ctx, (void *)&s->object_tab, - sizeof(s->object_tab[0]), - &s->object_size, s->object_count + 1)) - return -1; - if (unlikely((s->object_count + 1) >= s->hash_size)) { - new_hash_size = max_uint32(s->hash_size, 4); - while (new_hash_size <= s->object_count) - new_hash_size *= 2; - if (js_object_list_resize_hash(ctx, s, new_hash_size)) - return -1; - } - e = &s->object_tab[s->object_count++]; - h = js_object_list_get_hash(obj, s->hash_size); - e->obj = obj; - e->hash_next = s->hash_table[h]; - s->hash_table[h] = s->object_count - 1; - return 0; -} - -/* return -1 if not present or the object index */ -static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) -{ - JSObjectListEntry *e; - uint32_t h, p; - - /* must test empty size because there is no hash table */ - if (s->object_count == 0) - return -1; - h = js_object_list_get_hash(obj, s->hash_size); - p = s->hash_table[h]; - while (p != -1) { - e = &s->object_tab[p]; - if (e->obj == obj) - return p; - p = e->hash_next; - } - return -1; -} - -static void js_object_list_end(JSContext *ctx, JSObjectList *s) -{ - js_free(ctx, s->object_tab); - js_free(ctx, s->hash_table); -} - -/*******************************************************************/ -/* binary object writer & reader */ - -typedef enum BCTagEnum { - BC_TAG_NULL = 1, - BC_TAG_UNDEFINED, - BC_TAG_BOOL_FALSE, - BC_TAG_BOOL_TRUE, - BC_TAG_INT32, - BC_TAG_FLOAT64, - BC_TAG_STRING, - BC_TAG_OBJECT, - BC_TAG_ARRAY, - BC_TAG_BIG_INT, - BC_TAG_TEMPLATE_OBJECT, - BC_TAG_FUNCTION_BYTECODE, - BC_TAG_MODULE, - BC_TAG_TYPED_ARRAY, - BC_TAG_ARRAY_BUFFER, - BC_TAG_SHARED_ARRAY_BUFFER, - BC_TAG_DATE, - BC_TAG_OBJECT_VALUE, - BC_TAG_OBJECT_REFERENCE, -#ifdef CONFIG_BIGNUM - BC_TAG_BIG_FLOAT, - BC_TAG_BIG_DECIMAL, -#endif -} BCTagEnum; - -#ifdef CONFIG_BIGNUM -#define BC_VERSION 0x43 -#else -#define BC_VERSION 3 -#endif - -typedef struct BCWriterState { - JSContext *ctx; - DynBuf dbuf; - BOOL allow_bytecode : 8; - BOOL allow_sab : 8; - BOOL allow_reference : 8; - uint32_t first_atom; - uint32_t *atom_to_idx; - int atom_to_idx_size; - JSAtom *idx_to_atom; - int idx_to_atom_count; - int idx_to_atom_size; - uint8_t **sab_tab; - int sab_tab_len; - int sab_tab_size; - /* list of referenced objects (used if allow_reference = TRUE) */ - JSObjectList object_list; -} BCWriterState; - -#ifdef DUMP_READ_OBJECT -static const char * const bc_tag_str[] = { - "invalid", - "null", - "undefined", - "false", - "true", - "int32", - "float64", - "string", - "object", - "array", - "bigint", - "template", - "function", - "module", - "TypedArray", - "ArrayBuffer", - "SharedArrayBuffer", - "Date", - "ObjectValue", - "ObjectReference", -#ifdef CONFIG_BIGNUM - "bigfloat", - "bigdecimal", -#endif -}; -#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); -} - -static void bc_put_u16(BCWriterState *s, uint16_t v) -{ - 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 (is_be()) - v = bswap32(v); - dbuf_put_u32(&s->dbuf, v); -} - -static void bc_put_u64(BCWriterState *s, uint64_t v) -{ - if (is_be()) - v = bswap64(v); - dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); -} - -static void bc_put_leb128(BCWriterState *s, uint32_t v) -{ - dbuf_put_leb128(&s->dbuf, v); -} - -static void bc_put_sleb128(BCWriterState *s, int32_t v) -{ - dbuf_put_sleb128(&s->dbuf, v); -} - -static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n) -{ - *pflags = *pflags | (val << *pidx); - *pidx += n; -} - -static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) -{ - uint32_t v; - - if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { - *pres = atom; - return 0; - } - atom -= s->first_atom; - if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { - *pres = s->atom_to_idx[atom]; - return 0; - } - if (atom >= s->atom_to_idx_size) { - int old_size, i; - old_size = s->atom_to_idx_size; - if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, - sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, - atom + 1)) - return -1; - /* XXX: could add a specific js_resize_array() function to do it */ - for(i = old_size; i < s->atom_to_idx_size; i++) - s->atom_to_idx[i] = 0; - } - if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, - sizeof(s->idx_to_atom[0]), - &s->idx_to_atom_size, s->idx_to_atom_count + 1)) - goto fail; - - v = s->idx_to_atom_count++; - s->idx_to_atom[v] = atom + s->first_atom; - v += s->first_atom; - s->atom_to_idx[atom] = v; - *pres = v; - return 0; - fail: - *pres = 0; - return -1; -} - -static int bc_put_atom(BCWriterState *s, JSAtom atom) -{ - uint32_t v; - - if (__JS_AtomIsTaggedInt(atom)) { - v = (__JS_AtomToUInt32(atom) << 1) | 1; - } else { - if (bc_atom_to_idx(s, &v, atom)) - return -1; - v <<= 1; - } - bc_put_leb128(s, v); - return 0; -} - -static void bc_byte_swap(uint8_t *bc_buf, int bc_len) -{ - int pos, len, op, fmt; - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - len = short_opcode_info(op).size; - fmt = short_opcode_info(op).fmt; - switch(fmt) { - case OP_FMT_u16: - case OP_FMT_i16: - case OP_FMT_label16: - case OP_FMT_npop: - case OP_FMT_loc: - case OP_FMT_arg: - case OP_FMT_var_ref: - put_u16(bc_buf + pos + 1, - bswap16(get_u16(bc_buf + pos + 1))); - break; - case OP_FMT_i32: - case OP_FMT_u32: - case OP_FMT_const: - case OP_FMT_label: - case OP_FMT_atom: - case OP_FMT_atom_u8: - put_u32(bc_buf + pos + 1, - bswap32(get_u32(bc_buf + pos + 1))); - break; - case OP_FMT_atom_u16: - case OP_FMT_label_u16: - put_u32(bc_buf + pos + 1, - bswap32(get_u32(bc_buf + pos + 1))); - put_u16(bc_buf + pos + 1 + 4, - bswap16(get_u16(bc_buf + pos + 1 + 4))); - break; - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - put_u32(bc_buf + pos + 1, - bswap32(get_u32(bc_buf + pos + 1))); - put_u32(bc_buf + pos + 1 + 4, - bswap32(get_u32(bc_buf + pos + 1 + 4))); - if (fmt == OP_FMT_atom_label_u16) { - put_u16(bc_buf + pos + 1 + 4 + 4, - bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); - } - break; - case OP_FMT_npop_u16: - put_u16(bc_buf + pos + 1, - bswap16(get_u16(bc_buf + pos + 1))); - put_u16(bc_buf + pos + 1 + 2, - bswap16(get_u16(bc_buf + pos + 1 + 2))); - break; - default: - break; - } - pos += len; - } -} - -static int JS_WriteFunctionBytecode(BCWriterState *s, - const uint8_t *bc_buf1, int bc_len) -{ - int pos, len, op; - JSAtom atom; - uint8_t *bc_buf; - uint32_t val; - - bc_buf = js_malloc(s->ctx, bc_len); - if (!bc_buf) - return -1; - memcpy(bc_buf, bc_buf1, bc_len); - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - len = short_opcode_info(op).size; - switch(short_opcode_info(op).fmt) { - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - atom = get_u32(bc_buf + pos + 1); - if (bc_atom_to_idx(s, &val, atom)) - goto fail; - put_u32(bc_buf + pos + 1, val); - break; - default: - break; - } - pos += len; - } - - if (is_be()) - bc_byte_swap(bc_buf, bc_len); - - dbuf_put(&s->dbuf, bc_buf, bc_len); - - js_free(s->ctx, bc_buf); - return 0; - fail: - js_free(s->ctx, bc_buf); - return -1; -} - -static void JS_WriteString(BCWriterState *s, JSString *p) -{ - int i; - bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); - if (p->is_wide_char) { - for(i = 0; i < p->len; i++) - bc_put_u16(s, p->u.str16[i]); - } else { - dbuf_put(&s->dbuf, p->u.str8, p->len); - } -} - -static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) -{ - uint32_t tag, tag1; - int64_t e; - JSBigFloat *bf = JS_VALUE_GET_PTR(obj); - bf_t *a = &bf->num; - size_t len, i, n1, j; - limb_t v; - - tag = JS_VALUE_GET_TAG(obj); - switch(tag) { - case JS_TAG_BIG_INT: - tag1 = BC_TAG_BIG_INT; - break; -#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); - - /* sign + exponent */ - if (a->expn == BF_EXP_ZERO) - e = 0; - else if (a->expn == BF_EXP_INF) - e = 1; - else if (a->expn == BF_EXP_NAN) - e = 2; - else if (a->expn >= 0) - e = a->expn + 3; - else - e = a->expn; - e = (e * 2) | a->sign; - if (e < INT32_MIN || e > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); - return -1; - } - bc_put_sleb128(s, e); - - /* mantissa */ - if (a->len != 0) { - if (tag != JS_TAG_BIG_DECIMAL) { - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - v = a->tab[i]; - n1 = sizeof(limb_t); - while ((v & 0xff) == 0) { - n1--; - v >>= 8; - } - i++; - len = (a->len - i) * sizeof(limb_t) + n1; - if (len > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum is too large"); - return -1; - } - bc_put_leb128(s, len); - /* always saved in byte based little endian representation */ - for(j = 0; j < n1; j++) { - bc_put_u8(s, v >> (j * 8)); - } - for(; i < a->len; i++) { - limb_t v = a->tab[i]; -#if LIMB_BITS == 32 - bc_put_u32(s, v); -#else - bc_put_u64(s, v); -#endif - } - } else { - int bpos, d; - uint8_t v8; - size_t i0; - - /* little endian BCD */ - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - len = a->len * LIMB_DIGITS; - v = a->tab[i]; - j = 0; - while ((v % 10) == 0) { - j++; - v /= 10; - } - len -= j; - assert(len > 0); - if (len > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum is too large"); - return -1; - } - bc_put_leb128(s, len); - - bpos = 0; - v8 = 0; - i0 = i; - for(; i < a->len; i++) { - if (i != i0) { - v = a->tab[i]; - j = 0; - } - for(; j < LIMB_DIGITS; j++) { - d = v % 10; - v /= 10; - if (bpos == 0) { - v8 = d; - bpos = 1; - } else { - bc_put_u8(s, v8 | (d << 4)); - bpos = 0; - } - } - } - /* flush the last digit */ - if (bpos) { - bc_put_u8(s, v8); - } - } - } - return 0; -} - -static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); - -static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) -{ - JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); - uint32_t flags; - int idx, i; - - bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); - flags = idx = 0; - bc_set_flags(&flags, &idx, b->has_prototype, 1); - bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); - bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); - bc_set_flags(&flags, &idx, b->need_home_object, 1); - bc_set_flags(&flags, &idx, b->func_kind, 2); - bc_set_flags(&flags, &idx, b->new_target_allowed, 1); - bc_set_flags(&flags, &idx, b->super_call_allowed, 1); - bc_set_flags(&flags, &idx, b->super_allowed, 1); - bc_set_flags(&flags, &idx, b->arguments_allowed, 1); - bc_set_flags(&flags, &idx, b->has_debug, 1); - bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); - 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); - bc_put_leb128(s, b->stack_size); - bc_put_leb128(s, b->closure_var_count); - bc_put_leb128(s, b->cpool_count); - bc_put_leb128(s, b->byte_code_len); - if (b->vardefs) { - /* XXX: this field is redundant */ - bc_put_leb128(s, b->arg_count + b->var_count); - for(i = 0; i < b->arg_count + b->var_count; i++) { - JSVarDef *vd = &b->vardefs[i]; - bc_put_atom(s, vd->var_name); - bc_put_leb128(s, vd->scope_level); - bc_put_leb128(s, vd->scope_next + 1); - flags = idx = 0; - bc_set_flags(&flags, &idx, vd->var_kind, 4); - bc_set_flags(&flags, &idx, vd->is_const, 1); - bc_set_flags(&flags, &idx, vd->is_lexical, 1); - bc_set_flags(&flags, &idx, vd->is_captured, 1); - assert(idx <= 8); - bc_put_u8(s, flags); - } - } else { - bc_put_leb128(s, 0); - } - - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - bc_put_atom(s, cv->var_name); - bc_put_leb128(s, cv->var_idx); - flags = idx = 0; - bc_set_flags(&flags, &idx, cv->is_local, 1); - bc_set_flags(&flags, &idx, cv->is_arg, 1); - bc_set_flags(&flags, &idx, cv->is_const, 1); - bc_set_flags(&flags, &idx, cv->is_lexical, 1); - bc_set_flags(&flags, &idx, cv->var_kind, 4); - assert(idx <= 8); - bc_put_u8(s, flags); - } - - if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) - goto fail; - - if (b->has_debug) { - bc_put_atom(s, b->debug.filename); - bc_put_leb128(s, b->debug.line_num); - bc_put_leb128(s, b->debug.pc2line_len); - dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); - } - - for(i = 0; i < b->cpool_count; i++) { - if (JS_WriteObjectRec(s, b->cpool[i])) - goto fail; - } - return 0; - fail: - return -1; -} - -static int JS_WriteModule(BCWriterState *s, JSValueConst obj) -{ - JSModuleDef *m = JS_VALUE_GET_PTR(obj); - int i; - - bc_put_u8(s, BC_TAG_MODULE); - bc_put_atom(s, m->module_name); - - bc_put_leb128(s, m->req_module_entries_count); - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - bc_put_atom(s, rme->module_name); - } - - bc_put_leb128(s, m->export_entries_count); - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - bc_put_u8(s, me->export_type); - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - bc_put_leb128(s, me->u.local.var_idx); - } else { - bc_put_leb128(s, me->u.req_module_idx); - bc_put_atom(s, me->local_name); - } - bc_put_atom(s, me->export_name); - } - - bc_put_leb128(s, m->star_export_entries_count); - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - bc_put_leb128(s, se->req_module_idx); - } - - bc_put_leb128(s, m->import_entries_count); - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - bc_put_leb128(s, mi->var_idx); - bc_put_atom(s, mi->import_name); - bc_put_leb128(s, mi->req_module_idx); - } - - bc_put_u8(s, m->has_tla); - - if (JS_WriteObjectRec(s, m->func_obj)) - goto fail; - return 0; - fail: - return -1; -} - -static int JS_WriteArray(BCWriterState *s, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - uint32_t i, len; - JSValue val; - int ret; - BOOL is_template; - - if (s->allow_bytecode && !p->extensible) { - /* not extensible array: we consider it is a - template when we are saving bytecode */ - bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); - is_template = TRUE; - } else { - bc_put_u8(s, BC_TAG_ARRAY); - is_template = FALSE; - } - if (js_get_length32(s->ctx, &len, obj)) - goto fail1; - bc_put_leb128(s, len); - for(i = 0; i < len; i++) { - val = JS_GetPropertyUint32(s->ctx, obj, i); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; - } - if (is_template) { - val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; - } - return 0; - fail1: - return -1; -} - -static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - uint32_t i, prop_count; - JSShape *sh; - JSShapeProperty *pr; - int pass; - JSAtom atom; - - bc_put_u8(s, BC_TAG_OBJECT); - prop_count = 0; - sh = p->shape; - for(pass = 0; pass < 2; pass++) { - if (pass == 1) - bc_put_leb128(s, prop_count); - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { - atom = pr->atom; - if (atom != JS_ATOM_NULL && - JS_AtomIsString(s->ctx, atom) && - (pr->flags & JS_PROP_ENUMERABLE)) { - if (pr->flags & JS_PROP_TMASK) { - JS_ThrowTypeError(s->ctx, "only value properties are supported"); - goto fail; - } - if (pass == 0) { - prop_count++; - } else { - bc_put_atom(s, atom); - if (JS_WriteObjectRec(s, p->prop[i].u.value)) - goto fail; - } - } - } - } - return 0; - fail: - return -1; -} - -static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - JSTypedArray *ta = p->u.typed_array; - - bc_put_u8(s, BC_TAG_TYPED_ARRAY); - bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); - bc_put_leb128(s, p->u.array.count); - bc_put_leb128(s, ta->offset); - if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) - return -1; - return 0; -} - -static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - JSArrayBuffer *abuf = p->u.array_buffer; - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); - return -1; - } - bc_put_u8(s, BC_TAG_ARRAY_BUFFER); - bc_put_leb128(s, abuf->byte_length); - dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); - return 0; -} - -static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - JSArrayBuffer *abuf = p->u.array_buffer; - assert(!abuf->detached); /* SharedArrayBuffer are never detached */ - bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); - bc_put_leb128(s, abuf->byte_length); - bc_put_u64(s, (uintptr_t)abuf->data); - if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), - &s->sab_tab_size, s->sab_tab_len + 1)) - return -1; - /* keep the SAB pointer so that the user can clone it or free it */ - s->sab_tab[s->sab_tab_len++] = abuf->data; - return 0; -} - -static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) -{ - uint32_t tag; - - if (js_check_stack_overflow(s->ctx->rt, 0)) { - JS_ThrowStackOverflow(s->ctx); - return -1; - } - - tag = JS_VALUE_GET_NORM_TAG(obj); - switch(tag) { - case JS_TAG_NULL: - bc_put_u8(s, BC_TAG_NULL); - break; - case JS_TAG_UNDEFINED: - bc_put_u8(s, BC_TAG_UNDEFINED); - break; - case JS_TAG_BOOL: - bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); - break; - case JS_TAG_INT: - bc_put_u8(s, BC_TAG_INT32); - bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); - break; - case JS_TAG_FLOAT64: - { - JSFloat64Union u; - bc_put_u8(s, BC_TAG_FLOAT64); - u.d = JS_VALUE_GET_FLOAT64(obj); - bc_put_u64(s, u.u64); - } - break; - case JS_TAG_STRING: - { - JSString *p = JS_VALUE_GET_STRING(obj); - bc_put_u8(s, BC_TAG_STRING); - JS_WriteString(s, p); - } - break; - case JS_TAG_FUNCTION_BYTECODE: - if (!s->allow_bytecode) - goto invalid_tag; - if (JS_WriteFunctionTag(s, obj)) - goto fail; - break; - case JS_TAG_MODULE: - if (!s->allow_bytecode) - goto invalid_tag; - if (JS_WriteModule(s, obj)) - goto fail; - break; - case JS_TAG_OBJECT: - { - JSObject *p = JS_VALUE_GET_OBJ(obj); - int ret, idx; - - if (s->allow_reference) { - idx = js_object_list_find(s->ctx, &s->object_list, p); - if (idx >= 0) { - bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); - bc_put_leb128(s, idx); - break; - } else { - if (js_object_list_add(s->ctx, &s->object_list, p)) - goto fail; - } - } else { - if (p->tmp_mark) { - JS_ThrowTypeError(s->ctx, "circular reference"); - goto fail; - } - p->tmp_mark = 1; - } - switch(p->class_id) { - case JS_CLASS_ARRAY: - ret = JS_WriteArray(s, obj); - break; - case JS_CLASS_OBJECT: - ret = JS_WriteObjectTag(s, obj); - break; - case JS_CLASS_ARRAY_BUFFER: - ret = JS_WriteArrayBuffer(s, obj); - break; - case JS_CLASS_SHARED_ARRAY_BUFFER: - if (!s->allow_sab) - goto invalid_tag; - ret = JS_WriteSharedArrayBuffer(s, obj); - break; - case JS_CLASS_DATE: - bc_put_u8(s, BC_TAG_DATE); - ret = JS_WriteObjectRec(s, p->u.object_data); - break; - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: - 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; - default: - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_WriteTypedArray(s, obj); - } else { - JS_ThrowTypeError(s->ctx, "unsupported object class"); - ret = -1; - } - break; - } - p->tmp_mark = 0; - if (ret) - goto fail; - } - break; - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: -#endif - if (JS_WriteBigNum(s, obj)) - goto fail; - break; - default: - invalid_tag: - JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); - goto fail; - } - return 0; - - fail: - return -1; -} - -/* create the atom table */ -static int JS_WriteObjectAtoms(BCWriterState *s) -{ - JSRuntime *rt = s->ctx->rt; - DynBuf dbuf1; - int i, atoms_size; - - dbuf1 = s->dbuf; - js_dbuf_init(s->ctx, &s->dbuf); - 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++) { - JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]]; - JS_WriteString(s, p); - } - /* XXX: should check for OOM in above phase */ - - /* move the atoms at the start */ - /* XXX: could just append dbuf1 data, but it uses more memory if - dbuf1 is larger than dbuf */ - atoms_size = s->dbuf.size; - if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size)) - goto fail; - memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); - memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); - dbuf1.size += atoms_size; - dbuf_free(&s->dbuf); - s->dbuf = dbuf1; - return 0; - fail: - dbuf_free(&dbuf1); - return -1; -} - -uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags, uint8_t ***psab_tab, size_t *psab_tab_len) -{ - BCWriterState ss, *s = &ss; - - memset(s, 0, sizeof(*s)); - s->ctx = ctx; - s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); - s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); - s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); - /* XXX: could use a different version when bytecode is included */ - if (s->allow_bytecode) - s->first_atom = JS_ATOM_END; - else - s->first_atom = 1; - js_dbuf_init(ctx, &s->dbuf); - js_object_list_init(&s->object_list); - - if (JS_WriteObjectRec(s, obj)) - goto fail; - if (JS_WriteObjectAtoms(s)) - goto fail; - js_object_list_end(ctx, &s->object_list); - js_free(ctx, s->atom_to_idx); - js_free(ctx, s->idx_to_atom); - *psize = s->dbuf.size; - if (psab_tab) - *psab_tab = s->sab_tab; - if (psab_tab_len) - *psab_tab_len = s->sab_tab_len; - return s->dbuf.buf; - fail: - js_object_list_end(ctx, &s->object_list); - js_free(ctx, s->atom_to_idx); - js_free(ctx, s->idx_to_atom); - dbuf_free(&s->dbuf); - *psize = 0; - if (psab_tab) - *psab_tab = NULL; - if (psab_tab_len) - *psab_tab_len = 0; - return NULL; -} - -uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags) -{ - return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL); -} - -typedef struct BCReaderState { - JSContext *ctx; - const uint8_t *buf_start, *ptr, *buf_end; - uint32_t first_atom; - uint32_t idx_to_atom_count; - JSAtom *idx_to_atom; - int error_state; - BOOL allow_sab : 8; - BOOL allow_bytecode : 8; - BOOL is_rom_data : 8; - BOOL allow_reference : 8; - /* object references */ - JSObject **objects; - int objects_count; - int objects_size; - -#ifdef DUMP_READ_OBJECT - const uint8_t *ptr_last; - int level; -#endif -} BCReaderState; - -#ifdef DUMP_READ_OBJECT -static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) { - va_list ap; - int i, n, n0; - - if (!s->ptr_last) - s->ptr_last = s->buf_start; - - n = n0 = 0; - if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { - n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); - n += n0; - } - for (i = 0; s->ptr_last < s->ptr; i++) { - if ((i & 7) == 0 && i > 0) { - printf("\n%*s", n0, ""); - n = n0; - } - n += printf(" %02x", *s->ptr_last++); - } - if (*fmt == '}') - s->level--; - if (n < 32 + s->level * 2) { - printf("%*s", 32 + s->level * 2 - n, ""); - } - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - va_end(ap); - if (strchr(fmt, '{')) - s->level++; -} -#else -#define bc_read_trace(...) -#endif - -static int bc_read_error_end(BCReaderState *s) -{ - if (!s->error_state) { - JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); - } - return s->error_state = -1; -} - -static int bc_get_u8(BCReaderState *s, uint8_t *pval) -{ - if (unlikely(s->buf_end - s->ptr < 1)) { - *pval = 0; /* avoid warning */ - return bc_read_error_end(s); - } - *pval = *s->ptr++; - return 0; -} - -static int bc_get_u16(BCReaderState *s, uint16_t *pval) -{ - uint16_t v; - if (unlikely(s->buf_end - s->ptr < 2)) { - *pval = 0; /* avoid warning */ - return bc_read_error_end(s); - } - 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); - } - 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); - } - v = get_u64(s->ptr); - if (is_be()) - v = bswap64(v); - *pval = v; - s->ptr += 8; - return 0; -} - -static int bc_get_leb128(BCReaderState *s, uint32_t *pval) -{ - int ret; - ret = get_leb128(pval, s->ptr, s->buf_end); - if (unlikely(ret < 0)) - return bc_read_error_end(s); - s->ptr += ret; - return 0; -} - -static int bc_get_sleb128(BCReaderState *s, int32_t *pval) -{ - int ret; - ret = get_sleb128(pval, s->ptr, s->buf_end); - if (unlikely(ret < 0)) - return bc_read_error_end(s); - s->ptr += ret; - return 0; -} - -/* XXX: used to read an `int` with a positive value */ -static int bc_get_leb128_int(BCReaderState *s, int *pval) -{ - return bc_get_leb128(s, (uint32_t *)pval); -} - -static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval) -{ - uint32_t val; - if (bc_get_leb128(s, &val)) { - *pval = 0; - return -1; - } - *pval = val; - return 0; -} - -static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len) -{ - if (buf_len != 0) { - if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) - return bc_read_error_end(s); - memcpy(buf, s->ptr, buf_len); - s->ptr += buf_len; - } - return 0; -} - -static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) -{ - JSAtom atom; - - if (__JS_AtomIsTaggedInt(idx)) { - atom = idx; - } else if (idx < s->first_atom) { - atom = JS_DupAtom(s->ctx, idx); - } else { - idx -= s->first_atom; - if (idx >= s->idx_to_atom_count) { - JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", - (unsigned int)(s->ptr - s->buf_start)); - *patom = JS_ATOM_NULL; - return s->error_state = -1; - } - atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); - } - *patom = atom; - return 0; -} - -static int bc_get_atom(BCReaderState *s, JSAtom *patom) -{ - uint32_t v; - if (bc_get_leb128(s, &v)) - return -1; - if (v & 1) { - *patom = __JS_AtomFromUInt32(v >> 1); - return 0; - } else { - return bc_idx_to_atom(s, patom, v >> 1); - } -} - -static JSString *JS_ReadString(BCReaderState *s) -{ - uint32_t len; - size_t size; - BOOL is_wide_char; - JSString *p; - - if (bc_get_leb128(s, &len)) - return NULL; - is_wide_char = len & 1; - len >>= 1; - p = js_alloc_string(s->ctx, len, is_wide_char); - if (!p) { - s->error_state = -1; - return NULL; - } - size = (size_t)len << is_wide_char; - if ((s->buf_end - s->ptr) < size) { - bc_read_error_end(s); - js_free_string(s->ctx->rt, p); - return NULL; - } - memcpy(p->u.str8, s->ptr, size); - s->ptr += size; - if (is_wide_char) { - 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 - JS_DumpString(s->ctx->rt, p); printf("\n"); -#endif - return p; -} - -static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n) -{ - uint32_t val; - /* XXX: this does not work for n == 32 */ - val = (flags >> *pidx) & ((1U << n) - 1); - *pidx += n; - return val; -} - -static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, - int byte_code_offset, uint32_t bc_len) -{ - uint8_t *bc_buf; - int pos, len, op; - JSAtom atom; - uint32_t idx; - - if (s->is_rom_data) { - /* directly use the input buffer */ - if (unlikely(s->buf_end - s->ptr < bc_len)) - return bc_read_error_end(s); - bc_buf = (uint8_t *)s->ptr; - s->ptr += bc_len; - } else { - bc_buf = (void *)((uint8_t*)b + byte_code_offset); - if (bc_get_buf(s, bc_buf, bc_len)) - return -1; - } - b->byte_code_buf = bc_buf; - - if (is_be()) - bc_byte_swap(bc_buf, bc_len); - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - len = short_opcode_info(op).size; - switch(short_opcode_info(op).fmt) { - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - idx = get_u32(bc_buf + pos + 1); - if (s->is_rom_data) { - /* just increment the reference count of the atom */ - JS_DupAtom(s->ctx, (JSAtom)idx); - } else { - if (bc_idx_to_atom(s, &atom, idx)) { - /* Note: the atoms will be freed up to this position */ - b->byte_code_len = pos; - return -1; - } - put_u32(bc_buf + pos + 1, atom); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n"); -#endif - } - break; - default: - break; - } - pos += len; - } - return 0; -} - -static JSValue JS_ReadBigNum(BCReaderState *s, int tag) -{ - JSValue obj = JS_UNDEFINED; - uint8_t v8; - int32_t e; - uint32_t len; - limb_t l, i, n; - JSBigFloat *p; - limb_t v; - bf_t *a; - - p = js_new_bf(s->ctx); - if (!p) - goto fail; - switch(tag) { - case BC_TAG_BIG_INT: - obj = JS_MKPTR(JS_TAG_BIG_INT, p); - break; -#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(); - } - - /* sign + exponent */ - if (bc_get_sleb128(s, &e)) - goto fail; - - a = &p->num; - a->sign = e & 1; - e >>= 1; - if (e == 0) - a->expn = BF_EXP_ZERO; - else if (e == 1) - a->expn = BF_EXP_INF; - else if (e == 2) - a->expn = BF_EXP_NAN; - else if (e >= 3) - a->expn = e - 3; - else - a->expn = e; - - /* mantissa */ - if (a->expn != BF_EXP_ZERO && - a->expn != BF_EXP_INF && - a->expn != BF_EXP_NAN) { - if (bc_get_leb128(s, &len)) - goto fail; - bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); - if (len == 0) { - JS_ThrowInternalError(s->ctx, "invalid bignum length"); - goto fail; - } -#ifdef CONFIG_BIGNUM - if (tag == BC_TAG_BIG_DECIMAL) { - l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS; - } else -#endif - { - l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); - } - if (bf_resize(a, l)) { - JS_ThrowOutOfMemory(s->ctx); - goto fail; - } -#ifdef CONFIG_BIGNUM - if (tag == BC_TAG_BIG_DECIMAL) { - limb_t j; - int bpos, d; - - 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; - } - } else -#endif /* CONFIG_BIGNUM */ - { - 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; -#else - if (bc_get_u64(s, &v)) - goto fail; -#endif - a->tab[i] = v; - } - } - } - bc_read_trace(s, "}\n"); - return obj; - fail: - JS_FreeValue(s->ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadObjectRec(BCReaderState *s); - -static int BC_add_object_ref1(BCReaderState *s, JSObject *p) -{ - if (s->allow_reference) { - if (js_resize_array(s->ctx, (void *)&s->objects, - sizeof(s->objects[0]), - &s->objects_size, s->objects_count + 1)) - return -1; - s->objects[s->objects_count++] = p; - } - return 0; -} - -static int BC_add_object_ref(BCReaderState *s, JSValueConst obj) -{ - return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); -} - -static JSValue JS_ReadFunctionTag(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - JSFunctionBytecode bc, *b; - JSValue obj = JS_UNDEFINED; - uint16_t v16; - uint8_t v8; - int idx, i, local_count; - int function_size, cpool_offset, byte_code_offset; - int closure_var_offset, vardefs_offset; - - memset(&bc, 0, sizeof(bc)); - bc.header.ref_count = 1; - //bc.gc_header.mark = 0; - - if (bc_get_u16(s, &v16)) - goto fail; - idx = 0; - bc.has_prototype = bc_get_flags(v16, &idx, 1); - bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); - bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); - bc.need_home_object = bc_get_flags(v16, &idx, 1); - bc.func_kind = bc_get_flags(v16, &idx, 2); - bc.new_target_allowed = bc_get_flags(v16, &idx, 1); - bc.super_call_allowed = bc_get_flags(v16, &idx, 1); - bc.super_allowed = bc_get_flags(v16, &idx, 1); - bc.arguments_allowed = bc_get_flags(v16, &idx, 1); - bc.has_debug = bc_get_flags(v16, &idx, 1); - bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); - bc.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; - bc.js_mode = v8; - if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure - goto fail; - if (bc_get_leb128_u16(s, &bc.arg_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.var_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.defined_arg_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.stack_size)) - goto fail; - if (bc_get_leb128_int(s, &bc.closure_var_count)) - goto fail; - if (bc_get_leb128_int(s, &bc.cpool_count)) - goto fail; - if (bc_get_leb128_int(s, &bc.byte_code_len)) - goto fail; - if (bc_get_leb128_int(s, &local_count)) - goto fail; - - if (bc.has_debug) { - function_size = sizeof(*b); - } else { - function_size = offsetof(JSFunctionBytecode, debug); - } - cpool_offset = function_size; - function_size += bc.cpool_count * sizeof(*bc.cpool); - vardefs_offset = function_size; - function_size += local_count * sizeof(*bc.vardefs); - closure_var_offset = function_size; - function_size += bc.closure_var_count * sizeof(*bc.closure_var); - byte_code_offset = function_size; - if (!bc.read_only_bytecode) { - function_size += bc.byte_code_len; - } - - b = js_mallocz(ctx, function_size); - if (!b) - return JS_EXCEPTION; - - memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); - b->header.ref_count = 1; - if (local_count != 0) { - b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - } - if (b->closure_var_count != 0) { - b->closure_var = (void *)((uint8_t*)b + closure_var_offset); - } - 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 - bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); -#endif - bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", - b->arg_count, b->var_count, b->defined_arg_count, - b->closure_var_count, b->cpool_count); - bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", - b->stack_size, b->byte_code_len, local_count); - - if (local_count != 0) { - bc_read_trace(s, "vars {\n"); - for(i = 0; i < local_count; i++) { - JSVarDef *vd = &b->vardefs[i]; - if (bc_get_atom(s, &vd->var_name)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_level)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_next)) - goto fail; - vd->scope_next--; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - vd->var_kind = bc_get_flags(v8, &idx, 4); - vd->is_const = bc_get_flags(v8, &idx, 1); - vd->is_lexical = bc_get_flags(v8, &idx, 1); - vd->is_captured = bc_get_flags(v8, &idx, 1); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - if (b->closure_var_count != 0) { - bc_read_trace(s, "closure vars {\n"); - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - int var_idx; - if (bc_get_atom(s, &cv->var_name)) - goto fail; - if (bc_get_leb128_int(s, &var_idx)) - goto fail; - cv->var_idx = var_idx; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - cv->is_local = bc_get_flags(v8, &idx, 1); - cv->is_arg = bc_get_flags(v8, &idx, 1); - cv->is_const = bc_get_flags(v8, &idx, 1); - cv->is_lexical = bc_get_flags(v8, &idx, 1); - cv->var_kind = bc_get_flags(v8, &idx, 4); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - { - bc_read_trace(s, "bytecode {\n"); - if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) - goto fail; - bc_read_trace(s, "}\n"); - } - if (b->has_debug) { - /* read optional debug information */ - bc_read_trace(s, "debug {\n"); - if (bc_get_atom(s, &b->debug.filename)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.line_num)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.pc2line_len)) - goto fail; - if (b->debug.pc2line_len) { - b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); - if (!b->debug.pc2line_buf) - goto fail; - if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) - goto fail; - } -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); -#endif - bc_read_trace(s, "}\n"); - } - if (b->cpool_count != 0) { - bc_read_trace(s, "cpool {\n"); - for(i = 0; i < b->cpool_count; i++) { - JSValue val; - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - b->cpool[i] = val; - } - bc_read_trace(s, "}\n"); - } - b->realm = JS_DupContext(ctx); - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadModule(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - JSValue obj; - JSModuleDef *m = NULL; - JSAtom module_name; - int i; - uint8_t v8; - - if (bc_get_atom(s, &module_name)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); -#endif - m = js_new_module_def(ctx, module_name); - if (!m) - goto fail; - obj = JS_NewModuleValue(ctx, m); - if (bc_get_leb128_int(s, &m->req_module_entries_count)) - goto fail; - if (m->req_module_entries_count != 0) { - m->req_module_entries_size = m->req_module_entries_count; - m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); - if (!m->req_module_entries) - goto fail; - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (bc_get_atom(s, &rme->module_name)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->export_entries_count)) - goto fail; - if (m->export_entries_count != 0) { - m->export_entries_size = m->export_entries_count; - m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); - if (!m->export_entries) - goto fail; - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (bc_get_u8(s, &v8)) - goto fail; - me->export_type = v8; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - if (bc_get_leb128_int(s, &me->u.local.var_idx)) - goto fail; - } else { - if (bc_get_leb128_int(s, &me->u.req_module_idx)) - goto fail; - if (bc_get_atom(s, &me->local_name)) - goto fail; - } - if (bc_get_atom(s, &me->export_name)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->star_export_entries_count)) - goto fail; - if (m->star_export_entries_count != 0) { - m->star_export_entries_size = m->star_export_entries_count; - m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); - if (!m->star_export_entries) - goto fail; - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - if (bc_get_leb128_int(s, &se->req_module_idx)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->import_entries_count)) - goto fail; - if (m->import_entries_count != 0) { - m->import_entries_size = m->import_entries_count; - m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); - if (!m->import_entries) - goto fail; - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - if (bc_get_leb128_int(s, &mi->var_idx)) - goto fail; - if (bc_get_atom(s, &mi->import_name)) - goto fail; - if (bc_get_leb128_int(s, &mi->req_module_idx)) - goto fail; - } - } - - 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; - return obj; - fail: - if (m) { - js_free_module_def(ctx, m); - } - return JS_EXCEPTION; -} - -static JSValue JS_ReadObjectTag(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - JSValue obj; - uint32_t prop_count, i; - JSAtom atom; - JSValue val; - int ret; - - obj = JS_NewObject(ctx); - if (BC_add_object_ref(s, obj)) - goto fail; - if (bc_get_leb128(s, &prop_count)) - goto fail; - for(i = 0; i < prop_count; i++) { - if (bc_get_atom(s, &atom)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); -#endif - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) { - JS_FreeAtom(ctx, atom); - goto fail; - } - ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); - JS_FreeAtom(ctx, atom); - if (ret < 0) - goto fail; - } - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadArray(BCReaderState *s, int tag) -{ - JSContext *ctx = s->ctx; - JSValue obj; - uint32_t len, i; - JSValue val; - int ret, prop_flags; - BOOL is_template; - - obj = JS_NewArray(ctx); - if (BC_add_object_ref(s, obj)) - goto fail; - is_template = (tag == BC_TAG_TEMPLATE_OBJECT); - if (bc_get_leb128(s, &len)) - goto fail; - for(i = 0; i < len; i++) { - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (is_template) - prop_flags = JS_PROP_ENUMERABLE; - else - prop_flags = JS_PROP_C_W_E; - ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, - prop_flags); - if (ret < 0) - goto fail; - } - if (is_template) { - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (!JS_IsUndefined(val)) { - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); - if (ret < 0) - goto fail; - } - JS_PreventExtensions(ctx, obj); - } - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadTypedArray(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; - uint8_t array_tag; - JSValueConst args[3]; - uint32_t offset, len, idx; - - if (bc_get_u8(s, &array_tag)) - return JS_EXCEPTION; - if (array_tag >= JS_TYPED_ARRAY_COUNT) - return JS_ThrowTypeError(ctx, "invalid typed array"); - if (bc_get_leb128(s, &len)) - return JS_EXCEPTION; - if (bc_get_leb128(s, &offset)) - return JS_EXCEPTION; - /* XXX: this hack could be avoided if the typed array could be - created before the array buffer */ - idx = s->objects_count; - if (BC_add_object_ref1(s, NULL)) - goto fail; - array_buffer = JS_ReadObjectRec(s); - if (JS_IsException(array_buffer)) - return JS_EXCEPTION; - if (!js_get_array_buffer(ctx, array_buffer)) { - JS_FreeValue(ctx, array_buffer); - return JS_EXCEPTION; - } - args[0] = array_buffer; - args[1] = JS_NewInt64(ctx, offset); - args[2] = JS_NewInt64(ctx, len); - obj = js_typed_array_constructor(ctx, JS_UNDEFINED, - 3, args, - JS_CLASS_UINT8C_ARRAY + array_tag); - if (JS_IsException(obj)) - goto fail; - if (s->allow_reference) { - s->objects[idx] = JS_VALUE_GET_OBJ(obj); - } - JS_FreeValue(ctx, array_buffer); - return obj; - fail: - JS_FreeValue(ctx, array_buffer); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadArrayBuffer(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - uint32_t byte_length; - JSValue obj; - - if (bc_get_leb128(s, &byte_length)) - return JS_EXCEPTION; - if (unlikely(s->buf_end - s->ptr < byte_length)) { - bc_read_error_end(s); - return JS_EXCEPTION; - } - obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length); - if (JS_IsException(obj)) - goto fail; - if (BC_add_object_ref(s, obj)) - goto fail; - s->ptr += byte_length; - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - uint32_t byte_length; - uint8_t *data_ptr; - JSValue obj; - uint64_t u64; - - if (bc_get_leb128(s, &byte_length)) - return JS_EXCEPTION; - if (bc_get_u64(s, &u64)) - return JS_EXCEPTION; - data_ptr = (uint8_t *)(uintptr_t)u64; - /* the SharedArrayBuffer is cloned */ - obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length, - JS_CLASS_SHARED_ARRAY_BUFFER, - data_ptr, - NULL, NULL, FALSE); - if (JS_IsException(obj)) - goto fail; - if (BC_add_object_ref(s, obj)) - goto fail; - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadDate(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - JSValue val, obj = JS_UNDEFINED; - - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (!JS_IsNumber(val)) { - JS_ThrowTypeError(ctx, "Number tag expected for date"); - goto fail; - } - obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], - JS_CLASS_DATE); - if (JS_IsException(obj)) - goto fail; - if (BC_add_object_ref(s, obj)) - goto fail; - JS_SetObjectData(ctx, obj, val); - return obj; - fail: - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadObjectValue(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - JSValue val, obj = JS_UNDEFINED; - - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - obj = JS_ToObject(ctx, val); - if (JS_IsException(obj)) - goto fail; - if (BC_add_object_ref(s, obj)) - goto fail; - JS_FreeValue(ctx, val); - return obj; - fail: - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadObjectRec(BCReaderState *s) -{ - JSContext *ctx = s->ctx; - uint8_t tag; - JSValue obj = JS_UNDEFINED; - - if (js_check_stack_overflow(ctx->rt, 0)) - return JS_ThrowStackOverflow(ctx); - - if (bc_get_u8(s, &tag)) - return JS_EXCEPTION; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - switch(tag) { - case BC_TAG_NULL: - obj = JS_NULL; - break; - case BC_TAG_UNDEFINED: - obj = JS_UNDEFINED; - break; - case BC_TAG_BOOL_FALSE: - case BC_TAG_BOOL_TRUE: - obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); - break; - case BC_TAG_INT32: - { - int32_t val; - if (bc_get_sleb128(s, &val)) - return JS_EXCEPTION; - bc_read_trace(s, "%d\n", val); - obj = JS_NewInt32(ctx, val); - } - break; - case BC_TAG_FLOAT64: - { - JSFloat64Union u; - if (bc_get_u64(s, &u.u64)) - return JS_EXCEPTION; - bc_read_trace(s, "%g\n", u.d); - obj = __JS_NewFloat64(ctx, u.d); - } - break; - case BC_TAG_STRING: - { - JSString *p; - p = JS_ReadString(s); - if (!p) - return JS_EXCEPTION; - obj = JS_MKPTR(JS_TAG_STRING, p); - } - break; - case BC_TAG_FUNCTION_BYTECODE: - if (!s->allow_bytecode) - goto invalid_tag; - obj = JS_ReadFunctionTag(s); - break; - case BC_TAG_MODULE: - if (!s->allow_bytecode) - goto invalid_tag; - obj = JS_ReadModule(s); - break; - case BC_TAG_OBJECT: - obj = JS_ReadObjectTag(s); - break; - case BC_TAG_ARRAY: - case BC_TAG_TEMPLATE_OBJECT: - obj = JS_ReadArray(s, tag); - break; - case BC_TAG_TYPED_ARRAY: - obj = JS_ReadTypedArray(s); - break; - case BC_TAG_ARRAY_BUFFER: - obj = JS_ReadArrayBuffer(s); - break; - case BC_TAG_SHARED_ARRAY_BUFFER: - if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) - goto invalid_tag; - obj = JS_ReadSharedArrayBuffer(s); - break; - case BC_TAG_DATE: - obj = JS_ReadDate(s); - break; - case BC_TAG_OBJECT_VALUE: - obj = JS_ReadObjectValue(s); - break; - case BC_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case BC_TAG_BIG_FLOAT: - case BC_TAG_BIG_DECIMAL: -#endif - obj = JS_ReadBigNum(s, tag); - break; - case BC_TAG_OBJECT_REFERENCE: - { - uint32_t val; - if (!s->allow_reference) - return JS_ThrowSyntaxError(ctx, "object references are not allowed"); - if (bc_get_leb128(s, &val)) - return JS_EXCEPTION; - bc_read_trace(s, "%u\n", val); - if (val >= s->objects_count) { - return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", - val, s->objects_count); - } - obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); - } - break; - default: - invalid_tag: - return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", - tag, (unsigned int)(s->ptr - s->buf_start)); - } - bc_read_trace(s, "}\n"); - return obj; -} - -static int JS_ReadObjectAtoms(BCReaderState *s) -{ - uint8_t v8; - JSString *p; - int i; - JSAtom atom; - - if (bc_get_u8(s, &v8)) - return -1; - if (v8 != BC_VERSION) { - JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", - v8, BC_VERSION); - return -1; - } - if (bc_get_leb128(s, &s->idx_to_atom_count)) - return -1; - - bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count); - - if (s->idx_to_atom_count != 0) { - s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * - sizeof(s->idx_to_atom[0])); - if (!s->idx_to_atom) - return s->error_state = -1; - } - for(i = 0; i < s->idx_to_atom_count; i++) { - p = JS_ReadString(s); - if (!p) - return -1; - atom = JS_NewAtomStr(s->ctx, p); - if (atom == JS_ATOM_NULL) - return s->error_state = -1; - s->idx_to_atom[i] = atom; - if (s->is_rom_data && (atom != (i + s->first_atom))) - s->is_rom_data = FALSE; /* atoms must be relocated */ - } - bc_read_trace(s, "}\n"); - return 0; -} - -static void bc_reader_free(BCReaderState *s) -{ - int i; - if (s->idx_to_atom) { - for(i = 0; i < s->idx_to_atom_count; i++) { - JS_FreeAtom(s->ctx, s->idx_to_atom[i]); - } - js_free(s->ctx, s->idx_to_atom); - } - js_free(s->ctx, s->objects); -} - -JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags) -{ - BCReaderState ss, *s = &ss; - JSValue obj; - - ctx->binary_object_count += 1; - ctx->binary_object_size += buf_len; - - memset(s, 0, sizeof(*s)); - s->ctx = ctx; - s->buf_start = buf; - s->buf_end = buf + buf_len; - s->ptr = buf; - s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); - s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); - s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); - s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); - if (s->allow_bytecode) - s->first_atom = JS_ATOM_END; - else - s->first_atom = 1; - if (JS_ReadObjectAtoms(s)) { - obj = JS_EXCEPTION; - } else { - obj = JS_ReadObjectRec(s); - } - bc_reader_free(s); - return obj; -} - -/*******************************************************************/ -/* runtime functions & objects */ - -static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -static int check_function(JSContext *ctx, JSValueConst obj) -{ - if (likely(JS_IsFunction(ctx, obj))) - return 0; - JS_ThrowTypeError(ctx, "not a function"); - return -1; -} - -static int check_exception_free(JSContext *ctx, JSValue obj) -{ - JS_FreeValue(ctx, obj); - return JS_IsException(obj); -} - -static JSAtom find_atom(JSContext *ctx, const char *name) -{ - JSAtom atom; - int len; - - if (*name == '[') { - name++; - len = strlen(name) - 1; - /* We assume 8 bit non null strings, which is the case for these - symbols */ - for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) { - JSAtomStruct *p = ctx->rt->atom_array[atom]; - JSString *str = p; - if (str->len == len && !memcmp(str->u.str8, name, len)) - return JS_DupAtom(ctx, atom); - } - abort(); - } else { - atom = JS_NewAtom(ctx, name); - } - return atom; -} - -static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, - JSAtom atom, void *opaque) -{ - const JSCFunctionListEntry *e = opaque; - JSValue val; - - switch(e->def_type) { - case JS_DEF_CFUNC: - val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, - e->name, e->u.func.length, e->u.func.cproto, e->magic); - break; - case JS_DEF_PROP_STRING: - val = JS_NewAtomString(ctx, e->u.str); - break; - case JS_DEF_OBJECT: - val = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); - break; - default: - abort(); - } - return val; -} - -static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, - JSAtom atom, - const JSCFunctionListEntry *e) -{ - JSValue val; - int prop_flags = e->prop_flags; - - switch(e->def_type) { - case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */ - { - JSAtom atom1 = find_atom(ctx, e->u.alias.name); - switch (e->u.alias.base) { - case -1: - val = JS_GetProperty(ctx, obj, atom1); - break; - case 0: - val = JS_GetProperty(ctx, ctx->global_obj, atom1); - break; - case 1: - val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1); - break; - default: - abort(); - } - JS_FreeAtom(ctx, atom1); - if (atom == JS_ATOM_Symbol_toPrimitive) { - /* Symbol.toPrimitive functions are not writable */ - prop_flags = JS_PROP_CONFIGURABLE; - } else if (atom == JS_ATOM_Symbol_hasInstance) { - /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ - prop_flags = 0; - } - } - break; - case JS_DEF_CFUNC: - if (atom == JS_ATOM_Symbol_toPrimitive) { - /* Symbol.toPrimitive functions are not writable */ - prop_flags = JS_PROP_CONFIGURABLE; - } else if (atom == JS_ATOM_Symbol_hasInstance) { - /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ - prop_flags = 0; - } - JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, - (void *)e, prop_flags); - return 0; - case JS_DEF_CGETSET: /* XXX: use autoinit again ? */ - case JS_DEF_CGETSET_MAGIC: - { - JSValue getter, setter; - char buf[64]; - - getter = JS_UNDEFINED; - if (e->u.getset.get.generic) { - snprintf(buf, sizeof(buf), "get %s", e->name); - getter = JS_NewCFunction2(ctx, e->u.getset.get.generic, - buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter, - e->magic); - } - setter = JS_UNDEFINED; - if (e->u.getset.set.generic) { - snprintf(buf, sizeof(buf), "set %s", e->name); - setter = JS_NewCFunction2(ctx, e->u.getset.set.generic, - buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter, - e->magic); - } - JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags); - return 0; - } - break; - case JS_DEF_PROP_INT32: - val = JS_NewInt32(ctx, e->u.i32); - break; - case JS_DEF_PROP_INT64: - val = JS_NewInt64(ctx, e->u.i64); - break; - case JS_DEF_PROP_DOUBLE: - val = __JS_NewFloat64(ctx, e->u.f64); - break; - case JS_DEF_PROP_UNDEFINED: - val = JS_UNDEFINED; - break; - case JS_DEF_PROP_STRING: - case JS_DEF_OBJECT: - JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, - (void *)e, prop_flags); - return 0; - default: - abort(); - } - JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags); - return 0; -} - -void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, - const JSCFunctionListEntry *tab, int len) -{ - int i; - - for (i = 0; i < len; i++) { - const JSCFunctionListEntry *e = &tab[i]; - JSAtom atom = find_atom(ctx, e->name); - JS_InstantiateFunctionListItem(ctx, obj, atom, e); - JS_FreeAtom(ctx, atom); - } -} - -int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len) -{ - int i; - for(i = 0; i < len; i++) { - if (JS_AddModuleExport(ctx, m, tab[i].name)) - return -1; - } - return 0; -} - -int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len) -{ - int i; - JSValue val; - - for(i = 0; i < len; i++) { - const JSCFunctionListEntry *e = &tab[i]; - switch(e->def_type) { - case JS_DEF_CFUNC: - val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, - e->name, e->u.func.length, e->u.func.cproto, e->magic); - break; - case JS_DEF_PROP_STRING: - val = JS_NewString(ctx, e->u.str); - break; - case JS_DEF_PROP_INT32: - val = JS_NewInt32(ctx, e->u.i32); - break; - case JS_DEF_PROP_INT64: - val = JS_NewInt64(ctx, e->u.i64); - break; - case JS_DEF_PROP_DOUBLE: - val = __JS_NewFloat64(ctx, e->u.f64); - break; - case JS_DEF_OBJECT: - val = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); - break; - default: - abort(); - } - if (JS_SetModuleExport(ctx, m, e->name, val)) - return -1; - } - return 0; -} - -/* Note: 'func_obj' is not necessarily a constructor */ -static void JS_SetConstructor2(JSContext *ctx, - JSValueConst func_obj, - JSValueConst proto, - int proto_flags, int ctor_flags) -{ - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, - JS_DupValue(ctx, proto), proto_flags); - JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, - JS_DupValue(ctx, func_obj), - ctor_flags); - set_cycle_flag(ctx, func_obj); - set_cycle_flag(ctx, proto); -} - -void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst proto) -{ - JS_SetConstructor2(ctx, func_obj, proto, - 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); -} - -static void JS_NewGlobalCConstructor2(JSContext *ctx, - JSValue func_obj, - const char *name, - JSValueConst proto) -{ - JS_DefinePropertyValueStr(ctx, ctx->global_obj, name, - JS_DupValue(ctx, func_obj), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - JS_SetConstructor(ctx, func_obj, proto); - JS_FreeValue(ctx, func_obj); -} - -static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name, - JSCFunction *func, int length, - JSValueConst proto) -{ - JSValue func_obj; - func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0); - JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); - return func_obj; -} - -static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name, - JSCFunction *func, int length, - JSValueConst proto) -{ - JSValue func_obj; - func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0); - JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); - return func_obj; -} - -static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1); -} - -static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double d; - - /* XXX: does this work for bigfloat? */ - if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) - return JS_EXCEPTION; - return JS_NewBool(ctx, isnan(d)); -} - -static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double d; - if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) - return JS_EXCEPTION; - return JS_NewBool(ctx, isfinite(d)); -} - -/* Object class */ - -static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) -{ - int tag = JS_VALUE_GET_NORM_TAG(val); - JSValue obj; - - switch(tag) { - default: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - return JS_ThrowTypeError(ctx, "cannot convert to object"); - case JS_TAG_OBJECT: - case JS_TAG_EXCEPTION: - return JS_DupValue(ctx, val); - 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: - /* XXX: should call the string constructor */ - { - JSString *p1 = JS_VALUE_GET_STRING(val); - obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); - } - goto set_value; - case JS_TAG_BOOL: - obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); - goto set_value; - case JS_TAG_SYMBOL: - obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); - set_value: - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val)); - return obj; - } -} - -static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val) -{ - JSValue obj = JS_ToObject(ctx, val); - JS_FreeValue(ctx, val); - return obj; -} - -static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, - JSValueConst desc) -{ - JSValue val, getter, setter; - int flags; - - if (!JS_IsObject(desc)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - flags = 0; - val = JS_UNDEFINED; - getter = JS_UNDEFINED; - setter = JS_UNDEFINED; - if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_CONFIGURABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_CONFIGURABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_WRITABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_WRITABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_ENUMERABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_ENUMERABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { - flags |= JS_PROP_HAS_VALUE; - val = JS_GetProperty(ctx, desc, JS_ATOM_value); - if (JS_IsException(val)) - goto fail; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { - flags |= JS_PROP_HAS_GET; - getter = JS_GetProperty(ctx, desc, JS_ATOM_get); - if (JS_IsException(getter) || - !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { - JS_ThrowTypeError(ctx, "invalid getter"); - goto fail; - } - } - if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { - flags |= JS_PROP_HAS_SET; - setter = JS_GetProperty(ctx, desc, JS_ATOM_set); - if (JS_IsException(setter) || - !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { - JS_ThrowTypeError(ctx, "invalid setter"); - goto fail; - } - } - if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && - (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { - JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable"); - goto fail; - } - d->flags = flags; - d->value = val; - d->getter = getter; - d->setter = setter; - return 0; - fail: - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, getter); - JS_FreeValue(ctx, setter); - return -1; -} - -static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst desc, - int flags) -{ - JSPropertyDescriptor d; - int ret; - - if (js_obj_to_desc(ctx, &d, desc) < 0) - return -1; - - ret = JS_DefineProperty(ctx, obj, prop, - d.value, d.getter, d.setter, d.flags | flags); - js_free_desc(ctx, &d); - return ret; -} - -static __exception int JS_ObjectDefineProperties(JSContext *ctx, - JSValueConst obj, - JSValueConst properties) -{ - JSValue props, desc; - JSObject *p; - JSPropertyEnum *atoms; - uint32_t len, i; - int ret = -1; - - if (!JS_IsObject(obj)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - desc = JS_UNDEFINED; - props = JS_ToObject(ctx, properties); - if (JS_IsException(props)) - return -1; - p = JS_VALUE_GET_OBJ(props); - if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) - goto exception; - for(i = 0; i < len; i++) { - JS_FreeValue(ctx, desc); - desc = JS_GetProperty(ctx, props, atoms[i].atom); - if (JS_IsException(desc)) - goto exception; - if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0) - goto exception; - } - ret = 0; - -exception: - js_free_prop_enum(ctx, atoms, len); - JS_FreeValue(ctx, props); - JS_FreeValue(ctx, desc); - return ret; -} - -static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue ret; - if (!JS_IsUndefined(new_target) && - JS_VALUE_GET_OBJ(new_target) != - JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { - ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); - } else { - int tag = JS_VALUE_GET_NORM_TAG(argv[0]); - switch(tag) { - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_NewObject(ctx); - break; - default: - ret = JS_ToObject(ctx, argv[0]); - break; - } - } - return ret; -} - -static JSValue js_object_create(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst proto, props; - JSValue obj; - - proto = argv[0]; - if (!JS_IsObject(proto) && !JS_IsNull(proto)) - return JS_ThrowTypeError(ctx, "not a prototype"); - obj = JS_NewObjectProto(ctx, proto); - if (JS_IsException(obj)) - return JS_EXCEPTION; - props = argv[1]; - if (!JS_IsUndefined(props)) { - if (JS_ObjectDefineProperties(ctx, obj, props)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; -} - -static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValueConst val; - - val = argv[0]; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { - /* ES6 feature non compatible with ES5.1: primitive types are - accepted */ - if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || - JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) - return JS_ThrowTypeErrorNotAnObject(ctx); - } - return JS_GetPrototype(ctx, val); -} - -static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj; - obj = argv[0]; - if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) - return JS_EXCEPTION; - return JS_DupValue(ctx, obj); -} - -/* magic = 1 if called as Reflect.defineProperty */ -static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValueConst obj, prop, desc; - int ret, flags; - JSAtom atom; - - obj = argv[0]; - prop = argv[1]; - desc = argv[2]; - - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - flags = 0; - if (!magic) - flags |= JS_PROP_THROW; - ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); - JS_FreeAtom(ctx, atom); - if (ret < 0) { - return JS_EXCEPTION; - } else if (magic) { - return JS_NewBool(ctx, ret); - } else { - return JS_DupValue(ctx, obj); - } -} - -static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // defineProperties(obj, properties) - JSValueConst obj = argv[0]; - - if (JS_ObjectDefineProperties(ctx, obj, argv[1])) - return JS_EXCEPTION; - else - return JS_DupValue(ctx, obj); -} - -/* magic = 1 if called as __defineSetter__ */ -static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue obj; - JSValueConst prop, value, get, set; - int ret, flags; - JSAtom atom; - - prop = argv[0]; - value = argv[1]; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return JS_EXCEPTION; - - if (check_function(ctx, value)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - flags = JS_PROP_THROW | - JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; - if (magic) { - get = JS_UNDEFINED; - set = value; - flags |= JS_PROP_HAS_SET; - } else { - get = value; - set = JS_UNDEFINED; - flags |= JS_PROP_HAS_GET; - } - ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); - JS_FreeValue(ctx, obj); - JS_FreeAtom(ctx, atom); - if (ret < 0) { - return JS_EXCEPTION; - } else { - return JS_UNDEFINED; - } -} - -static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValueConst prop; - JSAtom atom; - JSValue ret, obj; - JSPropertyDescriptor desc; - int res, flags; - - if (magic) { - /* Reflect.getOwnPropertyDescriptor case */ - if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - obj = JS_DupValue(ctx, argv[0]); - } else { - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - return obj; - } - prop = argv[1]; - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - ret = JS_UNDEFINED; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); - if (res < 0) - goto exception; - if (res) { - ret = JS_NewObject(ctx); - if (JS_IsException(ret)) - goto exception1; - flags = JS_PROP_C_W_E | JS_PROP_THROW; - if (desc.flags & JS_PROP_GETSET) { - if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0 - || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0) - goto exception1; - } else { - if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 - || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0) - goto exception1; - } - if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, - 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), flags) < 0) - goto exception1; - js_free_desc(ctx, &desc); - } - } - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - return ret; - -exception1: - js_free_desc(ctx, &desc); - JS_FreeValue(ctx, ret); -exception: - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - //getOwnPropertyDescriptors(obj) - JSValue obj, r; - JSObject *p; - JSPropertyEnum *props; - uint32_t len, i; - - r = JS_UNDEFINED; - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - return JS_EXCEPTION; - - p = JS_VALUE_GET_OBJ(obj); - if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) - goto exception; - r = JS_NewObject(ctx); - if (JS_IsException(r)) - goto exception; - for(i = 0; i < len; i++) { - JSValue atomValue, desc; - JSValueConst args[2]; - - atomValue = JS_AtomToValue(ctx, props[i].atom); - if (JS_IsException(atomValue)) - goto exception; - args[0] = obj; - args[1] = atomValue; - desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); - JS_FreeValue(ctx, atomValue); - if (JS_IsException(desc)) - goto exception; - if (!JS_IsUndefined(desc)) { - if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - } - js_free_prop_enum(ctx, props, len); - JS_FreeValue(ctx, obj); - return r; - -exception: - js_free_prop_enum(ctx, props, len); - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, r); - return JS_EXCEPTION; -} - -static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, - int flags, int kind) -{ - JSValue obj, r, val, key, value; - JSObject *p; - JSPropertyEnum *atoms; - uint32_t len, i, j; - - r = JS_UNDEFINED; - val = JS_UNDEFINED; - obj = JS_ToObject(ctx, obj1); - if (JS_IsException(obj)) - return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(obj); - if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) - goto exception; - r = JS_NewArray(ctx); - if (JS_IsException(r)) - goto exception; - for(j = i = 0; i < len; i++) { - JSAtom atom = atoms[i].atom; - if (flags & JS_GPN_ENUM_ONLY) { - JSPropertyDescriptor desc; - int res; - - /* Check if property is still enumerable */ - res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); - if (res < 0) - goto exception; - if (!res) - continue; - js_free_desc(ctx, &desc); - if (!(desc.flags & JS_PROP_ENUMERABLE)) - continue; - } - switch(kind) { - default: - case JS_ITERATOR_KIND_KEY: - val = JS_AtomToValue(ctx, atom); - if (JS_IsException(val)) - goto exception; - break; - case JS_ITERATOR_KIND_VALUE: - val = JS_GetProperty(ctx, obj, atom); - if (JS_IsException(val)) - goto exception; - break; - case JS_ITERATOR_KIND_KEY_AND_VALUE: - val = JS_NewArray(ctx); - if (JS_IsException(val)) - goto exception; - key = JS_AtomToValue(ctx, atom); - if (JS_IsException(key)) - goto exception1; - if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) - goto exception1; - value = JS_GetProperty(ctx, obj, atom); - if (JS_IsException(value)) - goto exception1; - if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) - goto exception1; - break; - } - if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) - goto exception; - } - goto done; - -exception1: - JS_FreeValue(ctx, val); -exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; -done: - js_free_prop_enum(ctx, atoms, len); - JS_FreeValue(ctx, obj); - return r; -} - -static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_GetOwnPropertyNames2(ctx, argv[0], - JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); -} - -static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_GetOwnPropertyNames2(ctx, argv[0], - JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); -} - -static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int kind) -{ - return JS_GetOwnPropertyNames2(ctx, argv[0], - JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); -} - -static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int reflect) -{ - JSValueConst obj; - int ret; - - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - if (reflect) - return JS_ThrowTypeErrorNotAnObject(ctx); - else - return JS_FALSE; - } - ret = JS_IsExtensible(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int reflect) -{ - JSValueConst obj; - int ret; - - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - if (reflect) - return JS_ThrowTypeErrorNotAnObject(ctx); - else - return JS_DupValue(ctx, obj); - } - ret = JS_PreventExtensions(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - if (reflect) { - return JS_NewBool(ctx, ret); - } else { - if (!ret) - return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); - return JS_DupValue(ctx, obj); - } -} - -static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - JSAtom atom; - JSObject *p; - BOOL ret; - - atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) { - JS_FreeAtom(ctx, atom); - return obj; - } - p = JS_VALUE_GET_OBJ(obj); - ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - JSAtom atom; - JSObject *p; - BOOL ret; - - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - return obj; - atom = JS_ValueToAtom(ctx, argv[1]); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - p = JS_VALUE_GET_OBJ(obj); - ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToObject(ctx, this_val); -} - -static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, tag; - int is_array; - JSAtom atom; - JSObject *p; - - if (JS_IsNull(this_val)) { - tag = JS_NewString(ctx, "Null"); - } else if (JS_IsUndefined(this_val)) { - tag = JS_NewString(ctx, "Undefined"); - } else { - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return obj; - is_array = JS_IsArray(ctx, obj); - if (is_array < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - if (is_array) { - atom = JS_ATOM_Array; - } else if (JS_IsFunction(ctx, obj)) { - atom = JS_ATOM_Function; - } else { - p = JS_VALUE_GET_OBJ(obj); - switch(p->class_id) { - case JS_CLASS_STRING: - case JS_CLASS_ARGUMENTS: - case JS_CLASS_MAPPED_ARGUMENTS: - case JS_CLASS_ERROR: - case JS_CLASS_BOOLEAN: - case JS_CLASS_NUMBER: - case JS_CLASS_DATE: - case JS_CLASS_REGEXP: - atom = ctx->rt->class_array[p->class_id].class_name; - break; - default: - atom = JS_ATOM_Object; - break; - } - } - tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); - JS_FreeValue(ctx, obj); - if (JS_IsException(tag)) - return JS_EXCEPTION; - if (!JS_IsString(tag)) { - JS_FreeValue(ctx, tag); - tag = JS_AtomToString(ctx, atom); - } - } - return JS_ConcatString3(ctx, "[object ", tag, "]"); -} - -static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); -} - -static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // Object.assign(obj, source1) - JSValue obj, s; - int i; - - s = JS_UNDEFINED; - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - goto exception; - for (i = 1; i < argc; i++) { - if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { - s = JS_ToObject(ctx, argv[i]); - if (JS_IsException(s)) - goto exception; - if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) - goto exception; - JS_FreeValue(ctx, s); - } - } - return obj; -exception: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, s); - return JS_EXCEPTION; -} - -static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int freeze_flag) -{ - JSValueConst obj = argv[0]; - JSObject *p; - JSPropertyEnum *props; - uint32_t len, i; - int flags, desc_flags, res; - - if (!JS_IsObject(obj)) - return JS_DupValue(ctx, obj); - - res = JS_PreventExtensions(ctx, obj); - if (res < 0) - return JS_EXCEPTION; - if (!res) { - return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); - } - - p = JS_VALUE_GET_OBJ(obj); - flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; - if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) - return JS_EXCEPTION; - - for(i = 0; i < len; i++) { - JSPropertyDescriptor desc; - JSAtom prop = props[i].atom; - - desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; - if (freeze_flag) { - res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (res < 0) - goto exception; - if (res) { - if (desc.flags & JS_PROP_WRITABLE) - desc_flags |= JS_PROP_HAS_WRITABLE; - js_free_desc(ctx, &desc); - } - } - if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, - JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) - goto exception; - } - js_free_prop_enum(ctx, props, len); - return JS_DupValue(ctx, obj); - - exception: - js_free_prop_enum(ctx, props, len); - return JS_EXCEPTION; -} - -static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_frozen) -{ - JSValueConst obj = argv[0]; - JSObject *p; - JSPropertyEnum *props; - uint32_t len, i; - int flags, res; - - if (!JS_IsObject(obj)) - return JS_TRUE; - - p = JS_VALUE_GET_OBJ(obj); - flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; - if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) - return JS_EXCEPTION; - - for(i = 0; i < len; i++) { - JSPropertyDescriptor desc; - JSAtom prop = props[i].atom; - - res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (res < 0) - goto exception; - if (res) { - js_free_desc(ctx, &desc); - if ((desc.flags & JS_PROP_CONFIGURABLE) - || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { - res = FALSE; - goto done; - } - } - } - res = JS_IsExtensible(ctx, obj); - if (res < 0) - return JS_EXCEPTION; - res ^= 1; -done: - js_free_prop_enum(ctx, props, len); - return JS_NewBool(ctx, res); - -exception: - js_free_prop_enum(ctx, props, len); - return JS_EXCEPTION; -} - -static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, iter, next_method = JS_UNDEFINED; - JSValueConst iterable; - BOOL done; - - /* RequireObjectCoercible() not necessary because it is tested in - JS_GetIterator() by JS_GetProperty() */ - iterable = argv[0]; - - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return obj; - - iter = JS_GetIterator(ctx, iterable, FALSE); - if (JS_IsException(iter)) - goto fail; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail; - - for(;;) { - JSValue key, value, item; - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail; - if (done) { - JS_FreeValue(ctx, item); - break; - } - - key = JS_UNDEFINED; - value = JS_UNDEFINED; - if (!JS_IsObject(item)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail1; - } - key = JS_GetPropertyUint32(ctx, item, 0); - if (JS_IsException(key)) - goto fail1; - value = JS_GetPropertyUint32(ctx, item, 1); - if (JS_IsException(value)) { - JS_FreeValue(ctx, key); - goto fail1; - } - if (JS_DefinePropertyValueValue(ctx, obj, key, value, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) { - fail1: - JS_FreeValue(ctx, item); - goto fail; - } - JS_FreeValue(ctx, item); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - return obj; - fail: - if (JS_IsObject(iter)) { - /* close the iterator object, preserving pending exception */ - JS_IteratorClose(ctx, iter, TRUE); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -#if 0 -/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */ -static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]), - JS_DupValue(ctx, argv[2]), - JS_PROP_C_W_E | JS_PROP_THROW); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToObject(ctx, argv[0]); -} - -static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int hint = HINT_NONE; - - if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT) - hint = JS_VALUE_GET_INT(argv[1]); - - return JS_ToPrimitive(ctx, argv[0], hint); -} -#endif - -/* return an empty string if not an object */ -static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSAtom atom; - JSObject *p; - uint32_t tag; - int class_id; - - tag = JS_VALUE_GET_NORM_TAG(argv[0]); - if (tag == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(argv[0]); - class_id = p->class_id; - if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0])) - class_id = JS_CLASS_BYTECODE_FUNCTION; - atom = ctx->rt->class_array[class_id].class_name; - } else { - atom = JS_ATOM_empty_string; - } - return JS_AtomToString(ctx, atom); -} - -static JSValue js_object_is(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1])); -} - -#if 0 -static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_GetObjectData(ctx, argv[0]); -} - -static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1]))) - return JS_EXCEPTION; - return JS_DupValue(ctx, argv[1]); -} - -static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToPropertyKey(ctx, argv[0]); -} - -static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, JS_IsObject(argv[0])); -} - -static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1])); -} - -static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0])); -} -#endif - -static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj, - JSValueConst defaultConstructor) -{ - JSValue ctor, species; - - if (!JS_IsObject(obj)) - return JS_ThrowTypeErrorNotAnObject(ctx); - ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - if (JS_IsUndefined(ctor)) - return JS_DupValue(ctx, defaultConstructor); - if (!JS_IsObject(ctor)) { - JS_FreeValue(ctx, ctor); - return JS_ThrowTypeErrorNotAnObject(ctx); - } - species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); - JS_FreeValue(ctx, ctor); - if (JS_IsException(species)) - return species; - if (JS_IsUndefined(species) || JS_IsNull(species)) - return JS_DupValue(ctx, defaultConstructor); - if (!JS_IsConstructor(ctx, species)) { - JS_FreeValue(ctx, species); - return JS_ThrowTypeError(ctx, "not a constructor"); - } - return species; -} - -#if 0 -static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_SpeciesConstructor(ctx, argv[0], argv[1]); -} -#endif - -static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) -{ - JSValue val, ret; - - val = JS_ToObject(ctx, this_val); - if (JS_IsException(val)) - return val; - ret = JS_GetPrototype(ctx, val); - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, - JSValueConst proto) -{ - if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (!JS_IsObject(proto) && !JS_IsNull(proto)) - return JS_UNDEFINED; - if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) - return JS_EXCEPTION; - else - return JS_UNDEFINED; -} - -static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, v1; - JSValueConst v; - int res; - - v = argv[0]; - if (!JS_IsObject(v)) - return JS_FALSE; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return JS_EXCEPTION; - v1 = JS_DupValue(ctx, v); - for(;;) { - v1 = JS_GetPrototypeFree(ctx, v1); - if (JS_IsException(v1)) - goto exception; - if (JS_IsNull(v1)) { - res = FALSE; - break; - } - if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { - res = TRUE; - break; - } - /* avoid infinite loop (possible with proxies) */ - if (js_poll_interrupts(ctx)) - goto exception; - } - JS_FreeValue(ctx, v1); - JS_FreeValue(ctx, obj); - return JS_NewBool(ctx, res); - -exception: - JS_FreeValue(ctx, v1); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, res = JS_EXCEPTION; - JSAtom prop = JS_ATOM_NULL; - JSPropertyDescriptor desc; - int has_prop; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; - prop = JS_ValueToAtom(ctx, argv[0]); - if (unlikely(prop == JS_ATOM_NULL)) - goto exception; - - has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); - if (has_prop < 0) - goto exception; - if (has_prop) { - res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE); - js_free_desc(ctx, &desc); - } else { - res = JS_FALSE; - } - -exception: - JS_FreeAtom(ctx, prop); - JS_FreeValue(ctx, obj); - return res; -} - -static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int setter) -{ - JSValue obj, res = JS_EXCEPTION; - JSAtom prop = JS_ATOM_NULL; - JSPropertyDescriptor desc; - int has_prop; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; - prop = JS_ValueToAtom(ctx, argv[0]); - if (unlikely(prop == JS_ATOM_NULL)) - goto exception; - - for (;;) { - has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); - if (has_prop < 0) - goto exception; - if (has_prop) { - if (desc.flags & JS_PROP_GETSET) - res = JS_DupValue(ctx, setter ? desc.setter : desc.getter); - else - res = JS_UNDEFINED; - js_free_desc(ctx, &desc); - break; - } - obj = JS_GetPrototypeFree(ctx, obj); - if (JS_IsException(obj)) - goto exception; - if (JS_IsNull(obj)) { - res = JS_UNDEFINED; - break; - } - /* avoid infinite loop (possible with proxies) */ - if (js_poll_interrupts(ctx)) - goto exception; - } - -exception: - JS_FreeAtom(ctx, prop); - JS_FreeValue(ctx, obj); - return res; -} - -static const JSCFunctionListEntry js_object_funcs[] = { - JS_CFUNC_DEF("create", 2, js_object_create ), - JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), - JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ), - JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ), - JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), - JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), - JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), - JS_CFUNC_MAGIC_DEF("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 ), - JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ), - JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ), - JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ), - JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ), - JS_CFUNC_DEF("is", 2, js_object_is ), - JS_CFUNC_DEF("assign", 2, js_object_assign ), - JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ), - JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ), - JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ), - JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ), - JS_CFUNC_DEF("__getClass", 1, js_object___getClass ), - //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ), - //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ), - //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ), - //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ), - //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ), - //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ), - //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ), - //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ), - //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ), - //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ), - JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ), - JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ), -}; - -static const JSCFunctionListEntry js_object_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_object_toString ), - JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ), - JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ), - JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ), - JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ), - JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ), - JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ), - JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ), - JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ), - JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ), - JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ), -}; - -/* Function class */ - -static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_UNDEFINED; -} - -/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ -static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv, int magic) -{ - JSFunctionKindEnum func_kind = magic; - int i, n, ret; - JSValue s, proto, obj = JS_UNDEFINED; - StringBuffer b_s, *b = &b_s; - - string_buffer_init(ctx, b, 0); - string_buffer_putc8(b, '('); - - if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { - string_buffer_puts8(b, "async "); - } - string_buffer_puts8(b, "function"); - - if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { - string_buffer_putc8(b, '*'); - } - string_buffer_puts8(b, " anonymous("); - - n = argc - 1; - for(i = 0; i < n; i++) { - if (i != 0) { - string_buffer_putc8(b, ','); - } - if (string_buffer_concat_value(b, argv[i])) - goto fail; - } - string_buffer_puts8(b, "\n) {\n"); - if (n >= 0) { - if (string_buffer_concat_value(b, argv[n])) - goto fail; - } - string_buffer_puts8(b, "\n})"); - s = string_buffer_end(b); - if (JS_IsException(s)) - goto fail1; - - obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); - JS_FreeValue(ctx, s); - if (JS_IsException(obj)) - goto fail1; - if (!JS_IsUndefined(new_target)) { - /* set the prototype */ - proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); - if (JS_IsException(proto)) - goto fail1; - if (!JS_IsObject(proto)) { - JSContext *realm; - JS_FreeValue(ctx, proto); - realm = JS_GetFunctionRealm(ctx, new_target); - if (!realm) - goto fail1; - proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); - } - ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); - JS_FreeValue(ctx, proto); - if (ret < 0) - goto fail1; - } - return obj; - - fail: - string_buffer_free(b); - fail1: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValueConst obj) -{ - JSValue len_val; - len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); - if (JS_IsException(len_val)) { - *pres = 0; - return -1; - } - return JS_ToUint32Free(ctx, pres, len_val); -} - -static __exception int js_get_length64(JSContext *ctx, int64_t *pres, - JSValueConst obj) -{ - JSValue len_val; - len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); - if (JS_IsException(len_val)) { - *pres = 0; - return -1; - } - return JS_ToLengthFree(ctx, pres, len_val); -} - -static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len) -{ - uint32_t i; - for(i = 0; i < len; i++) { - JS_FreeValue(ctx, tab[i]); - } - js_free(ctx, tab); -} - -/* XXX: should use ValueArray */ -static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, - JSValueConst array_arg) -{ - uint32_t len, i; - JSValue *tab, ret; - JSObject *p; - - if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { - JS_ThrowTypeError(ctx, "not a object"); - return NULL; - } - if (js_get_length32(ctx, &len, array_arg)) - return NULL; - if (len > 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; - } - /* avoid allocating 0 bytes */ - tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); - if (!tab) - return NULL; - p = JS_VALUE_GET_OBJ(array_arg); - if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) && - p->fast_array && - len == p->u.array.count) { - for(i = 0; i < len; i++) { - tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); - } - } else { - for(i = 0; i < len; i++) { - ret = JS_GetPropertyUint32(ctx, array_arg, i); - if (JS_IsException(ret)) { - free_arg_list(ctx, tab, i); - return NULL; - } - tab[i] = ret; - } - } - *plen = len; - return tab; -} - -/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = - Reflect.apply */ -static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValueConst this_arg, array_arg; - uint32_t len; - JSValue *tab, ret; - - if (check_function(ctx, this_val)) - return JS_EXCEPTION; - this_arg = argv[0]; - array_arg = argv[1]; - if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || - JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { - return JS_Call(ctx, this_val, this_arg, 0, NULL); - } - tab = build_arg_list(ctx, &len, array_arg); - if (!tab) - return JS_EXCEPTION; - if (magic & 1) { - ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); - } else { - ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); - } - free_arg_list(ctx, tab, len); - return ret; -} - -static JSValue js_function_call(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (argc <= 0) { - return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); - } else { - return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); - } -} - -static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSBoundFunction *bf; - JSValue func_obj, name1, len_val; - JSObject *p; - int arg_count, i, ret; - - if (check_function(ctx, this_val)) - return JS_EXCEPTION; - - func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_BOUND_FUNCTION); - if (JS_IsException(func_obj)) - return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(func_obj); - p->is_constructor = JS_IsConstructor(ctx, this_val); - arg_count = max_int(0, argc - 1); - bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); - if (!bf) - goto exception; - bf->func_obj = JS_DupValue(ctx, this_val); - bf->this_val = JS_DupValue(ctx, argv[0]); - bf->argc = arg_count; - for(i = 0; i < arg_count; i++) { - bf->argv[i] = JS_DupValue(ctx, argv[i + 1]); - } - p->u.bound_function = bf; - - /* XXX: the spec could be simpler by only using GetOwnProperty */ - ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); - if (ret < 0) - goto exception; - if (!ret) { - len_val = JS_NewInt32(ctx, 0); - } else { - len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); - if (JS_IsException(len_val)) - goto exception; - if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { - /* most common case */ - int len1 = JS_VALUE_GET_INT(len_val); - if (len1 <= arg_count) - len1 = 0; - else - len1 -= arg_count; - len_val = JS_NewInt32(ctx, len1); - } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { - double d = JS_VALUE_GET_FLOAT64(len_val); - if (isnan(d)) { - d = 0.0; - } else { - d = trunc(d); - if (d <= (double)arg_count) - d = 0.0; - else - d -= (double)arg_count; /* also converts -0 to +0 */ - } - len_val = JS_NewFloat64(ctx, d); - } else { - JS_FreeValue(ctx, len_val); - len_val = JS_NewInt32(ctx, 0); - } - } - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, - len_val, JS_PROP_CONFIGURABLE); - - name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); - if (JS_IsException(name1)) - goto exception; - if (!JS_IsString(name1)) { - JS_FreeValue(ctx, name1); - name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); - } - name1 = JS_ConcatString3(ctx, "bound ", name1, ""); - if (JS_IsException(name1)) - goto exception; - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, - JS_PROP_CONFIGURABLE); - return func_obj; - exception: - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; - - if (check_function(ctx, this_val)) - return JS_EXCEPTION; - - p = JS_VALUE_GET_OBJ(this_val); - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b = p->u.func.function_bytecode; - if (b->has_debug && b->debug.source) { - return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); - } - func_kind = b->func_kind; - } - { - JSValue name; - const char *pref, *suff; - - switch(func_kind) { - default: - case JS_FUNC_NORMAL: - pref = "function "; - break; - case JS_FUNC_GENERATOR: - pref = "function *"; - break; - case JS_FUNC_ASYNC: - pref = "async function "; - break; - case JS_FUNC_ASYNC_GENERATOR: - pref = "async function *"; - break; - } - suff = "() {\n [native code]\n}"; - name = JS_GetProperty(ctx, this_val, JS_ATOM_name); - if (JS_IsUndefined(name)) - name = JS_AtomToString(ctx, JS_ATOM_empty_string); - return JS_ConcatString3(ctx, pref, name, suff); - } -} - -static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static const JSCFunctionListEntry js_function_proto_funcs[] = { - JS_CFUNC_DEF("call", 1, js_function_call ), - JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ), - JS_CFUNC_DEF("bind", 1, js_function_bind ), - JS_CFUNC_DEF("toString", 0, js_function_toString ), - JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ), - JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ), - JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ), -}; - -/* Error class */ - -static JSValue iterator_to_array(JSContext *ctx, JSValueConst items) -{ - JSValue iter, next_method = JS_UNDEFINED; - JSValue v, r = JS_UNDEFINED; - int64_t k; - BOOL done; - - iter = JS_GetIterator(ctx, items, FALSE); - if (JS_IsException(iter)) - goto exception; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto exception; - r = JS_NewArray(ctx); - if (JS_IsException(r)) - goto exception; - for (k = 0;; k++) { - v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(v)) - goto exception_close; - if (done) - break; - if (JS_DefinePropertyValueInt64(ctx, r, k, v, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception_close; - } - done: - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - return r; - exception_close: - JS_IteratorClose(ctx, iter, TRUE); - exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; - goto done; -} - -static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv, int magic) -{ - JSValue obj, msg, proto; - JSValueConst message, options; - int arg_index; - - if (JS_IsUndefined(new_target)) - new_target = JS_GetActiveFunction(ctx); - proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); - if (JS_IsException(proto)) - return proto; - if (!JS_IsObject(proto)) { - JSContext *realm; - JSValueConst proto1; - - JS_FreeValue(ctx, proto); - realm = JS_GetFunctionRealm(ctx, new_target); - if (!realm) - return JS_EXCEPTION; - if (magic < 0) { - proto1 = realm->class_proto[JS_CLASS_ERROR]; - } else { - proto1 = realm->native_error_proto[magic]; - } - proto = JS_DupValue(ctx, proto1); - } - obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR); - JS_FreeValue(ctx, proto); - if (JS_IsException(obj)) - return obj; - arg_index = (magic == JS_AGGREGATE_ERROR); - - message = argv[arg_index++]; - if (!JS_IsUndefined(message)) { - msg = JS_ToString(ctx, message); - if (unlikely(JS_IsException(msg))) - goto exception; - JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - } - - if (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)) - goto exception; - JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - } - - /* skip the Error() function in the backtrace */ - build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); - return obj; - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue name, msg; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - name = JS_GetProperty(ctx, this_val, JS_ATOM_name); - if (JS_IsUndefined(name)) - name = JS_AtomToString(ctx, JS_ATOM_Error); - else - name = JS_ToStringFree(ctx, name); - if (JS_IsException(name)) - return JS_EXCEPTION; - - msg = JS_GetProperty(ctx, this_val, JS_ATOM_message); - if (JS_IsUndefined(msg)) - msg = JS_AtomToString(ctx, JS_ATOM_empty_string); - else - msg = JS_ToStringFree(ctx, msg); - if (JS_IsException(msg)) { - JS_FreeValue(ctx, name); - return JS_EXCEPTION; - } - if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg)) - name = JS_ConcatString3(ctx, "", name, ": "); - return JS_ConcatString(ctx, name, msg); -} - -static const JSCFunctionListEntry js_error_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_error_toString ), - JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), - JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -/* AggregateError */ - -/* used by C code. */ -static JSValue js_aggregate_error_constructor(JSContext *ctx, - JSValueConst errors) -{ - JSValue obj; - - obj = JS_NewObjectProtoClass(ctx, - ctx->native_error_proto[JS_AGGREGATE_ERROR], - JS_CLASS_ERROR); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - return obj; -} - -/* Array */ - -static int JS_CopySubArray(JSContext *ctx, - JSValueConst obj, int64_t to_pos, - int64_t from_pos, int64_t count, int dir) -{ - JSObject *p; - int64_t i, from, to, len; - JSValue val; - int fromPresent; - - p = NULL; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) { - p = NULL; - } - } - - for (i = 0; i < count; ) { - if (dir < 0) { - from = from_pos + count - i - 1; - to = to_pos + count - i - 1; - } else { - from = from_pos + i; - to = to_pos + i; - } - if (p && p->fast_array && - from >= 0 && from < (len = p->u.array.count) && - to >= 0 && to < len) { - int64_t l, j; - /* Fast path for fast arrays. Since we don't look at the - prototype chain, we can optimize only the cases where - all the elements are present in the array. */ - l = count - i; - if (dir < 0) { - l = min_int64(l, from + 1); - l = min_int64(l, to + 1); - for(j = 0; j < l; j++) { - set_value(ctx, &p->u.array.u.values[to - j], - JS_DupValue(ctx, p->u.array.u.values[from - j])); - } - } else { - l = min_int64(l, len - from); - l = min_int64(l, len - to); - for(j = 0; j < l; j++) { - set_value(ctx, &p->u.array.u.values[to + j], - JS_DupValue(ctx, p->u.array.u.values[from + j])); - } - } - i += l; - } else { - fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); - if (fromPresent < 0) - goto exception; - - if (fromPresent) { - if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) - goto exception; - } else { - if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) - goto exception; - } - i++; - } - } - return 0; - - exception: - return -1; -} - -static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue obj; - int i; - - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); - if (JS_IsException(obj)) - return obj; - if (argc == 1 && JS_IsNumber(argv[0])) { - uint32_t len; - if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) - goto fail; - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) - goto fail; - } else { - for(i = 0; i < argc; i++) { - if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) - goto fail; - } - } - return obj; -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // from(items, mapfn = void 0, this_arg = void 0) - JSValueConst items = argv[0], mapfn, this_arg; - JSValueConst args[2]; - JSValue stack[2]; - JSValue iter, r, v, v2, arrayLike; - int64_t k, len; - int done, mapping; - - mapping = FALSE; - mapfn = JS_UNDEFINED; - this_arg = JS_UNDEFINED; - r = JS_UNDEFINED; - arrayLike = JS_UNDEFINED; - stack[0] = JS_UNDEFINED; - stack[1] = JS_UNDEFINED; - - if (argc > 1) { - mapfn = argv[1]; - if (!JS_IsUndefined(mapfn)) { - if (check_function(ctx, mapfn)) - goto exception; - mapping = 1; - if (argc > 2) - this_arg = argv[2]; - } - } - iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) - goto exception; - if (!JS_IsUndefined(iter)) { - JS_FreeValue(ctx, iter); - if (JS_IsConstructor(ctx, this_val)) - r = JS_CallConstructor(ctx, this_val, 0, NULL); - else - r = JS_NewArray(ctx); - if (JS_IsException(r)) - goto exception; - stack[0] = JS_DupValue(ctx, items); - if (js_for_of_start(ctx, &stack[1], FALSE)) - goto exception; - for (k = 0;; k++) { - v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); - if (JS_IsException(v)) - goto exception_close; - if (done) - break; - if (mapping) { - args[0] = v; - args[1] = JS_NewInt32(ctx, k); - v2 = JS_Call(ctx, mapfn, this_arg, 2, args); - JS_FreeValue(ctx, v); - v = v2; - if (JS_IsException(v)) - goto exception_close; - } - if (JS_DefinePropertyValueInt64(ctx, r, k, v, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception_close; - } - } else { - arrayLike = JS_ToObject(ctx, items); - if (JS_IsException(arrayLike)) - goto exception; - if (js_get_length64(ctx, &len, arrayLike) < 0) - goto exception; - v = JS_NewInt64(ctx, len); - args[0] = v; - if (JS_IsConstructor(ctx, this_val)) { - r = JS_CallConstructor(ctx, this_val, 1, args); - } else { - r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); - } - JS_FreeValue(ctx, v); - if (JS_IsException(r)) - goto exception; - for(k = 0; k < len; k++) { - v = JS_GetPropertyInt64(ctx, arrayLike, k); - if (JS_IsException(v)) - goto exception; - if (mapping) { - args[0] = v; - args[1] = JS_NewInt32(ctx, k); - v2 = JS_Call(ctx, mapfn, this_arg, 2, args); - JS_FreeValue(ctx, v); - v = v2; - if (JS_IsException(v)) - goto exception; - } - if (JS_DefinePropertyValueInt64(ctx, r, k, v, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) - goto exception; - goto done; - - exception_close: - if (!JS_IsUndefined(stack[0])) - JS_IteratorClose(ctx, stack[0], TRUE); - exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; - done: - JS_FreeValue(ctx, arrayLike); - JS_FreeValue(ctx, stack[0]); - JS_FreeValue(ctx, stack[1]); - return r; -} - -static JSValue js_array_of(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, args[1]; - int i; - - if (JS_IsConstructor(ctx, this_val)) { - args[0] = JS_NewInt32(ctx, argc); - obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args); - } else { - obj = JS_NewArray(ctx); - } - if (JS_IsException(obj)) - return JS_EXCEPTION; - for(i = 0; i < argc; i++) { - if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), - JS_PROP_THROW) < 0) { - goto fail; - } - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_IsArray(ctx, argv[0]); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_get_this(JSContext *ctx, - JSValueConst this_val) -{ - return JS_DupValue(ctx, this_val); -} - -static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, - JSValueConst len_val) -{ - JSValue ctor, ret, species; - int res; - JSContext *realm; - - res = JS_IsArray(ctx, obj); - if (res < 0) - return JS_EXCEPTION; - if (!res) - return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); - ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - if (JS_IsConstructor(ctx, ctor)) { - /* legacy web compatibility */ - realm = JS_GetFunctionRealm(ctx, ctor); - if (!realm) { - JS_FreeValue(ctx, ctor); - return JS_EXCEPTION; - } - if (realm != ctx && - js_same_value(ctx, ctor, realm->array_ctor)) { - JS_FreeValue(ctx, ctor); - ctor = JS_UNDEFINED; - } - } - if (JS_IsObject(ctor)) { - species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); - JS_FreeValue(ctx, ctor); - if (JS_IsException(species)) - return species; - ctor = species; - if (JS_IsNull(ctor)) - ctor = JS_UNDEFINED; - } - if (JS_IsUndefined(ctor)) { - return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); - } else { - ret = JS_CallConstructor(ctx, ctor, 1, &len_val); - JS_FreeValue(ctx, ctor); - return ret; - } -} - -static const JSCFunctionListEntry js_array_funcs[] = { - JS_CFUNC_DEF("isArray", 1, js_array_isArray ), - JS_CFUNC_DEF("from", 1, js_array_from ), - JS_CFUNC_DEF("of", 0, js_array_of ), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), -}; - -static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) -{ - JSValue val; - - if (!JS_IsObject(obj)) - return FALSE; - val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); - if (JS_IsException(val)) - return -1; - if (!JS_IsUndefined(val)) - return JS_ToBoolFree(ctx, val); - return JS_IsArray(ctx, obj); -} - -static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, ret; - int64_t len, idx; - JSValue *arrp; - uint32_t count; - - 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) { - ret = JS_UNDEFINED; - } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) { - ret = JS_DupValue(ctx, arrp[idx]); - } else { - int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret); - if (present < 0) - goto exception; - if (!present) - ret = JS_UNDEFINED; - } - JS_FreeValue(ctx, obj); - return ret; - exception: - JS_FreeValue(ctx, obj); - 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) -{ - JSValue obj, arr, val; - JSValueConst e; - int64_t len, k, n; - int i, res; - - arr = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; - - arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); - if (JS_IsException(arr)) - goto exception; - n = 0; - for (i = -1; i < argc; i++) { - if (i < 0) - e = obj; - else - e = argv[i]; - - res = JS_isConcatSpreadable(ctx, e); - if (res < 0) - goto exception; - if (res) { - if (js_get_length64(ctx, &len, e)) - goto exception; - if (n + len > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - for (k = 0; k < len; k++, n++) { - res = JS_TryGetPropertyInt64(ctx, e, k, &val); - if (res < 0) - goto exception; - if (res) { - if (JS_DefinePropertyValueInt64(ctx, arr, n, val, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - } - } else { - if (n >= MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - n++; - } - } - if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return arr; - -exception: - JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -#define special_every 0 -#define special_some 1 -#define special_forEach 2 -#define special_map 3 -#define special_filter 4 -#define special_TA 8 - -static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj); - -static JSValue js_typed_array___speciesCreate(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv); - -static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int special) -{ - JSValue obj, val, index_val, res, ret; - JSValueConst args[3]; - JSValueConst func, this_arg; - int64_t len, k, n; - int present; - - ret = JS_UNDEFINED; - val = JS_UNDEFINED; - if (special & special_TA) { - obj = JS_DupValue(ctx, this_val); - len = js_typed_array_get_length_internal(ctx, obj); - if (len < 0) - goto exception; - } else { - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - } - func = argv[0]; - this_arg = JS_UNDEFINED; - if (argc > 1) - this_arg = argv[1]; - - if (check_function(ctx, func)) - goto exception; - - switch (special) { - case special_every: - case special_every | special_TA: - ret = JS_TRUE; - break; - case special_some: - case special_some | special_TA: - ret = JS_FALSE; - break; - case special_map: - /* XXX: JS_ArraySpeciesCreate should take int64_t */ - ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); - if (JS_IsException(ret)) - goto exception; - break; - case special_filter: - ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); - if (JS_IsException(ret)) - goto exception; - break; - case special_map | special_TA: - args[0] = obj; - args[1] = JS_NewInt32(ctx, len); - ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); - if (JS_IsException(ret)) - goto exception; - break; - case special_filter | special_TA: - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto exception; - break; - } - n = 0; - - for(k = 0; k < len; k++) { - if (special & special_TA) { - val = JS_GetPropertyInt64(ctx, obj, k); - if (JS_IsException(val)) - goto exception; - present = TRUE; - } else { - present = JS_TryGetPropertyInt64(ctx, obj, k, &val); - if (present < 0) - goto exception; - } - if (present) { - index_val = JS_NewInt64(ctx, k); - if (JS_IsException(index_val)) - goto exception; - args[0] = val; - args[1] = index_val; - args[2] = obj; - res = JS_Call(ctx, func, this_arg, 3, args); - JS_FreeValue(ctx, index_val); - if (JS_IsException(res)) - goto exception; - switch (special) { - case special_every: - case special_every | special_TA: - if (!JS_ToBoolFree(ctx, res)) { - ret = JS_FALSE; - goto done; - } - break; - case special_some: - case special_some | special_TA: - if (JS_ToBoolFree(ctx, res)) { - ret = JS_TRUE; - goto done; - } - break; - case special_map: - if (JS_DefinePropertyValueInt64(ctx, ret, k, res, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - break; - case special_map | special_TA: - if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) - goto exception; - break; - case special_filter: - case special_filter | special_TA: - if (JS_ToBoolFree(ctx, res)) { - if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - break; - default: - JS_FreeValue(ctx, res); - break; - } - JS_FreeValue(ctx, val); - val = JS_UNDEFINED; - } - } -done: - if (special == (special_filter | special_TA)) { - JSValue arr; - args[0] = obj; - args[1] = JS_NewInt32(ctx, n); - arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); - if (JS_IsException(arr)) - goto exception; - args[0] = ret; - res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); - if (check_exception_free(ctx, res)) - goto exception; - JS_FreeValue(ctx, ret); - ret = arr; - } - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return ret; - -exception: - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -#define special_reduce 0 -#define special_reduceRight 1 - -static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int special) -{ - JSValue obj, val, index_val, acc, acc1; - JSValueConst args[4]; - JSValueConst func; - int64_t len, k, k1; - int present; - - acc = JS_UNDEFINED; - val = JS_UNDEFINED; - if (special & special_TA) { - obj = JS_DupValue(ctx, this_val); - len = js_typed_array_get_length_internal(ctx, obj); - if (len < 0) - goto exception; - } else { - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - } - func = argv[0]; - - if (check_function(ctx, func)) - goto exception; - - k = 0; - if (argc > 1) { - acc = JS_DupValue(ctx, argv[1]); - } else { - for(;;) { - if (k >= len) { - JS_ThrowTypeError(ctx, "empty array"); - goto exception; - } - k1 = (special & special_reduceRight) ? len - k - 1 : k; - k++; - if (special & special_TA) { - acc = JS_GetPropertyInt64(ctx, obj, k1); - if (JS_IsException(acc)) - goto exception; - break; - } else { - present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); - if (present < 0) - goto exception; - if (present) - break; - } - } - } - for (; k < len; k++) { - k1 = (special & special_reduceRight) ? len - k - 1 : k; - if (special & special_TA) { - val = JS_GetPropertyInt64(ctx, obj, k1); - if (JS_IsException(val)) - goto exception; - present = TRUE; - } else { - present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); - if (present < 0) - goto exception; - } - if (present) { - index_val = JS_NewInt64(ctx, k1); - if (JS_IsException(index_val)) - goto exception; - args[0] = acc; - args[1] = val; - args[2] = index_val; - args[3] = obj; - acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); - JS_FreeValue(ctx, index_val); - JS_FreeValue(ctx, val); - val = JS_UNDEFINED; - if (JS_IsException(acc1)) - goto exception; - JS_FreeValue(ctx, acc); - acc = acc1; - } - } - JS_FreeValue(ctx, obj); - return acc; - -exception: - JS_FreeValue(ctx, acc); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - int64_t len, start, end; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - start = 0; - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) - goto exception; - } - - end = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) - goto exception; - } - - /* XXX: should special case fast arrays */ - while (start < end) { - if (JS_SetPropertyInt64(ctx, obj, start, - JS_DupValue(ctx, argv[0])) < 0) - goto exception; - start++; - } - return obj; - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, val; - int64_t len, n; - JSValue *arrp; - uint32_t count; - int res; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = FALSE; - if (len > 0) { - n = 0; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) - goto exception; - } - if (js_get_fast_array(ctx, obj, &arrp, &count)) { - for (; n < count; n++) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), - JS_DupValue(ctx, arrp[n]), - JS_EQ_SAME_VALUE_ZERO)) { - res = TRUE; - goto done; - } - } - } - for (; n < len; n++) { - val = JS_GetPropertyInt64(ctx, obj, n); - if (JS_IsException(val)) - goto exception; - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, - JS_EQ_SAME_VALUE_ZERO)) { - res = TRUE; - break; - } - } - } - done: - JS_FreeValue(ctx, obj); - return JS_NewBool(ctx, res); - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, val; - int64_t len, n, res; - JSValue *arrp; - uint32_t count; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = -1; - if (len > 0) { - n = 0; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) - goto exception; - } - if (js_get_fast_array(ctx, obj, &arrp, &count)) { - for (; n < count; n++) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), - JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { - res = n; - goto done; - } - } - } - for (; n < len; n++) { - int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); - if (present < 0) - goto exception; - if (present) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { - res = n; - break; - } - } - } - } - done: - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, res); - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, val; - int64_t len, n, res; - int present; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = -1; - if (len > 0) { - n = len - 1; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) - goto exception; - } - /* XXX: should special case fast arrays */ - for (; n >= 0; n--) { - present = JS_TryGetPropertyInt64(ctx, obj, n, &val); - if (present < 0) - goto exception; - if (present) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { - res = n; - break; - } - } - } - } - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, res); - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -enum { - ArrayFind, - ArrayFindIndex, - ArrayFindLast, - ArrayFindLastIndex, -}; - -static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int mode) -{ - JSValueConst func, this_arg; - JSValueConst args[3]; - JSValue obj, val, index_val, res; - int64_t len, k, end; - int dir; - - index_val = JS_UNDEFINED; - val = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - func = argv[0]; - if (check_function(ctx, func)) - goto exception; - - this_arg = JS_UNDEFINED; - if (argc > 1) - this_arg = argv[1]; - - k = 0; - dir = 1; - end = len; - if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { - k = len - 1; - dir = -1; - end = -1; - } - - // TODO(bnoordhuis) add fast path for fast arrays - for(; k != end; k += dir) { - index_val = JS_NewInt64(ctx, k); - if (JS_IsException(index_val)) - goto exception; - val = JS_GetPropertyValue(ctx, obj, index_val); - if (JS_IsException(val)) - goto exception; - args[0] = val; - args[1] = index_val; - args[2] = this_val; - res = JS_Call(ctx, func, this_arg, 3, args); - if (JS_IsException(res)) - goto exception; - if (JS_ToBoolFree(ctx, res)) { - if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return index_val; - } else { - JS_FreeValue(ctx, index_val); - JS_FreeValue(ctx, obj); - return val; - } - } - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, index_val); - } - JS_FreeValue(ctx, obj); - if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) - return JS_NewInt32(ctx, -1); - else - return JS_UNDEFINED; - -exception: - JS_FreeValue(ctx, index_val); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, method, ret; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return JS_EXCEPTION; - method = JS_GetProperty(ctx, obj, JS_ATOM_join); - if (JS_IsException(method)) { - ret = JS_EXCEPTION; - } else - if (!JS_IsFunction(ctx, method)) { - /* Use intrinsic Object.prototype.toString */ - JS_FreeValue(ctx, method); - ret = js_object_toString(ctx, obj, 0, NULL); - } else { - ret = JS_CallFree(ctx, method, obj, 0, NULL); - } - JS_FreeValue(ctx, obj); - return ret; -} - -static JSValue js_array_join(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int toLocaleString) -{ - JSValue obj, sep = JS_UNDEFINED, el; - StringBuffer b_s, *b = &b_s; - JSString *p = NULL; - int64_t i, n; - int c; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &n, obj)) - goto exception; - - c = ','; /* default separator */ - if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { - sep = JS_ToString(ctx, argv[0]); - if (JS_IsException(sep)) - goto exception; - p = JS_VALUE_GET_STRING(sep); - if (p->len == 1 && !p->is_wide_char) - c = p->u.str8[0]; - else - c = -1; - } - string_buffer_init(ctx, b, 0); - - for(i = 0; i < n; i++) { - if (i > 0) { - if (c >= 0) { - string_buffer_putc8(b, c); - } else { - string_buffer_concat(b, p, 0, p->len); - } - } - el = JS_GetPropertyUint32(ctx, obj, i); - if (JS_IsException(el)) - goto fail; - if (!JS_IsNull(el) && !JS_IsUndefined(el)) { - if (toLocaleString) { - el = JS_ToLocaleStringFree(ctx, el); - } - if (string_buffer_concat_value_free(b, el)) - goto fail; - } - } - JS_FreeValue(ctx, sep); - JS_FreeValue(ctx, obj); - return string_buffer_end(b); - -fail: - string_buffer_free(b); - JS_FreeValue(ctx, sep); -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int shift) -{ - JSValue obj, res = JS_UNDEFINED; - int64_t len, newLen; - JSValue *arrp; - uint32_t count32; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = 0; - if (len > 0) { - newLen = len - 1; - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (shift) { - res = arrp[0]; - memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); - p->u.array.count--; - } else { - res = arrp[count32 - 1]; - p->u.array.count--; - } - } else { - if (shift) { - res = JS_GetPropertyInt64(ctx, obj, 0); - if (JS_IsException(res)) - goto exception; - if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) - goto exception; - } else { - res = JS_GetPropertyInt64(ctx, obj, newLen); - if (JS_IsException(res)) - goto exception; - } - if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return res; - - exception: - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int unshift) -{ - JSValue obj; - int i; - int64_t len, from, newLen; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = len + argc; - if (newLen > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - from = len; - if (unshift && argc > 0) { - if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) - goto exception; - from = 0; - } - for(i = 0; i < argc; i++) { - if (JS_SetPropertyInt64(ctx, obj, from + i, - JS_DupValue(ctx, argv[i])) < 0) - goto exception; - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, newLen); - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, lval, hval; - JSValue *arrp; - int64_t len, l, h; - int l_present, h_present; - uint32_t count32; - - lval = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { - uint32_t ll, hh; - - if (count32 > 1) { - for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { - lval = arrp[ll]; - arrp[ll] = arrp[hh]; - arrp[hh] = lval; - } - } - return obj; - } - - for (l = 0, h = len - 1; l < h; l++, h--) { - l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); - if (l_present < 0) - goto exception; - h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); - if (h_present < 0) - goto exception; - if (h_present) { - if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) - goto exception; - - if (l_present) { - if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { - lval = JS_UNDEFINED; - goto exception; - } - lval = JS_UNDEFINED; - } else { - if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) - goto exception; - } - } else { - if (l_present) { - if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) - goto exception; - if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { - lval = JS_UNDEFINED; - goto exception; - } - lval = JS_UNDEFINED; - } - } - } - return obj; - - exception: - JS_FreeValue(ctx, lval); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -// 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) -{ - JSValue obj, arr, val, len_val; - int64_t len, start, k, final, n, count, del_count, new_len; - int kPresent; - JSValue *arrp; - uint32_t count32, i, item_count; - - arr = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) - goto exception; - - if (splice) { - if (argc == 0) { - item_count = 0; - del_count = 0; - } else - if (argc == 1) { - item_count = 0; - del_count = len - start; - } else { - item_count = argc - 2; - if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) - goto exception; - } - if (len + item_count - del_count > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - count = del_count; - } else { - item_count = 0; /* avoid warning */ - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int64(final - start, 0); - } - len_val = JS_NewInt64(ctx, count); - arr = JS_ArraySpeciesCreate(ctx, obj, len_val); - JS_FreeValue(ctx, len_val); - if (JS_IsException(arr)) - goto exception; - - k = start; - final = start + count; - n = 0; - /* The fast array test on arr ensures that - JS_CreateDataPropertyUint32() won't modify obj in case arr is - an exotic object */ - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && - js_is_fast_array(ctx, arr)) { - /* XXX: should share code with fast array constructor */ - for (; k < final && k < count32; k++, n++) { - if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) - goto exception; - } - } - /* Copy the remaining elements if any (handle case of inherited properties) */ - for (; k < final; k++, n++) { - kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); - if (kPresent < 0) - goto exception; - if (kPresent) { - if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) - goto exception; - - if (splice) { - new_len = len + item_count - del_count; - if (item_count != del_count) { - if (JS_CopySubArray(ctx, obj, start + item_count, - start + del_count, len - (start + del_count), - item_count <= del_count ? +1 : -1) < 0) - goto exception; - - for (k = len; k-- > new_len; ) { - if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) - goto exception; - } - } - for (i = 0; i < item_count; i++) { - if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) - goto exception; - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) - goto exception; - } - JS_FreeValue(ctx, obj); - return arr; - - exception: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -static JSValue js_array_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) -{ - JSValue obj; - int64_t len, from, to, final, count; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) - goto exception; - - if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) - goto exception; - - final = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) - goto exception; - } - - count = min_int64(final - from, len - to); - - if (JS_CopySubArray(ctx, obj, to, from, count, - (from < to && to < from + count) ? -1 : +1)) - goto exception; - - return obj; - - exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, - JSValueConst source, int64_t sourceLen, - int64_t targetIndex, int depth, - JSValueConst mapperFunction, - JSValueConst thisArg) -{ - JSValue element; - int64_t sourceIndex, elementLen; - int present, is_array; - - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return -1; - } - - for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { - present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); - if (present < 0) - return -1; - if (!present) - continue; - if (!JS_IsUndefined(mapperFunction)) { - JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; - element = JS_Call(ctx, mapperFunction, thisArg, 3, args); - JS_FreeValue(ctx, (JSValue)args[0]); - JS_FreeValue(ctx, (JSValue)args[1]); - if (JS_IsException(element)) - return -1; - } - if (depth > 0) { - is_array = JS_IsArray(ctx, element); - if (is_array < 0) - goto fail; - if (is_array) { - if (js_get_length64(ctx, &elementLen, element) < 0) - goto fail; - targetIndex = JS_FlattenIntoArray(ctx, target, element, - elementLen, targetIndex, - depth - 1, - JS_UNDEFINED, JS_UNDEFINED); - if (targetIndex < 0) - goto fail; - JS_FreeValue(ctx, element); - continue; - } - } - if (targetIndex >= MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array too long"); - goto fail; - } - if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - return -1; - targetIndex++; - } - return targetIndex; - -fail: - JS_FreeValue(ctx, element); - return -1; -} - -static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int map) -{ - JSValue obj, arr; - JSValueConst mapperFunction, thisArg; - int64_t sourceLen; - int depthNum; - - arr = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &sourceLen, obj)) - goto exception; - - depthNum = 1; - mapperFunction = JS_UNDEFINED; - thisArg = JS_UNDEFINED; - if (map) { - mapperFunction = argv[0]; - if (argc > 1) { - thisArg = argv[1]; - } - if (check_function(ctx, mapperFunction)) - goto exception; - } else { - if (argc > 0 && !JS_IsUndefined(argv[0])) { - if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) - goto exception; - } - } - arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); - if (JS_IsException(arr)) - goto exception; - if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, - mapperFunction, thisArg) < 0) - goto exception; - JS_FreeValue(ctx, obj); - return arr; - -exception: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -/* Array sort */ - -typedef struct ValueSlot { - JSValue val; - JSString *str; - int64_t pos; -} ValueSlot; - -struct array_sort_context { - JSContext *ctx; - int exception; - int has_method; - JSValueConst method; -}; - -static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { - struct array_sort_context *psc = opaque; - JSContext *ctx = psc->ctx; - JSValueConst argv[2]; - JSValue res; - ValueSlot *ap = (ValueSlot *)(void *)a; - ValueSlot *bp = (ValueSlot *)(void *)b; - int cmp; - - if (psc->exception) - return 0; - - if (psc->has_method) { - /* custom sort function is specified as returning 0 for identical - * objects: avoid method call overhead. - */ - if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) - goto cmp_same; - argv[0] = ap->val; - argv[1] = bp->val; - res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); - if (JS_IsException(res)) - goto exception; - if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { - int val = JS_VALUE_GET_INT(res); - cmp = (val > 0) - (val < 0); - } else { - double val; - if (JS_ToFloat64Free(ctx, &val, res) < 0) - goto exception; - cmp = (val > 0) - (val < 0); - } - } else { - /* Not supposed to bypass ToString even for identical objects as - * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js - */ - if (!ap->str) { - JSValue str = JS_ToString(ctx, ap->val); - if (JS_IsException(str)) - goto exception; - ap->str = JS_VALUE_GET_STRING(str); - } - if (!bp->str) { - JSValue str = JS_ToString(ctx, bp->val); - if (JS_IsException(str)) - goto exception; - bp->str = JS_VALUE_GET_STRING(str); - } - cmp = js_string_compare(ctx, ap->str, bp->str); - } - if (cmp != 0) - return cmp; -cmp_same: - /* make sort stable: compare array offsets */ - return (ap->pos > bp->pos) - (ap->pos < bp->pos); - -exception: - psc->exception = 1; - return 0; -} - -static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - struct array_sort_context asc = { ctx, 0, 0, argv[0] }; - JSValue obj = JS_UNDEFINED; - ValueSlot *array = NULL; - size_t array_size = 0, pos = 0, n = 0; - int64_t i, len, undefined_count = 0; - int present; - - if (!JS_IsUndefined(asc.method)) { - if (check_function(ctx, asc.method)) - goto exception; - asc.has_method = 1; - } - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - /* XXX: should special case fast arrays */ - for (i = 0; i < len; i++) { - if (pos >= array_size) { - size_t new_size, slack; - ValueSlot *new_array; - new_size = (array_size + (array_size >> 1) + 31) & ~15; - new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); - if (new_array == NULL) - goto exception; - new_size += slack / sizeof(*new_array); - array = new_array; - array_size = new_size; - } - present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); - if (present < 0) - goto exception; - if (present == 0) - continue; - if (JS_IsUndefined(array[pos].val)) { - undefined_count++; - continue; - } - array[pos].str = NULL; - array[pos].pos = i; - pos++; - } - rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); - if (asc.exception) - goto exception; - - /* XXX: should special case fast arrays */ - while (n < pos) { - if (array[n].str) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); - if (array[n].pos == n) { - JS_FreeValue(ctx, array[n].val); - } else { - if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { - n++; - goto exception; - } - } - n++; - } - js_free(ctx, array); - for (i = n; undefined_count-- > 0; i++) { - if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) - goto fail; - } - for (; i < len; i++) { - if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) - goto fail; - } - return obj; - -exception: - for (; n < pos; n++) { - JS_FreeValue(ctx, array[n].val); - if (array[n].str) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); - } - js_free(ctx, array); -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -// 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; - uint32_t idx; -} JSArrayIteratorData; - -static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSArrayIteratorData *it = p->u.array_iterator_data; - if (it) { - JS_FreeValueRT(rt, it->obj); - js_free_rt(rt, it); - } -} - -static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSArrayIteratorData *it = p->u.array_iterator_data; - if (it) { - JS_MarkValue(rt, it->obj, mark_func); - } -} - -static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) -{ - JSValue obj; - int i; - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - for(i = 0; i < len; i++) { - if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; -} - -static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue enum_obj, arr; - JSArrayIteratorData *it; - JSIteratorKindEnum kind; - int class_id; - - kind = magic & 3; - if (magic & 4) { - /* string iterator case */ - arr = JS_ToStringCheckObject(ctx, this_val); - class_id = JS_CLASS_STRING_ITERATOR; - } else { - arr = JS_ToObject(ctx, this_val); - class_id = JS_CLASS_ARRAY_ITERATOR; - } - if (JS_IsException(arr)) - goto fail; - enum_obj = JS_NewObjectClass(ctx, class_id); - if (JS_IsException(enum_obj)) - goto fail; - it = js_malloc(ctx, sizeof(*it)); - if (!it) - goto fail1; - it->obj = arr; - it->kind = kind; - it->idx = 0; - JS_SetOpaque(enum_obj, it); - return enum_obj; - fail1: - JS_FreeValue(ctx, enum_obj); - fail: - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSArrayIteratorData *it; - uint32_t len, idx; - JSValue val, obj; - JSObject *p; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); - if (!it) - goto fail1; - if (JS_IsUndefined(it->obj)) - goto done; - p = JS_VALUE_GET_OBJ(it->obj); - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail1; - } - len = p->u.array.count; - } else { - if (js_get_length32(ctx, &len, it->obj)) { - fail1: - *pdone = FALSE; - return JS_EXCEPTION; - } - } - idx = it->idx; - if (idx >= len) { - JS_FreeValue(ctx, it->obj); - it->obj = JS_UNDEFINED; - done: - *pdone = TRUE; - return JS_UNDEFINED; - } - it->idx = idx + 1; - *pdone = FALSE; - if (it->kind == JS_ITERATOR_KIND_KEY) { - return JS_NewUint32(ctx, idx); - } else { - val = JS_GetPropertyUint32(ctx, it->obj, idx); - if (JS_IsException(val)) - return JS_EXCEPTION; - if (it->kind == JS_ITERATOR_KIND_VALUE) { - return val; - } else { - JSValueConst args[2]; - JSValue num; - num = JS_NewUint32(ctx, idx); - args[0] = num; - args[1] = val; - obj = js_create_array(ctx, 2, args); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, num); - return obj; - } - } -} - -static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_DupValue(ctx, this_val); -} - -static const JSCFunctionListEntry js_iterator_proto_funcs[] = { - JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ), -}; - -static const JSCFunctionListEntry js_array_proto_funcs[] = { - JS_CFUNC_DEF("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 ), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ), - JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ), - JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ), - JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), - JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), - JS_CFUNC_DEF("fill", 1, js_array_fill ), - JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 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 ), - JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ), - JS_CFUNC_DEF("toString", 0, js_array_toString ), - JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ), - JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ), - JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), - JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), - JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), - JS_CFUNC_DEF("reverse", 0, js_array_reverse ), - JS_CFUNC_DEF("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 ), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ), - JS_ALIAS_DEF("[Symbol.iterator]", "values" ), - JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), -}; - -static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ), -}; - -/* Number */ - -static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue val, obj; - if (argc == 0) { - val = JS_NewInt32(ctx, 0); - } else { - val = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(val)) - return val; - switch(JS_VALUE_GET_TAG(val)) { - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - double d; - bf_get_float64(&p->num, &d, BF_RNDN); - JS_FreeValue(ctx, val); - val = __JS_NewFloat64(ctx, d); - } - break; -#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; - } - } - if (!JS_IsUndefined(new_target)) { - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, val); - return obj; - } else { - return val; - } -} - -#if 0 -static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0])); -} - -static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t v; - if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0]))) - return JS_EXCEPTION; - return JS_NewInt64(ctx, v); -} -#endif - -static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (!JS_IsNumber(argv[0])) - return JS_FALSE; - return js_global_isNaN(ctx, this_val, argc, argv); -} - -static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (!JS_IsNumber(argv[0])) - return JS_FALSE; - return js_global_isFinite(ctx, this_val, argc, argv); -} - -static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_NumberIsInteger(ctx, argv[0]); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double d; - if (!JS_IsNumber(argv[0])) - return JS_FALSE; - if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) - return JS_EXCEPTION; - return JS_NewBool(ctx, is_safe_integer(d)); -} - -static const JSCFunctionListEntry js_number_funcs[] = { - /* global ParseInt and parseFloat should be defined already or delayed */ - JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ), - JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ), - JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ), - JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ), - JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ), - JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ), - JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), - JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), - JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), - JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), - JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), - JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ - JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ - JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ - //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ), - //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ), -}; - -static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsNumber(this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_NUMBER) { - if (JS_IsNumber(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a number"); -} - -static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisNumberValue(ctx, this_val); -} - -static int js_get_radix(JSContext *ctx, JSValueConst val) -{ - int radix; - if (JS_ToInt32Sat(ctx, &radix, val)) - return -1; - if (radix < 2 || radix > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - return -1; - } - return radix; -} - -static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue val; - int base; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (magic || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; - } - if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { - char buf1[70], *ptr; - ptr = i64toa(buf1 + sizeof(buf1), JS_VALUE_GET_INT(val), base); - return JS_NewString(ctx, ptr); - } - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (base != 10 && isfinite(d)) { - return js_dtoa_radix(ctx, d, base); - } - return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT); - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int f; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (JS_ToInt32Sat(ctx, &f, argv[0])) - return JS_EXCEPTION; - if (f < 0 || f > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - if (fabs(d) >= 1e21) { - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } else { - return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); - } -} - -static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int f, flags; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (JS_ToInt32Sat(ctx, &f, argv[0])) - return JS_EXCEPTION; - if (!isfinite(d)) { - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } - if (JS_IsUndefined(argv[0])) { - flags = 0; - f = 0; - } else { - if (f < 0 || f > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - f++; - flags = JS_DTOA_FIXED_FORMAT; - } - return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP); -} - -static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int p; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (JS_IsUndefined(argv[0])) - goto to_string; - if (JS_ToInt32Sat(ctx, &p, argv[0])) - return JS_EXCEPTION; - if (!isfinite(d)) { - to_string: - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } - if (p < 1 || p > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT); -} - -static const JSCFunctionListEntry js_number_proto_funcs[] = { - JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ), - JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ), - JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ), - JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ), - JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ), - JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), -}; - -static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *str, *p; - int radix, flags; - JSValue ret; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &radix, argv[1])) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - ret = JS_NAN; - } else { - p = str; - p += skip_spaces(p); - flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; - ret = js_atof(ctx, p, NULL, radix, flags); - } - JS_FreeCString(ctx, str); - return ret; -} - -static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *str, *p; - JSValue ret; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - ret = js_atof(ctx, p, NULL, 10, 0); - JS_FreeCString(ctx, str); - return ret; -} - -/* Boolean */ -static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue val, obj; - val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0])); - if (!JS_IsUndefined(new_target)) { - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, val); - return obj; - } else { - return val; - } -} - -static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BOOLEAN) { - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) - return p->u.object_data; - } - } - return JS_ThrowTypeError(ctx, "not a boolean"); -} - -static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val = js_thisBooleanValue(ctx, this_val); - if (JS_IsException(val)) - return val; - return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? - JS_ATOM_true : JS_ATOM_false); -} - -static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBooleanValue(ctx, this_val); -} - -static const JSCFunctionListEntry js_boolean_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_boolean_toString ), - JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ), -}; - -/* String */ - -static int js_string_get_own_property(JSContext *ctx, - JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop) -{ - JSObject *p; - JSString *p1; - uint32_t idx, ch; - - /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ - if (__JS_AtomIsTaggedInt(prop)) { - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { - p1 = JS_VALUE_GET_STRING(p->u.object_data); - idx = __JS_AtomToUInt32(prop); - if (idx < p1->len) { - if (desc) { - ch = string_get(p1, idx); - desc->flags = JS_PROP_ENUMERABLE; - desc->value = js_new_string_char(ctx, ch); - desc->getter = JS_UNDEFINED; - desc->setter = JS_UNDEFINED; - } - return TRUE; - } - } - } - return FALSE; -} - -static int js_string_define_own_property(JSContext *ctx, - JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, - JSValueConst setter, int flags) -{ - uint32_t idx; - JSObject *p; - JSString *p1, *p2; - - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - p = JS_VALUE_GET_OBJ(this_obj); - if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) - goto def; - p1 = JS_VALUE_GET_STRING(p->u.object_data); - if (idx >= p1->len) - goto def; - if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) - goto fail; - /* check that the same value is configured */ - if (flags & JS_PROP_HAS_VALUE) { - if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) - goto fail; - p2 = JS_VALUE_GET_STRING(val); - if (p2->len != 1) - goto fail; - if (string_get(p1, idx) != string_get(p2, 0)) { - fail: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); - } - } - return TRUE; - } else { - def: - return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, - flags | JS_PROP_NO_EXOTIC); - } -} - -static int js_string_delete_property(JSContext *ctx, - JSValueConst obj, JSAtom prop) -{ - uint32_t idx; - - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx < js_string_obj_get_length(ctx, obj)) { - return FALSE; - } - } - return TRUE; -} - -static const JSClassExoticMethods js_string_exotic_methods = { - .get_own_property = js_string_get_own_property, - .define_own_property = js_string_define_own_property, - .delete_property = js_string_delete_property, -}; - -static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue val, obj; - if (argc == 0) { - val = JS_AtomToString(ctx, JS_ATOM_empty_string); - } else { - if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { - JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]); - val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); - } else { - val = JS_ToString(ctx, argv[0]); - } - if (JS_IsException(val)) - return val; - } - if (!JS_IsUndefined(new_target)) { - JSString *p1 = JS_VALUE_GET_STRING(val); - - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); - if (!JS_IsException(obj)) { - JS_SetObjectData(ctx, obj, val); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); - } - return obj; - } else { - return val; - } -} - -static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_STRING) { - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a string"); -} - -static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int i; - StringBuffer b_s, *b = &b_s; - - string_buffer_init(ctx, b, argc); - - for(i = 0; i < argc; i++) { - int32_t c; - if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { - string_buffer_free(b); - return JS_EXCEPTION; - } - } - return string_buffer_end(b); -} - -static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double d; - int i, c; - StringBuffer b_s, *b = &b_s; - - /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ - - if (string_buffer_init(ctx, b, argc)) - goto fail; - for(i = 0; i < argc; i++) { - if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { - c = JS_VALUE_GET_INT(argv[i]); - if (c < 0 || c > 0x10ffff) - goto range_error; - } else { - if (JS_ToFloat64(ctx, &d, argv[i])) - goto fail; - if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d) - goto range_error; - } - if (string_buffer_putc(b, c)) - goto fail; - } - return string_buffer_end(b); - - range_error: - JS_ThrowRangeError(ctx, "invalid code point"); - fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // raw(temp,...a) - JSValue cooked, val, raw; - StringBuffer b_s, *b = &b_s; - int64_t i, n; - - string_buffer_init(ctx, b, 0); - raw = JS_UNDEFINED; - cooked = JS_ToObject(ctx, argv[0]); - if (JS_IsException(cooked)) - goto exception; - raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); - if (JS_IsException(raw)) - goto exception; - if (js_get_length64(ctx, &n, raw) < 0) - goto exception; - - for (i = 0; i < n; i++) { - val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); - if (JS_IsException(val)) - goto exception; - string_buffer_concat_value_free(b, val); - if (i < n - 1 && i + 1 < argc) { - if (string_buffer_concat_value(b, argv[i + 1])) - goto exception; - } - } - JS_FreeValue(ctx, cooked); - JS_FreeValue(ctx, raw); - return string_buffer_end(b); - -exception: - JS_FreeValue(ctx, cooked); - JS_FreeValue(ctx, raw); - string_buffer_free(b); - return JS_EXCEPTION; -} - -/* only used in test262 */ -JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint32_t start, end, i, n; - StringBuffer b_s, *b = &b_s; - - if (JS_ToUint32(ctx, &start, argv[0]) || - JS_ToUint32(ctx, &end, argv[1])) - return JS_EXCEPTION; - end = min_uint32(end, 0x10ffff + 1); - - if (start > end) { - start = end; - } - n = end - start; - if (end > 0x10000) { - n += end - max_uint32(start, 0x10000); - } - if (string_buffer_init2(ctx, b, n, end >= 0x100)) - return JS_EXCEPTION; - for(i = start; i < end; i++) { - string_buffer_putc(b, i); - } - return string_buffer_end(b); -} - -#if 0 -static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int c; - if (JS_ToInt32(ctx, &c, argv[0])) - return JS_EXCEPTION; - return JS_NewBool(ctx, lre_is_space(c)); -} -#endif - -static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - JSString *p; - int idx, c; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (JS_ToInt32Sat(ctx, &idx, argv[0])) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (idx < 0 || idx >= p->len) { - ret = JS_NAN; - } else { - c = string_get(p, idx); - ret = JS_NewInt32(ctx, c); - } - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_at) -{ - JSValue val, ret; - JSString *p; - int idx, c; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (JS_ToInt32Sat(ctx, &idx, argv[0])) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (idx < 0 && is_at) - idx += p->len; - if (idx < 0 || idx >= p->len) { - if (is_at) - ret = JS_UNDEFINED; - else - ret = js_new_string8(ctx, NULL, 0); - } else { - c = string_get(p, idx); - ret = js_new_string_char(ctx, c); - } - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - JSString *p; - int idx, c; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (JS_ToInt32Sat(ctx, &idx, argv[0])) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (idx < 0 || idx >= p->len) { - ret = JS_UNDEFINED; - } else { - c = string_getc(p, &idx); - ret = JS_NewInt32(ctx, c); - } - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue r; - int i; - - /* XXX: Use more efficient method */ - /* XXX: This method is OK if r has a single refcount */ - /* XXX: should use string_buffer? */ - r = JS_ToStringCheckObject(ctx, this_val); - for (i = 0; i < argc; i++) { - if (JS_IsException(r)) - break; - r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i])); - } - return r; -} - -static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) -{ - int i, c1, c2; - for (i = 0; i < len; i++) { - if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) - return c1 - c2; - } - return 0; -} - -static int string_indexof_char(JSString *p, int c, int from) -{ - /* assuming 0 <= from <= p->len */ - int i, len = p->len; - if (p->is_wide_char) { - for (i = from; i < len; i++) { - if (p->u.str16[i] == c) - return i; - } - } else { - if ((c & ~0xff) == 0) { - for (i = from; i < len; i++) { - if (p->u.str8[i] == (uint8_t)c) - return i; - } - } - } - return -1; -} - -static int string_indexof(JSString *p1, JSString *p2, int from) -{ - /* assuming 0 <= from <= p1->len */ - int c, i, j, len1 = p1->len, len2 = p2->len; - if (len2 == 0) - return from; - for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { - j = string_indexof_char(p1, c, i); - if (j < 0 || j + len2 > len1) - break; - if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) - return j; - } - return -1; -} - -static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) -{ - if (!unicode || index >= p->len || !p->is_wide_char) { - index++; - } else { - int index32 = (int)index; - string_getc(p, &index32); - index = index32; - } - return index; -} - -/* 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(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) -{ - JSValue str, v; - int i, len, v_len, pos, start, stop, ret, inc; - JSString *p; - JSString *p1; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - v = JS_ToString(ctx, argv[0]); - if (JS_IsException(v)) - goto fail; - p = JS_VALUE_GET_STRING(str); - p1 = JS_VALUE_GET_STRING(v); - len = p->len; - v_len = p1->len; - if (lastIndexOf) { - pos = len - v_len; - if (argc > 1) { - double d; - if (JS_ToFloat64(ctx, &d, argv[1])) - goto fail; - if (!isnan(d)) { - if (d <= 0) - pos = 0; - else if (d < pos) - pos = d; - } - } - start = pos; - stop = 0; - inc = -1; - } else { - pos = 0; - if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) - goto fail; - } - start = pos; - stop = len - v_len; - inc = 1; - } - ret = -1; - if (len >= v_len && inc * (stop - start) >= 0) { - for (i = start;; i += inc) { - if (!string_cmp(p, p1, i, 0, v_len)) { - ret = i; - break; - } - if (i == stop) - break; - } - } - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_NewInt32(ctx, ret); - -fail: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_EXCEPTION; -} - -/* return < 0 if exception or TRUE/FALSE */ -static int js_is_regexp(JSContext *ctx, JSValueConst obj); - -static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue str, v = JS_UNDEFINED; - int i, len, v_len, pos, start, stop, ret; - JSString *p; - JSString *p1; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - ret = js_is_regexp(ctx, argv[0]); - if (ret) { - if (ret > 0) - JS_ThrowTypeError(ctx, "regexp not supported"); - goto fail; - } - v = JS_ToString(ctx, argv[0]); - if (JS_IsException(v)) - goto fail; - p = JS_VALUE_GET_STRING(str); - p1 = JS_VALUE_GET_STRING(v); - len = p->len; - v_len = p1->len; - pos = (magic == 2) ? len : 0; - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) - goto fail; - } - len -= v_len; - ret = 0; - if (magic == 0) { - start = pos; - stop = len; - } else { - if (magic == 1) { - if (pos > len) - goto done; - } else { - pos -= v_len; - } - start = stop = pos; - } - if (start >= 0 && start <= stop) { - for (i = start;; i++) { - if (!string_cmp(p, p1, i, 0, v_len)) { - ret = 1; - break; - } - if (i == stop) - break; - } - } - done: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_NewBool(ctx, ret); - -fail: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_EXCEPTION; -} - -static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp) -{ - int ret; - JSValue flags; - - ret = js_is_regexp(ctx, regexp); - if (ret < 0) - return -1; - if (ret) { - flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags); - if (JS_IsException(flags)) - return -1; - if (JS_IsUndefined(flags) || JS_IsNull(flags)) { - JS_ThrowTypeError(ctx, "cannot convert to object"); - return -1; - } - flags = JS_ToStringFree(ctx, flags); - if (JS_IsException(flags)) - return -1; - ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0); - JS_FreeValue(ctx, flags); - if (ret < 0) { - JS_ThrowTypeError(ctx, "regexp must have the 'g' flag"); - return -1; - } - } - return 0; -} - -static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int atom) -{ - // match(rx), search(rx), matchAll(rx) - // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll - JSValueConst O = this_val, regexp = argv[0], args[2]; - JSValue matcher, S, rx, result, str; - int args_len; - - if (JS_IsUndefined(O) || JS_IsNull(O)) - return JS_ThrowTypeError(ctx, "cannot convert to object"); - - if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { - matcher = JS_GetProperty(ctx, regexp, atom); - if (JS_IsException(matcher)) - return JS_EXCEPTION; - if (atom == JS_ATOM_Symbol_matchAll) { - if (check_regexp_g_flag(ctx, regexp) < 0) { - JS_FreeValue(ctx, matcher); - return JS_EXCEPTION; - } - } - if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { - return JS_CallFree(ctx, matcher, regexp, 1, &O); - } - } - S = JS_ToString(ctx, O); - if (JS_IsException(S)) - return JS_EXCEPTION; - args_len = 1; - args[0] = regexp; - str = JS_UNDEFINED; - if (atom == JS_ATOM_Symbol_matchAll) { - str = JS_NewString(ctx, "g"); - if (JS_IsException(str)) - goto fail; - args[args_len++] = (JSValueConst)str; - } - rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); - JS_FreeValue(ctx, str); - if (JS_IsException(rx)) { - fail: - JS_FreeValue(ctx, S); - return JS_EXCEPTION; - } - result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S); - JS_FreeValue(ctx, S); - return result; -} - -static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // GetSubstitution(matched, str, position, captures, namedCaptures, rep) - JSValueConst matched, str, captures, namedCaptures, rep; - JSValue capture, name, s; - uint32_t position, len, matched_len, captures_len; - int i, j, j0, k, k1; - int c, c1; - StringBuffer b_s, *b = &b_s; - JSString *sp, *rp; - - matched = argv[0]; - str = argv[1]; - captures = argv[3]; - namedCaptures = argv[4]; - rep = argv[5]; - - if (!JS_IsString(rep) || !JS_IsString(str)) - return JS_ThrowTypeError(ctx, "not a string"); - - sp = JS_VALUE_GET_STRING(str); - rp = JS_VALUE_GET_STRING(rep); - - string_buffer_init(ctx, b, 0); - - captures_len = 0; - if (!JS_IsUndefined(captures)) { - if (js_get_length32(ctx, &captures_len, captures)) - goto exception; - } - if (js_get_length32(ctx, &matched_len, matched)) - goto exception; - if (JS_ToUint32(ctx, &position, argv[2]) < 0) - goto exception; - - len = rp->len; - i = 0; - for(;;) { - j = string_indexof_char(rp, '$', i); - if (j < 0 || j + 1 >= len) - break; - string_buffer_concat(b, rp, i, j); - j0 = j++; - c = string_get(rp, j++); - if (c == '$') { - string_buffer_putc8(b, '$'); - } else if (c == '&') { - if (string_buffer_concat_value(b, matched)) - goto exception; - } else if (c == '`') { - string_buffer_concat(b, sp, 0, position); - } else if (c == '\'') { - string_buffer_concat(b, sp, position + matched_len, sp->len); - } else if (c >= '0' && c <= '9') { - k = c - '0'; - if (j < len) { - c1 = string_get(rp, j); - if (c1 >= '0' && c1 <= '9') { - /* This behavior is specified in ES6 and refined in ECMA 2019 */ - /* ECMA 2019 does not have the extra test, but - Test262 S15.5.4.11_A3_T1..3 require this behavior */ - k1 = k * 10 + c1 - '0'; - if (k1 >= 1 && k1 < captures_len) { - k = k1; - j++; - } - } - } - if (k >= 1 && k < captures_len) { - s = JS_GetPropertyInt64(ctx, captures, k); - if (JS_IsException(s)) - goto exception; - if (!JS_IsUndefined(s)) { - if (string_buffer_concat_value_free(b, s)) - goto exception; - } - } else { - goto norep; - } - } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { - k = string_indexof_char(rp, '>', j); - if (k < 0) - goto norep; - name = js_sub_string(ctx, rp, j, k); - if (JS_IsException(name)) - goto exception; - capture = JS_GetPropertyValue(ctx, namedCaptures, name); - if (JS_IsException(capture)) - goto exception; - if (!JS_IsUndefined(capture)) { - if (string_buffer_concat_value_free(b, capture)) - goto exception; - } - j = k + 1; - } else { - norep: - string_buffer_concat(b, rp, j0, j); - } - i = j; - } - string_buffer_concat(b, rp, i, rp->len); - return string_buffer_end(b); -exception: - string_buffer_free(b); - return JS_EXCEPTION; -} - -static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int is_replaceAll) -{ - // replace(rx, rep) - JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; - JSValueConst args[6]; - JSValue str, search_str, replaceValue_str, repl_str; - JSString *sp, *searchp; - StringBuffer b_s, *b = &b_s; - int pos, functionalReplace, endOfLastMatch; - BOOL is_first; - - if (JS_IsUndefined(O) || JS_IsNull(O)) - return JS_ThrowTypeError(ctx, "cannot convert to object"); - - search_str = JS_UNDEFINED; - replaceValue_str = JS_UNDEFINED; - repl_str = JS_UNDEFINED; - - if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { - JSValue replacer; - if (is_replaceAll) { - if (check_regexp_g_flag(ctx, searchValue) < 0) - return JS_EXCEPTION; - } - replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); - if (JS_IsException(replacer)) - return JS_EXCEPTION; - if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { - args[0] = O; - args[1] = replaceValue; - return JS_CallFree(ctx, replacer, searchValue, 2, args); - } - } - string_buffer_init(ctx, b, 0); - - str = JS_ToString(ctx, O); - if (JS_IsException(str)) - goto exception; - search_str = JS_ToString(ctx, searchValue); - if (JS_IsException(search_str)) - goto exception; - functionalReplace = JS_IsFunction(ctx, replaceValue); - if (!functionalReplace) { - replaceValue_str = JS_ToString(ctx, replaceValue); - if (JS_IsException(replaceValue_str)) - goto exception; - } - - sp = JS_VALUE_GET_STRING(str); - searchp = JS_VALUE_GET_STRING(search_str); - endOfLastMatch = 0; - is_first = TRUE; - for(;;) { - if (unlikely(searchp->len == 0)) { - if (is_first) - pos = 0; - else if (endOfLastMatch >= sp->len) - pos = -1; - else - pos = endOfLastMatch + 1; - } else { - pos = string_indexof(sp, searchp, endOfLastMatch); - } - if (pos < 0) { - if (is_first) { - string_buffer_free(b); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - return str; - } else { - break; - } - } - if (functionalReplace) { - args[0] = search_str; - args[1] = JS_NewInt32(ctx, pos); - args[2] = str; - repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); - } else { - args[0] = search_str; - args[1] = str; - args[2] = JS_NewInt32(ctx, pos); - args[3] = JS_UNDEFINED; - args[4] = JS_UNDEFINED; - args[5] = replaceValue_str; - repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); - } - if (JS_IsException(repl_str)) - goto exception; - - string_buffer_concat(b, sp, endOfLastMatch, pos); - string_buffer_concat_value_free(b, repl_str); - endOfLastMatch = pos + searchp->len; - is_first = FALSE; - if (!is_replaceAll) - break; - } - string_buffer_concat(b, sp, endOfLastMatch, sp->len); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -exception: - string_buffer_free(b); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - JS_FreeValue(ctx, str); - return JS_EXCEPTION; -} - -static JSValue js_string_split(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // split(sep, limit) - JSValueConst O = this_val, separator = argv[0], limit = argv[1]; - JSValueConst args[2]; - JSValue S, A, R, T; - uint32_t lim, lengthA; - int64_t p, q, s, r, e; - JSString *sp, *rp; - - if (JS_IsUndefined(O) || JS_IsNull(O)) - return JS_ThrowTypeError(ctx, "cannot convert to object"); - - S = JS_UNDEFINED; - A = JS_UNDEFINED; - R = JS_UNDEFINED; - - if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { - JSValue splitter; - splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); - if (JS_IsException(splitter)) - return JS_EXCEPTION; - if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { - args[0] = O; - args[1] = limit; - return JS_CallFree(ctx, splitter, separator, 2, args); - } - } - S = JS_ToString(ctx, O); - if (JS_IsException(S)) - goto exception; - A = JS_NewArray(ctx); - if (JS_IsException(A)) - goto exception; - lengthA = 0; - if (JS_IsUndefined(limit)) { - lim = 0xffffffff; - } else { - if (JS_ToUint32(ctx, &lim, limit) < 0) - goto exception; - } - sp = JS_VALUE_GET_STRING(S); - s = sp->len; - R = JS_ToString(ctx, separator); - if (JS_IsException(R)) - goto exception; - rp = JS_VALUE_GET_STRING(R); - r = rp->len; - p = 0; - if (lim == 0) - goto done; - if (JS_IsUndefined(separator)) - goto add_tail; - if (s == 0) { - if (r != 0) - goto add_tail; - goto done; - } - q = p; - for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { - e = string_indexof(sp, rp, q); - if (e < 0) - break; - T = js_sub_string(ctx, sp, p, e); - if (JS_IsException(T)) - goto exception; - if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) - goto exception; - if (lengthA == lim) - goto done; - } -add_tail: - T = js_sub_string(ctx, sp, p, s); - if (JS_IsException(T)) - goto exception; - if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0) - goto exception; -done: - JS_FreeValue(ctx, S); - JS_FreeValue(ctx, R); - return A; - -exception: - JS_FreeValue(ctx, A); - JS_FreeValue(ctx, S); - JS_FreeValue(ctx, R); - return JS_EXCEPTION; -} - -static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str, ret; - int a, b, start, end; - JSString *p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - b = p->len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - } - if (a < b) { - start = a; - end = b; - } else { - start = b; - end = a; - } - ret = js_sub_string(ctx, p, start, end); - JS_FreeValue(ctx, str); - return ret; -} - -static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str, ret; - int a, len, n; - JSString *p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - n = len - a; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - } - ret = js_sub_string(ctx, p, a, a + n); - JS_FreeValue(ctx, str); - return ret; -} - -static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str, ret; - int len, start, end; - JSString *p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - end = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - } - ret = js_sub_string(ctx, p, start, max_int(end, start)); - JS_FreeValue(ctx, str); - return ret; -} - -static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int padEnd) -{ - JSValue str, v = JS_UNDEFINED; - StringBuffer b_s, *b = &b_s; - JSString *p, *p1 = NULL; - int n, len, c = ' '; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - goto fail1; - if (JS_ToInt32Sat(ctx, &n, argv[0])) - goto fail2; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (len >= n) - return str; - if (argc > 1 && !JS_IsUndefined(argv[1])) { - v = JS_ToString(ctx, argv[1]); - if (JS_IsException(v)) - goto fail2; - p1 = JS_VALUE_GET_STRING(v); - if (p1->len == 0) { - JS_FreeValue(ctx, v); - return str; - } - if (p1->len == 1) { - c = string_get(p1, 0); - p1 = NULL; - } - } - if (n > JS_STRING_LEN_MAX) { - JS_ThrowRangeError(ctx, "invalid string length"); - goto fail2; - } - if (string_buffer_init(ctx, b, n)) - goto fail3; - n -= len; - if (padEnd) { - if (string_buffer_concat(b, p, 0, len)) - goto fail; - } - if (p1) { - while (n > 0) { - int chunk = min_int(n, p1->len); - if (string_buffer_concat(b, p1, 0, chunk)) - goto fail; - n -= chunk; - } - } else { - if (string_buffer_fill(b, c, n)) - goto fail; - } - if (!padEnd) { - if (string_buffer_concat(b, p, 0, len)) - goto fail; - } - JS_FreeValue(ctx, v); - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -fail: - string_buffer_free(b); -fail3: - JS_FreeValue(ctx, v); -fail2: - JS_FreeValue(ctx, str); -fail1: - return JS_EXCEPTION; -} - -static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - StringBuffer b_s, *b = &b_s; - JSString *p; - int64_t val; - int n, len; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - goto fail; - if (JS_ToInt64Sat(ctx, &val, argv[0])) - goto fail; - if (val < 0 || val > 2147483647) { - JS_ThrowRangeError(ctx, "invalid repeat count"); - goto fail; - } - n = val; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (len == 0 || n == 1) - return str; - // XXX: potential arithmetic overflow - if (val * len > JS_STRING_LEN_MAX) { - JS_ThrowRangeError(ctx, "invalid string length"); - goto fail; - } - if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) - goto fail; - if (len == 1) { - string_buffer_fill(b, string_get(p, 0), n); - } else { - while (n-- > 0) { - string_buffer_concat(b, p, 0, len); - } - } - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -fail: - JS_FreeValue(ctx, str); - return JS_EXCEPTION; -} - -static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue str, ret; - int a, b, len; - JSString *p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - a = 0; - b = len = p->len; - if (magic & 1) { - while (a < len && lre_is_space(string_get(p, a))) - a++; - } - if (magic & 2) { - while (b > a && lre_is_space(string_get(p, b - 1))) - b--; - } - ret = js_sub_string(ctx, p, a, b); - JS_FreeValue(ctx, str); - return ret; -} - -static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToQuotedString(ctx, this_val); -} - -/* return 0 if before the first char */ -static int string_prevc(JSString *p, int *pidx) -{ - int idx, c, c1; - - idx = *pidx; - if (idx <= 0) - return 0; - idx--; - if (p->is_wide_char) { - c = p->u.str16[idx]; - if (is_lo_surrogate(c) && idx > 0) { - c1 = p->u.str16[idx - 1]; - if (is_hi_surrogate(c1)) { - c = from_surrogate(c1, c); - idx--; - } - } - } else { - c = p->u.str8[idx]; - } - *pidx = idx; - return c; -} - -static BOOL test_final_sigma(JSString *p, int sigma_pos) -{ - int k, c1; - - /* before C: skip case ignorable chars and check there is - a cased letter */ - k = sigma_pos; - for(;;) { - c1 = string_prevc(p, &k); - if (!lre_is_case_ignorable(c1)) - break; - } - if (!lre_is_cased(c1)) - return FALSE; - - /* after C: skip case ignorable chars and check there is - no cased letter */ - k = sigma_pos + 1; - for(;;) { - if (k >= p->len) - return TRUE; - c1 = string_getc(p, &k); - if (!lre_is_case_ignorable(c1)) - break; - } - return !lre_is_cased(c1); -} - -static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int to_lower) -{ - JSValue val; - StringBuffer b_s, *b = &b_s; - JSString *p; - int i, c, j, l; - uint32_t res[LRE_CC_RES_LEN_MAX]; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (p->len == 0) - return val; - if (string_buffer_init(ctx, b, p->len)) - goto fail; - for(i = 0; i < p->len;) { - c = string_getc(p, &i); - if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { - res[0] = 0x3c2; /* final sigma */ - l = 1; - } else { - l = lre_case_conv(res, c, to_lower); - } - for(j = 0; j < l; j++) { - if (string_buffer_putc(b, res[j])) - goto fail; - } - } - JS_FreeValue(ctx, val); - return string_buffer_end(b); - fail: - JS_FreeValue(ctx, val); - string_buffer_free(b); - return JS_EXCEPTION; -} - -#ifdef CONFIG_ALL_UNICODE - -/* return (-1, NULL) if exception, otherwise (len, buf) */ -static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1) -{ - JSValue val; - JSString *p; - uint32_t *buf; - int i, j, len; - - val = JS_ToString(ctx, val1); - if (JS_IsException(val)) - return -1; - p = JS_VALUE_GET_STRING(val); - len = p->len; - /* UTF32 buffer length is len minus the number of correct surrogates pairs */ - buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1)); - if (!buf) { - JS_FreeValue(ctx, val); - goto fail; - } - for(i = j = 0; i < len;) - buf[j++] = string_getc(p, &i); - JS_FreeValue(ctx, val); - *pbuf = buf; - return j; - fail: - *pbuf = NULL; - return -1; -} - -static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) -{ - int i; - StringBuffer b_s, *b = &b_s; - if (string_buffer_init(ctx, b, len)) - return JS_EXCEPTION; - for(i = 0; i < len; i++) { - if (string_buffer_putc(b, buf[i])) - goto fail; - } - return string_buffer_end(b); - fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -static 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, out_len; - UnicodeNormalizationEnum n_type; - JSValue val; - uint32_t *out_buf; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - - if (argc == 0 || JS_IsUndefined(argv[0])) { - n_type = UNICODE_NFC; - } else { - form = JS_ToCStringLen(ctx, &form_len, argv[0]); - if (!form) - goto fail1; - p = form; - if (p[0] != 'N' || p[1] != 'F') - goto bad_form; - p += 2; - is_compat = FALSE; - if (*p == 'K') { - is_compat = TRUE; - p++; - } - if (*p == 'C' || *p == 'D') { - n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); - if ((p + 1 - form) != form_len) - goto bad_form; - } else { - bad_form: - JS_FreeCString(ctx, form); - JS_ThrowRangeError(ctx, "bad normalization form"); - fail1: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - JS_FreeCString(ctx, form); - } - - 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; -} - -/* 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, - int argc, JSValueConst *argv) -{ - return js_thisStringValue(ctx, this_val); -} - -#if 0 -static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToStringCheckObject(ctx, argv[0]); -} - -static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToString(ctx, argv[0]); -} - -static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst - this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - int idx; - BOOL is_unicode; - JSString *p; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return str; - if (JS_ToInt32Sat(ctx, &idx, argv[1])) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - is_unicode = JS_ToBool(ctx, argv[2]); - p = JS_VALUE_GET_STRING(str); - if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) { - idx++; - } else { - string_getc(p, &idx); - } - JS_FreeValue(ctx, str); - return JS_NewInt32(ctx, idx); -} -#endif - -/* String Iterator */ - -static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSArrayIteratorData *it; - uint32_t idx, c, start; - JSString *p; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); - if (!it) { - *pdone = FALSE; - return JS_EXCEPTION; - } - if (JS_IsUndefined(it->obj)) - goto done; - p = JS_VALUE_GET_STRING(it->obj); - idx = it->idx; - if (idx >= p->len) { - JS_FreeValue(ctx, it->obj); - it->obj = JS_UNDEFINED; - done: - *pdone = TRUE; - return JS_UNDEFINED; - } - - start = idx; - c = string_getc(p, (int *)&idx); - it->idx = idx; - *pdone = FALSE; - if (c <= 0xffff) { - return js_new_string_char(ctx, c); - } else { - return js_new_string16(ctx, p->u.str16 + start, 2); - } -} - -/* ES6 Annex B 2.3.2 etc. */ -enum { - magic_string_anchor, - magic_string_big, - magic_string_blink, - magic_string_bold, - magic_string_fixed, - magic_string_fontcolor, - magic_string_fontsize, - magic_string_italics, - magic_string_link, - magic_string_small, - magic_string_strike, - magic_string_sub, - magic_string_sup, -}; - -static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue str; - const JSString *p; - StringBuffer b_s, *b = &b_s; - static struct { const char *tag, *attr; } const defs[] = { - { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, - { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL }, - { "a", "href" }, { "small", NULL }, { "strike", NULL }, - { "sub", NULL }, { "sup", NULL }, - }; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return JS_EXCEPTION; - string_buffer_init(ctx, b, 7); - string_buffer_putc8(b, '<'); - string_buffer_puts8(b, defs[magic].tag); - if (defs[magic].attr) { - // r += " " + attr + "=\"" + value + "\""; - JSValue value; - int i; - - string_buffer_putc8(b, ' '); - string_buffer_puts8(b, defs[magic].attr); - string_buffer_puts8(b, "=\""); - value = JS_ToStringCheckObject(ctx, argv[0]); - if (JS_IsException(value)) { - JS_FreeValue(ctx, str); - string_buffer_free(b); - return JS_EXCEPTION; - } - p = JS_VALUE_GET_STRING(value); - for (i = 0; i < p->len; i++) { - int c = string_get(p, i); - if (c == '"') { - string_buffer_puts8(b, """); - } else { - string_buffer_putc16(b, c); - } - } - JS_FreeValue(ctx, value); - string_buffer_putc8(b, '\"'); - } - // return r + ">" + str + "</" + tag + ">"; - string_buffer_putc8(b, '>'); - string_buffer_concat_value_free(b, str); - string_buffer_puts8(b, "</"); - string_buffer_puts8(b, defs[magic].tag); - string_buffer_putc8(b, '>'); - return string_buffer_end(b); -} - -static const JSCFunctionListEntry js_string_funcs[] = { - JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ), - JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ), - JS_CFUNC_DEF("raw", 1, js_string_raw ), - //JS_CFUNC_DEF("__toString", 1, js_string___toString ), - //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ), - //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ), - //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ), - //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ), -}; - -static const JSCFunctionListEntry js_string_proto_funcs[] = { - JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ), - JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ), - JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ), - 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 ), - JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ), - JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ), - JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ), - JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ), - JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ), - JS_CFUNC_DEF("split", 2, js_string_split ), - JS_CFUNC_DEF("substring", 2, js_string_substring ), - JS_CFUNC_DEF("substr", 2, js_string_substr ), - JS_CFUNC_DEF("slice", 2, js_string_slice ), - JS_CFUNC_DEF("repeat", 1, js_string_repeat ), - JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ), - JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ), - JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ), - JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ), - JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ), - JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ), - JS_ALIAS_DEF("trimRight", "trimEnd" ), - JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ), - JS_ALIAS_DEF("trimLeft", "trimStart" ), - JS_CFUNC_DEF("toString", 0, js_string_toString ), - JS_CFUNC_DEF("valueOf", 0, js_string_toString ), - JS_CFUNC_DEF("__quote", 1, js_string___quote ), - JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), - JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), - JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), - JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), - JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ), - JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ), - /* ES6 Annex B 2.3.2 etc. */ - JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ), - JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ), - JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ), - JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ), - JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ), - JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ), - JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ), - JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ), - JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ), - JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ), - JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ), - JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ), - JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ), -}; - -static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ), -}; - -#ifdef CONFIG_ALL_UNICODE -static const JSCFunctionListEntry js_string_proto_normalize[] = { - JS_CFUNC_DEF("normalize", 0, js_string_normalize ), -}; -#endif - -void JS_AddIntrinsicStringNormalize(JSContext *ctx) -{ -#ifdef CONFIG_ALL_UNICODE - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize, - countof(js_string_proto_normalize)); -#endif -} - -/* Math */ - -/* precondition: a and b are not NaN */ -static double js_fmin(double a, double b) -{ - if (a == 0 && b == 0) { - JSFloat64Union a1, b1; - a1.d = a; - b1.d = b; - a1.u64 |= b1.u64; - return a1.d; - } else { - return fmin(a, b); - } -} - -/* precondition: a and b are not NaN */ -static double js_fmax(double a, double b) -{ - if (a == 0 && b == 0) { - JSFloat64Union a1, b1; - a1.d = a; - b1.d = b; - a1.u64 &= b1.u64; - return a1.d; - } else { - return fmax(a, b); - } -} - -static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - BOOL is_max = magic; - double r, a; - int i; - uint32_t tag; - - if (unlikely(argc == 0)) { - return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); - } - - tag = JS_VALUE_GET_TAG(argv[0]); - if (tag == JS_TAG_INT) { - int a1, r1 = JS_VALUE_GET_INT(argv[0]); - for(i = 1; i < argc; i++) { - tag = JS_VALUE_GET_TAG(argv[i]); - if (tag != JS_TAG_INT) { - r = r1; - goto generic_case; - } - a1 = JS_VALUE_GET_INT(argv[i]); - if (is_max) - r1 = max_int(r1, a1); - else - r1 = min_int(r1, a1); - - } - return JS_NewInt32(ctx, r1); - } else { - if (JS_ToFloat64(ctx, &r, argv[0])) - return JS_EXCEPTION; - i = 1; - generic_case: - while (i < argc) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isnan(r)) { - if (isnan(a)) { - r = a; - } else { - if (is_max) - r = js_fmax(r, a); - else - r = js_fmin(r, a); - } - } - i++; - } - return JS_NewFloat64(ctx, r); - } -} - -static double js_math_sign(double a) -{ - if (isnan(a) || a == 0.0) - return a; - if (a < 0) - return -1; - else - return 1; -} - -static double js_math_round(double a) -{ - JSFloat64Union u; - uint64_t frac_mask, one; - unsigned int e, s; - - u.d = a; - e = (u.u64 >> 52) & 0x7ff; - if (e < 1023) { - /* abs(a) < 1 */ - if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) { - /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */ - u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52); - } else { - /* return +/-0.0 */ - u.u64 &= (uint64_t)1 << 63; - } - } else if (e < (1023 + 52)) { - s = u.u64 >> 63; - one = (uint64_t)1 << (52 - (e - 1023)); - frac_mask = one - 1; - u.u64 += (one >> 1) - s; - u.u64 &= ~frac_mask; /* truncate to an integer */ - } - /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ - return u.d; -} - -static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double r, a; - int i; - - r = 0; - if (argc > 0) { - if (JS_ToFloat64(ctx, &r, argv[0])) - return JS_EXCEPTION; - if (argc == 1) { - r = fabs(r); - } else { - /* use the built-in function to minimize precision loss */ - for (i = 1; i < argc; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - r = hypot(r, a); - } - } - } - return JS_NewFloat64(ctx, r); -} - -static double js_math_fround(double a) -{ - return (float)a; -} - -static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint32_t a, b, c; - int32_t d; - - if (JS_ToUint32(ctx, &a, argv[0])) - return JS_EXCEPTION; - if (JS_ToUint32(ctx, &b, argv[1])) - return JS_EXCEPTION; - c = a * b; - memcpy(&d, &c, sizeof(d)); - return JS_NewInt32(ctx, d); -} - -static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint32_t a, r; - - if (JS_ToUint32(ctx, &a, argv[0])) - return JS_EXCEPTION; - if (a == 0) - r = 32; - else - r = clz32(a); - return JS_NewInt32(ctx, r); -} - -/* xorshift* random number generator by Marsaglia */ -static uint64_t xorshift64star(uint64_t *pstate) -{ - uint64_t x; - x = *pstate; - x ^= x >> 12; - x ^= x << 25; - x ^= x >> 27; - *pstate = x; - return x * 0x2545F4914F6CDD1D; -} - -static void js_random_init(JSContext *ctx) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; - /* the state must be non zero */ - if (ctx->random_state == 0) - ctx->random_state = 1; -} - -static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFloat64Union u; - uint64_t v; - - v = xorshift64star(&ctx->random_state); - /* 1.0 <= u.d < 2 */ - u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); - return __JS_NewFloat64(ctx, u.d - 1.0); -} - -static const JSCFunctionListEntry js_math_funcs[] = { - JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), - JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), - JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), - JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), - JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), - JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), - JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), - - JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ), - JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ), - JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ), - JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ), - JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ), - JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ), - JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ), - JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ), - JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ), - JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ), - /* ES6 */ - JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ), - JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), - JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ), - JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ), - JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ), - JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ), - JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ), - JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), - JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), - JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), - JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), - JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), - JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), - JS_CFUNC_DEF("hypot", 2, js_math_hypot ), - JS_CFUNC_DEF("random", 0, js_math_random ), - JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), - JS_CFUNC_DEF("imul", 2, js_math_imul ), - JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ), - JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), - JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), - JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), - JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), - JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), - JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), - JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), - JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), -}; - -static const JSCFunctionListEntry js_math_obj[] = { - JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -/* Date */ - -/* 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) -{ - /* this is a fingerprinting vector, and doesn't work with seccomp - * anyway because the glibc localtime_r tries to openat(2) files - * in /usr/share/zoneinfo. */ - return 0; -#if 0 - time_t ti; - int res; - - time /= 1000; /* convert to seconds */ - if (sizeof(time_t) == 4) { - /* on 32-bit systems, we need to clamp the time value to the - range of `time_t`. This is better than truncating values to - 32 bits and hopefully provides the same result as 64-bit - implementation of localtime_r. - */ - if ((time_t)-1 < 0) { - if (time < INT32_MIN) { - time = INT32_MIN; - } else if (time > INT32_MAX) { - time = INT32_MAX; - } - } else { - if (time < 0) { - time = 0; - } else if (time > UINT32_MAX) { - time = UINT32_MAX; - } - } - } - ti = time; -#if defined(_WIN32) - { - struct tm *tm; - time_t gm_ti, loc_ti; - - tm = gmtime(&ti); - gm_ti = mktime(tm); - - tm = localtime(&ti); - 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; -#endif -} - -#if 0 -static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double dd; - - if (JS_ToFloat64(ctx, &dd, argv[0])) - return JS_EXCEPTION; - if (isnan(dd)) - return __JS_NewFloat64(ctx, dd); - else - return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); -} - -static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor, - JSValueConst def_proto) -{ - JSValue proto; - proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); - if (JS_IsException(proto)) - return proto; - if (!JS_IsObject(proto)) { - JS_FreeValue(ctx, proto); - proto = JS_DupValue(ctx, def_proto); - } - return proto; -} - -/* create a new date object */ -static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, proto; - proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]); - if (JS_IsException(proto)) - return proto; - obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE); - JS_FreeValue(ctx, proto); - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2])); - return obj; -} -#endif - -/* RegExp */ - -static void js_regexp_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSRegExp *re = &p->u.regexp; - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); -} - -/* create a string containing the RegExp bytecode */ -static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, - JSValueConst flags) -{ - const char *str; - int re_flags, mask; - uint8_t *re_bytecode_buf; - size_t i, len; - int re_bytecode_len; - JSValue ret; - char error_msg[64]; - - re_flags = 0; - if (!JS_IsUndefined(flags)) { - str = JS_ToCStringLen(ctx, &len, flags); - if (!str) - return JS_EXCEPTION; - /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ - for (i = 0; i < len; i++) { - switch(str[i]) { - case 'd': - mask = LRE_FLAG_INDICES; - break; - case 'g': - mask = LRE_FLAG_GLOBAL; - break; - case 'i': - mask = LRE_FLAG_IGNORECASE; - break; - case 'm': - mask = LRE_FLAG_MULTILINE; - break; - case 's': - mask = LRE_FLAG_DOTALL; - break; - case 'u': - mask = LRE_FLAG_UNICODE; - break; - case 'y': - mask = LRE_FLAG_STICKY; - break; - default: - goto bad_flags; - } - if ((re_flags & mask) != 0) { - bad_flags: - JS_FreeCString(ctx, str); - return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); - } - re_flags |= mask; - } - JS_FreeCString(ctx, str); - } - - str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UNICODE)); - if (!str) - return JS_EXCEPTION; - re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, - sizeof(error_msg), str, len, re_flags, ctx); - JS_FreeCString(ctx, str); - if (!re_bytecode_buf) { - JS_ThrowSyntaxError(ctx, "%s", error_msg); - return JS_EXCEPTION; - } - - ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len); - js_free(ctx, re_bytecode_buf); - return ret; -} - -/* create a RegExp object from a string containing the RegExp bytecode - and the source pattern */ -static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc) -{ - JSValue obj; - JSObject *p; - JSRegExp *re; - - /* sanity check */ - if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || - JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { - JS_ThrowTypeError(ctx, "string expected"); - fail: - JS_FreeValue(ctx, bc); - JS_FreeValue(ctx, pattern); - return JS_EXCEPTION; - } - - obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); - if (JS_IsException(obj)) - goto fail; - p = JS_VALUE_GET_OBJ(obj); - re = &p->u.regexp; - re->pattern = JS_VALUE_GET_STRING(pattern); - re->bytecode = JS_VALUE_GET_STRING(bc); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), - JS_PROP_WRITABLE); - return obj; -} - -static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error) -{ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_REGEXP) - return &p->u.regexp; - } - if (throw_error) { - JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); - } - return NULL; -} - -/* return < 0 if exception or TRUE/FALSE */ -static int js_is_regexp(JSContext *ctx, JSValueConst obj) -{ - JSValue m; - - if (!JS_IsObject(obj)) - return FALSE; - m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); - if (JS_IsException(m)) - return -1; - if (!JS_IsUndefined(m)) - return JS_ToBoolFree(ctx, m); - return js_get_regexp(ctx, obj, FALSE) != NULL; -} - -static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue pattern, flags, bc, val; - JSValueConst pat, flags1; - JSRegExp *re; - int pat_is_regexp; - - pat = argv[0]; - flags1 = argv[1]; - pat_is_regexp = js_is_regexp(ctx, pat); - if (pat_is_regexp < 0) - return JS_EXCEPTION; - if (JS_IsUndefined(new_target)) { - /* called as a function */ - new_target = JS_GetActiveFunction(ctx); - if (pat_is_regexp && JS_IsUndefined(flags1)) { - JSValue ctor; - BOOL res; - ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - res = js_same_value(ctx, ctor, new_target); - JS_FreeValue(ctx, ctor); - if (res) - return JS_DupValue(ctx, pat); - } - } - re = js_get_regexp(ctx, pat, FALSE); - if (re) { - pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); - if (JS_IsUndefined(flags1)) { - bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - goto no_compilation; - } else { - flags = JS_ToString(ctx, flags1); - if (JS_IsException(flags)) - goto fail; - } - } else { - flags = JS_UNDEFINED; - if (pat_is_regexp) { - pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); - if (JS_IsException(pattern)) - goto fail; - if (JS_IsUndefined(flags1)) { - flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); - if (JS_IsException(flags)) - goto fail; - } else { - flags = JS_DupValue(ctx, flags1); - } - } else { - pattern = JS_DupValue(ctx, pat); - flags = JS_DupValue(ctx, flags1); - } - if (JS_IsUndefined(pattern)) { - pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); - } else { - val = pattern; - pattern = JS_ToString(ctx, val); - JS_FreeValue(ctx, val); - if (JS_IsException(pattern)) - goto fail; - } - } - bc = js_compile_regexp(ctx, pattern, flags); - if (JS_IsException(bc)) - goto fail; - JS_FreeValue(ctx, flags); - no_compilation: - return js_regexp_constructor_internal(ctx, new_target, pattern, bc); - fail: - JS_FreeValue(ctx, pattern); - JS_FreeValue(ctx, flags); - return JS_EXCEPTION; -} - -static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRegExp *re1, *re; - JSValueConst pattern1, flags1; - JSValue bc, pattern; - - re = js_get_regexp(ctx, this_val, TRUE); - if (!re) - return JS_EXCEPTION; - pattern1 = argv[0]; - flags1 = argv[1]; - re1 = js_get_regexp(ctx, pattern1, FALSE); - if (re1) { - if (!JS_IsUndefined(flags1)) - return JS_ThrowTypeError(ctx, "flags must be undefined"); - pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern)); - bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode)); - } else { - bc = JS_UNDEFINED; - if (JS_IsUndefined(pattern1)) - pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); - else - pattern = JS_ToString(ctx, pattern1); - if (JS_IsException(pattern)) - goto fail; - bc = js_compile_regexp(ctx, pattern, flags1); - if (JS_IsException(bc)) - goto fail; - } - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - re->pattern = JS_VALUE_GET_STRING(pattern); - re->bytecode = JS_VALUE_GET_STRING(bc); - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, 0)) < 0) - return JS_EXCEPTION; - return JS_DupValue(ctx, this_val); - fail: - JS_FreeValue(ctx, pattern); - JS_FreeValue(ctx, bc); - return JS_EXCEPTION; -} - -#if 0 -static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - if (!re) - return JS_EXCEPTION; - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); -} - -static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - int flags; - - if (!re) - return JS_EXCEPTION; - flags = lre_get_flags(re->bytecode->u.str8); - return JS_NewInt32(ctx, flags); -} -#endif - -static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) -{ - JSRegExp *re; - JSString *p; - StringBuffer b_s, *b = &b_s; - int i, n, c, c2, bra; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) - goto empty_regex; - - re = js_get_regexp(ctx, this_val, TRUE); - if (!re) - return JS_EXCEPTION; - - p = re->pattern; - - if (p->len == 0) { - empty_regex: - return JS_NewString(ctx, "(?:)"); - } - string_buffer_init2(ctx, b, p->len, p->is_wide_char); - - /* Escape '/' and newline sequences as needed */ - bra = 0; - for (i = 0, n = p->len; i < n;) { - c2 = -1; - switch (c = string_get(p, i++)) { - case '\\': - if (i < n) - c2 = string_get(p, i++); - break; - case ']': - bra = 0; - break; - case '[': - if (!bra) { - if (i < n && string_get(p, i) == ']') - c2 = string_get(p, i++); - bra = 1; - } - break; - case '\n': - c = '\\'; - c2 = 'n'; - break; - case '\r': - c = '\\'; - c2 = 'r'; - break; - case '/': - if (!bra) { - c = '\\'; - c2 = '/'; - } - break; - } - string_buffer_putc16(b, c); - if (c2 >= 0) - string_buffer_putc16(b, c2); - } - return string_buffer_end(b); -} - -static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) -{ - JSRegExp *re; - int flags; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - re = js_get_regexp(ctx, this_val, FALSE); - if (!re) { - if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) - return JS_UNDEFINED; - else - return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); - } - - flags = lre_get_flags(re->bytecode->u.str8); - return JS_NewBool(ctx, flags & mask); -} - -static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) -{ - char str[8], *p = str; - int res; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices")); - if (res < 0) - goto exception; - if (res) - *p++ = 'd'; - res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); - if (res < 0) - goto exception; - if (res) - *p++ = 'g'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); - if (res < 0) - goto exception; - if (res) - *p++ = 'i'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); - if (res < 0) - goto exception; - if (res) - *p++ = 'm'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); - if (res < 0) - goto exception; - if (res) - *p++ = 's'; - res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); - if (res < 0) - goto exception; - if (res) - *p++ = 'u'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); - if (res < 0) - goto exception; - if (res) - *p++ = 'y'; - return JS_NewStringLen(ctx, str, p - str); - -exception: - return JS_EXCEPTION; -} - -static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue pattern, flags; - StringBuffer b_s, *b = &b_s; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - string_buffer_init(ctx, b, 0); - string_buffer_putc8(b, '/'); - pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); - if (string_buffer_concat_value_free(b, pattern)) - goto fail; - string_buffer_putc8(b, '/'); - flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); - if (string_buffer_concat_value_free(b, flags)) - goto fail; - return string_buffer_end(b); - -fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) -{ - if (!opaque) - return 0; - JSContext *ctx = opaque; - return js_check_stack_overflow(ctx->rt, alloca_size); -} - -#if 0 -void *lre_realloc(void *opaque, void *ptr, size_t size) -{ - JSContext *ctx = opaque; - /* No JS exception is raised here */ - return js_realloc_rt(ctx->rt, ptr, size); -} -#endif - -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 t, ret, str_val, obj, val, groups; - JSValue indices, indices_groups; - uint8_t *re_bytecode; - uint8_t **capture, *str_buf; - 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 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) { - last_index = 0; - } - str = JS_VALUE_GET_STRING(str_val); - capture_count = lre_get_capture_count(re_bytecode); - if (capture_count > 0) { - capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); - if (!capture) - goto fail; - } - shift = str->is_wide_char; - str_buf = str->u.str8; - if (last_index > str->len) { - rc = 2; - } else { - rc = lre_exec(capture, re_bytecode, - str_buf, last_index, str->len, - shift, ctx); - } - 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"); - goto fail; - } - } else { - int prop_flags; - if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0) - goto fail; - } - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - goto fail; - prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; - group_name_ptr = lre_get_groupnames(re_bytecode); - if (group_name_ptr) { - groups = JS_NewObjectProto(ctx, JS_NULL); - 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++) { - const char *name = NULL; - uint8_t **match = &capture[2 * i]; - int start = -1; - int end = -1; - JSValue val; - - 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; - 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 (name) { - if (JS_DefinePropertyValueStr(ctx, groups, name, - JS_DupValue(ctx, val), - prop_flags) < 0) { - JS_FreeValue(ctx, val); - goto fail; - } - } - - if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) - goto fail; - } - - t = groups, groups = JS_UNDEFINED; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, - t, prop_flags) < 0) { - goto fail; - } - - 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_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; - } - } - } - ret = obj; - obj = JS_UNDEFINED; -fail: - JS_FreeValue(ctx, indices_groups); - JS_FreeValue(ctx, indices); - JS_FreeValue(ctx, str_val); - JS_FreeValue(ctx, groups); - JS_FreeValue(ctx, obj); - js_free(ctx, capture); - return ret; -} - -/* delete portions of a string that match a given regex */ -static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - JSString *str; - JSValue str_val, val; - uint8_t *re_bytecode; - int ret; - uint8_t **capture, *str_buf; - int capture_count, shift, re_flags; - int next_src_pos, start, end; - int64_t last_index; - StringBuffer b_s, *b = &b_s; - - if (!re) - return JS_EXCEPTION; - - string_buffer_init(ctx, b, 0); - - capture = NULL; - str_val = JS_ToString(ctx, arg); - if (JS_IsException(str_val)) - goto fail; - str = JS_VALUE_GET_STRING(str_val); - re_bytecode = re->bytecode->u.str8; - re_flags = lre_get_flags(re_bytecode); - if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { - last_index = 0; - } else { - val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); - if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) - goto fail; - } - capture_count = lre_get_capture_count(re_bytecode); - if (capture_count > 0) { - capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); - if (!capture) - goto fail; - } - shift = str->is_wide_char; - str_buf = str->u.str8; - next_src_pos = 0; - for (;;) { - if (last_index > str->len) - break; - - ret = lre_exec(capture, re_bytecode, - str_buf, last_index, str->len, shift, ctx); - if (ret != 1) { - if (ret >= 0) { - if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, 0)) < 0) - goto fail; - } - } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); - goto fail; - } - break; - } - start = (capture[0] - str_buf) >> shift; - end = (capture[1] - str_buf) >> shift; - last_index = end; - if (next_src_pos < start) { - if (string_buffer_concat(b, str, next_src_pos, start)) - goto fail; - } - next_src_pos = end; - if (!(re_flags & LRE_FLAG_GLOBAL)) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, end)) < 0) - goto fail; - break; - } - if (end == start) { - if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) { - end++; - } else { - string_getc(str, &end); - } - } - last_index = end; - } - if (string_buffer_concat(b, str, next_src_pos, str->len)) - goto fail; - JS_FreeValue(ctx, str_val); - js_free(ctx, capture); - return string_buffer_end(b); -fail: - JS_FreeValue(ctx, str_val); - js_free(ctx, capture); - string_buffer_free(b); - return JS_EXCEPTION; -} - -static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) -{ - JSValue method, ret; - - method = JS_GetProperty(ctx, r, JS_ATOM_exec); - if (JS_IsException(method)) - return method; - if (JS_IsFunction(ctx, method)) { - ret = JS_CallFree(ctx, method, r, 1, &s); - if (JS_IsException(ret)) - return ret; - if (!JS_IsObject(ret) && !JS_IsNull(ret)) { - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); - } - return ret; - } - JS_FreeValue(ctx, method); - return js_regexp_exec(ctx, r, 1, &s); -} - -#if 0 -static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_RegExpExec(ctx, argv[0], argv[1]); -} -static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_RegExpDelete(ctx, argv[0], argv[1]); -} -#endif - -static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - BOOL ret; - - val = JS_RegExpExec(ctx, this_val, argv[0]); - if (JS_IsException(val)) - return JS_EXCEPTION; - ret = !JS_IsNull(val); - JS_FreeValue(ctx, val); - return JS_NewBool(ctx, ret); -} - -static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.match](str) - JSValueConst rx = this_val; - JSValue A, S, flags, result, matchStr; - int global, n, fullUnicode, isEmpty; - JSString *p; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - A = JS_UNDEFINED; - flags = JS_UNDEFINED; - result = JS_UNDEFINED; - matchStr = JS_UNDEFINED; - S = JS_ToString(ctx, argv[0]); - if (JS_IsException(S)) - goto exception; - - flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); - if (JS_IsException(flags)) - goto exception; - flags = JS_ToStringFree(ctx, flags); - if (JS_IsException(flags)) - 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; - - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) - goto exception; - A = JS_NewArray(ctx); - if (JS_IsException(A)) - goto exception; - n = 0; - for(;;) { - JS_FreeValue(ctx, result); - result = JS_RegExpExec(ctx, rx, S); - if (JS_IsException(result)) - goto exception; - if (JS_IsNull(result)) - break; - matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); - if (JS_IsException(matchStr)) - goto exception; - isEmpty = JS_IsEmptyString(matchStr); - if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) - goto exception; - if (isEmpty) { - int64_t thisIndex, nextIndex; - if (JS_ToLengthFree(ctx, &thisIndex, - JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) - goto exception; - p = JS_VALUE_GET_STRING(S); - nextIndex = string_advance_index(p, thisIndex, fullUnicode); - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) - goto exception; - } - } - if (n == 0) { - JS_FreeValue(ctx, A); - A = JS_NULL; - } - } - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, S); - return A; - -exception: - JS_FreeValue(ctx, A); - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, S); - return JS_EXCEPTION; -} - -typedef struct JSRegExpStringIteratorData { - JSValue iterating_regexp; - JSValue iterated_string; - BOOL global; - BOOL unicode; - BOOL done; -} JSRegExpStringIteratorData; - -static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; - if (it) { - JS_FreeValueRT(rt, it->iterating_regexp); - JS_FreeValueRT(rt, it->iterated_string); - js_free_rt(rt, it); - } -} - -static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; - if (it) { - JS_MarkValue(rt, it->iterating_regexp, mark_func); - JS_MarkValue(rt, it->iterated_string, mark_func); - } -} - -static JSValue js_regexp_string_iterator_next(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSRegExpStringIteratorData *it; - JSValueConst R, S; - JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; - JSString *sp; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); - if (!it) - goto exception; - if (it->done) { - *pdone = TRUE; - return JS_UNDEFINED; - } - R = it->iterating_regexp; - S = it->iterated_string; - match = JS_RegExpExec(ctx, R, S); - if (JS_IsException(match)) - goto exception; - if (JS_IsNull(match)) { - it->done = TRUE; - *pdone = TRUE; - return JS_UNDEFINED; - } else if (it->global) { - matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); - if (JS_IsException(matchStr)) - goto exception; - if (JS_IsEmptyString(matchStr)) { - int64_t thisIndex, nextIndex; - if (JS_ToLengthFree(ctx, &thisIndex, - JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) - goto exception; - sp = JS_VALUE_GET_STRING(S); - nextIndex = string_advance_index(sp, thisIndex, it->unicode); - if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, - JS_NewInt64(ctx, nextIndex)) < 0) - goto exception; - } - JS_FreeValue(ctx, matchStr); - } else { - it->done = TRUE; - } - *pdone = FALSE; - return match; - exception: - JS_FreeValue(ctx, match); - JS_FreeValue(ctx, matchStr); - *pdone = FALSE; - return JS_EXCEPTION; -} - -static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.matchAll](str) - JSValueConst R = this_val; - JSValue S, C, flags, matcher, iter; - JSValueConst args[2]; - JSString *strp; - int64_t lastIndex; - JSRegExpStringIteratorData *it; - - if (!JS_IsObject(R)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - C = JS_UNDEFINED; - flags = JS_UNDEFINED; - matcher = JS_UNDEFINED; - iter = JS_UNDEFINED; - - S = JS_ToString(ctx, argv[0]); - if (JS_IsException(S)) - goto exception; - C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor); - if (JS_IsException(C)) - goto exception; - flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags)); - if (JS_IsException(flags)) - goto exception; - args[0] = R; - args[1] = flags; - matcher = JS_CallConstructor(ctx, C, 2, args); - if (JS_IsException(matcher)) - goto exception; - if (JS_ToLengthFree(ctx, &lastIndex, - JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) - goto exception; - if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, - JS_NewInt64(ctx, lastIndex)) < 0) - goto exception; - - iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); - if (JS_IsException(iter)) - goto exception; - it = js_malloc(ctx, sizeof(*it)); - if (!it) - goto exception; - it->iterating_regexp = matcher; - it->iterated_string = S; - strp = JS_VALUE_GET_STRING(flags); - it->global = string_indexof_char(strp, 'g', 0) >= 0; - it->unicode = string_indexof_char(strp, 'u', 0) >= 0; - it->done = FALSE; - JS_SetOpaque(iter, it); - - JS_FreeValue(ctx, C); - JS_FreeValue(ctx, flags); - return iter; - exception: - JS_FreeValue(ctx, S); - JS_FreeValue(ctx, C); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, matcher); - JS_FreeValue(ctx, iter); - return JS_EXCEPTION; -} - -typedef struct ValueBuffer { - JSContext *ctx; - JSValue *arr; - JSValue def[4]; - int len; - int size; - int error_status; -} ValueBuffer; - -static int value_buffer_init(JSContext *ctx, ValueBuffer *b) -{ - b->ctx = ctx; - b->len = 0; - b->size = 4; - b->error_status = 0; - b->arr = b->def; - return 0; -} - -static void value_buffer_free(ValueBuffer *b) -{ - while (b->len > 0) - JS_FreeValue(b->ctx, b->arr[--b->len]); - if (b->arr != b->def) - js_free(b->ctx, b->arr); - b->arr = b->def; - b->size = 4; -} - -static int value_buffer_append(ValueBuffer *b, JSValue val) -{ - if (b->error_status) - return -1; - - if (b->len >= b->size) { - int new_size = (b->len + (b->len >> 1) + 31) & ~16; - size_t slack; - JSValue *new_arr; - - if (b->arr == b->def) { - new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); - if (new_arr) - memcpy(new_arr, b->def, sizeof b->def); - } else { - new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); - } - if (!new_arr) { - value_buffer_free(b); - JS_FreeValue(b->ctx, val); - b->error_status = -1; - return -1; - } - new_size += slack / sizeof(*new_arr); - b->arr = new_arr; - b->size = new_size; - } - b->arr[b->len++] = val; - return 0; -} - -static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx) -{ - JSValue val; - int res; - - val = JS_GetProperty(ctx, rx, JS_ATOM_constructor); - if (JS_IsException(val)) - return -1; - // rx.constructor === RegExp - res = js_same_value(ctx, val, ctx->regexp_ctor); - JS_FreeValue(ctx, val); - if (res) { - val = JS_GetProperty(ctx, rx, JS_ATOM_exec); - if (JS_IsException(val)) - return -1; - // rx.exec === RE_exec - res = JS_IsCFunction(ctx, val, js_regexp_exec, 0); - JS_FreeValue(ctx, val); - } - return res; -} - -static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.replace](str, rep) - JSValueConst rx = this_val, rep = argv[1]; - JSValueConst args[6]; - JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res; - JSString *p, *sp, *rp; - StringBuffer b_s, *b = &b_s; - ValueBuffer v_b, *results = &v_b; - int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; - uint32_t nCaptures; - int64_t position; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - string_buffer_init(ctx, b, 0); - value_buffer_init(ctx, results); - - rep_val = JS_UNDEFINED; - matched = JS_UNDEFINED; - tab = JS_UNDEFINED; - flags = JS_UNDEFINED; - rep_str = JS_UNDEFINED; - namedCaptures = JS_UNDEFINED; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - goto exception; - - sp = JS_VALUE_GET_STRING(str); - rp = NULL; - functionalReplace = JS_IsFunction(ctx, rep); - if (!functionalReplace) { - rep_val = JS_ToString(ctx, rep); - if (JS_IsException(rep_val)) - goto exception; - rp = JS_VALUE_GET_STRING(rep_val); - } - - flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); - if (JS_IsException(flags)) - goto exception; - flags = JS_ToStringFree(ctx, flags); - if (JS_IsException(flags)) - 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; - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) - goto exception; - } - - if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) { - /* use faster version for simple cases */ - res = JS_RegExpDelete(ctx, rx, str); - goto done; - } - for(;;) { - JSValue result; - result = JS_RegExpExec(ctx, rx, str); - if (JS_IsException(result)) - goto exception; - if (JS_IsNull(result)) - break; - if (value_buffer_append(results, result) < 0) - goto exception; - if (!is_global) - break; - JS_FreeValue(ctx, matched); - matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); - if (JS_IsException(matched)) - goto exception; - if (JS_IsEmptyString(matched)) { - /* always advance of at least one char */ - int64_t thisIndex, nextIndex; - if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) - goto exception; - nextIndex = string_advance_index(sp, thisIndex, fullUnicode); - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) - goto exception; - } - } - nextSourcePosition = 0; - for(j = 0; j < results->len; j++) { - JSValueConst result; - result = results->arr[j]; - if (js_get_length32(ctx, &nCaptures, result) < 0) - goto exception; - JS_FreeValue(ctx, matched); - matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); - if (JS_IsException(matched)) - goto exception; - if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) - goto exception; - if (position > sp->len) - position = sp->len; - else if (position < 0) - position = 0; - /* ignore substition if going backward (can happen - with custom regexp object) */ - JS_FreeValue(ctx, tab); - tab = JS_NewArray(ctx); - if (JS_IsException(tab)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - for(n = 1; n < nCaptures; n++) { - JSValue capN; - capN = JS_GetPropertyInt64(ctx, result, n); - if (JS_IsException(capN)) - goto exception; - if (!JS_IsUndefined(capN)) { - capN = JS_ToStringFree(ctx, capN); - if (JS_IsException(capN)) - goto exception; - } - if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - JS_FreeValue(ctx, namedCaptures); - namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); - if (JS_IsException(namedCaptures)) - goto exception; - if (functionalReplace) { - if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (!JS_IsUndefined(namedCaptures)) { - if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - args[0] = JS_UNDEFINED; - args[1] = tab; - JS_FreeValue(ctx, rep_str); - rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); - } else { - JSValue namedCaptures1; - if (!JS_IsUndefined(namedCaptures)) { - namedCaptures1 = JS_ToObject(ctx, namedCaptures); - if (JS_IsException(namedCaptures1)) - goto exception; - } else { - namedCaptures1 = JS_UNDEFINED; - } - args[0] = matched; - args[1] = str; - args[2] = JS_NewInt32(ctx, position); - args[3] = tab; - args[4] = namedCaptures1; - args[5] = rep_val; - JS_FreeValue(ctx, rep_str); - rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); - JS_FreeValue(ctx, namedCaptures1); - } - if (JS_IsException(rep_str)) - goto exception; - if (position >= nextSourcePosition) { - string_buffer_concat(b, sp, nextSourcePosition, position); - string_buffer_concat_value(b, rep_str); - nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; - } - } - string_buffer_concat(b, sp, nextSourcePosition, sp->len); - res = string_buffer_end(b); - goto done1; - -exception: - res = JS_EXCEPTION; -done: - string_buffer_free(b); -done1: - value_buffer_free(results); - JS_FreeValue(ctx, rep_val); - JS_FreeValue(ctx, matched); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, tab); - JS_FreeValue(ctx, rep_str); - JS_FreeValue(ctx, namedCaptures); - JS_FreeValue(ctx, str); - return res; -} - -static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst rx = this_val; - JSValue str, previousLastIndex, currentLastIndex, result, index; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - result = JS_UNDEFINED; - currentLastIndex = JS_UNDEFINED; - previousLastIndex = JS_UNDEFINED; - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - goto exception; - - previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); - if (JS_IsException(previousLastIndex)) - goto exception; - - if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) { - goto exception; - } - } - result = JS_RegExpExec(ctx, rx, str); - if (JS_IsException(result)) - goto exception; - currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); - if (JS_IsException(currentLastIndex)) - goto exception; - if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { - JS_FreeValue(ctx, previousLastIndex); - } else { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { - previousLastIndex = JS_UNDEFINED; - goto exception; - } - } - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, currentLastIndex); - - if (JS_IsNull(result)) { - return JS_NewInt32(ctx, -1); - } else { - index = JS_GetProperty(ctx, result, JS_ATOM_index); - JS_FreeValue(ctx, result); - return index; - } - -exception: - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, currentLastIndex); - JS_FreeValue(ctx, previousLastIndex); - return JS_EXCEPTION; -} - -static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.split](str, limit) - JSValueConst rx = this_val; - JSValueConst args[2]; - JSValue str, ctor, splitter, A, flags, z, sub; - JSString *strp; - uint32_t lim, size, p, q; - int unicodeMatching; - int64_t lengthA, e, numberOfCaptures, i; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - ctor = JS_UNDEFINED; - splitter = JS_UNDEFINED; - A = JS_UNDEFINED; - flags = JS_UNDEFINED; - z = JS_UNDEFINED; - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - goto exception; - ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); - if (JS_IsException(ctor)) - goto exception; - flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); - if (JS_IsException(flags)) - goto exception; - strp = JS_VALUE_GET_STRING(flags); - unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; - if (string_indexof_char(strp, 'y', 0) < 0) { - flags = JS_ConcatString3(ctx, "", flags, "y"); - if (JS_IsException(flags)) - goto exception; - } - args[0] = rx; - args[1] = flags; - splitter = JS_CallConstructor(ctx, ctor, 2, args); - if (JS_IsException(splitter)) - goto exception; - A = JS_NewArray(ctx); - if (JS_IsException(A)) - goto exception; - lengthA = 0; - if (JS_IsUndefined(argv[1])) { - lim = 0xffffffff; - } else { - if (JS_ToUint32(ctx, &lim, argv[1]) < 0) - goto exception; - if (lim == 0) - goto done; - } - strp = JS_VALUE_GET_STRING(str); - p = q = 0; - size = strp->len; - if (size == 0) { - z = JS_RegExpExec(ctx, splitter, str); - if (JS_IsException(z)) - goto exception; - if (JS_IsNull(z)) - goto add_tail; - goto done; - } - while (q < size) { - if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) - goto exception; - JS_FreeValue(ctx, z); - z = JS_RegExpExec(ctx, splitter, str); - if (JS_IsException(z)) - goto exception; - if (JS_IsNull(z)) { - q = string_advance_index(strp, q, unicodeMatching); - } else { - if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) - goto exception; - if (e > size) - e = size; - if (e == p) { - q = string_advance_index(strp, q, unicodeMatching); - } else { - sub = js_sub_string(ctx, strp, p, q); - if (JS_IsException(sub)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (lengthA == lim) - goto done; - p = e; - if (js_get_length64(ctx, &numberOfCaptures, z)) - goto exception; - for(i = 1; i < numberOfCaptures; i++) { - sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); - if (JS_IsException(sub)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (lengthA == lim) - goto done; - } - q = p; - } - } - } -add_tail: - if (p > size) - p = size; - sub = js_sub_string(ctx, strp, p, size); - if (JS_IsException(sub)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - goto done; -exception: - JS_FreeValue(ctx, A); - A = JS_EXCEPTION; -done: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, ctor); - JS_FreeValue(ctx, splitter); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, z); - return A; -} - -static const JSCFunctionListEntry js_regexp_funcs[] = { - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), - //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ), - //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ), -}; - -static const JSCFunctionListEntry js_regexp_proto_funcs[] = { - JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), - JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), - JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 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("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 ), - JS_CFUNC_DEF("toString", 0, js_regexp_toString ), - JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), - JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), - JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), - JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), - JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), - //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ), - //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ), -}; - -static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) -{ - ctx->compile_regexp = js_compile_regexp; -} - -void JS_AddIntrinsicRegExp(JSContext *ctx) -{ - JSValueConst obj; - - JS_AddIntrinsicRegExpCompiler(ctx); - - ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs, - countof(js_regexp_proto_funcs)); - obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2, - ctx->class_proto[JS_CLASS_REGEXP]); - ctx->regexp_ctor = JS_DupValue(ctx, obj); - JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs)); - - ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = - JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR], - js_regexp_string_iterator_proto_funcs, - countof(js_regexp_string_iterator_proto_funcs)); -} - -/* JSON */ - -static int json_parse_expect(JSParseState *s, int tok) -{ - if (s->token.val != tok) { - /* XXX: dump token correctly in all cases */ - return js_parse_error(s, "expecting '%c'", tok); - } - return json_next_token(s); -} - -static JSValue json_parse_value(JSParseState *s) -{ - JSContext *ctx = s->ctx; - JSValue val = JS_NULL; - int ret; - - switch(s->token.val) { - case '{': - { - JSValue prop_val; - JSAtom prop_name; - - if (json_next_token(s)) - goto fail; - val = JS_NewObject(ctx); - if (JS_IsException(val)) - goto fail; - if (s->token.val != '}') { - for(;;) { - if (s->token.val == TOK_STRING) { - prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); - if (prop_name == JS_ATOM_NULL) - goto fail; - } else if (s->ext_json && s->token.val == TOK_IDENT) { - prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); - } else { - js_parse_error(s, "expecting property name"); - goto fail; - } - if (json_next_token(s)) - goto fail1; - if (json_parse_expect(s, ':')) - goto fail1; - prop_val = json_parse_value(s); - if (JS_IsException(prop_val)) { - fail1: - JS_FreeAtom(ctx, prop_name); - goto fail; - } - ret = JS_DefinePropertyValue(ctx, val, prop_name, - prop_val, JS_PROP_C_W_E); - JS_FreeAtom(ctx, prop_name); - if (ret < 0) - goto fail; - - if (s->token.val != ',') - break; - if (json_next_token(s)) - goto fail; - if (s->ext_json && s->token.val == '}') - break; - } - } - if (json_parse_expect(s, '}')) - goto fail; - } - break; - case '[': - { - JSValue el; - uint32_t idx; - - if (json_next_token(s)) - goto fail; - val = JS_NewArray(ctx); - if (JS_IsException(val)) - goto fail; - if (s->token.val != ']') { - idx = 0; - for(;;) { - el = json_parse_value(s); - if (JS_IsException(el)) - goto fail; - ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); - if (ret < 0) - goto fail; - if (s->token.val != ',') - break; - if (json_next_token(s)) - goto fail; - idx++; - if (s->ext_json && s->token.val == ']') - break; - } - } - if (json_parse_expect(s, ']')) - goto fail; - } - break; - case TOK_STRING: - val = JS_DupValue(ctx, s->token.u.str.str); - if (json_next_token(s)) - goto fail; - break; - case TOK_NUMBER: - val = s->token.u.num.val; - if (json_next_token(s)) - goto fail; - break; - case TOK_IDENT: - if (s->token.u.ident.atom == JS_ATOM_false || - s->token.u.ident.atom == JS_ATOM_true) { - val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true); - } else if (s->token.u.ident.atom == JS_ATOM_null) { - val = JS_NULL; - } else { - goto def_token; - } - if (json_next_token(s)) - goto fail; - break; - default: - def_token: - if (s->token.val == TOK_EOF) { - js_parse_error(s, "Unexpected end of JSON input"); - } else { - js_parse_error(s, "unexpected token: '%.*s'", - (int)(s->buf_ptr - s->token.ptr), s->token.ptr); - } - goto fail; - } - return val; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename, int flags) -{ - JSParseState s1, *s = &s1; - JSValue val = JS_UNDEFINED; - - js_parse_init(ctx, s, buf, buf_len, filename); - s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); - if (json_next_token(s)) - goto fail; - val = json_parse_value(s); - if (JS_IsException(val)) - goto fail; - if (s->token.val != TOK_EOF) { - if (js_parse_error(s, "unexpected data at the end")) - goto fail; - } - return val; - fail: - JS_FreeValue(ctx, val); - free_token(s, &s->token); - return JS_EXCEPTION; -} - -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename) -{ - return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); -} - -static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, - JSAtom name, JSValueConst reviver) -{ - JSValue val, new_el, name_val, res; - JSValueConst args[2]; - int ret, is_array; - uint32_t i, len = 0; - JSAtom prop; - JSPropertyEnum *atoms = NULL; - - if (js_check_stack_overflow(ctx->rt, 0)) { - return JS_ThrowStackOverflow(ctx); - } - - val = JS_GetProperty(ctx, holder, name); - if (JS_IsException(val)) - return val; - if (JS_IsObject(val)) { - is_array = JS_IsArray(ctx, val); - if (is_array < 0) - goto fail; - if (is_array) { - if (js_get_length32(ctx, &len, val)) - goto fail; - } else { - ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); - if (ret < 0) - goto fail; - } - for(i = 0; i < len; i++) { - if (is_array) { - prop = JS_NewAtomUInt32(ctx, i); - if (prop == JS_ATOM_NULL) - goto fail; - } else { - prop = JS_DupAtom(ctx, atoms[i].atom); - } - new_el = internalize_json_property(ctx, val, prop, reviver); - if (JS_IsException(new_el)) { - JS_FreeAtom(ctx, prop); - goto fail; - } - if (JS_IsUndefined(new_el)) { - ret = JS_DeleteProperty(ctx, val, prop, 0); - } else { - ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E); - } - JS_FreeAtom(ctx, prop); - if (ret < 0) - goto fail; - } - } - js_free_prop_enum(ctx, atoms, len); - atoms = NULL; - name_val = JS_AtomToValue(ctx, name); - if (JS_IsException(name_val)) - goto fail; - args[0] = name_val; - args[1] = val; - res = JS_Call(ctx, reviver, holder, 2, args); - JS_FreeValue(ctx, name_val); - JS_FreeValue(ctx, val); - return res; - fail: - js_free_prop_enum(ctx, atoms, len); - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, root; - JSValueConst reviver; - const char *str; - size_t len; - - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; - obj = JS_ParseJSON(ctx, str, len, "<input>"); - JS_FreeCString(ctx, str); - if (JS_IsException(obj)) - return obj; - if (argc > 1 && JS_IsFunction(ctx, argv[1])) { - reviver = argv[1]; - root = JS_NewObject(ctx); - if (JS_IsException(root)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, - JS_PROP_C_W_E) < 0) { - JS_FreeValue(ctx, root); - return JS_EXCEPTION; - } - obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, - reviver); - JS_FreeValue(ctx, root); - } - return obj; -} - -typedef struct JSONStringifyContext { - JSValueConst replacer_func; - JSValue stack; - JSValue property_list; - JSValue gap; - JSValue empty; - StringBuffer *b; -} JSONStringifyContext; - -static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { - JSValue r = JS_ToQuotedString(ctx, val); - JS_FreeValue(ctx, val); - return r; -} - -static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, - JSValueConst holder, JSValue val, JSValueConst key) -{ - JSValue v; - JSValueConst args[2]; - - /* check for object.toJSON method */ - /* ECMA specifies this is done only for Object and BigInt */ - /* we do it for BigFloat and BigDecimal as an extension */ - if (JS_IsObject(val) || JS_IsBigInt(ctx, val) -#ifdef CONFIG_BIGNUM - || JS_IsBigFloat(val) || JS_IsBigDecimal(val) -#endif - ) { - JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); - if (JS_IsException(f)) - goto exception; - if (JS_IsFunction(ctx, f)) { - v = JS_CallFree(ctx, f, val, 1, &key); - JS_FreeValue(ctx, val); - val = v; - if (JS_IsException(val)) - goto exception; - } else { - JS_FreeValue(ctx, f); - } - } - - if (!JS_IsUndefined(jsc->replacer_func)) { - args[0] = key; - args[1] = val; - v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); - JS_FreeValue(ctx, val); - val = v; - if (JS_IsException(val)) - goto exception; - } - - switch (JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_OBJECT: - if (JS_IsFunction(ctx, val)) - break; - case JS_TAG_STRING: - case JS_TAG_INT: - case JS_TAG_FLOAT64: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: -#endif - case JS_TAG_EXCEPTION: - return val; - default: - break; - } - JS_FreeValue(ctx, val); - return JS_UNDEFINED; - -exception: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, - JSValueConst holder, JSValue val, - JSValueConst indent) -{ - JSValue indent1, sep, sep1, tab, v, prop; - JSObject *p; - int64_t i, len; - int cl, ret; - BOOL has_content; - - indent1 = JS_UNDEFINED; - sep = JS_UNDEFINED; - sep1 = JS_UNDEFINED; - tab = JS_UNDEFINED; - prop = JS_UNDEFINED; - - 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; - goto concat_primitive; - } else if (cl == JS_CLASS_NUMBER) { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - goto exception; - goto concat_primitive; - } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT -#ifdef CONFIG_BIGNUM - || cl == JS_CLASS_BIG_FLOAT - || cl == JS_CLASS_BIG_DECIMAL -#endif - ) - { - /* 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)) - goto exception; - if (JS_ToBoolFree(ctx, v)) { - JS_ThrowTypeError(ctx, "circular reference"); - goto exception; - } - indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap)); - if (JS_IsException(indent1)) - goto exception; - if (!JS_IsEmptyString(jsc->gap)) { - sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); - if (JS_IsException(sep)) - goto exception; - sep1 = JS_NewString(ctx, " "); - if (JS_IsException(sep1)) - goto exception; - } else { - sep = JS_DupValue(ctx, jsc->empty); - sep1 = JS_DupValue(ctx, jsc->empty); - } - v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); - if (check_exception_free(ctx, v)) - goto exception; - ret = JS_IsArray(ctx, val); - if (ret < 0) - goto exception; - if (ret) { - if (js_get_length64(ctx, &len, val)) - goto exception; - string_buffer_putc8(jsc->b, '['); - for(i = 0; i < len; i++) { - if (i > 0) - string_buffer_putc8(jsc->b, ','); - string_buffer_concat_value(jsc->b, sep); - v = JS_GetPropertyInt64(ctx, val, i); - if (JS_IsException(v)) - goto exception; - /* XXX: could do this string conversion only when needed */ - prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i)); - if (JS_IsException(prop)) - goto exception; - v = js_json_check(ctx, jsc, val, v, prop); - JS_FreeValue(ctx, prop); - prop = JS_UNDEFINED; - if (JS_IsException(v)) - goto exception; - if (JS_IsUndefined(v)) - v = JS_NULL; - if (js_json_to_str(ctx, jsc, val, v, indent1)) - goto exception; - } - if (len > 0 && !JS_IsEmptyString(jsc->gap)) { - string_buffer_putc8(jsc->b, '\n'); - string_buffer_concat_value(jsc->b, indent); - } - string_buffer_putc8(jsc->b, ']'); - } else { - if (!JS_IsUndefined(jsc->property_list)) - tab = JS_DupValue(ctx, jsc->property_list); - else - tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); - if (JS_IsException(tab)) - goto exception; - if (js_get_length64(ctx, &len, tab)) - goto exception; - string_buffer_putc8(jsc->b, '{'); - has_content = FALSE; - for(i = 0; i < len; i++) { - JS_FreeValue(ctx, prop); - prop = JS_GetPropertyInt64(ctx, tab, i); - if (JS_IsException(prop)) - goto exception; - v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop)); - if (JS_IsException(v)) - goto exception; - v = js_json_check(ctx, jsc, val, v, prop); - if (JS_IsException(v)) - goto exception; - if (!JS_IsUndefined(v)) { - if (has_content) - string_buffer_putc8(jsc->b, ','); - prop = JS_ToQuotedStringFree(ctx, prop); - if (JS_IsException(prop)) { - JS_FreeValue(ctx, v); - goto exception; - } - string_buffer_concat_value(jsc->b, sep); - string_buffer_concat_value(jsc->b, prop); - string_buffer_putc8(jsc->b, ':'); - string_buffer_concat_value(jsc->b, sep1); - if (js_json_to_str(ctx, jsc, val, v, indent1)) - goto exception; - has_content = TRUE; - } - } - if (has_content && !JS_IsEmptyString(jsc->gap)) { - string_buffer_putc8(jsc->b, '\n'); - string_buffer_concat_value(jsc->b, indent); - } - string_buffer_putc8(jsc->b, '}'); - } - if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) - goto exception; - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, tab); - JS_FreeValue(ctx, sep); - JS_FreeValue(ctx, sep1); - JS_FreeValue(ctx, indent1); - JS_FreeValue(ctx, prop); - return 0; - } - concat_primitive: - switch (JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_STRING: - val = JS_ToQuotedStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - goto concat_value; - case JS_TAG_FLOAT64: - if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { - val = JS_NULL; - } - goto concat_value; - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - concat_value: - return string_buffer_concat_value_free(jsc->b, val); - case JS_TAG_BIG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: -#endif - /* 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); - JS_FreeValue(ctx, sep); - JS_FreeValue(ctx, sep1); - JS_FreeValue(ctx, indent1); - JS_FreeValue(ctx, prop); - return -1; -} - -JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, - JSValueConst replacer, JSValueConst space0) -{ - StringBuffer b_s; - JSONStringifyContext jsc_s, *jsc = &jsc_s; - JSValue val, v, space, ret, wrapper; - int res; - int64_t i, j, n; - - jsc->replacer_func = JS_UNDEFINED; - jsc->stack = JS_UNDEFINED; - jsc->property_list = JS_UNDEFINED; - jsc->gap = JS_UNDEFINED; - jsc->b = &b_s; - jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); - ret = JS_UNDEFINED; - wrapper = JS_UNDEFINED; - - string_buffer_init(ctx, jsc->b, 0); - jsc->stack = JS_NewArray(ctx); - if (JS_IsException(jsc->stack)) - goto exception; - if (JS_IsFunction(ctx, replacer)) { - jsc->replacer_func = replacer; - } else { - res = JS_IsArray(ctx, replacer); - if (res < 0) - goto exception; - if (res) { - /* XXX: enumeration is not fully correct */ - jsc->property_list = JS_NewArray(ctx); - if (JS_IsException(jsc->property_list)) - goto exception; - if (js_get_length64(ctx, &n, replacer)) - goto exception; - for (i = j = 0; i < n; i++) { - JSValue present; - v = JS_GetPropertyInt64(ctx, replacer, i); - if (JS_IsException(v)) - goto exception; - if (JS_IsObject(v)) { - JSObject *p = JS_VALUE_GET_OBJ(v); - if (p->class_id == JS_CLASS_STRING || - p->class_id == JS_CLASS_NUMBER) { - v = JS_ToStringFree(ctx, v); - if (JS_IsException(v)) - goto exception; - } else { - JS_FreeValue(ctx, v); - continue; - } - } else if (JS_IsNumber(v)) { - v = JS_ToStringFree(ctx, v); - if (JS_IsException(v)) - goto exception; - } else if (!JS_IsString(v)) { - JS_FreeValue(ctx, v); - continue; - } - present = js_array_includes(ctx, jsc->property_list, - 1, (JSValueConst *)&v); - if (JS_IsException(present)) { - JS_FreeValue(ctx, v); - goto exception; - } - if (!JS_ToBoolFree(ctx, present)) { - JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); - } else { - JS_FreeValue(ctx, v); - } - } - } - } - space = JS_DupValue(ctx, space0); - if (JS_IsObject(space)) { - JSObject *p = JS_VALUE_GET_OBJ(space); - if (p->class_id == JS_CLASS_NUMBER) { - space = JS_ToNumberFree(ctx, space); - } else if (p->class_id == JS_CLASS_STRING) { - space = JS_ToStringFree(ctx, space); - } - if (JS_IsException(space)) { - JS_FreeValue(ctx, space); - goto exception; - } - } - if (JS_IsNumber(space)) { - int n; - if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) - goto exception; - jsc->gap = JS_NewStringLen(ctx, " ", n); - } else if (JS_IsString(space)) { - JSString *p = JS_VALUE_GET_STRING(space); - jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); - } else { - jsc->gap = JS_DupValue(ctx, jsc->empty); - } - JS_FreeValue(ctx, space); - if (JS_IsException(jsc->gap)) - goto exception; - wrapper = JS_NewObject(ctx); - if (JS_IsException(wrapper)) - goto exception; - if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, - JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) - goto exception; - val = JS_DupValue(ctx, obj); - - val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); - if (JS_IsException(val)) - goto exception; - if (JS_IsUndefined(val)) { - ret = JS_UNDEFINED; - goto done1; - } - if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) - goto exception; - - ret = string_buffer_end(jsc->b); - goto done; - -exception: - ret = JS_EXCEPTION; -done1: - string_buffer_free(jsc->b); -done: - JS_FreeValue(ctx, wrapper); - JS_FreeValue(ctx, jsc->empty); - JS_FreeValue(ctx, jsc->gap); - JS_FreeValue(ctx, jsc->property_list); - JS_FreeValue(ctx, jsc->stack); - return ret; -} - -static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // stringify(val, replacer, space) - return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]); -} - -static const JSCFunctionListEntry js_json_funcs[] = { - JS_CFUNC_DEF("parse", 2, js_json_parse ), - JS_CFUNC_DEF("stringify", 3, js_json_stringify ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_json_obj[] = { - JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicJSON(JSContext *ctx) -{ - /* add JSON as autoinit object */ - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); -} - -/* Reflect */ - -static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); -} - -static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst func, array_arg, new_target; - JSValue *tab, ret; - uint32_t len; - - func = argv[0]; - array_arg = argv[1]; - if (argc > 2) { - new_target = argv[2]; - if (!JS_IsConstructor(ctx, new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - } else { - new_target = func; - } - tab = build_arg_list(ctx, &len, array_arg); - if (!tab) - return JS_EXCEPTION; - ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); - free_arg_list(ctx, tab, len); - return ret; -} - -static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj; - JSAtom atom; - int ret; - - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, argv[1]); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_DeleteProperty(ctx, obj, atom, 0); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj, prop, receiver; - JSAtom atom; - JSValue ret; - - obj = argv[0]; - prop = argv[1]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (argc > 2) - receiver = argv[2]; - else - receiver = obj; - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); - JS_FreeAtom(ctx, atom); - return ret; -} - -static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj, prop; - JSAtom atom; - int ret; - - obj = argv[0]; - prop = argv[1]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_HasProperty(ctx, obj, atom); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj, prop, val, receiver; - int ret; - JSAtom atom; - - obj = argv[0]; - prop = argv[1]; - val = argv[2]; - if (argc > 3) - receiver = argv[3]; - else - receiver = obj; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_SetPropertyInternal(ctx, obj, atom, - JS_DupValue(ctx, val), receiver, 0); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - return JS_GetOwnPropertyNames2(ctx, argv[0], - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, - JS_ITERATOR_KIND_KEY); -} - -static const JSCFunctionListEntry js_reflect_funcs[] = { - JS_CFUNC_DEF("apply", 3, js_reflect_apply ), - JS_CFUNC_DEF("construct", 2, js_reflect_construct ), - JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ), - JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ), - JS_CFUNC_DEF("get", 2, js_reflect_get ), - JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ), - JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ), - JS_CFUNC_DEF("has", 2, js_reflect_has ), - JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ), - JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ), - JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), - JS_CFUNC_DEF("set", 3, js_reflect_set ), - JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_reflect_obj[] = { - JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -/* Proxy */ - -static void js_proxy_finalizer(JSRuntime *rt, JSValue val) -{ - JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); - if (s) { - JS_FreeValueRT(rt, s->target); - JS_FreeValueRT(rt, s->handler); - js_free_rt(rt, s); - } -} - -static void js_proxy_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); - if (s) { - JS_MarkValue(rt, s->target, mark_func); - JS_MarkValue(rt, s->handler, mark_func); - } -} - -static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "revoked proxy"); -} - -static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, - JSValueConst obj, JSAtom name) -{ - JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); - JSValue method; - - /* safer to test recursion in all proxy methods */ - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return NULL; - } - - /* 's' should never be NULL */ - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return NULL; - } - method = JS_GetProperty(ctx, s->handler, name); - if (JS_IsException(method)) - return NULL; - if (JS_IsNull(method)) - method = JS_UNDEFINED; - *pmethod = method; - return s; -} - -static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s; - JSValue method, ret, proto1; - int res; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); - if (!s) - return JS_EXCEPTION; - if (JS_IsUndefined(method)) - return JS_GetPrototype(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(ret)) - return ret; - if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && - JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { - goto fail; - } - res = JS_IsExtensible(ctx, s->target); - if (res < 0) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (!res) { - /* check invariant */ - proto1 = JS_GetPrototype(ctx, s->target); - if (JS_IsException(proto1)) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { - JS_FreeValue(ctx, proto1); - fail: - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); - } - JS_FreeValue(ctx, proto1); - } - return ret; -} - -static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, BOOL throw_flag) -{ - JSProxyData *s; - JSValue method, ret, proto1; - JSValueConst args[2]; - BOOL res; - int res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag); - args[0] = s->target; - args[1] = proto_val; - ret = JS_CallFree(ctx, method, s->handler, 2, args); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - if (!res) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "proxy: bad prototype"); - return -1; - } else { - return FALSE; - } - } - res2 = JS_IsExtensible(ctx, s->target); - if (res2 < 0) - return -1; - if (!res2) { - proto1 = JS_GetPrototype(ctx, s->target); - if (JS_IsException(proto1)) - return -1; - if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { - JS_FreeValue(ctx, proto1); - JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); - return -1; - } - JS_FreeValue(ctx, proto1); - } - return TRUE; -} - -static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s; - JSValue method, ret; - BOOL res; - int res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_IsExtensible(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - res2 = JS_IsExtensible(ctx, s->target); - if (res2 < 0) - return res2; - if (res != res2) { - JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); - return -1; - } - return res; -} - -static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s; - JSValue method, ret; - BOOL res; - int res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_PreventExtensions(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - if (res) { - res2 = JS_IsExtensible(ctx, s->target); - if (res2 < 0) - return res2; - if (res2) { - JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); - return -1; - } - } - return res; -} - -static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) -{ - JSProxyData *s; - JSValue method, ret1, atom_val; - int ret, res; - JSObject *p; - JSValueConst args[2]; - BOOL res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_HasProperty(ctx, s->target, atom); - atom_val = JS_AtomToValue(ctx, atom); - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = atom_val; - ret1 = JS_CallFree(ctx, method, s->handler, 2, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret1)) - return -1; - ret = JS_ToBoolFree(ctx, ret1); - if (!ret) { - JSPropertyDescriptor desc; - p = JS_VALUE_GET_OBJ(s->target); - res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); - if (res < 0) - return -1; - if (res) { - res2 = !(desc.flags & JS_PROP_CONFIGURABLE); - js_free_desc(ctx, &desc); - if (res2 || !p->extensible) { - JS_ThrowTypeError(ctx, "proxy: inconsistent has"); - return -1; - } - } - } - return ret; -} - -static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst receiver) -{ - JSProxyData *s; - JSValue method, ret, atom_val; - int res; - JSValueConst args[3]; - JSPropertyDescriptor desc; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); - if (!s) - return JS_EXCEPTION; - /* Note: recursion is possible thru the prototype of s->target */ - if (JS_IsUndefined(method)) - return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); - atom_val = JS_AtomToValue(ctx, atom); - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return JS_EXCEPTION; - } - args[0] = s->target; - args[1] = atom_val; - args[2] = receiver; - ret = JS_CallFree(ctx, method, s->handler, 3, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret)) - return JS_EXCEPTION; - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res < 0) - return JS_EXCEPTION; - if (res) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { - if (!js_same_value(ctx, desc.value, ret)) { - goto fail; - } - } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { - if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { - fail: - js_free_desc(ctx, &desc); - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); - } - } - js_free_desc(ctx, &desc); - } - return ret; -} - -static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst value, JSValueConst receiver, int flags) -{ - JSProxyData *s; - JSValue method, ret1, atom_val; - int ret, res; - JSValueConst args[4]; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_SetPropertyInternal(ctx, s->target, atom, - JS_DupValue(ctx, value), receiver, - flags); - } - atom_val = JS_AtomToValue(ctx, atom); - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = atom_val; - args[2] = value; - args[3] = receiver; - ret1 = JS_CallFree(ctx, method, s->handler, 4, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret1)) - return -1; - ret = JS_ToBoolFree(ctx, ret1); - if (ret) { - JSPropertyDescriptor desc; - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res < 0) - return -1; - if (res) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { - if (!js_same_value(ctx, desc.value, value)) { - goto fail; - } - } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { - fail: - js_free_desc(ctx, &desc); - JS_ThrowTypeError(ctx, "proxy: inconsistent set"); - return -1; - } - js_free_desc(ctx, &desc); - } - } else { - if ((flags & JS_PROP_THROW) || - ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeError(ctx, "proxy: cannot set property"); - return -1; - } - } - return ret; -} - -static JSValue js_create_desc(JSContext *ctx, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags) -{ - JSValue ret; - ret = JS_NewObject(ctx); - if (JS_IsException(ret)) - return ret; - if (flags & JS_PROP_HAS_GET) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_SET) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_VALUE) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_WRITABLE) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, flags & JS_PROP_WRITABLE), - 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), - 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), - JS_PROP_C_W_E); - } - return ret; -} - -static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, - JSValueConst obj, JSAtom prop) -{ - JSProxyData *s; - JSValue method, trap_result_obj, prop_val; - int res, target_desc_ret, ret; - JSObject *p; - JSValueConst args[2]; - JSPropertyDescriptor result_desc, target_desc; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); - if (!s) - return -1; - p = JS_VALUE_GET_OBJ(s->target); - if (JS_IsUndefined(method)) { - return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); - } - prop_val = JS_AtomToValue(ctx, prop); - if (JS_IsException(prop_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = prop_val; - trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); - JS_FreeValue(ctx, prop_val); - if (JS_IsException(trap_result_obj)) - return -1; - if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { - JS_FreeValue(ctx, trap_result_obj); - goto fail; - } - target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); - if (target_desc_ret < 0) { - JS_FreeValue(ctx, trap_result_obj); - return -1; - } - if (target_desc_ret) - js_free_desc(ctx, &target_desc); - if (JS_IsUndefined(trap_result_obj)) { - if (target_desc_ret) { - if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) - goto fail; - } - ret = FALSE; - } else { - int flags1, extensible_target; - extensible_target = JS_IsExtensible(ctx, s->target); - if (extensible_target < 0) { - JS_FreeValue(ctx, trap_result_obj); - return -1; - } - res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); - JS_FreeValue(ctx, trap_result_obj); - if (res < 0) - return -1; - - if (target_desc_ret) { - /* convert result_desc.flags to defineProperty flags */ - flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; - if (result_desc.flags & JS_PROP_GETSET) - flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; - else - flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; - /* XXX: not complete check: need to compare value & - getter/setter as in defineproperty */ - if (!check_define_prop_flags(target_desc.flags, flags1)) - goto fail1; - } else { - if (!extensible_target) - goto fail1; - } - if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { - if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) - goto fail1; - if ((result_desc.flags & - (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && - target_desc_ret && - (target_desc.flags & JS_PROP_WRITABLE) != 0) { - /* proxy-missing-checks */ - fail1: - js_free_desc(ctx, &result_desc); - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); - return -1; - } - } - ret = TRUE; - if (pdesc) { - *pdesc = result_desc; - } else { - js_free_desc(ctx, &result_desc); - } - } - return ret; -} - -static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags) -{ - JSProxyData *s; - JSValue method, ret1, prop_val, desc_val; - int res, ret; - JSObject *p; - JSValueConst args[3]; - JSPropertyDescriptor desc; - BOOL setting_not_configurable; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); - } - prop_val = JS_AtomToValue(ctx, prop); - if (JS_IsException(prop_val)) { - JS_FreeValue(ctx, method); - return -1; - } - desc_val = js_create_desc(ctx, val, getter, setter, flags); - if (JS_IsException(desc_val)) { - JS_FreeValue(ctx, prop_val); - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = prop_val; - args[2] = desc_val; - ret1 = JS_CallFree(ctx, method, s->handler, 3, args); - JS_FreeValue(ctx, prop_val); - JS_FreeValue(ctx, desc_val); - if (JS_IsException(ret1)) - return -1; - ret = JS_ToBoolFree(ctx, ret1); - if (!ret) { - if (flags & JS_PROP_THROW) { - JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); - return -1; - } else { - return 0; - } - } - p = JS_VALUE_GET_OBJ(s->target); - res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (res < 0) - return -1; - setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | - JS_PROP_CONFIGURABLE)) == - JS_PROP_HAS_CONFIGURABLE); - if (!res) { - if (!p->extensible || setting_not_configurable) - goto fail; - } else { - if (!check_define_prop_flags(desc.flags, flags) || - ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { - goto fail1; - } - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == - JS_PROP_GETSET) { - if ((flags & JS_PROP_HAS_GET) && - !js_same_value(ctx, getter, desc.getter)) { - goto fail1; - } - if ((flags & JS_PROP_HAS_SET) && - !js_same_value(ctx, setter, desc.setter)) { - goto fail1; - } - } - } else if (flags & JS_PROP_HAS_VALUE) { - if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == - JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { - /* missing-proxy-check feature */ - goto fail1; - } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && - !js_same_value(ctx, val, desc.value)) { - goto fail1; - } - } - if (flags & JS_PROP_HAS_WRITABLE) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | - JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { - /* proxy-missing-checks */ - fail1: - js_free_desc(ctx, &desc); - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); - return -1; - } - } - js_free_desc(ctx, &desc); - } - return 1; -} - -static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, - JSAtom atom) -{ - JSProxyData *s; - JSValue method, ret, atom_val; - int res, res2, is_extensible; - JSValueConst args[2]; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_DeleteProperty(ctx, s->target, atom, 0); - } - atom_val = JS_AtomToValue(ctx, atom);; - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = atom_val; - ret = JS_CallFree(ctx, method, s->handler, 2, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - if (res) { - JSPropertyDescriptor desc; - res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res2 < 0) - return -1; - if (res2) { - if (!(desc.flags & JS_PROP_CONFIGURABLE)) - goto fail; - is_extensible = JS_IsExtensible(ctx, s->target); - if (is_extensible < 0) - goto fail1; - if (!is_extensible) { - /* proxy-missing-checks */ - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); - fail1: - js_free_desc(ctx, &desc); - return -1; - } - js_free_desc(ctx, &desc); - } - } - return res; -} - -/* return the index of the property or -1 if not found */ -static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) -{ - int i; - for(i = 0; i < n; i++) { - if (tab[i].atom == atom) - return i; - } - return -1; -} - -static int js_proxy_get_own_property_names(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSValueConst obj) -{ - JSProxyData *s; - JSValue method, prop_array, val; - uint32_t len, i, len2; - JSPropertyEnum *tab, *tab2; - JSAtom atom; - JSPropertyDescriptor desc; - int res, is_extensible, idx; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, - JS_VALUE_GET_OBJ(s->target), - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); - } - prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(prop_array)) - return -1; - tab = NULL; - len = 0; - tab2 = NULL; - len2 = 0; - if (js_get_length32(ctx, &len, prop_array)) - goto fail; - if (len > 0) { - tab = js_mallocz(ctx, sizeof(tab[0]) * len); - if (!tab) - goto fail; - } - for(i = 0; i < len; i++) { - val = JS_GetPropertyUint32(ctx, prop_array, i); - if (JS_IsException(val)) - goto fail; - if (!JS_IsString(val) && !JS_IsSymbol(val)) { - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); - goto fail; - } - atom = JS_ValueToAtom(ctx, val); - JS_FreeValue(ctx, val); - if (atom == JS_ATOM_NULL) - goto fail; - tab[i].atom = atom; - tab[i].is_enumerable = FALSE; /* XXX: redundant? */ - } - - /* check duplicate properties (XXX: inefficient, could store the - * properties an a temporary object to use the hash) */ - for(i = 1; i < len; i++) { - if (find_prop_key(tab, i, tab[i].atom) >= 0) { - JS_ThrowTypeError(ctx, "proxy: duplicate property"); - goto fail; - } - } - - is_extensible = JS_IsExtensible(ctx, s->target); - if (is_extensible < 0) - goto fail; - - /* check if there are non configurable properties */ - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - goto fail; - } - if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) - goto fail; - for(i = 0; i < len2; i++) { - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - goto fail; - } - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), - tab2[i].atom); - if (res < 0) - goto fail; - if (res) { /* safety, property should be found */ - js_free_desc(ctx, &desc); - if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { - idx = find_prop_key(tab, len, tab2[i].atom); - if (idx < 0) { - JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); - goto fail; - } - /* mark the property as found */ - if (!is_extensible) - tab[idx].is_enumerable = TRUE; - } - } - } - if (!is_extensible) { - /* check that all property in 'tab' were checked */ - for(i = 0; i < len; i++) { - if (!tab[i].is_enumerable) { - JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); - goto fail; - } - } - } - - js_free_prop_enum(ctx, tab2, len2); - JS_FreeValue(ctx, prop_array); - *ptab = tab; - *plen = len; - return 0; - fail: - js_free_prop_enum(ctx, tab2, len2); - js_free_prop_enum(ctx, tab, len); - JS_FreeValue(ctx, prop_array); - return -1; -} - -static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSProxyData *s; - JSValue method, arg_array, ret; - JSValueConst args[3]; - - s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); - if (!s) - return JS_EXCEPTION; - if (!JS_IsConstructor(ctx, s->target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (JS_IsUndefined(method)) - return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); - arg_array = js_create_array(ctx, argc, argv); - if (JS_IsException(arg_array)) { - ret = JS_EXCEPTION; - goto fail; - } - args[0] = s->target; - args[1] = arg_array; - args[2] = new_target; - ret = JS_Call(ctx, method, s->handler, 3, args); - if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { - JS_FreeValue(ctx, ret); - ret = JS_ThrowTypeErrorNotAnObject(ctx); - } - fail: - JS_FreeValue(ctx, method); - JS_FreeValue(ctx, arg_array); - return ret; -} - -static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSProxyData *s; - JSValue method, arg_array, ret; - JSValueConst args[3]; - - if (flags & JS_CALL_FLAG_CONSTRUCTOR) - return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); - - s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply); - if (!s) - return JS_EXCEPTION; - if (!s->is_func) { - JS_FreeValue(ctx, method); - return JS_ThrowTypeError(ctx, "not a function"); - } - if (JS_IsUndefined(method)) - return JS_Call(ctx, s->target, this_obj, argc, argv); - arg_array = js_create_array(ctx, argc, argv); - if (JS_IsException(arg_array)) { - ret = JS_EXCEPTION; - goto fail; - } - args[0] = s->target; - args[1] = this_obj; - args[2] = arg_array; - ret = JS_Call(ctx, method, s->handler, 3, args); - fail: - JS_FreeValue(ctx, method); - JS_FreeValue(ctx, arg_array); - return ret; -} - -/* `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 0; -} - -static const JSClassExoticMethods js_proxy_exotic_methods = { - .get_own_property = js_proxy_get_own_property, - .define_own_property = js_proxy_define_own_property, - .delete_property = js_proxy_delete_property, - .get_own_property_names = js_proxy_get_own_property_names, - .has_property = js_proxy_has, - .get_property = js_proxy_get, - .set_property = js_proxy_set, -}; - -static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst target, handler; - JSValue obj; - JSProxyData *s; - - target = argv[0]; - handler = argv[1]; - if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || - JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY); - if (JS_IsException(obj)) - return obj; - s = js_malloc(ctx, sizeof(JSProxyData)); - if (!s) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - s->target = JS_DupValue(ctx, target); - s->handler = JS_DupValue(ctx, handler); - s->is_func = JS_IsFunction(ctx, target); - s->is_revoked = FALSE; - JS_SetOpaque(obj, s); - JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); - return obj; -} - -static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, - JSValue *func_data) -{ - JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); - if (s) { - /* We do not free the handler and target in case they are - referenced as constants in the C call stack */ - s->is_revoked = TRUE; - JS_FreeValue(ctx, func_data[0]); - func_data[0] = JS_NULL; - } - return JS_UNDEFINED; -} - -static JSValue js_proxy_revoke_constructor(JSContext *ctx, - JSValueConst proxy_obj) -{ - return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); -} - -static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; - - proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); - if (JS_IsException(proxy_obj)) - goto fail; - revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); - if (JS_IsException(revoke_obj)) - goto fail; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - goto fail; - // XXX: exceptions? - JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); - return obj; - fail: - JS_FreeValue(ctx, proxy_obj); - JS_FreeValue(ctx, revoke_obj); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_proxy_funcs[] = { - JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), -}; - -static const JSClassShortDef js_proxy_class_def[] = { - { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ -}; - -void JS_AddIntrinsicProxy(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValue obj1; - - if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { - init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, - countof(js_proxy_class_def)); - rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; - rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; - } - - obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2, - JS_CFUNC_constructor, 0); - JS_SetConstructorBit(ctx, obj1, TRUE); - JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, - countof(js_proxy_funcs)); - JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", - obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); -} - -/* Symbol */ - -static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue str; - JSString *p; - - if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (argc == 0 || JS_IsUndefined(argv[0])) { - p = NULL; - } else { - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return JS_EXCEPTION; - p = JS_VALUE_GET_STRING(str); - } - return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL); -} - -static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_SYMBOL) { - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a symbol"); -} - -static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - val = js_thisSymbolValue(ctx, this_val); - if (JS_IsException(val)) - return val; - /* XXX: use JS_ToStringInternal() with a flags */ - ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); - JS_FreeValue(ctx, val); - return ret; -} - -static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisSymbolValue(ctx, this_val); -} - -static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) -{ - JSValue val, ret; - JSAtomStruct *p; - - val = js_thisSymbolValue(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_PTR(val); - if (p->len == 0 && p->is_wide_char != 0) { - ret = JS_UNDEFINED; - } else { - ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); - } - JS_FreeValue(ctx, val); - return ret; -} - -static const JSCFunctionListEntry js_symbol_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_symbol_toString ), - JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ), - // XXX: should have writable: false - JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ), - JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), -}; - -static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return JS_EXCEPTION; - return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); -} - -static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSAtomStruct *p; - - if (!JS_IsSymbol(argv[0])) - return JS_ThrowTypeError(ctx, "not a symbol"); - p = JS_VALUE_GET_PTR(argv[0]); - if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) - return JS_UNDEFINED; - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); -} - -static const JSCFunctionListEntry js_symbol_funcs[] = { - JS_CFUNC_DEF("for", 1, js_symbol_for ), - JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ), -}; - -/* Set/Map/WeakSet/WeakMap */ - -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - BOOL empty; /* TRUE if the record is deleted */ - struct JSMapState *map; - struct JSMapRecord *next_weak_ref; - struct list_head link; - struct list_head hash_link; - JSValue key; - JSValue value; -} JSMapRecord; - -typedef struct JSMapState { - BOOL is_weak; /* TRUE if WeakSet/WeakMap */ - struct list_head records; /* list of JSMapRecord.link */ - uint32_t record_count; - struct list_head *hash_table; - uint32_t hash_size; /* must be a power of two */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ -} JSMapState; - -#define MAGIC_SET (1 << 0) -#define MAGIC_WEAK (1 << 1) - -static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s; - JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; - JSValueConst arr; - BOOL is_set, is_weak; - - is_set = magic & MAGIC_SET; - is_weak = ((magic & MAGIC_WEAK) != 0); - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); - if (JS_IsException(obj)) - return JS_EXCEPTION; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - goto fail; - init_list_head(&s->records); - s->is_weak = is_weak; - JS_SetOpaque(obj, s); - s->hash_size = 1; - s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); - if (!s->hash_table) - goto fail; - init_list_head(&s->hash_table[0]); - s->record_count_threshold = 4; - - arr = JS_UNDEFINED; - if (argc > 0) - arr = argv[0]; - if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { - JSValue item, ret; - BOOL done; - - adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); - if (JS_IsException(adder)) - goto fail; - if (!JS_IsFunction(ctx, adder)) { - JS_ThrowTypeError(ctx, "set/add is not a function"); - goto fail; - } - - iter = JS_GetIterator(ctx, arr, FALSE); - if (JS_IsException(iter)) - goto fail; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail; - - for(;;) { - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail; - if (done) { - JS_FreeValue(ctx, item); - break; - } - if (is_set) { - ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, item); - goto fail; - } - } else { - JSValue key, value; - JSValueConst args[2]; - key = JS_UNDEFINED; - value = JS_UNDEFINED; - if (!JS_IsObject(item)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail1; - } - key = JS_GetPropertyUint32(ctx, item, 0); - if (JS_IsException(key)) - goto fail1; - value = JS_GetPropertyUint32(ctx, item, 1); - if (JS_IsException(value)) - goto fail1; - args[0] = key; - args[1] = value; - ret = JS_Call(ctx, adder, obj, 2, args); - if (JS_IsException(ret)) { - fail1: - JS_FreeValue(ctx, item); - JS_FreeValue(ctx, key); - JS_FreeValue(ctx, value); - goto fail; - } - JS_FreeValue(ctx, key); - JS_FreeValue(ctx, value); - } - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, item); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, adder); - } - return obj; - fail: - if (JS_IsObject(iter)) { - /* close the iterator object, preserving pending exception */ - JS_IteratorClose(ctx, iter, TRUE); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, adder); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -/* XXX: could normalize strings to speed up comparison */ -static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key) -{ - uint32_t tag = JS_VALUE_GET_TAG(key); - /* convert -0.0 to +0.0 */ - if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { - key = JS_NewInt32(ctx, 0); - } - return key; -} - -/* XXX: better hash ? */ -static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) -{ - uint32_t tag = JS_VALUE_GET_NORM_TAG(key); - uint32_t h; - double d; - JSFloat64Union u; - - switch(tag) { - case JS_TAG_BOOL: - h = JS_VALUE_GET_INT(key); - break; - case JS_TAG_STRING: - h = hash_string(JS_VALUE_GET_STRING(key), 0); - break; - case JS_TAG_OBJECT: - case JS_TAG_SYMBOL: - h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; - break; - case JS_TAG_INT: - d = JS_VALUE_GET_INT(key); - goto hash_float64; - case JS_TAG_FLOAT64: - d = JS_VALUE_GET_FLOAT64(key); - /* normalize the NaN */ - if (isnan(d)) - d = JS_FLOAT64_NAN; - hash_float64: - u.d = d; - h = (u.u32[0] ^ u.u32[1]) * 3163; - return h ^= JS_TAG_FLOAT64; - default: - h = 0; /* XXX: bignum support */ - break; - } - h ^= tag; - return h; -} - -static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, - JSValueConst key) -{ - struct list_head *el; - JSMapRecord *mr; - uint32_t h; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_for_each(el, &s->hash_table[h]) { - mr = list_entry(el, JSMapRecord, hash_link); - if (js_same_value_zero(ctx, mr->key, key)) - return mr; - } - return NULL; -} - -static void map_hash_resize(JSContext *ctx, JSMapState *s) -{ - uint32_t new_hash_size, i, h; - size_t slack; - struct list_head *new_hash_table, *el; - JSMapRecord *mr; - - /* XXX: no reporting of memory allocation failure */ - if (s->hash_size == 1) - new_hash_size = 4; - else - new_hash_size = s->hash_size * 2; - new_hash_table = js_realloc2(ctx, s->hash_table, - sizeof(new_hash_table[0]) * new_hash_size, &slack); - if (!new_hash_table) - return; - new_hash_size += slack / sizeof(*new_hash_table); - - for(i = 0; i < new_hash_size; i++) - init_list_head(&new_hash_table[i]); - - list_for_each(el, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); - list_add_tail(&mr->hash_link, &new_hash_table[h]); - } - } - s->hash_table = new_hash_table; - s->hash_size = new_hash_size; - s->record_count_threshold = new_hash_size * 2; -} - -static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, - JSValueConst key) -{ - uint32_t h; - JSMapRecord *mr; - - mr = js_malloc(ctx, sizeof(*mr)); - if (!mr) - return NULL; - mr->ref_count = 1; - mr->map = s; - mr->empty = FALSE; - if (s->is_weak) { - JSObject *p = JS_VALUE_GET_OBJ(key); - /* Add the weak reference */ - mr->next_weak_ref = p->first_weak_ref; - p->first_weak_ref = mr; - } else { - JS_DupValue(ctx, key); - } - mr->key = (JSValue)key; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_add_tail(&mr->hash_link, &s->hash_table[h]); - list_add_tail(&mr->link, &s->records); - s->record_count++; - if (s->record_count >= s->record_count_threshold) { - map_hash_resize(ctx, s); - } - return mr; -} - -/* Remove the weak reference from the object weak - reference list. we don't use a doubly linked list to - save space, assuming a given object has few weak - references to it */ -static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) -{ - JSMapRecord **pmr, *mr1; - JSObject *p; - - p = JS_VALUE_GET_OBJ(mr->key); - pmr = &p->first_weak_ref; - for(;;) { - mr1 = *pmr; - assert(mr1 != NULL); - if (mr1 == mr) - break; - pmr = &mr1->next_weak_ref; - } - *pmr = mr1->next_weak_ref; -} - -static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) -{ - if (mr->empty) - return; - list_del(&mr->hash_link); - if (s->is_weak) { - delete_weak_ref(rt, mr); - } else { - JS_FreeValueRT(rt, mr->key); - } - JS_FreeValueRT(rt, mr->value); - if (--mr->ref_count == 0) { - list_del(&mr->link); - js_free_rt(rt, mr); - } else { - /* keep a zombie record for iterators */ - mr->empty = TRUE; - mr->key = JS_UNDEFINED; - mr->value = JS_UNDEFINED; - } - s->record_count--; -} - -static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) -{ - if (--mr->ref_count == 0) { - /* the record can be safely removed */ - assert(mr->empty); - list_del(&mr->link); - js_free_rt(rt, mr); - } -} - -static void reset_weak_ref(JSRuntime *rt, JSObject *p) -{ - JSMapRecord *mr, *mr_next; - JSMapState *s; - - /* first pass to remove the records from the WeakMap/WeakSet - lists */ - for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { - s = mr->map; - assert(s->is_weak); - assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ - list_del(&mr->hash_link); - list_del(&mr->link); - } - - /* second pass to free the values to avoid modifying the weak - reference list while traversing it. */ - for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) { - mr_next = mr->next_weak_ref; - JS_FreeValueRT(rt, mr->value); - js_free_rt(rt, mr); - } - - p->first_weak_ref = NULL; /* fail safe */ -} - -static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key, value; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - if (s->is_weak && !JS_IsObject(key)) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (magic & MAGIC_SET) - value = JS_UNDEFINED; - else - value = argv[1]; - mr = map_find_record(ctx, s, key); - if (mr) { - JS_FreeValue(ctx, mr->value); - } else { - mr = map_add_record(ctx, s, key); - if (!mr) - return JS_EXCEPTION; - } - mr->value = JS_DupValue(ctx, value); - return JS_DupValue(ctx, this_val); -} - -static JSValue js_map_get(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - if (!mr) - return JS_UNDEFINED; - else - return JS_DupValue(ctx, mr->value); -} - -static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - return JS_NewBool(ctx, mr != NULL); -} - -static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - if (!mr) - return JS_FALSE; - map_delete_record(ctx->rt, s, mr); - return JS_TRUE; -} - -static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - struct list_head *el, *el1; - JSMapRecord *mr; - - if (!s) - return JS_EXCEPTION; - list_for_each_safe(el, el1, &s->records) { - mr = list_entry(el, JSMapRecord, link); - map_delete_record(ctx->rt, s, mr); - } - return JS_UNDEFINED; -} - -static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - if (!s) - return JS_EXCEPTION; - return JS_NewUint32(ctx, s->record_count); -} - -static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSValueConst func, this_arg; - JSValue ret, args[3]; - struct list_head *el; - JSMapRecord *mr; - - if (!s) - return JS_EXCEPTION; - func = argv[0]; - if (argc > 1) - this_arg = argv[1]; - else - this_arg = JS_UNDEFINED; - if (check_function(ctx, func)) - return JS_EXCEPTION; - /* Note: the list can be modified while traversing it, but the - current element is locked */ - el = s->records.next; - while (el != &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - mr->ref_count++; - /* must duplicate in case the record is deleted */ - args[1] = JS_DupValue(ctx, mr->key); - if (magic) - args[0] = args[1]; - else - args[0] = JS_DupValue(ctx, mr->value); - args[2] = (JSValue)this_val; - ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); - JS_FreeValue(ctx, args[0]); - if (!magic) - JS_FreeValue(ctx, args[1]); - el = el->next; - map_decref_record(ctx->rt, mr); - if (JS_IsException(ret)) - return ret; - JS_FreeValue(ctx, ret); - } else { - el = el->next; - } - } - return JS_UNDEFINED; -} - -static 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; - JSMapState *s; - struct list_head *el, *el1; - JSMapRecord *mr; - - p = JS_VALUE_GET_OBJ(val); - s = p->u.map_state; - if (s) { - /* if the object is deleted we are sure that no iterator is - using it */ - list_for_each_safe(el, el1, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - if (s->is_weak) - delete_weak_ref(rt, mr); - else - JS_FreeValueRT(rt, mr->key); - JS_FreeValueRT(rt, mr->value); - } - js_free_rt(rt, mr); - } - js_free_rt(rt, s->hash_table); - js_free_rt(rt, s); - } -} - -static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSMapState *s; - struct list_head *el; - JSMapRecord *mr; - - s = p->u.map_state; - if (s) { - list_for_each(el, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!s->is_weak) - JS_MarkValue(rt, mr->key, mark_func); - JS_MarkValue(rt, mr->value, mark_func); - } - } -} - -/* Map Iterator */ - -typedef struct JSMapIteratorData { - JSValue obj; - JSIteratorKindEnum kind; - JSMapRecord *cur_record; -} JSMapIteratorData; - -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p; - JSMapIteratorData *it; - - p = JS_VALUE_GET_OBJ(val); - it = p->u.map_iterator_data; - if (it) { - /* During the GC sweep phase the Map finalizer may be - called before the Map iterator finalizer */ - if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { - map_decref_record(rt, it->cur_record); - } - JS_FreeValueRT(rt, it->obj); - js_free_rt(rt, it); - } -} - -static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSMapIteratorData *it; - it = p->u.map_iterator_data; - if (it) { - /* the record is already marked by the object */ - JS_MarkValue(rt, it->obj, mark_func); - } -} - -static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSIteratorKindEnum kind; - JSMapState *s; - JSMapIteratorData *it; - JSValue enum_obj; - - kind = magic >> 2; - magic &= 3; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - if (!s) - return JS_EXCEPTION; - enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); - if (JS_IsException(enum_obj)) - goto fail; - it = js_malloc(ctx, sizeof(*it)); - if (!it) { - JS_FreeValue(ctx, enum_obj); - goto fail; - } - it->obj = JS_DupValue(ctx, this_val); - it->kind = kind; - it->cur_record = NULL; - JS_SetOpaque(enum_obj, it); - return enum_obj; - fail: - return JS_EXCEPTION; -} - -static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSMapIteratorData *it; - JSMapState *s; - JSMapRecord *mr; - struct list_head *el; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); - if (!it) { - *pdone = FALSE; - return JS_EXCEPTION; - } - if (JS_IsUndefined(it->obj)) - goto done; - s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); - assert(s != NULL); - if (!it->cur_record) { - el = s->records.next; - } else { - mr = it->cur_record; - el = mr->link.next; - map_decref_record(ctx->rt, mr); /* the record can be freed here */ - } - for(;;) { - if (el == &s->records) { - /* no more record */ - it->cur_record = NULL; - JS_FreeValue(ctx, it->obj); - it->obj = JS_UNDEFINED; - done: - /* end of enumeration */ - *pdone = TRUE; - return JS_UNDEFINED; - } - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) - break; - /* get the next record */ - el = mr->link.next; - } - - /* lock the record so that it won't be freed */ - mr->ref_count++; - it->cur_record = mr; - *pdone = FALSE; - - if (it->kind == JS_ITERATOR_KIND_KEY) { - return JS_DupValue(ctx, mr->key); - } else { - JSValueConst args[2]; - args[0] = mr->key; - if (magic) - args[1] = mr->key; - else - args[1] = mr->value; - if (it->kind == JS_ITERATOR_KIND_VALUE) { - return JS_DupValue(ctx, args[1]); - } else { - return js_create_array(ctx, 2, args); - } - } -} - -static const JSCFunctionListEntry js_map_funcs[] = { - JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), -}; - -static const JSCFunctionListEntry js_map_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), - JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), - JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), - JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), - JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), - JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_set_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), - JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), - JS_ALIAS_DEF("keys", "values" ), - JS_ALIAS_DEF("[Symbol.iterator]", "values" ), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_weak_map_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_weak_set_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { - js_map_proto_funcs, - js_set_proto_funcs, - js_weak_map_proto_funcs, - js_weak_set_proto_funcs, - js_map_iterator_proto_funcs, - js_set_iterator_proto_funcs, -}; - -static const uint8_t js_map_proto_funcs_count[6] = { - countof(js_map_proto_funcs), - countof(js_set_proto_funcs), - countof(js_weak_map_proto_funcs), - countof(js_weak_set_proto_funcs), - countof(js_map_iterator_proto_funcs), - countof(js_set_iterator_proto_funcs), -}; - -void JS_AddIntrinsicMapSet(JSContext *ctx) -{ - int i; - JSValue obj1; - char buf[ATOM_GET_STR_BUF_SIZE]; - - for(i = 0; i < 4; i++) { - const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), - JS_ATOM_Map + i); - ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i], - js_map_proto_funcs_ptr[i], - js_map_proto_funcs_count[i]); - obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0, - JS_CFUNC_constructor_magic, i); - if (i < 2) { - JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs, - countof(js_map_funcs)); - } - JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]); - } - - for(i = 0; i < 2; i++) { - ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = - JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i], - js_map_proto_funcs_ptr[i + 4], - js_map_proto_funcs_count[i + 4]); - } -} - -/* Generator */ -static const JSCFunctionListEntry js_generator_function_proto_funcs[] = { - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE), -}; - -static const JSCFunctionListEntry js_generator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ), - JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ), - JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE), -}; - -/* Promise */ - -typedef struct JSPromiseData { - JSPromiseStateEnum promise_state; - /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ - struct list_head promise_reactions[2]; - BOOL is_handled; /* Note: only useful to debug */ - JSValue promise_result; -} JSPromiseData; - -typedef struct JSPromiseFunctionDataResolved { - int ref_count; - BOOL already_resolved; -} JSPromiseFunctionDataResolved; - -typedef struct JSPromiseFunctionData { - JSValue promise; - JSPromiseFunctionDataResolved *presolved; -} JSPromiseFunctionData; - -typedef struct JSPromiseReactionData { - struct list_head link; /* not used in promise_reaction_job */ - JSValue resolving_funcs[2]; - JSValue handler; -} JSPromiseReactionData; - -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); - -static void promise_reaction_data_free(JSRuntime *rt, - JSPromiseReactionData *rd) -{ - JS_FreeValueRT(rt, rd->resolving_funcs[0]); - JS_FreeValueRT(rt, rd->resolving_funcs[1]); - JS_FreeValueRT(rt, rd->handler); - js_free_rt(rt, rd); -} - -static JSValue promise_reaction_job(JSContext *ctx, int argc, - JSValueConst *argv) -{ - JSValueConst handler, arg, func; - JSValue res, res2; - BOOL is_reject; - - assert(argc == 5); - handler = argv[2]; - is_reject = JS_ToBool(ctx, argv[3]); - arg = argv[4]; -#ifdef DUMP_PROMISE - printf("promise_reaction_job: is_reject=%d\n", is_reject); -#endif - - if (JS_IsUndefined(handler)) { - if (is_reject) { - res = JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - res = JS_DupValue(ctx, arg); - } - } else { - res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); - } - is_reject = JS_IsException(res); - if (is_reject) - res = JS_GetException(ctx); - func = argv[is_reject]; - /* as an extension, we support undefined as value to avoid - creating a dummy promise in the 'await' implementation of async - functions */ - if (!JS_IsUndefined(func)) { - res2 = JS_Call(ctx, func, JS_UNDEFINED, - 1, (JSValueConst *)&res); - } else { - res2 = JS_UNDEFINED; - } - JS_FreeValue(ctx, res); - - return res2; -} - -void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, - JSHostPromiseRejectionTracker *cb, - void *opaque) -{ - rt->host_promise_rejection_tracker = cb; - rt->host_promise_rejection_tracker_opaque = opaque; -} - -static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, - JSValueConst value, BOOL is_reject) -{ - JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); - struct list_head *el, *el1; - JSPromiseReactionData *rd; - JSValueConst args[5]; - - if (!s || s->promise_state != JS_PROMISE_PENDING) - return; /* should never happen */ - set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); - s->promise_state = JS_PROMISE_FULFILLED + is_reject; -#ifdef DUMP_PROMISE - printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); -#endif - if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { - JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { - rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, - rt->host_promise_rejection_tracker_opaque); - } - } - - list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { - rd = list_entry(el, JSPromiseReactionData, link); - args[0] = rd->resolving_funcs[0]; - args[1] = rd->resolving_funcs[1]; - args[2] = rd->handler; - args[3] = JS_NewBool(ctx, is_reject); - args[4] = value; - JS_EnqueueJob(ctx, promise_reaction_job, 5, args); - list_del(&rd->link); - promise_reaction_data_free(ctx->rt, rd); - } - - list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { - rd = list_entry(el, JSPromiseReactionData, link); - list_del(&rd->link); - promise_reaction_data_free(ctx->rt, rd); - } -} - -static void reject_promise(JSContext *ctx, JSValueConst promise, - JSValueConst value) -{ - fulfill_or_reject_promise(ctx, promise, value, TRUE); -} - -static JSValue js_promise_resolve_thenable_job(JSContext *ctx, - int argc, JSValueConst *argv) -{ - JSValueConst promise, thenable, then; - JSValue args[2], res; - -#ifdef DUMP_PROMISE - printf("js_promise_resolve_thenable_job\n"); -#endif - assert(argc == 3); - promise = argv[0]; - thenable = argv[1]; - then = argv[2]; - if (js_create_resolving_functions(ctx, args, promise) < 0) - return JS_EXCEPTION; - res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); - if (JS_IsException(res)) { - JSValue error = JS_GetException(ctx); - res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - } - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - return res; -} - -static void js_promise_resolve_function_free_resolved(JSRuntime *rt, - JSPromiseFunctionDataResolved *sr) -{ - if (--sr->ref_count == 0) { - js_free_rt(rt, sr); - } -} - -static int js_create_resolving_functions(JSContext *ctx, - JSValue *resolving_funcs, - JSValueConst promise) - -{ - JSValue obj; - JSPromiseFunctionData *s; - JSPromiseFunctionDataResolved *sr; - int i, ret; - - sr = js_malloc(ctx, sizeof(*sr)); - if (!sr) - return -1; - sr->ref_count = 1; - sr->already_resolved = FALSE; /* must be shared between the two functions */ - ret = 0; - for(i = 0; i < 2; i++) { - obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); - if (JS_IsException(obj)) - goto fail; - s = js_malloc(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, obj); - fail: - - if (i != 0) - JS_FreeValue(ctx, resolving_funcs[0]); - ret = -1; - break; - } - sr->ref_count++; - s->presolved = sr; - s->promise = JS_DupValue(ctx, promise); - JS_SetOpaque(obj, s); - js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); - resolving_funcs[i] = obj; - } - js_promise_resolve_function_free_resolved(ctx->rt, sr); - return ret; -} - -static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) -{ - JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; - if (s) { - js_promise_resolve_function_free_resolved(rt, s->presolved); - JS_FreeValueRT(rt, s->promise); - js_free_rt(rt, s); - } -} - -static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; - if (s) { - JS_MarkValue(rt, s->promise, mark_func); - } -} - -static JSValue js_promise_resolve_function_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv, - int flags) -{ - JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSPromiseFunctionData *s; - JSValueConst resolution, args[3]; - JSValue then; - BOOL is_reject; - - s = p->u.promise_function_data; - if (!s || s->presolved->already_resolved) - return JS_UNDEFINED; - s->presolved->already_resolved = TRUE; - is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; - if (argc > 0) - resolution = argv[0]; - else - resolution = JS_UNDEFINED; -#ifdef DUMP_PROMISE - printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); - JS_DumpValue(ctx, resolution); - printf("\n"); -#endif - if (is_reject || !JS_IsObject(resolution)) { - goto done; - } else if (js_same_value(ctx, resolution, s->promise)) { - JS_ThrowTypeError(ctx, "promise self resolution"); - goto fail_reject; - } - then = JS_GetProperty(ctx, resolution, JS_ATOM_then); - if (JS_IsException(then)) { - JSValue error; - fail_reject: - error = JS_GetException(ctx); - reject_promise(ctx, s->promise, error); - JS_FreeValue(ctx, error); - } else if (!JS_IsFunction(ctx, then)) { - JS_FreeValue(ctx, then); - done: - fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); - } else { - args[0] = s->promise; - args[1] = resolution; - args[2] = then; - JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); - JS_FreeValue(ctx, then); - } - return JS_UNDEFINED; -} - -static void js_promise_finalizer(JSRuntime *rt, JSValue val) -{ - JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); - struct list_head *el, *el1; - int i; - - if (!s) - return; - for(i = 0; i < 2; i++) { - list_for_each_safe(el, el1, &s->promise_reactions[i]) { - JSPromiseReactionData *rd = - list_entry(el, JSPromiseReactionData, link); - promise_reaction_data_free(rt, rd); - } - } - JS_FreeValueRT(rt, s->promise_result); - js_free_rt(rt, s); -} - -static void js_promise_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); - struct list_head *el; - int i; - - if (!s) - return; - for(i = 0; i < 2; i++) { - list_for_each(el, &s->promise_reactions[i]) { - JSPromiseReactionData *rd = - list_entry(el, JSPromiseReactionData, link); - JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); - JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); - JS_MarkValue(rt, rd->handler, mark_func); - } - } - JS_MarkValue(rt, s->promise_result, mark_func); -} - -static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValueConst executor; - JSValue obj; - JSPromiseData *s; - JSValue args[2], ret; - int i; - - executor = argv[0]; - if (check_function(ctx, executor)) - return JS_EXCEPTION; - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); - if (JS_IsException(obj)) - return JS_EXCEPTION; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - goto fail; - s->promise_state = JS_PROMISE_PENDING; - s->is_handled = FALSE; - for(i = 0; i < 2; i++) - init_list_head(&s->promise_reactions[i]); - s->promise_result = JS_UNDEFINED; - JS_SetOpaque(obj, s); - if (js_create_resolving_functions(ctx, args, obj)) - goto fail; - ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); - if (JS_IsException(ret)) { - JSValue ret2, error; - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret2)) - goto fail1; - JS_FreeValue(ctx, ret2); - } - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - return obj; - fail1: - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_promise_executor(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - int i; - - for(i = 0; i < 2; i++) { - if (!JS_IsUndefined(func_data[i])) - return JS_ThrowTypeError(ctx, "resolving function already set"); - func_data[i] = JS_DupValue(ctx, argv[i]); - } - return JS_UNDEFINED; -} - -static JSValue js_promise_executor_new(JSContext *ctx) -{ - JSValueConst func_data[2]; - - func_data[0] = JS_UNDEFINED; - func_data[1] = JS_UNDEFINED; - return JS_NewCFunctionData(ctx, js_promise_executor, 2, - 0, 2, func_data); -} - -static JSValue js_new_promise_capability(JSContext *ctx, - JSValue *resolving_funcs, - JSValueConst ctor) -{ - JSValue executor, result_promise; - JSCFunctionDataRecord *s; - int i; - - executor = js_promise_executor_new(ctx); - if (JS_IsException(executor)) - return executor; - - if (JS_IsUndefined(ctor)) { - result_promise = js_promise_constructor(ctx, ctor, 1, - (JSValueConst *)&executor); - } else { - result_promise = JS_CallConstructor(ctx, ctor, 1, - (JSValueConst *)&executor); - } - if (JS_IsException(result_promise)) - goto fail; - s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); - for(i = 0; i < 2; i++) { - if (check_function(ctx, s->data[i])) - goto fail; - } - for(i = 0; i < 2; i++) - resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); - JS_FreeValue(ctx, executor); - return result_promise; - fail: - JS_FreeValue(ctx, executor); - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; -} - -JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) -{ - return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); -} - -static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue result_promise, resolving_funcs[2], ret; - BOOL is_reject = magic; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { - JSValue ctor; - BOOL is_same; - ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - is_same = js_same_value(ctx, ctor, this_val); - JS_FreeValue(ctx, ctor); - if (is_same) - return JS_DupValue(ctx, argv[0]); - } - result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); - if (JS_IsException(result_promise)) - return result_promise; - ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, result_promise); - return ret; - } - JS_FreeValue(ctx, ret); - return result_promise; -} - -static JSValue js_promise_withResolvers(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue result_promise, resolving_funcs[2], obj; - 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; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; - } - JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); - return obj; -} - -static __exception int remainingElementsCount_add(JSContext *ctx, - JSValueConst resolve_element_env, - int addend) -{ - JSValue val; - int remainingElementsCount; - - val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); - if (JS_IsException(val)) - return -1; - if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) - return -1; - remainingElementsCount += addend; - if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, - JS_NewInt32(ctx, remainingElementsCount)) < 0) - return -1; - return (remainingElementsCount == 0); -} - -#define PROMISE_MAGIC_all 0 -#define PROMISE_MAGIC_allSettled 1 -#define PROMISE_MAGIC_any 2 - -static JSValue js_promise_all_resolve_element(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, - JSValue *func_data) -{ - int resolve_type = magic & 3; - int is_reject = magic & 4; - BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); - JSValueConst values = func_data[2]; - JSValueConst resolve = func_data[3]; - JSValueConst resolve_element_env = func_data[4]; - JSValue ret, obj; - int is_zero, index; - - if (JS_ToInt32(ctx, &index, func_data[1])) - return JS_EXCEPTION; - if (alreadyCalled) - return JS_UNDEFINED; - func_data[0] = JS_NewBool(ctx, TRUE); - - if (resolve_type == PROMISE_MAGIC_allSettled) { - JSValue str; - - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled"); - if (JS_IsException(str)) - goto fail1; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, - str, - JS_PROP_C_W_E) < 0) - goto fail1; - if (JS_DefinePropertyValue(ctx, obj, - is_reject ? JS_ATOM_reason : JS_ATOM_value, - JS_DupValue(ctx, argv[0]), - JS_PROP_C_W_E) < 0) { - fail1: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } else { - obj = JS_DupValue(ctx, argv[0]); - } - if (JS_DefinePropertyValueUint32(ctx, values, index, - obj, JS_PROP_C_W_E) < 0) - return JS_EXCEPTION; - - is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); - if (is_zero < 0) - return JS_EXCEPTION; - if (is_zero) { - if (resolve_type == PROMISE_MAGIC_any) { - JSValue error; - error = js_aggregate_error_constructor(ctx, values); - if (JS_IsException(error)) - return JS_EXCEPTION; - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - } else { - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); - } - if (JS_IsException(ret)) - return ret; - JS_FreeValue(ctx, ret); - } - return JS_UNDEFINED; -} - -/* magic = 0: Promise.all 1: Promise.allSettled */ -static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue result_promise, resolving_funcs[2], item, next_promise, ret; - JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; - JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; - JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; - JSValueConst then_args[2], resolve_element_data[5]; - BOOL done; - int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); - if (JS_IsException(result_promise)) - return result_promise; - promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); - if (JS_IsException(promise_resolve) || - check_function(ctx, promise_resolve)) - goto fail_reject; - iter = JS_GetIterator(ctx, argv[0], FALSE); - if (JS_IsException(iter)) { - JSValue error; - fail_reject: - error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - (JSValueConst *)&error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret)) - goto fail; - JS_FreeValue(ctx, ret); - } else { - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail_reject; - values = JS_NewArray(ctx); - if (JS_IsException(values)) - goto fail_reject; - resolve_element_env = JS_NewArray(ctx); - if (JS_IsException(resolve_element_env)) - goto fail_reject; - /* remainingElementsCount field */ - if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, - JS_NewInt32(ctx, 1), - JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - goto fail_reject; - - index = 0; - for(;;) { - /* XXX: conformance: should close the iterator if error on 'done' - access, but not on 'value' access */ - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail_reject; - if (done) - break; - next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, (JSValueConst *)&item); - JS_FreeValue(ctx, item); - if (JS_IsException(next_promise)) { - fail_reject1: - JS_IteratorClose(ctx, iter, TRUE); - goto fail_reject; - } - resolve_element_data[0] = JS_NewBool(ctx, FALSE); - resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); - resolve_element_data[2] = values; - resolve_element_data[3] = resolving_funcs[is_promise_any]; - resolve_element_data[4] = resolve_element_env; - resolve_element = - JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, - magic, 5, resolve_element_data); - if (JS_IsException(resolve_element)) { - JS_FreeValue(ctx, next_promise); - goto fail_reject1; - } - - if (magic == PROMISE_MAGIC_allSettled) { - reject_element = - JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, - magic | 4, 5, resolve_element_data); - if (JS_IsException(reject_element)) { - JS_FreeValue(ctx, next_promise); - goto fail_reject1; - } - } else if (magic == PROMISE_MAGIC_any) { - if (JS_DefinePropertyValueUint32(ctx, values, index, - JS_UNDEFINED, JS_PROP_C_W_E) < 0) - goto fail_reject1; - reject_element = resolve_element; - resolve_element = JS_DupValue(ctx, resolving_funcs[0]); - } else { - reject_element = JS_DupValue(ctx, resolving_funcs[1]); - } - - if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { - JS_FreeValue(ctx, next_promise); - JS_FreeValue(ctx, resolve_element); - JS_FreeValue(ctx, reject_element); - goto fail_reject1; - } - - then_args[0] = resolve_element; - then_args[1] = reject_element; - ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); - JS_FreeValue(ctx, resolve_element); - JS_FreeValue(ctx, reject_element); - if (check_exception_free(ctx, ret)) - goto fail_reject1; - index++; - } - - is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); - if (is_zero < 0) - goto fail_reject; - if (is_zero) { - if (magic == PROMISE_MAGIC_any) { - JSValue error; - error = js_aggregate_error_constructor(ctx, values); - if (JS_IsException(error)) - goto fail_reject; - JS_FreeValue(ctx, values); - values = error; - } - ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, - 1, (JSValueConst *)&values); - if (check_exception_free(ctx, ret)) - goto fail_reject; - } - } - done: - JS_FreeValue(ctx, promise_resolve); - JS_FreeValue(ctx, resolve_element_env); - JS_FreeValue(ctx, values); - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return result_promise; - fail: - JS_FreeValue(ctx, result_promise); - result_promise = JS_EXCEPTION; - goto done; -} - -static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue result_promise, resolving_funcs[2], item, next_promise, ret; - JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; - JSValue promise_resolve = JS_UNDEFINED; - BOOL done; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); - if (JS_IsException(result_promise)) - return result_promise; - promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); - if (JS_IsException(promise_resolve) || - check_function(ctx, promise_resolve)) - goto fail_reject; - iter = JS_GetIterator(ctx, argv[0], FALSE); - if (JS_IsException(iter)) { - JSValue error; - fail_reject: - error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - (JSValueConst *)&error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret)) - goto fail; - JS_FreeValue(ctx, ret); - } else { - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail_reject; - - for(;;) { - /* XXX: conformance: should close the iterator if error on 'done' - access, but not on 'value' access */ - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail_reject; - if (done) - break; - next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, (JSValueConst *)&item); - JS_FreeValue(ctx, item); - if (JS_IsException(next_promise)) { - fail_reject1: - JS_IteratorClose(ctx, iter, TRUE); - goto fail_reject; - } - ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, - (JSValueConst *)resolving_funcs); - if (check_exception_free(ctx, ret)) - goto fail_reject1; - } - } - done: - JS_FreeValue(ctx, promise_resolve); - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return result_promise; - fail: - //JS_FreeValue(ctx, next_method); // why not??? - JS_FreeValue(ctx, result_promise); - result_promise = JS_EXCEPTION; - goto done; -} - -static __exception int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs) -{ - JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); - JSPromiseReactionData *rd_array[2], *rd; - int i, j; - - rd_array[0] = NULL; - rd_array[1] = NULL; - for(i = 0; i < 2; i++) { - JSValueConst handler; - rd = js_mallocz(ctx, sizeof(*rd)); - if (!rd) { - if (i == 1) - promise_reaction_data_free(ctx->rt, rd_array[0]); - return -1; - } - for(j = 0; j < 2; j++) - rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); - handler = resolve_reject[i]; - if (!JS_IsFunction(ctx, handler)) - handler = JS_UNDEFINED; - rd->handler = JS_DupValue(ctx, handler); - rd_array[i] = rd; - } - - if (s->promise_state == JS_PROMISE_PENDING) { - for(i = 0; i < 2; i++) - list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); - } else { - JSValueConst args[5]; - if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { - JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { - rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, - TRUE, rt->host_promise_rejection_tracker_opaque); - } - } - i = s->promise_state - JS_PROMISE_FULFILLED; - rd = rd_array[i]; - args[0] = rd->resolving_funcs[0]; - args[1] = rd->resolving_funcs[1]; - args[2] = rd->handler; - args[3] = JS_NewBool(ctx, i); - args[4] = s->promise_result; - JS_EnqueueJob(ctx, promise_reaction_job, 5, args); - for(i = 0; i < 2; i++) - promise_reaction_data_free(ctx->rt, rd_array[i]); - } - s->is_handled = TRUE; - return 0; -} - -static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue ctor, result_promise, resolving_funcs[2]; - JSPromiseData *s; - int i, ret; - - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); - if (!s) - return JS_EXCEPTION; - - ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); - JS_FreeValue(ctx, ctor); - if (JS_IsException(result_promise)) - return result_promise; - ret = perform_promise_then(ctx, this_val, argv, - (JSValueConst *)resolving_funcs); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (ret) { - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; - } - return result_promise; -} - -static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst args[2]; - args[0] = JS_UNDEFINED; - args[1] = argv[0]; - return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); -} - -static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return JS_DupValue(ctx, func_data[0]); -} - -static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); -} - -static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - JSValueConst ctor = func_data[0]; - JSValueConst onFinally = func_data[1]; - JSValue res, promise, ret, then_func; - - res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); - if (JS_IsException(res)) - return res; - promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); - JS_FreeValue(ctx, res); - if (JS_IsException(promise)) - return promise; - if (magic == 0) { - then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, - 0, 1, argv); - } else { - then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, - 0, 1, argv); - } - if (JS_IsException(then_func)) { - JS_FreeValue(ctx, promise); - return then_func; - } - ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); - JS_FreeValue(ctx, then_func); - return ret; -} - -static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst onFinally = argv[0]; - JSValue ctor, ret; - JSValue then_funcs[2]; - JSValueConst func_data[2]; - int i; - - ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - if (!JS_IsFunction(ctx, onFinally)) { - then_funcs[0] = JS_DupValue(ctx, onFinally); - then_funcs[1] = JS_DupValue(ctx, onFinally); - } else { - func_data[0] = ctor; - func_data[1] = onFinally; - for(i = 0; i < 2; i++) { - then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); - if (JS_IsException(then_funcs[i])) { - if (i == 1) - JS_FreeValue(ctx, then_funcs[0]); - JS_FreeValue(ctx, ctor); - return JS_EXCEPTION; - } - } - } - JS_FreeValue(ctx, ctor); - ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs); - JS_FreeValue(ctx, then_funcs[0]); - JS_FreeValue(ctx, then_funcs[1]); - return ret; -} - -static const JSCFunctionListEntry js_promise_funcs[] = { - JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), - JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), - JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), - JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), - JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), - JS_CFUNC_DEF("race", 1, js_promise_race ), - JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), -}; - -static const JSCFunctionListEntry js_promise_proto_funcs[] = { - JS_CFUNC_DEF("then", 2, js_promise_then ), - JS_CFUNC_DEF("catch", 1, js_promise_catch ), - JS_CFUNC_DEF("finally", 1, js_promise_finally ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), -}; - -/* AsyncFunction */ -static const JSCFunctionListEntry js_async_function_proto_funcs[] = { - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), -}; - -static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), - JS_ToBool(ctx, func_data[0])); -} - -static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, - BOOL done) -{ - JSValueConst func_data[1]; - - func_data[0] = (JSValueConst)JS_NewBool(ctx, done); - return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, - 1, 0, 1, func_data); -} - -/* AsyncIteratorPrototype */ - -static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { - JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), -}; - -/* AsyncFromSyncIteratorPrototype */ - -typedef struct JSAsyncFromSyncIteratorData { - JSValue sync_iter; - JSValue next_method; -} JSAsyncFromSyncIteratorData; - -static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSAsyncFromSyncIteratorData *s = - JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (s) { - JS_FreeValueRT(rt, s->sync_iter); - JS_FreeValueRT(rt, s->next_method); - js_free_rt(rt, s); - } -} - -static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSAsyncFromSyncIteratorData *s = - JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (s) { - JS_MarkValue(rt, s->sync_iter, mark_func); - JS_MarkValue(rt, s->next_method, mark_func); - } -} - -static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, - JSValueConst sync_iter) -{ - JSValue async_iter, next_method; - JSAsyncFromSyncIteratorData *s; - - next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); - if (JS_IsException(next_method)) - return JS_EXCEPTION; - async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (JS_IsException(async_iter)) { - JS_FreeValue(ctx, next_method); - return async_iter; - } - s = js_mallocz(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, async_iter); - JS_FreeValue(ctx, next_method); - return JS_EXCEPTION; - } - s->sync_iter = JS_DupValue(ctx, sync_iter); - s->next_method = next_method; - JS_SetOpaque(async_iter, s); - return async_iter; -} - -static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - JSValue promise, resolving_funcs[2], value, err, method; - JSAsyncFromSyncIteratorData *s; - int done; - int is_reject; - - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (!s) { - JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); - goto reject; - } - - if (magic == GEN_MAGIC_NEXT) { - method = JS_DupValue(ctx, s->next_method); - } else { - method = JS_GetProperty(ctx, s->sync_iter, - magic == GEN_MAGIC_RETURN ? JS_ATOM_return : - JS_ATOM_throw); - if (JS_IsException(method)) - goto reject; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - if (magic == GEN_MAGIC_RETURN) { - err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); - is_reject = 0; - } else { - err = JS_DupValue(ctx, argv[0]); - is_reject = 1; - } - goto done_resolve; - } - } - value = JS_IteratorNext2(ctx, s->sync_iter, method, - argc >= 1 ? 1 : 0, argv, &done); - JS_FreeValue(ctx, method); - if (JS_IsException(value)) - goto reject; - if (done == 2) { - JSValue obj = value; - value = JS_IteratorGetCompleteValue(ctx, obj, &done); - JS_FreeValue(ctx, obj); - if (JS_IsException(value)) - goto reject; - } - - if (JS_IsException(value)) { - JSValue res2; - reject: - err = JS_GetException(ctx); - is_reject = 1; - done_resolve: - res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, - 1, (JSValueConst *)&err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - } - { - JSValue value_wrapper_promise, resolve_reject[2]; - int res; - - value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - if (JS_IsException(value_wrapper_promise)) { - JS_FreeValue(ctx, value); - goto reject; - } - - resolve_reject[0] = - js_async_from_sync_iterator_unwrap_func_create(ctx, done); - if (JS_IsException(resolve_reject[0])) { - JS_FreeValue(ctx, value_wrapper_promise); - goto fail; - } - JS_FreeValue(ctx, value); - resolve_reject[1] = JS_UNDEFINED; - - res = perform_promise_then(ctx, value_wrapper_promise, - (JSValueConst *)resolve_reject, - (JSValueConst *)resolving_funcs); - JS_FreeValue(ctx, resolve_reject[0]); - JS_FreeValue(ctx, value_wrapper_promise); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - if (res) { - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; - } - } - return promise; - fail: - JS_FreeValue(ctx, value); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), - JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), - JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), -}; - -/* AsyncGeneratorFunction */ - -static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), -}; - -/* AsyncGenerator prototype */ - -static const JSCFunctionListEntry js_async_generator_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), - JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), - JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), -}; - -static JSClassShortDef const js_async_class_def[] = { - { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ - { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ - { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ - { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ - { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ - { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ - { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ - { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ - { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ -}; - -void JS_AddIntrinsicPromise(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValue obj1; - - if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { - init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, - countof(js_async_class_def)); - rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; - rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; - rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; - rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; - rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; - rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; - } - - /* Promise */ - ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], - js_promise_proto_funcs, - countof(js_promise_proto_funcs)); - obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, - JS_CFUNC_constructor, 0); - ctx->promise_ctor = JS_DupValue(ctx, obj1); - JS_SetPropertyFunctionList(ctx, obj1, - js_promise_funcs, - countof(js_promise_funcs)); - JS_NewGlobalCConstructor2(ctx, obj1, "Promise", - ctx->class_proto[JS_CLASS_PROMISE]); - - /* AsyncFunction */ - ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, - "AsyncFunction", 1, - JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, - ctx->function_ctor); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], - js_async_function_proto_funcs, - countof(js_async_function_proto_funcs)); - JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], - 0, JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); - - /* AsyncIteratorPrototype */ - ctx->async_iterator_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, - js_async_iterator_proto_funcs, - countof(js_async_iterator_proto_funcs)); - - /* AsyncFromSyncIteratorPrototype */ - ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = - JS_NewObjectProto(ctx, ctx->async_iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], - js_async_from_sync_iterator_proto_funcs, - countof(js_async_from_sync_iterator_proto_funcs)); - - /* AsyncGeneratorPrototype */ - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = - JS_NewObjectProto(ctx, ctx->async_iterator_proto); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], - js_async_generator_proto_funcs, - countof(js_async_generator_proto_funcs)); - - /* AsyncGeneratorFunction */ - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = - JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, - "AsyncGeneratorFunction", 1, - JS_CFUNC_constructor_or_func_magic, - JS_FUNC_ASYNC_GENERATOR, - ctx->function_ctor); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], - js_async_generator_function_proto_funcs, - countof(js_async_generator_function_proto_funcs)); - JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], - JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); - JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], - 0, JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); -} - -/* URI handling */ - -static int string_get_hex(JSString *p, int k, int n) { - int c = 0, h; - while (n-- > 0) { - if ((h = from_hex(string_get(p, k++))) < 0) - return -1; - c = (c << 4) | h; - } - return c; -} - -static int isURIReserved(int c) { - return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; -} - -static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap); - va_end(ap); - return -1; -} - -static int hex_decode(JSContext *ctx, JSString *p, int k) { - int c; - - if (k >= p->len || string_get(p, k) != '%') - return js_throw_URIError(ctx, "expecting %%"); - if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0) - return js_throw_URIError(ctx, "expecting hex digit"); - - return c; -} - -static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int isComponent) -{ - JSValue str; - StringBuffer b_s, *b = &b_s; - JSString *p; - int k, c, c1, n, c_min; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return str; - - string_buffer_init(ctx, b, 0); - - p = JS_VALUE_GET_STRING(str); - for (k = 0; k < p->len;) { - c = string_get(p, k); - if (c == '%') { - c = hex_decode(ctx, p, k); - if (c < 0) - goto fail; - k += 3; - if (c < 0x80) { - if (!isComponent && isURIReserved(c)) { - c = '%'; - k -= 2; - } - } else { - /* Decode URI-encoded UTF-8 sequence */ - if (c >= 0xc0 && c <= 0xdf) { - n = 1; - c_min = 0x80; - c &= 0x1f; - } else if (c >= 0xe0 && c <= 0xef) { - n = 2; - c_min = 0x800; - c &= 0xf; - } else if (c >= 0xf0 && c <= 0xf7) { - n = 3; - c_min = 0x10000; - c &= 0x7; - } else { - n = 0; - c_min = 1; - c = 0; - } - while (n-- > 0) { - c1 = hex_decode(ctx, p, k); - if (c1 < 0) - goto fail; - k += 3; - if ((c1 & 0xc0) != 0x80) { - c = 0; - break; - } - c = (c << 6) | (c1 & 0x3f); - } - if (c < c_min || c > 0x10FFFF || is_surrogate(c)) { - js_throw_URIError(ctx, "malformed UTF-8"); - goto fail; - } - } - } else { - k++; - } - string_buffer_putc(b, c); - } - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -fail: - JS_FreeValue(ctx, str); - string_buffer_free(b); - return JS_EXCEPTION; -} - -static int isUnescaped(int c) { - static char const unescaped_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "@*_+-./"; - return c < 0x100 && - memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1); -} - -static int isURIUnescaped(int c, int isComponent) { - return c < 0x100 && - ((c >= 0x61 && c <= 0x7a) || - (c >= 0x41 && c <= 0x5a) || - (c >= 0x30 && c <= 0x39) || - memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL || - (!isComponent && isURIReserved(c))); -} - -static int encodeURI_hex(StringBuffer *b, int c) { - uint8_t buf[6]; - int n = 0; - const char *hex = "0123456789ABCDEF"; - - buf[n++] = '%'; - if (c >= 256) { - buf[n++] = 'u'; - buf[n++] = hex[(c >> 12) & 15]; - buf[n++] = hex[(c >> 8) & 15]; - } - buf[n++] = hex[(c >> 4) & 15]; - buf[n++] = hex[(c >> 0) & 15]; - return string_buffer_write8(b, buf, n); -} - -static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int isComponent) -{ - JSValue str; - StringBuffer b_s, *b = &b_s; - JSString *p; - int k, c, c1; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return str; - - p = JS_VALUE_GET_STRING(str); - string_buffer_init(ctx, b, p->len); - for (k = 0; k < p->len;) { - c = string_get(p, k); - k++; - if (isURIUnescaped(c, isComponent)) { - string_buffer_putc16(b, c); - } else { - if (is_lo_surrogate(c)) { - js_throw_URIError(ctx, "invalid character"); - goto fail; - } 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 (!is_lo_surrogate(c1)) { - js_throw_URIError(ctx, "expecting surrogate pair"); - goto fail; - } - c = from_surrogate(c, c1); - } - if (c < 0x80) { - encodeURI_hex(b, c); - } else { - /* XXX: use C UTF-8 conversion ? */ - if (c < 0x800) { - encodeURI_hex(b, (c >> 6) | 0xc0); - } else { - if (c < 0x10000) { - encodeURI_hex(b, (c >> 12) | 0xe0); - } else { - encodeURI_hex(b, (c >> 18) | 0xf0); - encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80); - } - encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80); - } - encodeURI_hex(b, (c & 0x3f) | 0x80); - } - } - } - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -fail: - JS_FreeValue(ctx, str); - string_buffer_free(b); - return JS_EXCEPTION; -} - -static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - StringBuffer b_s, *b = &b_s; - JSString *p; - int i, len, c; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return str; - - p = JS_VALUE_GET_STRING(str); - string_buffer_init(ctx, b, p->len); - for (i = 0, len = p->len; i < len; i++) { - c = string_get(p, i); - if (isUnescaped(c)) { - string_buffer_putc16(b, c); - } else { - encodeURI_hex(b, c); - } - } - JS_FreeValue(ctx, str); - return string_buffer_end(b); -} - -static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - StringBuffer b_s, *b = &b_s; - JSString *p; - int i, len, c, n; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return str; - - string_buffer_init(ctx, b, 0); - p = JS_VALUE_GET_STRING(str); - for (i = 0, len = p->len; i < len; i++) { - c = string_get(p, i); - if (c == '%') { - if (i + 6 <= len - && string_get(p, i + 1) == 'u' - && (n = string_get_hex(p, i + 2, 4)) >= 0) { - c = n; - i += 6 - 1; - } else - if (i + 3 <= len - && (n = string_get_hex(p, i + 1, 2)) >= 0) { - c = n; - i += 3 - 1; - } - } - string_buffer_putc16(b, c); - } - JS_FreeValue(ctx, str); - return string_buffer_end(b); -} - -/* global object */ - -static const JSCFunctionListEntry js_global_funcs[] = { - JS_CFUNC_DEF("parseInt", 2, js_parseInt ), - JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ), - JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), - JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), - - JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ), - JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ), - JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ), - JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ), - JS_CFUNC_DEF("escape", 1, js_global_escape ), - JS_CFUNC_DEF("unescape", 1, js_global_unescape ), - JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), - JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), - JS_PROP_UNDEFINED_DEF("undefined", 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ), -}; - -/* Date */ - -static int64_t math_mod(int64_t a, int64_t b) { - /* return positive modulo */ - int64_t m = a % b; - return m + (m < 0) * b; -} - -static int64_t floor_div(int64_t a, int64_t b) { - /* integer division rounding toward -Infinity */ - int64_t m = a % b; - return (a - (m + (m < 0) * b)) / b; -} - -static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) -{ - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) - return JS_ToFloat64(ctx, valp, p->u.object_data); - } - JS_ThrowTypeError(ctx, "not a Date object"); - return -1; -} - -static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v) -{ - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_DATE) { - JS_FreeValue(ctx, p->u.object_data); - p->u.object_data = JS_NewFloat64(ctx, v); - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a Date object"); -} - -static int64_t days_from_year(int64_t y) { - return 365 * (y - 1970) + floor_div(y - 1969, 4) - - floor_div(y - 1901, 100) + floor_div(y - 1601, 400); -} - -static int64_t days_in_year(int64_t y) { - return 365 + !(y % 4) - !(y % 100) + !(y % 400); -} - -/* return the year, update days */ -static int64_t year_from_days(int64_t *days) { - int64_t y, d1, nd, d = *days; - y = floor_div(d * 10000, 3652425) + 1970; - /* the initial approximation is very good, so only a few - iterations are necessary */ - for(;;) { - d1 = d - days_from_year(y); - if (d1 < 0) { - y--; - d1 += days_in_year(y); - } else { - nd = days_in_year(y); - if (d1 < nd) - break; - d1 -= nd; - y++; - } - } - *days = d1; - return y; -} - -static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; -static char const day_names[] = "SunMonTueWedThuFriSat"; - -static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, - 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; - - if (JS_ThisTimeValue(ctx, &dval, obj)) - return -1; - - if (isnan(dval)) { - if (!force) - return FALSE; /* NaN */ - d = 0; /* initialize all fields to 0 */ - } else { - d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */ - if (is_local) { - tz = -getTimezoneOffset(d); - d += tz * 60000; - } - } - - /* result is >= 0, we can use % */ - h = math_mod(d, 86400000); - days = (d - h) / 86400000; - ms = h % 1000; - h = (h - ms) / 1000; - s = h % 60; - h = (h - s) / 60; - m = h % 60; - h = (h - m) / 60; - wd = math_mod(days + 4, 7); /* week day */ - y = year_from_days(&days); - - for(i = 0; i < 11; i++) { - md = month_days[i]; - if (i == 1) - md += days_in_year(y) - 365; - if (days < md) - break; - days -= md; - } - fields[0] = y; - fields[1] = i; - fields[2] = days + 1; - fields[3] = h; - fields[4] = m; - fields[5] = s; - fields[6] = ms; - fields[7] = wd; - fields[8] = tz; - return TRUE; -} - -static double time_clip(double t) { - if (t >= -8.64e15 && t <= 8.64e15) - return trunc(t) + 0.0; /* convert -0 to +0 */ - else - return NAN; -} - -/* The spec mandates the use of 'double' and it specifies the order - of the operations */ -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) - 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; - } - return time_clip(tv); -} - -static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - // get_date_field(obj, n, is_local) - double fields[9]; - int res, n, is_local; - - is_local = magic & 0x0F; - n = (magic >> 4) & 0x0F; - res = get_date_fields(ctx, this_val, fields, is_local, 0); - if (res < 0) - return JS_EXCEPTION; - if (!res) - return JS_NAN; - - if (magic & 0x100) { // getYear - fields[0] -= 1900; - } - return JS_NewFloat64(ctx, fields[n]); -} - -static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - // _field(obj, first_field, end_field, args, is_local) - double fields[9]; - int res, first_field, end_field, is_local, i, n; - double d, a; - - d = NAN; - first_field = (magic >> 8) & 0x0F; - end_field = (magic >> 4) & 0x0F; - is_local = magic & 0x0F; - - res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); - if (res < 0) - return JS_EXCEPTION; - - // Argument coercion is observable and must be done unconditionally. - n = min_int(argc, end_field - first_field); - for(i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - res = FALSE; - fields[first_field + i] = trunc(a); - } - if (res && argc > 0) - d = set_date_fields(fields, is_local); - - return JS_SetThisTimeValue(ctx, this_val, d); -} - -/* fmt: - 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" - 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" - 2: toISOString: "2018-01-02T23:02:56.927Z" - 3: toLocaleString: "1/2/2018, 11:40:40 PM" - part: 1=date, 2=time 3=all - XXX: should use a variant of strftime(). - */ -static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - // _string(obj, fmt, part) - char buf[64]; - double fields[9]; - int res, fmt, part, pos; - int y, mon, d, h, m, s, ms, wd, tz; - - fmt = (magic >> 4) & 0x0F; - part = magic & 0x0F; - - res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); - if (res < 0) - return JS_EXCEPTION; - if (!res) { - if (fmt == 2) - return JS_ThrowRangeError(ctx, "Date value is NaN"); - else - return JS_NewString(ctx, "Invalid Date"); - } - - y = fields[0]; - mon = fields[1]; - d = fields[2]; - h = fields[3]; - m = fields[4]; - s = fields[5]; - ms = fields[6]; - wd = fields[7]; - tz = fields[8]; - - pos = 0; - - if (part & 1) { /* date part */ - switch(fmt) { - case 0: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%.3s, %02d %.3s %0*d ", - day_names + wd * 3, d, - month_names + mon * 3, 4 + (y < 0), y); - break; - case 1: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%.3s %.3s %02d %0*d", - day_names + wd * 3, - month_names + mon * 3, d, 4 + (y < 0), y); - if (part == 3) { - buf[pos++] = ' '; - } - break; - case 2: - if (y >= 0 && y <= 9999) { - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%04d", y); - } else { - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%+07d", y); - } - pos += snprintf(buf + pos, sizeof(buf) - pos, - "-%02d-%02dT", mon + 1, d); - break; - case 3: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); - if (part == 3) { - buf[pos++] = ','; - buf[pos++] = ' '; - } - break; - } - } - if (part & 2) { /* time part */ - switch(fmt) { - case 0: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d:%02d:%02d GMT", h, m, s); - break; - case 1: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d:%02d:%02d GMT", h, m, s); - if (tz < 0) { - buf[pos++] = '-'; - tz = -tz; - } else { - buf[pos++] = '+'; - } - /* tz is >= 0, can use % */ - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d%02d", tz / 60, tz % 60); - /* XXX: tack the time zone code? */ - break; - case 2: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d:%02d:%02d.%03dZ", h, m, s, ms); - break; - case 3: - pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, - (h < 12) ? 'A' : 'P'); - break; - } - } - return JS_NewStringLen(ctx, buf, pos); -} - -/* OS dependent: return the UTC time in ms since 1970. */ -static int64_t date_now(void) { - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -} - -static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - // Date(y, mon, d, h, m, s, ms) - JSValue rv; - int i, n; - double a, val; - - if (JS_IsUndefined(new_target)) { - /* invoked as function */ - argc = 0; - } - n = argc; - if (n == 0) { - val = date_now(); - } else if (n == 1) { - JSValue v, dv; - if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(argv[0]); - if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { - if (JS_ToFloat64(ctx, &val, p->u.object_data)) - return JS_EXCEPTION; - val = time_clip(val); - goto has_val; - } - } - v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); - if (JS_IsString(v)) { - dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); - JS_FreeValue(ctx, v); - if (JS_IsException(dv)) - return JS_EXCEPTION; - if (JS_ToFloat64Free(ctx, &val, dv)) - return JS_EXCEPTION; - } else { - if (JS_ToFloat64Free(ctx, &val, v)) - return JS_EXCEPTION; - } - val = time_clip(val); - } else { - double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; - if (n > 7) - n = 7; - for(i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - break; - fields[i] = trunc(a); - if (i == 0 && fields[0] >= 0 && fields[0] < 100) - fields[0] += 1900; - } - val = (i == n) ? set_date_fields(fields, 1) : NAN; - } -has_val: -#if 0 - JSValueConst args[3]; - args[0] = new_target; - args[1] = ctx->class_proto[JS_CLASS_DATE]; - args[2] = JS_NewFloat64(ctx, val); - rv = js___date_create(ctx, JS_UNDEFINED, 3, args); -#else - rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); - if (!JS_IsException(rv)) - JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); -#endif - if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { - /* invoked as a function, return (new Date()).toString(); */ - JSValue s; - s = get_date_string(ctx, rv, 0, NULL, 0x13); - JS_FreeValue(ctx, rv); - rv = s; - } - return rv; -} - -static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // UTC(y, mon, d, h, m, s, ms) - 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)); -} - -/* Date string parsing */ - -static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) { - if (sp[*pp] == c) { - *pp += 1; - return TRUE; - } else { - return FALSE; - } -} - -/* 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; -} - -/* 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; -} - -/* 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; -} - -/* 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) -{ - int v = 0; - int c, p = *pp, p_start; - - p_start = p; - while ((c = sp[p]) >= '0' && c <= '9') { - v = v * 10 + c - '0'; - p++; - if (p - p_start == max_digits) - break; - } - if (p - p_start < min_digits) - return FALSE; - *pval = v; - *pp = p; - return TRUE; -} - -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; - - 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; - } - } - return TRUE; -} - -static uint8_t upper_ascii(uint8_t c) { - return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; -} - -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, 9)) - 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; - } - *pp = p; - *tzp = tz; - return TRUE; -} - -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++; - } - *pp = p; - return TRUE; -} - -static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) { - int n, i; - - 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; - } - } - return -1; -} - -static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) { - int n; - - n = find_abbrev(sp, *pp, month_names, 12); - if (n < 0) - return FALSE; - - *pval = n + 1; - *pp += 3; - return TRUE; -} - -/* parse toISOString format */ -static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) { - int sgn, i, p = 0; - - /* 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 (!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; - } - } - 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 (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]); - } - } - /* 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 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; -} - -/* 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, 9)) { - if (c == '-') { - if (val == 0) - return FALSE; - val = -val; - } - fields[0] = val; - has_year = TRUE; - } - } - } else - if (string_get_digits(sp, &p, &val, 1, 9)) { - 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; - } - } - } 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); - } - if (num_index + has_year + has_mon > 3) - return FALSE; - - switch (num_index) { - case 0: - if (!has_year) - return FALSE; - break; - case 1: - if (has_mon) - fields[2] = num[0]; - else - fields[1] = num[0]; - break; - 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 3: - fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100; - fields[1] = num[0]; - fields[2] = num[1]; - break; - default: - return FALSE; - } - if (fields[1] < 1 || fields[2] < 1) - return FALSE; - fields[1] -= 1; - return TRUE; -} - -static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue s, rv; - int fields[9]; - double fields1[9]; - double d; - int i, c; - JSString *sp; - uint8_t buf[128]; - 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); - /* 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_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: - case JS_ATOM_integer: - 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 - /* assuming -8.64e15 <= v <= -8.64e15 */ - 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 ), -}; - -JSValue JS_NewDate(JSContext *ctx, double epoch_ms) -{ - JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE); - if (JS_IsException(obj)) - return JS_EXCEPTION; - JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms))); - return obj; -} - -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; - } - fail: - JS_FreeValue(ctx, q_val); - JS_FreeValue(ctx, r_val); - return JS_EXCEPTION; -} - -static JSValue js_bigint_sqrt(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, *r, *rem; - int status; - JSValue r_val, rem_val; - - r_val = JS_NewBigInt(ctx); - if (JS_IsException(r_val)) - return JS_EXCEPTION; - rem_val = JS_NewBigInt(ctx); - if (JS_IsException(rem_val)) - return JS_EXCEPTION; - r = JS_GetBigInt(r_val); - rem = JS_GetBigInt(rem_val); - - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - goto fail; - status = bf_sqrtrem(r, rem, a); - JS_FreeBigInt(ctx, a, &a_s); - if (unlikely(status & ~BF_ST_INEXACT)) { - throw_bf_exception(ctx, status); - goto fail; - } - r_val = JS_CompactBigInt(ctx, r_val); - if (magic) { - JSValue ret; - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto fail; - JS_SetPropertyUint32(ctx, ret, 0, r_val); - JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); - return ret; - } else { - JS_FreeValue(ctx, rem_val); - return r_val; - } - fail: - JS_FreeValue(ctx, r_val); - JS_FreeValue(ctx, rem_val); - return JS_EXCEPTION; -} - -static JSValue js_bigint_op1(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - bf_t a_s, *a; - int64_t res; - - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - return JS_EXCEPTION; - switch(magic) { - case 0: /* floorLog2 */ - if (a->sign || a->expn <= 0) { - res = -1; - } else { - res = a->expn - 1; - } - break; - case 1: /* ctz */ - if (bf_is_zero(a)) { - res = -1; - } else { - res = bf_get_exp_min(a); - } - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - return JS_NewBigInt64(ctx, res); -} -#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); -} - -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)); -} - -#ifdef CONFIG_BIGNUM - -/* BigFloat */ - -static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigFloat(this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_FLOAT) { - if (JS_IsBigFloat(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigfloat"); -} - -static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int base; - JSValue ret; - - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (argc == 0 || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; - } - ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigFloatValue(ctx, this_val); -} - -static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) -{ - int rnd_mode; - if (JS_ToInt32Sat(ctx, &rnd_mode, val)) - return -1; - if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) { - JS_ThrowRangeError(ctx, "invalid rounding mode"); - return -1; - } - return rnd_mode; -} - -static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode, radix; - - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - radix = 10; - /* XXX: swap parameter order for rounding mode and radix */ - if (argc > 1) { - rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - if (argc > 2) { - radix = js_get_radix(ctx, argv[2]); - if (radix < 0) - goto fail; - } - ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val) -{ - BOOL res; - uint32_t tag; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - res = bf_is_finite(&p->num); - } - break; - default: - res = FALSE; - break; - } - return res; -} - -static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode, radix; - - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (!js_bigfloat_is_finite(ctx, val)) { - ret = JS_ToString(ctx, val); - } else if (JS_IsUndefined(argv[0])) { - ret = js_ftoa(ctx, val, 10, 0, - BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); - } else { - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - radix = 10; - if (argc > 1) { - rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - if (argc > 2) { - radix = js_get_radix(ctx, argv[2]); - if (radix < 0) - goto fail; - } - ret = js_ftoa(ctx, val, radix, f + 1, - rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); - } - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t p; - int rnd_mode, radix; - - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_IsUndefined(argv[0])) - goto to_string; - if (JS_ToInt64Sat(ctx, &p, argv[0])) - goto fail; - if (!js_bigfloat_is_finite(ctx, val)) { - to_string: - ret = JS_ToString(ctx, this_val); - } else { - if (p < 1 || p > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - radix = 10; - if (argc > 1) { - rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - if (argc > 2) { - radix = js_get_radix(ctx, argv[2]); - if (radix < 0) - goto fail; - } - ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED); - } - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), - JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), - JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), - JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), -}; - -static JSValue js_bigfloat_constructor(JSContext *ctx, - JSValueConst 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; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - bf_t *r; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - bf_set_zero(r, 0); - err = 0; - } else { - val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | - ATOD_TYPE_BIG_FLOAT | - ATOD_ACCEPT_PREFIX_AFTER_SIGN); - if (JS_IsException(val)) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - p += skip_spaces(p); - err = ((p - str) != len); - } - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal"); - } - } - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigfloat"); - } - } - return val; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigfloat_get_const(JSContext *ctx, - JSValueConst this_val, int magic) -{ - bf_t *r; - JSValue val; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - r = JS_GetBigFloat(val); - switch(magic) { - case 0: /* PI */ - bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case 1: /* LN2 */ - bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case 2: /* MIN_VALUE */ - case 3: /* MAX_VALUE */ - { - slimb_t e_range, e; - e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1); - bf_set_ui(r, 1); - if (magic == 2) { - e = -e_range + 2; - if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL) - e -= ctx->fp_env.prec - 1; - bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags); - } else { - bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec, - ctx->fp_env.flags); - bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags); - bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec, - ctx->fp_env.flags); - } - } - break; - case 4: /* EPSILON */ - bf_set_ui(r, 1); - bf_mul_2exp(r, 1 - ctx->fp_env.prec, - ctx->fp_env.prec, ctx->fp_env.flags); - break; - default: - abort(); - } - return val; -} - -static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - bf_t *a; - const char *str; - JSValue ret; - int radix; - JSFloatEnv *fe; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &radix, argv[1])) { - fail: - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } - fe = &ctx->fp_env; - if (argc > 2) { - fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); - if (!fe) - goto fail; - } - ret = JS_NewBigFloat(ctx); - if (JS_IsException(ret)) - goto done; - a = JS_GetBigFloat(ret); - /* XXX: use js_atof() */ - bf_atof(a, str, NULL, radix, fe->prec, fe->flags); - done: - JS_FreeCString(ctx, str); - return ret; -} - -static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst val = argv[0]; - JSBigFloat *p; - - if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) - return JS_FALSE; - p = JS_VALUE_GET_PTR(val); - return JS_NewBool(ctx, bf_is_finite(&p->num)); -} - -static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst val = argv[0]; - JSBigFloat *p; - - if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) - return JS_FALSE; - p = JS_VALUE_GET_PTR(val); - return JS_NewBool(ctx, bf_is_nan(&p->num)); -} - -enum { - MATH_OP_ABS, - MATH_OP_FLOOR, - MATH_OP_CEIL, - MATH_OP_ROUND, - MATH_OP_TRUNC, - MATH_OP_SQRT, - MATH_OP_FPROUND, - MATH_OP_ACOS, - MATH_OP_ASIN, - MATH_OP_ATAN, - MATH_OP_ATAN2, - MATH_OP_COS, - MATH_OP_EXP, - MATH_OP_LOG, - MATH_OP_POW, - MATH_OP_SIN, - MATH_OP_TAN, - MATH_OP_FMOD, - MATH_OP_REM, - MATH_OP_SIGN, - - MATH_OP_ADD, - MATH_OP_SUB, - MATH_OP_MUL, - MATH_OP_DIV, -}; - -static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, *r; - JSFloatEnv *fe; - int rnd_mode; - JSValue op1, res; - - op1 = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigFloat(ctx, &a_s, op1); - 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; -} - -static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; - JSFloatEnv *fe; - JSValue op1, op2, res; - - op1 = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - op2 = JS_ToNumeric(ctx, argv[1]); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return op2; - } - a = JS_ToBigFloat(ctx, &a_s, op1); - 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; - } - r = JS_GetBigFloat(res); - switch (magic) { - case MATH_OP_ATAN2: - fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_POW: - fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); - break; - case MATH_OP_FMOD: - fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); - break; - case MATH_OP_REM: - fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); - break; - case MATH_OP_ADD: - fe->status |= bf_add(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_SUB: - fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_MUL: - fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_DIV: - fe->status |= bf_div(r, a, b, fe->prec, fe->flags); - break; - default: - abort(); - } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return res; -} - -static const JSCFunctionListEntry js_bigfloat_funcs[] = { - JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ), - JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ), - JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ), - JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ), - JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ), - JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ), - JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ), - JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ), - JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ), - JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ), - JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ), - JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ), - JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ), - JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ), - JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ), - JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ), - JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ), - JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ), - JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ), - JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ), - JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ), - JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ), - JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ), - JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ), - JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ), - JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ), - JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ), - JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ), - JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ), -}; - -/* FloatEnv */ - -static JSValue js_float_env_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue obj; - JSFloatEnv *fe; - int64_t prec; - int flags, rndmode; - - prec = ctx->fp_env.prec; - flags = ctx->fp_env.flags; - if (!JS_IsUndefined(argv[0])) { - if (JS_ToInt64Sat(ctx, &prec, argv[0])) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */ - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt32Sat(ctx, &rndmode, argv[1])) - return JS_EXCEPTION; - if (rndmode < BF_RNDN || rndmode > BF_RNDF) - return JS_ThrowRangeError(ctx, "invalid rounding mode"); - flags = rndmode; - } - } - - obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); - if (JS_IsException(obj)) - return JS_EXCEPTION; - fe = js_malloc(ctx, sizeof(*fe)); - if (!fe) - return JS_EXCEPTION; - fe->prec = prec; - fe->flags = flags; - fe->status = 0; - JS_SetOpaque(obj, fe); - return obj; -} - -static void js_float_env_finalizer(JSRuntime *rt, JSValue val) -{ - JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); - js_free_rt(rt, fe); -} - -static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt64(ctx, ctx->fp_env.prec); -} - -static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); -} - -static JSValue js_float_env_setPrec(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst func; - int exp_bits, flags, saved_flags; - JSValue ret; - limb_t saved_prec; - int64_t prec; - - func = argv[0]; - if (JS_ToInt64Sat(ctx, &prec, argv[1])) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - exp_bits = BF_EXP_BITS_MAX; - - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) - return JS_EXCEPTION; - if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) - return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - } - - flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); - - saved_prec = ctx->fp_env.prec; - saved_flags = ctx->fp_env.flags; - - ctx->fp_env.prec = prec; - ctx->fp_env.flags = flags; - - ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); - /* always restore the floating point precision */ - ctx->fp_env.prec = saved_prec; - ctx->fp_env.flags = saved_flags; - return ret; -} - -#define FE_PREC (-1) -#define FE_EXP (-2) -#define FE_RNDMODE (-3) -#define FE_SUBNORMAL (-4) - -static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) -{ - JSFloatEnv *fe; - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - switch(magic) { - case FE_PREC: - return JS_NewInt64(ctx, fe->prec); - case FE_EXP: - return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); - case FE_RNDMODE: - return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); - case FE_SUBNORMAL: - return JS_NewBool(ctx, fe->flags & BF_FLAG_SUBNORMAL); - default: - return JS_NewBool(ctx, fe->status & magic); - } -} - -static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) -{ - JSFloatEnv *fe; - int b; - int64_t prec; - - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - switch(magic) { - case FE_PREC: - if (JS_ToInt64Sat(ctx, &prec, val)) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - fe->prec = prec; - break; - case FE_EXP: - if (JS_ToInt32Sat(ctx, &b, val)) - return JS_EXCEPTION; - if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) - return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | - bf_set_exp_bits(b); - break; - case FE_RNDMODE: - b = bigfloat_get_rnd_mode(ctx, val); - if (b < 0) - return JS_EXCEPTION; - fe->flags = (fe->flags & ~BF_RND_MASK) | b; - break; - case FE_SUBNORMAL: - b = JS_ToBool(ctx, val); - fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); - break; - default: - b = JS_ToBool(ctx, val); - fe->status = (fe->status & ~magic) & ((-b) & magic); - break; - } - return JS_UNDEFINED; -} - -static JSValue js_float_env_clearStatus(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - fe->status = 0; - return JS_UNDEFINED; -} - -static const JSCFunctionListEntry js_float_env_funcs[] = { - JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ), - JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ), - JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ), - JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ), - JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ), - JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), - JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), - JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), - JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), - JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), - JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), - JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), - JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), - JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), -}; - -static const JSCFunctionListEntry js_float_env_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_PREC ), - JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_EXP ), - JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_RNDMODE ), - JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_SUBNORMAL ), - JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_INVALID_OP ), - JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), - JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_OVERFLOW ), - JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_UNDERFLOW ), - JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_INEXACT ), - JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), -}; - -void JS_AddIntrinsicBigFloat(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - 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) -{ - redo: - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_BIG_DECIMAL: - break; - case JS_TAG_NULL: - if (!allow_null_or_undefined) - goto fail; - /* fall thru */ - case JS_TAG_BOOL: - case JS_TAG_INT: - { - bfdec_t *r; - int32_t v = JS_VALUE_GET_INT(val); - - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigDecimal(val); - if (bfdec_set_si(r, v)) { - JS_FreeValue(ctx, val); - val = JS_EXCEPTION; - break; - } - } - break; - case JS_TAG_FLOAT64: - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_STRING: - { - const char *str, *p; - size_t len; - int err; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - bfdec_t *r; - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigDecimal(val); - bfdec_set_zero(r, 0); - err = 0; - } else { - val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL); - if (JS_IsException(val)) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - p += skip_spaces(p); - err = ((p - str) != len); - } - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal"); - } - } - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_UNDEFINED: - { - bfdec_t *r; - if (!allow_null_or_undefined) - goto fail; - val = JS_NewBigDecimal(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigDecimal(val); - bfdec_set_nan(r); - } - break; - default: - fail: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal"); - } - return val; -} - -static JSValue js_bigdecimal_constructor(JSContext *ctx, - JSValueConst 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; -} - -static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigDecimal(this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_DECIMAL) { - if (JS_IsBigDecimal(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigdecimal"); -} - -static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - return JS_ToStringFree(ctx, val); -} - -static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigDecimalValue(ctx, this_val); -} - -static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj) -{ - const char *str; - size_t size; - int rnd_mode; - - str = JS_ToCStringLen(ctx, &size, obj); - if (!str) - return -1; - if (strlen(str) != size) - goto invalid_rounding_mode; - if (!strcmp(str, "floor")) { - rnd_mode = BF_RNDD; - } else if (!strcmp(str, "ceiling")) { - rnd_mode = BF_RNDU; - } else if (!strcmp(str, "down")) { - rnd_mode = BF_RNDZ; - } else if (!strcmp(str, "up")) { - rnd_mode = BF_RNDA; - } else if (!strcmp(str, "half-even")) { - rnd_mode = BF_RNDN; - } else if (!strcmp(str, "half-up")) { - rnd_mode = BF_RNDNA; - } else { - invalid_rounding_mode: - JS_FreeCString(ctx, str); - JS_ThrowTypeError(ctx, "invalid rounding mode"); - return -1; - } - JS_FreeCString(ctx, str); - return rnd_mode; -} - -typedef struct { - int64_t prec; - bf_flags_t flags; -} BigDecimalEnv; - -static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, - JSValueConst obj) -{ - JSValue prop; - int64_t val; - BOOL has_prec; - int rnd_mode; - - if (!JS_IsObject(obj)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); - if (JS_IsException(prop)) - return -1; - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop); - JS_FreeValue(ctx, prop); - if (rnd_mode < 0) - return -1; - fe->flags = rnd_mode; - - prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits); - if (JS_IsException(prop)) - return -1; - has_prec = FALSE; - if (!JS_IsUndefined(prop)) { - if (JS_ToInt64SatFree(ctx, &val, prop)) - return -1; - if (val < 1 || val > BF_PREC_MAX) - goto invalid_precision; - fe->prec = val; - has_prec = TRUE; - } - - prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits); - if (JS_IsException(prop)) - return -1; - if (!JS_IsUndefined(prop)) { - if (has_prec) { - JS_FreeValue(ctx, prop); - JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits"); - return -1; - } - if (JS_ToInt64SatFree(ctx, &val, prop)) - return -1; - if (val < 0 || val > BF_PREC_MAX) { - invalid_precision: - JS_ThrowTypeError(ctx, "invalid precision"); - return -1; - } - fe->prec = val; - fe->flags |= BF_FLAG_RADPNT_PREC; - has_prec = TRUE; - } - if (!has_prec) { - JS_ThrowTypeError(ctx, "precision must be present"); - return -1; - } - return 0; -} - - -static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bfdec_t *a, *b, r_s, *r = &r_s; - JSValue op1, op2, res; - BigDecimalEnv fe_s, *fe = &fe_s; - int op_count, ret; - - if (magic == MATH_OP_SQRT || - magic == MATH_OP_ROUND) - op_count = 1; - else - op_count = 2; - - op1 = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigDecimal(ctx, op1); - if (!a) { - JS_FreeValue(ctx, op1); - return JS_EXCEPTION; - } - if (op_count >= 2) { - op2 = JS_ToNumeric(ctx, argv[1]); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - return op2; - } - b = JS_ToBigDecimal(ctx, op2); - if (!b) - goto fail; - } else { - op2 = JS_UNDEFINED; - b = NULL; - } - fe->flags = BF_RNDZ; - fe->prec = BF_PREC_INF; - if (op_count < argc) { - if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) - goto fail; - } - - res = JS_NewBigDecimal(ctx); - if (JS_IsException(res)) { - fail: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - return JS_EXCEPTION; - } - r = JS_GetBigDecimal(res); - switch (magic) { - case MATH_OP_ADD: - ret = bfdec_add(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_SUB: - ret = bfdec_sub(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_MUL: - ret = bfdec_mul(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_DIV: - ret = bfdec_div(r, a, b, fe->prec, fe->flags); - break; - case MATH_OP_FMOD: - ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); - break; - case MATH_OP_SQRT: - ret = bfdec_sqrt(r, a, fe->prec, fe->flags); - break; - case MATH_OP_ROUND: - ret = bfdec_set(r, a); - if (!(ret & BF_ST_MEM_ERROR)) - ret = bfdec_round(r, fe->prec, fe->flags); - break; - default: - abort(); - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP | - BF_ST_OVERFLOW; - if (ret != 0) { - JS_FreeValue(ctx, res); - return throw_bf_exception(ctx, ret); - } else { - return res; - } -} - -static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (argc > 1) { - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t f; - int rnd_mode; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (JS_IsUndefined(argv[0])) { - ret = js_bigdecimal_to_string1(ctx, val, 0, - BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); - } else { - if (f < 0 || f > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (argc > 1) { - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - ret = js_bigdecimal_to_string1(ctx, val, f + 1, - rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); - } - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - int64_t p; - int rnd_mode; - - val = js_thisBigDecimalValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_IsUndefined(argv[0])) { - return JS_ToStringFree(ctx, val); - } - if (JS_ToInt64Sat(ctx, &p, argv[0])) - goto fail; - if (p < 1 || p > BF_PREC_MAX) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (argc > 1) { - rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - ret = js_bigdecimal_to_string1(ctx, val, p, - rnd_mode | BF_FTOA_FORMAT_FIXED); - JS_FreeValue(ctx, val); - return ret; - fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), - JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ), - JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ), - JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ), -}; - -static const JSCFunctionListEntry js_bigdecimal_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ), - JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ), - JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ), - JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ), - JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ), - JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), -}; - -void JS_AddIntrinsicBigDecimal(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - 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; -} - -#endif /* CONFIG_BIGNUM */ - -static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = { - "EvalError", "RangeError", "ReferenceError", - "SyntaxError", "TypeError", "URIError", - "InternalError", "AggregateError", -}; - -/* Minimum amount of objects to be able to compile code and display - error messages. No JSAtom should be allocated by this function. */ -static void JS_AddIntrinsicBasicObjects(JSContext *ctx) -{ - JSValue proto; - int i; - - ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); - ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, - JS_CFUNC_generic, 0, - ctx->class_proto[JS_CLASS_OBJECT]); - ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto); - ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx); -#if 0 - /* these are auto-initialized from js_error_proto_funcs, - but delaying might be a problem */ - JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name, - JS_AtomToString(ctx, JS_ATOM_Error), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message, - JS_AtomToString(ctx, JS_ATOM_empty_string), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); -#endif - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR], - js_error_proto_funcs, - countof(js_error_proto_funcs)); - - for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { - proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]); - JS_DefinePropertyValue(ctx, proto, JS_ATOM_name, - JS_NewAtomString(ctx, native_error_name[i]), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - JS_DefinePropertyValue(ctx, proto, JS_ATOM_message, - JS_AtomToString(ctx, JS_ATOM_empty_string), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - ctx->native_error_proto[i] = proto; - } - - /* the array prototype is an array */ - ctx->class_proto[JS_CLASS_ARRAY] = - JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_ARRAY); - - ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), - JS_PROP_INITIAL_HASH_SIZE, 1); - add_shape_property(ctx, &ctx->array_shape, NULL, - JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH); - - /* XXX: could test it on first context creation to ensure that no - new atoms are created in JS_AddIntrinsicBasicObjects(). It is - necessary to avoid useless renumbering of atoms after - JS_EvalBinary() if it is done just after - JS_AddIntrinsicBasicObjects(). */ - // assert(ctx->rt->atom_count == JS_ATOM_END); -} - -void JS_AddIntrinsicBaseObjects(JSContext *ctx) -{ - int i; - JSValueConst obj, number_obj; - JSValue obj1; - - ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); - - /* add caller and arguments properties to throw a TypeError */ - obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0); - JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, - obj1, ctx->throw_type_error, - JS_PROP_HAS_GET | JS_PROP_HAS_SET | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); - JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, - obj1, ctx->throw_type_error, - JS_PROP_HAS_GET | JS_PROP_HAS_SET | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); - - ctx->global_obj = JS_NewObject(ctx); - ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); - - /* Object */ - obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1, - ctx->class_proto[JS_CLASS_OBJECT]); - JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs)); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT], - js_object_proto_funcs, countof(js_object_proto_funcs)); - - /* Function */ - JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs)); - ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor, - "Function", 1, JS_CFUNC_constructor_or_func_magic, - JS_FUNC_NORMAL); - JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function", - ctx->function_proto); - - /* Error */ - obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor, - "Error", 1, JS_CFUNC_constructor_or_func_magic, -1); - JS_NewGlobalCConstructor2(ctx, obj1, - "Error", ctx->class_proto[JS_CLASS_ERROR]); - - /* 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, 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], - ctx->native_error_proto[i]); - } - - /* Iterator prototype */ - ctx->iterator_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->iterator_proto, - js_iterator_proto_funcs, - countof(js_iterator_proto_funcs)); - - /* Array */ - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY], - js_array_proto_funcs, - countof(js_array_proto_funcs)); - - obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1, - ctx->class_proto[JS_CLASS_ARRAY]); - ctx->array_ctor = JS_DupValue(ctx, obj); - JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, - countof(js_array_funcs)); - - /* XXX: create auto_initializer */ - { - /* initialize Array.prototype[Symbol.unscopables] */ - static const char unscopables[] = - "copyWithin" "\0" - "entries" "\0" - "fill" "\0" - "find" "\0" - "findIndex" "\0" - "findLast" "\0" - "findLastIndex" "\0" - "flat" "\0" - "flatMap" "\0" - "includes" "\0" - "keys" "\0" - "toReversed" "\0" - "toSorted" "\0" - "toSpliced" "\0" - "values" "\0"; - const char *p = unscopables; - obj1 = JS_NewObjectProto(ctx, JS_NULL); - for(p = unscopables; *p; p += strlen(p) + 1) { - JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E); - } - JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY], - JS_ATOM_Symbol_unscopables, obj1, - JS_PROP_CONFIGURABLE); - } - - /* needed to initialize arguments[Symbol.iterator] */ - ctx->array_proto_values = - JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); - - ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR], - js_array_iterator_proto_funcs, - countof(js_array_iterator_proto_funcs)); - - /* parseFloat and parseInteger must be defined before Number - because of the Number.parseFloat and Number.parseInteger - aliases */ - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs, - countof(js_global_funcs)); - - /* Number */ - ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_NUMBER); - JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0)); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER], - js_number_proto_funcs, - countof(js_number_proto_funcs)); - number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1, - ctx->class_proto[JS_CLASS_NUMBER]); - JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs)); - - /* Boolean */ - ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_BOOLEAN); - JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE)); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs, - countof(js_boolean_proto_funcs)); - JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1, - ctx->class_proto[JS_CLASS_BOOLEAN]); - - /* String */ - ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], - JS_CLASS_STRING); - JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string)); - obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1, - ctx->class_proto[JS_CLASS_STRING]); - JS_SetPropertyFunctionList(ctx, obj, js_string_funcs, - countof(js_string_funcs)); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs, - countof(js_string_proto_funcs)); - - ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR], - js_string_iterator_proto_funcs, - countof(js_string_iterator_proto_funcs)); - - /* Math: create as autoinit object */ - js_random_init(ctx); - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj)); - - /* ES6 Reflect: create as autoinit object */ - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj)); - - /* ES6 Symbol */ - ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs, - countof(js_symbol_proto_funcs)); - obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0, - ctx->class_proto[JS_CLASS_SYMBOL]); - JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs, - countof(js_symbol_funcs)); - for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) { - char buf[ATOM_GET_STR_BUF_SIZE]; - const char *str, *p; - str = JS_AtomGetStr(ctx, buf, sizeof(buf), i); - /* skip "Symbol." */ - p = strchr(str, '.'); - if (p) - str = p + 1; - JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0); - } - - /* ES6 Generator */ - ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR], - js_generator_proto_funcs, - countof(js_generator_proto_funcs)); - - ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor, - "GeneratorFunction", 1, - JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], - js_generator_function_proto_funcs, - countof(js_generator_function_proto_funcs)); - JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], - ctx->class_proto[JS_CLASS_GENERATOR], - JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); - JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], - 0, JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); - - /* global properties */ - ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1); - JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval, - JS_DupValue(ctx, ctx->eval_obj), - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - - JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, - JS_DupValue(ctx, ctx->global_obj), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); -} - -/* Typed Arrays */ - -static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { - 0, 0, 0, 1, 1, 2, 2, - 3, 3, /* BigInt64Array, BigUint64Array */ - 2, 3 -}; - -static JSValue js_array_buffer_constructor3(JSContext *ctx, - JSValueConst new_target, - uint64_t len, JSClassID class_id, - uint8_t *buf, - JSFreeArrayBufferDataFunc *free_func, - void *opaque, BOOL alloc_flag) -{ - JSRuntime *rt = ctx->rt; - JSValue obj; - JSArrayBuffer *abuf = NULL; - - obj = js_create_from_ctor(ctx, new_target, class_id); - if (JS_IsException(obj)) - return obj; - /* XXX: we are currently limited to 2 GB */ - if (len > INT32_MAX) { - JS_ThrowRangeError(ctx, "invalid array buffer length"); - goto fail; - } - abuf = js_malloc(ctx, sizeof(*abuf)); - if (!abuf) - goto fail; - abuf->byte_length = len; - if (alloc_flag) { - if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && - rt->sab_funcs.sab_alloc) { - abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, - max_int(len, 1)); - if (!abuf->data) - goto fail; - memset(abuf->data, 0, len); - } else { - /* the allocation must be done after the object creation */ - abuf->data = js_mallocz(ctx, max_int(len, 1)); - if (!abuf->data) - goto fail; - } - } else { - if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && - rt->sab_funcs.sab_dup) { - rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); - } - abuf->data = buf; - } - init_list_head(&abuf->array_list); - abuf->detached = FALSE; - abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); - abuf->opaque = opaque; - abuf->free_func = free_func; - if (alloc_flag && buf) - memcpy(abuf->data, buf, len); - JS_SetOpaque(obj, abuf); - return obj; - fail: - JS_FreeValue(ctx, obj); - js_free(ctx, abuf); - return JS_EXCEPTION; -} - -static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) -{ - js_free_rt(rt, ptr); -} - -static JSValue js_array_buffer_constructor2(JSContext *ctx, - JSValueConst new_target, - uint64_t len, JSClassID class_id) -{ - return js_array_buffer_constructor3(ctx, new_target, len, class_id, - NULL, js_array_buffer_free, NULL, - TRUE); -} - -static JSValue js_array_buffer_constructor1(JSContext *ctx, - JSValueConst new_target, - uint64_t len) -{ - return js_array_buffer_constructor2(ctx, new_target, len, - JS_CLASS_ARRAY_BUFFER); -} - -JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, - JSFreeArrayBufferDataFunc *free_func, void *opaque, - BOOL is_shared) -{ - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, - is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, - buf, free_func, opaque, FALSE); -} - -/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ -JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) -{ - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, - JS_CLASS_ARRAY_BUFFER, - (uint8_t *)buf, - js_array_buffer_free, NULL, - TRUE); -} - -static JSValue js_array_buffer_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - uint64_t len; - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - return js_array_buffer_constructor1(ctx, new_target, len); -} - -static JSValue js_shared_array_buffer_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - uint64_t len; - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - return js_array_buffer_constructor2(ctx, new_target, len, - JS_CLASS_SHARED_ARRAY_BUFFER); -} - -/* also used for SharedArrayBuffer */ -static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSArrayBuffer *abuf = p->u.array_buffer; - 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. */ - 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 { - if (abuf->free_func) - abuf->free_func(rt, abuf->opaque, abuf->data); - } - js_free_rt(rt, abuf); - } -} - -static JSValue js_array_buffer_isView(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - BOOL res; - res = FALSE; - if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(argv[0]); - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_DATAVIEW) { - res = TRUE; - } - } - return JS_NewBool(ctx, res); -} - -static const JSCFunctionListEntry js_array_buffer_funcs[] = { - JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), -}; - -static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); -} - -static JSValue js_array_buffer_get_byteLength(JSContext *ctx, - JSValueConst this_val, - int class_id) -{ - JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); - if (!abuf) - return JS_EXCEPTION; - /* return 0 if detached */ - return JS_NewUint32(ctx, abuf->byte_length); -} - -void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) -{ - JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); - struct list_head *el; - - if (!abuf || abuf->detached) - return; - if (abuf->free_func) - abuf->free_func(ctx->rt, abuf->opaque, abuf->data); - abuf->data = NULL; - abuf->byte_length = 0; - abuf->detached = TRUE; - - list_for_each(el, &abuf->array_list) { - JSTypedArray *ta; - JSObject *p; - - ta = list_entry(el, JSTypedArray, link); - p = ta->obj; - /* Note: the typed array length and offset fields are not modified */ - if (p->class_id != JS_CLASS_DATAVIEW) { - p->u.array.count = 0; - p->u.array.u.ptr = NULL; - } - } -} - -/* get an ArrayBuffer or SharedArrayBuffer */ -static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ARRAY_BUFFER && - p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { - fail: - JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); - return NULL; - } - return p->u.array_buffer; -} - -/* return NULL if exception. WARNING: any JS call can detach the - buffer and render the returned pointer invalid */ -uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj) -{ - JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj); - if (!abuf) - goto fail; - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - *psize = abuf->byte_length; - return abuf->data; - fail: - *psize = 0; - return NULL; -} - -static JSValue js_array_buffer_slice(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int class_id) -{ - JSArrayBuffer *abuf, *new_abuf; - int64_t len, start, end, new_len; - JSValue ctor, new_obj; - - abuf = JS_GetOpaque2(ctx, this_val, class_id); - if (!abuf) - return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - len = abuf->byte_length; - - if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) - return JS_EXCEPTION; - - end = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) - return JS_EXCEPTION; - } - new_len = max_int64(end - start, 0); - ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - if (JS_IsUndefined(ctor)) { - new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, - class_id); - } else { - JSValue args[1]; - args[0] = JS_NewInt64(ctx, new_len); - new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args); - JS_FreeValue(ctx, ctor); - JS_FreeValue(ctx, args[0]); - } - if (JS_IsException(new_obj)) - return new_obj; - new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); - if (!new_abuf) - goto fail; - if (js_same_value(ctx, new_obj, this_val)) { - JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); - goto fail; - } - if (new_abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - if (new_abuf->byte_length < new_len) { - JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); - goto fail; - } - /* must test again because of side effects */ - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - memcpy(new_abuf->data, abuf->data + start, new_len); - return new_obj; - fail: - JS_FreeValue(ctx, new_obj); - return JS_EXCEPTION; -} - -static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ), - JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ), -}; - -/* SharedArrayBuffer */ - -static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), -}; - -static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), - JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ), -}; - -static JSObject *get_typed_array(JSContext *ctx, - JSValueConst this_val, - int is_dataview) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(this_val); - if (is_dataview) { - if (p->class_id != JS_CLASS_DATAVIEW) - goto fail; - } else { - if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { - fail: - JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray"); - return NULL; - } - } - return p; -} - -/* WARNING: 'p' must be a typed array */ -static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p) -{ - JSTypedArray *ta = p->u.typed_array; - JSArrayBuffer *abuf = ta->buffer->u.array_buffer; - /* XXX: could simplify test by ensuring that - p->u.array.u.ptr is NULL iff it is detached */ - return abuf->detached; -} - -/* WARNING: 'p' must be a typed array. Works even if the array buffer - is detached */ -static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p) -{ - JSTypedArray *ta = p->u.typed_array; - int size_log2 = typed_array_size_log2(p->class_id); - return ta->length >> size_log2; -} - -static int validate_typed_array(JSContext *ctx, JSValueConst this_val) -{ - JSObject *p; - p = get_typed_array(ctx, this_val, 0); - if (!p) - return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } - return 0; -} - -static JSValue js_typed_array_get_length(JSContext *ctx, - JSValueConst this_val) -{ - JSObject *p; - p = get_typed_array(ctx, this_val, 0); - if (!p) - return JS_EXCEPTION; - return JS_NewInt32(ctx, p->u.array.count); -} - -static JSValue js_typed_array_get_buffer(JSContext *ctx, - JSValueConst this_val, int is_dataview) -{ - JSObject *p; - JSTypedArray *ta; - p = get_typed_array(ctx, this_val, is_dataview); - if (!p) - return JS_EXCEPTION; - ta = p->u.typed_array; - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); -} - -static JSValue js_typed_array_get_byteLength(JSContext *ctx, - JSValueConst this_val, - int is_dataview) -{ - JSObject *p; - JSTypedArray *ta; - p = get_typed_array(ctx, this_val, is_dataview); - if (!p) - return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) { - if (is_dataview) { - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } else { - return JS_NewInt32(ctx, 0); - } - } - ta = p->u.typed_array; - return JS_NewInt32(ctx, ta->length); -} - -static JSValue js_typed_array_get_byteOffset(JSContext *ctx, - JSValueConst this_val, - int is_dataview) -{ - JSObject *p; - JSTypedArray *ta; - p = get_typed_array(ctx, this_val, is_dataview); - if (!p) - return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) { - if (is_dataview) { - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } else { - return JS_NewInt32(ctx, 0); - } - } - ta = p->u.typed_array; - return JS_NewInt32(ctx, ta->offset); -} - -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. */ -JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, - size_t *pbyte_offset, - size_t *pbyte_length, - size_t *pbytes_per_element) -{ - JSObject *p; - JSTypedArray *ta; - p = get_typed_array(ctx, obj, FALSE); - if (!p) - return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - ta = p->u.typed_array; - if (pbyte_offset) - *pbyte_offset = ta->offset; - if (pbyte_length) - *pbyte_length = ta->length; - if (pbytes_per_element) { - *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); - } - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); -} - -static JSValue js_typed_array_get_toStringTag(JSContext *ctx, - JSValueConst this_val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_UNDEFINED; - p = JS_VALUE_GET_OBJ(this_val); - if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY)) - return JS_UNDEFINED; - return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); -} - -static JSValue js_typed_array_set_internal(JSContext *ctx, - JSValueConst dst, - JSValueConst src, - JSValueConst off) -{ - JSObject *p; - JSObject *src_p; - uint32_t i; - int64_t src_len, offset; - JSValue val, src_obj = JS_UNDEFINED; - - p = get_typed_array(ctx, dst, 0); - if (!p) - goto fail; - if (JS_ToInt64Sat(ctx, &offset, off)) - goto fail; - if (offset < 0) - goto range_error; - if (typed_array_is_detached(ctx, p)) { - detached: - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - src_obj = JS_ToObject(ctx, src); - if (JS_IsException(src_obj)) - goto fail; - src_p = JS_VALUE_GET_OBJ(src_obj); - if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && - src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - JSTypedArray *dest_ta = p->u.typed_array; - JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer; - JSTypedArray *src_ta = src_p->u.typed_array; - JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer; - int shift = typed_array_size_log2(p->class_id); - - if (src_abuf->detached) - goto detached; - - src_len = src_p->u.array.count; - if (offset > (int64_t)(p->u.array.count - src_len)) - goto range_error; - - /* copying between typed objects */ - if (src_p->class_id == p->class_id) { - /* same type, use memmove */ - memmove(dest_abuf->data + dest_ta->offset + (offset << shift), - src_abuf->data + src_ta->offset, src_len << shift); - goto done; - } - if (dest_abuf->data == src_abuf->data) { - /* copying between the same buffer using different types of mappings - would require a temporary buffer */ - } - /* otherwise, default behavior is slow but correct */ - } else { - if (js_get_length64(ctx, &src_len, src_obj)) - goto fail; - if (offset > (int64_t)(p->u.array.count - src_len)) { - range_error: - JS_ThrowRangeError(ctx, "invalid array length"); - goto fail; - } - } - for(i = 0; i < src_len; i++) { - val = JS_GetPropertyUint32(ctx, src_obj, i); - if (JS_IsException(val)) - goto fail; - if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) - goto fail; - } -done: - JS_FreeValue(ctx, src_obj); - return JS_UNDEFINED; -fail: - JS_FreeValue(ctx, src_obj); - return JS_EXCEPTION; -} - -static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - int64_t idx, len; - - p = get_typed_array(ctx, this_val, 0); - if (!p) - return JS_EXCEPTION; - - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return JS_EXCEPTION; - } - - if (JS_ToInt64Sat(ctx, &idx, argv[0])) - return JS_EXCEPTION; - - len = p->u.array.count; - if (idx < 0) - idx = len + idx; - if (idx < 0 || idx >= len) - return JS_UNDEFINED; - 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 (JS_ToInt64Sat(ctx, &idx, argv[0])) - return JS_EXCEPTION; - - len = p->u.array.count; - if (idx < 0) - idx = len + idx; - if (idx < 0 || idx >= len) - return JS_ThrowRangeError(ctx, "invalid array index"); - - val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); - if (JS_IsException(val)) - return JS_EXCEPTION; - - 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) -{ - JSValueConst offset = JS_UNDEFINED; - if (argc > 1) { - offset = argv[1]; - } - return js_typed_array_set_internal(ctx, this_val, argv[0], offset); -} - -static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - if (validate_typed_array(ctx, this_val)) - return JS_EXCEPTION; - return js_create_array_iterator(ctx, this_val, argc, argv, magic); -} - -/* return < 0 if exception */ -static int js_typed_array_get_length_internal(JSContext *ctx, - JSValueConst obj) -{ - JSObject *p; - p = get_typed_array(ctx, obj, 0); - if (!p) - return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } - return p->u.array.count; -} - -#if 0 -/* validate a typed array and return its length */ -static JSValue js_typed_array___getLength(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - BOOL ignore_detached = JS_ToBool(ctx, argv[1]); - - if (ignore_detached) { - return js_typed_array_get_length(ctx, argv[0]); - } else { - int len; - len = js_typed_array_get_length_internal(ctx, argv[0]); - if (len < 0) - return JS_EXCEPTION; - return JS_NewInt32(ctx, len); - } -} -#endif - -static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor, - int argc, JSValueConst *argv) -{ - JSValue ret; - int new_len; - int64_t len; - - ret = JS_CallConstructor(ctx, ctor, argc, argv); - if (JS_IsException(ret)) - return ret; - /* validate the typed array */ - new_len = js_typed_array_get_length_internal(ctx, ret); - if (new_len < 0) - goto fail; - if (argc == 1) { - /* ensure that it is large enough */ - if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) - goto fail; - if (new_len < len) { - JS_ThrowTypeError(ctx, "TypedArray length is too small"); - fail: - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - } - return ret; -} - -#if 0 -static JSValue js_typed_array___create(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1); -} -#endif - -static JSValue js_typed_array___speciesCreate(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj; - JSObject *p; - JSValue ctor, ret; - int argc1; - - obj = argv[0]; - p = get_typed_array(ctx, obj, 0); - if (!p) - return JS_EXCEPTION; - ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - argc1 = max_int(argc - 1, 0); - if (JS_IsUndefined(ctor)) { - ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1, - p->class_id); - } else { - ret = js_typed_array_create(ctx, ctor, argc1, argv + 1); - JS_FreeValue(ctx, ctor); - } - return ret; -} - -static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // from(items, mapfn = void 0, this_arg = void 0) - JSValueConst items = argv[0], mapfn, this_arg; - JSValueConst args[2]; - JSValue stack[2]; - JSValue iter, arr, r, v, v2; - int64_t k, len; - int done, mapping; - - mapping = FALSE; - mapfn = JS_UNDEFINED; - this_arg = JS_UNDEFINED; - r = JS_UNDEFINED; - arr = JS_UNDEFINED; - stack[0] = JS_UNDEFINED; - stack[1] = JS_UNDEFINED; - - if (argc > 1) { - mapfn = argv[1]; - if (!JS_IsUndefined(mapfn)) { - if (check_function(ctx, mapfn)) - goto exception; - mapping = 1; - if (argc > 2) - this_arg = argv[2]; - } - } - iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) - goto exception; - if (!JS_IsUndefined(iter)) { - JS_FreeValue(ctx, iter); - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - goto exception; - stack[0] = JS_DupValue(ctx, items); - if (js_for_of_start(ctx, &stack[1], FALSE)) - goto exception; - for (k = 0;; k++) { - v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); - if (JS_IsException(v)) - goto exception_close; - if (done) - break; - if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception_close; - } - } else { - arr = JS_ToObject(ctx, items); - if (JS_IsException(arr)) - goto exception; - } - if (js_get_length64(ctx, &len, arr) < 0) - goto exception; - v = JS_NewInt64(ctx, len); - args[0] = v; - r = js_typed_array_create(ctx, this_val, 1, args); - JS_FreeValue(ctx, v); - if (JS_IsException(r)) - goto exception; - for(k = 0; k < len; k++) { - v = JS_GetPropertyInt64(ctx, arr, k); - if (JS_IsException(v)) - goto exception; - if (mapping) { - args[0] = v; - args[1] = JS_NewInt32(ctx, k); - v2 = JS_Call(ctx, mapfn, this_arg, 2, args); - JS_FreeValue(ctx, v); - v = v2; - if (JS_IsException(v)) - goto exception; - } - if (JS_SetPropertyInt64(ctx, r, k, v) < 0) - goto exception; - } - goto done; - - exception_close: - if (!JS_IsUndefined(stack[0])) - JS_IteratorClose(ctx, stack[0], TRUE); - exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; - done: - JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, stack[0]); - JS_FreeValue(ctx, stack[1]); - return r; -} - -static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - JSValueConst args[1]; - int i; - - args[0] = JS_NewInt32(ctx, argc); - obj = js_typed_array_create(ctx, this_val, 1, args); - if (JS_IsException(obj)) - return obj; - - for(i = 0; i < argc; i++) { - if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; -} - -static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - int len, to, from, final, count, shift; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - - if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) - return JS_EXCEPTION; - - if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len)) - return JS_EXCEPTION; - - final = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) - return JS_EXCEPTION; - } - - count = min_int(final - from, len - to); - if (count > 0) { - p = JS_VALUE_GET_OBJ(this_val); - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - shift = typed_array_size_log2(p->class_id); - memmove(p->u.array.u.uint8_ptr + (to << shift), - p->u.array.u.uint8_ptr + (from << shift), - count << shift); - } - return JS_DupValue(ctx, this_val); -} - -static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - int len, k, final, shift; - uint64_t v64; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(this_val); - - if (p->class_id == JS_CLASS_UINT8C_ARRAY) { - int32_t v; - if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0]))) - return JS_EXCEPTION; - v64 = v; - } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { - uint32_t v; - if (JS_ToUint32(ctx, &v, argv[0])) - return JS_EXCEPTION; - v64 = v; - } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { - if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0])) - return JS_EXCEPTION; - } else { - double d; - if (JS_ToFloat64(ctx, &d, argv[0])) - return JS_EXCEPTION; - if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { - union { - float f; - uint32_t u32; - } u; - u.f = d; - v64 = u.u32; - } else { - JSFloat64Union u; - u.d = d; - v64 = u.u64; - } - } - - k = 0; - if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) - return JS_EXCEPTION; - } - - final = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) - return JS_EXCEPTION; - } - - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - - shift = typed_array_size_log2(p->class_id); - switch(shift) { - case 0: - if (k < final) { - memset(p->u.array.u.uint8_ptr + k, v64, final - k); - } - break; - case 1: - for(; k < final; k++) { - p->u.array.u.uint16_ptr[k] = v64; - } - break; - case 2: - for(; k < final; k++) { - p->u.array.u.uint32_ptr[k] = v64; - } - break; - case 3: - for(; k < final; k++) { - p->u.array.u.uint64_ptr[k] = v64; - } - break; - default: - abort(); - } - return JS_DupValue(ctx, this_val); -} - -static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int mode) -{ - JSValueConst func, this_arg; - JSValueConst args[3]; - JSValue val, index_val, res; - int len, k, end; - int dir; - - val = JS_UNDEFINED; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; - - func = argv[0]; - if (check_function(ctx, func)) - goto exception; - - this_arg = JS_UNDEFINED; - if (argc > 1) - this_arg = argv[1]; - - k = 0; - dir = 1; - end = len; - if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { - k = len - 1; - dir = -1; - end = -1; - } - - for(; k != end; k += dir) { - index_val = JS_NewInt32(ctx, k); - val = JS_GetPropertyValue(ctx, this_val, index_val); - if (JS_IsException(val)) - goto exception; - args[0] = val; - args[1] = index_val; - args[2] = this_val; - res = JS_Call(ctx, func, this_arg, 3, args); - if (JS_IsException(res)) - goto exception; - if (JS_ToBoolFree(ctx, res)) { - if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { - JS_FreeValue(ctx, val); - return index_val; - } else { - return val; - } - } - JS_FreeValue(ctx, val); - } - if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) - return JS_NewInt32(ctx, -1); - else - return JS_UNDEFINED; - -exception: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -#define special_indexOf 0 -#define special_lastIndexOf 1 -#define special_includes -1 - -static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int special) -{ - JSObject *p; - int len, tag, is_int, is_bigint, k, stop, inc, res = -1; - int64_t v64; - double d; - float f; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; - if (len == 0) - goto done; - - if (special == special_lastIndexOf) { - k = len - 1; - if (argc > 1) { - if (JS_ToFloat64(ctx, &d, argv[1])) - goto exception; - if (isnan(d)) { - k = 0; - } else { - if (d >= 0) { - if (d < k) { - k = d; - } - } else { - d += len; - if (d < 0) - goto done; - k = d; - } - } - } - stop = -1; - inc = -1; - } else { - k = 0; - if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) - goto exception; - } - stop = len; - inc = 1; - } - - p = JS_VALUE_GET_OBJ(this_val); - /* if the array was detached, no need to go further (but no - exception is raised) */ - if (typed_array_is_detached(ctx, p)) { - /* "includes" scans all the properties, so "undefined" can match */ - if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) - res = 0; - goto done; - } - - is_bigint = 0; - is_int = 0; /* avoid warning */ - v64 = 0; /* avoid warning */ - tag = JS_VALUE_GET_NORM_TAG(argv[0]); - if (tag == JS_TAG_INT) { - is_int = 1; - v64 = JS_VALUE_GET_INT(argv[0]); - d = v64; - } else - if (tag == JS_TAG_FLOAT64) { - d = JS_VALUE_GET_FLOAT64(argv[0]); - if (d >= INT64_MIN && d < 0x1p63) { - v64 = d; - is_int = (v64 == d); - } - } else if (tag == JS_TAG_BIG_INT) { - JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]); - - if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { - if (bf_get_int64(&v64, &p1->num, 0) != 0) - goto done; - } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { - if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) - goto done; - } else { - goto done; - } - d = 0; - is_bigint = 1; - } else { - goto done; - } - - switch (p->class_id) { - case JS_CLASS_INT8_ARRAY: - if (is_int && (int8_t)v64 == v64) - goto scan8; - break; - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - if (is_int && (uint8_t)v64 == v64) { - const uint8_t *pv, *pp; - uint16_t v; - scan8: - pv = p->u.array.u.uint8_ptr; - v = v64; - if (inc > 0) { - pp = memchr(pv + k, v, len - k); - if (pp) - res = pp - pv; - } else { - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - } - break; - case JS_CLASS_INT16_ARRAY: - if (is_int && (int16_t)v64 == v64) - goto scan16; - break; - case JS_CLASS_UINT16_ARRAY: - if (is_int && (uint16_t)v64 == v64) { - const uint16_t *pv; - uint16_t v; - scan16: - pv = p->u.array.u.uint16_ptr; - v = v64; - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - break; - case JS_CLASS_INT32_ARRAY: - if (is_int && (int32_t)v64 == v64) - goto scan32; - break; - case JS_CLASS_UINT32_ARRAY: - if (is_int && (uint32_t)v64 == v64) { - const uint32_t *pv; - uint32_t v; - scan32: - pv = p->u.array.u.uint32_ptr; - v = v64; - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - break; - case JS_CLASS_FLOAT32_ARRAY: - if (is_bigint) - break; - if (isnan(d)) { - const float *pv = p->u.array.u.float_ptr; - /* special case: indexOf returns -1, includes finds NaN */ - if (special != special_includes) - goto done; - for (; k != stop; k += inc) { - if (isnan(pv[k])) { - res = k; - break; - } - } - } else if ((f = (float)d) == d) { - const float *pv = p->u.array.u.float_ptr; - for (; k != stop; k += inc) { - if (pv[k] == f) { - res = k; - break; - } - } - } - break; - case JS_CLASS_FLOAT64_ARRAY: - if (is_bigint) - break; - if (isnan(d)) { - const double *pv = p->u.array.u.double_ptr; - /* special case: indexOf returns -1, includes finds NaN */ - if (special != special_includes) - goto done; - for (; k != stop; k += inc) { - if (isnan(pv[k])) { - res = k; - break; - } - } - } else { - const double *pv = p->u.array.u.double_ptr; - for (; k != stop; k += inc) { - if (pv[k] == d) { - res = k; - break; - } - } - } - break; - case JS_CLASS_BIG_INT64_ARRAY: - if (is_bigint || (is_math_mode(ctx) && is_int && - v64 >= -MAX_SAFE_INTEGER && - v64 <= MAX_SAFE_INTEGER)) { - goto scan64; - } - break; - case JS_CLASS_BIG_UINT64_ARRAY: - if (is_bigint || (is_math_mode(ctx) && is_int && - v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) { - const uint64_t *pv; - uint64_t v; - scan64: - pv = p->u.array.u.uint64_ptr; - v = v64; - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - break; - } - -done: - if (special == special_includes) - return JS_NewBool(ctx, res >= 0); - else - return JS_NewInt32(ctx, res); - -exception: - return JS_EXCEPTION; -} - -static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int toLocaleString) -{ - JSValue sep = JS_UNDEFINED, el; - StringBuffer b_s, *b = &b_s; - JSString *p = NULL; - int i, n; - int c; - - n = js_typed_array_get_length_internal(ctx, this_val); - if (n < 0) - goto exception; - - c = ','; /* default separator */ - if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { - sep = JS_ToString(ctx, argv[0]); - if (JS_IsException(sep)) - goto exception; - p = JS_VALUE_GET_STRING(sep); - if (p->len == 1 && !p->is_wide_char) - c = p->u.str8[0]; - else - c = -1; - } - string_buffer_init(ctx, b, 0); - - /* XXX: optimize with direct access */ - for(i = 0; i < n; i++) { - if (i > 0) { - if (c >= 0) { - if (string_buffer_putc8(b, c)) - goto fail; - } else { - if (string_buffer_concat(b, p, 0, p->len)) - goto fail; - } - } - el = JS_GetPropertyUint32(ctx, this_val, i); - /* Can return undefined for example if the typed array is detached */ - if (!JS_IsNull(el) && !JS_IsUndefined(el)) { - if (JS_IsException(el)) - goto fail; - if (toLocaleString) { - el = JS_ToLocaleStringFree(ctx, el); - } - if (string_buffer_concat_value_free(b, el)) - goto fail; - } - } - JS_FreeValue(ctx, sep); - return string_buffer_end(b); - -fail: - string_buffer_free(b); - JS_FreeValue(ctx, sep); -exception: - return JS_EXCEPTION; -} - -static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - int len; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - if (len > 0) { - p = JS_VALUE_GET_OBJ(this_val); - switch (typed_array_size_log2(p->class_id)) { - case 0: - { - uint8_t *p1 = p->u.array.u.uint8_ptr; - uint8_t *p2 = p1 + len - 1; - while (p1 < p2) { - uint8_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } - break; - case 1: - { - uint16_t *p1 = p->u.array.u.uint16_ptr; - uint16_t *p2 = p1 + len - 1; - while (p1 < p2) { - uint16_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } - break; - case 2: - { - uint32_t *p1 = p->u.array.u.uint32_ptr; - uint32_t *p2 = p1 + len - 1; - while (p1 < p2) { - uint32_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } - break; - case 3: - { - uint64_t *p1 = p->u.array.u.uint64_ptr; - uint64_t *p2 = p1 + len - 1; - while (p1 < p2) { - uint64_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } - break; - default: - abort(); - } - } - return JS_DupValue(ctx, this_val); -} - -static JSValue js_typed_array_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 JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst args[2]; - JSValue arr, val; - JSObject *p, *p1; - int n, len, start, final, count, shift; - - arr = JS_UNDEFINED; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; - - if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) - goto exception; - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int(final - start, 0); - - p = get_typed_array(ctx, this_val, 0); - if (p == NULL) - goto exception; - shift = typed_array_size_log2(p->class_id); - - args[0] = this_val; - args[1] = JS_NewInt32(ctx, count); - arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); - if (JS_IsException(arr)) - goto exception; - - if (count > 0) { - if (validate_typed_array(ctx, this_val) - || validate_typed_array(ctx, arr)) - goto exception; - - p1 = get_typed_array(ctx, arr, 0); - if (p1 != NULL && p->class_id == p1->class_id && - typed_array_get_length(ctx, p1) >= count && - typed_array_get_length(ctx, p) >= start + count) { - memcpy(p1->u.array.u.uint8_ptr, - p->u.array.u.uint8_ptr + (start << shift), - count << shift); - } else { - for (n = 0; n < count; n++) { - val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); - if (JS_IsException(val)) - goto exception; - if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val, - JS_PROP_THROW) < 0) - goto exception; - } - } - } - return arr; - - exception: - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst args[4]; - JSValue arr, byteOffset, ta_buffer; - JSObject *p; - int len, start, final, count, shift, offset; - - p = get_typed_array(ctx, this_val, 0); - if (!p) - goto exception; - len = p->u.array.count; - if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) - goto exception; - - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int(final - start, 0); - byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0); - if (JS_IsException(byteOffset)) - goto exception; - shift = typed_array_size_log2(p->class_id); - offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); - JS_FreeValue(ctx, byteOffset); - ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); - if (JS_IsException(ta_buffer)) - goto exception; - args[0] = this_val; - args[1] = ta_buffer; - args[2] = JS_NewInt32(ctx, offset); - args[3] = JS_NewInt32(ctx, count); - arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); - JS_FreeValue(ctx, ta_buffer); - return arr; - - exception: - return JS_EXCEPTION; -} - -/* TypedArray.prototype.sort */ - -static int js_cmp_doubles(double x, double y) -{ - if (isnan(x)) return isnan(y) ? 0 : +1; - if (isnan(y)) return -1; - if (x < y) return -1; - if (x > y) return 1; - if (x != 0) return 0; - if (signbit(x)) return signbit(y) ? 0 : -1; - else return signbit(y) ? 1 : 0; -} - -static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) { - return *(const int8_t *)a - *(const int8_t *)b; -} - -static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) { - return *(const uint8_t *)a - *(const uint8_t *)b; -} - -static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) { - return *(const int16_t *)a - *(const int16_t *)b; -} - -static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) { - return *(const uint16_t *)a - *(const uint16_t *)b; -} - -static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) { - int32_t x = *(const int32_t *)a; - int32_t y = *(const int32_t *)b; - return (y < x) - (y > x); -} - -static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) { - uint32_t x = *(const uint32_t *)a; - uint32_t y = *(const uint32_t *)b; - return (y < x) - (y > x); -} - -static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) { - int64_t x = *(const int64_t *)a; - int64_t y = *(const int64_t *)b; - return (y < x) - (y > x); -} - -static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { - uint64_t x = *(const uint64_t *)a; - uint64_t y = *(const uint64_t *)b; - return (y < x) - (y > x); -} - -static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { - return js_cmp_doubles(*(const float *)a, *(const float *)b); -} - -static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) { - return js_cmp_doubles(*(const double *)a, *(const double *)b); -} - -static JSValue js_TA_get_int8(JSContext *ctx, const void *a) { - return JS_NewInt32(ctx, *(const int8_t *)a); -} - -static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) { - return JS_NewInt32(ctx, *(const uint8_t *)a); -} - -static JSValue js_TA_get_int16(JSContext *ctx, const void *a) { - return JS_NewInt32(ctx, *(const int16_t *)a); -} - -static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) { - return JS_NewInt32(ctx, *(const uint16_t *)a); -} - -static JSValue js_TA_get_int32(JSContext *ctx, const void *a) { - return JS_NewInt32(ctx, *(const int32_t *)a); -} - -static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) { - return JS_NewUint32(ctx, *(const uint32_t *)a); -} - -static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { - return JS_NewBigInt64(ctx, *(int64_t *)a); -} - -static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { - return JS_NewBigUint64(ctx, *(uint64_t *)a); -} - -static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { - return __JS_NewFloat64(ctx, *(const float *)a); -} - -static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { - return __JS_NewFloat64(ctx, *(const double *)a); -} - -struct TA_sort_context { - JSContext *ctx; - int exception; /* 1 = exception, 2 = detached typed array */ - JSValueConst arr; - JSValueConst cmp; - JSValue (*getfun)(JSContext *ctx, const void *a); - uint8_t *array_ptr; /* cannot change unless the array is detached */ - int elt_size; -}; - -static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { - struct TA_sort_context *psc = opaque; - JSContext *ctx = psc->ctx; - uint32_t a_idx, b_idx; - JSValueConst argv[2]; - JSValue res; - int cmp; - - cmp = 0; - if (!psc->exception) { - /* 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 + - a_idx * (size_t)psc->elt_size); - argv[1] = psc->getfun(ctx, psc->array_ptr + - b_idx * (size_t)(psc->elt_size)); - res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); - if (JS_IsException(res)) { - psc->exception = 1; - goto done; - } - if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { - int val = JS_VALUE_GET_INT(res); - cmp = (val > 0) - (val < 0); - } else { - double val; - if (JS_ToFloat64Free(ctx, &val, res) < 0) { - psc->exception = 1; - goto done; - } else { - cmp = (val > 0) - (val < 0); - } - } - if (cmp == 0) { - /* make sort stable: compare array offsets */ - cmp = (a_idx > b_idx) - (a_idx < b_idx); - } - if (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]); - } - return cmp; -} - -static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSObject *p; - int len; - size_t elt_size; - struct TA_sort_context tsc; - void *array_ptr; - int (*cmpfun)(const void *a, const void *b, void *opaque); - - tsc.ctx = ctx; - tsc.exception = 0; - tsc.arr = this_val; - tsc.cmp = argv[0]; - - 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 (len > 1) { - p = JS_VALUE_GET_OBJ(this_val); - switch (p->class_id) { - case JS_CLASS_INT8_ARRAY: - tsc.getfun = js_TA_get_int8; - cmpfun = js_TA_cmp_int8; - break; - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - tsc.getfun = js_TA_get_uint8; - cmpfun = js_TA_cmp_uint8; - break; - case JS_CLASS_INT16_ARRAY: - tsc.getfun = js_TA_get_int16; - cmpfun = js_TA_cmp_int16; - break; - case JS_CLASS_UINT16_ARRAY: - tsc.getfun = js_TA_get_uint16; - cmpfun = js_TA_cmp_uint16; - break; - case JS_CLASS_INT32_ARRAY: - tsc.getfun = js_TA_get_int32; - cmpfun = js_TA_cmp_int32; - break; - case JS_CLASS_UINT32_ARRAY: - tsc.getfun = js_TA_get_uint32; - cmpfun = js_TA_cmp_uint32; - break; - case JS_CLASS_BIG_INT64_ARRAY: - tsc.getfun = js_TA_get_int64; - cmpfun = js_TA_cmp_int64; - break; - case JS_CLASS_BIG_UINT64_ARRAY: - tsc.getfun = js_TA_get_uint64; - cmpfun = js_TA_cmp_uint64; - break; - case JS_CLASS_FLOAT32_ARRAY: - tsc.getfun = js_TA_get_float32; - cmpfun = js_TA_cmp_float32; - break; - case JS_CLASS_FLOAT64_ARRAY: - tsc.getfun = js_TA_get_float64; - cmpfun = js_TA_cmp_float64; - break; - default: - abort(); - } - array_ptr = p->u.array.u.ptr; - elt_size = 1 << typed_array_size_log2(p->class_id); - if (!JS_IsUndefined(tsc.cmp)) { - uint32_t *array_idx; - void *array_tmp; - size_t i, j; - - /* XXX: a stable sort would use less memory */ - array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); - if (!array_idx) - return JS_EXCEPTION; - for(i = 0; i < len; i++) - array_idx[i] = i; - tsc.array_ptr = array_ptr; - tsc.elt_size = elt_size; - rqsort(array_idx, len, sizeof(array_idx[0]), - js_TA_cmp_generic, &tsc); - if (tsc.exception) { - 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; - } - memcpy(array_tmp, array_ptr, len * elt_size); - switch(elt_size) { - case 1: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; - } - break; - case 2: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; - } - break; - case 4: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; - } - break; - case 8: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; - } - break; - default: - abort(); - } - js_free(ctx, array_tmp); - } - js_free(ctx, array_idx); - } else { - rqsort(array_ptr, len, elt_size, cmpfun, &tsc); - if (tsc.exception) - return JS_EXCEPTION; - } - } - return JS_DupValue(ctx, this_val); -} - -static 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 ), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), - //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ), - //JS_CFUNC_DEF("__create", 2, js_typed_array___create ), - //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ), -}; - -static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { - JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), - JS_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 ), - JS_CFUNC_DEF("set", 1, js_typed_array_set ), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ), - JS_ALIAS_DEF("[Symbol.iterator]", "values" ), - JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), - JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ), - JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ), - JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ), - JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ), - JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ), - JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ), - JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), - JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), - JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), - JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 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 ), - JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ), - JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ), - //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ -}; - -static JSValue js_typed_array_base_constructor(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ThrowTypeError(ctx, "cannot be called"); -} - -/* 'obj' must be an allocated typed array object */ -static int typed_array_init(JSContext *ctx, JSValueConst obj, - JSValue buffer, uint64_t offset, uint64_t len) -{ - JSTypedArray *ta; - JSObject *p, *pbuffer; - JSArrayBuffer *abuf; - int size_log2; - - p = JS_VALUE_GET_OBJ(obj); - size_log2 = typed_array_size_log2(p->class_id); - ta = js_malloc(ctx, sizeof(*ta)); - if (!ta) { - JS_FreeValue(ctx, buffer); - return -1; - } - pbuffer = JS_VALUE_GET_OBJ(buffer); - abuf = pbuffer->u.array_buffer; - ta->obj = p; - ta->buffer = pbuffer; - ta->offset = offset; - ta->length = len << size_log2; - list_add_tail(&ta->link, &abuf->array_list); - p->u.typed_array = ta; - p->u.array.count = len; - p->u.array.u.ptr = abuf->data + offset; - return 0; -} - - -static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, - JSValueConst obj, JSValueConst method) -{ - JSValue arr, iter, next_method = JS_UNDEFINED, val; - BOOL done; - uint32_t k; - - *plen = 0; - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return arr; - iter = JS_GetIterator2(ctx, obj, method); - if (JS_IsException(iter)) - goto fail; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail; - k = 0; - for(;;) { - val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(val)) - goto fail; - if (done) { - JS_FreeValue(ctx, val); - break; - } - if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) - goto fail; - k++; - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - *plen = k; - return arr; - fail: - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -static JSValue js_typed_array_constructor_obj(JSContext *ctx, - JSValueConst new_target, - JSValueConst obj, - int classid) -{ - JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; - uint32_t i; - int size_log2; - int64_t len; - - size_log2 = typed_array_size_log2(classid); - ret = js_create_from_ctor(ctx, new_target, classid); - if (JS_IsException(ret)) - return JS_EXCEPTION; - - iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) - goto fail; - if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { - uint32_t len1; - arr = js_array_from_iterator(ctx, &len1, obj, iter); - JS_FreeValue(ctx, iter); - if (JS_IsException(arr)) - goto fail; - len = len1; - } else { - if (js_get_length64(ctx, &len, obj)) - goto fail; - arr = JS_DupValue(ctx, obj); - } - - buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, - len << size_log2); - if (JS_IsException(buffer)) - goto fail; - if (typed_array_init(ctx, ret, buffer, 0, len)) - goto fail; - - for(i = 0; i < len; i++) { - val = JS_GetPropertyUint32(ctx, arr, i); - if (JS_IsException(val)) - goto fail; - if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) - goto fail; - } - JS_FreeValue(ctx, arr); - return ret; - fail: - JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; -} - -static JSValue js_typed_array_constructor_ta(JSContext *ctx, - JSValueConst new_target, - JSValueConst src_obj, - int classid) -{ - JSObject *p, *src_buffer; - JSTypedArray *ta; - JSValue obj, buffer; - uint32_t len, i; - int size_log2; - JSArrayBuffer *src_abuf, *abuf; - - obj = js_create_from_ctor(ctx, new_target, classid); - if (JS_IsException(obj)) - return obj; - p = JS_VALUE_GET_OBJ(src_obj); - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - ta = p->u.typed_array; - len = p->u.array.count; - src_buffer = ta->buffer; - src_abuf = src_buffer->u.array_buffer; - size_log2 = typed_array_size_log2(classid); - buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, - (uint64_t)len << size_log2); - if (JS_IsException(buffer)) - goto fail; - /* necessary because it could have been detached */ - if (typed_array_is_detached(ctx, p)) { - JS_FreeValue(ctx, buffer); - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); - if (typed_array_init(ctx, obj, buffer, 0, len)) - goto fail; - if (p->class_id == classid) { - /* same type: copy the content */ - memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); - } else { - for(i = 0; i < len; i++) { - JSValue val; - val = JS_GetPropertyUint32(ctx, src_obj, i); - if (JS_IsException(val)) - goto fail; - if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) - goto fail; - } - } - return obj; - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_typed_array_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv, - int classid) -{ - JSValue buffer, obj; - JSArrayBuffer *abuf; - int size_log2; - uint64_t len, offset; - - size_log2 = typed_array_size_log2(classid); - if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, - len << size_log2); - if (JS_IsException(buffer)) - return JS_EXCEPTION; - offset = 0; - } else { - JSObject *p = JS_VALUE_GET_OBJ(argv[0]); - if (p->class_id == JS_CLASS_ARRAY_BUFFER || - p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { - abuf = p->u.array_buffer; - if (JS_ToIndex(ctx, &offset, argv[1])) - return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if ((offset & ((1 << size_log2) - 1)) != 0 || - offset > abuf->byte_length) - return JS_ThrowRangeError(ctx, "invalid offset"); - if (JS_IsUndefined(argv[2])) { - if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) - goto invalid_length; - len = (abuf->byte_length - offset) >> size_log2; - } else { - if (JS_ToIndex(ctx, &len, argv[2])) - return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if ((offset + (len << size_log2)) > abuf->byte_length) { - invalid_length: - return JS_ThrowRangeError(ctx, "invalid length"); - } - } - buffer = JS_DupValue(ctx, argv[0]); - } else { - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid); - } else { - return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); - } - } - } - - obj = js_create_from_ctor(ctx, new_target, classid); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, buffer); - return JS_EXCEPTION; - } - if (typed_array_init(ctx, obj, buffer, offset, len)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSTypedArray *ta = p->u.typed_array; - if (ta) { - /* during the GC the finalizers are called in an arbitrary - order so the ArrayBuffer finalizer may have been called */ - if (ta->link.next) { - list_del(&ta->link); - } - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); - js_free_rt(rt, ta); - } -} - -static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSTypedArray *ta = p->u.typed_array; - if (ta) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); - } -} - -static JSValue js_dataview_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSArrayBuffer *abuf; - uint64_t offset; - uint32_t len; - JSValueConst buffer; - JSValue obj; - JSTypedArray *ta; - JSObject *p; - - buffer = argv[0]; - abuf = js_get_array_buffer(ctx, buffer); - if (!abuf) - return JS_EXCEPTION; - offset = 0; - if (argc > 1) { - if (JS_ToIndex(ctx, &offset, argv[1])) - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if (offset > abuf->byte_length) - return JS_ThrowRangeError(ctx, "invalid byteOffset"); - len = abuf->byte_length - offset; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - uint64_t l; - if (JS_ToIndex(ctx, &l, argv[2])) - return JS_EXCEPTION; - if (l > len) - return JS_ThrowRangeError(ctx, "invalid byteLength"); - len = l; - } - - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); - if (JS_IsException(obj)) - return JS_EXCEPTION; - if (abuf->detached) { - /* could have been detached in js_create_from_ctor() */ - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - ta = js_malloc(ctx, sizeof(*ta)); - if (!ta) { - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - p = JS_VALUE_GET_OBJ(obj); - ta->obj = p; - ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer)); - ta->offset = offset; - ta->length = len; - list_add_tail(&ta->link, &abuf->array_list); - p->u.typed_array = ta; - return obj; -} - -static JSValue js_dataview_getValue(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, int class_id) -{ - JSTypedArray *ta; - JSArrayBuffer *abuf; - BOOL littleEndian, is_swap; - int size; - uint8_t *ptr; - uint32_t v; - uint64_t pos; - - ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); - if (!ta) - return JS_EXCEPTION; - size = 1 << typed_array_size_log2(class_id); - if (JS_ToIndex(ctx, &pos, argv[0])) - return JS_EXCEPTION; - 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); - if ((pos + size) > ta->length) - return JS_ThrowRangeError(ctx, "out of bound"); - ptr = abuf->data + ta->offset + pos; - - switch(class_id) { - case JS_CLASS_INT8_ARRAY: - return JS_NewInt32(ctx, *(int8_t *)ptr); - case JS_CLASS_UINT8_ARRAY: - return JS_NewInt32(ctx, *(uint8_t *)ptr); - case JS_CLASS_INT16_ARRAY: - v = get_u16(ptr); - if (is_swap) - v = bswap16(v); - return JS_NewInt32(ctx, (int16_t)v); - case JS_CLASS_UINT16_ARRAY: - v = get_u16(ptr); - if (is_swap) - v = bswap16(v); - return JS_NewInt32(ctx, v); - case JS_CLASS_INT32_ARRAY: - v = get_u32(ptr); - if (is_swap) - v = bswap32(v); - return JS_NewInt32(ctx, v); - case JS_CLASS_UINT32_ARRAY: - v = get_u32(ptr); - if (is_swap) - v = bswap32(v); - return JS_NewUint32(ctx, v); - case JS_CLASS_BIG_INT64_ARRAY: - { - uint64_t v; - v = get_u64(ptr); - if (is_swap) - v = bswap64(v); - return JS_NewBigInt64(ctx, v); - } - break; - case JS_CLASS_BIG_UINT64_ARRAY: - { - uint64_t v; - v = get_u64(ptr); - if (is_swap) - v = bswap64(v); - return JS_NewBigUint64(ctx, v); - } - break; - case JS_CLASS_FLOAT32_ARRAY: - { - union { - float f; - uint32_t i; - } u; - v = get_u32(ptr); - if (is_swap) - v = bswap32(v); - u.i = v; - return __JS_NewFloat64(ctx, u.f); - } - case JS_CLASS_FLOAT64_ARRAY: - { - union { - double f; - uint64_t i; - } u; - u.i = get_u64(ptr); - if (is_swap) - u.i = bswap64(u.i); - return __JS_NewFloat64(ctx, u.f); - } - default: - abort(); - } -} - -static JSValue js_dataview_setValue(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, int class_id) -{ - JSTypedArray *ta; - JSArrayBuffer *abuf; - BOOL littleEndian, is_swap; - int size; - uint8_t *ptr; - uint64_t v64; - uint32_t v; - uint64_t pos; - JSValueConst val; - - ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); - if (!ta) - return JS_EXCEPTION; - size = 1 << typed_array_size_log2(class_id); - if (JS_ToIndex(ctx, &pos, argv[0])) - return JS_EXCEPTION; - val = argv[1]; - v = 0; /* avoid warning */ - v64 = 0; /* avoid warning */ - if (class_id <= JS_CLASS_UINT32_ARRAY) { - if (JS_ToUint32(ctx, &v, val)) - return JS_EXCEPTION; - } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { - if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) - return JS_EXCEPTION; - } else { - double d; - if (JS_ToFloat64(ctx, &d, val)) - return JS_EXCEPTION; - if (class_id == JS_CLASS_FLOAT32_ARRAY) { - union { - float f; - uint32_t i; - } u; - u.f = d; - v = u.i; - } else { - JSFloat64Union u; - u.d = d; - v64 = u.u64; - } - } - 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); - if ((pos + size) > ta->length) - return JS_ThrowRangeError(ctx, "out of bound"); - ptr = abuf->data + ta->offset + pos; - - switch(class_id) { - case JS_CLASS_INT8_ARRAY: - case JS_CLASS_UINT8_ARRAY: - *ptr = v; - break; - case JS_CLASS_INT16_ARRAY: - case JS_CLASS_UINT16_ARRAY: - if (is_swap) - v = bswap16(v); - put_u16(ptr, v); - break; - case JS_CLASS_INT32_ARRAY: - case JS_CLASS_UINT32_ARRAY: - case JS_CLASS_FLOAT32_ARRAY: - if (is_swap) - v = bswap32(v); - put_u32(ptr, v); - break; - case JS_CLASS_BIG_INT64_ARRAY: - case JS_CLASS_BIG_UINT64_ARRAY: - case JS_CLASS_FLOAT64_ARRAY: - if (is_swap) - v64 = bswap64(v64); - put_u64(ptr, v64); - break; - default: - abort(); - } - return JS_UNDEFINED; -} - -static const JSCFunctionListEntry js_dataview_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ), - JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ), - JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ), - JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ), - JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ), - JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ), - JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), - 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("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), - JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), - JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), - JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ), - JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ), - JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ), - JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ), - JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), - 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("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), - JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), -}; - -/* Atomics */ -#ifdef CONFIG_ATOMICS - -typedef enum AtomicsOpEnum { - ATOMICS_OP_ADD, - ATOMICS_OP_AND, - ATOMICS_OP_OR, - ATOMICS_OP_SUB, - ATOMICS_OP_XOR, - ATOMICS_OP_EXCHANGE, - ATOMICS_OP_COMPARE_EXCHANGE, - ATOMICS_OP_LOAD, -} AtomicsOpEnum; - -static void *js_atomics_get_ptr(JSContext *ctx, - JSArrayBuffer **pabuf, - int *psize_log2, JSClassID *pclass_id, - JSValueConst obj, JSValueConst idx_val, - int is_waitable) -{ - JSObject *p; - JSTypedArray *ta; - JSArrayBuffer *abuf; - void *ptr; - uint64_t idx; - BOOL err; - int size_log2; - - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(obj); - if (is_waitable) - err = (p->class_id != JS_CLASS_INT32_ARRAY && - p->class_id != JS_CLASS_BIG_INT64_ARRAY); - else - err = !(p->class_id >= JS_CLASS_INT8_ARRAY && - p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); - if (err) { - fail: - JS_ThrowTypeError(ctx, "integer TypedArray expected"); - return NULL; - } - ta = p->u.typed_array; - abuf = ta->buffer->u.array_buffer; - if (!abuf->shared) { - if (is_waitable == 2) { - JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); - return NULL; - } - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return NULL; - } - } - if (JS_ToIndex(ctx, &idx, idx_val)) { - return NULL; - } - /* if the array buffer is detached, p->u.array.count = 0 */ - if (idx >= p->u.array.count) { - JS_ThrowRangeError(ctx, "out-of-bound access"); - return NULL; - } - size_log2 = typed_array_size_log2(p->class_id); - ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); - if (pabuf) - *pabuf = abuf; - if (psize_log2) - *psize_log2 = size_log2; - if (pclass_id) - *pclass_id = p->class_id; - return ptr; -} - -static JSValue js_atomics_op(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, int op) -{ - int size_log2; - uint64_t v, a, rep_val; - void *ptr; - JSValue ret; - JSClassID class_id; - JSArrayBuffer *abuf; - - ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, - argv[0], argv[1], 0); - if (!ptr) - return JS_EXCEPTION; - rep_val = 0; - if (op == ATOMICS_OP_LOAD) { - v = 0; - } else { - if (size_log2 == 3) { - int64_t v64; - if (JS_ToBigInt64(ctx, &v64, argv[2])) - return JS_EXCEPTION; - v = v64; - if (op == ATOMICS_OP_COMPARE_EXCHANGE) { - if (JS_ToBigInt64(ctx, &v64, argv[3])) - return JS_EXCEPTION; - rep_val = v64; - } - } else { - uint32_t v32; - if (JS_ToUint32(ctx, &v32, argv[2])) - return JS_EXCEPTION; - v = v32; - if (op == ATOMICS_OP_COMPARE_EXCHANGE) { - if (JS_ToUint32(ctx, &v32, argv[3])) - return JS_EXCEPTION; - rep_val = v32; - } - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } - - switch(op | (size_log2 << 3)) { - -#define OP(op_name, func_name) \ - case ATOMICS_OP_ ## op_name | (0 << 3): \ - a = func_name((_Atomic(uint8_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (1 << 3): \ - a = func_name((_Atomic(uint16_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (2 << 3): \ - a = func_name((_Atomic(uint32_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (3 << 3): \ - a = func_name((_Atomic(uint64_t) *)ptr, v); \ - break; - - OP(ADD, atomic_fetch_add) - OP(AND, atomic_fetch_and) - OP(OR, atomic_fetch_or) - OP(SUB, atomic_fetch_sub) - OP(XOR, atomic_fetch_xor) - OP(EXCHANGE, atomic_exchange) -#undef OP - - case ATOMICS_OP_LOAD | (0 << 3): - a = atomic_load((_Atomic(uint8_t) *)ptr); - break; - case ATOMICS_OP_LOAD | (1 << 3): - a = atomic_load((_Atomic(uint16_t) *)ptr); - break; - case ATOMICS_OP_LOAD | (2 << 3): - a = atomic_load((_Atomic(uint32_t) *)ptr); - break; - 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; - atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val); - a = v1; - } - break; - case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): - { - uint16_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val); - a = v1; - } - break; - case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): - { - uint32_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val); - a = v1; - } - break; - case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): - { - uint64_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val); - a = v1; - } - break; - default: - abort(); - } - - switch(class_id) { - case JS_CLASS_INT8_ARRAY: - a = (int8_t)a; - goto done; - case JS_CLASS_UINT8_ARRAY: - a = (uint8_t)a; - goto done; - case JS_CLASS_INT16_ARRAY: - a = (int16_t)a; - goto done; - case JS_CLASS_UINT16_ARRAY: - a = (uint16_t)a; - goto done; - case JS_CLASS_INT32_ARRAY: - done: - ret = JS_NewInt32(ctx, a); - break; - case JS_CLASS_UINT32_ARRAY: - ret = JS_NewUint32(ctx, a); - break; - case JS_CLASS_BIG_INT64_ARRAY: - ret = JS_NewBigInt64(ctx, a); - break; - case JS_CLASS_BIG_UINT64_ARRAY: - ret = JS_NewBigUint64(ctx, a); - break; - default: - abort(); - } - return ret; -} - -static JSValue js_atomics_store(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - int size_log2; - void *ptr; - JSValue ret; - JSArrayBuffer *abuf; - - ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, - argv[0], argv[1], 0); - if (!ptr) - return JS_EXCEPTION; - if (size_log2 == 3) { - int64_t v64; - ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); - if (JS_IsException(ret)) - return ret; - if (JS_ToBigInt64(ctx, &v64, ret)) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - atomic_store((_Atomic(uint64_t) *)ptr, v64); - } else { - uint32_t v; - /* XXX: spec, would be simpler to return the written value */ - ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); - if (JS_IsException(ret)) - return ret; - if (JS_ToUint32(ctx, &v, ret)) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - switch(size_log2) { - case 0: - atomic_store((_Atomic(uint8_t) *)ptr, v); - break; - case 1: - atomic_store((_Atomic(uint16_t) *)ptr, v); - break; - case 2: - atomic_store((_Atomic(uint32_t) *)ptr, v); - break; - default: - abort(); - } - } - return ret; -} - -static JSValue js_atomics_isLockFree(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - int v, ret; - if (JS_ToInt32Sat(ctx, &v, argv[0])) - return JS_EXCEPTION; - ret = (v == 1 || v == 2 || v == 4 || v == 8); - return JS_NewBool(ctx, ret); -} - -typedef struct JSAtomicsWaiter { - struct list_head link; - BOOL linked; - pthread_cond_t cond; - int32_t *ptr; -} JSAtomicsWaiter; - -static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; -static struct list_head js_atomics_waiter_list = - LIST_HEAD_INIT(js_atomics_waiter_list); - -static JSValue js_atomics_wait(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - int64_t v; - int32_t v32; - void *ptr; - int64_t timeout; - struct timespec ts; - JSAtomicsWaiter waiter_s, *waiter; - int ret, size_log2, res; - double d; - - ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, - argv[0], argv[1], 2); - if (!ptr) - return JS_EXCEPTION; - if (size_log2 == 3) { - if (JS_ToBigInt64(ctx, &v, argv[2])) - return JS_EXCEPTION; - } else { - if (JS_ToInt32(ctx, &v32, argv[2])) - return JS_EXCEPTION; - v = v32; - } - if (JS_ToFloat64(ctx, &d, argv[3])) - return JS_EXCEPTION; - /* 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; - else - timeout = (int64_t)d; - if (!ctx->rt->can_block) - return JS_ThrowTypeError(ctx, "cannot block in this thread"); - - /* XXX: inefficient if large number of waiters, should hash on - 'ptr' value */ - /* XXX: use Linux futexes when available ? */ - pthread_mutex_lock(&js_atomics_mutex); - if (size_log2 == 3) { - res = *(int64_t *)ptr != v; - } else { - res = *(int32_t *)ptr != v; - } - if (res) { - pthread_mutex_unlock(&js_atomics_mutex); - return JS_AtomToString(ctx, JS_ATOM_not_equal); - } - - waiter = &waiter_s; - waiter->ptr = ptr; - pthread_cond_init(&waiter->cond, NULL); - waiter->linked = TRUE; - list_add_tail(&waiter->link, &js_atomics_waiter_list); - - if (timeout == INT64_MAX) { - pthread_cond_wait(&waiter->cond, &js_atomics_mutex); - ret = 0; - } else { - /* XXX: use clock monotonic */ - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout / 1000; - ts.tv_nsec += (timeout % 1000) * 1000000; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, - &ts); - } - if (waiter->linked) - list_del(&waiter->link); - pthread_mutex_unlock(&js_atomics_mutex); - pthread_cond_destroy(&waiter->cond); - if (ret == ETIMEDOUT) { - return JS_AtomToString(ctx, JS_ATOM_timed_out); - } else { - return JS_AtomToString(ctx, JS_ATOM_ok); - } -} - -static JSValue js_atomics_notify(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - struct list_head *el, *el1, waiter_list; - int32_t count, n; - void *ptr; - JSAtomicsWaiter *waiter; - JSArrayBuffer *abuf; - - ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); - if (!ptr) - return JS_EXCEPTION; - - if (JS_IsUndefined(argv[2])) { - count = INT32_MAX; - } else { - if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - - n = 0; - if (abuf->shared && count > 0) { - pthread_mutex_lock(&js_atomics_mutex); - init_list_head(&waiter_list); - list_for_each_safe(el, el1, &js_atomics_waiter_list) { - waiter = list_entry(el, JSAtomicsWaiter, link); - if (waiter->ptr == ptr) { - list_del(&waiter->link); - waiter->linked = FALSE; - list_add_tail(&waiter->link, &waiter_list); - n++; - if (n >= count) - break; - } - } - list_for_each(el, &waiter_list) { - waiter = list_entry(el, JSAtomicsWaiter, link); - pthread_cond_signal(&waiter->cond); - } - pthread_mutex_unlock(&js_atomics_mutex); - } - return JS_NewInt32(ctx, n); -} - -static const JSCFunctionListEntry js_atomics_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), - JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), - JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), - JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), - JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), - JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), - JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), - JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), - JS_CFUNC_DEF("store", 3, js_atomics_store ), - JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), - JS_CFUNC_DEF("wait", 4, js_atomics_wait ), - JS_CFUNC_DEF("notify", 3, js_atomics_notify ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), -}; - -static const JSCFunctionListEntry js_atomics_obj[] = { - JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicAtomics(JSContext *ctx) -{ - /* add Atomics as autoinit object */ - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); -} - -#endif /* CONFIG_ATOMICS */ - -void JS_AddIntrinsicTypedArrays(JSContext *ctx) -{ - JSValue typed_array_base_proto, typed_array_base_func; - JSValueConst array_buffer_func, shared_array_buffer_func; - int i; - - ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER], - js_array_buffer_proto_funcs, - countof(js_array_buffer_proto_funcs)); - - array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer", - js_array_buffer_constructor, 1, - ctx->class_proto[JS_CLASS_ARRAY_BUFFER]); - JS_SetPropertyFunctionList(ctx, array_buffer_func, - js_array_buffer_funcs, - countof(js_array_buffer_funcs)); - - ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER], - js_shared_array_buffer_proto_funcs, - countof(js_shared_array_buffer_proto_funcs)); - - shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer", - js_shared_array_buffer_constructor, 1, - ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]); - JS_SetPropertyFunctionList(ctx, shared_array_buffer_func, - js_shared_array_buffer_funcs, - countof(js_shared_array_buffer_funcs)); - - typed_array_base_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, typed_array_base_proto, - js_typed_array_base_proto_funcs, - countof(js_typed_array_base_proto_funcs)); - - /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ - JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); - /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ - JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, - JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - - typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, - "TypedArray", 0); - JS_SetPropertyFunctionList(ctx, typed_array_base_func, - js_typed_array_base_funcs, - countof(js_typed_array_base_funcs)); - JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); - - /* 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]; - const char *name; - - ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto); - JS_DefinePropertyValueStr(ctx, ctx->class_proto[i], - "BYTES_PER_ELEMENT", - JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), - 0); - name = JS_AtomGetStr(ctx, buf, sizeof(buf), - JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); - func_obj = JS_NewCFunction3(ctx, ft.generic, - name, 3, JS_CFUNC_constructor_magic, i, - typed_array_base_func); - JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); - JS_DefinePropertyValueStr(ctx, func_obj, - "BYTES_PER_ELEMENT", - JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), - 0); - } - JS_FreeValue(ctx, typed_array_base_proto); - JS_FreeValue(ctx, typed_array_base_func); - - /* DataView */ - ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW], - js_dataview_proto_funcs, - countof(js_dataview_proto_funcs)); - JS_NewGlobalCConstructorOnly(ctx, "DataView", - js_dataview_constructor, 1, - ctx->class_proto[JS_CLASS_DATAVIEW]); - /* Atomics */ -#ifdef CONFIG_ATOMICS - JS_AddIntrinsicAtomics(ctx); -#endif -} diff --git a/lib/quickjs/quickjs.h b/lib/quickjs/quickjs.h deleted file mode 100644 index 366f341b..00000000 --- a/lib/quickjs/quickjs.h +++ /dev/null @@ -1,1098 +0,0 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef QUICKJS_H -#define QUICKJS_H - -#include <stdio.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define js_likely(x) __builtin_expect(!!(x), 1) -#define js_unlikely(x) __builtin_expect(!!(x), 0) -#define js_force_inline inline __attribute__((always_inline)) -#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) -#else -#define js_likely(x) (x) -#define js_unlikely(x) (x) -#define js_force_inline inline -#define __js_printf_like(a, b) -#endif - -#define JS_BOOL int - -typedef struct JSRuntime JSRuntime; -typedef struct JSContext JSContext; -typedef struct JSObject JSObject; -typedef struct JSClass JSClass; -typedef uint32_t JSClassID; -typedef uint32_t JSAtom; - -#if INTPTR_MAX >= INT64_MAX -#define JS_PTR64 -#define JS_PTR64_DEF(a) a -#else -#define JS_PTR64_DEF(a) -#endif - -#ifndef JS_PTR64 -#define JS_NAN_BOXING -#endif - -enum { - /* all tags with a reference count are negative */ - JS_TAG_FIRST = -11, /* first negative tag */ - JS_TAG_BIG_DECIMAL = -11, - JS_TAG_BIG_INT = -10, - JS_TAG_BIG_FLOAT = -9, - JS_TAG_SYMBOL = -8, - JS_TAG_STRING = -7, - JS_TAG_MODULE = -3, /* used internally */ - JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ - JS_TAG_OBJECT = -1, - - JS_TAG_INT = 0, - JS_TAG_BOOL = 1, - JS_TAG_NULL = 2, - JS_TAG_UNDEFINED = 3, - JS_TAG_UNINITIALIZED = 4, - JS_TAG_CATCH_OFFSET = 5, - JS_TAG_EXCEPTION = 6, - JS_TAG_FLOAT64 = 7, - /* any larger tag is FLOAT64 if JS_NAN_BOXING */ -}; - -typedef struct JSRefCountHeader { - int ref_count; -} JSRefCountHeader; - -#define JS_FLOAT64_NAN NAN - -#ifdef CONFIG_CHECK_JSVALUE -/* JSValue consistency : it is not possible to run the code in this - mode, but it is useful to detect simple reference counting - errors. It would be interesting to modify a static C analyzer to - handle specific annotations (clang has such annotations but only - for objective C) */ -typedef struct __JSValue *JSValue; -typedef const struct __JSValue *JSValueConst; - -#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf) -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) -#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) -#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) -#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) -#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) - -#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) -#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag)) - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - -#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1) - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -{ - return JS_MKVAL(JS_TAG_FLOAT64, (int)d); -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) -{ - return 0; -} - -#elif defined(JS_NAN_BOXING) - -typedef uint64_t JSValue; - -#define JSValueConst JSValue - -#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) -#define JS_VALUE_GET_INT(v) (int)(v) -#define JS_VALUE_GET_BOOL(v) (int)(v) -#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) - -#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) -#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) - -#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ - -static inline double JS_VALUE_GET_FLOAT64(JSValue v) -{ - union { - JSValue v; - double d; - } u; - u.v = v; - u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; - return u.d; -} - -#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -{ - union { - double d; - uint64_t u64; - } u; - JSValue v; - u.d = d; - /* normalize NaN */ - if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) - v = JS_NAN; - else - v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); - return v; -} - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) - -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -static inline int JS_VALUE_GET_NORM_TAG(JSValue v) -{ - uint32_t tag; - tag = JS_VALUE_GET_TAG(v); - if (JS_TAG_IS_FLOAT64(tag)) - return JS_TAG_FLOAT64; - else - return tag; -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) -{ - uint32_t tag; - tag = JS_VALUE_GET_TAG(v); - return tag == (JS_NAN >> 32); -} - -#else /* !JS_NAN_BOXING */ - -typedef union JSValueUnion { - int32_t int32; - double float64; - void *ptr; -} JSValueUnion; - -typedef struct JSValue { - JSValueUnion u; - int64_t tag; -} JSValue; - -#define JSValueConst JSValue - -#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) -#define JS_VALUE_GET_INT(v) ((v).u.int32) -#define JS_VALUE_GET_BOOL(v) ((v).u.int32) -#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) -#define JS_VALUE_GET_PTR(v) ((v).u.ptr) - -#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } -#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - -#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -{ - JSValue v; - v.tag = JS_TAG_FLOAT64; - v.u.float64 = d; - return v; -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) -{ - union { - double d; - uint64_t u64; - } u; - if (v.tag != JS_TAG_FLOAT64) - return 0; - u.d = v.u.float64; - return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; -} - -#endif /* !JS_NAN_BOXING */ - -#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) -#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) - -#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) - -/* special values */ -#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) -#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) -#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) -#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) -#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) -#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) - -/* flags for object properties */ -#define JS_PROP_CONFIGURABLE (1 << 0) -#define JS_PROP_WRITABLE (1 << 1) -#define JS_PROP_ENUMERABLE (1 << 2) -#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) -#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ -#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ -#define JS_PROP_NORMAL (0 << 4) -#define JS_PROP_GETSET (1 << 4) -#define JS_PROP_VARREF (2 << 4) /* used internally */ -#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ - -/* flags for JS_DefineProperty */ -#define JS_PROP_HAS_SHIFT 8 -#define JS_PROP_HAS_CONFIGURABLE (1 << 8) -#define JS_PROP_HAS_WRITABLE (1 << 9) -#define JS_PROP_HAS_ENUMERABLE (1 << 10) -#define JS_PROP_HAS_GET (1 << 11) -#define JS_PROP_HAS_SET (1 << 12) -#define JS_PROP_HAS_VALUE (1 << 13) - -/* throw an exception if false would be returned - (JS_DefineProperty/JS_SetProperty) */ -#define JS_PROP_THROW (1 << 14) -/* throw an exception if false would be returned in strict mode - (JS_SetProperty) */ -#define JS_PROP_THROW_STRICT (1 << 15) - -#define JS_PROP_NO_ADD (1 << 16) /* internal use */ -#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ - -#define JS_DEFAULT_STACK_SIZE (256 * 1024) - -/* JS_Eval() flags */ -#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ -#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ -#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ -#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ -#define JS_EVAL_TYPE_MASK (3 << 0) - -#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ -#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ -/* compile but do not run. The result is an object with a - JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed - with JS_EvalFunction(). */ -#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) -/* don't include the stack frames before this eval in the Error() backtraces */ -#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) -/* 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); -typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data); - -typedef struct JSMallocState { - size_t malloc_count; - size_t malloc_size; - size_t malloc_limit; - void *opaque; /* user opaque */ -} JSMallocState; - -typedef struct JSMallocFunctions { - void *(*js_malloc)(JSMallocState *s, size_t size); - void (*js_free)(JSMallocState *s, void *ptr); - void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size); - size_t (*js_malloc_usable_size)(const void *ptr); -} JSMallocFunctions; - -typedef struct JSGCObjectHeader JSGCObjectHeader; - -typedef void JSRuntimeCleanUpFunc(JSRuntime *rt); - -JSRuntime *JS_NewRuntime(void); -/* info lifetime must exceed that of rt */ -void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); -void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); -void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); -/* use 0 to disable maximum stack size check */ -void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); -/* should be called when changing thread to update the stack top value - used to check stack overflow. */ -void JS_UpdateStackTop(JSRuntime *rt); -JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); -void JS_FreeRuntime(JSRuntime *rt); -void *JS_GetRuntimeOpaque(JSRuntime *rt); -void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); -void JS_SetRuntimeCleanUpFunc(JSRuntime *rt, JSRuntimeCleanUpFunc cleanup_func); -typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); -void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -void JS_RunGC(JSRuntime *rt); -JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); - -JSContext *JS_NewContext(JSRuntime *rt); -void JS_FreeContext(JSContext *s); -JSContext *JS_DupContext(JSContext *ctx); -void *JS_GetContextOpaque(JSContext *ctx); -void JS_SetContextOpaque(JSContext *ctx, void *opaque); -JSRuntime *JS_GetRuntime(JSContext *ctx); -void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); -JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); - -/* the following functions are used to select the intrinsic object to - save memory */ -JSContext *JS_NewContextRaw(JSRuntime *rt); -void JS_AddIntrinsicBaseObjects(JSContext *ctx); -void JS_AddIntrinsicDate(JSContext *ctx); -void JS_AddIntrinsicEval(JSContext *ctx); -void JS_AddIntrinsicStringNormalize(JSContext *ctx); -void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); -void JS_AddIntrinsicRegExp(JSContext *ctx); -void JS_AddIntrinsicJSON(JSContext *ctx); -void JS_AddIntrinsicProxy(JSContext *ctx); -void JS_AddIntrinsicMapSet(JSContext *ctx); -void JS_AddIntrinsicTypedArrays(JSContext *ctx); -void JS_AddIntrinsicPromise(JSContext *ctx); -void JS_AddIntrinsicBigInt(JSContext *ctx); -void JS_AddIntrinsicBigFloat(JSContext *ctx); -void JS_AddIntrinsicBigDecimal(JSContext *ctx); -/* enable operator overloading */ -void JS_AddIntrinsicOperators(JSContext *ctx); -/* enable "use math" */ -void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); - -JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -void *js_malloc_rt(JSRuntime *rt, size_t size); -void js_free_rt(JSRuntime *rt, void *ptr); -void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); -size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); -void *js_mallocz_rt(JSRuntime *rt, size_t size); - -void *js_malloc(JSContext *ctx, size_t size); -void js_free(JSContext *ctx, void *ptr); -void *js_realloc(JSContext *ctx, void *ptr, size_t size); -size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); -void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); -void *js_mallocz(JSContext *ctx, size_t size); -char *js_strdup(JSContext *ctx, const char *str); -char *js_strndup(JSContext *ctx, const char *s, size_t n); - -typedef struct JSMemoryUsage { - int64_t malloc_size, malloc_limit, memory_used_size; - int64_t malloc_count; - int64_t memory_used_count; - int64_t atom_count, atom_size; - int64_t str_count, str_size; - int64_t obj_count, obj_size; - int64_t prop_count, prop_size; - int64_t shape_count, shape_size; - int64_t js_func_count, js_func_size, js_func_code_size; - int64_t js_func_pc2line_count, js_func_pc2line_size; - int64_t c_func_count, array_count; - int64_t fast_array_count, fast_array_elements; - int64_t binary_object_count, binary_object_size; -} JSMemoryUsage; - -void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); -void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); - -/* atom support */ -#define JS_ATOM_NULL 0 - -JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); -JSAtom JS_NewAtom(JSContext *ctx, const char *str); -JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); -JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); -void JS_FreeAtom(JSContext *ctx, JSAtom v); -void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); -JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); -JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); -JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); - -/* object class support */ - -typedef struct JSPropertyEnum { - JS_BOOL is_enumerable; - JSAtom atom; -} JSPropertyEnum; - -typedef struct JSPropertyDescriptor { - int flags; - JSValue value; - JSValue getter; - JSValue setter; -} JSPropertyDescriptor; - -typedef struct JSClassExoticMethods { - /* Return -1 if exception (can only happen in case of Proxy object), - FALSE if the property does not exists, TRUE if it exists. If 1 is - returned, the property descriptor 'desc' is filled if != NULL. */ - int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop); - /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, - -1 if exception. The 'is_enumerable' field is ignored. - */ - int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, - JSValueConst obj); - /* return < 0 if exception, or TRUE/FALSE */ - int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); - /* return < 0 if exception or TRUE/FALSE */ - int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); - /* The following methods can be emulated with the previous ones, - so they are usually not needed */ - /* return < 0 if exception or TRUE/FALSE */ - int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom); - JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst receiver); - /* return < 0 if exception or TRUE/FALSE */ - int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst value, JSValueConst receiver, int flags); -} JSClassExoticMethods; - -typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); -typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) -typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_val, int argc, JSValueConst *argv, - int flags); -typedef JS_BOOL JSClassCanDestroy(JSRuntime *rt, JSValueConst val); - -typedef struct JSClassDef { - const char *class_name; - JSClassFinalizer *finalizer; - JSClassGCMark *gc_mark; - /* if call != NULL, the object is a function. If (flags & - JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a - constructor. In this case, 'this_val' is new.target. A - constructor call only happens if the object constructor bit is - set (see JS_SetConstructorBit()). */ - JSClassCall *call; - /* XXX: suppress this indirection ? It is here only to save memory - because only a few classes need these methods */ - JSClassExoticMethods *exotic; - JSClassCanDestroy *can_destroy; -} JSClassDef; - -#define JS_INVALID_CLASS_ID 0 -JSClassID JS_NewClassID(JSClassID *pclass_id); -/* 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); - -/* value handling */ - -static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) -{ - return JS_MKVAL(JS_TAG_BOOL, (val != 0)); -} - -static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) -{ - return JS_MKVAL(JS_TAG_INT, val); -} - -static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) -{ - return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); -} - -static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) -{ - JSValue v; - if (val == (int32_t)val) { - v = JS_NewInt32(ctx, val); - } else { - v = __JS_NewFloat64(ctx, val); - } - return v; -} - -static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) -{ - JSValue v; - if (val <= 0x7fffffff) { - v = JS_NewInt32(ctx, val); - } else { - v = __JS_NewFloat64(ctx, val); - } - return v; -} - -JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); -JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); - -static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) -{ - int32_t val; - union { - double d; - uint64_t u; - } u, t; - 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 __JS_NewFloat64(ctx, d); -} - -static inline JS_BOOL JS_IsNumber(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); -} - -static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_INT; -} - -static inline JS_BOOL JS_IsBigFloat(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_FLOAT; -} - -static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) -{ - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_DECIMAL; -} - -static inline JS_BOOL JS_IsBool(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; -} - -static inline JS_BOOL JS_IsNull(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; -} - -static inline JS_BOOL JS_IsUndefined(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; -} - -static inline JS_BOOL JS_IsException(JSValueConst v) -{ - return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION); -} - -static inline JS_BOOL JS_IsUninitialized(JSValueConst v) -{ - return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED); -} - -static inline JS_BOOL JS_IsString(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; -} - -static inline JS_BOOL JS_IsSymbol(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; -} - -static inline JS_BOOL JS_IsObject(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; -} - -JSValue JS_Throw(JSContext *ctx, JSValue obj); -JSValue JS_GetException(JSContext *ctx); -JS_BOOL JS_HasException(JSContext *ctx); -JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); -void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, JS_BOOL flag); -void JS_ResetUncatchableError(JSContext *ctx); -JSValue JS_NewError(JSContext *ctx); -JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); -JSValue JS_ThrowOutOfMemory(JSContext *ctx); - -void __JS_FreeValue(JSContext *ctx, JSValue v); -static inline void JS_FreeValue(JSContext *ctx, JSValue v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - if (--p->ref_count <= 0) { - __JS_FreeValue(ctx, v); - } - } -} -void __JS_FreeValueRT(JSRuntime *rt, JSValue v); -static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - if (--p->ref_count <= 0) { - __JS_FreeValueRT(rt, v); - } - } -} - -static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - p->ref_count++; - } - return (JSValue)v; -} - -static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) -{ - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); - p->ref_count++; - } - return (JSValue)v; -} - -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) -{ - return JS_ToInt32(ctx, (int32_t*)pres, val); -} -int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); -int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); -int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); -/* return an exception if 'val' is a Number */ -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); -/* same as JS_ToInt64() but allow BigInt */ -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); - -JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); -JSValue JS_NewString(JSContext *ctx, const char *str); -JSValue JS_NewAtomString(JSContext *ctx, const char *str); -JSValue JS_ToString(JSContext *ctx, JSValueConst val); -JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); -const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8); -static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) -{ - return JS_ToCStringLen2(ctx, plen, val1, 0); -} -static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) -{ - return JS_ToCStringLen2(ctx, NULL, val1, 0); -} -void JS_FreeCString(JSContext *ctx, const char *ptr); - -JSValue JS_NewNarrowStringLen(JSContext *ctx, const char *str, size_t len); -JS_BOOL JS_IsStringWideChar(JSValue value); -uint8_t *JS_GetNarrowStringBuffer(JSValue value); - -JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); -JSValue JS_NewObjectClass(JSContext *ctx, int class_id); -JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); -JSValue JS_NewObject(JSContext *ctx); - -JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); -JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); -JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); - -JSValue JS_NewArray(JSContext *ctx); -int JS_IsArray(JSContext *ctx, JSValueConst val); - -JSValue JS_NewDate(JSContext *ctx, double epoch_ms); - -JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst receiver, - JS_BOOL throw_ref_error); -static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop) -{ - return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); -} -JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, - const char *prop); -JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx); - -int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValue val, JSValueConst this_obj, - int flags); -static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val) -{ - return JS_SetPropertyInternal(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW); -} -int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx, JSValue val); -int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, - int64_t idx, JSValue val); -int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, - const char *prop, JSValue val); -int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); -int JS_IsExtensible(JSContext *ctx, JSValueConst obj); -int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); -int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); -int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); -JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); - -#define JS_GPN_STRING_MASK (1 << 0) -#define JS_GPN_SYMBOL_MASK (1 << 1) -#define JS_GPN_PRIVATE_MASK (1 << 2) -/* only include the enumerable properties */ -#define JS_GPN_ENUM_ONLY (1 << 4) -/* set theJSPropertyEnum.is_enumerable field */ -#define JS_GPN_SET_ENUM (1 << 5) - -int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, - uint32_t *plen, JSValueConst obj, int flags); -int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop); - -JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv); -JSValue JS_NewObjectFromCtor(JSContext *ctx, JSValueConst ctor, - JSClassID class_id); -JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, - int argc, JSValueConst *argv); -JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, - int argc, JSValueConst *argv); -JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv); -JS_BOOL JS_DetectModule(const char *input, size_t input_len); -/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ -JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags); -/* same as JS_Eval() but with an explicit 'this_obj' parameter */ -JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, - const char *filename, int eval_flags); -JSValue JS_GetGlobalObject(JSContext *ctx); -int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); -int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, int flags); -int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val, int flags); -int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, - uint32_t idx, JSValue val, int flags); -int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, - const char *prop, JSValue val, int flags); -int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue getter, JSValue setter, - int flags); -void JS_SetOpaque(JSValue obj, void *opaque); -void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); -void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); - -/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename); -#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */ -JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename, int flags); -JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, - JSValueConst replacer, JSValueConst space0); - -typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); -JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, - JSFreeArrayBufferDataFunc *free_func, void *opaque, - JS_BOOL is_shared); -JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); -void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); -uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); - -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_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, - size_t *pbytes_per_element); -typedef struct { - void *(*sab_alloc)(void *opaque, size_t size); - void (*sab_free)(void *opaque, void *ptr); - void (*sab_dup)(void *opaque, void *ptr); - void *sab_opaque; -} JSSharedArrayBufferFunctions; -void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, - const JSSharedArrayBufferFunctions *sf); - -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, - JSValueConst reason, - JS_BOOL is_handled, void *opaque); -void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); - -/* return != 0 if the JS code needs to be interrupted */ -typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); -void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); -/* if can_block is TRUE, Atomics.wait() can be used */ -void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); -/* set the [IsHTMLDDA] internal slot */ -void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); - -typedef struct JSModuleDef JSModuleDef; - -/* return the module specifier (allocated with js_malloc()) or NULL if - exception */ -typedef char *JSModuleNormalizeFunc(JSContext *ctx, - const char *module_base_name, - const char *module_name, void *opaque); -typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, - const char *module_name, void *opaque); - -/* module_normalize = NULL is allowed and invokes the default module - filename normalizer */ -void JS_SetModuleLoaderFunc(JSRuntime *rt, - JSModuleNormalizeFunc *module_normalize, - JSModuleLoaderFunc *module_loader, void *opaque); -/* return the import.meta object of a module */ -JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); -JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); -JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m); - -/* JS Job support */ - -typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); -int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv); - -JS_BOOL JS_IsJobPending(JSRuntime *rt); -int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); - -/* Object Writer/Reader (currently only used to handle precompiled code) */ -#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ -#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ -#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ -#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to - encode arbitrary object - graph */ -uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags); -uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags, uint8_t ***psab_tab, size_t *psab_tab_len); - -#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ -#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */ -#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ -#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ -JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags); -/* instantiate and evaluate a bytecode function. Only used when - reading a script or module with JS_ReadObject() */ -JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); -/* load the dependencies of the module 'obj'. Useful when JS_ReadObject() - returns a module. */ -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() */ -JSValue JS_LoadModule(JSContext *ctx, const char *basename, - const char *filename); - -/* C function definition */ -typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ - JS_CFUNC_generic, - JS_CFUNC_generic_magic, - JS_CFUNC_constructor, - JS_CFUNC_constructor_magic, - JS_CFUNC_constructor_or_func, - JS_CFUNC_constructor_or_func_magic, - JS_CFUNC_f_f, - JS_CFUNC_f_f_f, - JS_CFUNC_getter, - JS_CFUNC_setter, - JS_CFUNC_getter_magic, - JS_CFUNC_setter_magic, - JS_CFUNC_iterator_next, -} JSCFunctionEnum; - -typedef union JSCFunctionType { - JSCFunction *generic; - JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); - JSCFunction *constructor; - JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic); - JSCFunction *constructor_or_func; - double (*f_f)(double); - double (*f_f_f)(double, double); - JSValue (*getter)(JSContext *ctx, JSValueConst this_val); - JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); - JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); - JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic); - JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int *pdone, int magic); -} JSCFunctionType; - -JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic); -JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, - int length, int magic, int data_len, - JSValueConst *data); - -static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, - int length) -{ - return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); -} - -static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, - const char *name, - int length, JSCFunctionEnum cproto, int magic) -{ - return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); -} -void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst proto); - -/* C property definition */ - -typedef struct JSCFunctionListEntry { - const char *name; - uint8_t prop_flags; - uint8_t def_type; - int16_t magic; - union { - struct { - uint8_t length; /* XXX: should move outside union */ - uint8_t cproto; /* XXX: should move outside union */ - JSCFunctionType cfunc; - } func; - struct { - JSCFunctionType get; - JSCFunctionType set; - } getset; - struct { - const char *name; - int base; - } alias; - struct { - const struct JSCFunctionListEntry *tab; - int len; - } prop_list; - const char *str; - int32_t i32; - int64_t i64; - double f64; - } u; -} JSCFunctionListEntry; - -#define JS_DEF_CFUNC 0 -#define JS_DEF_CGETSET 1 -#define JS_DEF_CGETSET_MAGIC 2 -#define JS_DEF_PROP_STRING 3 -#define JS_DEF_PROP_INT32 4 -#define JS_DEF_PROP_INT64 5 -#define JS_DEF_PROP_DOUBLE 6 -#define JS_DEF_PROP_UNDEFINED 7 -#define JS_DEF_OBJECT 8 -#define JS_DEF_ALIAS 9 - -/* Note: c++ does not like nested designators */ -#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } -#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } -#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } -#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } -#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } -#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } -#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } } -#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } } -#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } } -#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } } -#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } -#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } } -#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } -#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } - -void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, - const JSCFunctionListEntry *tab, - int len); - -/* C module definition */ - -typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); - -JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, - JSModuleInitFunc *func); -/* can only be called before the module is instantiated */ -int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str); -int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len); -/* can only be called after the module is instantiated */ -int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, - JSValue val); -int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len); - -#undef js_unlikely -#undef js_force_inline - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_H */ diff --git a/nim.cfg b/nim.cfg index 5e252ec2..e481f8f6 100644 --- a/nim.cfg +++ b/nim.cfg @@ -3,6 +3,7 @@ -p:"lib/" -p:"lib/chame" -p:"lib/chagashi" +-p:"lib/monoucha" --experimental:"overloadableEnums" --mm:refc --include:"utils/myaddr" @@ -10,3 +11,4 @@ --styleCheck:usages --styleCheck:hint --threads:off +-d:monouchaUseOpt=1 diff --git a/src/bindings/constcharp.nim b/src/bindings/constcharp.nim deleted file mode 100644 index f1753147..00000000 --- a/src/bindings/constcharp.nim +++ /dev/null @@ -1,6 +0,0 @@ -type - cstringConstImpl {.importc: "const char*".} = cstring - cstringConst* = distinct cstringConstImpl - -proc `[]`*(s: cstringConst; i: int): char = cstring(s)[i] -proc `$`*(s: cstringConst): string {.borrow.} diff --git a/src/bindings/libregexp.nim b/src/bindings/libregexp.nim deleted file mode 100644 index fe603908..00000000 --- a/src/bindings/libregexp.nim +++ /dev/null @@ -1,30 +0,0 @@ -type - LREFlag* {.size: sizeof(cint).} = enum - LRE_FLAG_GLOBAL = "g" - LRE_FLAG_IGNORECASE = "i" - LRE_FLAG_MULTILINE = "m" - LRE_FLAG_DOTALL = "s" - LRE_FLAG_UNICODE = "u" - LRE_FLAG_STICKY = "y" - - LREFlags* = set[LREFlag] - -func toCInt*(flags: LREFlags): cint = - cint(cast[uint8](flags)) - -func toLREFlags*(flags: cint): LREFlags = - cast[LREFlags](flags) - -{.passc: "-Ilib/".} - -{.push header: "quickjs/libregexp.h", importc.} -proc lre_compile*(plen: ptr cint; error_msg: cstring; error_msg_size: cint; - buf: cstring; buf_len: csize_t; re_flags: cint; opaque: pointer): ptr uint8 - -proc lre_exec*(capture: ptr ptr uint8; bc_buf, cbuf: ptr uint8; - cindex, clen, cbuf_type: cint; opaque: pointer): cint - -proc lre_get_capture_count*(bc_buf: ptr uint8): cint - -proc lre_get_flags*(bc_buf: ptr uint8): cint -{.pop.} diff --git a/src/bindings/libunicode.nim b/src/bindings/libunicode.nim deleted file mode 100644 index 18d146fe..00000000 --- a/src/bindings/libunicode.nim +++ /dev/null @@ -1,44 +0,0 @@ -type - DynBufReallocFunc = proc(opaque, p: pointer; size: csize_t): pointer {.cdecl.} - - CharRange* = object - len*: cint # in points, always even - size*: cint - points*: ptr uint32 # points sorted by increasing value - mem_opaque*: pointer - realloc_func*: DynBufReallocFunc - - UnicodeNormalizationEnum* {.size: sizeof(cint).} = enum - UNICODE_NFC, UNICODE_NFD, UNICODE_NKFC, UNICODE_NKFD - -{.passc: "-Ilib/".} - -{.push header: "quickjs/libunicode.h", importc.} - -proc cr_init*(cr: ptr CharRange; mem_opaque: pointer; - realloc_func: DynBufReallocFunc) - -proc cr_free*(cr: ptr CharRange) - -proc unicode_normalize*(pdst: ptr ptr uint32; src: ptr uint32; src_len: cint; - n_type: UnicodeNormalizationEnum; opaque: pointer; - realloc_func: DynBufReallocFunc): cint - -proc unicode_script*(cr: ptr CharRange; script_name: cstring; is_ext: cint): - cint -proc unicode_prop*(cr: ptr CharRange; prop_name: cstring): cint -proc unicode_general_category*(cr: ptr CharRange; gc_name: cstring): cint - -const LRE_CC_RES_LEN_MAX* = 3 - -# conv_type: -# 0 = to upper -# 1 = to lower -# 2 = case folding -# res must be an array of LRE_CC_RES_LEN_MAX -proc lre_case_conv*(res: ptr UncheckedArray[uint32]; c: uint32; - conv_type: cint): cint - -proc lre_is_space*(c: uint32): cint - -{.pop.} diff --git a/src/bindings/quickjs.nim b/src/bindings/quickjs.nim deleted file mode 100644 index 20dd048b..00000000 --- a/src/bindings/quickjs.nim +++ /dev/null @@ -1,573 +0,0 @@ -import bindings/constcharp - -const qjsheader = "quickjs/quickjs.h" - -{.passc: "-Ilib/".} -{.passl: "-Llib/ -lquickjs -lm -lpthread".} - -const ## all tags with a reference count are negative - JS_TAG_FIRST* = -10 ## first negative tag - JS_TAG_BIG_INT* = -10 - JS_TAG_BIG_FLOAT* = -9 - JS_TAG_SYMBOL* = -8 - JS_TAG_STRING* = -7 - JS_TAG_SHAPE* = -6 ## used internally during GC - JS_TAG_ASYNC_FUNCTION* = -5 ## used internally during GC - JS_TAG_VAR_REF* = -4 ## used internally during GC - JS_TAG_MODULE* = -3 ## used internally - JS_TAG_FUNCTION_BYTECODE* = -2 ## used internally - JS_TAG_OBJECT* = -1 - JS_TAG_INT* = 0 - JS_TAG_BOOL* = 1 - JS_TAG_NULL* = 2 - JS_TAG_UNDEFINED* = 3 - JS_TAG_UNINITIALIZED* = 4 - JS_TAG_CATCH_OFFSET* = 5 - JS_TAG_EXCEPTION* = 6 - JS_TAG_FLOAT64* = 7 ## any larger tag is FLOAT64 if JS_NAN_BOXING - -when sizeof(int) < sizeof(int64): - {.passc: "-DJS_NAN_BOXING".} - type - JSValue* {.importc, header: qjsheader.} = distinct uint64 - - template JS_VALUE_GET_TAG*(v: untyped): int32 = - cast[int32](cast[uint64](v) shr 32) - - template JS_VALUE_GET_PTR*(v: untyped): pointer = - cast[pointer](v) - - template JS_MKVAL*(t, val: untyped): JSValue = - JSValue((cast[uint64](int64(t)) shl 32) or uint32(val)) - - template JS_MKPTR*(t, p: untyped): JSValue = - JSValue((cast[uint64](int64(t)) shl 32) or cast[uint](p)) - - proc `==`*(a, b: JSValue): bool {.borrow.} -else: - type - JSValueUnion* {.importc, header: qjsheader, union.} = object - int32*: int32 - float64*: float64 - `ptr`*: pointer - JSValue* {.importc, header: qjsheader.} = object - u*: JSValueUnion - tag*: int64 - - template JS_VALUE_GET_TAG*(v: untyped): int32 = - cast[int32](v.tag) - - template JS_VALUE_GET_PTR*(v: untyped): pointer = - cast[pointer](v.u) - - template JS_MKVAL*(t, val: untyped): JSValue = - JSValue(u: JSValueUnion(`int32`: val), tag: t) - - template JS_MKPTR*(t, p: untyped): JSValue = - JSValue(u: JSValueUnion(`ptr`: p), tag: t) - -type - JSRuntimeT {.importc: "JSRuntime", header: qjsheader, - incompleteStruct.} = object - JSContextT {.importc: "JSContext", header: qjsheader, - incompleteStruct.} = object - JSModuleDefT {.importc: "JSModuleDef", header: qjsheader, - incompleteStruct.} = object - - JSRuntime* = ptr JSRuntimeT - JSContext* = ptr JSContextT - JSModuleDef* = ptr JSModuleDefT - JSCFunction* = proc(ctx: JSContext; this_val: JSValue; argc: cint; - argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.} - JSCFunctionData* = proc(ctx: JSContext; this_val: JSValue; argc: cint; - argv: ptr UncheckedArray[JSValue]; magic: cint; - func_data: ptr UncheckedArray[JSValue]): JSValue {.cdecl.} - JSGetterFunction* = proc(ctx: JSContext; this_val: JSValue): JSValue {.cdecl.} - JSSetterFunction* = proc(ctx: JSContext; this_val, val: JSValue): - JSValue {.cdecl.} - JSGetterMagicFunction* = proc(ctx: JSContext; this_val: JSValue; magic: cint): - JSValue {.cdecl.} - JSSetterMagicFunction* = proc(ctx: JSContext; this_val, val: JSValue; - magic: cint): JSValue {.cdecl.} - JSInterruptHandler* = proc(rt: JSRuntime; opaque: pointer): cint {.cdecl.} - JSClassID* = uint32 - JSAtom* = distinct uint32 - JSClassFinalizer* = proc(rt: JSRuntime; val: JSValue) {.cdecl.} - JSClassCheckDestroy* = proc(rt: JSRuntime; val: JSValue): JS_BOOL {.cdecl.} - JSClassGCMark* = proc(rt: JSRuntime; val: JSValue; mark_func: JS_MarkFunc) - {.cdecl.} - JS_MarkFunc* = proc(rt: JSRuntime; gp: ptr JSGCObjectHeader) {.cdecl.} - JSModuleNormalizeFunc* = proc(ctx: JSContext; module_base_name, - module_name: cstringConst; opaque: pointer): cstring {.cdecl.} - JSModuleLoaderFunc* = proc(ctx: JSContext; module_name: cstringConst, - opaque: pointer): JSModuleDef {.cdecl.} - JSJobFunc* = proc(ctx: JSContext; argc: cint; - argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.} - JSGCObjectHeader* {.importc, header: qjsheader.} = object - JSFreeArrayBufferDataFunc* = proc(rt: JSRuntime; opaque, p: pointer) {.cdecl.} - - JSPropertyDescriptor* {.importc, header: qjsheader.} = object - flags*: cint - value*: JSValue - getter*: JSValue - setter*: JSValue - - JSClassExoticMethods* {.importc, header: qjsheader.} = object - get_own_property*: proc(ctx: JSContext; desc: ptr JSPropertyDescriptor; - obj: JSValue; prop: JSAtom): cint {.cdecl.} - get_own_property_names*: proc(ctx: JSContext; - ptab: ptr ptr UncheckedArray[JSPropertyEnum]; plen: ptr uint32; - obj: JSValue): cint {.cdecl.} - delete_property*: proc(ctx: JSContext; obj: JSValue; prop: JSAtom): cint - {.cdecl.} - define_own_property*: proc(ctx: JSContext; this_obj: JSValue; prop: JSAtom; - val, getter, setter: JSValue; flags: cint): cint {.cdecl.} - has_property*: proc(ctx: JSContext; obj: JSValue; atom: JSAtom): cint - {.cdecl.} - get_property*: proc(ctx: JSContext; obj: JSValue; atom: JSAtom; - receiver: JSValue; flags: cint): JSValue {.cdecl.} - set_property*: proc(ctx: JSContext; obj: JSValue; atom: JSAtom; - value, receiver: JSValue; flags: cint): cint {.cdecl.} - - JSClassExoticMethodsConst* {.importc: "const JSClassExoticMethods *", - header: qjsheader.} = ptr JSClassExoticMethods - - JSRuntimeCleanUpFunc* {.importc.} = proc(rt: JSRuntime) {.cdecl.} - - JSClassDef* {.importc, header: qjsheader.} = object - class_name*: cstring - finalizer*: JSClassFinalizer - gc_mark*: JSClassGCMark - call*: pointer - exotic*: JSClassExoticMethodsConst - can_destroy*: JSClassCheckDestroy - - JSClassDefConst* {.importc: "const JSClassDef *", - header: qjsheader.} = ptr JSClassDef - - JSMemoryUsage* = object - malloc_size*, malloc_limit*, memory_used_size*: int64 - malloc_count*: int64 - memory_used_count*: int64 - atom_count*, atom_size*: int64 - str_count*, str_size*: int64 - obj_count*, obj_size*: int64 - prop_count*, prop_size*: int64 - shape_count*, shape_size*: int64 - js_func_count*, js_func_size*, js_func_code_size*: int64 - js_func_pc2line_count*, js_func_pc2line_size*: int64 - c_func_count*, array_count*: int64 - fast_array_count*, fast_array_elements*: int64 - binary_object_count*, binary_object_size*: int64 - - JSCFunctionEnum* {.size: sizeof(uint8).} = enum - JS_CFUNC_generic, JS_CFUNC_generic_magic, JS_CFUNC_constructor, - JS_CFUNC_constructor_magic, JS_CFUNC_constructor_or_func, - JS_CFUNC_constructor_or_func_magic, JS_CFUNC_f_f, JS_CFUNC_f_f_f, - JS_CFUNC_getter, JS_CFUNC_setter, JS_CFUNC_getter_magic, - JS_CFUNC_setter_magic, JS_CFUNC_iterator_next - - JSCFunctionType* {.importc, union.} = object - generic*: JSCFunction - getter*: JSGetterFunction - setter*: JSSetterFunction - getter_magic*: JSGetterMagicFunction - setter_magic*: JSSetterMagicFunction - - JSCFunctionListEntryFunc = object - length*: uint8 - cproto*: JSCFunctionEnum - cfunc*: JSCFunctionType - - JSCFunctionListEntryGetSet = object - get*: JSCFunctionType - set*: JSCFunctionType - - JSCFunctionListEntryAlias = object - name: cstring - base: cint - - JSCFunctionListEntryPropList = object - tab: ptr UncheckedArray[JSCFunctionListEntry] - len: cint - - JSCFunctionListEntryU* {.union.} = object - `func`* {.importc: "func".}: JSCFunctionListEntryFunc - getset: JSCFunctionListEntryGetSet - alias: JSCFunctionListEntryAlias - prop_list: JSCFunctionListEntryPropList - str: cstring - i32: int32 - i64: int64 - f64: cdouble - - JSCFunctionListEntry* {.importc.} = object - name*: cstring - prop_flags*: uint8 - def_type*: uint8 - magic*: int16 - u* {.importc.}: JSCFunctionListEntryU - - JSRefCountHeader* {.importc.} = object - ref_count* {.importc.}: cint - - JS_BOOL* = distinct cint - - JSPropertyEnum* {.importc.} = object - is_enumerable*: JS_BOOL - atom*: JSAtom - - JSClassEnum* {.size: sizeof(uint32).} = enum - JS_CLASS_OBJECT = 1 - JS_CLASS_ARRAY - JS_CLASS_ERROR - - JSMallocState* {.importc.} = object - malloc_count*: csize_t - malloc_size*: csize_t - malloc_limit*: csize_t - opaque*: pointer - - JSMallocFunctions* {.importc.} = object - js_malloc*: proc(s: ptr JSMallocState; size: csize_t): pointer {.cdecl.} - js_free*: proc(s: ptr JSMallocState; p: pointer) {.cdecl.} - js_realloc*: proc(s: ptr JSMallocState; p: pointer; size: csize_t): pointer - {.cdecl.} - js_malloc_usable_size*: proc(p: pointer) {.cdecl.} - -func `==`*(a, b: JSAtom): bool {.borrow.} - -converter toBool*(js: JS_BOOL): bool {.inline.} = - cast[cint](js) != 0 - -converter toJSBool*(b: bool): JS_BOOL {.inline.} = - cast[JS_BOOL](cint(b)) - -converter toJSClassID*(e: JSClassEnum): JSClassID {.inline.} = - JSClassID(e) - -const - JS_NULL* = JS_MKVAL(JS_TAG_NULL, 0) - JS_UNDEFINED* = JS_MKVAL(JS_TAG_UNDEFINED, 0) - JS_FALSE* = JS_MKVAL(JS_TAG_BOOL, 0) - JS_TRUE* = JS_MKVAL(JS_TAG_BOOL, 1) - JS_EXCEPTION* = JS_MKVAL(JS_TAG_EXCEPTION, 0) - JS_UNINITIALIZED* = JS_MKVAL(JS_TAG_UNINITIALIZED, 0) - -const - JS_EVAL_TYPE_GLOBAL* = (0 shl 0) ## global code (default) - JS_EVAL_TYPE_MODULE* = (1 shl 0) ## module code - JS_EVAL_TYPE_DIRECT* = (2 shl 0) ## direct call (internal use) - JS_EVAL_TYPE_INDIRECT* = (3 shl 0) ## indirect call (internal use) - JS_EVAL_TYPE_MASK* = (3 shl 0) - JS_EVAL_FLAG_SHEBANG* = (1 shl 2) ## skip first line beginning with '#!' - JS_EVAL_FLAG_STRICT* = (1 shl 3) ## force 'strict' mode - JS_EVAL_FLAG_STRIP* = (1 shl 4) ## force 'strip' mode - JS_EVAL_FLAG_COMPILE_ONLY* = (1 shl 5) ## internal use - -const - JS_DEF_CFUNC* = 0 - JS_DEF_CGETSET* = 1 - JS_DEF_CGETSET_MAGIC* = 2 - JS_DEF_PROP_STRING* = 3 - JS_DEF_PROP_INT32* = 4 - JS_DEF_PROP_INT64* = 5 - JS_DEF_PROP_DOUBLE* = 6 - JS_DEF_PROP_UNDEFINED* = 7 - JS_DEF_OBJECT* = 8 - JS_DEF_ALIAS* = 9 - -const - JS_PROP_CONFIGURABLE* = (1 shl 0) - JS_PROP_WRITABLE* = (1 shl 1) - JS_PROP_ENUMERABLE* = (1 shl 2) - JS_PROP_C_W_E* = (JS_PROP_CONFIGURABLE or JS_PROP_WRITABLE or - JS_PROP_ENUMERABLE) - JS_PROP_LENGTH* = (1 shl 3) # used internally in Arrays - JS_PROP_TMASK* = (3 shl 4) # mask for NORMAL, GETSET, VARREF, AUTOINIT - JS_PROP_NORMAL* = (0 shl 4) - JS_PROP_GETSET* = (1 shl 4) - JS_PROP_VARREF* = (2 shl 4) # used internally - JS_PROP_AUTOINIT* = (3 shl 4) # used internally - JS_PROP_THROW* = (1 shl 14) - -const - JS_GPN_STRING_MASK* = (1 shl 0) - JS_GPN_SYMBOL_MASK* = (1 shl 1) - JS_GPN_PRIVATE_MASK* = (1 shl 2) - JS_GPN_ENUM_ONLY* = (1 shl 3) - JS_GPN_SET_ENUM* = (1 shl 4) - -const - JS_PARSE_JSON_EXT* = (1 shl 0) - -template JS_CFUNC_DEF*(n: string; len: uint8; func1: JSCFunction): - JSCFunctionListEntry = - JSCFunctionListEntry(name: cstring(n), - prop_flags: JS_PROP_WRITABLE or JS_PROP_CONFIGURABLE, - def_type: JS_DEF_CFUNC, - u: JSCFunctionListEntryU( - `func`: JSCFunctionListEntryFunc( - length: len, - cproto: JS_CFUNC_generic, - cfunc: JSCFunctionType(generic: func1)))) - -template JS_CFUNC_DEF_NOCONF*(n: string; len: uint8; func1: JSCFunction): - JSCFunctionListEntry = - JSCFunctionListEntry(name: cstring(n), - prop_flags: JS_PROP_ENUMERABLE, - def_type: JS_DEF_CFUNC, - u: JSCFunctionListEntryU( - `func`: JSCFunctionListEntryFunc( - length: len, - cproto: JS_CFUNC_generic, - cfunc: JSCFunctionType(generic: func1)))) - -template JS_CGETSET_DEF*(n: string; fgetter: JSGetterFunction; - fsetter: JSSetterFunction): JSCFunctionListEntry = - JSCFunctionListEntry(name: cstring(n), - prop_flags: JS_PROP_CONFIGURABLE, - def_type: JS_DEF_CGETSET, - u: JSCFunctionListEntryU( - getset: JSCFunctionListEntryGetSet( - get: JSCFunctionType(getter: fgetter), - set: JSCFunctionType(setter: fsetter)))) - -template JS_CGETSET_DEF_NOCONF*(n: string; fgetter: JSGetterFunction; - fsetter: JSSetterFunction): JSCFunctionListEntry = - JSCFunctionListEntry(name: cstring(n), - prop_flags: JS_PROP_ENUMERABLE, - def_type: JS_DEF_CGETSET, - u: JSCFunctionListEntryU( - getset: JSCFunctionListEntryGetSet( - get: JSCFunctionType(getter: fgetter), - set: JSCFunctionType(setter: fsetter)))) - -template JS_CGETSET_MAGIC_DEF*(n: string; fgetter, fsetter: typed; - m: int16): JSCFunctionListEntry = - JSCFunctionListEntry(name: cstring(n), - prop_flags: JS_PROP_CONFIGURABLE, - def_type: JS_DEF_CGETSET_MAGIC, - magic: m, - u: JSCFunctionListEntryU( - getset: JSCFunctionListEntryGetSet( - get: JSCFunctionType(getter_magic: fgetter), - set: JSCFunctionType(setter_magic: fsetter)))) - -{.push header: qjsheader, importc, cdecl.} - -proc JS_NewRuntime*(): JSRuntime -proc JS_NewRuntime2*(mf: ptr JSMallocFunctions; opaque: pointer): JSRuntime -proc JS_FreeRuntime*(rt: JSRuntime) -proc JS_GetRuntime*(ctx: JSContext): JSRuntime - -proc JS_ComputeMemoryUsage*(rt: JSRuntime; s: ptr JSMemoryUsage) -proc JS_RunGC*(rt: JSRuntime) - -proc JS_NewContext*(rt: JSRuntime): JSContext -proc JS_NewContextRaw*(rt: JSRuntime): JSContext -proc JS_FreeContext*(ctx: JSContext) - -proc JS_GetGlobalObject*(ctx: JSContext): JSValue -proc JS_IsInstanceOf*(ctx: JSContext; val: JSValue; obj: JSValue): cint - -proc JS_NewArray*(ctx: JSContext): JSValue -proc JS_NewObject*(ctx: JSContext): JSValue -proc JS_NewObjectClass*(ctx: JSContext; class_id: JSClassID): JSValue -proc JS_NewObjectProto*(ctx: JSContext; proto: JSValue): JSValue -proc JS_NewObjectProtoClass*(ctx: JSContext; proto: JSValue; - class_id: JSClassID): JSValue -proc JS_NewPromiseCapability*(ctx: JSContext; - resolving_funcs: ptr UncheckedArray[JSValue]): JSValue -proc JS_SetOpaque*(obj: JSValue; opaque: pointer) -proc JS_GetOpaque*(obj: JSValue; class_id: JSClassID): pointer -proc JS_GetOpaque2*(ctx: JSContext; obj: JSValue; class_id: JSClassID): pointer -proc JS_GetClassID*(obj: JSValue): JSClassID - -proc JS_ParseJSON*(ctx: JSContext; buf: cstring; buf_len: csize_t; - filename: cstring): JSValue -proc JS_ParseJSON2*(ctx: JSContext; buf: cstring; buf_len: csize_t; - filename: cstring; flags: cint): JSValue - -proc JS_NewArrayBuffer*(ctx: JSContext; buf: ptr UncheckedArray[uint8]; - len: csize_t; free_func: JSFreeArrayBufferDataFunc; opaque: pointer; - is_shared: JS_BOOL): JSValue -proc JS_GetArrayBuffer*(ctx: JSContext; psize: ptr csize_t; obj: JSValue): - ptr uint8 -proc JS_GetTypedArrayBuffer*(ctx: JSContext; obj: JSValue; - pbyte_offset, pbyte_length, pbytes_per_element: ptr csize_t): JSValue - -proc JS_NewClassID*(pclass_id: ptr JSClassID): JSClassID -proc JS_NewClass*(rt: JSRuntime; class_id: JSClassID; - class_def: ptr JSClassDef): cint -proc JS_IsRegisteredClass*(rt: JSRuntime; class_id: JSClassID): cint -proc JS_SetClassProto*(ctx: JSContext; class_id: JSClassID; obj: JSValue) -proc JS_GetClassProto*(ctx: JSContext; class_id: JSClassID): JSValue -proc JS_SetConstructor*(ctx: JSContext; func_obj: JSValue; proto: JSValue) -proc JS_SetPrototype*(ctx: JSContext; obj: JSValue; proto_val: JSValue): cint -proc JS_GetPrototype*(ctx: JSContext; val: JSValue): JSValue - -proc JS_NewBool*(ctx: JSContext; val: JS_BOOL): JSValue -proc JS_NewInt32*(ctx: JSContext; val: int32): JSValue -proc JS_NewCatchOffset*(ctx: JSContext; val: int32): JSValue -proc JS_NewInt64*(ctx: JSContext; val: int64): JSValue -proc JS_NewUint32*(ctx: JSContext; val: uint32): JSValue -proc JS_NewBigInt64*(ctx: JSContext; val: int64): JSValue -proc JS_NewBigUInt64*(ctx: JSContext; val: uint64): JSValue -proc JS_NewFloat64*(ctx: JSContext; val: cdouble): JSValue - -const JS_ATOM_NULL* = JSAtom(0) - -proc JS_NewAtomLen*(ctx: JSContext; str: cstring; len: csize_t): JSAtom -proc JS_NewAtomUInt32*(ctx: JSContext; u: uint32): JSAtom -proc JS_ValueToAtom*(ctx: JSContext; val: JSValue): JSAtom -proc JS_AtomToValue*(ctx: JSContext; atom: JSAtom): JSValue -proc JS_AtomToCString*(ctx: JSContext; atom: JSAtom): cstring -proc JS_FreeAtom*(ctx: JSContext; atom: JSAtom) -proc JS_FreeAtomRT*(rt: JSRuntime; atom: JSAtom) - -proc JS_NewCFunction2*(ctx: JSContext; cfunc: JSCFunction; name: cstring; - length: cint; proto: JSCFunctionEnum; magic: cint): JSValue -proc JS_NewCFunctionData*(ctx: JSContext; cfunc: JSCFunctionData; - length, magic, data_len: cint; data: ptr UncheckedArray[JSValue]): JSValue -proc JS_NewCFunction*(ctx: JSContext; cfunc: JSCFunction; name: cstring; - length: cint): JSValue - -proc JS_NewString*(ctx: JSContext; str: cstring): JSValue -proc JS_NewStringLen*(ctx: JSContext; str: cstring; len1: csize_t): JSValue -proc JS_NewAtomString*(ctx: JSContext; str: cstring): JSValue -proc JS_ToString*(ctx: JSContext; val: JSValue): JSValue - -proc JS_SetProperty*(ctx: JSContext; this_obj: JSValue; prop: JSAtom; - val: JSValue): cint -proc JS_SetPropertyUint32*(ctx: JSContext; this_obj: JSValue; idx: uint32; - val: JSValue): cint -proc JS_SetPropertyInt64*(ctx: JSContext; this_obj: JSValue; idx: int64; - val: JSValue): cint -proc JS_SetPropertyStr*(ctx: JSContext; this_obj: JSValue; prop: cstring; - val: JSValue): cint -proc JS_SetPropertyFunctionList*(ctx: JSContext; obj: JSValue; - tab: ptr JSCFunctionListEntry; len: cint) -proc JS_GetProperty*(ctx: JSContext; this_obj: JSValue; prop: JSAtom): JSValue -proc JS_GetPropertyStr*(ctx: JSContext; this_obj: JSValue; prop: cstring): - JSValue -proc JS_GetPropertyUint32*(ctx: JSContext; this_obj: JSValue; idx: uint32): - JSValue -proc JS_GetOwnPropertyNames*(ctx: JSContext; - ptab: ptr ptr UncheckedArray[JSPropertyEnum]; plen: ptr uint32; - obj: JSValue; flags: cint): cint - -proc JS_GetOwnProperty*(ctx: JSContext; desc: ptr JSPropertyDescriptor; - obj: JSValue; prop: JSAtom): cint -proc JS_Call*(ctx: JSContext; func_obj, this_obj: JSValue; argc: cint; - argv: ptr UncheckedArray[JSValue]): JSValue -proc JS_NewObjectFromCtor*(ctx: JSContext; ctor: JSValue; - class_id: JSClassID): JSValue -proc JS_Invoke*(ctx: JSContext; this_obj: JSValue; atom: JSAtom; argc: cint; - argv: ptr UncheckedArray[JSValue]): JSValue -proc JS_CallConstructor*(ctx: JSContext; func_obj: JSValue; argc: cint; - argv: ptr UncheckedArray[JSValue]): JSValue - -proc JS_DefineProperty*(ctx: JSContext; this_obj: JSValue; prop: JSAtom; - val, getter, setter: JSValue; flags: cint): cint -proc JS_DefinePropertyValue*(ctx: JSContext; this_obj: JSValue; prop: JSAtom; - val: JSValue; flags: cint): cint -proc JS_DefinePropertyValueUint32*(ctx: JSContext; this_obj: JSValue; - idx: uint32; val: JSValue; flags: cint): cint -proc JS_DefinePropertyValueStr*(ctx: JSContext; this_obj: JSValue; - prop: cstring; val: JSValue; flags: cint): cint -proc JS_DefinePropertyValueGetSet*(ctx: JSContext; this_obj: JSValue; - prop: JSAtom; getter, setter: JSValue; flags: cint): cint - -proc JS_FreeValue*(ctx: JSContext; v: JSValue) -proc JS_FreeValueRT*(rt: JSRuntime; v: JSValue) -proc JS_DupValue*(ctx: JSContext; v: JSValue): JSValue - -proc JS_ToBool*(ctx: JSContext; val: JSValue): cint # return -1 for JS_EXCEPTION -proc JS_ToInt32*(ctx: JSContext; pres: ptr int32; val: JSValue): cint -proc JS_ToUint32*(ctx: JSContext; pres: ptr uint32; val: JSValue): cint -proc JS_ToInt64*(ctx: JSContext; pres: ptr int64; val: JSValue): cint -proc JS_ToIndex*(ctx: JSContext; plen: ptr uint64; val: JSValue): cint -proc JS_ToFloat64*(ctx: JSContext; pres: ptr float64; val: JSValue): cint -# return an exception if 'val' is a Number -proc JS_ToBigInt64*(ctx: JSContext; pres: ptr int64; val: JSValue): cint -# same as JS_ToInt64 but allow BigInt -proc JS_ToInt64Ext*(ctx: JSContext; pres: ptr int64; val: JSValue): cint - -proc JS_ToCStringLen*(ctx: JSContext; plen: ptr csize_t; val1: JSValue): cstring -proc JS_ToCString*(ctx: JSContext; val1: JSValue): cstring -proc JS_FreeCString*(ctx: JSContext, `ptr`: cstring) - -proc JS_NewNarrowStringLen*(ctx: JSContext; s: cstring; len: csize_t): JSValue -proc JS_IsStringWideChar*(str: JSValue): JS_BOOL -proc JS_GetNarrowStringBuffer*(str: JSValue): ptr UncheckedArray[uint8] -proc JS_GetStringLength*(str: JSValue): uint32 - -proc JS_Eval*(ctx: JSContext; input: cstring; input_len: csize_t; - filename: cstring; eval_flags: cint): JSValue -proc JS_EvalFunction*(ctx: JSContext; val: JSValue): JSValue -proc JS_SetInterruptHandler*(rt: JSRuntime; cb: JSInterruptHandler; - opaque: pointer) -proc JS_SetCanBlock*(rt: JSRuntime; can_block: JS_BOOL) -proc JS_SetIsHTMLDDA*(ctx: JSContext; obj: JSValue) - -proc JS_IsNumber*(v: JSValue): JS_BOOL -proc JS_IsBigInt*(v: JSValue): JS_BOOL -proc JS_IsBigFloat*(v: JSValue): JS_BOOL -proc JS_IsBigDecimal*(v: JSValue): JS_BOOL -proc JS_IsBool*(v: JSValue): JS_BOOL -proc JS_IsNull*(v: JSValue): JS_BOOL -proc JS_IsUndefined*(v: JSValue): JS_BOOL -proc JS_IsException*(v: JSValue): JS_BOOL -proc JS_IsUninitialized*(v: JSValue): JS_BOOL -proc JS_IsString*(v: JSValue): JS_BOOL -proc JS_IsSymbol*(v: JSValue): JS_BOOL -proc JS_IsObject*(v: JSValue): JS_BOOL - -proc JS_IsFunction*(ctx: JSContext; val: JSValue): JS_BOOL -proc JS_IsArray*(ctx: JSContext; v: JSValue): cint - -proc JS_Throw*(ctx: JSContext; obj: JSValue): JSValue -proc JS_GetException*(ctx: JSContext): JSValue -proc JS_IsError*(ctx: JSContext; v: JSValue): JS_BOOL -proc JS_SetUncatchableError*(ctx: JSContext; val: JSValue; flag: JS_BOOL) -proc JS_ResetUncatchableError*(ctx: JSContext) -proc JS_NewError*(ctx: JSContext): JSValue -proc JS_ThrowSyntaxError*(ctx: JSContext; fmt: cstring): JSValue {.varargs, - discardable.} -proc JS_ThrowTypeError*(ctx: JSContext; fmt: cstring): JSValue {.varargs, - discardable.} -proc JS_ThrowReferenceError*(ctx: JSContext; fmt: cstring): JSValue {.varargs, - discardable.} -proc JS_ThrowRangeError*(ctx: JSContext; fmt: cstring): JSValue {.varargs, - discardable.} -proc JS_ThrowInternalError*(ctx: JSContext; fmt: cstring): JSValue {.varargs, - discardable.} - -proc JS_SetModuleLoaderFunc*(rt: JSRuntime; - module_normalize: JSModuleNormalizeFunc; module_loader: JSModuleLoaderFunc; - opaque: pointer) -proc JS_GetImportMeta*(ctx: JSContext; m: JSModuleDef): JSValue -proc JS_GetModuleName*(ctx: JSContext; m: JSModuleDef): JSAtom - -proc JS_EnqueueJob*(ctx: JSContext; job_func: JSJobFunc; argc: cint; - argv: ptr UncheckedArray[JSValue]): cint -proc JS_IsJobPending*(rt: JSRuntime): JS_BOOL -proc JS_ExecutePendingJob*(rt: JSRuntime; pctx: ptr JSContext): cint - -proc JS_GetRuntimeOpaque*(rt: JSRuntime): pointer -proc JS_SetRuntimeOpaque*(rt: JSRuntime; p: pointer) -proc JS_SetRuntimeCleanUpFunc*(rt: JSRuntime; - cleanup_func: JSRuntimeCleanUpFunc) - -proc JS_SetContextOpaque*(ctx: JSContext; opaque: pointer) -proc JS_GetContextOpaque*(ctx: JSContext): pointer - -proc js_malloc*(ctx: JSContext; size: csize_t): pointer -proc js_mallocz*(ctx: JSContext; size: csize_t): pointer -proc js_realloc*(ctx: JSContext; p: pointer; size: csize_t): pointer -proc js_free_rt*(rt: JSRuntime; p: pointer) -proc js_free*(ctx: JSContext; p: pointer) - -proc js_strdup*(ctx: JSContext; str: cstring): cstring -{.pop.} diff --git a/src/config/chapath.nim b/src/config/chapath.nim index d9ba3c50..8397cb3d 100644 --- a/src/config/chapath.nim +++ b/src/config/chapath.nim @@ -2,10 +2,10 @@ import std/options import std/os import std/posix -import js/fromjs -import js/javascript -import js/jserror -import js/tojs +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/tojs import types/opt import utils/twtstr diff --git a/src/config/config.nim b/src/config/config.nim index 063c32fc..35ca1a17 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -4,26 +4,27 @@ import std/streams import std/strutils import std/tables -import bindings/quickjs import config/chapath import config/mailcap import config/mimetypes import config/toml -import js/jserror -import js/fromjs -import js/javascript import js/jscolor -import js/jstypes -import js/jspropenumlist -import js/jsregex -import js/tojs import loader/headers +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/jspropenumlist +import monoucha/jsregex +import monoucha/jstypes +import monoucha/quickjs +import monoucha/tojs import types/cell import types/color import types/cookie import types/opt import types/urimethodmap import types/url +import utils/regexutils import utils/twtstr import chagashi/charset diff --git a/src/html/chadombuilder.nim b/src/html/chadombuilder.nim index 3c5b2c12..708b127c 100644 --- a/src/html/chadombuilder.nim +++ b/src/html/chadombuilder.nim @@ -1,19 +1,19 @@ import std/deques import std/options +import std/tables +import chagashi/charset +import chame/htmlparser +import chame/tags import html/catom import html/dom import html/enums -import js/jserror -import js/fromjs -import js/javascript +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import types/opt import types/url -import chagashi/charset - -import chame/htmlparser -import chame/tags - export htmlparser.ParseResult # DOMBuilder implementation for Chawan. @@ -260,6 +260,9 @@ proc parseHTMLFragment*(element: Element; s: string): seq[Node] = builder.finish() return root.childList +# Forward declaration hack +domParseHTMLFragment = parseHTMLFragment + proc newHTML5ParserWrapper*(window: Window; url: URL; factory: CAtomFactory; confidence: CharsetConfidence; charset: Charset): HTML5ParserWrapper = let opts = HTML5ParserOpts[Node, CAtom](scripting: window.settings.scripting) diff --git a/src/html/dom.nim b/src/html/dom.nim index af839cea..6eb1079f 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -22,19 +22,20 @@ import io/dynstream import io/promise import js/console import js/domexception -import js/fromjs -import js/javascript -import js/jserror -import js/jsopaque -import js/jspropenumlist -import js/jsutils import js/timeout -import js/tojs import loader/loader import loader/request +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/jsopaque +import monoucha/jspropenumlist +import monoucha/jsutils +import monoucha/tojs import types/blob import types/color import types/matrix +import types/opt import types/referrer import types/url import types/vector @@ -4189,11 +4190,14 @@ proc toBlob(ctx: JSContext; this: HTMLCanvasElement; callback: JSValue; JS_FreeValue(ctx, res) return JS_UNDEFINED -import html/chadombuilder +# Forward declaration hack +var domParseHTMLFragment*: proc(element: Element; s: string): seq[Node] + {.nimcall.} + # https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm proc fragmentParsingAlgorithm*(element: Element; s: string): DocumentFragment = #TODO xml - let newChildren = parseHTMLFragment(element, s) + let newChildren = domParseHTMLFragment(element, s) let fragment = element.document.newDocumentFragment() for child in newChildren: fragment.append(child) diff --git a/src/html/env.nim b/src/html/env.nim index 76aa2007..fadcd52c 100644 --- a/src/html/env.nim +++ b/src/html/env.nim @@ -1,6 +1,5 @@ import std/selectors -import bindings/quickjs import html/catom import html/chadombuilder import html/dom @@ -14,17 +13,19 @@ import js/base64 import js/console import js/domexception import js/encoding -import js/jserror import js/intl -import js/javascript -import js/jstypes import js/timeout -import js/tojs import loader/headers import loader/loader import loader/request import loader/response +import monoucha/javascript +import monoucha/jserror +import monoucha/jstypes +import monoucha/quickjs +import monoucha/tojs import types/blob +import types/opt import types/url import types/winattrs diff --git a/src/html/event.nim b/src/html/event.nim index 7ae024db..fd818048 100644 --- a/src/html/event.nim +++ b/src/html/event.nim @@ -2,13 +2,13 @@ import std/math import std/options import std/times -import bindings/quickjs -import js/jserror -import js/fromjs -import js/javascript -import js/jstypes -import js/jsutils -import js/tojs +import monoucha/quickjs +import monoucha/jserror +import monoucha/fromjs +import monoucha/javascript +import monoucha/jstypes +import monoucha/jsutils +import monoucha/tojs import types/opt type diff --git a/src/html/formdata.nim b/src/html/formdata.nim index 7f099acd..df839d3f 100644 --- a/src/html/formdata.nim +++ b/src/html/formdata.nim @@ -1,3 +1,4 @@ +import chame/tags import html/catom import html/dom import html/enums @@ -5,14 +6,13 @@ import io/dynstream import io/posixstream import js/base64 import js/domexception -import js/javascript -import js/tojs +import monoucha/javascript +import monoucha/tojs import types/blob import types/formdata +import types/opt import utils/twtstr -import chame/tags - proc constructEntryList*(form: HTMLFormElement; submitter: Element = nil; encoding = "UTF-8"): seq[FormDataEntry] diff --git a/src/html/script.nim b/src/html/script.nim index c0962b13..59ada2f0 100644 --- a/src/html/script.nim +++ b/src/html/script.nim @@ -1,4 +1,4 @@ -import js/javascript +import monoucha/javascript import types/referrer import types/url diff --git a/src/html/xmlhttprequest.nim b/src/html/xmlhttprequest.nim index 33f1e61c..92065ab4 100644 --- a/src/html/xmlhttprequest.nim +++ b/src/html/xmlhttprequest.nim @@ -1,15 +1,16 @@ import std/options import std/strutils -import bindings/quickjs import html/dom import html/event import js/domexception -import js/fromjs -import js/javascript import loader/headers import loader/request import loader/response +import monoucha/fromjs +import monoucha/javascript +import monoucha/quickjs +import types/opt import types/url type diff --git a/src/io/promise.nim b/src/io/promise.nim index 6e57791b..183091cb 100644 --- a/src/io/promise.nim +++ b/src/io/promise.nim @@ -1,11 +1,11 @@ import std/tables -import bindings/quickjs -import js/jserror -import js/javascript -import js/jsutils -import js/jsopaque -import js/tojs +import monoucha/quickjs +import monoucha/jserror +import monoucha/javascript +import monoucha/jsutils +import monoucha/jsopaque +import monoucha/tojs import types/opt type diff --git a/src/io/urlfilter.nim b/src/io/urlfilter.nim index 4d1d2404..e86e0e6b 100644 --- a/src/io/urlfilter.nim +++ b/src/io/urlfilter.nim @@ -1,6 +1,6 @@ import std/options -import js/jsregex +import monoucha/jsregex import types/url #TODO add denyhost/s for blocklists diff --git a/src/js/base64.nim b/src/js/base64.nim index a1693aa3..85844e09 100644 --- a/src/js/base64.nim +++ b/src/js/base64.nim @@ -1,7 +1,7 @@ -import bindings/quickjs +import monoucha/quickjs import js/domexception -import js/javascript -import js/jstypes +import monoucha/javascript +import monoucha/jstypes import types/opt import utils/twtstr diff --git a/src/js/console.nim b/src/js/console.nim index 712f228c..0de66162 100644 --- a/src/js/console.nim +++ b/src/js/console.nim @@ -1,6 +1,6 @@ import io/dynstream -import js/jserror -import js/javascript +import monoucha/jserror +import monoucha/javascript type Console* = ref object err*: DynStream diff --git a/src/js/domexception.nim b/src/js/domexception.nim index 902cd618..68e313e9 100644 --- a/src/js/domexception.nim +++ b/src/js/domexception.nim @@ -1,8 +1,9 @@ import std/tables -import bindings/quickjs -import js/jserror -import js/javascript +import monoucha/javascript +import monoucha/jserror +import monoucha/quickjs +import types/opt const NamesTable = { "IndexSizeError": 1u16, diff --git a/src/js/encoding.nim b/src/js/encoding.nim index edf0cf97..ccadce16 100644 --- a/src/js/encoding.nim +++ b/src/js/encoding.nim @@ -1,13 +1,13 @@ -import bindings/quickjs -import js/jserror -import js/javascript -import js/jstypes - import chagashi/charset import chagashi/decoder import chagashi/decodercore import chagashi/validator import chagashi/validatorcore +import monoucha/javascript +import monoucha/jserror +import monoucha/jstypes +import monoucha/quickjs +import types/opt type JSTextEncoder = ref object diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim deleted file mode 100644 index 055ae19e..00000000 --- a/src/js/fromjs.nim +++ /dev/null @@ -1,445 +0,0 @@ -import std/macros -import std/options -import std/tables -import std/unicode - -import bindings/quickjs -import js/jserror -import js/jstypes -import js/jsopaque -import types/opt -import utils/twtstr - -proc fromJS*[T](ctx: JSContext; val: JSValue): JSResult[T] - -func isInstanceOfNonGlobal(ctx: JSContext; val: JSValue; class: string): bool = - let ctxOpaque = ctx.getOpaque() - var classid = JS_GetClassID(val) - let tclassid = ctxOpaque.creg[class] - var found = false - while true: - if classid == tclassid: - found = true - break - ctxOpaque.parents.withValue(classid, val): - classid = val[] - do: - classid = 0 # not defined by Chawan; assume parent is Object. - if classid == 0: - break - return found - -func isInstanceOfGlobal(ctx: JSContext; val: JSValue; class: string): bool = - let ctxOpaque = ctx.getOpaque() - #TODO gparent only works for a single level. (But this is not really a - # problem right now, because our global objects have at most one inheritance - # level.) - if ctx.isGlobal(class) or ctxOpaque.creg[class] == ctxOpaque.gparent: - # undefined -> global - if JS_IsUndefined(val): - return true - if JS_IsObject(val): - let global = JS_GetGlobalObject(ctx) - let p0 = JS_VALUE_GET_PTR(global) - let p1 = JS_VALUE_GET_PTR(val) - JS_FreeValue(ctx, global) - if p0 == p1: - return true - return false - -func isInstanceOf*(ctx: JSContext; val: JSValue; class: string): bool = - return ctx.isInstanceOfGlobal(val, class) or - ctx.isInstanceOfNonGlobal(val, class) - -func toString(ctx: JSContext; val: JSValue): Opt[string] = - var plen: csize_t - let outp = JS_ToCStringLen(ctx, addr plen, val) # cstring - if outp != nil: - var ret = newString(plen) - if plen != 0: - prepareMutation(ret) - copyMem(addr ret[0], outp, plen) - result = ok(ret) - JS_FreeCString(ctx, outp) - -func fromJSString(ctx: JSContext; val: JSValue): JSResult[string] = - var plen: csize_t - let outp = JS_ToCStringLen(ctx, addr plen, val) # cstring - if outp == nil: - return err() - var ret = newString(plen) - if plen != 0: - prepareMutation(ret) - copyMem(addr ret[0], outp, plen) - JS_FreeCString(ctx, outp) - return ok(ret) - -func fromJSInt[T: SomeInteger](ctx: JSContext; val: JSValue): - JSResult[T] = - when T is int: - # Always int32, so we don't risk 32-bit only breakage. - # If int64 is needed, specify it explicitly. - var ret: int32 - if JS_ToInt32(ctx, addr ret, val) < 0: - return err() - return ok(int(ret)) - elif T is int32: - var ret: int32 - if JS_ToInt32(ctx, addr ret, val) < 0: - return err() - return ok(ret) - elif T is int64: - var ret: int64 - if JS_ToInt64(ctx, addr ret, val) < 0: - return err() - return ok(ret) - elif T is uint32: - var ret: uint32 - if JS_ToUint32(ctx, addr ret, val) < 0: - return err() - return ok(ret) - else: - static: - error($T & " cannot be converted to JS automatically") - -proc fromJSFloat64(ctx: JSContext; val: JSValue): JSResult[float64] = - var f64: float64 - if JS_ToFloat64(ctx, addr f64, val) < 0: - return err() - return ok(f64) - -macro fromJSTupleBody(a: tuple) = - let len = a.getType().len - 1 - let done = ident("done") - result = newStmtList(quote do: - var `done`: bool) - for i in 0..<len: - result.add(quote do: - let next = JS_Call(ctx, nextMethod, it, 0, nil) - if JS_IsException(next): - return err() - defer: JS_FreeValue(ctx, next) - let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone]) - if JS_IsException(doneVal): - return err() - defer: JS_FreeValue(ctx, doneVal) - `done` = ?fromJS[bool](ctx, doneVal) - if `done`: - return errTypeError("Too few arguments in sequence (got " & $`i` & - ", expected " & $`len` & ")") - let valueVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstValue]) - if JS_IsException(valueVal): - return err() - defer: JS_FreeValue(ctx, valueVal) - `a`[`i`] = ?fromJS[typeof(`a`[`i`])](ctx, valueVal) - ) - if i == len - 1: - result.add(quote do: - let next = JS_Call(ctx, nextMethod, it, 0, nil) - if JS_IsException(next): - return err() - defer: JS_FreeValue(ctx, next) - let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone]) - `done` = ?fromJS[bool](ctx, doneVal) - var i = `i` - # we're emulating a sequence, so we must query all remaining parameters - # too: - while not `done`: - inc i - let next = JS_Call(ctx, nextMethod, it, 0, nil) - if JS_IsException(next): - return err() - defer: JS_FreeValue(ctx, next) - let doneVal = JS_GetProperty(ctx, next, - ctx.getOpaque().strRefs[jstDone]) - if JS_IsException(doneVal): - return err() - defer: JS_FreeValue(ctx, doneVal) - `done` = ?fromJS[bool](ctx, doneVal) - if `done`: - let msg = "Too many arguments in sequence (got " & $i & - ", expected " & $`len` & ")" - return err(newTypeError(msg)) - JS_FreeValue(ctx, JS_GetProperty(ctx, next, - ctx.getOpaque().strRefs[jstValue])) - ) - -proc fromJSTuple[T: tuple](ctx: JSContext; val: JSValue): JSResult[T] = - let itprop = JS_GetProperty(ctx, val, ctx.getOpaque().symRefs[jsyIterator]) - if JS_IsException(itprop): - return err() - defer: JS_FreeValue(ctx, itprop) - let it = JS_Call(ctx, itprop, val, 0, nil) - if JS_IsException(it): - return err() - defer: JS_FreeValue(ctx, it) - let nextMethod = JS_GetProperty(ctx, it, ctx.getOpaque().strRefs[jstNext]) - if JS_IsException(nextMethod): - return err() - defer: JS_FreeValue(ctx, nextMethod) - var x: T - fromJSTupleBody(x) - return ok(x) - -proc fromJSSeq[T](ctx: JSContext; val: JSValue): JSResult[seq[T]] = - let itprop = JS_GetProperty(ctx, val, ctx.getOpaque().symRefs[jsyIterator]) - if JS_IsException(itprop): - return err() - defer: JS_FreeValue(ctx, itprop) - let it = JS_Call(ctx, itprop, val, 0, nil) - if JS_IsException(it): - return err() - defer: JS_FreeValue(ctx, it) - let nextMethod = JS_GetProperty(ctx, it, ctx.getOpaque().strRefs[jstNext]) - if JS_IsException(nextMethod): - return err() - defer: JS_FreeValue(ctx, nextMethod) - var s = newSeq[T]() - while true: - let next = JS_Call(ctx, nextMethod, it, 0, nil) - if JS_IsException(next): - return err() - defer: JS_FreeValue(ctx, next) - let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone]) - if JS_IsException(doneVal): - return err() - defer: JS_FreeValue(ctx, doneVal) - let done = ?fromJS[bool](ctx, doneVal) - if done: - break - let valueVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstValue]) - if JS_IsException(valueVal): - return err() - defer: JS_FreeValue(ctx, valueVal) - let genericRes = ?fromJS[typeof(s[0])](ctx, valueVal) - s.add(genericRes) - return ok(s) - -proc fromJSSet[T](ctx: JSContext; val: JSValue): JSResult[set[T]] = - let itprop = JS_GetProperty(ctx, val, ctx.getOpaque().symRefs[jsyIterator]) - if JS_IsException(itprop): - return err() - defer: JS_FreeValue(ctx, itprop) - let it = JS_Call(ctx, itprop, val, 0, nil) - if JS_IsException(it): - return err() - defer: JS_FreeValue(ctx, it) - let nextMethod = JS_GetProperty(ctx, it, ctx.getOpaque().strRefs[jstNext]) - if JS_IsException(nextMethod): - return err() - defer: JS_FreeValue(ctx, nextMethod) - var s: set[T] - while true: - let next = JS_Call(ctx, nextMethod, it, 0, nil) - if JS_IsException(next): - return err() - defer: JS_FreeValue(ctx, next) - let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone]) - if JS_IsException(doneVal): - return err() - defer: JS_FreeValue(ctx, doneVal) - let done = ?fromJS[bool](ctx, doneVal) - if done: - break - let valueVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstValue]) - if JS_IsException(valueVal): - return err() - defer: JS_FreeValue(ctx, valueVal) - let genericRes = ?fromJS[T](ctx, valueVal) - s.incl(genericRes) - return ok(s) - -proc fromJSTable[A, B](ctx: JSContext; val: JSValue): JSResult[Table[A, B]] = - if not JS_IsObject(val): - return err(newTypeError("object expected")) - var ptab: ptr UncheckedArray[JSPropertyEnum] - var plen: uint32 - let flags = cint(JS_GPN_STRING_MASK) - if JS_GetOwnPropertyNames(ctx, addr ptab, addr plen, val, flags) == -1: - # exception - return err() - defer: - for i in 0 ..< plen: - JS_FreeAtom(ctx, ptab[i].atom) - js_free(ctx, ptab) - var res = Table[A, B]() - for i in 0 ..< plen: - let atom = ptab[i].atom - let k = JS_AtomToValue(ctx, atom) - defer: JS_FreeValue(ctx, k) - let kn = ?fromJS[A](ctx, k) - let v = JS_GetProperty(ctx, val, atom) - defer: JS_FreeValue(ctx, v) - let vn = ?fromJS[B](ctx, v) - res[kn] = vn - return ok(res) - -template optionType[T](o: type Option[T]): auto = - T - -# Option vs Opt: -# Option is for nullable types, e.g. if you want to return either a string -# or null. (This is rather pointless for anything else.) -# Opt is for passing down exceptions received up in the chain. -# So e.g. none(T) translates to JS_NULL, but err() translates to JS_EXCEPTION. -proc fromJSOption[T](ctx: JSContext; val: JSValue): JSResult[Option[T]] = - if JS_IsNull(val): - return ok(none(T)) - let res = ?fromJS[T](ctx, val) - return ok(option(res)) - -proc fromJSBool(ctx: JSContext; val: JSValue): JSResult[bool] = - let ret = JS_ToBool(ctx, val) - if ret == -1: # exception - return err() - if ret == 0: - return ok(false) - return ok(true) - -proc fromJSEnum[T: enum](ctx: JSContext; val: JSValue): JSResult[T] = - if JS_IsException(val): - return err() - let s = ?toString(ctx, val) - let r = strictParseEnum[T](s) - if r.isSome: - return ok(r.get) - return errTypeError("`" & s & "' is not a valid value for enumeration " & $T) - -proc fromJSPObj0(ctx: JSContext; val: JSValue; t: string): - JSResult[pointer] = - if JS_IsException(val): - return err(nil) - if JS_IsNull(val): - return ok(nil) - if ctx.isInstanceOfGlobal(val, t): - return ok(?getGlobalOpaque0(ctx, val)) - if not JS_IsObject(val): - return err(newTypeError("Value is not an object")) - if not isInstanceOfNonGlobal(ctx, val, t): - return errTypeError(t & " expected") - let classid = JS_GetClassID(val) - let op = JS_GetOpaque(val, classid) - return ok(op) - -proc fromJSObject[T: ref object](ctx: JSContext; val: JSValue): JSResult[T] = - return ok(cast[T](?fromJSPObj0(ctx, val, $T))) - -proc fromJSVoid(ctx: JSContext; val: JSValue): JSResult[void] = - if JS_IsException(val): - return err() - return ok() - -proc fromJSDict[T: JSDict](ctx: JSContext; val: JSValue): JSResult[T] = - if not JS_IsUndefined(val) and not JS_IsNull(val) and not JS_IsObject(val): - return err(newTypeError("Dictionary is not an object")) - #TODO throw on missing required values - var d = T() - if JS_IsObject(val): - for k, v in d.fieldPairs: - let esm = JS_GetPropertyStr(ctx, val, k) - if not JS_IsUndefined(esm): - v = ?fromJS[typeof(v)](ctx, esm) - when v isnot JSValue: - JS_FreeValue(ctx, esm) - return ok(d) - -proc fromJSArrayBuffer(ctx: JSContext; val: JSValue): JSResult[JSArrayBuffer] = - var len: csize_t - let p = JS_GetArrayBuffer(ctx, addr len, val) - if p == nil: - return err() - let abuf = JSArrayBuffer( - len: len, - p: cast[ptr UncheckedArray[uint8]](p) - ) - return ok(abuf) - -proc fromJSArrayBufferView(ctx: JSContext; val: JSValue): - JSResult[JSArrayBufferView] = - var offset: csize_t - var nmemb: csize_t - var nsize: csize_t - let jsbuf = JS_GetTypedArrayBuffer(ctx, val, addr offset, addr nmemb, - addr nsize) - let abuf = ?fromJSArrayBuffer(ctx, jsbuf) - let view = JSArrayBufferView( - abuf: abuf, - offset: offset, - nmemb: nmemb, - nsize: nsize - ) - return ok(view) - -type FromJSAllowedT = (object and not (Result|Option|Table|JSValue|JSDict| - JSArrayBuffer|JSArrayBufferView|JSUint8Array)) - -macro fromJS2(ctx: JSContext; val: JSValue; x: static string): untyped = - let id = ident("fromJS" & x) - return quote do: - `id`(`ctx`, `val`) - -proc fromJS*[T](ctx: JSContext; val: JSValue): JSResult[T] = - when T is string: - return fromJSString(ctx, val) - elif T is Option: - return fromJSOption[optionType(T)](ctx, val) - elif T is seq: - return fromJSSeq[typeof(result.get.items)](ctx, val) - elif T is set: - return fromJSSet[typeof(result.get.items)](ctx, val) - elif T is tuple: - return fromJSTuple[T](ctx, val) - elif T is bool: - return fromJSBool(ctx, val) - elif typeof(result).valType is Table: - return fromJSTable[typeof(result.get.keys), - typeof(result.get.values)](ctx, val) - elif T is SomeInteger: - return fromJSInt[T](ctx, val) - elif T is float64: - return fromJSFloat64(ctx, val) - elif T is enum: - return fromJSEnum[T](ctx, val) - elif T is JSValue: - return ok(val) - elif T is ref object: - return fromJSObject[T](ctx, val) - elif T is void: - return fromJSVoid(ctx, val) - elif T is JSDict: - return fromJSDict[T](ctx, val) - elif T is JSArrayBuffer: - return fromJSArrayBuffer(ctx, val) - elif T is JSArrayBufferView: - return fromJSArrayBufferView(ctx, val) - else: - return fromJS2(ctx, val, $T) - -const JS_ATOM_TAG_INT = 1u32 shl 31 - -func JS_IsNumber*(v: JSAtom): JS_BOOL = - return (uint32(v) and JS_ATOM_TAG_INT) != 0 - -func fromJS*[T: string|uint32|JSAtom](ctx: JSContext; atom: JSAtom): Opt[T] = - when T is JSAtom: - return ok(atom) - elif T is SomeNumber: - if JS_IsNumber(atom): - return ok(uint32(atom) and (not JS_ATOM_TAG_INT)) - return err() - else: - let cs = JS_AtomToCString(ctx, atom) - if cs == nil: - return err() - let s = $cs - JS_FreeCString(ctx, cs) - return ok(s) - -proc fromJSPObj[T](ctx: JSContext; val: JSValue): JSResult[ptr T] = - return cast[JSResult[ptr T]](fromJSPObj0(ctx, val, $T)) - -template fromJSP*[T](ctx: JSContext; val: JSValue): untyped = - when T is FromJSAllowedT: - fromJSPObj[T](ctx, val) - else: - fromJS[T](ctx, val) diff --git a/src/js/intl.nim b/src/js/intl.nim index 8be5a768..4ae264e4 100644 --- a/src/js/intl.nim +++ b/src/js/intl.nim @@ -1,9 +1,9 @@ # Very minimal Intl module... TODO make it more complete -import bindings/quickjs -import js/javascript -import js/jstypes -import js/tojs +import monoucha/quickjs +import monoucha/javascript +import monoucha/jstypes +import monoucha/tojs type NumberFormat = ref object diff --git a/src/js/javascript.nim b/src/js/javascript.nim deleted file mode 100644 index cdf6b127..00000000 --- a/src/js/javascript.nim +++ /dev/null @@ -1,1719 +0,0 @@ -# JavaScript binding generator. Horrifying, I know. But it works! -# Warning: Function overloading is currently not implemented. Though there is a -# block dielabel: -# ... -# around each bound function call, so it shouldn't be too difficult to get it -# working. (This would involve generating JS functions in registerType.) -# Now for the pragmas: -# {.jsctor.} for constructors. These need no `this' value, and are bound as -# regular constructors in JS. They must return a ref object, which will have -# a JS counterpart too. (Other functions can return ref objects too, which -# will either use the existing JS counterpart, if exists, or create a new -# one. In other words: cross-language reference semantics work seamlessly.) -# {.jsfunc.} is used for binding normal functions. Needs a `this' value, as all -# following pragmas. As mentioned before, overloading doesn't work but OR -# generics do. By default, the Nim function name is bound; if this is not -# desired, you can rename the function like this: {.jsfunc: "preferredName".} -# This also works for all other pragmas that define named functions in JS. -# {.jsstfunc.} binds static functions. Unlike .jsfunc, it does not have a -# `this' value. A class name must be specified, e.g. {.jsstfunc: "URL".} to -# define on the URL class. To rename a static function, use the syntax -# "ClassName:funcName", e.g. "Response:error". -# {.jsget.}, {.jsfget.} must be specified on object fields; these generate -# regular getter & setter functions. -# {.jsufget, jsuffget, jsuffunc.} For fields with the [LegacyUnforgeable] -# WebIDL property. -# This makes it so a non-configurable/writable, but enumerable property -# is defined on the object when the *constructor* is called (i.e. NOT on -# the prototype.) -# {.jsfget.} and {.jsfset.} for getters/setters. Note the `f'; bare jsget/jsset -# can only be used on object fields. (I initially wanted to use the same -# keyword, unfortunately that didn't work out.) -# {.jsgetprop.} for property getters. Called when GetOwnProperty would return -# nothing. The key must be either a JSAtom, uint32 or string. (Note that the -# string option copies.) -# {.jssetprop.} for property setters. Called on SetProperty - in fact this -# is the set() method of Proxy, except it always returns true. Same rules as -# jsgetprop for keys. -# {.jsdelprop.} for property deletion. It is like the deleteProperty method -# of Proxy. Must return true if deleted, false if not deleted. -# {.jshasprop.} for overriding has_property. Must return a boolean. -# {.jspropnames.} overrides get_own_property_names. Must return a -# JSPropertyEnumList object. - -import std/macros -import std/options -import std/sets -import std/strutils -import std/tables -import std/unicode - -import js/jserror -import js/fromjs -import js/jsopaque -import js/tojs -import types/opt - -import bindings/quickjs - -export opt -export options -export tables - -export - JS_NULL, JS_UNDEFINED, JS_FALSE, JS_TRUE, JS_EXCEPTION, JS_UNINITIALIZED - -export - JS_EVAL_TYPE_GLOBAL, - JS_EVAL_TYPE_MODULE, - JS_EVAL_TYPE_DIRECT, - JS_EVAL_TYPE_INDIRECT, - JS_EVAL_TYPE_MASK, - JS_EVAL_FLAG_SHEBANG, - JS_EVAL_FLAG_STRICT, - JS_EVAL_FLAG_STRIP, - JS_EVAL_FLAG_COMPILE_ONLY - -export JSRuntime, JSContext, JSValue, JSClassID, JSAtom - -export - JS_GetGlobalObject, JS_FreeValue, JS_IsException, JS_GetPropertyStr, - JS_IsFunction, JS_NewCFunctionData, JS_Call, JS_DupValue, JS_IsUndefined - -when sizeof(int) < sizeof(int64): - export quickjs.`==` - -type - JSFunctionList = openArray[JSCFunctionListEntry] - - BoundFunction = object - t: BoundFunctionType - name: string - id: NimNode - magic: uint16 - unforgeable: bool - isstatic: bool - ctorBody: NimNode - - BoundFunctionType = enum - bfFunction = "js_func" - bfConstructor = "js_ctor" - bfGetter = "js_get" - bfSetter = "js_set" - bfPropertyGet = "js_prop_get" - bfPropertySet = "js_prop_set" - bfPropertyDel = "js_prop_del" - bfPropertyHas = "js_prop_has" - bfPropertyNames = "js_prop_names" - bfFinalizer = "js_fin" - -var runtimes {.threadvar.}: seq[JSRuntime] - -proc bindMalloc(s: ptr JSMallocState; size: csize_t): pointer {.cdecl.} = - return alloc(size) - -proc bindFree(s: ptr JSMallocState; p: pointer) {.cdecl.} = - if p == nil: - return - dealloc(p) - -proc bindRealloc(s: ptr JSMallocState; p: pointer; size: csize_t): pointer - {.cdecl.} = - return realloc(p, size) - -proc jsRuntimeCleanUp(rt: JSRuntime) {.cdecl.} = - let rtOpaque = rt.getOpaque() - GC_unref(rtOpaque) - assert rtOpaque.destroying == nil - var ps: seq[pointer] = @[] - for p in rtOpaque.plist.values: - ps.add(p) - rtOpaque.plist.clear() - var unrefs: seq[proc() {.closure.}] = @[] - for (_, unref) in rtOpaque.refmap.values: - unrefs.add(unref) - rtOpaque.refmap.clear() - for unref in unrefs: - unref() - for p in ps: - #TODO maybe finalize? - let val = JS_MKPTR(JS_TAG_OBJECT, p) - JS_SetOpaque(val, nil) - JS_FreeValueRT(rt, val) - JS_RunGC(rt) - -proc newJSRuntime*(): JSRuntime = - var mf {.global.} = JSMallocFunctions( - js_malloc: bindMalloc, - js_free: bindFree, - js_realloc: bindRealloc, - js_malloc_usable_size: nil - ) - let rt = JS_NewRuntime2(addr mf, nil) - let opaque = JSRuntimeOpaque() - GC_ref(opaque) - JS_SetRuntimeOpaque(rt, cast[pointer](opaque)) - JS_SetRuntimeCleanUpFunc(rt, jsRuntimeCleanUp) - # Must be added after opaque is set, or there is a chance of - # nim_finalize_for_js dereferencing it (at the new call). - runtimes.add(rt) - return rt - -proc newJSContext*(rt: JSRuntime): JSContext = - let ctx = JS_NewContext(rt) - let opaque = newJSContextOpaque(ctx) - GC_ref(opaque) - JS_SetContextOpaque(ctx, cast[pointer](opaque)) - return ctx - -func getClass*(ctx: JSContext; class: string): JSClassID = - # This function *should* never fail. - ctx.getOpaque().creg[class] - -func hasClass*(ctx: JSContext; class: type): bool = - return $class in ctx.getOpaque().creg - -proc free*(ctx: JSContext) = - var opaque = ctx.getOpaque() - if opaque != nil: - for a in opaque.symRefs: - JS_FreeAtom(ctx, a) - for a in opaque.strRefs: - JS_FreeAtom(ctx, a) - for v in opaque.valRefs: - JS_FreeValue(ctx, v) - for classid, v in opaque.ctors: - JS_FreeValue(ctx, v) - for v in opaque.errCtorRefs: - JS_FreeValue(ctx, v) - if opaque.globalUnref != nil: - opaque.globalUnref() - GC_unref(opaque) - JS_FreeContext(ctx) - -proc free*(rt: JSRuntime) = - JS_FreeRuntime(rt) - runtimes.del(runtimes.find(rt)) - -proc setGlobal*[T](ctx: JSContext; obj: T) = - # Add JSValue reference. - let global = JS_GetGlobalObject(ctx) - let opaque = cast[pointer](obj) - ctx.setOpaque(global, opaque) - GC_ref(obj) - let rtOpaque = JS_GetRuntime(ctx).getOpaque() - ctx.getOpaque().globalUnref = proc() = - GC_unref(obj) - rtOpaque.plist.del(opaque) - JS_FreeValue(ctx, global) - -proc setInterruptHandler*(rt: JSRuntime; cb: JSInterruptHandler; - opaque: pointer = nil) = - JS_SetInterruptHandler(rt, cb, opaque) - -proc getExceptionMsg*(ctx: JSContext): string = - result = "" - let ex = JS_GetException(ctx) - let str = fromJS[string](ctx, ex) - if str.isSome: - result &= str.get & '\n' - let stack = JS_GetPropertyStr(ctx, ex, cstring("stack")); - if not JS_IsUndefined(stack): - result &= fromJS[string](ctx, stack).get("") - JS_FreeValue(ctx, stack) - JS_FreeValue(ctx, ex) - -proc getExceptionMsg*(ctx: JSContext; err: JSError): string = - if err != nil: - JS_FreeValue(ctx, ctx.toJS(err)) # note: this implicitly throws - return ctx.getExceptionMsg() - -# Returns early with err(JSContext) if an exception was thrown in a -# context. -proc runJSJobs*(rt: JSRuntime): Err[JSContext] = - while JS_IsJobPending(rt): - var ctx: JSContext - let r = JS_ExecutePendingJob(rt, addr ctx) - if r == -1: - return err(ctx) - ok() - -# Add all LegacyUnforgeable functions defined on the prototype chain to -# the opaque. -# Since every prototype has a list of all its ancestor's LegacyUnforgeable -# functions, it is sufficient to simply merge the new list of new classes -# with their parent's list to achieve this. -proc addClassUnforgeable(ctx: JSContext; proto: JSValue; - classid, parent: JSClassID; ourUnforgeable: JSFunctionList) = - let ctxOpaque = ctx.getOpaque() - var merged = @ourUnforgeable - ctxOpaque.unforgeable.withValue(parent, uf): - merged.add(uf[]) - if merged.len > 0: - ctxOpaque.unforgeable[classid] = merged - let ufp = addr ctxOpaque.unforgeable[classid][0] - JS_SetPropertyFunctionList(ctx, proto, ufp, cint(merged.len)) - -func newJSClass*(ctx: JSContext; cdef: JSClassDefConst; tname: string; - nimt: pointer; ctor: JSCFunction; funcs: JSFunctionList; parent: JSClassID; - asglobal: bool; nointerface: bool; finalizer: JSFinalizerFunction; - namespace: JSValue; errid: Opt[JSErrorEnum]; - unforgeable, staticfuns: JSFunctionList; - ishtmldda: bool): JSClassID {.discardable.} = - let rt = JS_GetRuntime(ctx) - discard JS_NewClassID(addr result) - var ctxOpaque = ctx.getOpaque() - var rtOpaque = rt.getOpaque() - if JS_NewClass(rt, result, cdef) != 0: - raise newException(Defect, "Failed to allocate JS class: " & - $cdef.class_name) - ctxOpaque.typemap[nimt] = result - ctxOpaque.creg[tname] = result - ctxOpaque.parents[result] = parent - if ishtmldda: - ctxOpaque.htmldda = result - if finalizer != nil: - rtOpaque.fins[result] = finalizer - let proto = if parent != 0: - let parentProto = JS_GetClassProto(ctx, parent) - let x = JS_NewObjectProtoClass(ctx, parentProto, parent) - JS_FreeValue(ctx, parentProto) - x - else: - JS_NewObject(ctx) - if funcs.len > 0: - # We avoid funcs being GC'ed by putting the list in rtOpaque. - # (QuickJS uses the pointer later.) - #TODO maybe put them in ctxOpaque instead? - rtOpaque.flist.add(@funcs) - JS_SetPropertyFunctionList(ctx, proto, addr rtOpaque.flist[^1][0], - cint(funcs.len)) - #TODO check if this is an indexed property getter - if cdef.exotic != nil and cdef.exotic.get_own_property != nil: - let val = JS_DupValue(ctx, ctxOpaque.valRefs[jsvArrayPrototypeValues]) - let itSym = ctxOpaque.symRefs[jsyIterator] - doAssert JS_SetProperty(ctx, proto, itSym, val) == 1 - let news = JS_NewAtomString(ctx, cdef.class_name) - doAssert not JS_IsException(news) - ctx.definePropertyC(proto, ctxOpaque.symRefs[jsyToStringTag], - JS_DupValue(ctx, news)) - JS_SetClassProto(ctx, result, proto) - ctx.addClassUnforgeable(proto, result, parent, unforgeable) - if asglobal: - let global = JS_GetGlobalObject(ctx) - assert ctxOpaque.gclaz == "" - ctxOpaque.gclaz = tname - ctxOpaque.gparent = parent - ctx.definePropertyC(global, ctxOpaque.symRefs[jsyToStringTag], - JS_DupValue(ctx, news)) - if JS_SetPrototype(ctx, global, proto) != 1: - raise newException(Defect, "Failed to set global prototype: " & - $cdef.class_name) - # Global already exists, so set unforgeable functions here - ctxOpaque.unforgeable.withValue(result, uf): - JS_SetPropertyFunctionList(ctx, global, addr uf[][0], cint(uf[].len)) - JS_FreeValue(ctx, global) - JS_FreeValue(ctx, news) - let jctor = JS_NewCFunction2(ctx, ctor, cstring($cdef.class_name), 0, - JS_CFUNC_constructor, 0) - if staticfuns.len > 0: - rtOpaque.flist.add(@staticfuns) - JS_SetPropertyFunctionList(ctx, jctor, addr rtOpaque.flist[^1][0], - cint(staticfuns.len)) - JS_SetConstructor(ctx, jctor, proto) - if errid.isSome: - ctx.getOpaque().errCtorRefs[errid.get] = JS_DupValue(ctx, jctor) - ctxOpaque.ctors[result] = JS_DupValue(ctx, jctor) - if not nointerface: - if JS_IsNull(namespace): - let global = JS_GetGlobalObject(ctx) - ctx.definePropertyCW(global, $cdef.class_name, jctor) - JS_FreeValue(ctx, global) - else: - ctx.definePropertyCW(namespace, $cdef.class_name, jctor) - else: - JS_FreeValue(ctx, jctor) - -type FuncParam = tuple - name: string - t: NimNode - val: Option[NimNode] - generic: Option[NimNode] - isptr: bool - -func getMinArgs(params: seq[FuncParam]): int = - for i in 0..<params.len: - let it = params[i] - if it[2].isSome: - return i - let t = it.t - if t.kind == nnkBracketExpr: - if t.typeKind == varargs.getType().typeKind: - assert i == params.high, "Not even nim can properly handle this..." - return i - return params.len - -func fromJSP[T: string|uint32](ctx: JSContext; atom: JSAtom): Opt[T] = - return fromJS[T](ctx, atom) - -proc defineConsts*[T](ctx: JSContext; classid: JSClassID; - consts: static openArray[(string, T)]) = - let proto = ctx.getOpaque().ctors[classid] - for (k, v) in consts: - ctx.definePropertyE(proto, k, v) - -proc defineConsts*(ctx: JSContext; classid: JSClassID; - consts: typedesc[enum]; astype: typedesc) = - let proto = ctx.getOpaque().ctors[classid] - for e in consts: - ctx.definePropertyE(proto, $e, astype(e)) - -type - JSFuncGenerator = ref object - t: BoundFunctionType - thisname: Option[string] - funcName: string - generics: Table[string, seq[NimNode]] - funcParams: seq[FuncParam] - passCtx: bool - thisType: string - thisTypeNode: NimNode - returnType: Option[NimNode] - newName: NimNode - newBranchList: seq[NimNode] - errval: NimNode # JS_EXCEPTION or -1 - # die: didn't match parameters, but could still match other ones - dielabel: NimNode - jsFunCallLists: seq[NimNode] - jsFunCallList: NimNode - jsFunCall: NimNode - jsCallAndRet: NimNode - minArgs: int - actualMinArgs: int # minArgs without JSContext - i: int # nim parameters accounted for - j: int # js parameters accounted for (not including fix ones, e.g. `this') - unforgeable: bool - isstatic: bool - -var BoundFunctions {.compileTime.}: Table[string, seq[BoundFunction]] - -proc getGenerics(fun: NimNode): Table[string, seq[NimNode]] = - var node = fun.findChild(it.kind == nnkBracket) - if node.kind == nnkNilLit: - return # no bracket - node = node.findChild(it.kind == nnkGenericParams) - if node.kind == nnkNilLit: - return # no generics - node = node.findChild(it.kind == nnkIdentDefs) - var stack: seq[NimNode] - for i in countdown(node.len - 1, 0): stack.add(node[i]) - var gen_name: NimNode - var gen_types: seq[NimNode] - template add_gen = - if gen_name != nil: - assert gen_types.len != 0 - result[gen_name.strVal] = gen_types - gen_types.setLen(0) - - while stack.len > 0: - let node = stack.pop() - case node.kind - of nnkIdent: - add_gen - gen_name = node - of nnkSym: - assert gen_name != nil - gen_types.add(node) - of nnkInfix: - assert node[0].eqIdent(ident("|")) or node[0].eqIdent(ident("or")), - "Only OR generics are supported." - for i in countdown(node.len - 1, 1): # except infix ident - stack.add(node[i]) - of nnkBracketExpr: - gen_types.add(node) - else: - discard - add_gen - -proc getParams(fun: NimNode): seq[FuncParam] = - let formalParams = fun.findChild(it.kind == nnkFormalParams) - var funcParams: seq[FuncParam] - var returnType = none(NimNode) - if formalParams[0].kind != nnkEmpty: - returnType = some(formalParams[0]) - for i in 1..<fun.params.len: - let it = formalParams[i] - let tt = it[^2] - var t: NimNode - if it[^2].kind != nnkEmpty: - t = `tt` - elif it[^1].kind != nnkEmpty: - let x = it[^1] - t = quote do: - typeof(`x`) - else: - error("?? " & treeRepr(it)) - let isptr = t.kind == nnkVarTy - if t.kind in {nnkRefTy, nnkVarTy}: - t = t[0] - let val = if it[^1].kind != nnkEmpty: - let x = it[^1] - some(newPar(x)) - else: - none(NimNode) - var g = none(NimNode) - for i in 0 ..< it.len - 2: - let name = $it[i] - funcParams.add((name, t, val, g, isptr)) - funcParams - -proc getReturn(fun: NimNode): Option[NimNode] = - let formalParams = fun.findChild(it.kind == nnkFormalParams) - if formalParams[0].kind != nnkEmpty: - some(formalParams[0]) - else: - none(NimNode) - -template getJSParams(): untyped = - [ - (quote do: JSValue), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("this"), quote do: JSValue), - newIdentDefs(ident("argc"), quote do: cint), - newIdentDefs(ident("argv"), quote do: ptr UncheckedArray[JSValue]) - ] - -template getJSGetterParams(): untyped = - [ - (quote do: JSValue), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("this"), quote do: JSValue), - ] - -template getJSGetPropParams(): untyped = - [ - (quote do: cint), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("desc"), quote do: ptr JSPropertyDescriptor), - newIdentDefs(ident("obj"), quote do: JSValue), - newIdentDefs(ident("prop"), quote do: JSAtom), - ] - -template getJSSetPropParams(): untyped = - [ - (quote do: cint), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("obj"), quote do: JSValue), - newIdentDefs(ident("atom"), quote do: JSAtom), - newIdentDefs(ident("value"), quote do: JSValue), - newIdentDefs(ident("receiver"), quote do: JSValue), - newIdentDefs(ident("flags"), quote do: cint), - ] - -template getJSDelPropParams(): untyped = - [ - (quote do: cint), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("obj"), quote do: JSValue), - newIdentDefs(ident("prop"), quote do: JSAtom), - ] - -template getJSHasPropParams(): untyped = - [ - (quote do: cint), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("obj"), quote do: JSValue), - newIdentDefs(ident("atom"), quote do: JSAtom), - ] - - -template getJSSetterParams(): untyped = - [ - (quote do: JSValue), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("this"), quote do: JSValue), - newIdentDefs(ident("val"), quote do: JSValue), - ] - -template getJSPropNamesParams(): untyped = - [ - (quote do: cint), - newIdentDefs(ident("ctx"), quote do: JSContext), - newIdentDefs(ident("ptab"), quote do: ptr JSPropertyEnumArray), - newIdentDefs(ident("plen"), quote do: ptr uint32), - newIdentDefs(ident("obj"), quote do: JSValue) - ] - -template fromJS_or_return*(t, ctx, val: untyped): untyped = - let x = fromJS[t](ctx, val) - if x.isNone: - return toJS(ctx, x.error) - x.get - -template fromJSP_or_return*(t, ctx, val: untyped): untyped = - let x = fromJSP[t](ctx, val) - if x.isNone: - return toJS(ctx, x.error) - x.get - -template fromJS_or_die*(t, ctx, val, dl: untyped): untyped = - let x = fromJSP[t](ctx, val) - if x.isNone: - break dl - x.get - -proc addParam2(gen: var JSFuncGenerator; s, t, val: NimNode; - fallback: NimNode = nil) = - let dl = gen.dielabel - let stmt = quote do: - fromJS_or_die(`t`, ctx, `val`, `dl`) - for i in 0..gen.jsFunCallLists.high: - if fallback == nil: - gen.jsFunCallLists[i].add(newLetStmt(s, stmt)) - else: - let j = gen.j - gen.jsFunCallLists[i].add(newLetStmt(s, quote do: - if `j` < argc and not JS_IsUndefined(argv[`j`]): - `stmt` - else: - `fallback`)) - -proc addValueParam(gen: var JSFuncGenerator; s, t: NimNode; - fallback: NimNode = nil) = - let j = gen.j - gen.addParam2(s, t, quote do: argv[`j`], fallback) - -proc addUnionParamBranch(gen: var JSFuncGenerator; query, newBranch: NimNode; - fallback: NimNode = nil) = - let i = gen.i - let query = if fallback == nil: query else: - quote do: (`i` < argc and `query`) - let newBranch = newStmtList(newBranch) - for i in 0..gen.jsFunCallLists.high: - var ifstmt = newIfStmt((query, newBranch)) - let oldBranch = newStmtList() - ifstmt.add(newTree(nnkElse, oldBranch)) - gen.jsFunCallLists[i].add(ifstmt) - gen.jsFunCallLists[i] = oldBranch - gen.newBranchList.add(newBranch) - -func isSequence*(ctx: JSContext; o: JSValue): bool = - if not JS_IsObject(o): - return false - let prop = JS_GetProperty(ctx, o, ctx.getOpaque().symRefs[jsyIterator]) - # prop can't be exception (throws_ref_error is 0 and tag is object) - result = not JS_IsUndefined(prop) - JS_FreeValue(ctx, prop) - -proc addUnionParam0(gen: var JSFuncGenerator; tt, s, val: NimNode; - fallback: NimNode = nil) = - # Union types. - #TODO quite a few types are still missing. - let flattened = gen.generics[tt.strVal] # flattened member types - var tableg = none(NimNode) - var seqg = none(NimNode) - var numg = none(NimNode) - var objg = none(NimNode) - var hasString = false - var hasJSValue = false - var hasBoolean = false - let ev = gen.errval - let dl = gen.dielabel - for g in flattened: - if g.len > 0 and g[0] == Table.getType(): - tableg = some(g) - elif g.typekind == ntySequence: - seqg = some(g) - elif g == string.getTypeInst(): - hasString = true - elif g == JSValue.getTypeInst(): - hasJSValue = true - elif g == bool.getTypeInst(): - hasBoolean = true - elif g == int.getTypeInst(): #TODO should be SomeNumber - assert numg.isNone - numg = some(g) - elif g == uint32.getTypeInst(): #TODO should be SomeNumber - assert numg.isNone - numg = some(g) - elif g.getTypeInst().getTypeImpl().kind == nnkRefTy: - # Assume it's ref object. - objg = some(g) - else: - error("Type not supported yet") - - # 5. If V is a platform object, then: - if objg.isSome: - let t = objg.get - let x = ident("x") - let query = quote do: - let `x` = fromJS[`t`](ctx, `val`) - `x`.isSome - gen.addUnionParamBranch(query, quote do: - let `s` = `x`.get, - fallback) - # 10. If Type(V) is Object, then: - # Sequence: - if seqg.isSome: - let query = quote do: - isSequence(ctx, `val`) - let a = seqg.get[1] - gen.addUnionParamBranch(query, quote do: - let `s` = fromJS_or_die(seq[`a`], ctx, `val`, `dl`), - fallback) - # Record: - if tableg.isSome: - let a = tableg.get[1] - let b = tableg.get[2] - let query = quote do: - JS_IsObject(`val`) - gen.addUnionParamBranch(query, quote do: - let `s` = fromJS_or_die(Table[`a`, `b`], ctx, `val`, `dl`), - fallback) - # Object (JSObject variant): - #TODO non-JS objects (i.e. ref object) - if hasJSValue: - let query = quote do: - JS_IsObject(`val`) - gen.addUnionParamBranch(query, quote do: - let `s` = fromJS_or_die(JSValue, ctx, `val`, `dl`), - fallback) - # 11. If Type(V) is Boolean, then: - if hasBoolean: - let query = quote do: - JS_IsBool(`val`) - gen.addUnionParamBranch(query, quote do: - let `s` = fromJS_or_die(bool, ctx, `val`, `dl`), - fallback) - # 12. If Type(V) is Number, then: - if numg.isSome: - let ng = numg.get - let query = quote do: - JS_IsNumber(`val`) - gen.addUnionParamBranch(query, quote do: - let `s` = fromJS_or_die(`ng`, ctx, `val`, `dl`), - fallback) - # 14. If types includes a string type, then return the result of converting V - # to that type. - if hasString: - gen.addParam2(s, string.getType(), quote do: `val`, fallback) - # 16. If types includes a numeric type, then return the result of converting - # V to that numeric type. - elif numg.isSome: - gen.addParam2(s, numg.get.getType(), quote do: `val`, fallback) - # 17. If types includes boolean, then return the result of converting V to - # boolean. - elif hasBoolean: - gen.addParam2(s, bool.getType(), quote do: `val`, fallback) - # 19. Throw a TypeError. - else: - gen.addParam2(s, string.getType(), quote do: - if true: - discard JS_ThrowTypeError(ctx, "No match for union type") - return `ev` - JS_NULL, fallback) - - for branch in gen.newBranchList: - gen.jsFunCallLists.add(branch) - gen.newBranchList.setLen(0) - -proc addUnionParam(gen: var JSFuncGenerator; tt, s: NimNode; - fallback: NimNode = nil) = - let j = gen.j - gen.addUnionParam0(tt, s, quote do: argv[`j`], fallback) - -proc addFixParam(gen: var JSFuncGenerator; name: string) = - var s = ident("arg_" & $gen.i) - let t = gen.funcParams[gen.i].t - let id = ident(name) - if t.typeKind == ntyGenericParam: - gen.addUnionParam0(t, s, id) - else: - gen.addParam2(s, t, id) - if gen.funcParams[gen.i].isptr: - s = quote do: `s`[] - gen.jsFunCall.add(s) - inc gen.i - -proc addRequiredParams(gen: var JSFuncGenerator) = - while gen.i < gen.minArgs: - var s = ident("arg_" & $gen.i) - let tt = gen.funcParams[gen.i].t - if tt.typeKind == ntyGenericParam: - gen.addUnionParam(tt, s) - else: - gen.addValueParam(s, tt) - if gen.funcParams[gen.i].isptr: - s = quote do: `s`[] - gen.jsFunCall.add(s) - inc gen.j - inc gen.i - -proc addOptionalParams(gen: var JSFuncGenerator) = - while gen.i < gen.funcParams.len: - let j = gen.j - var s = ident("arg_" & $gen.i) - let tt = gen.funcParams[gen.i].t - if tt.typeKind == varargs.getType().typeKind: # pray it's not a generic... - let vt = tt[1].getType() - for i in 0..gen.jsFunCallLists.high: - gen.jsFunCallLists[i].add(newLetStmt(s, quote do: - var valist: seq[`vt`] = @[] - for i in `j`..<argc: - let it = fromJS_or_return(`vt`, ctx, argv[i]) - valist.add(it) - valist - )) - else: - if gen.funcParams[gen.i][2].isNone: - error("No fallback value. Maybe a non-optional parameter follows an " & - "optional parameter?") - let fallback = gen.funcParams[gen.i][2].get - if tt.typeKind == ntyGenericParam: - gen.addUnionParam(tt, s, fallback) - else: - gen.addValueParam(s, tt, fallback) - if gen.funcParams[gen.i].isptr: - s = quote do: `s`[] - gen.jsFunCall.add(s) - inc gen.j - inc gen.i - -proc finishFunCallList(gen: var JSFuncGenerator) = - for branch in gen.jsFunCallLists: - branch.add(gen.jsFunCall) - -var existingFuncs {.compileTime.}: HashSet[string] -var jsDtors {.compileTime.}: HashSet[string] - -proc registerFunction(typ: string; nf: BoundFunction) = - BoundFunctions.withValue(typ, val): - val[].add(nf) - do: - BoundFunctions[typ] = @[nf] - existingFuncs.incl(nf.id.strVal) - -proc registerFunction(typ: string; t: BoundFunctionType; name: string; - id: NimNode; magic: uint16 = 0; uf = false; isstatic = false; - ctorBody: NimNode = nil) = - registerFunction(typ, BoundFunction( - t: t, - name: name, - id: id, - magic: magic, - unforgeable: uf, - isstatic: isstatic, - ctorBody: ctorBody - )) - -proc registerConstructor(gen: JSFuncGenerator; jsProc: NimNode) = - registerFunction(gen.thisType, gen.t, gen.funcName, gen.newName, - uf = gen.unforgeable, isstatic = gen.isstatic, ctorBody = jsProc) - -proc registerFunction(gen: JSFuncGenerator) = - registerFunction(gen.thisType, gen.t, gen.funcName, gen.newName, - uf = gen.unforgeable, isstatic = gen.isstatic) - -export JS_ThrowTypeError, JS_ThrowRangeError, JS_ThrowSyntaxError, - JS_ThrowInternalError, JS_ThrowReferenceError - -proc newJSProcBody(gen: var JSFuncGenerator; isva: bool): NimNode = - let tt = gen.thisType - let fn = gen.funcName - let ma = gen.actualMinArgs - result = newStmtList() - if isva: - result.add(quote do: - if argc < `ma`: - return JS_ThrowTypeError(ctx, - "At least %d arguments required, but only %d passed", cint(`ma`), - cint(argc)) - ) - if gen.thisname.isSome and not gen.isstatic: - let tn = ident(gen.thisname.get) - let ev = gen.errval - result.add(quote do: - if not ctx.isInstanceOf(`tn`, `tt`): - discard JS_ThrowTypeError(ctx, - "'%s' called on an object that is not an instance of %s", `fn`, `tt`) - return `ev` - ) - result.add(gen.jsCallAndRet) - -proc newJSProc(gen: var JSFuncGenerator; params: openArray[NimNode]; - isva = true): NimNode = - let jsBody = gen.newJSProcBody(isva) - let jsPragmas = newNimNode(nnkPragma).add(ident("cdecl")) - return newProc(gen.newName, params, jsBody, pragmas = jsPragmas) - -func getFuncName(fun: NimNode; jsname, staticName: string): string = - if jsname != "": - return jsname - if staticName != "": - let i = staticName.find('.') - if i != -1: - return staticName.substr(i + 1) - let x = $fun[0] - if x == "$": - # stringifier - return "toString" - return x - -func getErrVal(t: BoundFunctionType): NimNode = - if t in {bfPropertyGet, bfPropertySet, bfPropertyDel, bfPropertyHas, - bfPropertyNames}: - return quote do: cint(-1) - return quote do: JS_EXCEPTION - -proc addJSContext(gen: var JSFuncGenerator) = - if gen.funcParams.len > gen.i: - if gen.funcParams[gen.i].t.eqIdent(ident("JSContext")): - gen.passCtx = true - gen.jsFunCall.add(ident("ctx")) - inc gen.i - elif gen.funcParams[gen.i].t.eqIdent(ident("JSRuntime")): - inc gen.i # special case for finalizers that have a JSRuntime param - -proc addThisName(gen: var JSFuncGenerator; thisname: Option[string]) = - if thisname.isSome: - gen.thisTypeNode = gen.funcParams[gen.i].t - gen.thisType = $gen.funcParams[gen.i].t - gen.newName = ident($gen.t & "_" & gen.thisType & "_" & gen.funcName) - else: - let rt = gen.returnType.get - if rt.kind in {nnkRefTy, nnkPtrTy}: - gen.thisTypeNode = rt[0] - gen.thisType = rt[0].strVal - else: - if rt.kind == nnkBracketExpr: - gen.thisTypeNode = rt[1] - gen.thisType = rt[1].strVal - else: - gen.thisTypeNode = rt - gen.thisType = rt.strVal - gen.newName = ident($gen.t & "_" & gen.funcName) - -func getActualMinArgs(gen: var JSFuncGenerator): int = - var ma = gen.minArgs - if gen.thisname.isSome and not gen.isstatic: - dec ma - if gen.passCtx: - dec ma - assert ma >= 0 - return ma - -func until(s: string; c: char; starti = 0): string = - result = "" - for i, cc in s: - if cc == c: - break - result &= cc - -proc initGenerator(fun: NimNode; t: BoundFunctionType; thisname = some("this"); - jsname = ""; unforgeable = false; staticName = ""): JSFuncGenerator = - let jsFunCallList = newStmtList() - let funcParams = getParams(fun) - var gen = JSFuncGenerator( - t: t, - funcName: getFuncName(fun, jsname, staticName), - generics: getGenerics(fun), - funcParams: funcParams, - returnType: getReturn(fun), - minArgs: funcParams.getMinArgs(), - thisname: thisname, - errval: getErrVal(t), - dielabel: ident("ondie"), - jsFunCallList: jsFunCallList, - jsFunCallLists: @[jsFunCallList], - jsFunCall: newCall(fun[0]), - unforgeable: unforgeable, - isstatic: staticName != "" - ) - gen.addJSContext() - gen.actualMinArgs = gen.getActualMinArgs() # must come after passctx is set - if staticName == "": - gen.addThisName(thisname) - else: - gen.thisType = staticName.until('.') - gen.newName = ident($gen.t & "_" & gen.funcName) - return gen - -proc makeJSCallAndRet(gen: var JSFuncGenerator; okstmt, errstmt: NimNode) = - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = if gen.returnType.isSome: - quote do: - block `dl`: - return ctx.toJS(`jfcl`) - `errstmt` - else: - quote do: - block `dl`: - `jfcl` - `okstmt` - `errstmt` - -proc makeCtorJSCallAndRet(gen: var JSFuncGenerator; errstmt: NimNode) = - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = quote do: - block `dl`: - return ctx.toJSNew(`jfcl`, this) - `errstmt` - -macro jsctor*(fun: typed) = - var gen = initGenerator(fun, bfConstructor, thisname = none(string)) - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO implement function overloading - error("Function overloading hasn't been implemented yet...") - gen.addRequiredParams() - gen.addOptionalParams() - gen.finishFunCallList() - let errstmt = quote do: - return JS_ThrowTypeError(ctx, "Invalid parameters passed to constructor") - gen.makeCtorJSCallAndRet(errstmt) - let jsProc = gen.newJSProc(getJSParams()) - gen.registerConstructor(jsProc) - return newStmtList(fun) - -macro jshasprop*(fun: typed) = - var gen = initGenerator(fun, bfPropertyHas, thisname = some("obj")) - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO ditto - error("Function overloading hasn't been implemented yet...") - gen.addFixParam("obj") - gen.addFixParam("atom") - gen.finishFunCallList() - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = quote do: - block `dl`: - let retv = `jfcl` - return cint(retv) - doAssert false # TODO? - let jsProc = gen.newJSProc(getJSHasPropParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -macro jsgetprop*(fun: typed) = - var gen = initGenerator(fun, bfPropertyGet, thisname = some("obj")) - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO ditto - error("Function overloading hasn't been implemented yet...") - gen.addFixParam("obj") - gen.addFixParam("prop") - gen.finishFunCallList() - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = quote do: - block `dl`: - let retv = ctx.toJS(`jfcl`) - if retv != JS_NULL: - if desc != nil: - # From quickjs.h: - # > If 1 is returned, the property descriptor 'desc' is filled - # > if != NULL. - # So desc may be nil. - desc[].setter = JS_UNDEFINED - desc[].getter = JS_UNDEFINED - desc[].value = retv - desc[].flags = 0 - return cint(1) - return cint(0) - let jsProc = gen.newJSProc(getJSGetPropParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -macro jssetprop*(fun: typed) = - var gen = initGenerator(fun, bfPropertySet, thisname = some("obj")) - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO ditto - error("Function overloading hasn't been implemented yet...") - gen.addFixParam("receiver") - gen.addFixParam("atom") - gen.addFixParam("value") - gen.finishFunCallList() - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = if gen.returnType.isSome: - quote do: - block `dl`: - let v = toJS(ctx, `jfcl`) - if not JS_IsException(v): - return cint(1) - return cint(-1) - else: - quote do: - block `dl`: - `jfcl` - return cint(1) - return cint(-1) - let jsProc = gen.newJSProc(getJSSetPropParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -macro jsdelprop*(fun: typed) = - var gen = initGenerator(fun, bfPropertyDel, thisname = some("obj")) - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO ditto - error("Function overloading hasn't been implemented yet...") - gen.addFixParam("obj") - gen.addFixParam("prop") - gen.finishFunCallList() - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = quote do: - block `dl`: - let retv = `jfcl` - return cint(retv) - return cint(-1) - let jsProc = gen.newJSProc(getJSDelPropParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -macro jspropnames*(fun: typed) = - var gen = initGenerator(fun, bfPropertyNames, thisname = some("obj")) - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO ditto - error("Function overloading hasn't been implemented yet...") - gen.addFixParam("obj") - gen.finishFunCallList() - let jfcl = gen.jsFunCallList - let dl = gen.dielabel - gen.jsCallAndRet = quote do: - block `dl`: - let retv = `jfcl` - ptab[] = retv.buffer - plen[] = retv.len - return cint(0) - return cint(-1) - let jsProc = gen.newJSProc(getJSPropNamesParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -macro jsfgetn(jsname: static string; uf: static bool; fun: typed) = - var gen = initGenerator(fun, bfGetter, jsname = jsname, unforgeable = uf) - if gen.actualMinArgs != 0 or gen.funcParams.len != gen.minArgs: - error("jsfget functions must only accept one parameter.") - if gen.returnType.isNone: - error("jsfget functions must have a return type.") - if gen.newName.strVal in existingFuncs: - #TODO TODO TODO ditto - error("Function overloading hasn't been implemented yet...") - gen.addFixParam("this") - gen.finishFunCallList() - gen.makeJSCallAndRet(nil, quote do: discard) - let jsProc = gen.newJSProc(getJSGetterParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -# "Why?" So the compiler doesn't cry. -template jsfget*(fun: typed) = - jsfgetn("", false, fun) - -template jsuffget*(fun: typed) = - jsfgetn("", true, fun) - -template jsfget*(jsname: static string; fun: typed) = - jsfgetn(jsname, false, fun) - -template jsuffget*(jsname: static string; fun: typed) = - jsfgetn(jsname, true, fun) - -# Ideally we could simulate JS setters using nim setters, but nim setters -# won't accept types that don't match their reflected field's type. -macro jsfsetn(jsname: static string; fun: typed) = - var gen = initGenerator(fun, bfSetter, jsname = jsname) - if gen.actualMinArgs != 1 or gen.funcParams.len != gen.minArgs: - error("jsfset functions must accept two parameters") - if gen.returnType.isSome: - let rt = gen.returnType.get - #TODO ?? - let rtType = rt[0] - let errType = getTypeInst(Err) - if not errType.sameType(rtType) and not rtType.sameType(errType): - error("jsfset functions must not have a return type") - gen.addFixParam("this") - gen.addFixParam("val") - gen.finishFunCallList() - # return param anyway - let okstmt = quote do: discard - let errstmt = quote do: return JS_DupValue(ctx, val) - gen.makeJSCallAndRet(okstmt, errstmt) - let jsProc = gen.newJSProc(getJSSetterParams(), false) - gen.registerFunction() - return newStmtList(fun, jsProc) - -template jsfset*(fun: typed) = - jsfsetn("", fun) - -template jsfset*(jsname: static string; fun: typed) = - jsfsetn(jsname, fun) - -macro jsfuncn*(jsname: static string; uf: static bool; - staticName: static string; fun: typed) = - var gen = initGenerator(fun, bfFunction, jsname = jsname, unforgeable = uf, - staticName = staticName) - if gen.minArgs == 0 and not gen.isstatic: - error("Zero-parameter functions are not supported. " & - "(Maybe pass Window or Client?)") - if not gen.isstatic: - gen.addFixParam("this") - gen.addRequiredParams() - gen.addOptionalParams() - gen.finishFunCallList() - let okstmt = quote do: - return JS_UNDEFINED - let errstmt = quote do: - return JS_ThrowTypeError(ctx, "Invalid parameters passed to function") - gen.makeJSCallAndRet(okstmt, errstmt) - let jsProc = gen.newJSProc(getJSParams()) - gen.registerFunction() - return newStmtList(fun, jsProc) - -template jsfunc*(fun: typed) = - jsfuncn("", false, "", fun) - -template jsuffunc*(fun: typed) = - jsfuncn("", true, "", fun) - -template jsfunc*(jsname: static string; fun: typed) = - jsfuncn(jsname, false, "", fun) - -template jsuffunc*(jsname: static string; fun: typed) = - jsfuncn(jsname, true, "", fun) - -template jsstfunc*(name: static string; fun: typed) = - jsfuncn("", false, name, fun) - -macro jsfin*(fun: typed) = - var gen = initGenerator(fun, bfFinalizer, thisname = some("fin")) - let finName = gen.newName - let finFun = ident(gen.funcName) - let t = gen.thisTypeNode - if gen.minArgs == 1: - let jsProc = quote do: - proc `finName`(rt: JSRuntime; val: JSValue) = - let opaque = JS_GetOpaque(val, JS_GetClassID(val)) - if opaque != nil: - `finFun`(cast[`t`](opaque)) - gen.registerFunction() - result = newStmtList(fun, jsProc) - elif gen.minArgs == 2: - let jsProc = quote do: - proc `finName`(rt: JSRuntime; val: JSValue) = - let opaque = JS_GetOpaque(val, JS_GetClassID(val)) - if opaque != nil: - `finFun`(rt, cast[`t`](opaque)) - gen.registerFunction() - result = newStmtList(fun, jsProc) - else: - error("Expected one or two parameters") - -# Having the same names for these and the macros leads to weird bugs, so the -# macros get an additional f. -template jsget*() {.pragma.} -template jsget*(name: string) {.pragma.} -template jsset*() {.pragma.} -template jsset*(name: string) {.pragma.} -template jsgetset*() {.pragma.} -template jsgetset*(name: string) {.pragma.} -template jsufget*() {.pragma.} -template jsufget*(name: string) {.pragma.} - -proc js_illegal_ctor*(ctx: JSContext; this: JSValue; argc: cint; - argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.} = - return JS_ThrowTypeError(ctx, "Illegal constructor") - -type - JSObjectPragma = object - name: string - varsym: NimNode - unforgeable: bool - - JSObjectPragmas = object - jsget: seq[JSObjectPragma] - jsset: seq[JSObjectPragma] - jsinclude: seq[JSObjectPragma] - -func getPragmaName(varPragma: NimNode): string = - if varPragma.kind == nnkExprColonExpr: - return $varPragma[0] - return $varPragma - -func getStringFromPragma(varPragma: NimNode): Option[string] = - if varPragma.kind == nnkExprColonExpr: - if not varPragma.len == 1 and varPragma[1].kind == nnkStrLit: - error("Expected string as pragma argument") - return some($varPragma[1]) - -proc findPragmas(t: NimNode): JSObjectPragmas = - let typ = t.getTypeInst()[1] # The type, as declared. - var impl = typ.getTypeImpl() # ref t - if impl.kind in {nnkRefTy, nnkPtrTy}: - impl = impl[0].getImpl() - else: - impl = typ.getImpl() - # stolen from std's macros.customPragmaNode - var identDefsStack = newSeq[NimNode](impl[2].len) - for i in 0 ..< identDefsStack.len: - identDefsStack[i] = impl[2][i] - var pragmas: JSObjectPragmas - while identDefsStack.len > 0: - var identDefs = identDefsStack.pop() - case identDefs.kind - of nnkRecList: - for child in identDefs.children: - identDefsStack.add(child) - of nnkRecCase: - # Add condition definition - identDefsStack.add(identDefs[0]) - # Add branches - for i in 1 ..< identDefs.len: - identDefsStack.add(identDefs[i].last) - else: - for i in 0 .. identDefs.len - 3: - let varNode = identDefs[i] - if varNode.kind == nnkPragmaExpr: - var varName = varNode[0] - if varName.kind == nnkPostfix: - # This is a public field. We are skipping the postfix * - varName = varName[1] - var varPragmas = varNode[1] - for varPragma in varPragmas: - let pragmaName = getPragmaName(varPragma) - var op = JSObjectPragma( - name: getStringFromPragma(varPragma).get($varName), - varsym: varName - ) - case pragmaName - of "jsget": pragmas.jsget.add(op) - of "jsset": pragmas.jsset.add(op) - of "jsufget": # LegacyUnforgeable - op.unforgeable = true - pragmas.jsget.add(op) - of "jsgetset": - pragmas.jsget.add(op) - pragmas.jsset.add(op) - of "jsinclude": pragmas.jsinclude.add(op) - return pragmas - -proc nim_finalize_for_js*(obj: pointer) = - for rt in runtimes: - let rtOpaque = rt.getOpaque() - rtOpaque.plist.withValue(obj, v): - let p = v[] - let val = JS_MKPTR(JS_TAG_OBJECT, p) - let classid = JS_GetClassID(val) - rtOpaque.fins.withValue(classid, fin): - fin[](rt, val) - JS_SetOpaque(val, nil) - rtOpaque.plist.del(obj) - if rtOpaque.destroying == obj: - # Allow QJS to collect the JSValue through checkDestroy. - rtOpaque.destroying = nil - else: - JS_FreeValueRT(rt, val) - -type - TabGetSet* = object - name*: string - get*: JSGetterMagicFunction - set*: JSSetterMagicFunction - magic*: int16 - - TabFunc* = object - name*: string - fun*: JSCFunction - -template jsDestructor*[U](T: typedesc[ref U]) = - static: - jsDtors.incl($T) - {.warning[Deprecated]:off.}: - proc `=destroy`(obj: var U) = - nim_finalize_for_js(addr obj) - -template jsDestructor*(T: typedesc[object]) = - static: - jsDtors.incl($T) - {.warning[Deprecated]:off.}: - proc `=destroy`(obj: var T) = - nim_finalize_for_js(addr obj) - -type RegistryInfo = object - t: NimNode # NimNode of type - name: string # JS name, if this is the empty string then it equals tname - tabList: NimNode # array of function table - ctorImpl: NimNode # definition & body of constructor - ctorFun: NimNode # constructor ident - getset: Table[string, (NimNode, NimNode, bool)] # name -> get, set, uf - propGetFun: NimNode # custom get function ident - propSetFun: NimNode # custom set function ident - propDelFun: NimNode # custom del function ident - propHasFun: NimNode # custom has function ident - propNamesFun: NimNode # custom property names function ident - finFun: NimNode # finalizer ident - finName: NimNode # finalizer wrapper ident - dfin: NimNode # CheckDestroy finalizer ident - classDef: NimNode # ClassDef ident - tabUnforgeable: NimNode # array of unforgeable function table - tabStatic: NimNode # array of static function table - -func tname(info: RegistryInfo): string = - return info.t.strVal - -# Differs from tname if the Nim object's name differs from the JS object's -# name. -func jsname(info: RegistryInfo): string = - if info.name != "": - return info.name - return info.tname - -proc newRegistryInfo(t: NimNode; name: string): RegistryInfo = - return RegistryInfo( - t: t, - name: name, - classDef: ident("classDef"), - tabList: newNimNode(nnkBracket), - tabUnforgeable: newNimNode(nnkBracket), - tabStatic: newNimNode(nnkBracket), - finName: newNilLit(), - finFun: newNilLit(), - propGetFun: newNilLit(), - propSetFun: newNilLit(), - propDelFun: newNilLit(), - propHasFun: newNilLit(), - propNamesFun: newNilLit() - ) - -proc bindConstructor(stmts: NimNode; info: var RegistryInfo): NimNode = - if info.ctorFun != nil: - stmts.add(info.ctorImpl) - return info.ctorFun - return ident("js_illegal_ctor") - -proc registerGetters(stmts: NimNode; info: RegistryInfo; - jsget: seq[JSObjectPragma]) = - let t = info.t - let tname = info.tname - let jsname = info.jsname - for op in jsget: - let node = op.varsym - let fn = op.name - let id = ident($bfGetter & "_" & tname & "_" & fn) - stmts.add(quote do: - proc `id`(ctx: JSContext; this: JSValue): JSValue {.cdecl.} = - if not ctx.isInstanceOf(this, `tname`): - return JS_ThrowTypeError(ctx, - "'%s' called on an object that is not an instance of %s", `fn`, - `jsname`) - let arg_0 = fromJSP_or_return(`t`, ctx, this) - when typeof(arg_0.`node`) is object: - return toJSP(ctx, arg_0, arg_0.`node`) - else: - return toJS(ctx, arg_0.`node`) - ) - registerFunction(tname, BoundFunction( - t: bfGetter, - name: fn, - id: id, - unforgeable: op.unforgeable - )) - -proc registerSetters(stmts: NimNode; info: RegistryInfo; - jsset: seq[JSObjectPragma]) = - let t = info.t - let tname = info.tname - let jsname = info.jsname - for op in jsset: - let node = op.varsym - let fn = op.name - let id = ident($bfSetter & "_" & tname & "_" & fn) - stmts.add(quote do: - proc `id`(ctx: JSContext; this: JSValue; val: JSValue): JSValue - {.cdecl.} = - if not ctx.isInstanceOf(this, `tname`): - return JS_ThrowTypeError(ctx, - "'%s' called on an object that is not an instance of %s", `fn`, - `jsname`) - let arg_0 = fromJSP_or_return(`t`, ctx, this) - let arg_1 = val - # Note: if you get a compiler error that leads back to here, that - # might be because you added jsset to a non-ref object type. - arg_0.`node` = fromJS_or_return(typeof(arg_0.`node`), ctx, arg_1) - return JS_DupValue(ctx, arg_1) - ) - registerFunction(tname, bfSetter, fn, id) - -proc bindFunctions(stmts: NimNode; info: var RegistryInfo) = - BoundFunctions.withValue(info.tname, funs): - for fun in funs[].mitems: - var f0 = fun.name - let f1 = fun.id - if fun.name.endsWith("_exceptions"): - fun.name = fun.name.substr(0, fun.name.high - "_exceptions".len) - case fun.t - of bfFunction: - f0 = fun.name - if fun.unforgeable: - info.tabUnforgeable.add(quote do: - JS_CFUNC_DEF_NOCONF(`f0`, 0, cast[JSCFunction](`f1`))) - elif fun.isstatic: - info.tabStatic.add(quote do: - JS_CFUNC_DEF(`f0`, 0, cast[JSCFunction](`f1`))) - else: - info.tabList.add(quote do: - JS_CFUNC_DEF(`f0`, 0, cast[JSCFunction](`f1`))) - of bfConstructor: - info.ctorImpl = fun.ctorBody - if info.ctorFun != nil: - error("Class " & info.tname & " has 2+ constructors.") - info.ctorFun = f1 - of bfGetter: - info.getset.withValue(f0, exv): - exv[0] = f1 - exv[2] = fun.unforgeable - do: - info.getset[f0] = (f1, newNilLit(), fun.unforgeable) - of bfSetter: - info.getset.withValue(f0, exv): - exv[1] = f1 - do: - info.getset[f0] = (newNilLit(), f1, false) - of bfPropertyGet: - if info.propGetFun.kind != nnkNilLit: - error("Class " & info.tname & " has 2+ property getters.") - info.propGetFun = f1 - of bfPropertySet: - if info.propSetFun.kind != nnkNilLit: - error("Class " & info.tname & " has 2+ property setters.") - info.propSetFun = f1 - of bfPropertyDel: - if info.propDelFun.kind != nnkNilLit: - error("Class " & info.tname & " has 2+ property setters.") - info.propDelFun = f1 - of bfPropertyHas: - if info.propHasFun.kind != nnkNilLit: - error("Class " & info.tname & " has 2+ hasprop getters.") - info.propHasFun = f1 - of bfPropertyNames: - if info.propNamesFun.kind != nnkNilLit: - error("Class " & info.tname & " has 2+ propnames getters.") - info.propNamesFun = f1 - of bfFinalizer: - f0 = fun.name - info.finFun = ident(f0) - info.finName = f1 - -proc bindGetSet(stmts: NimNode; info: RegistryInfo) = - for k, (get, set, unforgeable) in info.getset: - if not unforgeable: - info.tabList.add(quote do: JS_CGETSET_DEF(`k`, `get`, `set`)) - else: - info.tabUnforgeable.add(quote do: - JS_CGETSET_DEF_NOCONF(`k`, `get`, `set`)) - -proc bindExtraGetSet(stmts: NimNode; info: var RegistryInfo; - extraGetSet: openArray[TabGetSet]) = - for x in extraGetSet: - let k = x.name - let g = x.get - let s = x.set - let m = x.magic - info.tabList.add(quote do: JS_CGETSET_MAGIC_DEF(`k`, `g`, `s`, `m`)) - -proc bindCheckDestroy(stmts: NimNode; info: RegistryInfo) = - let t = info.t - let dfin = info.dfin - stmts.add(quote do: - proc `dfin`(rt: JSRuntime; val: JSValue): JS_BOOL {.cdecl.} = - let opaque = JS_GetOpaque(val, JS_GetClassID(val)) - if opaque != nil: - when `t` is ref object: - # Before this function is called, the ownership model is - # JSObject -> Nim object. - # Here we change it to Nim object -> JSObject. - # As a result, Nim object's reference count can now reach zero (it is - # no longer "referenced" by the JS object). - # nim_finalize_for_js will be invoked by the Nim GC when the Nim - # refcount reaches zero. Then, the JS object's opaque will be set - # to nil, and its refcount decreased again, so next time this - # function will return true. - # - # Actually, we need another hack to ensure correct - # operation. GC_unref may call the destructor of this object, and - # in this case we cannot ask QJS to keep the JSValue alive. So we set - # the "destroying" pointer to the current opaque, and return true if - # the opaque was collected. - rt.getOpaque().destroying = opaque - GC_unref(cast[`t`](opaque)) - if rt.getOpaque().destroying == nil: - # Looks like GC_unref called nim_finalize_for_js for this pointer. - # This means we can allow QJS to collect this JSValue. - return true - else: - rt.getOpaque().destroying = nil - # Returning false from this function signals to the QJS GC that it - # should not be collected yet. Accordingly, the JSObject's refcount - # will be set to one again. - return false - else: - # This is not a reference, just a pointer with a reference to the - # root ancestor object. - # Remove the reference, allowing destruction of the root object once - # again. - let rtOpaque = rt.getOpaque() - var crefunref: tuple[cref, cunref: (proc())] - discard rtOpaque.refmap.pop(opaque, crefunref) - crefunref.cunref() - # Of course, nim_finalize_for_js might only be called later for - # this object, because the parent can still have references to it. - # (And for the same reason, a reference to the same object might - # still be necessary.) - # Accordingly, we return false here as well. - return false - return true - ) - -proc bindEndStmts(endstmts: NimNode; info: RegistryInfo) = - let jsname = info.jsname - let dfin = info.dfin - let classDef = info.classDef - if info.propGetFun.kind != nnkNilLit or - info.propSetFun.kind != nnkNilLit or - info.propDelFun.kind != nnkNilLit or - info.propHasFun.kind != nnkNilLit or - info.propNamesFun.kind != nnkNilLit: - let propGetFun = info.propGetFun - let propSetFun = info.propSetFun - let propDelFun = info.propDelFun - let propHasFun = info.propHasFun - let propNamesFun = info.propNamesFun - endstmts.add(quote do: - var exotic {.global.} = JSClassExoticMethods( - get_own_property: `propGetFun`, - get_own_property_names: `propNamesFun`, - has_property: `propHasFun`, - set_property: `propSetFun`, - delete_property: `propDelFun` - ) - var cd {.global.} = JSClassDef( - class_name: `jsname`, - can_destroy: `dfin`, - exotic: addr exotic - ) - let `classDef` = JSClassDefConst(addr cd) - ) - else: - endstmts.add(quote do: - var cd {.global.} = JSClassDef( - class_name: `jsname`, - can_destroy: `dfin` - ) - let `classDef` = JSClassDefConst(addr cd)) - -macro registerType*(ctx: JSContext; t: typed; parent: JSClassID = 0; - asglobal: static bool = false; globalparent: static bool = false; - nointerface = false; name: static string = ""; - hasExtraGetSet: static bool = false; - extraGetSet: static openArray[TabGetSet] = []; namespace = JS_NULL; - errid = opt(JSErrorEnum); ishtmldda = false): JSClassID = - var stmts = newStmtList() - var info = newRegistryInfo(t, name) - if not asglobal: - info.dfin = ident("js_" & t.strVal & "ClassCheckDestroy") - if info.tname notin jsDtors: - warning("No destructor has been defined for type " & info.tname) - else: - info.dfin = newNilLit() - if info.tname in jsDtors: - error("Global object " & info.tname & " must not have a destructor!") - let pragmas = findPragmas(t) - stmts.registerGetters(info, pragmas.jsget) - stmts.registerSetters(info, pragmas.jsset) - stmts.bindFunctions(info) - stmts.bindGetSet(info) - if hasExtraGetSet: - #HACK: for some reason, extraGetSet gets weird contents when nothing is - # passed to it. So we need an extra flag to signal if anything has - # been passed to it at all. - stmts.bindExtraGetSet(info, extraGetSet) - let sctr = stmts.bindConstructor(info) - if not asglobal: - stmts.bindCheckDestroy(info) - let endstmts = newStmtList() - endstmts.bindEndStmts(info) - let tabList = info.tabList - let finName = info.finName - let classDef = info.classDef - let tname = info.tname - let unforgeable = info.tabUnforgeable - let staticfuns = info.tabStatic - let global = asglobal and not globalparent - endstmts.add(quote do: - `ctx`.newJSClass(`classDef`, `tname`, getTypePtr(`t`), `sctr`, `tabList`, - `parent`, bool(`global`), `nointerface`, `finName`, `namespace`, - `errid`, `unforgeable`, `staticfuns`, `ishtmldda`) - ) - stmts.add(newBlockStmt(endstmts)) - return stmts - -proc getMemoryUsage*(rt: JSRuntime): string = - var m: JSMemoryUsage - JS_ComputeMemoryUsage(rt, addr m) - template row(title: string; count, size, sz2, cnt2: int64, name: string): - string = - let fv0 = $(float(sz2) / float(cnt2)) - var fv = fv0.until('.') - if fv.len < fv0.len: - fv &= '.' & fv0[fv.len + 1] - else: - fv &= ".0" - title & ": " & $count & " " & $size & " (" & fv & ")" & name & "\n" - template row(title: string; count, size, sz2: int64, name: string): - string = - row(title, count, size, sz2, count, name) - template row(title: string; count, size: int64, name: string): string = - row(title, count, size, size, name) - var s = "" - if m.malloc_count != 0: - s &= row("memory allocated", m.malloc_count, m.malloc_size, "/block") - s &= row("memory used", m.memory_used_count, m.memory_used_size, - m.malloc_size - m.memory_used_size, " average slack") - if m.atom_count != 0: - s &= row("atoms", m.atom_count, m.atom_size, "/atom") - if m.str_count != 0: - s &= row("strings", m.str_count, m.str_size, "/string") - if m.obj_count != 0: - s &= row("objects", m.obj_count, m.obj_size, "/object") & - row("properties", m.prop_count, m.prop_size, m.prop_size, m.obj_count, - "/object") & - row("shapes", m.shape_count, m.shape_size, "/shape") - if m.js_func_count != 0: - s &= row("js functions", m.js_func_count, m.js_func_size, "/function") - if m.c_func_count != 0: - s &= "native functions: " & $m.c_func_count & "\n" - if m.array_count != 0: - s &= "arrays: " & $m.array_count & "\n" & - "fast arrays: " & $m.fast_array_count & "\n" & - row("fast array elements", m.fast_array_elements, - m.fast_array_elements * sizeof(JSValue), m.fast_array_elements, - m.fast_array_count, "") - if m.binary_object_count != 0: - s &= "binary objects: " & $m.binary_object_count & " " & - $m.binary_object_size - return s - -proc eval*(ctx: JSContext; s, file: string; evalFlags = JS_EVAL_TYPE_GLOBAL): - JSValue = - return JS_Eval(ctx, cstring(s), csize_t(s.len), cstring(file), - cint(evalFlags)) - -proc compileScript*(ctx: JSContext; s, file: string): JSValue = - return ctx.eval(s, file, JS_EVAL_FLAG_COMPILE_ONLY) - -proc compileModule*(ctx: JSContext; s, file: string): JSValue = - return ctx.eval(s, file, JS_EVAL_TYPE_MODULE or JS_EVAL_FLAG_COMPILE_ONLY) - -proc evalFunction*(ctx: JSContext; val: JSValue): JSValue = - return JS_EvalFunction(ctx, val) diff --git a/src/js/jscolor.nim b/src/js/jscolor.nim index a73fb599..aa6fd8ed 100644 --- a/src/js/jscolor.nim +++ b/src/js/jscolor.nim @@ -1,11 +1,12 @@ import std/strutils -import bindings/quickjs -import js/jserror -import js/fromjs -import js/javascript -import js/tojs +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/quickjs +import monoucha/tojs import types/color +import types/opt import utils/charcategory import utils/twtstr diff --git a/src/js/jserror.nim b/src/js/jserror.nim deleted file mode 100644 index b4101830..00000000 --- a/src/js/jserror.nim +++ /dev/null @@ -1,59 +0,0 @@ -import types/opt - -type - JSError* = ref object of RootObj - e*: JSErrorEnum - message*: string - - JSErrorEnum* = enum - # QuickJS internal errors - jeEvalError = "EvalError" - jeRangeError = "RangeError" - jeReferenceError = "ReferenceError" - jeSyntaxError = "SyntaxError" - jeTypeError = "TypeError" - jeURIError = "URIError" - jeInternalError = "InternalError" - jeAggregateError = "AggregateError" - # Chawan errors - jeDOMException = "DOMException" - - JSResult*[T] = Result[T, JSError] - -const QuickJSErrors* = [ - jeEvalError, - jeRangeError, - jeReferenceError, - jeSyntaxError, - jeTypeError, - jeURIError, - jeInternalError, - jeAggregateError -] - -proc newEvalError*(message: string): JSError = - return JSError(e: jeEvalError, message: message) - -proc newRangeError*(message: string): JSError = - return JSError(e: jeRangeError, message: message) - -proc newReferenceError*(message: string): JSError = - return JSError(e: jeReferenceError, message: message) - -proc newSyntaxError*(message: string): JSError = - return JSError(e: jeSyntaxError, message: message) - -proc newTypeError*(message: string): JSError = - return JSError(e: jeTypeError, message: message) - -proc newURIError*(message: string): JSError = - return JSError(e: jeURIError, message: message) - -proc newInternalError*(message: string): JSError = - return JSError(e: jeInternalError, message: message) - -proc newAggregateError*(message: string): JSError = - return JSError(e: jeAggregateError, message: message) - -template errTypeError*(message: string): untyped = - err(newTypeError(message)) diff --git a/src/js/jsmodule.nim b/src/js/jsmodule.nim index 90ad7130..cdb836e6 100644 --- a/src/js/jsmodule.nim +++ b/src/js/jsmodule.nim @@ -1,7 +1,7 @@ -import bindings/constcharp -import bindings/quickjs -import js/javascript -import js/tojs +import monoucha/constcharp +import monoucha/javascript +import monoucha/quickjs +import monoucha/tojs proc setImportMeta(ctx: JSContext; funcVal: JSValue; isMain: bool) = let m = cast[JSModuleDef](JS_VALUE_GET_PTR(funcVal)) diff --git a/src/js/jsopaque.nim b/src/js/jsopaque.nim deleted file mode 100644 index 2ce6bbc1..00000000 --- a/src/js/jsopaque.nim +++ /dev/null @@ -1,128 +0,0 @@ -import std/tables - -import bindings/quickjs -import js/jserror -import types/opt - -type - JSSymbolRef* = enum - jsyIterator = "iterator" - jsyAsyncIterator = "asyncIterator" - jsyToStringTag = "toStringTag" - - JSStrRef* = enum - jstDone = "done" - jstValue = "value" - jstNext = "next" - jstPrototype = "prototype" - jstThen = "then" - - JSValueRef* = enum - jsvArrayPrototypeValues = "Array.prototype.values" - jsvUint8Array = "Uint8Array" - jsvObjectPrototypeValueOf = "Object.prototype.valueOf" - jsvSet = "Set" - jsvFunction = "Function" - - JSContextOpaque* = ref object - creg*: Table[string, JSClassID] - typemap*: Table[pointer, JSClassID] - ctors*: Table[JSClassID, JSValue] - parents*: Table[JSClassID, JSClassID] - # Parent unforgeables are merged on class creation. - # (i.e. to set all unforgeables on the prototype chain, it is enough to set) - # `unforgeable[classid]'.) - unforgeable*: Table[JSClassID, seq[JSCFunctionListEntry]] - gclaz*: string - gparent*: JSClassID - symRefs*: array[JSSymbolRef, JSAtom] - strRefs*: array[JSStrRef, JSAtom] - valRefs*: array[JSValueRef, JSValue] - errCtorRefs*: array[JSErrorEnum, JSValue] - htmldda*: JSClassID # only one of these exists: document.all. - globalUnref*: proc() {.closure.} - - JSFinalizerFunction* = proc(rt: JSRuntime; val: JSValue) {.nimcall, - raises: [].} - - JSRuntimeOpaque* = ref object - plist*: Table[pointer, pointer] # Nim, JS - flist*: seq[seq[JSCFunctionListEntry]] - fins*: Table[JSClassID, JSFinalizerFunction] - refmap*: Table[pointer, tuple[cref, cunref: (proc() {.closure.})]] - destroying*: pointer - -func newJSContextOpaque*(ctx: JSContext): JSContextOpaque = - let opaque = JSContextOpaque() - block: # get well-known symbols and other functions - let global = JS_GetGlobalObject(ctx) - let sym = JS_GetPropertyStr(ctx, global, "Symbol") - for s in JSSymbolRef: - let name = $s - let val = JS_GetPropertyStr(ctx, sym, cstring(name)) - assert JS_IsSymbol(val) - opaque.symRefs[s] = JS_ValueToAtom(ctx, val) - JS_FreeValue(ctx, val) - JS_FreeValue(ctx, sym) - for s in JSStrRef: - let ss = $s - opaque.strRefs[s] = JS_NewAtomLen(ctx, cstring(ss), csize_t(ss.len)) - for s in JSValueRef: - let ss = $s - let ret = JS_Eval(ctx, cstring(ss), csize_t(ss.len), "<init>", 0) - assert JS_IsFunction(ctx, ret) - opaque.valRefs[s] = ret - for e in JSErrorEnum: - let s = $e - let err = JS_GetPropertyStr(ctx, global, cstring(s)) - opaque.errCtorRefs[e] = err - JS_FreeValue(ctx, global) - return opaque - -func getOpaque*(ctx: JSContext): JSContextOpaque = - return cast[JSContextOpaque](JS_GetContextOpaque(ctx)) - -func getOpaque*(rt: JSRuntime): JSRuntimeOpaque = - return cast[JSRuntimeOpaque](JS_GetRuntimeOpaque(rt)) - -func isGlobal*(ctx: JSContext; class: string): bool = - assert class != "" - return ctx.getOpaque().gclaz == class - -proc setOpaque*(ctx: JSContext; val: JSValue; opaque: pointer) = - let rt = JS_GetRuntime(ctx) - let rtOpaque = rt.getOpaque() - let p = JS_VALUE_GET_PTR(val) - rtOpaque.plist[opaque] = p - JS_SetOpaque(val, opaque) - -# getOpaque, but doesn't work for global objects. -func getOpaque0*(val: JSValue): pointer = - if JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT: - return JS_GetOpaque(val, JS_GetClassID(val)) - -func getGlobalOpaque0*(ctx: JSContext; val = JS_UNDEFINED): Opt[pointer] = - let global = JS_GetGlobalObject(ctx) - if JS_IsUndefined(val) or val == global: - let opaque = JS_GetOpaque(global, JS_CLASS_OBJECT) - JS_FreeValue(ctx, global) - return ok(opaque) - JS_FreeValue(ctx, global) - return err() - -func getGlobalOpaque*(ctx: JSContext; T: typedesc; val = JS_UNDEFINED): Opt[T] = - return ok(cast[T](?getGlobalOpaque0(ctx, val))) - -func getOpaque*(ctx: JSContext; val: JSValue; class: string): pointer = - # Unfortunately, we can't change the global object's class. - #TODO: or maybe we can, but I'm afraid of breaking something. - # This needs further investigation. - if ctx.isGlobal(class): - let global = JS_GetGlobalObject(ctx) - let opaque = JS_GetOpaque(global, JS_CLASS_OBJECT) - JS_FreeValue(ctx, global) - return opaque - return getOpaque0(val) - -func getOpaque*[T: ref object](ctx: JSContext; val: JSValue): T = - cast[T](getOpaque(ctx, val, $T)) diff --git a/src/js/jspropenumlist.nim b/src/js/jspropenumlist.nim deleted file mode 100644 index 65c16c2f..00000000 --- a/src/js/jspropenumlist.nim +++ /dev/null @@ -1,42 +0,0 @@ -import bindings/quickjs - -type - JSPropertyEnumArray* = ptr UncheckedArray[JSPropertyEnum] - - JSPropertyEnumList* = object - buffer*: JSPropertyEnumArray - size: uint32 - len*: uint32 - ctx: JSContext - - JSPropertyEnumWrapper* = object - is_enumerable: bool - name: string - -func newJSPropertyEnumList*(ctx: JSContext; size: uint32): JSPropertyEnumList = - let p = js_malloc(ctx, csize_t(sizeof(JSPropertyEnum)) * csize_t(size)) - let buffer = cast[JSPropertyEnumArray](p) - return JSPropertyEnumList( - ctx: ctx, - buffer: buffer, - size: size - ) - -proc grow(this: var JSPropertyEnumList) = - this.size *= 2 - let p = js_realloc(this.ctx, this.buffer, csize_t(this.size)) - this.buffer = cast[JSPropertyEnumArray](p) - -proc add*(this: var JSPropertyEnumList; val: uint32) = - let i = this.len - inc this.len - if this.size < this.len: - this.grow() - this.buffer[i].atom = JS_NewAtomUInt32(this.ctx, val) - -proc add*(this: var JSPropertyEnumList; val: string) = - let i = this.len - inc this.len - if this.size < this.len: - this.grow() - this.buffer[i].atom = JS_NewAtomLen(this.ctx, cstring(val), csize_t(val.len)) diff --git a/src/js/jsregex.nim b/src/js/jsregex.nim deleted file mode 100644 index 9fb09872..00000000 --- a/src/js/jsregex.nim +++ /dev/null @@ -1,150 +0,0 @@ -# Interface for QuickJS libregexp. -import std/unicode - -import bindings/libregexp -import types/opt -import utils/twtstr - -export LREFlags - -type - Regex* = object - bytecode: seq[uint8] - when defined(debug): - buf: string - - RegexCapture* = tuple # start, end - s, e: int - - RegexResult* = object - success*: bool - captures*: seq[seq[RegexCapture]] - -when defined(debug): - func `$`*(regex: Regex): string = - regex.buf - -# this is hardcoded into quickjs, so we must override it here. -proc lre_realloc(opaque, p: pointer; size: csize_t): pointer {.exportc.} = - return realloc(p, size) - -proc compileRegex*(buf: string; flags: LREFlags = {}): Result[Regex, string] = - var errorMsg = newString(64) - var plen: cint - let bytecode = lre_compile(addr plen, cstring(errorMsg), cint(errorMsg.len), - cstring(buf), csize_t(buf.len), flags.toCInt, nil) - if bytecode == nil: - return err(errorMsg.until('\0')) # Failed to compile. - assert plen > 0 - var bcseq = newSeqUninitialized[uint8](plen) - copyMem(addr bcseq[0], bytecode, plen) - dealloc(bytecode) - var regex = Regex(bytecode: bcseq) - when defined(debug): - regex.buf = buf - return ok(regex) - -func countBackslashes(buf: string; i: int): int = - var j = 0 - for i in countdown(i, 0): - if buf[i] != '\\': - break - inc j - return j - -# ^abcd -> ^abcd -# efgh$ -> efgh$ -# ^ijkl$ -> ^ijkl$ -# mnop -> ^mnop$ -proc compileMatchRegex*(buf: string): Result[Regex, string] = - if buf.len == 0: - return compileRegex(buf) - if buf[0] == '^': - return compileRegex(buf) - if buf[^1] == '$': - # Check whether the final dollar sign is escaped. - if buf.len == 1 or buf[^2] != '\\': - return compileRegex(buf) - let j = buf.countBackslashes(buf.high - 2) - if j mod 2 == 1: # odd, because we do not count the last backslash - return compileRegex(buf) - # escaped. proceed as if no dollar sign was at the end - if buf[^1] == '\\': - # Check if the regex contains an invalid trailing backslash. - let j = buf.countBackslashes(buf.high - 1) - if j mod 2 != 1: # odd, because we do not count the last backslash - return err("unexpected end") - var buf2 = "^" - buf2 &= buf - buf2 &= "$" - return compileRegex(buf2) - -proc compileSearchRegex*(str: string; defaultFlags: LREFlags): - Result[Regex, string] = - # Emulate vim's \c/\C: override defaultFlags if one is found, then remove it - # from str. - # Also, replace \< and \> with \b as (a bit sloppy) vi emulation. - var flags = defaultFlags - var s = newStringOfCap(str.len) - var quot = false - for c in str: - if quot: - quot = false - case c - of 'c': flags.incl(LRE_FLAG_IGNORECASE) - of 'C': flags.excl(LRE_FLAG_IGNORECASE) - of '<', '>': s &= "\\b" - else: s &= '\\' & c - elif c == '\\': - quot = true - else: - s &= c - if quot: - s &= '\\' - flags.incl(LRE_FLAG_GLOBAL) # for easy backwards matching - return compileRegex(s, flags) - -proc exec*(regex: Regex; str: string; start = 0; length = -1; nocaps = false): - RegexResult = - let length = if length == -1: - str.len - else: - length - assert start in 0 .. length - let bytecode = unsafeAddr regex.bytecode[0] - let captureCount = lre_get_capture_count(bytecode) - var capture: ptr UncheckedArray[int] = nil - if captureCount > 0: - let size = sizeof(ptr uint8) * captureCount * 2 - capture = cast[ptr UncheckedArray[int]](alloc0(size)) - var cstr = cstring(str) - let flags = lre_get_flags(bytecode).toLREFlags - var start = start - while true: - let ret = lre_exec(cast[ptr ptr uint8](capture), bytecode, - cast[ptr uint8](cstr), cint(start), cint(length), cint(3), nil) - if ret != 1: #TODO error handling? (-1) - break - result.success = true - if captureCount == 0 or nocaps: - break - var caps: seq[RegexCapture] = @[] - let cstrAddress = cast[int](cstr) - let ps = start - start = capture[1] - cstrAddress - for i in 0 ..< captureCount: - let s = capture[i * 2] - cstrAddress - let e = capture[i * 2 + 1] - cstrAddress - caps.add((s, e)) - result.captures.add(caps) - if LRE_FLAG_GLOBAL notin flags: - break - if start >= str.len: - break - if ps == start: - start += runeLenAt(str, start) - if captureCount > 0: - dealloc(capture) - -proc match*(regex: Regex; str: string; start = 0; length = str.len): bool = - return regex.exec(str, start, length, nocaps = true).success diff --git a/src/js/jstypes.nim b/src/js/jstypes.nim deleted file mode 100644 index 9e1d72ea..00000000 --- a/src/js/jstypes.nim +++ /dev/null @@ -1,43 +0,0 @@ -import bindings/quickjs - -# This is the WebIDL dictionary type. -# We only use it for type inference in generics. -#TODO required members -type JSDict* = object of RootObj - -# Containers compatible with the internal representation of strings in QuickJS. -# To convert these, a copy is still needed; however, they remove the UTF-8 -# transcoding step. -type - NarrowString* = distinct string - WideString* = distinct seq[uint16] - -# Various containers for array buffer types. -# Converting these only requires copying the metadata; buffers are never copied. -type - JSArrayBuffer* = object - p*: ptr UncheckedArray[uint8] - len*: csize_t - dealloc*: JSFreeArrayBufferDataFunc - - JSArrayBufferView* = object - abuf*: JSArrayBuffer - offset*: csize_t # offset into the buffer - nmemb*: csize_t # number of members - nsize*: csize_t # member size - - JSUint8Array* = object - abuf*: JSArrayBuffer - offset*: csize_t # offset into the buffer - nmemb*: csize_t # number of members - -func high*(abuf: JSArrayBuffer): int = - return int(abuf.len) - 1 - -# A specialization of JSValue to make writing generic code for functions -# easier. -type JSValueFunction* = ref object - fun*: JSValue - -converter toJSValue*(f: JSValueFunction): JSValue = - f.fun diff --git a/src/js/jsutils.nim b/src/js/jsutils.nim deleted file mode 100644 index b8a8398e..00000000 --- a/src/js/jsutils.nim +++ /dev/null @@ -1,9 +0,0 @@ -import bindings/quickjs - -template toJSValueArray*(a: openArray[JSValue]): ptr UncheckedArray[JSValue] = - cast[ptr UncheckedArray[JSValue]](unsafeAddr a[0]) - -# Warning: this must be a template, because we're taking the address of -# the passed value, and Nim is pass-by-value. -template toJSValueArray*(a: JSValue): ptr UncheckedArray[JSValue] = - cast[ptr UncheckedArray[JSValue]](unsafeAddr a) diff --git a/src/js/timeout.nim b/src/js/timeout.nim index 3c95dada..9c1b0dba 100644 --- a/src/js/timeout.nim +++ b/src/js/timeout.nim @@ -2,7 +2,7 @@ import std/selectors import std/tables import io/dynstream -import js/javascript +import monoucha/javascript type TimeoutState* = object timeoutid: int32 diff --git a/src/js/tojs.nim b/src/js/tojs.nim deleted file mode 100644 index 59109884..00000000 --- a/src/js/tojs.nim +++ /dev/null @@ -1,399 +0,0 @@ -# Automatic conversion of Nim types to JavaScript types. -# -# Every conversion involves copying unless explicitly noted below. -# -# * Primitives are converted to their respective JavaScript counterparts. -# * seq is converted to a JS array. Note: this always copies the seq's contents. -# * enum is converted to its stringifier's output. -# * JSValue is returned as-is, *without* a DupValue operation. -# * JSError is converted to a new error object corresponding to the error -# it represents. -# * JSArrayBuffer, JSUint8Array are converted to a JS object without copying -# their contents. -# * NarrowString is converted to a JS narrow string (with copying). For more -# information on JS string handling, see js/jstypes.nim. -# * Finally, ref object is converted to a JS object whose opaque is the ref -# object. (See below.) -# -# Note that ref objects can be seamlessly converted to JS objects, despite -# the fact that they are managed by two separate garbage collectors. This -# works thanks to a patch in QJS and machine oil. Basically: -# -# * Nim objects registered with registerType can be paired with one (1) -# JS object each. -# * This happens on-demand, whenever the Nim object has to be converted into JS. -# * Once the conversion happened, the JS object will be kept alive until the -# Nim object is destroyed, so that JS properties on the JS object are not -# lost during a re-conversion. -# * Similarly, the Nim object is kept alive so long as the JS object is alive. -# * The patched in can_destroy hook is used to synchronize reference counts -# of the two objects; this way, no memory leak occurs. -# -# There are also toJSP variants of object converters. These work identically -# to ref object converters, except the reference count of the closest -# `ref object' ancestor is incremented/decremented when synchronizing refcounts -# with the JS object pair. - -import std/options -import std/tables -import std/unicode - -import bindings/quickjs -import js/jserror -import js/jstypes -import js/jsutils -import js/jsopaque -import types/opt - -# Convert Nim types to the corresponding JavaScript type. -# This does not work with var objects. -proc toJS*(ctx: JSContext; s: string): JSValue -proc toJS*(ctx: JSContext; r: Rune): JSValue -proc toJS*(ctx: JSContext; n: int64): JSValue -proc toJS*(ctx: JSContext; n: int32): JSValue -proc toJS*(ctx: JSContext; n: int): JSValue -proc toJS*(ctx: JSContext; n: uint16): JSValue -proc toJS*(ctx: JSContext; n: uint32): JSValue -proc toJS*(ctx: JSContext; n: uint64): JSValue -proc toJS*(ctx: JSContext; n: float64): JSValue -proc toJS*(ctx: JSContext; b: bool): JSValue -proc toJS*[U, V](ctx: JSContext; t: Table[U, V]): JSValue -proc toJS*(ctx: JSContext; opt: Option): JSValue -proc toJS*[T, E](ctx: JSContext; opt: Result[T, E]): JSValue -proc toJS*(ctx: JSContext; s: seq): JSValue -proc toJS*[T](ctx: JSContext; s: set[T]): JSValue -proc toJS*(ctx: JSContext; t: tuple): JSValue -proc toJS*(ctx: JSContext; e: enum): JSValue -proc toJS*(ctx: JSContext; j: JSValue): JSValue -proc toJS*(ctx: JSContext; obj: ref object): JSValue -proc toJS*(ctx: JSContext; err: JSError): JSValue -proc toJS*(ctx: JSContext; abuf: JSArrayBuffer): JSValue -proc toJS*(ctx: JSContext; u8a: JSUint8Array): JSValue -proc toJS*(ctx: JSContext; ns: NarrowString): JSValue -proc toJS*(ctx: JSContext; dict: JSDict): JSValue - -# Convert Nim types to the corresponding JavaScript type, with knowledge of -# the parent object. -# This supports conversion of var object types. -# -# The idea here is to allow conversion of var objects to quasi-reference types -# by saving a pointer to their ancestor and incrementing/decrementing the -# ancestor's reference count instead. -proc toJSP*(ctx: JSContext; parent: ref object; child: var object): JSValue -proc toJSP*(ctx: JSContext; parent: ptr object; child: var object): JSValue - -# Same as toJS, but used in constructors. ctor contains the target prototype, -# used for subclassing from JS. -proc toJSNew*(ctx: JSContext; obj: ref object; ctor: JSValue): JSValue -proc toJSNew*[T, E](ctx: JSContext; opt: Result[T, E]; ctor: JSValue): JSValue - -# Avoid accidentally calling toJSP on objects that we have explicit toJS -# converters for. -template makeToJSP(typ: untyped) = - template toJSP*(ctx: JSContext; parent: ref object; child: var typ): JSValue = - toJS(ctx, child) - template toJSP*(ctx: JSContext; parent: ptr object; child: var typ): JSValue = - toJS(ctx, child) -makeToJSP(Table) -makeToJSP(Option) -makeToJSP(Result) -makeToJSP(JSValue) -makeToJSP(JSDict) - -# Note: this consumes `prop'. -proc defineProperty(ctx: JSContext; this: JSValue; name: JSAtom; - prop: JSValue; flags = cint(0)) = - if JS_DefinePropertyValue(ctx, this, name, prop, flags) <= 0: - raise newException(Defect, "Failed to define property string") - -proc defineProperty(ctx: JSContext; this, name, prop: JSValue; - flags = cint(0)) = - let atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if unlikely(atom == JS_ATOM_NULL): - raise newException(Defect, "Failed to define property string") - ctx.defineProperty(this, atom, prop, flags) - JS_FreeAtom(ctx, atom); - -proc definePropertyC*(ctx: JSContext; this: JSValue; name: JSAtom; - prop: JSValue) = - ctx.defineProperty(this, name, prop, JS_PROP_CONFIGURABLE) - -proc defineProperty(ctx: JSContext; this: JSValue; name: string; - prop: JSValue; flags = cint(0)) = - if JS_DefinePropertyValueStr(ctx, this, cstring(name), prop, flags) <= 0: - raise newException(Defect, "Failed to define property string: " & name) - -proc definePropertyC*(ctx: JSContext; this: JSValue; name: string; - prop: JSValue) = - ctx.defineProperty(this, name, prop, JS_PROP_CONFIGURABLE) - -proc defineProperty*[T](ctx: JSContext; this: JSValue; name: string; prop: T; - flags = cint(0)) = - defineProperty(ctx, this, name, toJS(ctx, prop), flags) - -proc definePropertyE*[T](ctx: JSContext; this: JSValue; name: string; - prop: T) = - defineProperty(ctx, this, name, prop, JS_PROP_ENUMERABLE) - -proc definePropertyCW*[T](ctx: JSContext; this: JSValue; name: string; - prop: T) = - defineProperty(ctx, this, name, prop, JS_PROP_CONFIGURABLE or - JS_PROP_WRITABLE) - -proc definePropertyCWE*[T](ctx: JSContext; this: JSValue; name: string; - prop: T) = - defineProperty(ctx, this, name, prop, JS_PROP_C_W_E) - -proc newFunction*(ctx: JSContext; args: openArray[string]; body: string): - JSValue = - var paramList: seq[JSValue] = @[] - for arg in args: - paramList.add(toJS(ctx, arg)) - paramList.add(toJS(ctx, body)) - let fun = JS_CallConstructor(ctx, ctx.getOpaque().valRefs[jsvFunction], - cint(paramList.len), paramList.toJSValueArray()) - for param in paramList: - JS_FreeValue(ctx, param) - return fun - -proc toJS*(ctx: JSContext; s: cstring): JSValue = - return JS_NewString(ctx, s) - -proc toJS*(ctx: JSContext; s: string): JSValue = - return toJS(ctx, cstring(s)) - -proc toJS*(ctx: JSContext; r: Rune): JSValue = - return toJS(ctx, $r) - -proc toJS*(ctx: JSContext; n: int32): JSValue = - return JS_NewInt32(ctx, n) - -proc toJS*(ctx: JSContext; n: int64): JSValue = - return JS_NewInt64(ctx, n) - -# Always int32, so we don't risk 32-bit only breakage. -proc toJS*(ctx: JSContext; n: int): JSValue = - return toJS(ctx, int32(n)) - -proc toJS*(ctx: JSContext; n: uint16): JSValue = - return JS_NewUint32(ctx, uint32(n)) - -proc toJS*(ctx: JSContext; n: uint32): JSValue = - return JS_NewUint32(ctx, n) - -proc toJS*(ctx: JSContext; n: uint64): JSValue = - #TODO this is incorrect - return JS_NewFloat64(ctx, float64(n)) - -proc toJS*(ctx: JSContext; n: float64): JSValue = - return JS_NewFloat64(ctx, n) - -proc toJS*(ctx: JSContext; b: bool): JSValue = - return JS_NewBool(ctx, b) - -proc toJS*[U, V](ctx: JSContext; t: Table[U, V]): JSValue = - let obj = JS_NewObject(ctx) - if not JS_IsException(obj): - for k, v in t: - definePropertyCWE(ctx, obj, k, v) - return obj - -proc toJS*(ctx: JSContext; opt: Option): JSValue = - if opt.isSome: - return toJS(ctx, opt.get) - return JS_NULL - -proc toJS*[T, E](ctx: JSContext; opt: Result[T, E]): JSValue = - if opt.isSome: - when not (T is void): - return toJS(ctx, opt.get) - else: - return JS_UNDEFINED - else: - when not (E is void): - if opt.error != nil: - return JS_Throw(ctx, toJS(ctx, opt.error)) - return JS_EXCEPTION - -proc toJS*(ctx: JSContext; s: seq): JSValue = - let a = JS_NewArray(ctx) - if not JS_IsException(a): - for i, x in s: - let val = toJS(ctx, x) - if JS_IsException(val): - return val - ctx.defineProperty(a, JS_NewInt64(ctx, int64(i)), val, - JS_PROP_C_W_E or JS_PROP_THROW) - return a - -proc toJS*[T](ctx: JSContext; s: set[T]): JSValue = - #TODO this is a bit lazy :p - var x = newSeq[T]() - for e in s: - x.add(e) - var a = toJS(ctx, x) - if JS_IsException(a): - return a - let ret = JS_CallConstructor(ctx, ctx.getOpaque().valRefs[jsvSet], 1, - a.toJSValueArray()) - JS_FreeValue(ctx, a) - return ret - -proc toJS(ctx: JSContext; t: tuple): JSValue = - let a = JS_NewArray(ctx) - if not JS_IsException(a): - var i = 0 - for f in t.fields: - let val = toJS(ctx, f) - if JS_IsException(val): - return val - ctx.defineProperty(a, JS_NewInt64(ctx, int64(i)), val, - JS_PROP_C_W_E or JS_PROP_THROW) - inc i - return a - -proc toJSP0(ctx: JSContext; p, tp: pointer; ctor: JSValue; - needsref: var bool): JSValue = - JS_GetRuntime(ctx).getOpaque().plist.withValue(p, obj): - # a JSValue already points to this object. - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, obj[])) - let ctxOpaque = ctx.getOpaque() - let class = ctxOpaque.typemap[tp] - let jsObj = JS_NewObjectFromCtor(ctx, ctor, class) - if JS_IsException(jsObj): - return jsObj - setOpaque(ctx, jsObj, p) - # We are constructing a new JS object, so we must add unforgeable properties - # here. - ctxOpaque.unforgeable.withValue(class, uf): - JS_SetPropertyFunctionList(ctx, jsObj, addr uf[][0], cint(uf[].len)) - needsref = true - if unlikely(ctxOpaque.htmldda == class): - JS_SetIsHTMLDDA(ctx, jsObj) - return jsObj - -# Get a unique pointer for each type. -proc getTypePtr*[T](x: T): pointer = - when T is RootRef: - # I'm so sorry. - # (This dereferences the object's first member, m_type. Probably.) - return cast[ptr pointer](x)[] - elif T is RootObj: - return cast[pointer](x) - else: - return getTypeInfo(x) - -func getTypePtr*(t: typedesc[ref object]): pointer = - var x = t() - return getTypePtr(x) - -func getTypePtr*(t: type): pointer = - var x: t - return getTypePtr(x) - -proc toJSRefObj(ctx: JSContext; obj: ref object): JSValue = - if obj == nil: - return JS_NULL - let p = cast[pointer](obj) - let tp = getTypePtr(obj) - var needsref = false - let val = toJSP0(ctx, p, tp, JS_UNDEFINED, needsref) - if needsref: - GC_ref(obj) - return val - -proc toJS*(ctx: JSContext; obj: ref object): JSValue = - return toJSRefObj(ctx, obj) - -proc toJSNew*(ctx: JSContext; obj: ref object; ctor: JSValue): JSValue = - if obj == nil: - return JS_NULL - let p = cast[pointer](obj) - let tp = getTypePtr(obj) - var needsref = false - let val = toJSP0(ctx, p, tp, ctor, needsref) - if needsref: - GC_ref(obj) - return val - -proc toJSNew[T, E](ctx: JSContext; opt: Result[T, E]; ctor: JSValue): JSValue = - if opt.isSome: - when not (T is void): - return toJSNew(ctx, opt.get, ctor) - else: - return JS_UNDEFINED - else: - when not (E is void): - let res = toJS(ctx, opt.error) - if not JS_IsNull(res): - return JS_Throw(ctx, res) - else: - return JS_NULL - -proc toJS(ctx: JSContext; e: enum): JSValue = - return toJS(ctx, $e) - -proc toJS(ctx: JSContext; j: JSValue): JSValue = - return j - -proc toJS*(ctx: JSContext; err: JSError): JSValue = - if err == nil: - return JS_EXCEPTION - if err.e notin QuickJSErrors: - return toJSRefObj(ctx, err) - var msg = toJS(ctx, err.message) - if JS_IsException(msg): - return msg - let ctor = ctx.getOpaque().errCtorRefs[err.e] - let ret = JS_CallConstructor(ctx, ctor, 1, msg.toJSValueArray()) - JS_FreeValue(ctx, msg) - return ret - -proc toJS*(ctx: JSContext; abuf: JSArrayBuffer): JSValue = - return JS_NewArrayBuffer(ctx, abuf.p, abuf.len, abuf.dealloc, nil, false) - -proc toJS*(ctx: JSContext; u8a: JSUint8Array): JSValue = - let jsabuf = toJS(ctx, u8a.abuf) - let ctor = ctx.getOpaque().valRefs[jsvUint8Array] - let ret = JS_CallConstructor(ctx, ctor, 1, jsabuf.toJSValueArray()) - JS_FreeValue(ctx, jsabuf) - return ret - -proc toJS*(ctx: JSContext; ns: NarrowString): JSValue = - return JS_NewNarrowStringLen(ctx, cstring(ns), csize_t(string(ns).len)) - -proc toJS*(ctx: JSContext; dict: JSDict): JSValue = - let obj = JS_NewObject(ctx) - if JS_IsException(obj): - return obj - for k, v in dict.fieldPairs: - ctx.defineProperty(obj, k, v) - return obj - -proc toJSP(ctx: JSContext; parent: ref object; child: var object): JSValue = - let p = addr child - # Save parent as the original ancestor for this tree. - JS_GetRuntime(ctx).getOpaque().refmap[p] = ( - (proc() = - GC_ref(parent)), - (proc() = - GC_unref(parent)) - ) - let tp = getTypePtr(child) - var needsref = false - let val = toJSP0(ctx, p, tp, JS_UNDEFINED, needsref) - if needsref: - GC_ref(parent) - return val - -proc toJSP(ctx: JSContext; parent: ptr object; child: var object): JSValue = - let p = addr child - # Increment the reference count of parent's root ancestor, and save the - # increment/decrement callbacks for the child as well. - let rtOpaque = JS_GetRuntime(ctx).getOpaque() - let ru = rtOpaque.refmap[parent] - ru.cref() - rtOpaque.refmap[p] = ru - let tp = getTypePtr(child) - return toJSP0(ctx, p, tp) diff --git a/src/loader/headers.nim b/src/loader/headers.nim index 21900189..ff7f9ed3 100644 --- a/src/loader/headers.nim +++ b/src/loader/headers.nim @@ -1,9 +1,10 @@ import std/tables -import bindings/quickjs -import js/jserror -import js/fromjs -import js/javascript +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/quickjs +import types/opt import utils/twtstr type diff --git a/src/loader/loader.nim b/src/loader/loader.nim index 8bc23c16..c84f247a 100644 --- a/src/loader/loader.nim +++ b/src/loader/loader.nim @@ -33,15 +33,16 @@ import io/serversocket import io/socketstream import io/tempfile import io/urlfilter -import js/jserror -import js/javascript import loader/cgi import loader/connecterror import loader/headers import loader/loaderhandle import loader/request import loader/response +import monoucha/javascript +import monoucha/jserror import types/cookie +import types/opt import types/referrer import types/urimethodmap import types/url diff --git a/src/loader/request.nim b/src/loader/request.nim index 1398d25f..fc6c43f5 100644 --- a/src/loader/request.nim +++ b/src/loader/request.nim @@ -2,15 +2,16 @@ import std/options import std/strutils import std/tables -import bindings/quickjs import html/script -import js/fromjs -import js/javascript -import js/jserror -import js/jstypes import loader/headers +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/jstypes +import monoucha/quickjs import types/blob import types/formdata +import types/opt import types/referrer import types/url diff --git a/src/loader/response.nim b/src/loader/response.nim index 4cf17c99..ca300957 100644 --- a/src/loader/response.nim +++ b/src/loader/response.nim @@ -1,22 +1,22 @@ import std/strutils import std/tables -import bindings/quickjs +import chagashi/charset +import chagashi/decoder +import chagashi/validator import io/promise import io/socketstream -import js/jserror -import js/javascript import loader/headers import loader/request +import monoucha/javascript +import monoucha/jserror +import monoucha/quickjs import types/blob +import types/opt import types/url import utils/mimeguess import utils/twtstr -import chagashi/charset -import chagashi/decoder -import chagashi/validator - type ResponseType* = enum TYPE_DEFAULT = "default" diff --git a/src/local/client.nim b/src/local/client.nim index a14ce63c..4b81f668 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -9,8 +9,6 @@ import std/strutils import std/tables import std/unicode -import bindings/constcharp -import bindings/quickjs import config/config import html/catom import html/chadombuilder @@ -30,16 +28,9 @@ import js/base64 import js/console import js/domexception import js/encoding -import js/fromjs import js/intl -import js/javascript -import js/jserror import js/jsmodule -import js/jsopaque -import js/jstypes -import js/jsutils import js/timeout -import js/tojs import loader/headers import loader/loader import loader/request @@ -47,6 +38,15 @@ import local/container import local/lineedit import local/pager import local/term +import monoucha/constcharp +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/jsopaque +import monoucha/jstypes +import monoucha/jsutils +import monoucha/quickjs +import monoucha/tojs import server/buffer import server/forkserver import types/blob diff --git a/src/local/container.nim b/src/local/container.nim index 00553190..8439b32a 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -3,6 +3,7 @@ import std/net import std/options import std/os import std/posix +import std/tables import std/unicode import config/config @@ -12,18 +13,19 @@ import io/dynstream import io/promise import io/serversocket import io/socketstream -import js/javascript -import js/jsregex -import js/jstypes import layout/renderdocument import loader/headers import loader/loader import loader/request import local/select +import monoucha/javascript +import monoucha/jsregex +import monoucha/jstypes import server/buffer import types/cell import types/color import types/cookie +import types/opt import types/referrer import types/url import types/winattrs diff --git a/src/local/lineedit.nim b/src/local/lineedit.nim index 04507de7..22fd3988 100644 --- a/src/local/lineedit.nim +++ b/src/local/lineedit.nim @@ -1,8 +1,8 @@ import std/strutils import std/unicode -import bindings/quickjs -import js/javascript +import monoucha/quickjs +import monoucha/javascript import types/cell import types/opt import types/winattrs diff --git a/src/local/pager.nim b/src/local/pager.nim index 367802d9..8eb42b65 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -8,8 +8,6 @@ import std/selectors import std/tables import std/unicode -import bindings/libregexp -import bindings/quickjs import config/chapath import config/config import config/mailcap @@ -21,13 +19,6 @@ import io/socketstream import io/stdio import io/tempfile import io/urlfilter -import js/fromjs -import js/javascript -import js/jserror -import js/jsregex -import js/jstypes -import js/jsutils -import js/tojs import loader/connecterror import loader/headers import loader/loader @@ -36,6 +27,15 @@ import local/container import local/lineedit import local/select import local/term +import monoucha/fromjs +import monoucha/javascript +import monoucha/jserror +import monoucha/jsregex +import monoucha/jstypes +import monoucha/jsutils +import monoucha/libregexp +import monoucha/quickjs +import monoucha/tojs import server/buffer import server/forkserver import types/cell @@ -46,6 +46,7 @@ import types/url import types/winattrs import utils/luwrap import utils/mimeguess +import utils/regexutils import utils/strwidth import utils/twtstr diff --git a/src/local/select.nim b/src/local/select.nim index 997eff1c..9e2f2c22 100644 --- a/src/local/select.nim +++ b/src/local/select.nim @@ -1,6 +1,6 @@ import std/unicode -import js/jsregex +import monoucha/jsregex import server/buffer import types/cell import utils/luwrap diff --git a/src/main.nim b/src/main.nim index 808eff00..e070597f 100644 --- a/src/main.nim +++ b/src/main.nim @@ -1,21 +1,20 @@ import version -# Note: we can't just import std/os or the compiler cries. (No idea why.) -from std/os import getEnv, putEnv, commandLineParams, getCurrentDir import std/options +import std/os import std/posix -import server/forkserver +import chagashi/charset import config/chapath import config/config -import js/javascript import local/client import local/term +import monoucha/javascript +import server/forkserver +import types/opt import utils/strwidth import utils/twtstr -import chagashi/charset - const ChaVersionStr = block: var s = "Chawan browser v0.1 " when defined(debug): diff --git a/src/server/buffer.nim b/src/server/buffer.nim index 9a141135..e471c1ae 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -10,8 +10,6 @@ import std/selectors import std/tables import std/unicode -import bindings/libregexp -import bindings/quickjs import config/config import css/cascade import css/cssparser @@ -37,14 +35,16 @@ import io/promise import io/serversocket import io/socketstream import js/console -import js/fromjs -import js/javascript -import js/jsregex import js/timeout -import js/tojs import layout/renderdocument import loader/headers import loader/loader +import monoucha/fromjs +import monoucha/javascript +import monoucha/jsregex +import monoucha/libregexp +import monoucha/quickjs +import monoucha/tojs import types/blob import types/cell import types/color diff --git a/src/types/blob.nim b/src/types/blob.nim index dd812c02..ce5311ea 100644 --- a/src/types/blob.nim +++ b/src/types/blob.nim @@ -2,9 +2,9 @@ import std/options import std/posix import std/strutils -import js/fromjs -import js/javascript -import js/jstypes +import monoucha/fromjs +import monoucha/javascript +import monoucha/jstypes import utils/mimeguess type diff --git a/src/types/cookie.nim b/src/types/cookie.nim index d1302710..2cc5928c 100644 --- a/src/types/cookie.nim +++ b/src/types/cookie.nim @@ -2,9 +2,9 @@ import std/strutils import std/times import io/urlfilter -import js/jserror -import js/javascript -import js/jsregex +import monoucha/jserror +import monoucha/javascript +import monoucha/jsregex import types/url import types/opt import utils/twtstr diff --git a/src/types/formdata.nim b/src/types/formdata.nim index ec3d8bbf..26fd7975 100644 --- a/src/types/formdata.nim +++ b/src/types/formdata.nim @@ -2,7 +2,7 @@ import std/strutils import io/dynstream import io/posixstream -import js/javascript +import monoucha/javascript import types/blob import utils/twtstr diff --git a/src/types/url.nim b/src/types/url.nim index 075e3b08..627af4aa 100644 --- a/src/types/url.nim +++ b/src/types/url.nim @@ -5,11 +5,12 @@ import std/strutils import std/tables import std/unicode -import bindings/libunicode -import js/jserror -import js/javascript import lib/punycode +import monoucha/javascript +import monoucha/jserror +import monoucha/libunicode import types/blob +import types/opt import utils/luwrap import utils/map import utils/twtstr diff --git a/src/utils/luwrap.nim b/src/utils/luwrap.nim index 853d3015..7ccb2c51 100644 --- a/src/utils/luwrap.nim +++ b/src/utils/luwrap.nim @@ -2,7 +2,7 @@ import std/algorithm import std/strutils import std/unicode -import bindings/libunicode +import monoucha/libunicode import utils/charcategory proc passRealloc(opaque, p: pointer; size: csize_t): pointer {.cdecl.} = @@ -50,7 +50,8 @@ proc capitalizeLU*(s: string): string = result = newStringOfCap(s.len) var wordStart = true for r in s.runes: - if lre_is_space(uint32(r)) == 1: + if uint32(r) < 256 and char(r) in AsciiWhitespace or + lre_is_space_non_ascii(uint32(r)) == 1: wordStart = true result &= $r elif wordStart: diff --git a/src/utils/regexutils.nim b/src/utils/regexutils.nim new file mode 100644 index 00000000..ab7308f6 --- /dev/null +++ b/src/utils/regexutils.nim @@ -0,0 +1,64 @@ +import types/opt + +import monoucha/jsregex +import monoucha/libregexp + +func countBackslashes(buf: string; i: int): int = + var j = 0 + for i in countdown(i, 0): + if buf[i] != '\\': + break + inc j + return j + +# ^abcd -> ^abcd +# efgh$ -> efgh$ +# ^ijkl$ -> ^ijkl$ +# mnop -> ^mnop$ +proc compileMatchRegex*(buf: string): Result[Regex, string] = + if buf.len == 0: + return compileRegex(buf) + if buf[0] == '^': + return compileRegex(buf) + if buf[^1] == '$': + # Check whether the final dollar sign is escaped. + if buf.len == 1 or buf[^2] != '\\': + return compileRegex(buf) + let j = buf.countBackslashes(buf.high - 2) + if j mod 2 == 1: # odd, because we do not count the last backslash + return compileRegex(buf) + # escaped. proceed as if no dollar sign was at the end + if buf[^1] == '\\': + # Check if the regex contains an invalid trailing backslash. + let j = buf.countBackslashes(buf.high - 1) + if j mod 2 != 1: # odd, because we do not count the last backslash + return err("unexpected end") + var buf2 = "^" + buf2 &= buf + buf2 &= "$" + return compileRegex(buf2) + +proc compileSearchRegex*(str: string; defaultFlags: LREFlags): + Result[Regex, string] = + # Emulate vim's \c/\C: override defaultFlags if one is found, then remove it + # from str. + # Also, replace \< and \> with \b as (a bit sloppy) vi emulation. + var flags = defaultFlags + var s = newStringOfCap(str.len) + var quot = false + for c in str: + if quot: + quot = false + case c + of 'c': flags.incl(LRE_FLAG_IGNORECASE) + of 'C': flags.excl(LRE_FLAG_IGNORECASE) + of '<', '>': s &= "\\b" + else: s &= '\\' & c + elif c == '\\': + quot = true + else: + s &= c + if quot: + s &= '\\' + flags.incl(LRE_FLAG_GLOBAL) # for easy backwards matching + return compileRegex(s, flags) diff --git a/src/version.nim b/src/version.nim index 66e81a9f..c1c90c54 100644 --- a/src/version.nim +++ b/src/version.nim @@ -24,7 +24,9 @@ macro checkVersion(xs: static string; major, minor, patch: int) = tryImport chagashi/version, "chagashi" tryImport chame/version, "chame" +tryImport monoucha/version, "monoucha" static: checkVersion("chagashi", 0, 4, 2) checkVersion("chame", 0, 14, 5) + checkVersion("monoucha", 0, 1, 1) |